From bd4e115a78b52e3fbc016e5e30bb0e19b2a9e7d6 Mon Sep 17 00:00:00 2001 From: vsr Date: Thu, 9 Aug 2012 10:03:55 +0000 Subject: [PATCH] Merge from V6_main_20120808 08Aug12 --- Makefile.am | 40 +- SMESH_version.h.in | 32 +- adm_local/Makefile.am | 32 +- adm_local/cmake_files/FindSMESH.cmake | 42 + adm_local/cmake_files/Makefile.am | 25 + adm_local/unix/Makefile.am | 30 +- adm_local/unix/config_files/Makefile.am | 33 +- adm_local/unix/config_files/check_Platform.m4 | 31 +- adm_local/unix/config_files/check_SMESH.m4 | 31 +- adm_local/unix/config_files/check_cgal.m4 | 101 + adm_local/unix/config_files/check_cgns.m4 | 103 + adm_local/unix/config_files/check_f77.m4 | 30 +- adm_local/unix/config_files/check_padder.m4 | 69 + adm_local/unix/config_files/check_qwt.m4 | 187 + adm_local/unix/make_common_starter.am | 42 +- bin/Makefile.am | 34 +- bin/VERSION.in | 4 +- bin/smesh_setenv.py | 84 + build_cmake | 27 + build_cmake.bat | 20 + build_configure | 112 +- clean_configure | 34 +- configure.ac | 305 +- doc/Makefile.am | 33 +- doc/docutils/Makefile.am | 97 + doc/docutils/conf.py.in | 200 + doc/docutils/docapi.rst | 17 + doc/docutils/index.rst | 10 + doc/docutils/overview.rst | 24 + doc/salome/Makefile.am | 30 +- doc/salome/gui/Makefile.am | 30 +- doc/salome/gui/SMESH/Makefile.am | 78 +- doc/salome/gui/SMESH/doxyfile.in | 52 +- doc/salome/gui/SMESH/doxyfile_py.in | 45 +- .../gui/SMESH/images/2d_from_3d_dlg.png | Bin 0 -> 17623 bytes .../gui/SMESH/images/2d_from_3d_ico.png | Bin 0 -> 406 bytes .../gui/SMESH/images/2d_from_3d_menu.png | Bin 0 -> 24838 bytes .../gui/SMESH/images/a-arithmetic1d.png | Bin 12239 -> 14764 bytes .../gui/SMESH/images/a-averagelength.png | Bin 13679 -> 13595 bytes doc/salome/gui/SMESH/images/a-clipping2.png | Bin 21116 -> 17327 bytes doc/salome/gui/SMESH/images/a-creategroup.png | Bin 23197 -> 23996 bytes .../SMESH/images/a-createpolyhedralvolume.png | Bin 19771 -> 22736 bytes doc/salome/gui/SMESH/images/a-nbsegments1.png | Bin 5628 -> 15530 bytes doc/salome/gui/SMESH/images/a-nbsegments2.png | Bin 6027 -> 21081 bytes .../gui/SMESH/images/a-startendlength.png | Bin 5404 -> 17193 bytes .../gui/SMESH/images/a-transparency.png | Bin 7152 -> 9149 bytes doc/salome/gui/SMESH/images/add0delement.png | Bin 0 -> 15836 bytes doc/salome/gui/SMESH/images/add_0delement.png | Bin 0 -> 1014 bytes doc/salome/gui/SMESH/images/add_ball.png | Bin 0 -> 1578 bytes doc/salome/gui/SMESH/images/addball.png | Bin 0 -> 17190 bytes doc/salome/gui/SMESH/images/addedge.png | Bin 10602 -> 14703 bytes doc/salome/gui/SMESH/images/addhexahedron.png | Bin 12169 -> 17546 bytes doc/salome/gui/SMESH/images/addinfo_group.png | Bin 0 -> 20564 bytes doc/salome/gui/SMESH/images/addinfo_mesh.png | Bin 0 -> 39855 bytes .../gui/SMESH/images/addinfo_submesh.png | Bin 0 -> 15335 bytes doc/salome/gui/SMESH/images/addnode.png | Bin 9245 -> 13371 bytes .../gui/SMESH/images/addnode_notebook.png | Bin 11965 -> 14609 bytes doc/salome/gui/SMESH/images/addpolygon.png | Bin 12475 -> 16868 bytes doc/salome/gui/SMESH/images/addquadrangle.png | Bin 13439 -> 17782 bytes .../gui/SMESH/images/addtetrahedron.png | Bin 11925 -> 17173 bytes doc/salome/gui/SMESH/images/addtriangle.png | Bin 12493 -> 16714 bytes .../gui/SMESH/images/advanced_mesh_infos.png | Bin 24892 -> 33649 bytes doc/salome/gui/SMESH/images/aqt.png | Bin 20575 -> 25279 bytes .../SMESH/images/bare_border_faces_smpl.png | Bin 0 -> 9095 bytes .../SMESH/images/bare_border_volumes_smpl.png | Bin 0 -> 7555 bytes .../gui/SMESH/images/blsurf_parameters.png | Bin 38953 -> 0 bytes .../images/blsurf_parameters_advanced.png | Bin 25172 -> 0 bytes doc/salome/gui/SMESH/images/bnd_box.png | Bin 0 -> 17441 bytes .../gui/SMESH/images/bnd_box_preview.png | Bin 0 -> 56083 bytes .../gui/SMESH/images/cartesian3D_hyp.png | Bin 0 -> 34405 bytes .../gui/SMESH/images/cartesian3D_sphere.png | Bin 0 -> 12475 bytes doc/salome/gui/SMESH/images/colors_size.png | Bin 0 -> 20715 bytes .../gui/SMESH/images/controls_popup.png | Bin 0 -> 3441 bytes doc/salome/gui/SMESH/images/convert.png | Bin 11849 -> 13963 bytes doc/salome/gui/SMESH/images/copy_mesh_dlg.png | Bin 0 -> 24617 bytes .../gui/SMESH/images/copy_mesh_icon.png | Bin 0 -> 1117 bytes .../images/create_groups_from_geometry.png | Bin 17338 -> 14205 bytes doc/salome/gui/SMESH/images/creategroup.png | Bin 30825 -> 33751 bytes .../SMESH/images/creategroup_on_filter.png | Bin 0 -> 21645 bytes .../gui/SMESH/images/custom_point_marker.png | Bin 0 -> 8145 bytes doc/salome/gui/SMESH/images/dialog.png | Bin 17037 -> 9281 bytes .../distributionwithanalyticdensity.png | Bin 13463 -> 35416 bytes .../images/distributionwithtabledensity.png | Bin 16346 -> 38836 bytes doc/salome/gui/SMESH/images/double_faces.png | Bin 0 -> 6934 bytes doc/salome/gui/SMESH/images/double_nodes.png | Bin 0 -> 6636 bytes doc/salome/gui/SMESH/images/duplicate01.png | Bin 0 -> 22358 bytes doc/salome/gui/SMESH/images/duplicate02.png | Bin 0 -> 27801 bytes .../gui/SMESH/images/duplicate_nodes.png | Bin 0 -> 1057 bytes doc/salome/gui/SMESH/images/editgroup.png | Bin 36255 -> 38932 bytes doc/salome/gui/SMESH/images/elem_info.png | Bin 0 -> 907 bytes doc/salome/gui/SMESH/images/eleminfo1.png | Bin 14708 -> 21263 bytes doc/salome/gui/SMESH/images/eleminfo2.png | Bin 16004 -> 42074 bytes .../SMESH/images/extr_along_wire_after.png | Bin 0 -> 7221 bytes .../SMESH/images/extr_along_wire_before.png | Bin 0 -> 7120 bytes doc/salome/gui/SMESH/images/extrusion1.png | Bin 21458 -> 38296 bytes doc/salome/gui/SMESH/images/extrusion2.png | Bin 21556 -> 0 bytes .../gui/SMESH/images/extrusionalongaline1.png | Bin 14477 -> 25647 bytes .../gui/SMESH/images/extrusionalongaline2.png | Bin 14477 -> 28621 bytes doc/salome/gui/SMESH/images/findelement1.png | Bin 0 -> 24077 bytes doc/salome/gui/SMESH/images/findelement2.png | Bin 0 -> 14796 bytes doc/salome/gui/SMESH/images/findelement3.png | Bin 0 -> 918 bytes doc/salome/gui/SMESH/images/formula1.png | Bin 8611 -> 21412 bytes doc/salome/gui/SMESH/images/formula2.png | Bin 13997 -> 49955 bytes doc/salome/gui/SMESH/images/formula4.png | Bin 5266 -> 11944 bytes doc/salome/gui/SMESH/images/formula5.png | Bin 0 -> 13384 bytes doc/salome/gui/SMESH/images/free_nodes.png | Bin 8524 -> 13943 bytes .../images/ghs3d_parameters_advanced.png | Bin 30204 -> 0 bytes .../SMESH/images/ghs3d_parameters_basic.png | Bin 19585 -> 0 bytes doc/salome/gui/SMESH/images/head.png | Bin 0 -> 78545 bytes .../gui/SMESH/images/hyp_source_edges.png | Bin 0 -> 13394 bytes .../gui/SMESH/images/hyp_source_faces.png | Bin 0 -> 13297 bytes .../gui/SMESH/images/hypo_fixedpnt_dlg.png | Bin 0 -> 24062 bytes .../gui/SMESH/images/hypo_quad_params_1.png | Bin 0 -> 1029 bytes .../gui/SMESH/images/hypo_quad_params_2.png | Bin 0 -> 2115 bytes .../SMESH/images/hypo_quad_params_dialog.png | Bin 0 -> 21190 bytes .../gui/SMESH/images/hypo_quad_params_res.png | Bin 0 -> 2227 bytes .../SMESH/images/hypo_quad_params_res_2.png | Bin 0 -> 9391 bytes .../gui/SMESH/images/hypo_radquad_dlg.png | Bin 0 -> 37524 bytes doc/salome/gui/SMESH/images/hypo_sets.png | Bin 0 -> 7070 bytes doc/salome/gui/SMESH/images/image146.png | Bin 30825 -> 21981 bytes doc/salome/gui/SMESH/images/image152.png | Bin 18866 -> 21808 bytes doc/salome/gui/SMESH/images/image42.png | Bin 0 -> 857 bytes doc/salome/gui/SMESH/images/image43.png | Bin 0 -> 965 bytes doc/salome/gui/SMESH/images/image49.png | Bin 861 -> 988 bytes doc/salome/gui/SMESH/images/image79.jpg | Bin 49431 -> 57934 bytes doc/salome/gui/SMESH/images/image99.gif | Bin 10733 -> 15691 bytes doc/salome/gui/SMESH/images/image_octa12.png | Bin 0 -> 5486 bytes .../SMESH/images/max_element_length_2d.png | Bin 0 -> 18345 bytes .../SMESH/images/max_element_length_3d.png | Bin 0 -> 19127 bytes doc/salome/gui/SMESH/images/mergeelems.png | Bin 25706 -> 29443 bytes .../gui/SMESH/images/mergeelems_auto.png | Bin 0 -> 15116 bytes doc/salome/gui/SMESH/images/mergenodes.png | Bin 31378 -> 35421 bytes .../gui/SMESH/images/mergenodes_auto.png | Bin 0 -> 24225 bytes .../SMESH/images/mesh_evaluation_succeed.png | Bin 0 -> 22763 bytes doc/salome/gui/SMESH/images/mesh_fixedpnt.png | Bin 0 -> 14279 bytes .../gui/SMESH/images/mesh_order_123.png | Bin 0 -> 5488 bytes .../gui/SMESH/images/mesh_order_123_res.png | Bin 0 -> 41002 bytes .../gui/SMESH/images/mesh_order_213.png | Bin 0 -> 5801 bytes .../gui/SMESH/images/mesh_order_213_res.png | Bin 0 -> 44216 bytes .../gui/SMESH/images/mesh_order_321.png | Bin 0 -> 5948 bytes .../gui/SMESH/images/mesh_order_321_res.png | Bin 0 -> 46127 bytes .../SMESH/images/mesh_order_no_concurrent.png | Bin 0 -> 4435 bytes .../gui/SMESH/images/mesh_order_preview.png | Bin 0 -> 4849 bytes .../gui/SMESH/images/mesh_radquad_01.png | Bin 0 -> 20203 bytes .../gui/SMESH/images/mesh_radquad_02.png | Bin 0 -> 12970 bytes .../gui/SMESH/images/meshcomputationfail.png | Bin 19509 -> 30461 bytes .../gui/SMESH/images/meshcut_plugin.png | Bin 0 -> 32268 bytes doc/salome/gui/SMESH/images/meshtopass.png | Bin 19997 -> 17937 bytes doc/salome/gui/SMESH/images/min_distance.png | Bin 0 -> 22662 bytes .../gui/SMESH/images/min_distance_preview.png | Bin 0 -> 116380 bytes doc/salome/gui/SMESH/images/moving_nodes1.png | Bin 15776 -> 7760 bytes doc/salome/gui/SMESH/images/moving_nodes2.png | Bin 20232 -> 6900 bytes doc/salome/gui/SMESH/images/netgen2d.png | Bin 7772 -> 0 bytes .../gui/SMESH/images/netgen3d_simple.png | Bin 26853 -> 0 bytes .../SMESH/images/over_constrained_faces.png | Bin 0 -> 6599 bytes .../SMESH/images/over_constrained_volumes.png | Bin 0 -> 8184 bytes doc/salome/gui/SMESH/images/pattern2d.png | Bin 0 -> 578 bytes .../gui/SMESH/images/point_marker_widget1.png | Bin 0 -> 4405 bytes .../gui/SMESH/images/point_marker_widget2.png | Bin 0 -> 4322 bytes doc/salome/gui/SMESH/images/pref21.png | Bin 0 -> 85160 bytes doc/salome/gui/SMESH/images/pref22.png | Bin 0 -> 44149 bytes doc/salome/gui/SMESH/images/pref23.png | Bin 0 -> 31529 bytes doc/salome/gui/SMESH/images/pref24.png | Bin 0 -> 53003 bytes doc/salome/gui/SMESH/images/projection_1d.png | Bin 17865 -> 15597 bytes doc/salome/gui/SMESH/images/projection_2d.png | Bin 22576 -> 19002 bytes doc/salome/gui/SMESH/images/projection_3d.png | Bin 23518 -> 26694 bytes .../gui/SMESH/images/remove_nodes_icon.png | Bin 0 -> 720 bytes .../SMESH/images/remove_orphan_nodes_icon.png | Bin 0 -> 807 bytes .../gui/SMESH/images/removeorphannodes.png | Bin 0 -> 8075 bytes .../gui/SMESH/images/reorient_2d_face.png | Bin 0 -> 20186 bytes .../gui/SMESH/images/reorient_2d_point.png | Bin 0 -> 20814 bytes .../gui/SMESH/images/reorient_faces_face.png | Bin 0 -> 782 bytes doc/salome/gui/SMESH/images/revolution2.png | Bin 19487 -> 0 bytes doc/salome/gui/SMESH/images/rotation.png | Bin 17643 -> 30608 bytes .../gui/SMESH/images/scalar_bar_dlg.png | Bin 0 -> 22098 bytes doc/salome/gui/SMESH/images/scale01.png | Bin 0 -> 27468 bytes doc/salome/gui/SMESH/images/scale02.png | Bin 0 -> 30454 bytes doc/salome/gui/SMESH/images/scale03.png | Bin 0 -> 22827 bytes doc/salome/gui/SMESH/images/scale04.png | Bin 0 -> 20061 bytes doc/salome/gui/SMESH/images/scale06.png | Bin 0 -> 22679 bytes doc/salome/gui/SMESH/images/scale07.png | Bin 0 -> 20034 bytes doc/salome/gui/SMESH/images/scale09.png | Bin 0 -> 30799 bytes doc/salome/gui/SMESH/images/scaleinit01.png | Bin 0 -> 17713 bytes doc/salome/gui/SMESH/images/scaleinit02.png | Bin 0 -> 17482 bytes doc/salome/gui/SMESH/images/scaleres03.png | Bin 0 -> 6098 bytes doc/salome/gui/SMESH/images/scaleres04.png | Bin 0 -> 6096 bytes doc/salome/gui/SMESH/images/scaleres06.png | Bin 0 -> 10446 bytes doc/salome/gui/SMESH/images/scaleres07.png | Bin 0 -> 7782 bytes doc/salome/gui/SMESH/images/scaleres09.png | Bin 0 -> 9768 bytes .../gui/SMESH/images/split_into_tetra.png | Bin 0 -> 24205 bytes .../SMESH/images/split_into_tetra_icon.png | Bin 0 -> 923 bytes .../gui/SMESH/images/std_point_marker.png | Bin 0 -> 8630 bytes doc/salome/gui/SMESH/images/symmetry1.png | Bin 15073 -> 25845 bytes doc/salome/gui/SMESH/images/symmetry2.png | Bin 17111 -> 30194 bytes doc/salome/gui/SMESH/images/symmetry3.png | Bin 17096 -> 30070 bytes doc/salome/gui/SMESH/images/translation1.png | Bin 16412 -> 27969 bytes doc/salome/gui/SMESH/images/translation2.png | Bin 13573 -> 24628 bytes .../images/use_existing_face_sample_mesh.png | Bin 0 -> 14946 bytes .../gui/SMESH/images/using_notebook_smesh.png | Bin 0 -> 14950 bytes .../gui/SMESH/images/viscous_layers_hyp.png | Bin 0 -> 18704 bytes .../gui/SMESH/images/viscous_layers_mesh.png | Bin 0 -> 12533 bytes .../gui/SMESH/input/1d_meshing_hypo.doc | 59 +- .../gui/SMESH/input/2d_meshing_hypo.doc | 76 +- doc/salome/gui/SMESH/input/about_filters.doc | 31 + doc/salome/gui/SMESH/input/about_hypo.doc | 22 +- doc/salome/gui/SMESH/input/about_meshes.doc | 39 +- .../SMESH/input/about_quality_controls.doc | 45 +- .../SMESH/input/adding_nodes_and_elements.doc | 59 +- .../SMESH/input/adding_quadratic_elements.doc | 21 +- .../gui/SMESH/input/additional_hypo.doc | 50 +- doc/salome/gui/SMESH/input/area.doc | 3 +- doc/salome/gui/SMESH/input/aspect_ratio.doc | 17 +- .../gui/SMESH/input/aspect_ratio_3d.doc | 8 +- .../gui/SMESH/input/bare_border_face.doc | 15 + .../gui/SMESH/input/bare_border_volumes.doc | 15 + .../gui/SMESH/input/basic_meshing_algos.doc | 28 +- doc/salome/gui/SMESH/input/blsurf_hypo.doc | 222 - .../gui/SMESH/input/building_compounds.doc | 2 + doc/salome/gui/SMESH/input/cartesian_algo.doc | 75 + .../changing_orientation_of_elements.doc | 10 +- doc/salome/gui/SMESH/input/clipping.doc | 20 +- doc/salome/gui/SMESH/input/colors_size.doc | 37 + .../gui/SMESH/input/constructing_meshes.doc | 334 +- .../SMESH/input/constructing_submeshes.doc | 44 +- .../input/convert_to_from_quadratic_mesh.doc | 30 +- doc/salome/gui/SMESH/input/copy_mesh.doc | 56 + .../input/create_groups_from_geometry.doc | 17 + .../gui/SMESH/input/creating_groups.doc | 195 +- .../gui/SMESH/input/cut_mesh_by_plane.doc | 55 + .../gui/SMESH/input/deleting_groups.doc | 6 +- .../SMESH/input/double_elements_control.doc | 14 + .../gui/SMESH/input/double_nodes_control.doc | 16 + .../gui/SMESH/input/double_nodes_page.doc | 72 + doc/salome/gui/SMESH/input/editing_groups.doc | 32 +- doc/salome/gui/SMESH/input/extrusion.doc | 41 +- .../gui/SMESH/input/extrusion_along_path.doc | 77 +- .../gui/SMESH/input/find_element_by_point.doc | 41 + doc/salome/gui/SMESH/input/free_faces.doc | 17 +- doc/salome/gui/SMESH/input/free_nodes.doc | 14 +- .../SMESH/input/generate_flat_elements.doc | 12 + doc/salome/gui/SMESH/input/ghs3d_hypo.doc | 69 - doc/salome/gui/SMESH/input/ghs3dprl_hypo.doc | 392 - .../input/group_of_underlying_elements.doc | 34 + .../gui/SMESH/input/grouping_elements.doc | 47 +- .../input/importing_exporting_meshes.doc | 12 +- doc/salome/gui/SMESH/input/index.doc | 19 +- doc/salome/gui/SMESH/input/length_2d.doc | 4 +- .../gui/SMESH/input/make_2dmesh_from_3d.doc | 52 + .../gui/SMESH/input/max_element_length_2d.doc | 28 + .../gui/SMESH/input/max_element_length_3d.doc | 29 + doc/salome/gui/SMESH/input/measurements.doc | 70 + .../gui/SMESH/input/merging_elements.doc | 76 +- doc/salome/gui/SMESH/input/merging_nodes.doc | 42 +- doc/salome/gui/SMESH/input/mesh_infos.doc | 124 +- .../gui/SMESH/input/mesh_preferences.doc | 203 + .../gui/SMESH/input/mesh_through_point.doc | 35 +- doc/salome/gui/SMESH/input/minimum_angle.doc | 3 +- .../gui/SMESH/input/modifying_meshes.doc | 21 +- doc/salome/gui/SMESH/input/moving_nodes.doc | 36 - .../gui/SMESH/input/netgen_2d_3d_hypo.doc | 67 - .../SMESH/input/over_constrained_faces.doc | 19 + .../SMESH/input/over_constrained_volumes.doc | 18 + .../gui/SMESH/input/pattern_mapping.doc | 267 +- doc/salome/gui/SMESH/input/point_marker.doc | 53 + doc/salome/gui/SMESH/input/preview_meshes.doc | 36 - doc/salome/gui/SMESH/input/prism_3d_algo.doc | 12 +- .../gui/SMESH/input/projection_algos.doc | 54 +- .../input/radial_quadrangle_1D2D_algo.doc | 29 + .../input/removing_nodes_and_elements.doc | 66 +- doc/salome/gui/SMESH/input/reorient_faces.doc | 61 + doc/salome/gui/SMESH/input/revolution.doc | 52 +- doc/salome/gui/SMESH/input/rotation.doc | 77 +- doc/salome/gui/SMESH/input/scalar_bar.doc | 40 + doc/salome/gui/SMESH/input/scale.doc | 134 + .../SMESH/input/selection_filter_library.doc | 101 +- doc/salome/gui/SMESH/input/skew.doc | 3 +- .../gui/SMESH/input/smeshpy_interface.doc | 205 +- doc/salome/gui/SMESH/input/smoothing.doc | 67 +- doc/salome/gui/SMESH/input/split_to_tetra.doc | 55 + doc/salome/gui/SMESH/input/symmetry.doc | 87 +- doc/salome/gui/SMESH/input/taper.doc | 4 +- doc/salome/gui/SMESH/input/translation.doc | 77 +- .../gui/SMESH/input/tui_cartesian_algo.doc | 49 + .../gui/SMESH/input/tui_creating_meshes.doc | 117 +- .../SMESH/input/tui_defining_hypotheses.doc | 353 +- doc/salome/gui/SMESH/input/tui_filters.doc | 770 + .../input/tui_generate_flat_elements.doc | 82 + .../gui/SMESH/input/tui_grouping_elements.doc | 57 +- .../gui/SMESH/input/tui_measurements.doc | 84 + .../gui/SMESH/input/tui_modifying_meshes.doc | 201 +- .../gui/SMESH/input/tui_notebook_smesh.doc | 47 + .../gui/SMESH/input/tui_quality_controls.doc | 221 +- .../SMESH/input/tui_transforming_meshes.doc | 266 +- .../SMESH/input/tui_use_existing_faces.doc | 124 + .../gui/SMESH/input/tui_viewing_meshes.doc | 98 +- .../input/tui_work_on_objects_from_gui.doc | 27 + .../gui/SMESH/input/use_existing_algos.doc | 66 + .../SMESH/input/using_notebook_smesh_page.doc | 17 +- .../SMESH/input/viewing_meshes_overview.doc | 29 +- doc/salome/gui/SMESH/input/volume.doc | 4 +- doc/salome/gui/SMESH/input/warping.doc | 4 +- doc/salome/gui/SMESH/static/doxygen.css | 956 +- doc/salome/gui/SMESH/static/footer.html | 18 +- doc/salome/gui/SMESH/static/header.html | 12 - doc/salome/gui/SMESH/static/header.html.in | 20 + doc/salome/gui/SMESH/static/header_py.html.in | 21 + doc/salome/tui/Makefile.am | 52 +- doc/salome/tui/doxyfile.in | 80 +- doc/salome/tui/extra/AddNetgenInSalome2.pdf | Bin 70677 -> 0 bytes doc/salome/tui/extra/AddNetgenInSalome2.ps | 13935 ---------------- doc/salome/tui/extra/AddNetgenInSalome2.sxw | Bin 13363 -> 0 bytes doc/salome/tui/extra/PluginMeshers.html | 344 - doc/salome/tui/extra/PluginMeshers.txt | 188 - doc/salome/tui/images/head.png | Bin 0 -> 78545 bytes doc/salome/tui/images/smeshscreen.png | Bin 0 -> 61648 bytes doc/salome/tui/input/index.doc | 13 + doc/salome/tui/static/doxygen.css | 916 +- doc/salome/tui/static/footer.html | 19 +- doc/salome/tui/static/header.html.in | 20 + doc/salome/tui/static/myheader.html | 13 - idl/Makefile.am | 55 +- idl/SMESH_BasicHypothesis.idl | 414 +- idl/SMESH_Filter.idl | 138 +- idl/SMESH_Gen.idl | 192 +- idl/SMESH_Group.idl | 76 +- idl/SMESH_Hypothesis.idl | 48 +- idl/SMESH_Measurements.idl | 61 + idl/SMESH_Mesh.idl | 294 +- idl/SMESH_MeshEditor.idl | 723 +- idl/SMESH_Pattern.idl | 31 +- resources/Makefile.am | 76 +- resources/SMESHCatalog.xml.in | 4 +- resources/{SalomeApp.xml => SalomeApp.xml.in} | 61 +- resources/StdMeshers.xml | 238 +- resources/advanced_mesh_info.png | Bin 973 -> 988 bytes resources/bare_border_face.png | Bin 0 -> 3168 bytes resources/bare_border_volume.png | Bin 0 -> 3369 bytes resources/copy_mesh.png | Bin 0 -> 1117 bytes resources/mesh_2d_from_3d.png | Bin 0 -> 406 bytes resources/mesh_ball.png | Bin 0 -> 781 bytes resources/mesh_biquad_quadrangle.png | Bin 0 -> 673 bytes resources/mesh_bounding_box.png | Bin 0 -> 3449 bytes resources/mesh_duplicate_nodes.png | Bin 0 -> 914 bytes resources/mesh_duplicate_nodes_with_elem.png | Bin 0 -> 891 bytes resources/mesh_elem_info.png | Bin 0 -> 907 bytes resources/mesh_equal_edge.png | Bin 0 -> 456 bytes resources/mesh_equal_face.png | Bin 0 -> 600 bytes resources/mesh_equal_node.png | Bin 0 -> 430 bytes resources/mesh_equal_volume.png | Bin 0 -> 623 bytes resources/mesh_find_elem_by_point.png | Bin 0 -> 890 bytes resources/mesh_hypo_viscous_layers.png | Bin 0 -> 1239 bytes resources/mesh_max_element_length_2d.png | Bin 0 -> 857 bytes resources/mesh_max_element_length_3d.png | Bin 0 -> 965 bytes resources/mesh_min_dist.png | Bin 0 -> 3360 bytes resources/mesh_octahedron.png | Bin 0 -> 485 bytes resources/mesh_pentahedron.png | Bin 0 -> 936 bytes resources/mesh_quadrangle_quadpref.png | Bin 0 -> 368 bytes .../mesh_quadrangle_quadpref_reversed.png | Bin 0 -> 357 bytes resources/mesh_quadrangle_reduced.png | Bin 0 -> 459 bytes resources/mesh_quadrangle_standard.png | Bin 0 -> 308 bytes resources/mesh_quadrangle_triapref.png | Bin 0 -> 391 bytes resources/mesh_rem_orphan_nodes.png | Bin 0 -> 807 bytes resources/mesh_tree_algo_0D.png | Bin 0 -> 253 bytes resources/mesh_tree_algo_existing_2D.png | Bin 0 -> 270 bytes resources/mesh_tree_algo_prism.png | Bin 0 -> 314 bytes .../mesh_tree_algo_radial_quadrangle_1D2D.png | Bin 0 -> 338 bytes resources/mesh_tree_group_on_filter.png | Bin 0 -> 611 bytes resources/mesh_tree_hypo_viscous_layers.png | Bin 0 -> 250 bytes resources/mesh_triquad_hexahedron.png | Bin 0 -> 878 bytes resources/over_constrained_face.png | Bin 0 -> 3329 bytes resources/over_constrained_volume.png | Bin 0 -> 3444 bytes resources/reorient_faces_face.png | Bin 0 -> 782 bytes resources/reorient_faces_point.png | Bin 0 -> 839 bytes resources/scale.png | Bin 0 -> 885 bytes resources/scale_along_axes.png | Bin 0 -> 874 bytes resources/split_into_tetra.png | Bin 0 -> 923 bytes src/Controls/Makefile.am | 39 +- src/Controls/SMESHControls.cxx | 33 +- src/Controls/SMESH_Controls.cxx | 1566 +- src/Controls/SMESH_Controls.hxx | 45 - src/Controls/SMESH_ControlsDef.hxx | 351 +- src/Driver/Driver_Document.cxx | 31 +- src/Driver/Driver_Document.h | 31 +- src/Driver/Driver_Mesh.cxx | 69 +- src/Driver/Driver_Mesh.h | 39 +- src/Driver/Driver_SMDS_Mesh.cxx | 31 +- src/Driver/Driver_SMDS_Mesh.h | 31 +- src/Driver/Driver_SMESHDS_Mesh.cxx | 31 +- src/Driver/Driver_SMESHDS_Mesh.h | 31 +- src/Driver/Makefile.am | 32 +- src/DriverCGNS/DriverCGNS_Read.cxx | 1195 ++ src/DriverCGNS/DriverCGNS_Read.hxx | 59 + src/DriverCGNS/DriverCGNS_Write.cxx | 585 + src/DriverCGNS/DriverCGNS_Write.hxx | 53 + src/DriverCGNS/Makefile.am | 50 + src/DriverCGNS/SMESH_DriverCGNS.hxx | 40 + src/DriverDAT/DAT_Test.cxx | 31 +- src/DriverDAT/DriverDAT_R_SMDS_Mesh.cxx | 103 +- src/DriverDAT/DriverDAT_R_SMDS_Mesh.h | 31 +- .../DriverDAT_R_SMESHDS_Document.cxx | 22 - src/DriverDAT/DriverDAT_R_SMESHDS_Document.h | 32 - src/DriverDAT/DriverDAT_R_SMESHDS_Mesh.cxx | 22 - src/DriverDAT/DriverDAT_R_SMESHDS_Mesh.h | 32 - src/DriverDAT/DriverDAT_W_SMDS_Mesh.cxx | 38 +- src/DriverDAT/DriverDAT_W_SMDS_Mesh.h | 31 +- .../DriverDAT_W_SMESHDS_Document.cxx | 22 - src/DriverDAT/DriverDAT_W_SMESHDS_Document.h | 32 - src/DriverDAT/DriverDAT_W_SMESHDS_Mesh.cxx | 22 - src/DriverDAT/DriverDAT_W_SMESHDS_Mesh.h | 36 - src/DriverDAT/Makefile.am | 42 +- src/DriverDAT/SMESH_DriverDAT.hxx | 33 +- src/DriverMED/DriverMED_Family.cxx | 137 +- src/DriverMED/DriverMED_Family.h | 70 +- src/DriverMED/DriverMED_R_SMDS_Mesh.cxx | 22 - src/DriverMED/DriverMED_R_SMDS_Mesh.h | 32 - .../DriverMED_R_SMESHDS_Document.cxx | 22 - src/DriverMED/DriverMED_R_SMESHDS_Document.h | 32 - src/DriverMED/DriverMED_R_SMESHDS_Mesh.cxx | 725 +- src/DriverMED/DriverMED_R_SMESHDS_Mesh.h | 33 +- src/DriverMED/DriverMED_W_SMDS_Mesh.cxx | 22 - src/DriverMED/DriverMED_W_SMDS_Mesh.h | 32 - .../DriverMED_W_SMESHDS_Document.cxx | 26 - src/DriverMED/DriverMED_W_SMESHDS_Document.h | 32 - src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx | 400 +- src/DriverMED/DriverMED_W_SMESHDS_Mesh.h | 37 +- src/DriverMED/MED_Test.cxx | 31 +- src/DriverMED/Makefile.am | 50 +- src/DriverMED/SMESH_DriverMED.hxx | 33 +- src/DriverSTL/DriverSTL_R_SMDS_Mesh.cxx | 31 +- src/DriverSTL/DriverSTL_R_SMDS_Mesh.h | 31 +- src/DriverSTL/DriverSTL_W_SMDS_Mesh.cxx | 267 +- src/DriverSTL/DriverSTL_W_SMDS_Mesh.h | 40 +- src/DriverSTL/Makefile.am | 33 +- src/DriverSTL/SMESH_DriverSTL.hxx | 33 +- src/DriverSTL/STL_Test.cxx | 31 +- src/DriverUNV/DriverUNV_R_SMDS_Mesh.cxx | 655 +- src/DriverUNV/DriverUNV_R_SMDS_Mesh.h | 31 +- .../DriverUNV_R_SMESHDS_Document.cxx | 22 - src/DriverUNV/DriverUNV_R_SMESHDS_Document.h | 32 - src/DriverUNV/DriverUNV_R_SMESHDS_Mesh.cxx | 22 - src/DriverUNV/DriverUNV_R_SMESHDS_Mesh.h | 32 - src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx | 392 +- src/DriverUNV/DriverUNV_W_SMDS_Mesh.h | 31 +- .../DriverUNV_W_SMESHDS_Document.cxx | 22 - src/DriverUNV/DriverUNV_W_SMESHDS_Document.h | 32 - src/DriverUNV/DriverUNV_W_SMESHDS_Mesh.cxx | 22 - src/DriverUNV/DriverUNV_W_SMESHDS_Mesh.h | 32 - src/DriverUNV/Makefile.am | 46 +- src/DriverUNV/SMESH_DriverUNV.hxx | 33 +- src/DriverUNV/UNV164_Structure.cxx | 83 + src/DriverUNV/UNV164_Structure.hxx | 94 + src/DriverUNV/UNV2411_Structure.cxx | 79 +- src/DriverUNV/UNV2411_Structure.hxx | 41 +- src/DriverUNV/UNV2412_Structure.cxx | 263 +- src/DriverUNV/UNV2412_Structure.hxx | 43 +- src/DriverUNV/UNV2417_Structure.cxx | 80 +- src/DriverUNV/UNV2417_Structure.hxx | 35 +- src/DriverUNV/UNV2420_Structure.cxx | 152 + src/DriverUNV/UNV2420_Structure.hxx | 119 + src/DriverUNV/UNV_Test.cxx | 38 +- src/DriverUNV/UNV_Utilities.cxx | 37 +- src/DriverUNV/UNV_Utilities.hxx | 70 +- src/MEFISTO2/Makefile.am | 34 +- src/MEFISTO2/Rn.h | 38 +- src/MEFISTO2/aptrte.cxx | 196 +- src/MEFISTO2/aptrte.h | 153 +- src/MEFISTO2/areteideale.f | 27 +- src/MEFISTO2/trte.f | 28 +- src/Makefile.am | 47 +- src/OBJECT/Makefile.am | 57 +- src/OBJECT/SMESH_Actor.cxx | 1433 +- src/OBJECT/SMESH_Actor.h | 112 +- src/OBJECT/SMESH_ActorDef.h | 158 +- src/OBJECT/SMESH_ActorUtils.cxx | 171 +- src/OBJECT/SMESH_ActorUtils.h | 88 +- src/OBJECT/SMESH_CellLabelActor.cxx | 185 + src/OBJECT/SMESH_CellLabelActor.h | 82 + src/OBJECT/SMESH_DeviceActor.cxx | 310 +- src/OBJECT/SMESH_DeviceActor.h | 82 +- src/OBJECT/SMESH_ExtractGeometry.cxx | 70 +- src/OBJECT/SMESH_ExtractGeometry.h | 31 +- src/OBJECT/SMESH_FaceOrientationFilter.cxx | 54 +- src/OBJECT/SMESH_FaceOrientationFilter.h | 41 +- src/OBJECT/SMESH_NodeLabelActor.cxx | 187 + src/OBJECT/SMESH_NodeLabelActor.h | 78 + src/OBJECT/SMESH_Object.cxx | 530 +- src/OBJECT/SMESH_Object.h | 44 +- src/OBJECT/SMESH_ObjectDef.h | 57 +- src/OBJECT/SMESH_PreviewActorsCollection.cxx | 247 + src/OBJECT/SMESH_PreviewActorsCollection.h | 92 + src/OBJECT/SMESH_ScalarBarActor.cxx | 923 + src/OBJECT/SMESH_ScalarBarActor.h | 246 + src/PluginUtils/GeomSelectionTools.cxx | 342 + src/PluginUtils/GeomSelectionTools.h | 91 + src/PluginUtils/Makefile.am | 61 + src/SMDS/Makefile.am | 71 +- src/SMDS/Notes | 235 + src/SMDS/ObjectPool.hxx | 134 + src/SMDS/SMDSAbs_ElementType.hxx | 118 +- src/SMDS/SMDS_BallElement.cxx | 102 + src/SMDS/SMDS_BallElement.hxx | 60 + src/SMDS/SMDS_Downward.cxx | 2235 +++ src/SMDS/SMDS_Downward.hxx | 381 + src/SMDS/SMDS_EdgePosition.cxx | 56 +- src/SMDS/SMDS_EdgePosition.hxx | 42 +- src/SMDS/SMDS_ElemIterator.hxx | 39 +- src/SMDS/SMDS_FaceOfEdges.cxx | 129 +- src/SMDS/SMDS_FaceOfEdges.hxx | 70 +- src/SMDS/SMDS_FaceOfNodes.cxx | 107 +- src/SMDS/SMDS_FaceOfNodes.hxx | 54 +- src/SMDS/SMDS_FacePosition.cxx | 61 +- src/SMDS/SMDS_FacePosition.hxx | 49 +- src/SMDS/SMDS_Iterator.hxx | 53 +- src/SMDS/SMDS_IteratorOfElements.cxx | 129 +- src/SMDS/SMDS_IteratorOfElements.hxx | 57 +- src/SMDS/SMDS_IteratorOnIterators.hxx | 67 + src/SMDS/SMDS_LinearEdge.cxx | 162 + src/SMDS/SMDS_LinearEdge.hxx | 68 + src/SMDS/SMDS_MemoryLimit.cxx | 30 +- src/SMDS/SMDS_Mesh.cxx | 3424 ++-- src/SMDS/SMDS_Mesh.hxx | 753 +- src/SMDS/SMDS_Mesh0DElement.cxx | 147 + src/SMDS/SMDS_Mesh0DElement.hxx | 56 + src/SMDS/SMDS_MeshCell.cxx | 428 + src/SMDS/SMDS_MeshCell.hxx | 72 + src/SMDS/SMDS_MeshEdge.cxx | 163 +- src/SMDS/SMDS_MeshEdge.hxx | 69 +- src/SMDS/SMDS_MeshElement.cxx | 204 +- src/SMDS/SMDS_MeshElement.hxx | 139 +- src/SMDS/SMDS_MeshElementIDFactory.cxx | 172 +- src/SMDS/SMDS_MeshElementIDFactory.hxx | 54 +- src/SMDS/SMDS_MeshFace.cxx | 38 +- src/SMDS/SMDS_MeshFace.hxx | 38 +- src/SMDS/SMDS_MeshGroup.cxx | 107 +- src/SMDS/SMDS_MeshGroup.hxx | 65 +- src/SMDS/SMDS_MeshIDFactory.cxx | 79 +- src/SMDS/SMDS_MeshIDFactory.hxx | 50 +- src/SMDS/SMDS_MeshInfo.hxx | 281 +- src/SMDS/SMDS_MeshNode.cxx | 327 +- src/SMDS/SMDS_MeshNode.hxx | 106 +- src/SMDS/SMDS_MeshNodeIDFactory.cxx | 149 + src/SMDS/SMDS_MeshNodeIDFactory.hxx | 65 + src/SMDS/SMDS_MeshObject.cxx | 31 +- src/SMDS/SMDS_MeshObject.hxx | 31 +- src/SMDS/SMDS_MeshVolume.cxx | 37 +- src/SMDS/SMDS_MeshVolume.hxx | 40 +- src/SMDS/SMDS_PolygonalFaceOfNodes.cxx | 37 +- src/SMDS/SMDS_PolygonalFaceOfNodes.hxx | 34 +- src/SMDS/SMDS_PolyhedralVolumeOfNodes.cxx | 34 +- src/SMDS/SMDS_PolyhedralVolumeOfNodes.hxx | 34 +- src/SMDS/SMDS_Position.cxx | 55 +- src/SMDS/SMDS_Position.hxx | 48 +- src/SMDS/SMDS_QuadraticEdge.cxx | 38 +- src/SMDS/SMDS_QuadraticEdge.hxx | 37 +- src/SMDS/SMDS_QuadraticFaceOfNodes.cxx | 43 +- src/SMDS/SMDS_QuadraticFaceOfNodes.hxx | 34 +- src/SMDS/SMDS_QuadraticVolumeOfNodes.cxx | 52 +- src/SMDS/SMDS_QuadraticVolumeOfNodes.hxx | 34 +- src/SMDS/SMDS_SetIterator.hxx | 94 +- src/SMDS/SMDS_SpacePosition.cxx | 55 +- src/SMDS/SMDS_SpacePosition.hxx | 45 +- src/SMDS/SMDS_StdIterator.hxx | 80 + src/SMDS/SMDS_TypeOfPosition.hxx | 41 +- src/SMDS/SMDS_UnstructuredGrid.cxx | 1145 ++ src/SMDS/SMDS_UnstructuredGrid.hxx | 125 + src/SMDS/SMDS_VertexPosition.cxx | 51 +- src/SMDS/SMDS_VertexPosition.hxx | 38 +- src/SMDS/SMDS_VolumeOfFaces.cxx | 116 +- src/SMDS/SMDS_VolumeOfFaces.hxx | 56 +- src/SMDS/SMDS_VolumeOfNodes.cxx | 243 +- src/SMDS/SMDS_VolumeOfNodes.hxx | 111 +- src/SMDS/SMDS_VolumeTool.cxx | 1188 +- src/SMDS/SMDS_VolumeTool.hxx | 129 +- src/SMDS/SMDS_VtkCellIterator.cxx | 205 + src/SMDS/SMDS_VtkCellIterator.hxx | 72 + src/SMDS/SMDS_VtkEdge.cxx | 172 + src/SMDS/SMDS_VtkEdge.hxx | 59 + src/SMDS/SMDS_VtkFace.cxx | 301 + src/SMDS/SMDS_VtkFace.hxx | 62 + src/SMDS/SMDS_VtkVolume.cxx | 675 + src/SMDS/SMDS_VtkVolume.hxx | 79 + src/SMDS/SMESH_SMDS.hxx | 31 +- src/SMDS/chrono.cxx | 86 + src/SMDS/chrono.hxx | 75 + src/SMESH/Makefile.am | 62 +- src/SMESH/SMESH_0D_Algo.cxx | 31 +- src/SMESH/SMESH_0D_Algo.hxx | 31 +- src/SMESH/SMESH_1D_Algo.cxx | 31 +- src/SMESH/SMESH_1D_Algo.hxx | 31 +- src/SMESH/SMESH_2D_Algo.cxx | 31 +- src/SMESH/SMESH_2D_Algo.hxx | 31 +- src/SMESH/SMESH_3D_Algo.cxx | 31 +- src/SMESH/SMESH_3D_Algo.hxx | 31 +- src/SMESH/SMESH_Algo.cxx | 253 +- src/SMESH/SMESH_Algo.hxx | 111 +- src/SMESH/SMESH_Comment.hxx | 68 - ...MESH_DataMapOfElemPtrSequenceOfElemPtr.hxx | 54 - src/SMESH/SMESH_Gen.cxx | 624 +- src/SMESH/SMESH_Gen.hxx | 82 +- src/SMESH/SMESH_Group.cxx | 64 +- src/SMESH/SMESH_Group.hxx | 37 +- src/SMESH/SMESH_HypoFilter.cxx | 94 +- src/SMESH/SMESH_HypoFilter.hxx | 48 +- src/SMESH/SMESH_Hypothesis.cxx | 55 +- src/SMESH/SMESH_Hypothesis.hxx | 46 +- ...IndexedDataMapOfShapeIndexedMapOfShape.hxx | 55 - src/SMESH/SMESH_Mesh.cxx | 1009 +- src/SMESH/SMESH_Mesh.hxx | 198 +- src/SMESH/SMESH_MeshEditor.cxx | 8632 +++++++--- src/SMESH/SMESH_MeshEditor.hxx | 369 +- src/SMESH/SMESH_MesherHelper.cxx | 3594 +++- src/SMESH/SMESH_MesherHelper.hxx | 317 +- src/SMESH/SMESH_Octree.cxx | 183 - src/SMESH/SMESH_Octree.hxx | 99 - src/SMESH/SMESH_OctreeNode.cxx | 352 - src/SMESH/SMESH_OctreeNode.hxx | 129 - src/SMESH/SMESH_Pattern.cxx | 213 +- src/SMESH/SMESH_Pattern.hxx | 38 +- src/SMESH/SMESH_ProxyMesh.cxx | 545 + src/SMESH/SMESH_ProxyMesh.hxx | 173 + src/SMESH/SMESH_SMESH.hxx | 33 +- src/SMESH/SMESH_SequenceOfElemPtr.hxx | 40 - src/SMESH/SMESH_SequenceOfNode.hxx | 40 - src/SMESH/SMESH_subMesh.cxx | 897 +- src/SMESH/SMESH_subMesh.hxx | 125 +- src/SMESH/SMESH_subMeshEventListener.hxx | 69 +- src/SMESH/memoire.h | 43 + src/SMESHClient/Makefile.am | 43 +- src/SMESHClient/SMESHClientBin.cxx | 31 +- src/SMESHClient/SMESH_Client.cxx | 600 +- src/SMESHClient/SMESH_Client.hxx | 37 +- src/SMESHDS/Makefile.am | 38 +- src/SMESHDS/SMESHDS_Command.cxx | 440 +- src/SMESHDS/SMESHDS_Command.hxx | 121 +- src/SMESHDS/SMESHDS_CommandType.hxx | 41 +- src/SMESHDS/SMESHDS_DataMapOfShape.hxx | 31 +- src/SMESHDS/SMESHDS_Document.cxx | 100 +- src/SMESHDS/SMESHDS_Document.hxx | 73 +- src/SMESHDS/SMESHDS_Group.cxx | 46 +- src/SMESHDS/SMESHDS_Group.hxx | 37 +- src/SMESHDS/SMESHDS_GroupBase.cxx | 33 +- src/SMESHDS/SMESHDS_GroupBase.hxx | 42 +- src/SMESHDS/SMESHDS_GroupOnFilter.cxx | 190 + src/SMESHDS/SMESHDS_GroupOnFilter.hxx | 74 + src/SMESHDS/SMESHDS_GroupOnGeom.cxx | 58 +- src/SMESHDS/SMESHDS_GroupOnGeom.hxx | 38 +- src/SMESHDS/SMESHDS_Hypothesis.cxx | 62 +- src/SMESHDS/SMESHDS_Hypothesis.hxx | 34 +- src/SMESHDS/SMESHDS_Mesh.cxx | 1074 +- src/SMESHDS/SMESHDS_Mesh.hxx | 572 +- src/SMESHDS/SMESHDS_Script.cxx | 135 +- src/SMESHDS/SMESHDS_Script.hxx | 111 +- src/SMESHDS/SMESHDS_SubMesh.cxx | 378 +- src/SMESHDS/SMESHDS_SubMesh.hxx | 76 +- src/SMESHDS/SMESH_Controls.hxx | 79 + src/SMESHDS/SMESH_SMESHDS.hxx | 31 +- src/SMESHFiltersSelection/Makefile.am | 40 +- .../SMESH_LogicalFilter.cxx | 52 +- .../SMESH_LogicalFilter.hxx | 37 +- .../SMESH_NumberFilter.cxx | 31 +- .../SMESH_NumberFilter.hxx | 31 +- src/SMESHFiltersSelection/SMESH_Type.h | 43 +- .../SMESH_TypeFilter.cxx | 204 +- .../SMESH_TypeFilter.hxx | 31 +- src/SMESHGUI/Makefile.am | 109 +- src/SMESHGUI/SMESHGUI.cxx | 4487 +++-- src/SMESHGUI/SMESHGUI.h | 96 +- src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx | 437 +- src/SMESHGUI/SMESHGUI_AddMeshElementDlg.h | 46 +- .../SMESHGUI_AddQuadraticElementDlg.cxx | 712 +- .../SMESHGUI_AddQuadraticElementDlg.h | 66 +- src/SMESHGUI/SMESHGUI_BuildCompoundDlg.cxx | 146 +- src/SMESHGUI/SMESHGUI_BuildCompoundDlg.h | 36 +- src/SMESHGUI/SMESHGUI_ClippingDlg.cxx | 916 +- src/SMESHGUI/SMESHGUI_ClippingDlg.h | 147 +- src/SMESHGUI/SMESHGUI_ComputeDlg.cxx | 1135 +- src/SMESHGUI/SMESHGUI_ComputeDlg.h | 181 +- src/SMESHGUI/SMESHGUI_ConvToQuadDlg.cxx | 54 +- src/SMESHGUI/SMESHGUI_ConvToQuadDlg.h | 35 +- src/SMESHGUI/SMESHGUI_ConvToQuadOp.cxx | 174 +- src/SMESHGUI/SMESHGUI_ConvToQuadOp.h | 33 +- src/SMESHGUI/SMESHGUI_CopyMeshDlg.cxx | 697 + src/SMESHGUI/SMESHGUI_CopyMeshDlg.h | 130 + src/SMESHGUI/SMESHGUI_CreatePatternDlg.cxx | 85 +- src/SMESHGUI/SMESHGUI_CreatePatternDlg.h | 31 +- .../SMESHGUI_CreatePolyhedralVolumeDlg.cxx | 623 +- .../SMESHGUI_CreatePolyhedralVolumeDlg.h | 41 +- src/SMESHGUI/SMESHGUI_DeleteGroupDlg.cxx | 42 +- src/SMESHGUI/SMESHGUI_DeleteGroupDlg.h | 31 +- src/SMESHGUI/SMESHGUI_Dialog.cxx | 31 +- src/SMESHGUI/SMESHGUI_Dialog.h | 35 +- src/SMESHGUI/SMESHGUI_Displayer.cxx | 86 +- src/SMESHGUI/SMESHGUI_Displayer.h | 31 +- src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.cxx | 624 + src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.h | 120 + .../SMESHGUI_ExtrusionAlongPathDlg.cxx | 614 +- src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.h | 53 +- src/SMESHGUI/SMESHGUI_ExtrusionDlg.cxx | 674 +- src/SMESHGUI/SMESHGUI_ExtrusionDlg.h | 57 +- src/SMESHGUI/SMESHGUI_FileInfoDlg.cxx | 30 +- src/SMESHGUI/SMESHGUI_FileInfoDlg.h | 30 +- src/SMESHGUI/SMESHGUI_FileValidator.cxx | 74 + src/SMESHGUI/SMESHGUI_FileValidator.h | 46 + src/SMESHGUI/SMESHGUI_Filter.cxx | 69 +- src/SMESHGUI/SMESHGUI_Filter.h | 31 +- src/SMESHGUI/SMESHGUI_FilterDlg.cxx | 895 +- src/SMESHGUI/SMESHGUI_FilterDlg.h | 50 +- src/SMESHGUI/SMESHGUI_FilterLibraryDlg.cxx | 91 +- src/SMESHGUI/SMESHGUI_FilterLibraryDlg.h | 33 +- src/SMESHGUI/SMESHGUI_FilterUtils.cxx | 31 +- src/SMESHGUI/SMESHGUI_FilterUtils.h | 31 +- src/SMESHGUI/SMESHGUI_FindElemByPointDlg.cxx | 553 + src/SMESHGUI/SMESHGUI_FindElemByPointDlg.h | 120 + src/SMESHGUI/SMESHGUI_GEOMGenUtils.cxx | 44 +- src/SMESHGUI/SMESHGUI_GEOMGenUtils.h | 33 +- src/SMESHGUI/SMESHGUI_GroupDlg.cxx | 1510 +- src/SMESHGUI/SMESHGUI_GroupDlg.h | 72 +- src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.cxx | 59 +- src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.h | 30 +- src/SMESHGUI/SMESHGUI_GroupOpDlg.cxx | 115 +- src/SMESHGUI/SMESHGUI_GroupOpDlg.h | 36 +- src/SMESHGUI/SMESHGUI_GroupUtils.cxx | 37 +- src/SMESHGUI/SMESHGUI_GroupUtils.h | 35 +- src/SMESHGUI/SMESHGUI_Hypotheses.cxx | 418 +- src/SMESHGUI/SMESHGUI_Hypotheses.h | 108 +- src/SMESHGUI/SMESHGUI_HypothesesUtils.cxx | 557 +- src/SMESHGUI/SMESHGUI_HypothesesUtils.h | 53 +- src/SMESHGUI/SMESHGUI_IdValidator.h | 49 +- src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.cxx | 529 + src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.h | 121 + src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx | 317 +- src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h | 41 +- src/SMESHGUI/SMESHGUI_Measurements.cxx | 1231 ++ src/SMESHGUI/SMESHGUI_Measurements.h | 172 + ..._EditMeshDlg.cxx => SMESHGUI_MergeDlg.cxx} | 497 +- ...HGUI_EditMeshDlg.h => SMESHGUI_MergeDlg.h} | 64 +- src/SMESHGUI/SMESHGUI_MeshDlg.cxx | 77 +- src/SMESHGUI/SMESHGUI_MeshDlg.h | 34 +- src/SMESHGUI/SMESHGUI_MeshEditPreview.cxx | 54 +- src/SMESHGUI/SMESHGUI_MeshEditPreview.h | 31 +- src/SMESHGUI/SMESHGUI_MeshInfo.cxx | 2175 +++ src/SMESHGUI/SMESHGUI_MeshInfo.h | 305 + src/SMESHGUI/SMESHGUI_MeshInfosBox.cxx | 475 + src/SMESHGUI/SMESHGUI_MeshInfosBox.h | 88 + src/SMESHGUI/SMESHGUI_MeshInfosDlg.cxx | 645 - src/SMESHGUI/SMESHGUI_MeshInfosDlg.h | 131 - src/SMESHGUI/SMESHGUI_MeshOp.cxx | 614 +- src/SMESHGUI/SMESHGUI_MeshOp.h | 46 +- src/SMESHGUI/SMESHGUI_MeshOrderDlg.cxx | 330 + src/SMESHGUI/SMESHGUI_MeshOrderDlg.h | 112 + src/SMESHGUI/SMESHGUI_MeshOrderOp.cxx | 325 + src/SMESHGUI/SMESHGUI_MeshOrderOp.h | 93 + src/SMESHGUI/SMESHGUI_MeshPatternDlg.cxx | 147 +- src/SMESHGUI/SMESHGUI_MeshPatternDlg.h | 31 +- src/SMESHGUI/SMESHGUI_MeshUtils.cxx | 110 +- src/SMESHGUI/SMESHGUI_MeshUtils.h | 41 +- src/SMESHGUI/SMESHGUI_MoveNodesDlg.cxx | 660 - src/SMESHGUI/SMESHGUI_MoveNodesDlg.h | 105 - src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx | 269 +- src/SMESHGUI/SMESHGUI_MultiEditDlg.h | 66 +- src/SMESHGUI/SMESHGUI_NodesDlg.cxx | 204 +- src/SMESHGUI/SMESHGUI_NodesDlg.h | 42 +- src/SMESHGUI/SMESHGUI_Operation.cxx | 97 +- src/SMESHGUI/SMESHGUI_Operation.h | 35 +- src/SMESHGUI/SMESHGUI_PatternUtils.cxx | 31 +- src/SMESHGUI/SMESHGUI_PatternUtils.h | 31 +- src/SMESHGUI/SMESHGUI_PatternWidget.cxx | 35 +- src/SMESHGUI/SMESHGUI_PatternWidget.h | 35 +- .../SMESHGUI_Preferences_ColorDlg.cxx | 428 +- src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.h | 82 +- .../SMESHGUI_Preferences_ScalarBarDlg.cxx | 349 +- .../SMESHGUI_Preferences_ScalarBarDlg.h | 64 +- src/SMESHGUI/SMESHGUI_PreviewDlg.cxx | 125 + src/SMESHGUI/SMESHGUI_PreviewDlg.h | 67 + src/SMESHGUI/SMESHGUI_RemoveElementsDlg.cxx | 150 +- src/SMESHGUI/SMESHGUI_RemoveElementsDlg.h | 32 +- src/SMESHGUI/SMESHGUI_RemoveNodesDlg.cxx | 153 +- src/SMESHGUI/SMESHGUI_RemoveNodesDlg.h | 32 +- src/SMESHGUI/SMESHGUI_RenumberingDlg.cxx | 84 +- src/SMESHGUI/SMESHGUI_RenumberingDlg.h | 31 +- src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx | 852 + src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h | 118 + src/SMESHGUI/SMESHGUI_RevolutionDlg.cxx | 318 +- src/SMESHGUI/SMESHGUI_RevolutionDlg.h | 51 +- src/SMESHGUI/SMESHGUI_RotationDlg.cxx | 246 +- src/SMESHGUI/SMESHGUI_RotationDlg.h | 41 +- src/SMESHGUI/SMESHGUI_ScaleDlg.cxx | 1136 ++ src/SMESHGUI/SMESHGUI_ScaleDlg.h | 150 + src/SMESHGUI/SMESHGUI_Selection.cxx | 242 +- src/SMESHGUI/SMESHGUI_Selection.h | 40 +- src/SMESHGUI/SMESHGUI_SelectionOp.cxx | 37 +- src/SMESHGUI/SMESHGUI_SelectionOp.h | 37 +- src/SMESHGUI/SMESHGUI_SewingDlg.cxx | 77 +- src/SMESHGUI/SMESHGUI_SewingDlg.h | 31 +- src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.cxx | 289 +- src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.h | 31 +- src/SMESHGUI/SMESHGUI_SingleEditDlg.cxx | 98 +- src/SMESHGUI/SMESHGUI_SingleEditDlg.h | 31 +- src/SMESHGUI/SMESHGUI_SmoothingDlg.cxx | 264 +- src/SMESHGUI/SMESHGUI_SmoothingDlg.h | 39 +- src/SMESHGUI/SMESHGUI_SpinBox.cxx | 69 +- src/SMESHGUI/SMESHGUI_SpinBox.h | 38 +- .../SMESHGUI_StandardMeshInfosDlg.cxx | 472 - src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.h | 89 - src/SMESHGUI/SMESHGUI_SymmetryDlg.cxx | 262 +- src/SMESHGUI/SMESHGUI_SymmetryDlg.h | 45 +- src/SMESHGUI/SMESHGUI_TranslationDlg.cxx | 317 +- src/SMESHGUI/SMESHGUI_TranslationDlg.h | 42 +- src/SMESHGUI/SMESHGUI_TransparencyDlg.cxx | 89 +- src/SMESHGUI/SMESHGUI_TransparencyDlg.h | 31 +- src/SMESHGUI/SMESHGUI_Utils.cxx | 160 +- src/SMESHGUI/SMESHGUI_Utils.h | 44 +- src/SMESHGUI/SMESHGUI_VTKUtils.cxx | 804 +- src/SMESHGUI/SMESHGUI_VTKUtils.h | 77 +- src/SMESHGUI/SMESHGUI_WhatIsDlg.cxx | 589 - src/SMESHGUI/SMESHGUI_WhatIsDlg.h | 105 - src/SMESHGUI/SMESHGUI_XmlHandler.cxx | 47 +- src/SMESHGUI/SMESHGUI_XmlHandler.h | 40 +- src/SMESHGUI/SMESH_SMESHGUI.hxx | 33 +- src/SMESHGUI/SMESH_images.ts | 164 +- src/SMESHGUI/SMESH_msg_en.ts | 11619 +++++++------ src/SMESHGUI/SMESH_msg_fr.ts | 6968 ++++++++ src/SMESHUtils/Makefile.am | 57 + src/{SMESH => SMESHUtils}/SMESH_Block.cxx | 105 +- src/{SMESH => SMESHUtils}/SMESH_Block.hxx | 44 +- src/SMESHUtils/SMESH_Comment.hxx | 76 + .../SMESH_ComputeError.hxx | 42 +- src/SMESHUtils/SMESH_File.cxx | 240 + src/SMESHUtils/SMESH_File.hxx | 95 + src/SMESHUtils/SMESH_Octree.cxx | 211 + src/SMESHUtils/SMESH_Octree.hxx | 127 + src/SMESHUtils/SMESH_OctreeNode.cxx | 437 + src/SMESHUtils/SMESH_OctreeNode.hxx | 137 + src/SMESHUtils/SMESH_TypeDefs.hxx | 184 + src/SMESHUtils/SMESH_Utils.hxx | 40 + src/SMESH_I/Makefile.am | 51 +- src/SMESH_I/SMESH.hxx | 33 +- src/SMESH_I/SMESHEngine.cxx | 31 +- src/SMESH_I/SMESH_0D_Algo_i.cxx | 31 +- src/SMESH_I/SMESH_0D_Algo_i.hxx | 31 +- src/SMESH_I/SMESH_1D_Algo_i.cxx | 31 +- src/SMESH_I/SMESH_1D_Algo_i.hxx | 31 +- src/SMESH_I/SMESH_2D_Algo_i.cxx | 31 +- src/SMESH_I/SMESH_2D_Algo_i.hxx | 31 +- src/SMESH_I/SMESH_2smeshpy.cxx | 3095 +++- src/SMESH_I/SMESH_2smeshpy.hxx | 438 +- src/SMESH_I/SMESH_3D_Algo_i.cxx | 31 +- src/SMESH_I/SMESH_3D_Algo_i.hxx | 31 +- src/SMESH_I/SMESH_Algo_i.cxx | 31 +- src/SMESH_I/SMESH_Algo_i.hxx | 31 +- src/SMESH_I/SMESH_DumpPython.cxx | 534 +- src/SMESH_I/SMESH_Filter_i.cxx | 865 +- src/SMESH_I/SMESH_Filter_i.hxx | 344 +- src/SMESH_I/SMESH_Gen_i.cxx | 3581 ++-- src/SMESH_I/SMESH_Gen_i.hxx | 248 +- src/SMESH_I/SMESH_Gen_i_1.cxx | 317 +- src/SMESH_I/SMESH_Group_i.cxx | 529 +- src/SMESH_I/SMESH_Group_i.hxx | 119 +- src/SMESH_I/SMESH_Hypothesis_i.cxx | 178 +- src/SMESH_I/SMESH_Hypothesis_i.hxx | 67 +- src/SMESH_I/SMESH_MEDFamily_i.cxx | 93 +- src/SMESH_I/SMESH_MEDFamily_i.hxx | 41 +- src/SMESH_I/SMESH_MEDMesh_i.cxx | 1331 +- src/SMESH_I/SMESH_MEDMesh_i.hxx | 103 +- src/SMESH_I/SMESH_MEDSupport_i.cxx | 340 +- src/SMESH_I/SMESH_MEDSupport_i.hxx | 111 +- src/SMESH_I/SMESH_Measurements_i.cxx | 259 + src/SMESH_I/SMESH_Measurements_i.hxx | 63 + src/SMESH_I/SMESH_MeshEditor_i.cxx | 4495 +++-- src/SMESH_I/SMESH_MeshEditor_i.hxx | 567 +- src/SMESH_I/SMESH_Mesh_i.cxx | 2660 ++- src/SMESH_I/SMESH_Mesh_i.hxx | 319 +- src/SMESH_I/SMESH_NoteBook.cxx | 600 +- src/SMESH_I/SMESH_NoteBook.hxx | 25 +- src/SMESH_I/SMESH_Pattern_i.cxx | 59 +- src/SMESH_I/SMESH_Pattern_i.hxx | 31 +- src/SMESH_I/SMESH_PreMeshInfo.cxx | 1278 ++ src/SMESH_I/SMESH_PreMeshInfo.hxx | 125 + src/SMESH_I/SMESH_PythonDump.hxx | 149 +- src/SMESH_I/SMESH_subMesh_i.cxx | 183 +- src/SMESH_I/SMESH_subMesh_i.hxx | 83 +- src/SMESH_I/smeshpy.py | 32 +- src/SMESH_PY/Makefile.am | 25 + src/SMESH_PY/__init__.py | 19 + src/SMESH_PY/smeshstudytools.py | 214 + src/SMESH_SWIG/Makefile.am | 31 +- src/SMESH_SWIG/PAL_MESH_041_mesh.py | 34 +- src/SMESH_SWIG/PAL_MESH_043_2D.py | 33 +- src/SMESH_SWIG/PAL_MESH_043_3D.py | 33 +- src/SMESH_SWIG/SMESH_AdvancedEditor.py | 38 +- src/SMESH_SWIG/SMESH_BelongToGeom.py | 33 +- src/SMESH_SWIG/SMESH_BuildCompound.py | 36 +- src/SMESH_SWIG/SMESH_GroupFromGeom.py | 32 +- src/SMESH_SWIG/SMESH_GroupFromGeom2.py | 32 +- src/SMESH_SWIG/SMESH_GroupLyingOnGeom.py | 36 +- src/SMESH_SWIG/SMESH_Nut.py | 38 +- src/SMESH_SWIG/SMESH_Partition1_tetra.py | 35 +- src/SMESH_SWIG/SMESH_Sphere.py | 37 +- src/SMESH_SWIG/SMESH_blocks.py | 33 +- src/SMESH_SWIG/SMESH_box.py | 33 +- src/SMESH_SWIG/SMESH_box2_tetra.py | 33 +- src/SMESH_SWIG/SMESH_box3_tetra.py | 34 +- src/SMESH_SWIG/SMESH_box_tetra.py | 33 +- src/SMESH_SWIG/SMESH_controls.py | 44 +- src/SMESH_SWIG/SMESH_demo_hexa2_upd.py | 33 +- src/SMESH_SWIG/SMESH_fixation.py | 32 +- src/SMESH_SWIG/SMESH_fixation_hexa.py | 33 +- src/SMESH_SWIG/SMESH_fixation_netgen.py | 33 +- src/SMESH_SWIG/SMESH_fixation_tetra.py | 33 +- src/SMESH_SWIG/SMESH_flight_skin.py | 33 +- src/SMESH_SWIG/SMESH_freebord.py | 33 +- src/SMESH_SWIG/SMESH_hexaedre.py | 33 +- src/SMESH_SWIG/SMESH_mechanic.py | 41 +- src/SMESH_SWIG/SMESH_mechanic_editor.py | 46 +- src/SMESH_SWIG/SMESH_mechanic_netgen.py | 33 +- src/SMESH_SWIG/SMESH_mechanic_tetra.py | 33 +- src/SMESH_SWIG/SMESH_reg.py | 34 +- src/SMESH_SWIG/SMESH_shared_modules.py | 32 +- src/SMESH_SWIG/SMESH_test.py | 39 +- src/SMESH_SWIG/SMESH_test0.py | 32 +- src/SMESH_SWIG/SMESH_test1.py | 35 +- src/SMESH_SWIG/SMESH_test1_AndDisplay.py | 34 +- src/SMESH_SWIG/SMESH_test2.py | 32 +- src/SMESH_SWIG/SMESH_test3.py | 32 +- src/SMESH_SWIG/SMESH_test4.py | 33 +- src/SMESH_SWIG/SMESH_test5.py | 34 +- src/SMESH_SWIG/StdMeshersDC.py | 1126 ++ src/SMESH_SWIG/batchmode_mefisto.py | 32 +- src/SMESH_SWIG/batchmode_smesh.py | 33 +- src/SMESH_SWIG/ex00_all.py | 32 +- src/SMESH_SWIG/ex01_cube2build.py | 34 +- src/SMESH_SWIG/ex02_cube2primitive.py | 34 +- src/SMESH_SWIG/ex03_cube2partition.py | 34 +- src/SMESH_SWIG/ex04_cube5tetraHexa.py | 34 +- src/SMESH_SWIG/ex05_hole1build.py | 34 +- src/SMESH_SWIG/ex06_hole1boolean.py | 36 +- src/SMESH_SWIG/ex07_hole1partition.py | 36 +- src/SMESH_SWIG/ex08_hole2build.py | 34 +- src/SMESH_SWIG/ex09_grid4build.py | 34 +- src/SMESH_SWIG/ex10_grid4geometry.py | 34 +- src/SMESH_SWIG/ex11_grid3partition.py | 36 +- src/SMESH_SWIG/ex12_grid17partition.py | 34 +- src/SMESH_SWIG/ex13_hole1partial.py | 36 +- src/SMESH_SWIG/ex14_cyl1holed.py | 34 +- src/SMESH_SWIG/ex15_cyl2geometry.py | 36 +- src/SMESH_SWIG/ex16_cyl2complementary.py | 35 +- src/SMESH_SWIG/ex17_dome1.py | 34 +- src/SMESH_SWIG/ex18_dome2.py | 34 +- src/SMESH_SWIG/ex19_sphereINcube.py | 67 +- src/SMESH_SWIG/ex21_lamp.py | 34 +- src/SMESH_SWIG/ex24_cylinder.py | 35 +- src/SMESH_SWIG/ex29_refine.py | 31 +- src/SMESH_SWIG/ex30_groupsOp.py | 165 +- src/SMESH_SWIG/ex30_tepal.py | 25 +- src/SMESH_SWIG/ex31_dimGroup.py | 114 +- src/SMESH_SWIG/smesh.py | 58 +- src/SMESH_SWIG/smeshDC.py | 3987 ++--- src/SMESH_SWIG_WITHIHM/Makefile.am | 34 +- src/SMESH_SWIG_WITHIHM/libSMESH_Swig.cxx | 272 +- src/SMESH_SWIG_WITHIHM/libSMESH_Swig.h | 32 +- src/SMESH_SWIG_WITHIHM/libSMESH_Swig.i | 30 +- src/StdMeshers/Makefile.am | 72 +- src/StdMeshers/SMESH_StdMeshers.hxx | 33 +- src/StdMeshers/StdMeshers_Arithmetic1D.cxx | 73 +- src/StdMeshers/StdMeshers_Arithmetic1D.hxx | 45 +- src/StdMeshers/StdMeshers_AutomaticLength.cxx | 37 +- src/StdMeshers/StdMeshers_AutomaticLength.hxx | 33 +- .../StdMeshers_CartesianParameters3D.cxx | 453 + .../StdMeshers_CartesianParameters3D.hxx | 145 + src/StdMeshers/StdMeshers_Cartesian_3D.cxx | 2185 +++ src/StdMeshers/StdMeshers_Cartesian_3D.hxx | 64 + .../StdMeshers_CompositeHexa_3D.cxx | 386 +- .../StdMeshers_CompositeHexa_3D.hxx | 53 +- .../StdMeshers_CompositeSegment_1D.cxx | 134 +- .../StdMeshers_CompositeSegment_1D.hxx | 39 +- src/StdMeshers/StdMeshers_Deflection1D.cxx | 33 +- src/StdMeshers/StdMeshers_Deflection1D.hxx | 32 +- src/StdMeshers/StdMeshers_Distribution.cxx | 37 +- src/StdMeshers/StdMeshers_Distribution.hxx | 43 +- src/StdMeshers/StdMeshers_FaceSide.cxx | 469 +- src/StdMeshers/StdMeshers_FaceSide.hxx | 116 +- src/StdMeshers/StdMeshers_FixedPoints1D.cxx | 244 + src/StdMeshers/StdMeshers_FixedPoints1D.hxx | 88 + src/StdMeshers/StdMeshers_HexaFromSkin_3D.cxx | 1303 ++ src/StdMeshers/StdMeshers_HexaFromSkin_3D.hxx | 54 + src/StdMeshers/StdMeshers_Hexa_3D.cxx | 1471 +- src/StdMeshers/StdMeshers_Hexa_3D.hxx | 105 +- src/StdMeshers/StdMeshers_ImportSource.cxx | 490 + src/StdMeshers/StdMeshers_ImportSource.hxx | 99 + src/StdMeshers/StdMeshers_Import_1D.cxx | 1055 ++ src/StdMeshers/StdMeshers_Import_1D.hxx | 81 + src/StdMeshers/StdMeshers_Import_1D2D.cxx | 778 + src/StdMeshers/StdMeshers_Import_1D2D.hxx | 60 + .../StdMeshers_LayerDistribution.cxx | 31 +- .../StdMeshers_LayerDistribution.hxx | 31 +- .../StdMeshers_LayerDistribution2D.cxx | 59 + .../StdMeshers_LayerDistribution2D.hxx | 53 + src/StdMeshers/StdMeshers_LengthFromEdges.cxx | 31 +- src/StdMeshers/StdMeshers_LengthFromEdges.hxx | 32 +- src/StdMeshers/StdMeshers_LocalLength.cxx | 36 +- src/StdMeshers/StdMeshers_LocalLength.hxx | 32 +- src/StdMeshers/StdMeshers_MEFISTO_2D.cxx | 173 +- src/StdMeshers/StdMeshers_MEFISTO_2D.hxx | 50 +- src/StdMeshers/StdMeshers_MaxElementArea.cxx | 34 +- src/StdMeshers/StdMeshers_MaxElementArea.hxx | 31 +- .../StdMeshers_MaxElementVolume.cxx | 33 +- .../StdMeshers_MaxElementVolume.hxx | 33 +- src/StdMeshers/StdMeshers_MaxLength.cxx | 41 +- src/StdMeshers/StdMeshers_MaxLength.hxx | 37 +- .../StdMeshers_NotConformAllowed.cxx | 31 +- .../StdMeshers_NotConformAllowed.hxx | 31 +- src/StdMeshers/StdMeshers_NumberOfLayers.cxx | 33 +- src/StdMeshers/StdMeshers_NumberOfLayers.hxx | 31 +- .../StdMeshers_NumberOfLayers2D.cxx | 60 + .../StdMeshers_NumberOfLayers2D.hxx | 52 + .../StdMeshers_NumberOfSegments.cxx | 142 +- .../StdMeshers_NumberOfSegments.hxx | 49 +- src/StdMeshers/StdMeshers_Penta_3D.cxx | 612 +- src/StdMeshers/StdMeshers_Penta_3D.hxx | 67 +- src/StdMeshers/StdMeshers_Prism_3D.cxx | 1022 +- src/StdMeshers/StdMeshers_Prism_3D.hxx | 112 +- .../StdMeshers_ProjectionSource1D.cxx | 36 +- .../StdMeshers_ProjectionSource1D.hxx | 31 +- .../StdMeshers_ProjectionSource2D.cxx | 63 +- .../StdMeshers_ProjectionSource2D.hxx | 33 +- .../StdMeshers_ProjectionSource3D.cxx | 38 +- .../StdMeshers_ProjectionSource3D.hxx | 33 +- src/StdMeshers/StdMeshers_ProjectionUtils.cxx | 1000 +- src/StdMeshers/StdMeshers_ProjectionUtils.hxx | 108 +- src/StdMeshers/StdMeshers_Projection_1D.cxx | 156 +- src/StdMeshers/StdMeshers_Projection_1D.hxx | 34 +- src/StdMeshers/StdMeshers_Projection_1D2D.cxx | 264 + src/StdMeshers/StdMeshers_Projection_1D2D.hxx | 52 + src/StdMeshers/StdMeshers_Projection_2D.cxx | 947 +- src/StdMeshers/StdMeshers_Projection_2D.hxx | 34 +- src/StdMeshers/StdMeshers_Projection_3D.cxx | 190 +- src/StdMeshers/StdMeshers_Projection_3D.hxx | 34 +- src/StdMeshers/StdMeshers_Propagation.cxx | 49 +- src/StdMeshers/StdMeshers_Propagation.hxx | 33 +- .../StdMeshers_QuadToTriaAdaptor.cxx | 1759 +- .../StdMeshers_QuadToTriaAdaptor.hxx | 95 +- .../StdMeshers_QuadrangleParams.cxx | 175 + .../StdMeshers_QuadrangleParams.hxx | 86 + .../StdMeshers_QuadranglePreference.cxx | 56 +- .../StdMeshers_QuadranglePreference.hxx | 35 +- src/StdMeshers/StdMeshers_Quadrangle_2D.cxx | 2597 ++- src/StdMeshers/StdMeshers_Quadrangle_2D.hxx | 96 +- src/StdMeshers/StdMeshers_QuadraticMesh.cxx | 33 +- src/StdMeshers/StdMeshers_QuadraticMesh.hxx | 33 +- src/StdMeshers/StdMeshers_RadialPrism_3D.cxx | 216 +- src/StdMeshers/StdMeshers_RadialPrism_3D.hxx | 34 +- .../StdMeshers_RadialQuadrangle_1D2D.cxx | 1285 ++ .../StdMeshers_RadialQuadrangle_1D2D.hxx | 76 + src/StdMeshers/StdMeshers_Regular_1D.cxx | 335 +- src/StdMeshers/StdMeshers_Regular_1D.hxx | 45 +- .../StdMeshers_SegmentAroundVertex_0D.cxx | 48 +- .../StdMeshers_SegmentAroundVertex_0D.hxx | 33 +- .../StdMeshers_SegmentLengthAroundVertex.cxx | 34 +- .../StdMeshers_SegmentLengthAroundVertex.hxx | 33 +- src/StdMeshers/StdMeshers_StartEndLength.cxx | 76 +- src/StdMeshers/StdMeshers_StartEndLength.hxx | 45 +- .../StdMeshers_TrianglePreference.cxx | 128 - .../StdMeshers_TrianglePreference.hxx | 68 - .../StdMeshers_UseExisting_1D2D.cxx | 67 +- .../StdMeshers_UseExisting_1D2D.hxx | 34 +- src/StdMeshers/StdMeshers_ViscousLayers.cxx | 4483 +++++ src/StdMeshers/StdMeshers_ViscousLayers.hxx | 94 + src/StdMeshersGUI/Makefile.am | 59 +- src/StdMeshersGUI/SMESH_StdMeshersGUI.hxx | 33 +- src/StdMeshersGUI/StdMeshersGUI.cxx | 34 +- .../StdMeshersGUI_CartesianParamCreator.cxx | 698 + .../StdMeshersGUI_CartesianParamCreator.h | 142 + .../StdMeshersGUI_DistrPreview.cxx | 86 +- .../StdMeshersGUI_DistrPreview.h | 31 +- .../StdMeshersGUI_DistrTable.cxx | 137 +- src/StdMeshersGUI/StdMeshersGUI_DistrTable.h | 31 +- .../StdMeshersGUI_FixedPointsParamWdg.cxx | 394 + .../StdMeshersGUI_FixedPointsParamWdg.h | 92 + ...tdMeshersGUI_LayerDistributionParamWdg.cxx | 54 +- .../StdMeshersGUI_LayerDistributionParamWdg.h | 34 +- .../StdMeshersGUI_NbSegmentsCreator.cxx | 150 +- .../StdMeshersGUI_NbSegmentsCreator.h | 37 +- .../StdMeshersGUI_ObjectReferenceParamWdg.cxx | 133 +- .../StdMeshersGUI_ObjectReferenceParamWdg.h | 63 +- .../StdMeshersGUI_QuadrangleParamWdg.cxx | 100 + .../StdMeshersGUI_QuadrangleParamWdg.h | 49 + .../StdMeshersGUI_StdHypothesisCreator.cxx | 757 +- .../StdMeshersGUI_StdHypothesisCreator.h | 33 +- .../StdMeshersGUI_SubShapeSelectorWdg.cxx | 610 + .../StdMeshersGUI_SubShapeSelectorWdg.h | 136 + src/StdMeshersGUI/StdMeshers_images.ts | 99 +- src/StdMeshersGUI/StdMeshers_msg_en.ts | 812 +- src/StdMeshersGUI/StdMeshers_msg_fr.ts | 484 + src/StdMeshers_I/Makefile.am | 68 +- src/StdMeshers_I/SMESH_StdMeshers_I.hxx | 33 +- .../StdMeshers_Arithmetic1D_i.cxx | 180 +- .../StdMeshers_Arithmetic1D_i.hxx | 59 +- .../StdMeshers_AutomaticLength_i.cxx | 33 +- .../StdMeshers_AutomaticLength_i.hxx | 31 +- .../StdMeshers_CartesianParameters3D_i.cxx | 293 + .../StdMeshers_CartesianParameters3D_i.hxx | 116 + .../StdMeshers_Cartesian_3D_i.cxx | 79 + .../StdMeshers_Cartesian_3D_i.hxx | 58 + .../StdMeshers_CompositeSegment_1D_i.cxx | 31 +- .../StdMeshers_CompositeSegment_1D_i.hxx | 31 +- .../StdMeshers_Deflection1D_i.cxx | 55 +- .../StdMeshers_Deflection1D_i.hxx | 36 +- .../StdMeshers_FixedPoints1D_i.cxx | 288 + .../StdMeshers_FixedPoints1D_i.hxx | 85 + src/StdMeshers_I/StdMeshers_Hexa_3D_i.cxx | 39 +- src/StdMeshers_I/StdMeshers_Hexa_3D_i.hxx | 33 +- .../StdMeshers_ImportSource1D_i.cxx | 280 + .../StdMeshers_ImportSource1D_i.hxx | 74 + .../StdMeshers_ImportSource2D_i.cxx | 280 + .../StdMeshers_ImportSource2D_i.hxx | 74 + src/StdMeshers_I/StdMeshers_Import_1D2D_i.cxx | 67 + src/StdMeshers_I/StdMeshers_Import_1D2D_i.hxx | 53 + src/StdMeshers_I/StdMeshers_Import_1D_i.cxx | 85 + src/StdMeshers_I/StdMeshers_Import_1D_i.hxx | 54 + .../StdMeshers_LayerDistribution2D_i.cxx | 90 + .../StdMeshers_LayerDistribution2D_i.hxx | 61 + .../StdMeshers_LayerDistribution_i.cxx | 52 +- .../StdMeshers_LayerDistribution_i.hxx | 37 +- .../StdMeshers_LengthFromEdges_i.cxx | 41 +- .../StdMeshers_LengthFromEdges_i.hxx | 31 +- src/StdMeshers_I/StdMeshers_LocalLength_i.cxx | 46 +- src/StdMeshers_I/StdMeshers_LocalLength_i.hxx | 35 +- src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.cxx | 39 +- src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.hxx | 33 +- .../StdMeshers_MaxElementArea_i.cxx | 55 +- .../StdMeshers_MaxElementArea_i.hxx | 37 +- .../StdMeshers_MaxElementVolume_i.cxx | 54 +- .../StdMeshers_MaxElementVolume_i.hxx | 35 +- src/StdMeshers_I/StdMeshers_MaxLength_i.cxx | 56 +- src/StdMeshers_I/StdMeshers_MaxLength_i.hxx | 40 +- .../StdMeshers_NotConformAllowed_i.cxx | 31 +- .../StdMeshers_NotConformAllowed_i.hxx | 31 +- .../StdMeshers_NumberOfLayers2D_i.cxx | 88 + .../StdMeshers_NumberOfLayers2D_i.hxx | 58 + .../StdMeshers_NumberOfLayers_i.cxx | 44 +- .../StdMeshers_NumberOfLayers_i.hxx | 36 +- .../StdMeshers_NumberOfSegments_i.cxx | 177 +- .../StdMeshers_NumberOfSegments_i.hxx | 49 +- src/StdMeshers_I/StdMeshers_ObjRefUlils.cxx | 86 +- src/StdMeshers_I/StdMeshers_ObjRefUlils.hxx | 55 +- src/StdMeshers_I/StdMeshers_Prism_3D_i.cxx | 43 +- src/StdMeshers_I/StdMeshers_Prism_3D_i.hxx | 31 +- .../StdMeshers_ProjectionSource1D_i.cxx | 72 +- .../StdMeshers_ProjectionSource1D_i.hxx | 37 +- .../StdMeshers_ProjectionSource2D_i.cxx | 80 +- .../StdMeshers_ProjectionSource2D_i.hxx | 39 +- .../StdMeshers_ProjectionSource3D_i.cxx | 83 +- .../StdMeshers_ProjectionSource3D_i.hxx | 37 +- .../StdMeshers_Projection_1D_2D_3D_i.cxx | 82 +- .../StdMeshers_Projection_1D_2D_3D_i.hxx | 66 +- src/StdMeshers_I/StdMeshers_Propagation_i.cxx | 31 +- src/StdMeshers_I/StdMeshers_Propagation_i.hxx | 31 +- .../StdMeshers_QuadrangleParams_i.cxx | 245 + .../StdMeshers_QuadrangleParams_i.hxx | 82 + .../StdMeshers_QuadranglePreference_i.cxx | 32 +- .../StdMeshers_QuadranglePreference_i.hxx | 32 +- .../StdMeshers_Quadrangle_2D_i.cxx | 39 +- .../StdMeshers_Quadrangle_2D_i.hxx | 33 +- .../StdMeshers_QuadraticMesh_i.cxx | 31 +- .../StdMeshers_QuadraticMesh_i.hxx | 31 +- .../StdMeshers_RadialQuadrangle_1D2D_i.cxx | 68 + .../StdMeshers_RadialQuadrangle_1D2D_i.hxx | 53 + src/StdMeshers_I/StdMeshers_Regular_1D_i.cxx | 32 +- src/StdMeshers_I/StdMeshers_Regular_1D_i.hxx | 34 +- .../StdMeshers_SegmentAroundVertex_0D_i.cxx | 31 +- .../StdMeshers_SegmentAroundVertex_0D_i.hxx | 31 +- ...StdMeshers_SegmentLengthAroundVertex_i.cxx | 44 +- ...StdMeshers_SegmentLengthAroundVertex_i.hxx | 35 +- .../StdMeshers_StartEndLength_i.cxx | 165 +- .../StdMeshers_StartEndLength_i.hxx | 56 +- .../StdMeshers_TrianglePreference_i.cxx | 95 - .../StdMeshers_TrianglePreference_i.hxx | 58 - .../StdMeshers_UseExisting_1D2D_i.cxx | 30 +- .../StdMeshers_UseExisting_1D2D_i.hxx | 30 +- .../StdMeshers_ViscousLayers_i.cxx | 231 + .../StdMeshers_ViscousLayers_i.hxx | 74 + src/StdMeshers_I/StdMeshers_i.cxx | 102 +- src/Tools/Makefile.am | 31 + src/Tools/MeshCut/AUTHORS | 19 + src/Tools/MeshCut/Makefile.am | 59 + src/Tools/MeshCut/MeshCutDialog.ui | 273 + src/Tools/MeshCut/MeshCut_Carre.cxx | 54 + src/Tools/MeshCut/MeshCut_Carre.hxx | 39 + src/Tools/MeshCut/MeshCut_Cas.cxx | 945 ++ src/Tools/MeshCut/MeshCut_Cas.hxx | 31 + src/Tools/MeshCut/MeshCut_Cube.cxx | 58 + src/Tools/MeshCut/MeshCut_Cube.hxx | 39 + src/Tools/MeshCut/MeshCut_DC.cxx | 1124 ++ src/Tools/MeshCut/MeshCut_Fonctions.cxx | 188 + src/Tools/MeshCut/MeshCut_Fonctions.hxx | 36 + src/Tools/MeshCut/MeshCut_Globals.hxx | 62 + src/Tools/MeshCut/MeshCut_Maillage.cxx | 1789 ++ src/Tools/MeshCut/MeshCut_Maillage.hxx | 165 + src/Tools/MeshCut/MeshCut_Utils.cxx | 1104 ++ src/Tools/MeshCut/MeshCut_Utils.hxx | 81 + src/Tools/MeshCut/README | 26 + src/Tools/MeshCut/meshcut_plugin.py | 140 + src/Tools/padder/Makefile.am | 22 + src/Tools/padder/README.txt | 38 + src/Tools/padder/doc/Makefile.am | 39 + src/Tools/padder/doc/doxyfile.in | 77 + .../padder/doc/images/SMESH_spadder_end.png | Bin 0 -> 82471 bytes .../SMESH_spadder_inputdialog_concrete.png | Bin 0 -> 85319 bytes .../SMESH_spadder_inputdialog_start.png | Bin 0 -> 94767 bytes .../SMESH_spadder_inputdialog_steelbar.png | Bin 0 -> 87059 bytes .../padder/doc/images/SMESH_spadder_menu.png | Bin 0 -> 116970 bytes ..._spadder_plugindialog_compute_finished.png | Bin 0 -> 96313 bytes ...ESH_spadder_plugindialog_compute_ready.png | Bin 0 -> 80869 bytes ...H_spadder_plugindialog_compute_running.png | Bin 0 -> 89493 bytes .../SMESH_spadder_plugindialog_published.png | Bin 0 -> 108235 bytes .../SMESH_spadder_plugindialog_start.png | Bin 0 -> 77424 bytes .../padder/doc/images/SMESH_spadder_start.png | Bin 0 -> 66799 bytes .../padder/doc/input/padder_userguide.doc | 139 + src/Tools/padder/meshjob/Makefile.am | 20 + src/Tools/padder/meshjob/idl/MESHJOB.idl | 126 + src/Tools/padder/meshjob/idl/Makefile.am | 95 + .../padder/meshjob/idl/SPADDERPluginTest.idl | 46 + src/Tools/padder/meshjob/impl/Makefile.am | 89 + .../padder/meshjob/impl/MeshJobManager_i.cxx | 631 + .../padder/meshjob/impl/MeshJobManager_i.hxx | 81 + .../meshjob/impl/SPADDERPluginTester_i.cxx | 157 + .../meshjob/impl/SPADDERPluginTester_i.hxx | 47 + src/Tools/padder/meshjob/impl/testhelper.hxx | 66 + src/Tools/padder/resources/Makefile.am | 25 + src/Tools/padder/resources/SPADDERCatalog.xml | 16 + .../resources/appligen/CatalogResources.xml | 21 + .../padder/resources/appligen/Makefile.am | 40 + .../padder/resources/appligen/README.txt | 24 + .../padder/resources/appligen/SalomeApp.xml | 40 + .../resources/appligen/appli-splashscreen.jpg | Bin 0 -> 111329 bytes .../padder/resources/appligen/appligen.sh.in | 50 + .../resources/appligen/config_appli.xml.in | 15 + src/Tools/padder/resources/appligen/genenv.sh | 60 + .../padder/resources/padderexe/Makefile.am | 42 + .../resources/padderexe/buildparticules.py | 42 + .../resources/padderexe/envPadder.sh.in | 27 + .../padderexe/med2/REF_FinalEDMesh.med | Bin 0 -> 244932 bytes .../padderexe/med2/REF_spheres.dat.xyz | 4097 +++++ .../resources/padderexe/med2/concrete.med | Bin 0 -> 32448 bytes .../padder/resources/padderexe/med2/data.txt | 5 + .../resources/padderexe/med2/ferraill.med | Bin 0 -> 16472 bytes .../resources/padderexe/med2/ferrtran.med | Bin 0 -> 17880 bytes .../resources/padderexe/med2/padder.exe | Bin 0 -> 2634908 bytes .../resources/padderexe/med3/concrete.med | Bin 0 -> 32448 bytes .../padder/resources/padderexe/med3/data.txt | 4 + .../resources/padderexe/med3/ferraill.med | Bin 0 -> 16472 bytes .../resources/padderexe/med3/padder.exe | Bin 0 -> 861247 bytes .../padder/resources/padderexe/padder.sh | 34 + .../padder/resources/padderexe/particules.png | Bin 0 -> 335310 bytes src/Tools/padder/spadderpy/Makefile.am | 36 + src/Tools/padder/spadderpy/__init__.py | 93 + src/Tools/padder/spadderpy/configreader.py | 140 + src/Tools/padder/spadderpy/gui/Makefile.am | 60 + src/Tools/padder/spadderpy/gui/__init__.py | 20 + src/Tools/padder/spadderpy/gui/addinput.png | Bin 0 -> 1653 bytes src/Tools/padder/spadderpy/gui/clear.png | Bin 0 -> 1516 bytes src/Tools/padder/spadderpy/gui/compute.png | Bin 0 -> 1470 bytes src/Tools/padder/spadderpy/gui/concrete.png | Bin 0 -> 353 bytes .../padder/spadderpy/gui/deleteinput.png | Bin 0 -> 1619 bytes src/Tools/padder/spadderpy/gui/input.png | Bin 0 -> 1493 bytes src/Tools/padder/spadderpy/gui/inputdata.py | 77 + src/Tools/padder/spadderpy/gui/inputdialog.py | 376 + src/Tools/padder/spadderpy/gui/inputframe.ui | 204 + src/Tools/padder/spadderpy/gui/parameters.png | Bin 0 -> 1493 bytes .../padder/spadderpy/gui/plugindialog.py | 403 + .../padder/spadderpy/gui/plugindialog.ui | 168 + src/Tools/padder/spadderpy/gui/publish.png | Bin 0 -> 1622 bytes src/Tools/padder/spadderpy/gui/refresh.png | Bin 0 -> 1546 bytes src/Tools/padder/spadderpy/gui/select.png | Bin 0 -> 1305 bytes src/Tools/padder/spadderpy/gui/steelbar.png | Bin 0 -> 538 bytes src/Tools/padder/spadderpy/padder.cfg.in | 35 + src/Tools/padder/spadderpy/plugin/Makefile.am | 33 + .../padder/spadderpy/plugin/envPlugins.sh.in | 22 + .../padder/spadderpy/plugin/spadderPlugin.py | 36 + src/Tools/padder/unittests/Makefile.am | 31 + src/Tools/padder/unittests/__init__.py | 18 + src/Tools/padder/unittests/autotest.sh.in | 61 + .../unittests/usecase_meshJobManager.py | 102 + .../unittests/usecase_spadderPluginTester.py | 51 + src/Tools/smesh_plugins.py | 33 + 1285 files changed, 164584 insertions(+), 66105 deletions(-) create mode 100644 adm_local/cmake_files/FindSMESH.cmake create mode 100644 adm_local/cmake_files/Makefile.am create mode 100644 adm_local/unix/config_files/check_cgal.m4 create mode 100644 adm_local/unix/config_files/check_cgns.m4 create mode 100644 adm_local/unix/config_files/check_padder.m4 create mode 100644 adm_local/unix/config_files/check_qwt.m4 create mode 100644 bin/smesh_setenv.py create mode 100755 build_cmake create mode 100644 build_cmake.bat create mode 100644 doc/docutils/Makefile.am create mode 100644 doc/docutils/conf.py.in create mode 100644 doc/docutils/docapi.rst create mode 100644 doc/docutils/index.rst create mode 100644 doc/docutils/overview.rst create mode 100644 doc/salome/gui/SMESH/images/2d_from_3d_dlg.png create mode 100644 doc/salome/gui/SMESH/images/2d_from_3d_ico.png create mode 100644 doc/salome/gui/SMESH/images/2d_from_3d_menu.png create mode 100644 doc/salome/gui/SMESH/images/add0delement.png create mode 100644 doc/salome/gui/SMESH/images/add_0delement.png create mode 100644 doc/salome/gui/SMESH/images/add_ball.png create mode 100644 doc/salome/gui/SMESH/images/addball.png create mode 100644 doc/salome/gui/SMESH/images/addinfo_group.png create mode 100644 doc/salome/gui/SMESH/images/addinfo_mesh.png create mode 100644 doc/salome/gui/SMESH/images/addinfo_submesh.png create mode 100644 doc/salome/gui/SMESH/images/bare_border_faces_smpl.png create mode 100644 doc/salome/gui/SMESH/images/bare_border_volumes_smpl.png delete mode 100644 doc/salome/gui/SMESH/images/blsurf_parameters.png delete mode 100644 doc/salome/gui/SMESH/images/blsurf_parameters_advanced.png create mode 100644 doc/salome/gui/SMESH/images/bnd_box.png create mode 100644 doc/salome/gui/SMESH/images/bnd_box_preview.png create mode 100644 doc/salome/gui/SMESH/images/cartesian3D_hyp.png create mode 100644 doc/salome/gui/SMESH/images/cartesian3D_sphere.png create mode 100755 doc/salome/gui/SMESH/images/colors_size.png create mode 100755 doc/salome/gui/SMESH/images/controls_popup.png create mode 100644 doc/salome/gui/SMESH/images/copy_mesh_dlg.png create mode 100644 doc/salome/gui/SMESH/images/copy_mesh_icon.png create mode 100644 doc/salome/gui/SMESH/images/creategroup_on_filter.png create mode 100755 doc/salome/gui/SMESH/images/custom_point_marker.png create mode 100644 doc/salome/gui/SMESH/images/double_faces.png create mode 100644 doc/salome/gui/SMESH/images/double_nodes.png create mode 100644 doc/salome/gui/SMESH/images/duplicate01.png create mode 100644 doc/salome/gui/SMESH/images/duplicate02.png create mode 100644 doc/salome/gui/SMESH/images/duplicate_nodes.png create mode 100644 doc/salome/gui/SMESH/images/elem_info.png create mode 100644 doc/salome/gui/SMESH/images/extr_along_wire_after.png create mode 100644 doc/salome/gui/SMESH/images/extr_along_wire_before.png delete mode 100755 doc/salome/gui/SMESH/images/extrusion2.png create mode 100644 doc/salome/gui/SMESH/images/findelement1.png create mode 100644 doc/salome/gui/SMESH/images/findelement2.png create mode 100644 doc/salome/gui/SMESH/images/findelement3.png create mode 100644 doc/salome/gui/SMESH/images/formula5.png delete mode 100644 doc/salome/gui/SMESH/images/ghs3d_parameters_advanced.png delete mode 100644 doc/salome/gui/SMESH/images/ghs3d_parameters_basic.png create mode 100755 doc/salome/gui/SMESH/images/head.png create mode 100644 doc/salome/gui/SMESH/images/hyp_source_edges.png create mode 100644 doc/salome/gui/SMESH/images/hyp_source_faces.png create mode 100755 doc/salome/gui/SMESH/images/hypo_fixedpnt_dlg.png create mode 100755 doc/salome/gui/SMESH/images/hypo_quad_params_1.png create mode 100644 doc/salome/gui/SMESH/images/hypo_quad_params_2.png create mode 100644 doc/salome/gui/SMESH/images/hypo_quad_params_dialog.png create mode 100644 doc/salome/gui/SMESH/images/hypo_quad_params_res.png create mode 100644 doc/salome/gui/SMESH/images/hypo_quad_params_res_2.png create mode 100755 doc/salome/gui/SMESH/images/hypo_radquad_dlg.png create mode 100644 doc/salome/gui/SMESH/images/hypo_sets.png create mode 100755 doc/salome/gui/SMESH/images/image42.png create mode 100755 doc/salome/gui/SMESH/images/image43.png create mode 100644 doc/salome/gui/SMESH/images/image_octa12.png create mode 100755 doc/salome/gui/SMESH/images/max_element_length_2d.png create mode 100755 doc/salome/gui/SMESH/images/max_element_length_3d.png create mode 100644 doc/salome/gui/SMESH/images/mergeelems_auto.png create mode 100644 doc/salome/gui/SMESH/images/mergenodes_auto.png create mode 100755 doc/salome/gui/SMESH/images/mesh_evaluation_succeed.png create mode 100755 doc/salome/gui/SMESH/images/mesh_fixedpnt.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_123.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_123_res.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_213.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_213_res.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_321.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_321_res.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_no_concurrent.png create mode 100644 doc/salome/gui/SMESH/images/mesh_order_preview.png create mode 100755 doc/salome/gui/SMESH/images/mesh_radquad_01.png create mode 100755 doc/salome/gui/SMESH/images/mesh_radquad_02.png create mode 100644 doc/salome/gui/SMESH/images/meshcut_plugin.png create mode 100644 doc/salome/gui/SMESH/images/min_distance.png create mode 100644 doc/salome/gui/SMESH/images/min_distance_preview.png delete mode 100644 doc/salome/gui/SMESH/images/netgen2d.png delete mode 100644 doc/salome/gui/SMESH/images/netgen3d_simple.png create mode 100644 doc/salome/gui/SMESH/images/over_constrained_faces.png create mode 100644 doc/salome/gui/SMESH/images/over_constrained_volumes.png create mode 100644 doc/salome/gui/SMESH/images/pattern2d.png create mode 100755 doc/salome/gui/SMESH/images/point_marker_widget1.png create mode 100755 doc/salome/gui/SMESH/images/point_marker_widget2.png create mode 100755 doc/salome/gui/SMESH/images/pref21.png create mode 100755 doc/salome/gui/SMESH/images/pref22.png create mode 100755 doc/salome/gui/SMESH/images/pref23.png create mode 100755 doc/salome/gui/SMESH/images/pref24.png create mode 100644 doc/salome/gui/SMESH/images/remove_nodes_icon.png create mode 100644 doc/salome/gui/SMESH/images/remove_orphan_nodes_icon.png create mode 100644 doc/salome/gui/SMESH/images/removeorphannodes.png create mode 100644 doc/salome/gui/SMESH/images/reorient_2d_face.png create mode 100644 doc/salome/gui/SMESH/images/reorient_2d_point.png create mode 100644 doc/salome/gui/SMESH/images/reorient_faces_face.png delete mode 100755 doc/salome/gui/SMESH/images/revolution2.png create mode 100755 doc/salome/gui/SMESH/images/scalar_bar_dlg.png create mode 100644 doc/salome/gui/SMESH/images/scale01.png create mode 100644 doc/salome/gui/SMESH/images/scale02.png create mode 100644 doc/salome/gui/SMESH/images/scale03.png create mode 100644 doc/salome/gui/SMESH/images/scale04.png create mode 100644 doc/salome/gui/SMESH/images/scale06.png create mode 100644 doc/salome/gui/SMESH/images/scale07.png create mode 100644 doc/salome/gui/SMESH/images/scale09.png create mode 100644 doc/salome/gui/SMESH/images/scaleinit01.png create mode 100644 doc/salome/gui/SMESH/images/scaleinit02.png create mode 100644 doc/salome/gui/SMESH/images/scaleres03.png create mode 100644 doc/salome/gui/SMESH/images/scaleres04.png create mode 100644 doc/salome/gui/SMESH/images/scaleres06.png create mode 100644 doc/salome/gui/SMESH/images/scaleres07.png create mode 100644 doc/salome/gui/SMESH/images/scaleres09.png create mode 100644 doc/salome/gui/SMESH/images/split_into_tetra.png create mode 100644 doc/salome/gui/SMESH/images/split_into_tetra_icon.png create mode 100755 doc/salome/gui/SMESH/images/std_point_marker.png create mode 100644 doc/salome/gui/SMESH/images/use_existing_face_sample_mesh.png create mode 100644 doc/salome/gui/SMESH/images/using_notebook_smesh.png create mode 100644 doc/salome/gui/SMESH/images/viscous_layers_hyp.png create mode 100644 doc/salome/gui/SMESH/images/viscous_layers_mesh.png create mode 100644 doc/salome/gui/SMESH/input/about_filters.doc create mode 100644 doc/salome/gui/SMESH/input/bare_border_face.doc create mode 100644 doc/salome/gui/SMESH/input/bare_border_volumes.doc delete mode 100644 doc/salome/gui/SMESH/input/blsurf_hypo.doc create mode 100644 doc/salome/gui/SMESH/input/cartesian_algo.doc create mode 100644 doc/salome/gui/SMESH/input/colors_size.doc create mode 100644 doc/salome/gui/SMESH/input/copy_mesh.doc create mode 100755 doc/salome/gui/SMESH/input/create_groups_from_geometry.doc create mode 100644 doc/salome/gui/SMESH/input/cut_mesh_by_plane.doc create mode 100644 doc/salome/gui/SMESH/input/double_elements_control.doc create mode 100644 doc/salome/gui/SMESH/input/double_nodes_control.doc create mode 100644 doc/salome/gui/SMESH/input/double_nodes_page.doc create mode 100644 doc/salome/gui/SMESH/input/find_element_by_point.doc create mode 100644 doc/salome/gui/SMESH/input/generate_flat_elements.doc delete mode 100644 doc/salome/gui/SMESH/input/ghs3d_hypo.doc delete mode 100644 doc/salome/gui/SMESH/input/ghs3dprl_hypo.doc create mode 100755 doc/salome/gui/SMESH/input/group_of_underlying_elements.doc create mode 100644 doc/salome/gui/SMESH/input/make_2dmesh_from_3d.doc create mode 100644 doc/salome/gui/SMESH/input/max_element_length_2d.doc create mode 100644 doc/salome/gui/SMESH/input/max_element_length_3d.doc create mode 100644 doc/salome/gui/SMESH/input/measurements.doc create mode 100644 doc/salome/gui/SMESH/input/mesh_preferences.doc delete mode 100644 doc/salome/gui/SMESH/input/moving_nodes.doc delete mode 100644 doc/salome/gui/SMESH/input/netgen_2d_3d_hypo.doc create mode 100644 doc/salome/gui/SMESH/input/over_constrained_faces.doc create mode 100644 doc/salome/gui/SMESH/input/over_constrained_volumes.doc create mode 100644 doc/salome/gui/SMESH/input/point_marker.doc delete mode 100644 doc/salome/gui/SMESH/input/preview_meshes.doc create mode 100644 doc/salome/gui/SMESH/input/radial_quadrangle_1D2D_algo.doc create mode 100644 doc/salome/gui/SMESH/input/reorient_faces.doc create mode 100755 doc/salome/gui/SMESH/input/scalar_bar.doc create mode 100644 doc/salome/gui/SMESH/input/scale.doc create mode 100644 doc/salome/gui/SMESH/input/split_to_tetra.doc create mode 100644 doc/salome/gui/SMESH/input/tui_cartesian_algo.doc create mode 100755 doc/salome/gui/SMESH/input/tui_filters.doc create mode 100644 doc/salome/gui/SMESH/input/tui_generate_flat_elements.doc create mode 100644 doc/salome/gui/SMESH/input/tui_measurements.doc create mode 100644 doc/salome/gui/SMESH/input/tui_notebook_smesh.doc create mode 100644 doc/salome/gui/SMESH/input/tui_use_existing_faces.doc create mode 100644 doc/salome/gui/SMESH/input/tui_work_on_objects_from_gui.doc create mode 100644 doc/salome/gui/SMESH/input/use_existing_algos.doc delete mode 100755 doc/salome/gui/SMESH/static/header.html create mode 100755 doc/salome/gui/SMESH/static/header.html.in create mode 100644 doc/salome/gui/SMESH/static/header_py.html.in delete mode 100644 doc/salome/tui/extra/AddNetgenInSalome2.pdf delete mode 100644 doc/salome/tui/extra/AddNetgenInSalome2.ps delete mode 100644 doc/salome/tui/extra/AddNetgenInSalome2.sxw delete mode 100755 doc/salome/tui/extra/PluginMeshers.html delete mode 100644 doc/salome/tui/extra/PluginMeshers.txt create mode 100755 doc/salome/tui/images/head.png create mode 100755 doc/salome/tui/images/smeshscreen.png create mode 100644 doc/salome/tui/input/index.doc create mode 100755 doc/salome/tui/static/header.html.in delete mode 100755 doc/salome/tui/static/myheader.html create mode 100644 idl/SMESH_Measurements.idl rename resources/{SalomeApp.xml => SalomeApp.xml.in} (56%) create mode 100644 resources/bare_border_face.png create mode 100644 resources/bare_border_volume.png create mode 100644 resources/copy_mesh.png create mode 100644 resources/mesh_2d_from_3d.png create mode 100644 resources/mesh_ball.png create mode 100644 resources/mesh_biquad_quadrangle.png create mode 100755 resources/mesh_bounding_box.png create mode 100644 resources/mesh_duplicate_nodes.png create mode 100644 resources/mesh_duplicate_nodes_with_elem.png create mode 100644 resources/mesh_elem_info.png create mode 100644 resources/mesh_equal_edge.png create mode 100644 resources/mesh_equal_face.png create mode 100644 resources/mesh_equal_node.png create mode 100644 resources/mesh_equal_volume.png create mode 100644 resources/mesh_find_elem_by_point.png create mode 100644 resources/mesh_hypo_viscous_layers.png create mode 100755 resources/mesh_max_element_length_2d.png create mode 100755 resources/mesh_max_element_length_3d.png create mode 100755 resources/mesh_min_dist.png create mode 100644 resources/mesh_octahedron.png create mode 100644 resources/mesh_pentahedron.png create mode 100644 resources/mesh_quadrangle_quadpref.png create mode 100644 resources/mesh_quadrangle_quadpref_reversed.png create mode 100644 resources/mesh_quadrangle_reduced.png create mode 100644 resources/mesh_quadrangle_standard.png create mode 100644 resources/mesh_quadrangle_triapref.png create mode 100644 resources/mesh_rem_orphan_nodes.png create mode 100644 resources/mesh_tree_algo_0D.png create mode 100644 resources/mesh_tree_algo_existing_2D.png create mode 100644 resources/mesh_tree_algo_prism.png create mode 100644 resources/mesh_tree_algo_radial_quadrangle_1D2D.png create mode 100644 resources/mesh_tree_group_on_filter.png create mode 100644 resources/mesh_tree_hypo_viscous_layers.png create mode 100644 resources/mesh_triquad_hexahedron.png create mode 100644 resources/over_constrained_face.png create mode 100644 resources/over_constrained_volume.png create mode 100644 resources/reorient_faces_face.png create mode 100644 resources/reorient_faces_point.png create mode 100644 resources/scale.png create mode 100644 resources/scale_along_axes.png create mode 100644 resources/split_into_tetra.png delete mode 100644 src/Controls/SMESH_Controls.hxx create mode 100644 src/DriverCGNS/DriverCGNS_Read.cxx create mode 100644 src/DriverCGNS/DriverCGNS_Read.hxx create mode 100644 src/DriverCGNS/DriverCGNS_Write.cxx create mode 100644 src/DriverCGNS/DriverCGNS_Write.hxx create mode 100644 src/DriverCGNS/Makefile.am create mode 100755 src/DriverCGNS/SMESH_DriverCGNS.hxx delete mode 100644 src/DriverDAT/DriverDAT_R_SMESHDS_Document.cxx delete mode 100644 src/DriverDAT/DriverDAT_R_SMESHDS_Document.h delete mode 100644 src/DriverDAT/DriverDAT_R_SMESHDS_Mesh.cxx delete mode 100644 src/DriverDAT/DriverDAT_R_SMESHDS_Mesh.h delete mode 100644 src/DriverDAT/DriverDAT_W_SMESHDS_Document.cxx delete mode 100644 src/DriverDAT/DriverDAT_W_SMESHDS_Document.h delete mode 100644 src/DriverDAT/DriverDAT_W_SMESHDS_Mesh.cxx delete mode 100644 src/DriverDAT/DriverDAT_W_SMESHDS_Mesh.h delete mode 100644 src/DriverMED/DriverMED_R_SMDS_Mesh.cxx delete mode 100644 src/DriverMED/DriverMED_R_SMDS_Mesh.h delete mode 100644 src/DriverMED/DriverMED_R_SMESHDS_Document.cxx delete mode 100644 src/DriverMED/DriverMED_R_SMESHDS_Document.h delete mode 100644 src/DriverMED/DriverMED_W_SMDS_Mesh.cxx delete mode 100644 src/DriverMED/DriverMED_W_SMDS_Mesh.h delete mode 100644 src/DriverMED/DriverMED_W_SMESHDS_Document.cxx delete mode 100644 src/DriverMED/DriverMED_W_SMESHDS_Document.h delete mode 100644 src/DriverUNV/DriverUNV_R_SMESHDS_Document.cxx delete mode 100644 src/DriverUNV/DriverUNV_R_SMESHDS_Document.h delete mode 100644 src/DriverUNV/DriverUNV_R_SMESHDS_Mesh.cxx delete mode 100644 src/DriverUNV/DriverUNV_R_SMESHDS_Mesh.h delete mode 100644 src/DriverUNV/DriverUNV_W_SMESHDS_Document.cxx delete mode 100644 src/DriverUNV/DriverUNV_W_SMESHDS_Document.h delete mode 100644 src/DriverUNV/DriverUNV_W_SMESHDS_Mesh.cxx delete mode 100644 src/DriverUNV/DriverUNV_W_SMESHDS_Mesh.h create mode 100644 src/DriverUNV/UNV164_Structure.cxx create mode 100644 src/DriverUNV/UNV164_Structure.hxx create mode 100644 src/DriverUNV/UNV2420_Structure.cxx create mode 100644 src/DriverUNV/UNV2420_Structure.hxx create mode 100644 src/OBJECT/SMESH_CellLabelActor.cxx create mode 100644 src/OBJECT/SMESH_CellLabelActor.h create mode 100644 src/OBJECT/SMESH_NodeLabelActor.cxx create mode 100644 src/OBJECT/SMESH_NodeLabelActor.h create mode 100644 src/OBJECT/SMESH_PreviewActorsCollection.cxx create mode 100644 src/OBJECT/SMESH_PreviewActorsCollection.h create mode 100644 src/OBJECT/SMESH_ScalarBarActor.cxx create mode 100644 src/OBJECT/SMESH_ScalarBarActor.h create mode 100644 src/PluginUtils/GeomSelectionTools.cxx create mode 100644 src/PluginUtils/GeomSelectionTools.h create mode 100644 src/PluginUtils/Makefile.am create mode 100644 src/SMDS/Notes create mode 100644 src/SMDS/ObjectPool.hxx create mode 100644 src/SMDS/SMDS_BallElement.cxx create mode 100644 src/SMDS/SMDS_BallElement.hxx create mode 100644 src/SMDS/SMDS_Downward.cxx create mode 100644 src/SMDS/SMDS_Downward.hxx create mode 100644 src/SMDS/SMDS_IteratorOnIterators.hxx create mode 100644 src/SMDS/SMDS_LinearEdge.cxx create mode 100644 src/SMDS/SMDS_LinearEdge.hxx create mode 100644 src/SMDS/SMDS_Mesh0DElement.cxx create mode 100644 src/SMDS/SMDS_Mesh0DElement.hxx create mode 100644 src/SMDS/SMDS_MeshCell.cxx create mode 100644 src/SMDS/SMDS_MeshCell.hxx create mode 100644 src/SMDS/SMDS_MeshNodeIDFactory.cxx create mode 100644 src/SMDS/SMDS_MeshNodeIDFactory.hxx create mode 100644 src/SMDS/SMDS_StdIterator.hxx create mode 100644 src/SMDS/SMDS_UnstructuredGrid.cxx create mode 100644 src/SMDS/SMDS_UnstructuredGrid.hxx create mode 100644 src/SMDS/SMDS_VtkCellIterator.cxx create mode 100644 src/SMDS/SMDS_VtkCellIterator.hxx create mode 100644 src/SMDS/SMDS_VtkEdge.cxx create mode 100644 src/SMDS/SMDS_VtkEdge.hxx create mode 100644 src/SMDS/SMDS_VtkFace.cxx create mode 100644 src/SMDS/SMDS_VtkFace.hxx create mode 100644 src/SMDS/SMDS_VtkVolume.cxx create mode 100644 src/SMDS/SMDS_VtkVolume.hxx create mode 100644 src/SMDS/chrono.cxx create mode 100644 src/SMDS/chrono.hxx delete mode 100644 src/SMESH/SMESH_Comment.hxx delete mode 100644 src/SMESH/SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx delete mode 100644 src/SMESH/SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx delete mode 100644 src/SMESH/SMESH_Octree.cxx delete mode 100644 src/SMESH/SMESH_Octree.hxx delete mode 100644 src/SMESH/SMESH_OctreeNode.cxx delete mode 100644 src/SMESH/SMESH_OctreeNode.hxx create mode 100644 src/SMESH/SMESH_ProxyMesh.cxx create mode 100644 src/SMESH/SMESH_ProxyMesh.hxx delete mode 100644 src/SMESH/SMESH_SequenceOfElemPtr.hxx delete mode 100644 src/SMESH/SMESH_SequenceOfNode.hxx create mode 100644 src/SMESH/memoire.h create mode 100644 src/SMESHDS/SMESHDS_GroupOnFilter.cxx create mode 100644 src/SMESHDS/SMESHDS_GroupOnFilter.hxx create mode 100644 src/SMESHDS/SMESH_Controls.hxx create mode 100644 src/SMESHGUI/SMESHGUI_CopyMeshDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_CopyMeshDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.h create mode 100755 src/SMESHGUI/SMESHGUI_FileValidator.cxx create mode 100755 src/SMESHGUI/SMESHGUI_FileValidator.h create mode 100644 src/SMESHGUI/SMESHGUI_FindElemByPointDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_FindElemByPointDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.cxx create mode 100644 src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.h create mode 100644 src/SMESHGUI/SMESHGUI_Measurements.cxx create mode 100644 src/SMESHGUI/SMESHGUI_Measurements.h rename src/SMESHGUI/{SMESHGUI_EditMeshDlg.cxx => SMESHGUI_MergeDlg.cxx} (69%) rename src/SMESHGUI/{SMESHGUI_EditMeshDlg.h => SMESHGUI_MergeDlg.h} (69%) create mode 100644 src/SMESHGUI/SMESHGUI_MeshInfo.cxx create mode 100644 src/SMESHGUI/SMESHGUI_MeshInfo.h create mode 100644 src/SMESHGUI/SMESHGUI_MeshInfosBox.cxx create mode 100644 src/SMESHGUI/SMESHGUI_MeshInfosBox.h delete mode 100644 src/SMESHGUI/SMESHGUI_MeshInfosDlg.cxx delete mode 100644 src/SMESHGUI/SMESHGUI_MeshInfosDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_MeshOrderDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_MeshOrderDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_MeshOrderOp.cxx create mode 100644 src/SMESHGUI/SMESHGUI_MeshOrderOp.h delete mode 100644 src/SMESHGUI/SMESHGUI_MoveNodesDlg.cxx delete mode 100644 src/SMESHGUI/SMESHGUI_MoveNodesDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_PreviewDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_PreviewDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h create mode 100644 src/SMESHGUI/SMESHGUI_ScaleDlg.cxx create mode 100644 src/SMESHGUI/SMESHGUI_ScaleDlg.h delete mode 100644 src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.cxx delete mode 100644 src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.h delete mode 100755 src/SMESHGUI/SMESHGUI_WhatIsDlg.cxx delete mode 100755 src/SMESHGUI/SMESHGUI_WhatIsDlg.h create mode 100755 src/SMESHGUI/SMESH_msg_fr.ts create mode 100644 src/SMESHUtils/Makefile.am rename src/{SMESH => SMESHUtils}/SMESH_Block.cxx (94%) rename src/{SMESH => SMESHUtils}/SMESH_Block.hxx (90%) create mode 100644 src/SMESHUtils/SMESH_Comment.hxx rename src/{SMESH => SMESHUtils}/SMESH_ComputeError.hxx (66%) create mode 100644 src/SMESHUtils/SMESH_File.cxx create mode 100644 src/SMESHUtils/SMESH_File.hxx create mode 100644 src/SMESHUtils/SMESH_Octree.cxx create mode 100644 src/SMESHUtils/SMESH_Octree.hxx create mode 100644 src/SMESHUtils/SMESH_OctreeNode.cxx create mode 100644 src/SMESHUtils/SMESH_OctreeNode.hxx create mode 100644 src/SMESHUtils/SMESH_TypeDefs.hxx create mode 100755 src/SMESHUtils/SMESH_Utils.hxx create mode 100644 src/SMESH_I/SMESH_Measurements_i.cxx create mode 100644 src/SMESH_I/SMESH_Measurements_i.hxx create mode 100644 src/SMESH_I/SMESH_PreMeshInfo.cxx create mode 100644 src/SMESH_I/SMESH_PreMeshInfo.hxx create mode 100644 src/SMESH_PY/Makefile.am create mode 100644 src/SMESH_PY/__init__.py create mode 100644 src/SMESH_PY/smeshstudytools.py create mode 100644 src/SMESH_SWIG/StdMeshersDC.py create mode 100644 src/StdMeshers/StdMeshers_CartesianParameters3D.cxx create mode 100644 src/StdMeshers/StdMeshers_CartesianParameters3D.hxx create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D.cxx create mode 100644 src/StdMeshers/StdMeshers_Cartesian_3D.hxx create mode 100644 src/StdMeshers/StdMeshers_FixedPoints1D.cxx create mode 100644 src/StdMeshers/StdMeshers_FixedPoints1D.hxx create mode 100644 src/StdMeshers/StdMeshers_HexaFromSkin_3D.cxx create mode 100644 src/StdMeshers/StdMeshers_HexaFromSkin_3D.hxx create mode 100644 src/StdMeshers/StdMeshers_ImportSource.cxx create mode 100644 src/StdMeshers/StdMeshers_ImportSource.hxx create mode 100644 src/StdMeshers/StdMeshers_Import_1D.cxx create mode 100644 src/StdMeshers/StdMeshers_Import_1D.hxx create mode 100644 src/StdMeshers/StdMeshers_Import_1D2D.cxx create mode 100644 src/StdMeshers/StdMeshers_Import_1D2D.hxx create mode 100644 src/StdMeshers/StdMeshers_LayerDistribution2D.cxx create mode 100644 src/StdMeshers/StdMeshers_LayerDistribution2D.hxx create mode 100644 src/StdMeshers/StdMeshers_NumberOfLayers2D.cxx create mode 100644 src/StdMeshers/StdMeshers_NumberOfLayers2D.hxx create mode 100644 src/StdMeshers/StdMeshers_Projection_1D2D.cxx create mode 100644 src/StdMeshers/StdMeshers_Projection_1D2D.hxx create mode 100644 src/StdMeshers/StdMeshers_QuadrangleParams.cxx create mode 100644 src/StdMeshers/StdMeshers_QuadrangleParams.hxx create mode 100644 src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx create mode 100644 src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.hxx delete mode 100644 src/StdMeshers/StdMeshers_TrianglePreference.cxx delete mode 100644 src/StdMeshers/StdMeshers_TrianglePreference.hxx create mode 100644 src/StdMeshers/StdMeshers_ViscousLayers.cxx create mode 100644 src/StdMeshers/StdMeshers_ViscousLayers.hxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h create mode 100644 src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.cxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.h create mode 100644 src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.cxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.h create mode 100644 src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.cxx create mode 100644 src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.h create mode 100755 src/StdMeshersGUI/StdMeshers_msg_fr.ts create mode 100644 src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_Cartesian_3D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_Cartesian_3D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_FixedPoints1D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_FixedPoints1D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_ImportSource1D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_ImportSource1D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_ImportSource2D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_ImportSource2D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_Import_1D2D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_Import_1D2D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_Import_1D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_Import_1D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_QuadrangleParams_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_QuadrangleParams_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.hxx delete mode 100644 src/StdMeshers_I/StdMeshers_TrianglePreference_i.cxx delete mode 100644 src/StdMeshers_I/StdMeshers_TrianglePreference_i.hxx create mode 100644 src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx create mode 100644 src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx create mode 100644 src/Tools/Makefile.am create mode 100644 src/Tools/MeshCut/AUTHORS create mode 100644 src/Tools/MeshCut/Makefile.am create mode 100644 src/Tools/MeshCut/MeshCutDialog.ui create mode 100644 src/Tools/MeshCut/MeshCut_Carre.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Carre.hxx create mode 100644 src/Tools/MeshCut/MeshCut_Cas.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Cas.hxx create mode 100644 src/Tools/MeshCut/MeshCut_Cube.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Cube.hxx create mode 100644 src/Tools/MeshCut/MeshCut_DC.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Fonctions.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Fonctions.hxx create mode 100644 src/Tools/MeshCut/MeshCut_Globals.hxx create mode 100644 src/Tools/MeshCut/MeshCut_Maillage.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Maillage.hxx create mode 100644 src/Tools/MeshCut/MeshCut_Utils.cxx create mode 100644 src/Tools/MeshCut/MeshCut_Utils.hxx create mode 100644 src/Tools/MeshCut/README create mode 100644 src/Tools/MeshCut/meshcut_plugin.py create mode 100644 src/Tools/padder/Makefile.am create mode 100644 src/Tools/padder/README.txt create mode 100755 src/Tools/padder/doc/Makefile.am create mode 100755 src/Tools/padder/doc/doxyfile.in create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_end.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_inputdialog_concrete.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_inputdialog_start.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_inputdialog_steelbar.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_menu.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_finished.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_ready.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_running.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_plugindialog_published.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_plugindialog_start.png create mode 100644 src/Tools/padder/doc/images/SMESH_spadder_start.png create mode 100644 src/Tools/padder/doc/input/padder_userguide.doc create mode 100644 src/Tools/padder/meshjob/Makefile.am create mode 100644 src/Tools/padder/meshjob/idl/MESHJOB.idl create mode 100644 src/Tools/padder/meshjob/idl/Makefile.am create mode 100644 src/Tools/padder/meshjob/idl/SPADDERPluginTest.idl create mode 100644 src/Tools/padder/meshjob/impl/Makefile.am create mode 100644 src/Tools/padder/meshjob/impl/MeshJobManager_i.cxx create mode 100644 src/Tools/padder/meshjob/impl/MeshJobManager_i.hxx create mode 100644 src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.cxx create mode 100644 src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.hxx create mode 100644 src/Tools/padder/meshjob/impl/testhelper.hxx create mode 100644 src/Tools/padder/resources/Makefile.am create mode 100644 src/Tools/padder/resources/SPADDERCatalog.xml create mode 100644 src/Tools/padder/resources/appligen/CatalogResources.xml create mode 100644 src/Tools/padder/resources/appligen/Makefile.am create mode 100644 src/Tools/padder/resources/appligen/README.txt create mode 100644 src/Tools/padder/resources/appligen/SalomeApp.xml create mode 100644 src/Tools/padder/resources/appligen/appli-splashscreen.jpg create mode 100755 src/Tools/padder/resources/appligen/appligen.sh.in create mode 100644 src/Tools/padder/resources/appligen/config_appli.xml.in create mode 100755 src/Tools/padder/resources/appligen/genenv.sh create mode 100644 src/Tools/padder/resources/padderexe/Makefile.am create mode 100755 src/Tools/padder/resources/padderexe/buildparticules.py create mode 100644 src/Tools/padder/resources/padderexe/envPadder.sh.in create mode 100644 src/Tools/padder/resources/padderexe/med2/REF_FinalEDMesh.med create mode 100644 src/Tools/padder/resources/padderexe/med2/REF_spheres.dat.xyz create mode 100644 src/Tools/padder/resources/padderexe/med2/concrete.med create mode 100644 src/Tools/padder/resources/padderexe/med2/data.txt create mode 100644 src/Tools/padder/resources/padderexe/med2/ferraill.med create mode 100644 src/Tools/padder/resources/padderexe/med2/ferrtran.med create mode 100755 src/Tools/padder/resources/padderexe/med2/padder.exe create mode 100644 src/Tools/padder/resources/padderexe/med3/concrete.med create mode 100644 src/Tools/padder/resources/padderexe/med3/data.txt create mode 100644 src/Tools/padder/resources/padderexe/med3/ferraill.med create mode 100755 src/Tools/padder/resources/padderexe/med3/padder.exe create mode 100755 src/Tools/padder/resources/padderexe/padder.sh create mode 100644 src/Tools/padder/resources/padderexe/particules.png create mode 100644 src/Tools/padder/spadderpy/Makefile.am create mode 100644 src/Tools/padder/spadderpy/__init__.py create mode 100644 src/Tools/padder/spadderpy/configreader.py create mode 100644 src/Tools/padder/spadderpy/gui/Makefile.am create mode 100644 src/Tools/padder/spadderpy/gui/__init__.py create mode 100644 src/Tools/padder/spadderpy/gui/addinput.png create mode 100644 src/Tools/padder/spadderpy/gui/clear.png create mode 100644 src/Tools/padder/spadderpy/gui/compute.png create mode 100644 src/Tools/padder/spadderpy/gui/concrete.png create mode 100644 src/Tools/padder/spadderpy/gui/deleteinput.png create mode 100644 src/Tools/padder/spadderpy/gui/input.png create mode 100644 src/Tools/padder/spadderpy/gui/inputdata.py create mode 100644 src/Tools/padder/spadderpy/gui/inputdialog.py create mode 100644 src/Tools/padder/spadderpy/gui/inputframe.ui create mode 100644 src/Tools/padder/spadderpy/gui/parameters.png create mode 100644 src/Tools/padder/spadderpy/gui/plugindialog.py create mode 100644 src/Tools/padder/spadderpy/gui/plugindialog.ui create mode 100644 src/Tools/padder/spadderpy/gui/publish.png create mode 100644 src/Tools/padder/spadderpy/gui/refresh.png create mode 100644 src/Tools/padder/spadderpy/gui/select.png create mode 100644 src/Tools/padder/spadderpy/gui/steelbar.png create mode 100644 src/Tools/padder/spadderpy/padder.cfg.in create mode 100644 src/Tools/padder/spadderpy/plugin/Makefile.am create mode 100644 src/Tools/padder/spadderpy/plugin/envPlugins.sh.in create mode 100644 src/Tools/padder/spadderpy/plugin/spadderPlugin.py create mode 100644 src/Tools/padder/unittests/Makefile.am create mode 100644 src/Tools/padder/unittests/__init__.py create mode 100644 src/Tools/padder/unittests/autotest.sh.in create mode 100644 src/Tools/padder/unittests/usecase_meshJobManager.py create mode 100644 src/Tools/padder/unittests/usecase_spadderPluginTester.py create mode 100644 src/Tools/smesh_plugins.py diff --git a/Makefile.am b/Makefile.am index 507a6f312..c887e3f82 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # -* Makefile *- # Author : Patrick GOLDBRONN (CEA) # Date : 28/06/2001 @@ -28,8 +26,8 @@ include $(top_srcdir)/adm_local/unix/make_common_starter.am if SMESH_ENABLE_GUI ACLOCAL_AMFLAGS = -I adm_local/unix/config_files \ - -I ${KERNEL_ROOT_DIR}/salome_adm/unix/config_files \ -I ${GUI_ROOT_DIR}/adm_local/unix/config_files \ + -I ${KERNEL_ROOT_DIR}/salome_adm/unix/config_files \ -I ${MED_ROOT_DIR}/adm_local/unix/config_files \ -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files else !SMESH_ENABLE_GUI @@ -39,17 +37,19 @@ else !SMESH_ENABLE_GUI -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files endif -SUBDIRS = idl adm_local resources src doc bin +SUBDIRS = idl adm_local resources src bin doc -DIST_SUBDIRS = idl adm_local resources src doc bin +DIST_SUBDIRS = idl adm_local resources src bin doc -DISTCLEANFILES = a.out aclocal.m4 configure +DISTCLEANFILES = a.out aclocal.m4 configure local-install.sh hack_libtool salomeinclude_DATA = SMESH_version.h EXTRA_DIST += \ build_configure \ clean_configure \ + build_cmake \ + build_cmake.bat \ LICENCE dist-hook: diff --git a/SMESH_version.h.in b/SMESH_version.h.in index d9b835afa..76a669881 100644 --- a/SMESH_version.h.in +++ b/SMESH_version.h.in @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_version.h // Author : Vadim SANDLER // Module : SALOME @@ -32,5 +33,6 @@ #define SMESH_VERSION_STR "@VERSION@" #define SMESH_VERSION @XVERSION@ +#define SMESH_DEVELOPMENT @VERSION_DEV@ #endif // __SMESH_VERSION_H__ diff --git a/adm_local/Makefile.am b/adm_local/Makefile.am index f35273fac..6d9ac170c 100644 --- a/adm_local/Makefile.am +++ b/adm_local/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + include $(top_srcdir)/adm_local/unix/make_common_starter.am -SUBDIRS = unix +SUBDIRS = unix cmake_files diff --git a/adm_local/cmake_files/FindSMESH.cmake b/adm_local/cmake_files/FindSMESH.cmake new file mode 100644 index 000000000..edcba9d22 --- /dev/null +++ b/adm_local/cmake_files/FindSMESH.cmake @@ -0,0 +1,42 @@ +# Copyright (C) 2007-2012 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 +# + +SET(SMESH_CXXFLAGS -I${SMESH_ROOT_DIR}/include/salome) + +FIND_LIBRARY(GeomSelectionTools GeomSelectionTools ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MEFISTO2D MEFISTO2D ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MeshDriverDAT MeshDriverDAT ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MeshDriverMED MeshDriverMED ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MeshDriver MeshDriver ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MeshDriverSTL MeshDriverSTL ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(MeshDriverUNV MeshDriverUNV ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SalomeIDLSMESH SalomeIDLSMESH ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMDS SMDS ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHClient SMESHClient ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHControls SMESHControls ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHDS SMESHDS ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHEngine SMESHEngine ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHFiltersSelection SMESHFiltersSelection ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHimpl SMESHimpl ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHObject SMESHObject ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESH SMESH ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(StdMeshersEngine StdMeshersEngine ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(StdMeshersGUI StdMeshersGUI ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(StdMeshers StdMeshers ${SMESH_ROOT_DIR}/lib/salome) +FIND_LIBRARY(SMESHUtils SMESHUtils ${SMESH_ROOT_DIR}/lib/salome) diff --git a/adm_local/cmake_files/Makefile.am b/adm_local/cmake_files/Makefile.am new file mode 100644 index 000000000..59484d20f --- /dev/null +++ b/adm_local/cmake_files/Makefile.am @@ -0,0 +1,25 @@ +# Copyright (C) 2007-2012 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 + +admlocal_cmakedir = $(admlocaldir)/cmake_files + +dist_admlocal_cmake_DATA = \ +FindSMESH.cmake diff --git a/adm_local/unix/Makefile.am b/adm_local/unix/Makefile.am index 5f6d5f753..0d5ee76b8 100644 --- a/adm_local/unix/Makefile.am +++ b/adm_local/unix/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + include $(top_srcdir)/adm_local/unix/make_common_starter.am SUBDIRS = config_files diff --git a/adm_local/unix/config_files/Makefile.am b/adm_local/unix/config_files/Makefile.am index 0ed70f5f8..938083baa 100644 --- a/adm_local/unix/config_files/Makefile.am +++ b/adm_local/unix/config_files/Makefile.am @@ -1,27 +1,24 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + include $(top_srcdir)/adm_local/unix/make_common_starter.am dist_admlocalm4_DATA = \ check_SMESH.m4 \ - check_f77.m4 \ - check_Platform.m4 + check_Platform.m4 diff --git a/adm_local/unix/config_files/check_Platform.m4 b/adm_local/unix/config_files/check_Platform.m4 index ff6606562..93c7267e7 100755 --- a/adm_local/unix/config_files/check_Platform.m4 +++ b/adm_local/unix/config_files/check_Platform.m4 @@ -1,24 +1,25 @@ -dnl Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE dnl -dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS dnl -dnl This library is free software; you can redistribute it and/or -dnl modify it under the terms of the GNU Lesser General Public -dnl License as published by the Free Software Foundation; either -dnl version 2.1 of the License. +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. dnl -dnl This library is distributed in the hope that it will be useful, -dnl but WITHOUT ANY WARRANTY; without even the implied warranty of -dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -dnl Lesser General Public License for more details. +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. dnl -dnl You should have received a copy of the GNU Lesser General Public -dnl License along with this library; if not, write to the Free Software -dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA dnl -dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com dnl + AC_DEFUN([CHECK_PLATFORM],[ AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_CPP])dnl diff --git a/adm_local/unix/config_files/check_SMESH.m4 b/adm_local/unix/config_files/check_SMESH.m4 index 4c0cdb6e4..9096571c1 100644 --- a/adm_local/unix/config_files/check_SMESH.m4 +++ b/adm_local/unix/config_files/check_SMESH.m4 @@ -1,24 +1,25 @@ -dnl Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE dnl -dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS dnl -dnl This library is free software; you can redistribute it and/or -dnl modify it under the terms of the GNU Lesser General Public -dnl License as published by the Free Software Foundation; either -dnl version 2.1 of the License. +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. dnl -dnl This library is distributed in the hope that it will be useful, -dnl but WITHOUT ANY WARRANTY; without even the implied warranty of -dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -dnl Lesser General Public License for more details. +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. dnl -dnl You should have received a copy of the GNU Lesser General Public -dnl License along with this library; if not, write to the Free Software -dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA dnl -dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com dnl + # Check availability of SMesh binary distribution # # Author : Nicolas REJNERI (OPEN CASCADE, 2003) diff --git a/adm_local/unix/config_files/check_cgal.m4 b/adm_local/unix/config_files/check_cgal.m4 new file mode 100644 index 000000000..2ec433615 --- /dev/null +++ b/adm_local/unix/config_files/check_cgal.m4 @@ -0,0 +1,101 @@ +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +dnl +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl + +AC_DEFUN([CHECK_CGAL],[ +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([CHECK_BOOST]) + +AC_CHECKING(for CGAL) + +AC_ARG_WITH(cgal, + [ --with-cgal=DIR root directory path to CGAL installation ], + [CGALHOME="$withval" + AC_MSG_RESULT("select $withval as path to CGAL") + ]) + +AC_SUBST(CGAL_INCLUDES) +AC_SUBST(CGAL_LIBS) +AC_SUBST(CGALHOME) + +CGAL_INCLUDES="" +CGAL_LIBS="" + +cgal_ok=no + +LOCAL_INCLUDES="" +LOCAL_LIBS="-lCGAL" + +if test "x$CGALHOME" != "xno"; then + if test "x$CGALHOME" == "xyes"; then + CGALHOME="" + fi + if test -z $CGALHOME + then + AC_MSG_WARN(undefined CGALHOME variable which specify CGAL library installation directory) + AC_PATH_PROG(BINDIR, cgal_create_cmake_script) + if test "x$BINDIR" != "x" ; then + CGALHOME=$BINDIR + CGALHOME=`echo ${CGALHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + CGALHOME=`echo ${CGALHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + fi + fi + if test ! -z $CGALHOME + then + cgal_ok=yes + LOCAL_INCLUDES="-I$CGALHOME/include" + if test "x$CGALHOME" != "x/usr"; then + LOCAL_LIBS="-L$CGALHOME/lib $LOCAL_LIBS" + fi + fi + +dnl check cgallib header + + # CPPFLAGS_old=$CPPFLAGS + # CPPFLAGS="$CPPFLAGS $LOCAL_INCLUDES -I$BOOSTDIR/include" + + # AC_CHECK_HEADER(algorithm.h,cgal_ok=yes ,cgal_ok=no) + + # CPPFLAGS=$CPPFLAGS_old +fi + +# if test "x$cgal_ok" = "xyes" +# then +# dnl check cgal library +# LIBS_old="$LIBS" +# LIBS="$LIBS $LOCAL_LIBS" +# AC_CHECK_LIB(cgns,cg_open,cgns_ok=yes,cgns_ok=no) +# LIBS="$LIBS_old" +# fi + +if test "x$cgal_ok" = "xyes" +then + CGAL_LIBS="$LOCAL_LIBS" + CGAL_INCLUDES="$LOCAL_INCLUDES" + CPPFLAGS="-DWITH_CGAL $CPPFLAGS" +fi + +AC_MSG_RESULT(for CGAL: $cgal_ok) + +AM_CONDITIONAL(WITH_CGAL, [test x"$cgal_ok" = xyes]) + +])dnl diff --git a/adm_local/unix/config_files/check_cgns.m4 b/adm_local/unix/config_files/check_cgns.m4 new file mode 100644 index 000000000..4e25e27c6 --- /dev/null +++ b/adm_local/unix/config_files/check_cgns.m4 @@ -0,0 +1,103 @@ +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +dnl +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl + +AC_DEFUN([CHECK_CGNS],[ +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([CHECK_HDF5])dnl + +AC_CHECKING(for CGNS) + +AC_ARG_WITH(cgns, + [ --with-cgns=DIR root directory path to CGNS installation ], + [CGNSHOME="$withval" + AC_MSG_RESULT("select $withval as path to CGNS") + ]) + +AC_SUBST(CGNS_INCLUDES) +AC_SUBST(CGNS_LIBS) + +CGNS_INCLUDES="" +CGNS_LIBS="" + +cgns_ok=no + +LOCAL_INCLUDES="" +LOCAL_LIBS="-lcgns $HDF5_LIBS" + +if test "x$CGNSHOME" != "xno"; then + if test "x$CGNSHOME" == "xyes"; then + CGNSHOME="" + fi + if test -z $CGNSHOME + then + AC_MSG_WARN(undefined CGNSHOME variable which specify CGNS library installation directory) + AC_PATH_PROG(BINDIR, cgnsversion) + if test "x$BINDIR" != "x" ; then + CGNSHOME=$BINDIR + CGNSHOME=`echo ${CGNSHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + CGNSHOME=`echo ${CGNSHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + fi + fi + if test ! -z $CGNSHOME + then + LOCAL_INCLUDES="-I$CGNSHOME/include" + if test "x$CGNSHOME" != "x/usr"; then + LOCAL_LIBS="-L$CGNSHOME/lib $LOCAL_LIBS" + fi + fi + +dnl check cgnslib header + + CPPFLAGS_old=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LOCAL_INCLUDES" + + AC_CHECK_HEADER(cgnslib.h,cgns_ok=yes ,cgns_ok=no) + + CPPFLAGS=$CPPFLAGS_old +fi + +if test "x$cgns_ok" = "xyes" +then + +dnl check cgns library + + LIBS_old="$LIBS" + LIBS="$LIBS $LOCAL_LIBS" + AC_CHECK_LIB(cgns,cg_open,cgns_ok=yes,cgns_ok=no) + + LIBS="$LIBS_old" + +fi + +if test "x$cgns_ok" = "xyes" +then + CGNS_LIBS="$LOCAL_LIBS" + CGNS_INCLUDES="$LOCAL_INCLUDES" + CPPFLAGS="-DWITH_CGNS $CPPFLAGS" +fi + +AC_MSG_RESULT(for CGNS: $cgns_ok) + +AM_CONDITIONAL(WITH_CGNS, [test x"$cgns_ok" = xyes]) + +])dnl diff --git a/adm_local/unix/config_files/check_f77.m4 b/adm_local/unix/config_files/check_f77.m4 index e03c6c165..64d3288ab 100644 --- a/adm_local/unix/config_files/check_f77.m4 +++ b/adm_local/unix/config_files/check_f77.m4 @@ -1,23 +1,23 @@ -dnl Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE dnl -dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS dnl -dnl This library is free software; you can redistribute it and/or -dnl modify it under the terms of the GNU Lesser General Public -dnl License as published by the Free Software Foundation; either -dnl version 2.1 of the License. +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. dnl -dnl This library is distributed in the hope that it will be useful, -dnl but WITHOUT ANY WARRANTY; without even the implied warranty of -dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -dnl Lesser General Public License for more details. +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. dnl -dnl You should have received a copy of the GNU Lesser General Public -dnl License along with this library; if not, write to the Free Software -dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA dnl -dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com dnl AC_DEFUN([CHECK_F77],[ diff --git a/adm_local/unix/config_files/check_padder.m4 b/adm_local/unix/config_files/check_padder.m4 new file mode 100644 index 000000000..ab30fecab --- /dev/null +++ b/adm_local/unix/config_files/check_padder.m4 @@ -0,0 +1,69 @@ +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +dnl +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl + +AC_DEFUN([CHECK_PADDER],[ +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([CHECK_CGAL]) + +AC_CHECKING(for PADDER) + +padder_ok=no + +AC_ARG_WITH(padder, + [ --with-padder=DIR root directory path to PADDER installation ], + [PADDERHOME="$withval" + AC_MSG_RESULT("select $withval as path to PADDER") + ]) + +if test "x$PADDERHOME" != "xno"; then + if test "x$PADDERHOME" == "xyes"; then + PADDERHOME="" + fi + if test -z $PADDERHOME + then + AC_MSG_WARN(undefined PADDERHOME variable which specify PADDER installation directory) + AC_PATH_PROG(BINDIR, padder.exe) + if test "x$BINDIR" != "x" ; then + PADDERHOME=$BINDIR + PADDERHOME=`echo ${PADDERHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + PADDERHOME=`echo ${PADDERHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + fi + fi + if test ! -z $PADDERHOME + then + AC_CHECK_FILE($PADDERHOME/padder.env,padder_ok=yes,padder_ok=no) + fi +fi + +if test "x$padder_ok" = "xyes" +then + CPPFLAGS="-DWITH_PADDER $CPPFLAGS" +fi + +AC_SUBST(PADDERHOME) + +AC_MSG_RESULT(for PADDER: $padder_ok) + +AM_CONDITIONAL(WITH_PADDER, [test x"$padder_ok" = xyes]) + +])dnl diff --git a/adm_local/unix/config_files/check_qwt.m4 b/adm_local/unix/config_files/check_qwt.m4 new file mode 100644 index 000000000..b15142068 --- /dev/null +++ b/adm_local/unix/config_files/check_qwt.m4 @@ -0,0 +1,187 @@ +dnl Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +dnl +dnl Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +dnl CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +dnl +dnl This library is free software; you can redistribute it and/or +dnl modify it under the terms of the GNU Lesser General Public +dnl License as published by the Free Software Foundation; either +dnl version 2.1 of the License. +dnl +dnl This library is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl Lesser General Public License for more details. +dnl +dnl You should have received a copy of the GNU Lesser General Public +dnl License along with this library; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +dnl +dnl See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +dnl +AC_DEFUN([CHECK_QWT],[ +AC_REQUIRE([CHECK_QT])dnl +AC_REQUIRE([AC_LINKER_OPTIONS])dnl + +AC_CHECKING(for qwt) + +AC_LANG_SAVE +AC_LANG_CPLUSPLUS + +qwt_ok=yes + +dnl where is qwt ? + +AC_ARG_WITH(qwt, + [ --with-qwt=DIR directory path to QWT installation ], + [QWTHOME="$withval" + AC_MSG_RESULT("select $withval as path to QWT") + ]) + +AC_ARG_WITH(qwt_inc, + [ --with-qwt_inc=DIR directory path to QWT includes ], + [QWT_INCDIR="$withval" + AC_MSG_RESULT("select $withval as path to QWT includes") + ]) + +libqwt_name=qwt +if test -z $QWTHOME; then + AC_MSG_RESULT(QWTHOME not defined) + AC_MSG_NOTICE(Trying native Qwt...) + exist_ok=no + if test "x$exist_ok" = "xno"; then + for d in /usr /usr/local ; do + for extension in qwt-qt4 qwt; do + AC_CHECK_FILE(${d}/lib${LIB_LOCATION_SUFFIX}/lib${extension}.so,exist_ok=yes,exist_ok=no) + if test "x$exist_ok" = "xyes"; then + QWTHOME=$d + AC_MSG_RESULT(lib${extension}.so detected in $d/lib) + libqwt_name=${extension} + dnl break, libqwt-qt4.so is choosen before libqwt.so since it is surely the Qt4 version. + break + fi + done + if test "x$exist_ok" = "xyes"; then + break + fi + done + fi + if test "x$exist_ok" = "xno"; then + for d in `echo $LD_LIBRARY_PATH | sed -e "s/:/ /g"` ; do + if test -f $d/libqwt.so ; then + AC_MSG_RESULT(libqwt.so detected in $d) + QWTHOME=$d + QWTHOME=`echo ${QWTHOME} | sed -e "s,[[^/]]*$,,;s,/$,,;s,^$,.,"` + exist_ok=yes + break + fi + done + fi + if test "x$exist_ok" = "xyes"; then + if test -z $QWT_INCDIR; then + QWT_INCDIR=$QWTHOME"/include/qwt-qt4" + if test ! -f $QWT_INCDIR/qwt.h ; then + QWT_INCDIR=/usr/include/qwt + fi + if test ! -f $QWT_INCDIR/qwt.h ; then + QWT_INCDIR=$QWTHOME"/include" + fi + if test ! -f $QWT_INCDIR/qwt.h ; then + QWT_INCDIR=/usr/lib/qt4/include/qwt + fi + if test ! -f $QWT_INCDIR/qwt.h ; then + QWT_INCDIR=/usr/include/qwt-qt4 + fi + fi + else + qwt_ok=no + fi +else + AC_MSG_NOTICE(Trying Qwt from $QWTHOME ...) + if test -z $QWT_INCDIR; then + QWT_INCDIR="$QWTHOME/include" + fi +fi + +if test "x$qwt_ok" = xno -o ! -d "$QWTHOME" ; then + AC_MSG_RESULT(no) + AC_MSG_WARN(qwt not found) + qwt_ok=no +else + CPPFLAGS_old=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $QT_INCLUDES -I$QWT_INCDIR" + + AC_CHECK_HEADER(qwt.h,qwt_ok=yes,qwt_ok=no) + CPPFLAGS=$CPPFLAGS_old + + AC_MSG_CHECKING(include of qwt headers) + + if test "x$qwt_ok" = xno ; then + AC_MSG_RESULT(no) + AC_MSG_WARN(qwt not found) + else + AC_MSG_RESULT(yes) + QWT_INCLUDES=-I$QWT_INCDIR + fi + + # + # test Qwt libraries + # + if test "x$qwt_ok" = "xyes" ; then + AC_MSG_CHECKING(linking qwt library) + + LIBS_old=$LIBS + LIBS="$LIBS $QT_LIBS" + if test "x$QWTHOME" = "x/usr" ; then + LIBS="$LIBS -l${libqwt_name}" + else + LIBS="$LIBS -L$QWTHOME/lib -l${libqwt_name}" + fi + + CXXFLAGS_old=$CXXFLAGS + CXXFLAGS="$CXXFLAGS $QT_INCLUDES $QWT_INCLUDES" + + AC_CACHE_VAL(salome_cv_lib_qwt,[ + AC_TRY_LINK( +#include +#include +, int n; + char **s; + QApplication a(n, s); + QwtPlot p; + p.resize( 600, 400 ); + p.show(); + a.exec();, + eval "salome_cv_lib_qwt=yes",eval "salome_cv_lib_qwt=no") + ]) + qwt_ok="$salome_cv_lib_qwt" + + if test "x$qwt_ok" = "xno" ; then + AC_MSG_RESULT(unable to link with qwt library) + AC_MSG_RESULT(QWTHOME environment variable may be wrong) + else + AC_MSG_RESULT(yes) + if test "x$QWTHOME" = "x/usr" ; then + QWT_LIBS=" -l${libqwt_name}" + else + QWT_LIBS="-L$QWTHOME/lib -l${libqwt_name}" + fi + fi + + LIBS=$LIBS_old + CXXFLAGS=$CXXFLAGS_old + fi +fi + +AC_SUBST(QWT_INCLUDES) +AC_SUBST(QWT_LIBS) + +AC_LANG_RESTORE + +AC_MSG_RESULT(for qwt: $qwt_ok) + +# Save cache +AC_CACHE_SAVE + +])dnl +dnl diff --git a/adm_local/unix/make_common_starter.am b/adm_local/unix/make_common_starter.am index 36ad8c3a7..295f19f81 100644 --- a/adm_local/unix/make_common_starter.am +++ b/adm_local/unix/make_common_starter.am @@ -1,24 +1,30 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 # -# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com + +# ============================================================ +# The following is to avoid PACKAGE_... env variable +# redefinition compilation warnings +# ============================================================ # +AM_CXXFLAGS = @KERNEL_CXXFLAGS@ -include SALOMEconfig.h +AM_CPPFLAGS = @KERNEL_CXXFLAGS@ -include SALOMEconfig.h + # ============================================================ # This file defines the common definitions used in several # Makefile. This file must be included, if needed, by the file @@ -33,6 +39,12 @@ salomescriptdir = $(bindir) salomepythondir = $(pythondir)/salome salomepyexecdir = $(pyexecdir)/salome +# Root directory of the python packages of SMESH +smeshpypkgdir = $(salomepythondir)/salome/smesh + +# Directory for installing SALOME plugins files +salomepluginsdir = $(prefix)/share/salome/plugins/@MODULE_NAME@ + # Directory for installing idl files salomeidldir = $(prefix)/idl/salome diff --git a/bin/Makefile.am b/bin/Makefile.am index dc7848fe5..7e1eea667 100644 --- a/bin/Makefile.am +++ b/bin/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # -* Makefile *- # Author : Guillaume Boulant (CSSI) # Module : SMESH @@ -28,5 +26,9 @@ include $(top_srcdir)/adm_local/unix/make_common_starter.am # non-distributed files nodist_salomescript_DATA = VERSION +# python files +dist_salomescript_PYTHON = \ + smesh_setenv.py + # distributed files dist_salomescript_SCRIPTS = diff --git a/bin/VERSION.in b/bin/VERSION.in index fa49d303f..de81461c8 100755 --- a/bin/VERSION.in +++ b/bin/VERSION.in @@ -1 +1,3 @@ -THIS IS SALOME - SMESH VERSION: @VERSION@ +[SALOME SMESH] : @VERSION@ +[DEVELOPMENT] : @VERSION_DEV@ +[DESCRIPTION] : SALOME Mesh module diff --git a/bin/smesh_setenv.py b/bin/smesh_setenv.py new file mode 100644 index 000000000..a58406c1e --- /dev/null +++ b/bin/smesh_setenv.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# + +import os, sys +from setenv import add_path, get_lib_dir, salome_subdir + +# ----------------------------------------------------------------------------- + +def set_env(args): + """Add to the PATH-variables modules specific paths""" + psep = os.pathsep + python_version="python%d.%d" % sys.version_info[0:2] + + + if not os.environ.has_key("SALOME_StdMeshersResources"): + os.environ["SALOME_StdMeshersResources"] \ + = os.path.join(os.environ["SMESH_ROOT_DIR"],"share",salome_subdir,"resources","smesh") + pass + + # find plugins + plugin_list = [] + resource_path_list = [] + for env_var in os.environ.keys(): + value = os.environ[env_var] + if env_var[-9:] == "_ROOT_DIR" and value: + plugin_root = value + plugin = env_var[:-9] # plugin name may have wrong case + + # look for NAMEOFPlugin.xml file among resource files + resource_dir = os.path.join(plugin_root,"share",salome_subdir,"resources",plugin.lower()) + if not os.access( resource_dir, os.F_OK ): continue + for resource_file in os.listdir( resource_dir ): + if not resource_file.endswith( ".xml") or \ + resource_file.lower() != plugin.lower() + ".xml": + continue + # use "resources" attribute of "meshers-group" as name of plugin in a right case + from xml.dom.minidom import parse + xml_doc = parse( os.path.join( resource_dir, resource_file )) + meshers_nodes = xml_doc.getElementsByTagName("meshers-group") + if not meshers_nodes or not meshers_nodes[0].hasAttribute("resources"): continue + plugin = meshers_nodes[0].getAttribute("resources") + if plugin in plugin_list: continue + + # add paths of plugin + plugin_list.append(plugin) + if not os.environ.has_key("SALOME_"+plugin+"Resources"): + resource_path = os.path.join(plugin_root,"share",salome_subdir,"resources",plugin.lower()) + os.environ["SALOME_"+plugin+"Resources"] = resource_path + resource_path_list.append( resource_path ) + add_path(os.path.join(plugin_root,get_lib_dir(),python_version, "site-packages",salome_subdir), "PYTHONPATH") + add_path(os.path.join(plugin_root,get_lib_dir(),salome_subdir), "PYTHONPATH") + + if sys.platform == "win32": + add_path(os.path.join(plugin_root,get_lib_dir(),salome_subdir), "PATH") + add_path(os.path.join(plugin_root,"bin",salome_subdir), "PYTHONPATH") + else: + add_path(os.path.join(plugin_root,get_lib_dir(),salome_subdir), "LD_LIBRARY_PATH") + add_path(os.path.join(plugin_root,"bin",salome_subdir), "PYTHONPATH") + add_path(os.path.join(plugin_root,"bin",salome_subdir), "PATH") + pass + pass + break + plugin_list.append("StdMeshers") + os.environ["SMESH_MeshersList"] = ":".join(plugin_list) + os.environ["SalomeAppConfig"] = os.environ["SalomeAppConfig"] + psep + psep.join(resource_path_list) + diff --git a/build_cmake b/build_cmake new file mode 100755 index 000000000..01bb2ac7f --- /dev/null +++ b/build_cmake @@ -0,0 +1,27 @@ +#!/bin/sh +# Copyright (C) 2007-2012 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 +# + +CURRENT_DIR=`pwd` +CONF_DIR=`echo $0 | sed -e "s,[^/]*$,,;s,/$,,;s,^$,.,"` +cd ${CONF_DIR} +python $KERNEL_ROOT_DIR/salome_adm/cmake_files/am2cmake.py --smesh +status=$? +cd ${CURRENT_DIR} +exit $status diff --git a/build_cmake.bat b/build_cmake.bat new file mode 100644 index 000000000..890adcdff --- /dev/null +++ b/build_cmake.bat @@ -0,0 +1,20 @@ +@REM Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +@REM +@REM This library is free software; you can redistribute it and/or +@REM modify it under the terms of the GNU Lesser General Public +@REM License as published by the Free Software Foundation; either +@REM version 2.1 of the License. +@REM +@REM This library is distributed in the hope that it will be useful, +@REM but WITHOUT ANY WARRANTY; without even the implied warranty of +@REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +@REM Lesser General Public License for more details. +@REM +@REM You should have received a copy of the GNU Lesser General Public +@REM License along with this library; if not, write to the Free Software +@REM Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +@REM +@REM See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +@REM + +%PYTHONBIN% %KERNEL_ROOT_DIR%\salome_adm\cmake_files\am2cmake.py --smesh diff --git a/build_configure b/build_configure index d135d9e8d..ce47d8056 100755 --- a/build_configure +++ b/build_configure @@ -1,25 +1,26 @@ #!/bin/bash -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tool for updating list of .in file for the SALOME project # and regenerating configure script # Author : Marc Tajchman - CEA @@ -29,7 +30,6 @@ # ORIG_DIR=`pwd` CONF_DIR=`echo $0 | sed -e "s,[^/]*$,,;s,/$,,;s,^$,.,"` -SMESH_WITH_GUI="yes" ######################################################################## # Test if the KERNEL_ROOT_DIR is set correctly @@ -46,28 +46,6 @@ fi # exit #fi -for option -do - case $option in - -with-gui | --with-gui) - SMESH_WITH_GUI="yes" - break;; - -without-gui | --without-gui | -with-gui=no | --with-gui=no) - SMESH_WITH_GUI="no" - break;; - esac -done - -######################################################################## -# Test if the GUI_ROOT_DIR is set correctly - -if test ${SMESH_WITH_GUI} = yes; then - if test ! -d "${GUI_ROOT_DIR}"; then - echo "failed : GUI_ROOT_DIR variable is not correct !" - exit - fi -fi - ######################################################################## # Test if the MED_ROOT_DIR is set correctly @@ -89,61 +67,41 @@ cd ${CONF_DIR} ABS_CONF_DIR=`pwd` ####################################################################### -# Update configure.ac script: to set SMESH_WITH_GUI variable -sed -e s/SMESH_WITH_GUI=[a-z]*/SMESH_WITH_GUI=${SMESH_WITH_GUI}/g configure.ac > configure.tmp -mv -f configure.tmp configure.ac - -mkdir -p salome_adm/unix/config_files -#cp -f ${KERNEL_ROOT_DIR}/salome_adm/unix/config_files/* salome_adm/unix/config_files -#cp -f ${KERNEL_ROOT_DIR}/salome_adm/unix/pythonbe.py salome_adm/unix - -cp -f ${KERNEL_ROOT_DIR}/salome_adm/unix/SALOMEconfig.h.in salome_adm/unix - -#cp -f ${GUI_ROOT_DIR}/adm_local/unix/config_files/* salome_adm/unix/config_files -#cp -f ${MED_ROOT_DIR}/adm_local/unix/config_files/* salome_adm/unix/config_files -#cp -f ${GEOM_ROOT_DIR}/adm_local/unix/config_files/* salome_adm/unix/config_files - -# remove KERNEL deprecated configure files -#for deprecated in ac_cc_warnings.m4 ac_cxx_partial_specialization.m4 \ -# check_mico.m4 config.guess ltmain.sh ac_cxx_bool.m4 ltconfig ac_cxx_typename.m4 \ -# check_pthreads.m4 config.sub libtool.m4 ac_cxx_mutable.m4 missing -# do -# rm -f salome_adm/unix/config_files/${deprecated} -# done - # ____________________________________________________________________ # aclocal creates the aclocal.m4 file from the standard macro and the -# custom macro embedded in the directory salome_adm/unix/config_files -# and KERNEL config_files directory. +# custom macro embedded in the directory adm_local/unix/config_files, +# KERNEL salome_adm/unix/config_files, GEOM and MED adm_local/unix/config_files +# directories. # output: # aclocal.m4 # autom4te.cache (directory) echo "====================================================== aclocal" -if test ${SMESH_WITH_GUI} = yes; then +if test -d "${GUI_ROOT_DIR}"; then aclocal -I adm_local/unix/config_files \ -I ${KERNEL_ROOT_DIR}/salome_adm/unix/config_files \ - -I ${GUI_ROOT_DIR}/adm_local/unix/config_files \ - -I ${MED_ROOT_DIR}/adm_local/unix/config_files \ - -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files || exit 1 + -I ${GUI_ROOT_DIR}/adm_local/unix/config_files \ + -I ${MED_ROOT_DIR}/adm_local/unix/config_files \ + -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files || exit 1 else aclocal -I adm_local/unix/config_files \ -I ${KERNEL_ROOT_DIR}/salome_adm/unix/config_files \ - -I ${MED_ROOT_DIR}/adm_local/unix/config_files \ - -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files || exit 1 + -I ${MED_ROOT_DIR}/adm_local/unix/config_files \ + -I ${GEOM_ROOT_DIR}/adm_local/unix/config_files || exit 1 fi + # ____________________________________________________________________ # libtoolize creates some configuration files (ltmain.sh, # config.guess and config.sub). It only depends on the libtool # version. The files are created in the directory specified with the # AC_CONFIG_AUX_DIR() tag (see configure.ac). # output: -# salome_adm/unix/config_files/config.guess -# salome_adm/unix/config_files/config.sub -# salome_adm/unix/config_files/ltmain.sh -#echo "====================================================== libtoolize" +# adm_local/unix/config_files/config.guess +# adm_local/unix/config_files/config.sub +# adm_local/unix/config_files/ltmain.sh +echo "==================================================== libtoolize" libtoolize --force --copy --automake || exit 1 @@ -163,11 +121,11 @@ autoconf # AC_CONFIG_AUX_DIR() tag (see configure.ac). This step also # creates the Makefile.in files from the Makefile.am files. # output: -# salome_adm/unix/config_files/compile -# salome_adm/unix/config_files/depcomp -# salome_adm/unix/config_files/install-sh -# salome_adm/unix/config_files/missing -# salome_adm/unix/config_files/py-compile +# adm_local/unix/config_files/compile +# adm_local/unix/config_files/depcomp +# adm_local/unix/config_files/install-sh +# adm_local/unix/config_files/missing +# adm_local/unix/config_files/py-compile # Makefile.in (from Makefile.am) echo "====================================================== automake" diff --git a/clean_configure b/clean_configure index f57f7b3bf..09bfbd7bd 100755 --- a/clean_configure +++ b/clean_configure @@ -1,25 +1,23 @@ #!/bin/sh -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + rm -rf autom4te.cache aclocal.m4 configure make_config find . -name "*~" -print -exec rm {} \; find . -name "*.pyc" -print -exec rm {} \; @@ -30,6 +28,8 @@ find bin -name Makefile.in | xargs rm -f find doc -name Makefile.in | xargs rm -f find idl -name Makefile.in | xargs rm -f find resources -name Makefile.in | xargs rm -f -find salome_adm -name Makefile.in | xargs rm -f +find adm_local -name Makefile.in | xargs rm -f find src -name Makefile.in | xargs rm -f rm -f Makefile.in +cd adm_local/unix/config_files +rm -f config.* depcomp install-sh l*.m4 ltmain.sh missing py-compile diff --git a/configure.ac b/configure.ac index 4aeb847b6..65f1a6852 100644 --- a/configure.ac +++ b/configure.ac @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Author : Marc Tajchman (CEA) # Date : 28/06/2001 # Modified by : Patrick GOLDBRONN (CEA) @@ -26,14 +24,16 @@ # Modified by : Alexander BORODIN (OCN) - autotools usage # Created from configure.in.base # -AC_INIT([Salome2 Project SMESH module], [5.1.0], [webmaster.salome@opencascade.com], [SalomeSMESH]) -AC_CONFIG_AUX_DIR(salome_adm/unix/config_files) +AC_INIT([Salome2 Project SMESH module], [6.5.0], [webmaster.salome@opencascade.com], [SalomeSMESH]) +AC_CONFIG_AUX_DIR(adm_local/unix/config_files) AC_CANONICAL_HOST AC_CANONICAL_TARGET -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([-Wno-portability]) XVERSION=`echo $VERSION | awk -F. '{printf("0x%02x%02x%02x",$1,$2,$3)}'` AC_SUBST(XVERSION) +VERSION_DEV=1 +AC_SUBST(VERSION_DEV) # set up MODULE_NAME variable for dynamic construction of directories (resources, etc.) MODULE_NAME=smesh @@ -81,12 +81,13 @@ echo AC_PROG_MAKE_SET AC_PROG_INSTALL +AC_LOCAL_INSTALL dnl dnl libtool macro check for CC, LD, NM, LN_S, RANLIB, STRIP + pour les librairies dynamiques ! echo echo --------------------------------------------- -echo Coniguring production +echo Configuring production echo --------------------------------------------- echo AC_ENABLE_DEBUG(yes) @@ -107,7 +108,7 @@ dnl Fix up the INSTALL macro if it s a relative path. We want the dnl full-path to the binary instead. case "$INSTALL" in *install-sh*) - INSTALL='\${ROOT_BUILDDIR}'/salome_adm/unix/config_files/install-sh + INSTALL='\${ROOT_BUILDDIR}'/adm_local/unix/config_files/install-sh ;; esac @@ -166,7 +167,15 @@ dnl testing MPICH dnl --------------------------------------------- dnl -CHECK_MPICH +dnl CHECK_MPICH + +echo +echo --------------------------------------------- +echo testing MPI +echo --------------------------------------------- +echo + +CHECK_MPI echo echo --------------------------------------------- @@ -212,10 +221,6 @@ echo ENABLE_PTHREADS -SMESH_WITH_GUI=yes - -AM_CONDITIONAL(SMESH_ENABLE_GUI, [test "${SMESH_WITH_GUI}" = "yes"]) - if test "x${GUI_DISABLE_CORBA}" != "xyes" ; then echo echo --------------------------------------------- @@ -255,8 +260,34 @@ dnl CHECK_MICO fi +echo +echo --------------------------------------------- +echo Testing GUI +echo --------------------------------------------- +echo + +CHECK_GUI_MODULE + +gui_ok=no +if test "${SalomeGUI_need}" != "no" -a "${FullGUI_ok}" = "yes" ; then + gui_ok=yes +fi + +AM_CONDITIONAL(SMESH_ENABLE_GUI, [test "${gui_ok}" = "yes"]) + +if test "${SalomeGUI_need}" == "yes"; then + if test "${FullGUI_ok}" != "yes"; then + AC_MSG_WARN(For configure SMESH module necessary full GUI!) + fi +elif test "${SalomeGUI_need}" == "auto"; then + if test "${FullGUI_ok}" != "yes"; then + AC_MSG_WARN(Full GUI not found. Build will be done without GUI!) + fi +elif test "${SalomeGUI_need}" == "no"; then + echo Build without GUI option has been chosen +fi -if test "${SMESH_WITH_GUI}" = "yes"; then +if test "${gui_ok}" = "yes"; then echo echo --------------------------------------------- echo testing openGL @@ -275,33 +306,37 @@ if test "${SMESH_WITH_GUI}" = "yes"; then echo echo --------------------------------------------- - echo testing VTK + echo testing sip echo --------------------------------------------- echo - CHECK_VTK + CHECK_SIP echo echo --------------------------------------------- - echo Testing GUI + echo testing pyqt echo --------------------------------------------- echo - CHECK_SALOME_GUI + CHECK_PYQT echo echo --------------------------------------------- - echo Testing full GUI + echo Testing qwt echo --------------------------------------------- echo - CHECK_CORBA_IN_GUI - if test "x${CORBA_IN_GUI}" != "xyes"; then - echo "failed : For configure SMESH module necessary full GUI !" - exit - fi + CHECK_QWT fi +echo +echo --------------------------------------------- +echo testing VTK +echo --------------------------------------------- +echo + +CHECK_VTK + echo echo --------------------------------------------- echo testing HDF5 @@ -310,6 +345,14 @@ echo CHECK_HDF5 +echo +echo --------------------------------------------- +echo testing MED3 +echo --------------------------------------------- +echo + +CHECK_MED3 + echo echo --------------------------------------------- echo BOOST Library @@ -326,25 +369,28 @@ echo CHECK_CAS -if test "${SMESH_WITH_GUI}" = "yes"; then - echo echo --------------------------------------------- -echo Testing qwt +echo Testing html generators echo --------------------------------------------- echo -CHECK_QWT - -fi +CHECK_HTML_GENERATORS echo echo --------------------------------------------- -echo Testing html generators +echo testing sphinx echo --------------------------------------------- echo +CHECK_SPHINX -CHECK_HTML_GENERATORS +echo +echo --------------------------------------------- +echo testing libxm +echo --------------------------------------------- +echo +dnl Check the libxml that will be required to use the SALOME launcher +CHECK_LIBXML echo echo --------------------------------------------- @@ -372,6 +418,31 @@ CHECK_MED CHECK_PLATFORM +echo +echo --------------------------------------------- +echo Testing CGNS library +echo --------------------------------------------- +echo + +CHECK_CGNS + +echo +echo --------------------------------------------- +echo Testing PADDER library +echo --------------------------------------------- +echo + +CHECK_CGAL +CHECK_PADDER + +echo +echo --------------------------------------------- +echo Testing TBB library +echo --------------------------------------------- +echo + +CHECK_TBB + echo echo --------------------------------------------- echo Summary @@ -379,12 +450,13 @@ echo --------------------------------------------- echo echo Configure -if test "${SMESH_WITH_GUI}" = "yes"; then -variables="cc_ok fortran_ok boost_ok lex_yacc_ok python_ok swig_ok threads_ok OpenGL_ok qt_ok vtk_ok hdf5_ok omniORB_ok occ_ok doxygen_ok graphviz_ok qwt_ok Kernel_ok Geom_ok Med_ok SalomeGUI_ok" -fi -if test "${SMESH_WITH_GUI}" = "no"; then -variables="cc_ok fortran_ok boost_ok lex_yacc_ok python_ok swig_ok threads_ok hdf5_ok omniORB_ok occ_ok doxygen_ok graphviz_ok Kernel_ok Geom_ok Med_ok" +if test "${gui_ok}" = "yes"; then + variables="cc_ok fortran_ok boost_ok lex_yacc_ok python_ok swig_ok threads_ok OpenGL_ok qt_ok vtk_ok hdf5_ok cgns_ok tbb_ok omniORB_ok occ_ok doxygen_ok graphviz_ok sphinx_ok qwt_ok Kernel_ok Geom_ok Med_ok gui_ok" +elif test "${SalomeGUI_need}" != "no"; then + variables="cc_ok fortran_ok boost_ok lex_yacc_ok python_ok swig_ok threads_ok vtk_ok hdf5_ok cgns_ok tbb_ok med3_ok omniORB_ok occ_ok doxygen_ok graphviz_ok sphinx_ok Kernel_ok Geom_ok Med_ok gui_ok" +else + variables="cc_ok fortran_ok boost_ok lex_yacc_ok python_ok swig_ok threads_ok vtk_ok hdf5_ok cgns_ok tbb_ok med3_ok omniORB_ok occ_ok doxygen_ok graphviz_ok sphinx_ok Kernel_ok Geom_ok Med_ok" fi for var in $variables @@ -397,6 +469,16 @@ echo echo "Default ORB : $DEFAULT_ORB" echo +echo "Optionnal products (for plugins):" +optional_vars="cgal_ok padder_ok" +for var in $optional_vars +do + printf " %10s : " `echo \$var | sed -e "s,_ok,,"` + eval echo \$$var +done + + + dnl We don t need to say when we re entering directories if we re using dnl GNU make becuase make does it for us. if test "X$GMAKE" = "Xyes"; then @@ -414,6 +496,9 @@ dnl AM_CONDITIONAL(ENABLE_OCCVIEWER, [test "$DISABLE_OCCVIEWER" = no]) dnl AM_CONDITIONAL(ENABLE_VTKVIEWER, [test "$DISABLE_VTKVIEWER" = no]) dnl AM_CONDITIONAL(ENABLE_SALOMEOBJECT, [test "$DISABLE_SALOMEOBJECT" = no]) +dnl Build with SMESH cancel compute feature +AC_DEFINE(WITH_SMESH_CANCEL_COMPUTE) + echo echo --------------------------------------------- echo generating Makefiles and configure files @@ -425,49 +510,91 @@ echo # chmod +x ./bin/salome/*; \ #]) +AC_HACK_LIBTOOL +AC_CONFIG_COMMANDS([hack_libtool],[ +sed -i "s%^CC=\"\(.*\)\"%hack_libtool (){ \n\ + $(pwd)/hack_libtool \1 \"\$[@]\" \n\ +}\n\ +CC=\"hack_libtool\"%g" libtool +sed -i "s%\(\s*\)for searchdir in \$newlib_search_path \$lib_search_path \$sys_lib_search_path \$shlib_search_path; do%\1searchdirs=\"\$newlib_search_path \$lib_search_path \$sys_lib_search_path \$shlib_search_path\"\n\1for searchdir in \$searchdirs; do%g" libtool +sed -i "s%\(\s*\)searchdirs=\"\$newlib_search_path \$lib_search_path \(.*\)\"%\1searchdirs=\"\$newlib_search_path \$lib_search_path\"\n\1sss_beg=\"\"\n\1sss_end=\"\2\"%g" libtool +sed -i "s%\(\s*\)\(for searchdir in \$searchdirs; do\)%\1for sss in \$searchdirs; do\n\1 if ! test -d \$sss; then continue; fi\n\1 ssss=\$(cd \$sss; pwd)\n\1 if test \"\$ssss\" != \"\" \&\& test -d \$ssss; then\n\1 case \$ssss in\n\1 /usr/lib | /usr/lib64 ) ;;\n\1 * ) sss_beg=\"\$sss_beg \$ssss\" ;;\n\1 esac\n\1 fi\n\1done\n\1searchdirs=\"\$sss_beg \$sss_end\"\n\1\2%g" libtool +],[]) + # This list is initiated using autoscan and must be updated manually # when adding a new file .in to manage. When you execute # autoscan, the Makefile list is generated in the output file configure.scan. # This could be helpfull to update de configuration. AC_OUTPUT([ \ - ./salome_adm/unix/SALOMEconfig.h \ - ./adm_local/Makefile \ - ./adm_local/unix/Makefile \ - ./adm_local/unix/config_files/Makefile \ - ./bin/VERSION \ - ./bin/Makefile \ - ./SMESH_version.h \ - ./doc/Makefile \ - ./doc/salome/Makefile \ - ./doc/salome/gui/Makefile \ - ./doc/salome/gui/SMESH/Makefile \ - ./doc/salome/gui/SMESH/doxyfile \ - ./doc/salome/gui/SMESH/doxyfile_py \ - ./doc/salome/tui/Makefile \ - ./doc/salome/tui/doxyfile \ - ./src/Makefile \ - ./src/Controls/Makefile \ - ./src/Driver/Makefile \ - ./src/DriverDAT/Makefile \ - ./src/DriverMED/Makefile \ - ./src/DriverSTL/Makefile \ - ./src/DriverUNV/Makefile \ - ./src/MEFISTO2/Makefile \ - ./src/OBJECT/Makefile \ - ./src/SMDS/Makefile \ - ./src/SMESH/Makefile \ - ./src/SMESHClient/Makefile \ - ./src/SMESHDS/Makefile \ - ./src/SMESHFiltersSelection/Makefile \ - ./src/SMESHGUI/Makefile \ - ./src/SMESH_I/Makefile \ - ./src/SMESH_SWIG/Makefile \ - ./src/SMESH_SWIG_WITHIHM/Makefile \ - ./src/StdMeshers/Makefile \ - ./src/StdMeshersGUI/Makefile \ - ./src/StdMeshers_I/Makefile \ - ./resources/Makefile \ - ./resources/SMESHCatalog.xml \ - ./idl/Makefile \ + adm_local/Makefile \ + adm_local/cmake_files/Makefile \ + adm_local/unix/Makefile \ + adm_local/unix/config_files/Makefile \ + bin/VERSION \ + bin/Makefile \ + SMESH_version.h \ + doc/Makefile \ + doc/docutils/Makefile \ + doc/docutils/conf.py \ + doc/salome/Makefile \ + doc/salome/gui/Makefile \ + doc/salome/gui/SMESH/Makefile \ + doc/salome/gui/SMESH/doxyfile \ + doc/salome/gui/SMESH/doxyfile_py \ + doc/salome/gui/SMESH/static/header.html \ + doc/salome/gui/SMESH/static/header_py.html \ + doc/salome/tui/Makefile \ + doc/salome/tui/doxyfile \ + doc/salome/tui/static/header.html \ + src/Makefile \ + src/Controls/Makefile \ + src/Driver/Makefile \ + src/DriverDAT/Makefile \ + src/DriverMED/Makefile \ + src/DriverSTL/Makefile \ + src/DriverUNV/Makefile \ + src/DriverCGNS/Makefile \ + src/MEFISTO2/Makefile \ + src/OBJECT/Makefile \ + src/PluginUtils/Makefile \ + src/SMDS/Makefile \ + src/SMESH/Makefile \ + src/SMESHUtils/Makefile \ + src/SMESHClient/Makefile \ + src/SMESHDS/Makefile \ + src/SMESHFiltersSelection/Makefile \ + src/SMESHGUI/Makefile \ + src/SMESH_I/Makefile \ + src/SMESH_SWIG/Makefile \ + src/SMESH_SWIG_WITHIHM/Makefile \ + src/StdMeshers/Makefile \ + src/StdMeshersGUI/Makefile \ + src/StdMeshers_I/Makefile \ + src/SMESH_PY/Makefile \ + src/Tools/Makefile \ + src/Tools/MeshCut/Makefile \ + src/Tools/padder/Makefile \ + src/Tools/padder/meshjob/Makefile \ + src/Tools/padder/meshjob/idl/Makefile \ + src/Tools/padder/meshjob/impl/Makefile \ + src/Tools/padder/spadderpy/Makefile \ + src/Tools/padder/spadderpy/padder.cfg \ + src/Tools/padder/spadderpy/gui/Makefile \ + src/Tools/padder/spadderpy/plugin/Makefile \ + src/Tools/padder/spadderpy/plugin/envPlugins.sh \ + src/Tools/padder/resources/Makefile \ + src/Tools/padder/resources/appligen/Makefile \ + src/Tools/padder/resources/appligen/appligen.sh \ + src/Tools/padder/resources/appligen/config_appli.xml \ + src/Tools/padder/resources/padderexe/Makefile \ + src/Tools/padder/resources/padderexe/envPadder.sh \ + src/Tools/padder/unittests/Makefile \ + src/Tools/padder/unittests/autotest.sh \ + src/Tools/padder/doc/Makefile \ + src/Tools/padder/doc/doxyfile \ + resources/Makefile \ + resources/SMESHCatalog.xml \ + resources/SalomeApp.xml \ + idl/Makefile \ Makefile ]) diff --git a/doc/Makefile.am b/doc/Makefile.am index 1902d9d21..6d2dfb201 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # -* Makefile *- # Author : Patrick GOLDBRONN (CEA) # Date : 30/11/2001 @@ -26,7 +24,8 @@ # $Header$ # source path # -SUBDIRS = salome +SUBDIRS = salome docutils +#SUBDIRS = salome 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 index 000000000..1775115ee --- /dev/null +++ b/doc/docutils/Makefile.am @@ -0,0 +1,97 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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)/tui/SMESH/docutils + +RSTFILES = \ + index.rst \ + overview.rst \ + docapi.rst + +EXTRA_DIST += $(RSTFILES) + +SPHINXOPTS = +SOURCEDIR = $(srcdir) +SPHINXBUILD = sphinx-build +PAPEROPT_a4 = -D latex_paper_size=a4 +ALLSPHINXOPTS = -d doctrees $(PAPEROPT_a4) $(SPHINXOPTS) $(SOURCEDIR) + +if SMESH_ENABLE_GUI +SPHINX_PYTHONPATH = $(prefix)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(prefix)/lib64/python$(PYTHON_VERSION)/site-packages/salome:$(GUI_ROOT_DIR)/lib/salome:$(GUI_ROOT_DIR)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(GUI_ROOT_DIR)/lib64/python$(PYTHON_VERSION)/site-packages/salome:$(KERNEL_ROOT_DIR)/bin/salome:$(KERNEL_ROOT_DIR)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(KERNEL_ROOT_DIR)/lib64/python$(PYTHON_VERSION)/site-packages/salome:$(OMNIORB_ROOT)/lib/python$(PYTHON_VERSION)/site-packages:$(OMNIORB_ROOT)/lib64/python$(PYTHON_VERSION)/site-packages +else !SMESH_ENABLE_GUI +SPHINX_PYTHONPATH = $(prefix)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(prefix)/lib64/python$(PYTHON_VERSION)/site-packages/salome:$(KERNEL_ROOT_DIR)/bin/salome:$(KERNEL_ROOT_DIR)/lib/python$(PYTHON_VERSION)/site-packages/salome:$(KERNEL_ROOT_DIR)/lib64/python$(PYTHON_VERSION)/site-packages/salome:$(OMNIORB_ROOT)/lib/python$(PYTHON_VERSION)/site-packages:$(OMNIORB_ROOT)/lib64/python$(PYTHON_VERSION)/site-packages +endif + +if SMESH_ENABLE_GUI +SPHINX_LD_LIBRARY_PATH = $(GUI_ROOT_DIR)/lib/salome:$(KERNEL_ROOT_DIR)/lib/salome:$(OMNIORB_ROOT)/lib +else !SMESH_ENABLE_GUI +SPHINX_LD_LIBRARY_PATH = $(KERNEL_ROOT_DIR)/lib/salome:$(OMNIORB_ROOT)/lib +endif + + +.PHONY: latex + +if SPHINX_IS_OK + +html/index.html:$(RSTFILES) + mkdir -p html doctrees + PYTHONPATH=$(SPHINX_PYTHONPATH):${PYTHONPATH} \ + LD_LIBRARY_PATH=$(SPHINX_LD_LIBRARY_PATH):${LD_LIBRARY_PATH} \ + $(SPHINXBUILD) -c $(top_builddir)/doc/docutils -W -b html $(ALLSPHINXOPTS) html + @echo + @echo "Build finished. The HTML pages are in html." + +else + +html/index.html: + @echo "Documentation for Python package not built. Sphinx was not present at configure time." + +endif + +latex: + mkdir -p latex doctrees + PYTHONPATH=$(SPHINX_PYTHONPATH):${PYTHONPATH} \ + LD_LIBRARY_PATH=$(SPHINX_LD_LIBRARY_PATH):${LD_LIBRARY_PATH} \ + $(SPHINXBUILD) -c $(top_builddir)/doc/docutils -W -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." + +install-data-local: html/index.html + test -z $(pydocdir) || mkdir -p $(DESTDIR)$(pydocdir) + if test -d "html"; then b=; else b="$(srcdir)/"; fi; \ + cp -rf $$b"html"/* $(pydocdir) ; \ + if test -f $$b"latex"/smeshpy.pdf; then cp -f $$b"latex"/smeshpy.pdf $(pydocdir) ; fi; + +uninstall-local: + -test -d $(pydocdir) && chmod -R +w $(pydocdir) && rm -rf $(pydocdir)/* + +clean-local: + -rm -rf html latex doctrees + if test -d "html"; then rm -rf html ; fi + +dist-hook: + -test -d html && cp -Rp html $(distdir) diff --git a/doc/docutils/conf.py.in b/doc/docutils/conf.py.in new file mode 100644 index 000000000..1aea4dc54 --- /dev/null +++ b/doc/docutils/conf.py.in @@ -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 = 'SMESH 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 = '@VERSION@' +# The full version, including alpha/beta/rc tags. +release = '@VERSION@' + +# 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 +# " v 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/. +html_copy_source = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a 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 = 'smeshpydoc' + + +# 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', 'smeshpy.tex', 'Documentation of the SMESH 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 = '@srcdir@/../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 index 000000000..b39c124dc --- /dev/null +++ b/doc/docutils/docapi.rst @@ -0,0 +1,17 @@ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Documentation of the programming interface (API) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +This section describes the python packages and modules of the +``salome.smesh`` python package. The main part is generated from the +code documentation included in source python files. + +:mod:`salome.smesh` -- Package containing the SMESH python utilities +==================================================================== + +:mod:`smeshstudytools` -- Tools to access SMESH objects in the study +-------------------------------------------------------------------- + +.. automodule:: salome.smesh.smeshstudytools + :members: diff --git a/doc/docutils/index.rst b/doc/docutils/index.rst new file mode 100644 index 000000000..4863e8288 --- /dev/null +++ b/doc/docutils/index.rst @@ -0,0 +1,10 @@ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Documentation of the SMESH python package +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +.. toctree:: + :maxdepth: 3 + + overview.rst + docapi.rst diff --git a/doc/docutils/overview.rst b/doc/docutils/overview.rst new file mode 100644 index 000000000..ce82cc812 --- /dev/null +++ b/doc/docutils/overview.rst @@ -0,0 +1,24 @@ + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +General presentation of the SMESH python package +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +The SMESH python package contains (today) helper functions to +manipulate mesh elements and interact with these elements. + +Note that these functions either encapsulate the python programming +interface of SMESH core (the CORBA or SWIG interface for example) or +extend existing utilities as the ``smesh.py`` module. + +The functions are distributed in the python package +``salome.smesh``. + +The specification of the programming interface of this package is +detailled in the part :doc:`Documentation of the programming interface +(API)` 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 GEOM the + package ``salome.geom``. diff --git a/doc/salome/Makefile.am b/doc/salome/Makefile.am index 5a92861e5..8bf9f392d 100644 --- a/doc/salome/Makefile.am +++ b/doc/salome/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # -* Makefile *- # Author : Patrick GOLDBRONN (CEA) # Date : 30/11/2001 diff --git a/doc/salome/gui/Makefile.am b/doc/salome/gui/Makefile.am index 0e41ee5ff..9582cd97e 100644 --- a/doc/salome/gui/Makefile.am +++ b/doc/salome/gui/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : Makefile.in # Author : Vasily Rusyaev (Open Cascade NN) # Modified by : Alexander BORODIN (OCN) - autotools usage diff --git a/doc/salome/gui/SMESH/Makefile.am b/doc/salome/gui/SMESH/Makefile.am index a93b57ccb..cc3ddb251 100755 --- a/doc/salome/gui/SMESH/Makefile.am +++ b/doc/salome/gui/SMESH/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : Makefile.in # Author : Vasily Rusyaev (Open Cascade NN) # Modified by : Alexander BORODIN (OCN) - autotools usage @@ -26,48 +24,50 @@ # include $(top_srcdir)/adm_local/unix/make_common_starter.am -EXTRA_DIST += images input static +EXTRA_DIST += images input static/footer.html static/doxygen.css + +guidocdir = $(docdir)/gui/SMESH +guidoc_DATA = images/head.png + usr_docs: doxyfile_py doxyfile - echo "===========================================" ; \ - echo "Generating Python interface documentation"; \ - echo "===========================================" ; \ - $(DOXYGEN) doxyfile_py ; \ - echo "===========================================" ; \ - echo "Replacing smeshDC by smesh" ; \ - echo "===========================================" ; \ - files=`find smeshpy_doc -type f` ; \ - for filen in $${files} ; do \ - sed -e "s/\/smesh/g" -e "s/smesh\.smesh/smesh/g" \ - -e "s/smesh::smesh/smesh/g" $${filen} > $${filen}_ ; \ - mv -f $${filen}_ $${filen} ; \ - done ; \ - echo "===========================================" ; \ - echo "Generating GUI documentation" ; \ - echo "===========================================" ; \ - $(DOXYGEN) doxyfile ; + echo "===========================================" ; \ + echo "Replacing smeshDC by smesh" ; \ + echo "===========================================" ; \ + awk '/^class Mesh:/ { mesh_found=1 } // { if (mesh_found) {print $$0; next} } /^ +(def|#)/ { match( $$0, /^ +/); print substr( $$0, 1+RLENGTH ); next } /^class smeshDC/ { next } //' \ + $(top_srcdir)/src/SMESH_SWIG/smeshDC.py > ./smesh.py ; \ + echo "===========================================" ; \ + echo "Generating Python interface documentation"; \ + echo "===========================================" ; \ + $(DOXYGEN) doxyfile_py ; \ + echo "===========================================" ; \ + echo "Generating GUI documentation" ; \ + echo "===========================================" ; \ + $(DOXYGEN) doxyfile ; \ + rm -f ./smesh.py docs: usr_docs clean-local: - @for filen in `find . -maxdepth 1` ; do \ + @for filen in `find . -maxdepth 1` ; do \ case $${filen} in \ ./Makefile | ./doxyfile | ./doxyfile_py ) ;; \ - . | .. ) ;; \ + . | .. | ./static ) ;; \ *) echo "Removing $${filen}" ; rm -rf $${filen} ;; \ esac ; \ done ; install-data-local: usr_docs $(INSTALL) -d $(DESTDIR)$(docdir)/gui/SMESH - @for filen in `find . -maxdepth 1` ; do \ + @for filen in `find . -maxdepth 1` ; do \ case $${filen} in \ ./Makefile | ./doxyfile | ./doxyfile_py ) ;; \ ./doxyfile.bak | ./doxyfile_py.bak ) ;; \ - . | .. ) ;; \ + . | .. | ./static ) ;; \ *) echo "Installing $${filen}" ; cp -rp $${filen} $(DESTDIR)$(docdir)/gui/SMESH ;; \ esac ; \ - done ; + done ; \ + cp -rp $(srcdir)/images/head.png $(DESTDIR)$(docdir)/gui/SMESH/smeshpy_doc ; uninstall-local: rm -rf $(DESTDIR)$(docdir)/gui/SMESH diff --git a/doc/salome/gui/SMESH/doxyfile.in b/doc/salome/gui/SMESH/doxyfile.in index b084396ba..bceb23d55 100755 --- a/doc/salome/gui/SMESH/doxyfile.in +++ b/doc/salome/gui/SMESH/doxyfile.in @@ -1,28 +1,29 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -PROJECT_NAME = "Mesh Module Reference Manual v.@VERSION@" +PROJECT_NAME = "SALOME Mesh User's Guide" OUTPUT_DIRECTORY = . CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English @@ -37,10 +38,10 @@ WARNINGS = YES #--------------------------------------------------------------------------- #Input related options #--------------------------------------------------------------------------- -INPUT = @srcdir@/input +INPUT = @srcdir@/input @top_srcdir@/src/Tools/padder/doc/input FILE_PATTERNS = *.doc EXCLUDE = -IMAGE_PATH = @srcdir@/images +IMAGE_PATH = @srcdir@/images @top_srcdir@/src/Tools/padder/doc/images EXAMPLE_PATH = @top_srcdir@/src/SMESH_SWIG #--------------------------------------------------------------------------- @@ -48,18 +49,25 @@ EXAMPLE_PATH = @top_srcdir@/src/SMESH_SWIG #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = . -HTML_HEADER = @srcdir@/static/header.html +HTML_HEADER = @builddir@/static/header.html HTML_FOOTER = @srcdir@/static/footer.html -#HTML_STYLESHEET = @srcdir@/static/doxygen.css +HTML_STYLESHEET = @srcdir@/static/doxygen.css TOC_EXPAND = YES DISABLE_INDEX = NO GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 300 +#--------------------------------------------------------------------------- +#SORT related options +#--------------------------------------------------------------------------- +SORT_GROUP_NAMES = NO + + #--------------------------------------------------------------------------- #LaTeX related option #--------------------------------------------------------------------------- GENERATE_LATEX = NO +EXTRA_PACKAGES = amsmath #--------------------------------------------------------------------------- #RTF related options @@ -69,4 +77,6 @@ GENERATE_RTF = NO #--------------------------------------------------------------------------- #External reference options #--------------------------------------------------------------------------- -TAGFILES = smeshpy_doc.tag=smeshpy_doc +TAGFILES = smeshpy_doc.tag=../SMESH/smeshpy_doc #rnv: 07.04.2011 Workaround for the doxygen 1.7.3: + #because it wrongly defines location of the html files for search. +SEARCHENGINE = YES diff --git a/doc/salome/gui/SMESH/doxyfile_py.in b/doc/salome/gui/SMESH/doxyfile_py.in index c35d4aaa7..19e21b96e 100755 --- a/doc/salome/gui/SMESH/doxyfile_py.in +++ b/doc/salome/gui/SMESH/doxyfile_py.in @@ -1,28 +1,29 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -PROJECT_NAME = "Mesh Module Reference Manual v.@VERSION@" +PROJECT_NAME = "SALOME Mesh User's Guide" OUTPUT_DIRECTORY = . CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English @@ -98,8 +99,8 @@ EXAMPLE_RECURSIVE = NO #--------------------------------------------------------------------------- #Input related options #--------------------------------------------------------------------------- -INPUT = @top_srcdir@/src/SMESH_SWIG -FILE_PATTERNS = smeshDC.py +INPUT = smesh.py @top_srcdir@/src/SMESH_SWIG/StdMeshersDC.py +FILE_PATTERNS = IMAGE_PATH = @srcdir@/images RECURSIVE = NO EXAMPLE_PATH = @top_srcdir@/src/SMESH_SWIG @@ -109,11 +110,11 @@ EXAMPLE_PATH = @top_srcdir@/src/SMESH_SWIG #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = smeshpy_doc -HTML_HEADER = @srcdir@/static/header.html +HTML_HEADER = @builddir@/static/header_py.html HTML_FOOTER = @srcdir@/static/footer.html -#HTML_STYLESHEET = @srcdir@/static/doxygen.css +HTML_STYLESHEET = @srcdir@/static/doxygen.css TOC_EXPAND = YES -DISABLE_INDEX = YES +DISABLE_INDEX = NO GENERATE_TREEVIEW = NO #--------------------------------------------------------------------------- @@ -143,6 +144,7 @@ CALL_GRAPH = NO GRAPHICAL_HIERARCHY = NO DIRECTORY_GRAPH = NO DOT_IMAGE_FORMAT = jpg +DOT_FONTNAME = Arial DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 @@ -157,3 +159,4 @@ DOT_CLEANUP = YES #External reference options #--------------------------------------------------------------------------- GENERATE_TAGFILE = smeshpy_doc.tag +SEARCHENGINE = YES diff --git a/doc/salome/gui/SMESH/images/2d_from_3d_dlg.png b/doc/salome/gui/SMESH/images/2d_from_3d_dlg.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c8ca5cfd9f6b67a4112e5ab440357a2f09579d GIT binary patch literal 17623 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/2d_from_3d_menu.png b/doc/salome/gui/SMESH/images/2d_from_3d_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..acb7b349e3fb1c24bcbbf4fb695ed8bfebbb23fe GIT binary patch literal 24838 zcmbTe1z45qx;Bc9NGgqhbVvx&sYrK8halbEp#q{H-7VcMAgxG9cXx?&cb#XfwfFwd z+Ur02oc~_$Smk%)X3{>zPk zG6V_f36g~HOJ$eDjcHdGCBw7pUFYzR(M%Z+zFA~O3ejcs$(y8peUks9>)nH3hFd<* zcxbO29OQ4j#KwNvY#Y1%z1-wwJ~DkE54lk42jxhSql!$TZw|WQM($qT&$_>bR$zDR z)fMHB4lgC{^BqgLA5&&?r_hcRYSM&Kwp;2TNq9yO)}l+)&`o*IUeXfPB1_bay9_s^ z%G1i{D$_MhzU*Z+bb`XZ{8rRc z)xC}K-hpysxrVc~e4fsM!NJw3>gV-thx+4XhE~iGlyXB=wuG6cN1KykhK5w0=SPME z84?9*rD;8T{v>XnRCQea>fMhQHa9nqSS9R4Qsu|usp#l(b%F+IWyOV z-EXH!+3Dfk?rZRL*Vos7Mi^9}QGsIP^*hpfHjGfwlkhHKswrId({CD#tN_$D1HR=V?bgLY2;KuVdcH%s#m7+jsA>TF>x3eDvsZ zK!846c2_cgLUXe)IR(XdrDcL;0X#A+Qc^h!3kIKi7=JEK_mS!@_B#0vhmooGn(-c2LC~*Cc8K%=Z~YP$Vf`0s;chpP8P&<49!>F+a)`5Wh?Q4eRW#J6FX))gR$L4S#l{ofy@oR#YjD2wM252 z%Zz(T{CA~iW@ajhRdSV2PEN+6bCu~QnZG}cpppCY3qz2Sl{I`|Kyj;XUlys#EYu1u znN8X<=q$dqvokPLDrRrmdA8JfTj%DjTLjPEAU(ii`nDO`Z?g2`{VkFZ;>VArS9+2x z$sQxl`sMj?6=UOe!}(b0+j~+mOon?)?UkYyzX>ylR6U9ATD^O8J z(C}~}QPH~^h0))?b2;!3YgSt1j*hjrw=<}fyc-)+8yFZ+D>L|P8CP7)oGunZ&CNX? z{Zl2K4E7B_tJB+c4jG#Ms3YG$3JD2~ zRoffCjUgMioxEdSx6|~1l9CdWfS?_!={iGx*gp&1I)goD=&d;@7nDp)!OhLhJgJ0R zL&sgudqZPmf7iM0Td;HI6Ek^cc{(_-!Qq2j?o_FD*ql=NNl3(DfhmL>79LLi@ZrOa z;Do3sD)`hjixt@?V%;?<-dEL_3yM$LPIedh^O&iqmILAwhnajaSy))~FV7AUJGM8) zI$zsNjqOE=XgV8g7;IZx+cY>vLh1dV2D9ZJkonRwGUCujNjA5%j5pLcu2aLmMw26L zZEYMXqM{#7`qTU@^hqET3B z>Fzqnl~-RX=!P2 zJ}HpaM~fTtb}2`pY}(vPL^jEJgI1x@J&-9yz-oXDr%v@yRV%%}#Pi%~VPWCqPlB~t zolB|Zcm>L|T7w7obEB>&WMuC@eE7g4CbZg0?!#_{`TF(ile4p%B@U~Kib_i3C3>$Y zRH=qXa`fI0L^*AY{p^Z+-oJhb)kLLOXL^iE0{wSZ^-EMzfkdc`oK6JcM1+^@L6p-L z|3}*kddb1&?($bV19MQhMx6w%jw2ZaIZr}vsF;nyK#BWX4rVpFB&imm( z-4ML%^FEp1eL|qXX|}<$xvdR8vvcmJ54ZPK-L!^T6U!9ZvW@cb!RjC#GqYIsV1KRi z_R|k9w1yW=yfe>gO+T=kjXg-pxt{@>1iI?vF`{QdjV~%Ms(%--u%JJ`y!_gD5}xPN zy<5JRLixi173QNTWK4d>(y`BeS}`X3+I`PE-$V78sL~!@qz!$whsyc3L^P-~A2ge(^Y#qlH;c4u14>GsP1Xe8q%Tm(|GwYD+ft(Ut2vxXsu})YQLNObr#=j3 z{>Ebu+H5!@f4!a#j&FUmCumjq2F=~zamdx9qAMU_=>6Gf&*gQ+(buN08+~4SZZLU!TRqL+L zjq-7i>9w%O`PRRFpt&t)iKgpqO;saSo`j?a>iqOXQC(dk%iC6Y1%>yLnL>ps`y-mx zfKvyA*3;?1d`#zP6^b{pGQbUQ1AAy%-TLFkH3t&%gU^x zyFEd<`qAK6yN8U7>TS{0;kb3M9)In<#4(@OyZACL@Ay7w@@RL)ecSeAM=vjb zS`fw1*f?FDHujmGP}2G0!h*?6-RbaVkiVH30;H@@siyF()h0RYmrk;oRRDxBa-p05 z5t4ws9{WPrMe}BTw?QIzOaBJ_*4pg{(G8uS2jiAjR@~W4Vez2Gv zjSC<3F6vz$u?Tu>6wx3;!&*gKz#FQMT_0zb|95~>U3i08r|vhv%};@WukwfYYG*Hl z6B8f%SL6(vCA)8NCwpJH)^-vP-v~`kmW2+wI-HLOkR!i5Dcez;6T{zo#-|?b<)9FRN2ZJiebA)rg^>prD+{7t)dM_0mnd)+rO<5tM;}d{ySjw)eNN zad2!ePHeYow*`<|gNWKbVNoENj200C4k&KCiigW9H5;bC+70nWicw_nKAFF_x4$39 z@6Pc~lMGuXo=tD2&J}?;ah|Gte=VkIzw1@Nj^zPxRT{<^g3$^kh?OGA^?chFcvnpu!F-l%W>lrPov4Le|);E zoI3Grc^I=|`2A;zPXq;1`ugN+99D%YSl{7j*ot=>ZjOM*cJ{5reB-yrTM$M_b71*z- zhMmz?qou&E5Stf(OW*n-nu@=}Tl;v?=k_Dg;ewKXrsb}DGzwd5B_0jQ3^j{Z&NY92 z^muj;ZN;1ScNn$I$(#=cY!$1e>97p*Z0ev^)2(qOIernBNt>fgub0lX+_2_@ zG<$T6^g;wPxQ#|P9Eywn_~Ge%g*{}P?D|os-c7ssGBjho$8_-I^b{*QaM5GNRhs&d zpPwHPS!n)HU7Ql(^Pk2>FO)a3|rkg}g(gEI_h?hT8kJbhGU$qQ@iZ64P) z_(TEEGfYg(A5dJ2q~^ni)Dk%WRW(4Z$RLWZvs3yX>aKEeR(p51;YfjcMrLOFb*Fe$k{f$>rRC&r_*qFY_A5hc<z7t#KF_!egx}sb$NbzII7DH zMHC7M*W1=FWQ>ex-w$*uq2Y~KhgeYr>&SJqZBN*Zvk7KjcU8If^f`A)f0$hDODMQF zIFzh8NO?C-j=#I3ICHwh>jg)8GP6FJkjv)D16+nT{b?dUqP#0+T^7+0oH8hg3Sb;y z(8=h{nfipke@)zB006**s_pV;& z!qVU0508i#$SYumBA1J`b1rf80?jJfr9aC72+HX!Ah(3K6d34-*8(?2tD}987hBGz zUUz7je6sTA@$Q7`xc<+T6=4T0!CkFA_1)vw*)6ltqUvd_d2%9NJwA_AuDKUG1S%7tqo}^D?5lJ9eO^_sIl9(6H!zQFF9vg ziF&H?&~@j@vDZFYw~&U?!_qkBY{s7^xcJ>F^z`((9PuTwL2BL97d}l~hNkTk73OiQ zhA2>Zb5(EgO1g{#w<~TqqXz3U|uoxP}c3?d}`Ew-@^<*WVcJu^VsCEiV2d zz1q2lbat@%3TQ{?Dc`)wMSjp%#r$EuogcT50C$InhvV})F}(6e&(v-7qNJi457tnH z$8mgcw7UHU$em*}ci?-w=5Y_S%vACmcox6g z+sWWOn+|4`zWI3*D9n0+(rnvHyRNM;YA(vy+O3)grJh8!BTQ-AKK7+*43B5aKOdDk zr!g}(ndCHC43&SKc=s|lO~N0YZPdi%5$2rTz}}u6aBa!Vhvnna*|JG$wN8c86p{ZO zm##DD0;NI-1j{kHS@3B7p&L-q&^&isQ~uMFBn1rxfQPMmLjEbCkj(Ajj;N>H?nf3$ zhIDjve3u7Q}qD+qi0Iq7rZ9U*{ZJ$d# zcs$7Fq0pNG4!gAjx+~U0W>hZ^cHHu?ZxzM;vhMe;-s>5V&i?wG(nwZk_~go~;hWWT zd}t`9hlfWI@~y_`yu3V%uu6~{sG!=T5pif$on}t@4WT~mC2U}P^qUovjD?LD!v51Lf0MQq(2XBm(`T#9R8(r@=VJpz7 zKdb3sp7u&Kx((osoSfXnr0J59rqM66;LCnvQo8kQ!|gkFum}hwMvJspL8U3wtfGGM zWUj`$rwfo3fq=(}W0F!$eKqO`GAgP;^Cv9)AW;xEKzoH%ay!~U{GKTwAi|(lt8WFA z(*ar>L6Vu|^z;EBZ+#tIueO=L`RgNEIuN6i^Yg{!;V17o!W{-});Vr^OP_7c1 zb_C^g-x|)m16P}H@f8Jk*lQs-x0)|D;Z{!%18m|?*Lmwktcft3#piUlsoWpWJGc%h zI5{~by+9Qsq=+{G&BtMDDh@b}R%rpO%|8^#LdIyx>_P2amQB@PN!&$;i*^((O4G$&Cz>U0KD8IT-II?Q?mrF(mm1W$!KBK=cZbN=YUi8Am$~gQ@8m7z{wS z1O7bj`DzVl;5(^C<UyCT!oay4Jd z_(n`qbcg&sO=XnYTZ?C3nm6(#K73N+;Lq6mr3h4$Y1Rp9ceW+ z@i{p;cjL;PA_tcN#=cAu@ca}MWC)}d7{~9*uKq=RX7tYF_}JX>a^p!IT}GX{*n$Fv zuT}*^>&1;dfK2YApzN8Tl>-vt!qyuE z_4H4Y^K7b=rmm*1Qsz(J8TszfSETw~_Li>KKh^(s2Ier-ioaeJ31Bu*eC-xi+;|;U zyWKFk>Pr}OH%V4?MdJ#L4OWN0@*ybJIVNf5Shj2~J8wYu5^nUmMEW~gOd5#GsJ}D! zbJU`T@G$7B9j^cDg#1`KXZ=hoCt%)OCyKsG4)_}ywa7jV;XA(sSvxKs243!6O+FL( z+lg|@$nH1mbrM=OIC&pUzOTw!HzvaCVz&)yQ#>f8HBOt7va;yK^+)k-E#P^SL$lC- z`&L3qD*yOF&qu=SR=EA&U0vVU+d=JV@9HYB&oKSVZ9WwIb!{*k1M1GEWel@!57cKc zC18twm3^#Y36&fij<3UJpMk<=50)Bs;?8-OD#B@lV!7M=NNy}BswR#*$cD#tjS}N} zS?{i31L5qzgT(25p2Ax-_6;&}Wx?C?OTDw0GpsoZ6p0xhDHcl-?VmQ~0(fT7Elq6X z_=joPdXzm`~usCWAE_cLCv~LWzZg_m9d*qx3UzX!y>O{ zfVi-@-z#Vgjt<~1*5}U=qUuwXf|Bz<(^56UCA3s_7IE;X&}xbt+;ggvTMH?=v#cv^ z*k%KHNfHCSBaEiFO<68!m<{KK3{g+lC*gS!vh3ahY#5+f>=@0U-qd(?$UOPl4(3#o*ICB#z49sbJ@HD67k&Y!iB_jDU4^5nv!w{7N8t-SEKIuJ3kt) zFRgFhygAnBE$DuK0Gbj@k7StG18@Bi<6VbB}9cC123z-bkPgJZUm^KLQ zOf-%w+5^Gl0cPaB|Dk96z@ed>3bOtnrTP?Mg(@p6Kk>DBsz0i8dk{2{HTK4TNL_CA z$svN5AKqsE{I)s}(ZqUd_@h}K8qQK~T@DkM7?dP*OF8PHQR(7$Na5rZ1bx{Ug_9Nb zJbEAzf?x-$@U^EO?ttG>=K*&BtsM^8?X5QK>Hyxn;?^%A;YIwlGUXd7ohJ)|wV&76 z!wb3$x z{D*^E3@X7a1^q2{p0%S~%F2Hdlw%|MMp(M-is5c}_<;?Jq{|8Jn^ zcZ+y199n~krKF`%VSP5n%iD0BvHvN?5*O84P1o?e{dbs~@3i{I_&Czu$%(_1caY2L zg3HZ^Pt=o4 z<=l#jJw86_u1mREQ#Q%w<>kfpz{Kc$B}+0_C{t0P+8Be%(9oAuP&Uw?1VKc3cq*)W z*~~)V1;s8W$H>$2AUUakrw^Zowac9CqvyP<7HGda>*G= z*|LsIccL>iv&|!Gn=g` zZYUOD*i@m_%Jp(sPBnu7)cgE-a;oQfZ{NLR(cv15;|qb9S(#*EwQPadE?hu=g&Gxm zIeJVOIXTtEuKRzOsw}_YX96A@1TPTwxP40*a6_oheP$Io%JjgWpptXE3fx`>E`0O(I((INr_)Ut`c zuPcj2ZE<2|nsG83=!Rvq=*NhLM4-@v))o<1$Fl>#MTnx42Pxe2^q-n_b;Nc1)bissIA+pQ~Ek^~c0r`o&B@DM55v zI4C8BG&iNO{cmG^NNV1f`dP0Z{nNU;WIHLdGK)#<`@2VIKa{ma6pn16r6Ig3sBN+| z2gk?Rz>Z}*q)bieK*6kZBqYC?%knl7E~h%+OU*>Qc8ew*HVVQUt+FPC77B)_|EEvL zDg|ou{d`rtwZ*0&?|>+h{kqMiEF`=9Du~YI{O-=&26oRE4`1Bv%cS#fmnOu+w>(8y z&Ko=vWRrO2A(+F8ET7*0b#PU!-mPM@;lH49F>(6sAzsnk)K**n^53+o?4lwfrM8Qr zNF&9%*3R`4yDAHf*ESr4LD064-honJXJ_{*B*b{)n<*j_Ff=rjKV3K(Je>t<^?Z4+ zz^_>EPj&Tl-^@qR^M{32v~}(HPDKVp`DH)oK8b%TsK=(9rjq~p)2FZCLZ}+}j_>cb zk(Rq1ngKRmov3)q&COkRG+|DHmy(#+_2JF~ATm^pjD<7IW<+L>RoMuG7+Le=QUod) z%$-3FNrV#>3Tcg(FK-A42sn*`SxZJiajQ^83JfEt2aKAPNb)*5J@8Z!Zv?pjnq-#` zpmOBDJlb^9=WNPNOZ&h);{6lkRF;Hp1oyf4@Rj|m3698q>c0sWzroKKLjiX2^MCg|0Qs+*}bm9%f^z0%RuOx)}+=TO5_yeEcGgpq_%96vcfjAq^ZEHgM~8%%$01b4 z9_@d5<`2>1MN{SHR#wDblp}%Hf+!sf45)8=G3SI5`%>zCm# z@hZNmndLf8!m3lzwBIlxwEs1s%w5bFy0rnOW`0OhAQLBU5Z!0uTug

z@`pzo^d-y;oD%-*u7(UkMC~IQk;;nqbFm(+Clpnfezae6*Rb2ld2H4!I4;i)mcVNe z78B$3I4-1|rr+S`-og6CZNuQNO@o%B9FzR>{tYrqx4ujzsWXwr?$msBj^BxVhC}zKi~1Fu60!`{q?Fa@7@1n%e3MuATs40aqdI2mz{V|N zNz2GYc|LR_QVq~P(6EjL?SN~+e%QG+3Ronv(Kht=S>5DU3c zryGyQYoqeap1P&ucyC8Us<)*a2(4YJ9eqmTu*OAZ%`Z_IePhzG(i+%?*>;*{=dM7s`PB-Ea}Ei~W9 zA|ctb^Y*iOd`jty31zELX>^%Ijh#iYr!}l!VauLyerb8x56~BG;Ell7eO1c_Su(U~ z-|g-p`A|3f&aJ=J+pgL!UzyWYpbZIk-_cz7Mr=BqH+;D6f>*@xFxO~0|Fer7HXHfW zMaCVmFgeJbC34xO$!c3SURGMxcD~Ipa8UjQ4twL}5s~-x`7BTuvduwNadecGpDTkU z8r^D)xI&lccGpSV7JrY~jndzD%_{Dy#6id@6(F1}4JtZ%7Cd;^8l*|G&MkJCis~yj zIPp;rXR2Gj*nW@-`}R*-@(w~@lb67}QZ=ir-tAvH;+`(iA7(p!iTo05rUv2=oB7C> zmp*q{-uys92syqb`mD-|+OLCC({2Zuafe_SwQezG<`3arSi`ndU(dKU@|* z(?E3U&fHYBeci!;6yNzqIg;%0sQQwUygVkLqRJXXy3?Qi_urh4h7fPad;Jt4FhSvx zc_aU%_BaWeuwQ#lwq|}vwS3=~MRk5IXNRXE+}SxrMXma!eHG@a7nhgXvJeJTj_0uK zfLPzNIBBp15UMidc;xek5%8Go)kjPc5}lP?c?gxfZ40KNrv3=RCfNE)D@TBBre=1R zJ7b=Mn4#l!Y6$0Q<-oz!&W;t}GD3-iBw?vhH(}Mv>E!Iy$6+&swSoj^CLX+B#8M(< zpKJ2&(ed#mhEa%(5V*Gl;7BKOh50?eV>Rh};?Mq_u#Lc(G#gx~WUn(*$SRsu5Jt2& zN75-ln4w%m3<5A;6?5TQIUOA$r;%yMZr?e*gN=iSMM&5|>?6vu*U!01So6_Z@HFw3 zmZW%S}18_e2Nr!^EKmXQz0@gSsPdy7VZX&D2h2#T=QH zXU#iNlnV@YX#THUYHjY$u!n{}FdjQ-O*v^Y5X#?txlQsOEMHhgNf#F`5Qu0XTlwV+ zD&$Yhk6kx|vRKH&p#NAmoc>`?$tG+6oAb1~3mKoy1rz8KSO|pa{o-%lJb_#t>@-wC zK|#o8p=$BW&Zdb3CM?k2Vtv@Iy=Pk!-kF7npWH`5Evw%`@DlAB2eKzmkfJ0-ppf7A z1p6MCz+O_%0AsJ=AV@2B(P|vN<$ks(!*lR@&LlJ>xU<>N~A%Ot;$ZvmloyqSt z1Urp+)Erz86FVF$D%d>U-Z|evw-B@4bwyOyn24;*%zjWaVUK}L138&8r%jER$zk-= z9O6+2SNiVdsURmi_TcAG^r4HT4x9Zm(hLkNf?B4AdY|3Eci=GCyJmd6J{i&btQ$|G zIb-UB#myhk}B4zM}jlI zywDOTEF!|q$id5-0FtVtq~ui1tP&baFwNWV8l(E9To=C8A17AgOX0Kem~{?vK2FMX zAF?@xu%&pO5dg_XZ8+DNa3GMV61pX!BWt_qFC)9FWfgE<&FPCKx6w$=` zrHaU-2+6NjD-D^Oa+OxL^e?`NEELOi;YM8XNuavfl;ZQ#oCg%oP$c7Cl8!UomDZx; zF@Gd%Z295uP;NvgZ6d@tWC7KE0(ubuQ3_Fu$;$4^0&2y-Ad2A$X$4Rd3VPL9s&9Ux zcAuSGwE7lNZ~M4~v-)`qW>0$1Mhj>Ei+-t|KR9W63n^BdvJ{CPH=BMl$cLY55?`6`iOsb0OjmCQfv^8%dHkMRlV1=J67zd=I_^k_yR83U5bzkkB)AIpihVI z{YNfKVPdhI)+9D`LTeBNyTcbEZRel7@!`elpKf;LP){iSk-ba-_GcMQsyP5B|9z*B`e{ z$GwjxgzSsClsw}jsVDkq>S4;)U@I!I2FN=)tPc2rV=|a2h1YO(X0B4CWvW@!Z!*^4 z$qSKR4?uSSO2$@QD$TSa#BGgOX+yIRpq9(41FBAVgV{MziHPJ8Sd3I2` z{8D$b&W7%4wR$C)f{g4&z0S5xVt($tis5nO{*N$>ez!UA>oj~j%s+~fvexA5OU$u; z@$;7xb_X6vBWwC)<@URZ@g|S3WW;0c8*F%dUc9$PdV4f9TahZ4jGKS?3+9_HF$LG# zewo;rH$U)RB_FYTW>ff;O3}88?{UL^_r}$;CwDWw#n_0iUYvdoiG9AvJnE`Sxq4RS zPX0m-l~!8yDqZRk_XlLZ@I-r>&!0@>vSs@pbCqDE7Ps2zJxIUjOvcA|k%wXifS9jC zoRh6qMoB%sv$}r!y-y5Hz?v2c3L7f^Go<|Cj`oQ4YF^W4uxQ^*`WG}to=ODuSPk-B zE~SG5m@ZE*Pag?FWu#>0X5-b&j58)7p_x>R^_4(fFh#7GXrtyCn-I zri9EdNq|DyVbtAsuzJh$>_uDfXpN{uO)5y0)Z5SpQ`dCuy z>7J+Ka-(xpS4lbjTEQc|Lm$ae#L) z908Y4GMRY){(Xe2%%Jh@6RhBP;q17ZQkJyd?m|mOcJ|`>`uc%wsFjXWf>9fWTRgs< z{Cc^b?&`m!)r>I8~e=W+&k zjSCR(|Cm2Ynh zZELAREl_#is7o#4YMjHNtY5$|4`Q#C%mzbK?vYqgzA`_&5|?nr$uu;KygcqStFRN`jIh5~Ww$kVEbKfbxAk5|i|c^+EZidI zQO%?IB_?Amsh((d<)y*p3TWE@DIv|ic`L<<3spc_OWM^C$1t-c5>NMQ8dplk-b3l( z>q{FNW zGwNIKSB(S?@=-zeeQKGn*()5dNs#heq+h#YHR=CUE6lDb!_FW6FuZog4EgWk&EiJ> zn0w~mpn~+Q{_V6rhFt@O-0+rKPc%r*c|De5_9I1YuPqVj^riL)Bn0?BIdSnnr<<+= zJ)y%nNcwD0{7b8pjN<;8eiy#U_#O#v7yEwGRBq++#?}15-o3AeYR!(c=dIt~wxJvK zB!1Q@7K0fHx5KqoCW)H68yrC&3?XcFw?~&NN?C8Uj|T4=S`UrwoF>5UoDcdSWO%>9w%sT zjj=FTH_FWK_i`F|hCBSS%{Qg@@oC=}k9d?V7ZDXDa@O8CBVfPvIC{x|37xbj?rxo^ zNVt_SQ$nk8j~2>ZG<|cSn+f^({rS4f!dEOE4BHdr2?f1;ulhZY!{!$k_oi>cwA(-Q z{-jNFAo5HP>iL{<$$q;~=L&3iQPx04G{4~S_Un9F0*)^cxDTSv_)w6un+HhBMdU5Z zSaZ}__-C|V%$FG^^1I_&TU$dBOY|D?@D$ve8g{}Byb~q4+#eghbU&N)a}h1w%X%+< zzg>PVs)D~kD)GK&{TXpL@?XDUeZV%Jo$F?e!+XW%0|jJZaq*RbK}P4)bK_uQUQwVv zkPbvbuvO5Galf-VXHQSjqttIMyZTTYS7+*ZA!!Xth!)nv zzqEVpuN@*B+)J+yq+clbqbH)F|5UT2{I)0%oE);^p=bQX=>74jg}#$VyIf7}=~^A> z+Sd>epcvo3e{WSESNl?fQXsGUAE-}VgfXnevDMdfiim~^nF_LKC3&$L^B@_wgvFNBT9p+g|Dx?rR@ zxGnuh%8+RVNEzsfEKVD0AJK^=A&((0DVbJQHc%N{|2rEoKp0(1v`aaC2EGaeiKFg5 z@8%_Lo_pQY60QQ2%Vjxnv7yC#zEH56Mru{$wdsws0R4&c9xC%$YH-nuc?N!)u-U-~ z18Dyc)cP>!=;`TcIl4_7)xl#=gnD=K7)tHXkg|^F;cMXjh-nk3_PaSu1k;qmK)=Av zzL<3<-QCU7(V{hzF|4>VTTb)o^6Zts>7_$n#{(8+>_>Cuc=mccvkj9Z*WC#$47$OY zU+3~r-2TKFBjN%uH>U?A*}zygz|Rh+cIY2sM?Rx7BNbIkWdVrJFn0jE1|9$*#BHCj zvsd&S^^m7f=5l~R2BT+x@koIQF=sDohhQ3&^_i|`Z)|Y9o1zxLrbouQi$d8A z#ea^fLkaV z&}}quoDl)+A&b$pu6SoLA1lP>XB&o311jk-flyt;(%;^~zxd<9{nNFJwXV;-PEQkw zxx?L8dMuZw!!tgr+INg^g^9w92Pn&{{S<5jWB!7km$mR;1PUGjTucC~Ood~a@X z(i3kB7Pc4{8kS;)OgK7RY4x$&b`Yhu*bzk{Hi`x|-g7oijJM**m+o!5>pdYRIJ~Hq z&37dOnn3x_^nQVa1@p=E2KVvo?cf2e92V(@7xA9UL?i_*49ewxKeuY~#VJHccz&1n z8e1HgKF!?MIJFr^?HnO2W_aZ)a6F@^Kr(%AF9OD zy@u#-Iw;&3{z-sQB_!JdxUnU-sp~$;#>P#Q-;k+`%-W356+tlRWb>E8Kh4<=SZ2f6OC9Q zn}QXyrjNvWC79iOj7&o((Lr#v|)%gVR_ zQ>L!2HYf5)^aT@dYz+rK#QN}a_FdMxI-9oK+dWR3owgZeq61aO&5J_p`EPd1lF6P& z>WFA)t?xwgSx@!9fMds_Lp$rb-MgUMLhuNtkbl4hB#r32{o9v{Kz^(EupDMn1GcJ& z8NX`RYI1Nzmy&uPo!&WJ81-xnz!1EUFK~B==VEot8{1zHD!nQUT1Yx{Z&}Z8z*}t- z4cu&;dMa?yo4&AXxZV=!n(`^zxPdlHkX?%JbSa`K4)nfMWqJ`Ut;9`p*pZdXq@D+1 zf2V7!PI|qsD4<^NWJ1D@i>+h@_L-?@HnzJC;sa^?WC%^+4k{&*v6Yh z&aRfsjTdG392T*IERQd>raTL*s1b`mM<+4H?>>^27ob4IX{W~f$p>qiMZK@JZPV^FQ@{xcr*wWOqPzJ^^4Euvi*{@(R?+L@nZ zQ6;1nLLqHN0=)^0O{FB_zb>TcmzNDAqp;J6pS|x3E?ywU6pcW6I(%mLg_;W2lS(IA zUph;_U}P?z{;!587bxT*KxSYP{uRO&S?P7IWpbuS%*)S*CndMDyF2fLAyBdjYInZN z%r7}*lTTx`^*X)4Fd$(Fwi5{q5Dl%iW4L?w@e%*yYR=>T9DY?nbv0q}A$1FSucSk> z`qNeTkl+=-@-zBdnRxcJPix-gE zVb~hxcnAmxJm5y*s~FY-9M^e`K@$fH(qig6*1N;?QC_Xl{~FMIR~W`WZ-J^R7^4|B zO(7#3pZD^Q*1sp>wdK7{)NUPqH0R&Z2dx zWhDZP`0f<{M+G_0Pwgg6YK1FJ)f9=-*S835-!o0SLE8Ndt8!o6Ci98klFb`;d|Z2_ zrb!25Xq*Qy2OZ|TKB9^+ez!BLo-zU}00hBdG1m9{_4EYD;y8GCN(wqh*OR>a-*fUW zuLb>O$gq`>m8a|s6>Ri=TUG028ih}o2?iC^sn$=E;EWCxsam^Pvh3wYGn$#k;tgsk zmD&B$mckU#vVZl$=G~1rKlPq%jQB7^qgaKh-u>O3`ITSXDaWpjy~Z<%``jKq=Cq^H z`h8LJ^Q(7IXxZ4QC&zA*Zbz_LI79~;cgSy|Q_#|W0=)neGW0GkC*8rz$%xsj&yQ%* zhRt^O_uoS6ubRkozdJctzHk%W@)BLC8a=)Aal}ixpMDGz-#8yFW&fItmDH1^KsD5B zZuT93Si8`xSNGA;qZbG{Kf{3Bvo}B9X>PXif9}p8IBa?bj^YQ6!n^cYmNV}{2o78U z|2Qv$i@OpgRx^+-M+%5Y$&gBlygH#Yj_ub+j9;UpF&{ebfXstUTg0@v(E19aA_J^b zhqa;4;H~iS^IPW)K5Q>+HW~m**`Iqc`S_goXcUG*aRXYr4t6ZI3$ZuYnKS zH~o|((gEe~E{X=!illny-Z56h`If?&@-AH+e>Ulp*s<03+S*wYU8?ZOo7q{$LdXG{ zRSN};q^_>6kx`~La&m2#$3BmFoxVWPBihUswws(&9x#ZBX0;r~fV?Hfl=E)Op23yd zAGZ7R#1h<&9cRJuN|kx-x9OrJSy@?&xRHz}9Yj@fYRWo-B3;6#s<{#o^NRwP3q(U2 z&xuK0@A&k0ZAFaxqCaSQ!xmQQcqhA2SFU%=3o)xB+o+@DDzynA34B4X3$zGDyE{?? zU>ae4FMTK%&Wl$|fyi*p7LL7$2)_KgGbewrOzq%CQ5e51X8la>848a8+m|*SDTB2F zKa1%a95UZdbeJ;t7rZ+8<>qy|XQ<;ce+Rxl1P;;4XPV?W5aW?xmdFRR)+;)`y=)~~V9}&O=5LFimq377y zl{pHYfysf-YVgSsqZd*_Fs#W6-+wTVh>4Ps{iM=GIDzZ5-(kXma5;D*f7lFq7PrkD zVo3Z3m=kunmhDMC-K%k$68ER8u9`A+ybRnAbKg^DDu%7jW2;%VB#hX)09eH`gcZuA?Ah}JSB?c&;45mCGly*y5fz=d@sQLMM zD1u>N7M1|FgN!HI5R<<{{ZM9e#8OY|(LtBRACp1!6KyW3gkR_KF_Gb=>~U~&EZ!1a z)9(E3+v|Vivs4sf-Q!9NuuZ8j>xiaq{-p8`MD(`EQj*#a!nUsV$wYmjtZk z(s-N~nsSjRI1Z@_>?zxh3Td{Fw?C5{s7j08GBh;Qs@MV5M z!XKh&&+Y$6ud@}yj6CFW)r$!@Ebw!4a}mL9Qc`Pe4;vNq_OPJ{3huO;aM;qPV={#4 zqMe>;u`(>TK22UfxFXi)&b9hY=lDuCicR>o)1qWQm4-vWPu(RwR-J_3h#7r&RAohK zKs)R~27^G#=PzFxzjponv2F_rY^@KZT5KLTTWL=!!DXTmm*#FTeGeAt~#$@#&$O!o+nmlw%#!E zIyc2)23yn3=E zj*Wzb1W_pPlL=(wYl#|Yyl5!{&wDcgnse zOV+Vx9U;aqTPdL|*_$L48j*b+LS@Sg3C&oF30cRIWn_1*IlupTohRpY9`wYR-0u53 z*ZTf^sPO*qpXy?VBNKJ?^(&Z+uECH7Fep&l2UNN|)kTWPo;@g5VPy7Mj~Dy6C7LEq z)0rDB{r|^X*FIf-arpF83u+itO;sfWYD6t$K@~KIlZg9W(>CQT%^~OW`_~fvP?aYO z{BjoSyP}W)j?lSRvS%B^CXPo=E|Zr2_-)t=v9Pnp!OU*taAjpBAQNW*2WfYd{6!|? zVIdqF8(S6%;z~xSj%pXN?;;|odfK zmTjHVmGWHS=PmVMA1&kRUD)dXFpZZEEgMJlKMRRUmtf8di~ClFiS^u;_uEf401Ony z1)#el4i~sJ1iHHm=4vN6Ls4*?Y&F1F6tO5GnA(}Y1?4S2A74x1-PGh{|CVNiIZkJ! zY4F49%a<=*A3eN2H)a%aSHw5zd;ws1h~0q;+hX?XdEbJjlfo1>mZ+2b0rK*Cu+hL~ zuMYPlXufE|z{U%f>3|``CMJRp1WSQ%vH311r-7O^e+IP>ePb?yRtzdMNHKuxgOGEx zR#5`VlCy}ga(%AQCfnf%z!zfU<3X1|{DZ%4J?RpVS@u~qREyj9vW#Ue#N-~nvX zxiyWEiC;RqnW^=3^94lSqVAao2sWWn`!XHFEw9>}+jGrrqZ<1rU0HwfLst8_oR{t~ zk7IaB<4{6k%(-v|%gBqlbD(ZzW@t(rDc{~=X!z17;1>CNHc0AjdIa;87cxM4ttT(H@(m}fmQ?Js; zh?IIhAvqm5<~IgMOl}}0&Gs-gL+go=ugfDv&+qJ7J$nD5qt>MHe*I{;-3l^Rqx6%< zw~47KZ4iNG4>x+U3jmZ10<8(WMDrn-*rfj@MdpZFm0WnJ4hTW|Mzx-rM%&CL_zm6c zu}`-H1vu4%3dBs`plpIUdtsW=%1BGg1K9N*l*!NGOt)x%*$fRZbasy+pwUpd4k-60 zBw0HtPEwu^Pd;>C99zO9IugB@&mGgwJA#xUi!)_ z@CL~}FW9|jnY<&n?zVQA_HZ_9>gyL{!iZj}aH$&;`%i{^T3w|bW!Z(hRE}A5l;V>? zs|xo4+~jB-$390BD{y`K5N2e)iW;av&vY#Mss@NkKuc%wU8yt^SQcm5tXXim!D6w- zo5vlc&sjGFUxK{hwz)ECaSC@GEhUZo`3z!31yE^VAp}p15Ttq0|L~8`=LA_G5rI^3 z?YY^Vb25qv&ri81h^UteYm_!dX;?jaZTDozqYkM99LF;dLj$ZZ@rjZe+ z@!ia~&m>3#C?OU>!-8dd<;1YsG`}A;2~cR5nVD_;X*rQC5|{z;0)*Y_U7%#NAUa_u~TRQ0RjV7bnD!*yShl)+Ct*t2%Ofuwkyu3ufKt!{MFg`uq0Tva0 z)dnaT*;IE>u7Otd+?GtMPhrTHnh%VQ2HfgJgaAO9f!zQLt>Ywyx&a18#&V?;d6B^A z|J3W`8b1HyS$eC)AAaRQcuQqv6_7L0IlOcrwwCW8*mHZo0CWU4dqFEakOgk}2pWM| z3w4HeeL@>FwPUkmv!)5{u2+(Ca!maF%hPVB{G<#TQtj)a7Ec7|ebzJX`ZGgjqk-ny4%hq;@{hr^xD1YoiHRzXaIeG~llU`qktjHtQ{7o;nNS%w z0gRqf-Gjr&&Y2G-t$>HZa*p5IfX!!*1FfgM=dF@;7cJ)_a7KbGirL4r9l+6q9{7=B zGoiQ~$A_N2g z-OcPEJkZ<5tHx{R_9uvgKob_MurIv^3?wx3)cGJVh6Fv+0R!l>vxTT`jv)YUf?gS1 z7^$3T1vna>qu8Zq*59~&+KK|^QIkbb_tno*@=J;+a|R$ zIjxrp<^k$Zybii?fY^!5-_FEg03;rG8e15DiBULIl@@%SsBiq&NAfDrfQ_Lj5P4bT zF3>Ttem}PlDGqKf?BOzCpG@WE01WdTu5yCeR`7N>SdZM9A4~?507QI7-Zl9MDGWU8)gB`d1skY!eh zzOqFo+=t?TP*oBVaRzZh7<>N(tn&O zF6>ddq?|#>?hLfyI5cwOaC)74(&C)S6Z+-rM&9o4fB`)wfZovp`l6BcT>! zg?}}}l(n_0npv+t`4wvh9_sep`^}H7d*;X==#6goN}0Xw#fBbagtO8o0$e+;_nkg-hEYO7f-KQu8+UC)ifQq@WAKe%v$J}CEsJMB z-8D2e_FK+Jx8sYhO_f*l*cavxC*GFT7iBb&2PTLm*)7S%{uAwo=;>3zyvf`=4bPX$ zC!G6lQ*;%ebwDGDF`eGYA%&t{?o!aX`4?zS>Hh$?s7hov{8FX7Z^m?oJA35<8n9Rk ztTCqFI*F_E24VdYDSe`z{fd4rNEqBdKt!=7-MVe^sz&Px5|E;>br=HbzRJ~H#hr=uLQ z@i0aL-YhODsdn@`?v%;6$>pbzWT8lN?KgIdyLb!7wfO-WJrDtmR}Frlv@XR$$JqP& z8hz2oQuF?(4J#X4?2ykCZ1&ZoE0XARR?HK9EIX{@O6I_sfQ5^z8&FQKUF+-DlUMJP zXgCEEtZA+SXxj^vjTHLZj_}D4RiPNjFt^=yLS$bcbn4zoJ?}WrM`{`SMfZ_vGDQ`M zKjpa=`}i?0@Tnj{W9H;Ms%~a>3S_-Pyu49x3aGjyOcWJN28~5t9zR^6MSjp3auvDl03*gt%AJ-Vmm8luD{NO+{YvkNryodq6s51V*}UI9oN{Uf zgv6u|3gr@wttB6Q@AO|>HV^G>3J*Oe=}kw}&7f@cT<~u!dO&G`lbc4kwATCD1E`u8V;HB@g)ZDlfugc>0x2Er)|y zeKBhu?9)9t7}#>iXnqo?m4F?7KpIwiUeG90+H~0WQzd-#7a8F_phpXVR)LX$;UkGe+Il}RI~xZn6@oi;BL#Wcn^%wj z@4@3|585T)PSITq7GpXPpepb`GqOVE{zLS11L!euDm=x)@g1!a_uDcxRq1e9`N^kQ znYOtVn$Q5gc&W!P{%_{~-zG3`{qM$occUzV10ysKSn*r@DN6Tkbe_YPE;~h`8CgWu zZw@L2r%%6Ry0yPIplhH@<-% z&0+t1zT{0BZz-OqwY6IcZ*<^Hl+IwD%1tVn$|#;-Wa+YqaNt;8z8=qUw)=*OKx*m@ zo-5#F#dC;$J0;zM(rt@E(FvyB9u9UB%ijJ(uT(m_rIb#dtyH>i8;)2IOmKU6|MBlK zRM;iMEMm;-Yo&rmKaMsIskiyn@aeRJsml^*6rPth8@o*6a$$=1w5&{BS^1}YXbpfj z#K2|eHi};(A$2+@=_7as78BLOc0jw$v@u_)A?@)?>+Tlm8#$r1RgC|_$Bx&RY(c(t zPc_^ocOZ*RQF_Rx)_VXv2p+fK@!1BMC4a`B6n()rI467}?bCW2&dx^iUHHZ4J@ga1 z%_d*Qb>M(1>=_hg{Zwi1nGLwA-WMA z?e?3)Rpd&7|G27s0rA>GHZ1%JK`rtcGGGEU5 zOCoi?P@p{k(@MVl{7Fr%a1BGlwwG(eK0_-4G8E3_WJH-~zp(1o%yn;Tvpz}puRk?3 zTf>5vnm%@Lt4*16Z@>@yc>jr}j)dcQD*_y)_R^i>CPq-T-*P*wW+)bayM9n_iWa=S z@kcT&Ty#{KTk5S6K7*?UEm^Xz=Nq)UGnbr^5o3nE$H)@lG*W+duyH$zvh(1?R)Z_v zQq7{dbZ?J>zMRW_`7+Z?-(=wDmg&mdV;Y#nwrWRI-enW852r=O#XZ|!KJm@twr$I(swN_B`gAXl=D!kGNkBo1*?ryQD zw=w*vuWlx|#~iotP2}QOFn^K0{l3f;Umw)Lu28|m#LD@*dZ3<#qv3b`b}PDGF*~{1 zTXc&I&*$X9h@Em?;_kLY-&-TTOrN1S%v=FMaecm1P+`+EVsr9D@PZ-~7(=pTBHen9 zCl2?KcQV(qCCBEghqT9D8e4U|q+n{eM)cfiROm&WR20= zqM%Fq3N;0nzT(0UgIYuaNilC?OgAj(~q3`s-5`^KoCs`s2l zl{G&0wp=`~P)mL@!_~t2Zh`JyGacAKFYP1;vgAwew#5%P-%;hCtYQW74i$%L&xnd7 zTD|{>B8a?let2MGxg$ed2Bb*As{1>P&t)46gYnV*4yo-OP~<`)!lVCL z^SCJ5;=38p)uU?g?$bTe9D&FT{oty&z}Gigo*vcO#NyBcaPi72Bl0NYZd*U!3hVvN zX)8z*NvUXs^AA2f#9)*Vk=gC9uVtDRQ4DjMvLDdzoOAE(kf z3a%7B3|zw%(Kf}@TLm$*qyD6$)9)`9Vm7Bc)@FC#GaWF!Y;kvc!DwfTd{NxaG&Lmv zH`~Ona`%#+UduD(&14a#1Bv5R9iOA=(F#&TMU{#e2u@!61dE3BcM*u3yCUdx(VVt{ z0jAW>_)m`RKkJT(Z%>F>1S4gSA@z|C;@!_>i$a2X;S*PWu)ECZH2YC$MDxp^LOFS@ z9(gYMj|sXy^A56p5oI`TITkhJG+SEaWkfiIA7gTMWh|xvSuKj9Q^2}#8pvZWVYhzG ztWMuK`f7HILUh41+)#1iugw5y4j57J35)ctOl7H!c%c^Yy`Z@Op56U09ftTmI>(2lr>dy{V{J_cU6v_N;)n;Un@!<2d6Ne*`h6d={{&~Bi?p43^;=xixCdTdreRa{*(hGW#*1~? a*tag1me!JTe+U;%qo-x8S#s4e>c0S`)s2tjbL>4(MWNd*CAyita#3e6a;`sC&;M zRB~^nxX{$*C2a6(yL#0?r8Gws=WU0+wjvt~o9Fj&y_`*wGBVHO&ru~#pLjwS&p9@t zQ!!}IcbFgyHdO>+2W3rnW_!OD931@8@NDfZ4<)(m?{zvqq}VM|Qc~=Pu#3iSm+mq$ zs;W0YX8F3ri1DB9od5AP-i4Bpk#Pep=U%;iT}oQI?L=UJj5JC?ibJTY7sPm0F%5JS}o zayDvWx8c3t^)4nPg&l_Nu5>hp^vAy=B|4pi9XG7M6iRFDE4=@zlt0P{g{b{je0<<> z6JZpt$t^ut?(7v^>T@fDs9tVYW1x`pEBEQ7$=Eh5yEdU-Yal%XP2iBBCjaQE zSkW03U-6B%fr8G{)IP%Gn8rFibF!FoMH*W>+rMB5T{&e*iju|>OmJp30^&d=rLmCg z^wB3wQp`SAq?eYSpn(N~+czC=*s9C~wyX!x*2p>3>F zi%8C0T{dX4nyaA>tLk=5l5*s_{_kt2lbeylWiw_=zkhC>{%|`uPEf=Z&0Zt0dEYTa zr)Cm#=Fja-U>jp>a*n6KZ*ElsF|G7<4!7!;iP(GIL(!Ypk|#S8`BFA%?1IZU#DvPSgo4|zp={nXR^)|Z0uoEX%OX!q@fjFYQFUY{^i z)8T#*!ae#CZ?vi#5b8sD(|cSLZ(*sH&`sHyvk-Dvk%%*elkUa#wAfd!VkgCfoD)QX zDNG$mpWKAV@t}u}%%wRVv{x|Hl8Cy1p{Ro2BHm-%*^W>J$A@`ueB+QHm%wGyN3- z$9S(}vNnT;_DP|p*0%i!Q3B8DMa!95%=CCWX&VJ%`jgJ|Nozz@M$d+v#h5xmP-JzX zq|buG6^?whXYUy<4^y&SL<|`}So)?Lj@yQ5yWHy4D=Ph<(z43(MpR~2$Y51YTZCyP z^)%~o)%zOdUZ*gL(!~Sm>-6G}RTuYIxSEDBMg#m*AM!iTsJrhHm?xf6n|&3Xo61+b z@;&ZDhtt+-cPY2C1LWHI*9x)-Ox!ty(-fIC9lG4+%Z!|-Y_CX{<9<}4{W6w^m*7h> z9mcOaAOUS<%jetkQK)2hO%XL}72ysKf8IJmIub+2-EK8KD{sS;ZPJ30d^AF6XGSLb z`Djh!z(9BM=&BCJVVN8;VqyC2W4ez=oqN$mx`aY|gUM%|NeTNJqwcJJS?iG5fzmah z55M;T9?|m>Kn2QfNrYWVNKUyyjSpwP~gOvf6z{znOuN z@g^w|q+@*AGY6;L;`ek+jpAl4-sMoU3Cm-yw3>n$^ip#;p-of{L0<6$bRt&?ZZ0DtA%WEW;rk$+?_s5-<1UX^^|(aHj8tjS!ZoygP2#1CnvRq8 z)Gm+LV;%2?mQrYPcaSZ46hzF2T5A+uO;v6zTK|xrB@m&Koj(=uxhs@*nQEQ9q~h$6 z&S2ib@Rxw5b&}c7`dKo_nM6wg=`+t)`n~<{&aDL=C!jeaM6XShx;VvWV7ltBH`Dnz z>NZaszn=`BeiW9)`2fkQdZnIJ0=g!Q9;uHq?)qh7m&$NHf5E4KsfW8{969jy?oq0- zK;=gMoYyM`1PivQLqv4e@_1VFy|Gi2^~L&=*|*Fi-d}JO4f@^^;wy4bwA4pw2$IMV z?bK=6+Q0EsVm)R!L_2cqhiA zcE+fM#HYDAWg2?{ahRMsRh#O3MnR$ibI`p*>UF&l>TRC(sz2fx3?_tdt7JI!E#-=5 zGkr2|PB}GMzL7h{rNX9 zWPeZRkBHQnubhyTHEa%#V7Ic(nnNGXyhg}X=Zlss9uOaq==fe*$+%b9u<1z{*d7JL zWtitse3bKc=>6p7MtuF$-rNoTWm8%#@i1Hh#lpDp3OO}Iq$ORVx}zNt^+N@{bJ|F? zlQeLX)SiMx+`c|fR3x`h@Yr)fgqua2lwA>j$$AL_CPv2FdYNN6;G5FY_X(A4KLpF= z%nSq#J_Y}t58suR&K}r2FYL)!-7t59l+?bEJlv00`qXutUY9>O6Jju+HhyqRk(4yb zVVdH3ntv>@Q6F38s^C-bwda444tW)DrEB1xfO;sfEW>vP6qOQr-i?lGs+JZj%E-u= zuc?E(ymZrBzb>E)%!Bm?fF`l2WsJK-`F zm7J`AjDK+M^3|)~ZEB2b2q+IsrtTU-QxZgO8syQGUNT(&UDdl6cNsiN!?)Dk>$Jkn zJ#w0Gew>Cj-KZj~!nDd|Z? zZqM7_MFnCJfJSA@wNo!sg-?gSGfgA3n_g8p&oM`>Ev7Y&Q%&koNzu{@sW!H(jEw17ni{r&{{D7OSPkYp9bw12;aP|?!ympk z7R9lI_PlIzDk=o^bXf@$ur=u-&vzbyQ>{NFn&}^l~ex;ACYD{K$H@U8M46ZNua^Z|!{!j!@hNJaSP;sEKhgF_Cgb zPa{$v?%tp0!r;EY)R`3t?P~PGDX@5xMs?+C%5#f~CKEnzx04VFoy+UvU1>oj?rxei zo*2@n=%26&hF;{3A*~pTtT7dr8_hR6I9e^;dWcgM`zGkTGR*!pqAH^(N4AgNO0((R z+S=OrkwSgAXhzA|E#KJKTk%|a+S7F#4sP&=mcvdR^Y^p*f6PTn_R2E&#M-(&dIZs_ zwwghfHkzzf7l*mK-ij1IqO5npG=3RFw0JG%*)ZEGY#A0=(3-zI5>`>*QU2 zBv3gkE33vmeTnZ-$3XW6&GMSsO6!ICt1LB+kB{Gim9c~F zGwaw?Z}@g9+kJY@_uX_yji|v~M>t1iNox>EOnkZw3T90uXhm5lp&OS6WoHi|f1rJ% zoc;YLsUXpWz7(N85ysz?<^8obU=xm2x{(d$shDky*ShTw9>*_ATYWI>=%xyzLN&eqp(o|)uo81|I0co+YrI7MJA8f0#%y)CIE?z? zm;6PTeEt5qu*1Au!J49UHXr-;N>7DZpQ_LhZ_nGR*5Y;Q%~OQrtj+>NCX2~To#abz z)9D^JB)2c|+3kueSuMUS9B|DR&x4sDVj4kW-$s|+g(S1|yMlu8)6<5K1~{CG$Ie#~ z$CuKxyD>=PJ8Z4(xbmw2`o;^le=+lEC^;oDp*t23{INW02{%X;;y-(!{2+obFzj4_k>FS1a!=JEBj-yAzN`a>*1Nnv4O)rk4KFTZ1UcA|t#=1Xx*Gvn$F+o(s2KJ!J zh}VZPt3^MoB;<~mSjteL2EYBRGz83FYDo1x(~7Y}c&EvC(~1Rlq&ziiOhL2rI!sI^ zD=IknvZtq~?w1+S!cgdCNl8gQKE5(d&Jx-R9 z3GwiJjDMiBJ*;CZlEA{ss_(4y;>8P2-Lj8ca|naIMgSBkj4}Fgj%MYA`0-<6dT3&aUfp8t#~cApo$4^Pyl14pAH=4M zdkhqsaaa4)Pvp2KJTT}GGVqZa;nG_kDx~0;(oeFbJK0#0Z4x_*N=;Q-RWD8wu#c|b zL_bMNjpQtH^}1LJKc%)tRXrG*yG2?+OdKUiUvp`MnDJh7!fx!rc?~6aHP_$+o2&JwY>7P=}5BvW4GDM6uqJ)zrw^}!S#XTPYZ{O-wx)6 zBQ@AR1P8|wHu$TsYqsxhCqB@3B}H%swPPWMek8uj!t z^NR=gOM0WfCrt_Cn*1&ZHQx3GJNzqbLVIPnSh0JI5|791LR@TcezV@q;rbW;lB8Fg z#al72cUA`>Z-?D(uwOw7Q=Nz#W_e&YJWuvUdfP79tsU@oH=0g(fLa5<%*EvJU@r1- zdTgc_>+S6w=3zuTE$i+8)mko?XKqOSI%*sg4BiARA0dA(U{gTb-MhYnavHI!j}zD@ z$8qe-A%NF>%4TC^j0=oT2aXDLd*jC(X7UHRFUf~ov)R@1VqGpXGCD-*#7x>Yopj`_ zO2B7j2I4wD2ZY8RNJ~TgQPGo0{ALGE6M4iBvjXM9#mJnnmI?Tv7p>sC7jm7o))cpcX8 z?DVA9dT?MMQS{Aym+N&gr>{?__}z)GT)E=5zt=UOL3xDzw7WFv=jDN&SzSds_DRTE zSlsu+&~{I>Zgh-{B-BiLhBkT~@x7hCa}>0PUay(zbzG}8AY)T5`bDt^=2)nPMVy

5Ke6ddO=&i@v+SEkkoOg`W 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 literal 12239 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 EAK@i2JOBUy diff --git a/doc/salome/gui/SMESH/images/a-averagelength.png b/doc/salome/gui/SMESH/images/a-averagelength.png index 70e2afd26dd6ed299365193b2073c28946ed0695..c8ca5ac772651c15214eb8396d322187201641dc 100755 GIT binary patch literal 13595 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 literal 13679 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

b@R{rFI3w-&fUGRNK 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 YI!K0}S+lQUDm%jB4El7j+4cMX1gu={BLDyZ literal 23197 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 z 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 literal 19771 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 c9oU;z4HV74KkXfnB0pJQT=!S5C4bbPyhe` literal 5628 zcma)A2UHX5wjM-5MT&}ma1cZ+hy+23GyxR>sY;1JLJvxfbOHnr6hV;Qs|th;he%6+ z0D2He3>*l>5Sj|1h9D(CXfNly_3pa&z5CX>v(|hwYi9mE|DJ#E@7sG~O^tQAPYRy| z0DxOx@BU)|V9R8k6OMDR@B?&G5dhA8*1xZ9{^S*H0v?~&h3xn>r5e5b$in})1TJpJ zDURcOQ^~9PWTUe$jwB1~q>s7tA1k&*U$V`su2`GQwY0FcM0khDUUy19$pk%`f>=M} zPZFO_`;?CVBBAg7{*OS5`HAd5CnLQBlKa(>-eCz(=Uk}ShypT_O5P7EPAx>b6l5bf z0>XB`s<)V2(m&&>ARG$-d|D}zZ}ZSk#c()impV80oTQbqfJ`0~*chXIe8J>;0F1u?b=XMeW#nNX> zMrUKM;$)|oBYFIF%|UKU+sgrD^q_Pm17b5qQ|dehEWYzS==JO@Az5vK=EgDLhRzZX zE4Sz69z+;x(lIeTle=Oh)B=MYxZ^X6e~<=?4XVQIxvis@_h)o=2>pjq<39U3KV1!6AlYk~q+edkW+(Uc?MEU*d=&CnL_`E#S9ei){CUx) zEU~H1fk&LIz9@%6DveG1^cUP>?Ia(1_0t*Y-G2ON*j%^IUR>!cC7fZlGuYOCHSx|~ z!X8m<|30pMO9vyjql{U1O6(D2w2DheklOvntlwYfzPVtRvWhfL`xthhkMaAiK=DW% zZ^Xlr`l-GrKrMkLu`xX>6J{gGSCIxIrKwM6alPSbvQr0zK7^H}StQw{f+EInDjrHBv(^GePt4>uFkHjK>7W)}TSZL5k!W>oBKSS_{cU zUW5^Nq2PfvzkStfQR;Jl!b-W`dwkSzqgXA9x3`Ak=qALuGZ1t)jqLkzne6Lpw^P%p z(?VVQVG{`VJPi4H1rdzU2|MgI!NbSBEg9oEJ+cQ|P=xm{3k2;cy|=%1$Bg_9V-t4J z$Ah7cB@$Z1mKUX^hmb=>UQ_IeA!5D<*_V*rC2S}?D~n2}+!qz)?5(aHfo)j*wjHX7 z55==^!61B{sGNt_pS8v8$zvWJv}{q<@|$o{QwIaKJEf%s8M=o%g6QAZ1btSpDu~0e z{)h1H3y3XzS}SS47;>=cgzOem^($n^HZ%u4=_Og=4^I+IF~X4Y6w0{ttX=OTSteqqHti;Mnm0=O!^Lb`bbe%sJk6y!RAC?(AFCP&rj^#<8E$KZ z3U$L)-#Fbb`v?@is)9Kfy#qS*T@aXaVoV-pF=W#@gg5O38?~RYJhdscW@xN#j})qQ zl8$>F4W4_p62Yutj+1Y&l!E+0IJnHeskz}3P<8ZGc^eo8{f$b&f!o%1p`B|D@|RtLElyAJ^?G^ z@Ap0H&@i2KiX#y5LqO1^w=dt}O5xp%bm0Y8Inue4V3{JBXpA?D=r(c;dgf|hhGoR{ z2pPsXs6(-Q-M4Okb5R=#V5(>KPNbf>^m83DdC*(5)^Lw*Nl^u~JR4kWsEU~*9+hvD z*Z{XGjnS15jX`%15aGrL0f5fa|A1p$pmiD|8{Yr=IuBs#q#a2J4Y74eXq;K~BM950 zu9V(2=LBN%w))$PJAVc(1x+2CIBjJ^^CA>-Uz4ts#tpN~_blOII1SlbybZ3F_JTIv!JyWawQmy!hN?s^QS5Y>pZy9DG!9d{zD^xzUOz{MZ@!L|t%|gS1n>23#lTH5`dQ~UVQyef zlhb6SteiEfsJq(akbv2Fg!WKLLaP#$dmekobX}0fOF{VPaKtN>PN#$=!hS3+Z6=k_nC(`gX{KlmYoI(tKtQ8K5=kk%<# zaOo$Pr%fYHBWe=FNtbI2HL>dc2hMg@HF!wd%g3*^;aQ%itOQBU9rHdoAN{CLtN?U| zI=d$vh1?y@zz(Lx5((*4YC0iUcV-!H)(O|Kr0ZS3Wj}^`>bE57c42glV9184dY`pc zwg4MN<2PPcb0587U(b&{hoI^iFldEo;IQYmO0o`vo9QdmiihyKBWlp>Ziu%|c9SkyYO3Gv!=&Q^E9gGIB912w`A zibLt?sc7tY@@8e+mg^X~?Cq4POn)%A>-8o6ppA?x#p54>wr2KiGxdHF90aN%AKj zs0&7(1meZ!jv*;mCpp^>W~=F)e1-rp85oz%XAS^TwJlu$@Lc;j8*o(j{{!Wr=?}XY zhN?)3FIz-Xvk&W8<@#yI5L0uebsPFoI1y|_O8@@2pvKb;3WCixWJ`+xKd*o5Z*P8% zl7+Dk7jilD3)p!9$~hiTcfy5_sKTI`mtX|QBzNP)O`XDbw|jI~ zshj74Kb##LE^Y~~8$E<#Zrr#bL_%{9a*xO%@w|%25{@gIdmj780J-df0+4?wi&_iI z1eS#F_lQ5uy&}y#0RW3G&$yV_(u>q%B9tRIDZA5k92!7buu4jAe- zZJ+xQ=XwPwD=mxS>O2Z8mMf81WX}|D27t&*{|a$TFS&T$FganN1o?Kq<*4b%XRDP1 z9rO0qrvSd+Sk>u&!?0#3$f2T==Wcz=3Nqq0 z_r6!%#(Qg&SjiByYyN_09#Rj?xZMB=vpTQ(_QAar$#U%ZM|!(2rX!QCx11yM+xvBD z6LXQ(T=I2rVz7lV(L5;Clp>~8iDOvMoOfmiM47VWc~#2DVq|z z4+>D7myqc>mlwgexW~zUqpesUPp}38wp+Ypn<>Q=1BG>7Zt+Sf;kpewauh6G1S?LyGUYf~4PDt{H5$uyMSo|flLlHmE zM6BU|B-AkpHlun?P|V~tdxBa0$_EJAs-{e z(1twt1j2u|j#dAkA`y>iogaW=010L%v0lqd_&8MX&p7HYsj007ciTcA2!ZqNvb-Nf<*g-nillUeFQ%$`0kx5_g8;6=?Cm2$Cx7~?~gv<0)t{c-7Hw(wh#I{ zIsRQDbx%0Pe5xzzuJAf?Q-HNa`vhL;JT@@h0_w0gGBM$;21RlKL*`$nSY^gB;DO!^ zUOmZXpd&_)-QX0POIfGRmU)lgq4?sP7YljA8?B*omF_Js9(};^R)*Q3sq$KXY1~x@ zYDwZp)%`pTQ$;iJa-aC~!O!&z-QlVZiZZ*Z6UJ)t|B;&Y6>*#`kmJqj+P>I;md^Ze z+Co%a$C+a9hR+SGq6L}R7{X?bJ<3#F8_QCDx5U&U+SO}%%=Ml-AMr5KfN}PDvXf`6 zktHdJgz+w zec2r9W`CVIbz8(VOMsnuZfEeM^2`HU49tV3YaNvJ12?_$^41% zZ@yroV3N&@tLM}T9jaCMV|6on-s^8=AgMGioSz7Oozva0CvT<&a=(UBcMB#rRLSo< zxJSVY%gpQ-J$nIW_T;WbpU^4A=6PcUc%iEr-9G?exF#A{m&u31g|6AO)7?i~@60y; z2mwVDn!4w|UtO2IMIuNNI+y{u1#Rp6jPS`f$c?8iZhf`mgoAW7#r zTZHfrgR}_tg1aZSWA`=gJ=f-UJdhUx!pCJefa3|%NB%Fgbn(4%(oM9OIBL)0z8j}z zR}3S$KAhe~?gHa-tWi$(-x6!4@y7tSna?x#=uSB1RXb5FgXQBO!1R_N2vUYUfr(a< z5%}qJ+cT76Iph5qm#6|FffwuQ7%j#{1K${e6_8QCQPq3K8vIi3rkH}ErhrtUOb#GpfDJ3Mq@|h5>V)7gAmW zM+HSm9ofcK-Tx`P3a@W^b7V{JPUq^X6i-c5!C5})nXFPPXIoxf6$$Re;1ErcO zg4dZ%CmR(7DfTOu%vEaqUzV}HQosrRs2XKIySEagUG(BsrQUro2(=&!t^=9Q8O?PX z8AiQPok$;T6cVULQsS90{_(L_n#)4fu4V8Hmy?L6o;%q%_26wPg|3V$6@@`i>#t?7 ze{}_VlQI|_7zBg=;`WbQeYYqY5N_VPsYKQu4_=n`!+d_`n;nd#m=Ev7M`j&~({l!c z8t2_xHVjA*x)5o0YlpY!0z4N_a8IR+J^}69yQP-bp!elYq5L!au-$&IGgfM@=C}Gl z#y+5)3R3-P-VQCd+*sZ?`9+&Ox?klzSZ}+ig`{EUy`(=| zX1HfJ1~d;rCF$bZH$ZbG6oGipTBzXfP=WVQ%C!9NJRgXNxg_po5KMr!EZ;pb#SyP^yoe|BUoK5lbzVD${f89nbdz)d>g9bLFgWY|q=xt*V%shulCZg9H8`-;bX+rc$&7l(t;U8Ew zKRASC%i6)cUVPs4#4|E3W=bM+Nv`NiQ=$h~6r%QCF@@SA4Mn&2-Vv-%~$Fj1|MSY9htI z0UguaZeJ%)f=a{uu0QLnbg$GVumh)z=Y=|Q{?$x$jAXTC~* zbOUr@H=7ChIq{UDrbN#6&e7EQh`k|ndxv3uFW1$=5e6?$kK>;;o?Z`bou)q7MNoG5vm<11lA z63Ha|Wlc4c0O;FQ30T6e|N5~H&l^)1@!c>cIGBDoD?Ir9*JP4FtYsA<_r&%fEt2B+ z$LUl$jC+EPJj$Tf{l~X+nr{WUhcOkq$(Zn&Va2jwf9x245PgsD$k{^CB1|CP&;CnW zF~V?Cy9qaBznPW_)n>kM)Rzt7no~v7-iM>~m7k}1dPC%Hxo@55;z*;j7q|n|gA+Ta z@Io(!nGa&4d_SqlvwDVIj#nP)R}9xW3is0cH?7iGRPVPnGQYkfsCh)EHqp+5i~D&b z%3b%`qPc~Bj)Dduo4@n0u*lL$&h>-i$I1^%Vp;)?b(#t!WI_@eZBPENL+<%$N9~ZpqLE?aUMZ&u2C?Uf%`p=D2rREQArT z(#Co>*AoVHL`JS(dYjPWo{LJy*M$oYqKUbpk zn?4-87As+wa-nw6Wk*Nu)ydDeB+K*VBSGu4-}f#N-*X-MqGuX%@eVKV_ph5~XltEi z#MYZr7LA{%GbYwF`=GhoAp3K+wt8B(E@7Z2U^OWrEbIzpQ3_@GHd2@E%D%diUi8qf zxh&hI?2o-f-&B~Ue%$SHbzm?;e2%V}i@Rfz9do+getrHLQkmk`4_{e`~(>DpLC@KQFzHr)kBoahWShZxu_m z6P0V<(jl?$=LhikxDg`W4JclykxmTLFXx*Woi(u97wP?uaCgP(uJXkQn-3$Y9C|AI ztN%srwFg1GqiCT_eE$vS(;V1M1UND0bG&U0T*n8bmlo;b9#?nK)G{f**$y>bFB}Tf zhZ+}s_Uv=b2wE2gAM>eO9ftOJOV9gh4`$u2t@3VSw6ASG$Z~dFEgzn>u?ie^=MvtY zXnpKkMrtmw^n{KE6BvzD?~-S@iX~42KlT-WK=}Q0)SRW30F`w05RU7$Mt?}v&9)k2 zM|mO&vIV#NlhaRTYTaa#em)^PTUCy8TeL58)5@uH346a=J5N6!UgC&hck(_QPU>fc z%svF{7-POU!aB1;Od3vb%s!`0bY#JZ^>TW6II*F=bfQW)eQMZl;h{ zqh@?UHpqio%Nojesp&5?@{3a^m(|}s%OoHz(NK+|QgixYeEhWJG96iMV&sTx;**>u z-ItlB>4EV&;&~Z$#`gUm5B+ARCAdktJU(V8*(L$0E)=oXj~W94hX!l%(njV3(H!;p;`Cw13AkXCmQB0ldURb~N!gY$u@E$%}6pHh5**pOYlD48JCfe02 z;mls46`x$Og1L*mZ{KqenT5fYn&g4V-36a#+8Y-?-f@-p(YH0T;xbOF`58H$wBWvj zH6``szR6C*X0>K)_x-YCkW~GWrsugL@pk)8Gm&RxXMx*C+3lTYTX}X7t(1vN9 z7kmOP!Y5^$$UNF5gU52XS4qu*l3L}MIzf@?pXMJE&Ycpu1??7F%X3q-?IZSAu)Za!uT3y{;_SWRBj zQ7Y_6yW9&9CljAAI!`ix2}Es9t+X7u$qkmi;MrEf^+e%}Azx%ATyYgJ_$^@&o7PJu zJIrbR!l3(5E{6<4 zdQ>s2tdE`Cf}|a06UWcWuEt7?v^1&^JFuH7OV{#O&VTPyXCKGgftye+fXi1p}kdeTme=dS%#Sqe1L zYe!4I39&=JWjxPq?amq8ZLu1rS-;$UrX?YnVsm#SU98gZ+)adB)UXjr)kZPvxd}d2 z?Z~7Z!C##>?8MFuT3jx;Y~M$lsC>#|yBFZ}PZ<7+{AW#PT9!ma%wV{Xy>wGO^|6Hr z+S$9+zjxt=JF`(Sh0HxVq;O>#W5Jn*V%}Q zief&0E@W$KYr{qh5D9z%am}~G*2mNukDe1nb7T?!GlR~+=WLdf@eSvHboKNUUNa+u z^FQ&EL=3zHw;G5n`=MVfl+6}c$s;8v_hZX??gv|?M&Iakn49TCjW^%mg7Ve# z{$^!&S87cx#ALEAVoFI2T>IF|KltAANcb_{-4Fz=-NBD$hawu8->^k^1Ri$r zZpJX&D_00KlkZe(3>Y=)eqEz%1+zN?Jk`~+>O0!xVprmi5M>E3qQ=9n(NsqiGnn=q zNh2t5LrI$Dg_iM$hOu7SLrdM5!&7JU)E{~;#rgR7it5n;bPE#77{FxSF4Y{H z&WF#*$;pr}p!-QcNH{c{!h5|oB53=Cf}K4kL&U#AXYFZnO3IVKkPvbjnx86qHMv(; z&asSI!?U&Bv5r*2fp5#UQCF^x*L?l`Bg4brX~@&8jP=dm-15n-#ML{SKO^L^PdA$> z8N{28?Se2?7(?FUgzkr`Wx;6=Ow$j>Gy4q0F>XP^#Ikh2Kisj8JyxiB1z7m8!}0)0=k^WMrWBq(Tgh;u0$ zOz}D=+pTJ4E!H@r3)Wp9Rp*P7^(3p7zUM|NDz6qA9eO$ze4Z@A)j90q93LN_?N7u- zM`OF5ZMXHu(Av?(#m9fp(0Kj%ry}!6Wo2cSObQQ9*Hi1&E2Q7mW&uWwvYUem3hVoN zeH`(ERszkg7nvV-IbL(S!+$lI$bC*sEIU*3#bx-E8%BvvOQ_Lte|vmxQps|c!Iz2v zSsKyp@}R5y8^LG%nk-WVjMYfgPKs=6M(%HdB2E+Kdg5oU{Ip6Xx-%HO+^u#3jvJHi zWjl1~zX}#BH%<0ty~AEV!@wKdnaJbzxUtVrBIDqQ%^hJQw|06nVI*yIwA@x@J~w{n zIlRX`_otzup~CCYbADkVf!!>&-u!HP?CfwU?f&-i^t3H)2U5xybw^>KK0P;=@N3~4 zDn1*P_-m^Wk6@d%9-HMYIB=v$jKxP7b#-+P%in>;#q_rq`+DFY+_9ClwMOgt5%znd z;C6`xlFvzcLJ0?y^3rE1WRiQ&b|#SK2?I4t8BZ?C^$FO_rhhEBIj$WZjwEyE8oMc;S)s^(zbxqo(<>El)})5x*a*by;g6*vbp|IPcL=IadL5CIMx4o&Yw9NnS5twXY-P}k9sb4 zY{#AoJ6F85K#|6tC#7CGN8Ui4gHbH&d5*kGu6`Lq^r)5y5;AgT3naLcEE|g`O1g8yuJio@y!7{9 z6BALUp9@uSqUHN0O%xIYty)|(b0}#dQd5VuyzUWx)$Bu%8%qqI`h074yM!&%yG8$9 zWdax9z4v87L-poZ{zq<@R#akQ|FvDLhi7|R+qawZT_ZEd)YMd_K~!WUA~JHjHdFjo z3b&oVTD7Svc9M?|T(#NEW*)H@>>DPg(M(U`GbYHp(wV5_WOK~`UtjpQZ{Ip^CK$b9 zGscXfl%*i!%Kl)3L{c=8+4#fU+&o{sl)=-}6O<|R%N3L%1D+&X=a%Z%x3>6}iyv1m z7O)?#X@4YnrID_lB$(HxaG2{~@2B#+UFdh6yzK1hfy>QEOgJ3U|9T1YQ@J!$d@Iwd zrfqmQRIewrT)Pd{UX9SKRmF@!z1fxhaL66wQsy9^-4wOpsk`PyhD}tHRl<nUX|W>5{zI9%lw6bisK2FJ|{CNfafybs1@_PleBrBf9y zoGz{)?86n4tNjKB!Q$JeE&rX+%)kMo?U`EQ zA$7K%t}cXU&jKP4H zH2<=9_t0y||MKamohVF}z+GZhaBE~Fw(Q_gYrWk)7Uu(xpF*?I6YHOsbm}#h8Q@|1 zE+NMGYQHBTAsI~NPg>8`-`m?mc5r;_iwVHS?o4Tt)??X;1?b?jm4^5*ArO99_d$yX z?n{*LP|%s5p}%ILMEE+O%#c+%))xm+jR*(`aQ_&Hnb}L%@@3QO@s>FB^J|-;)j+To zKpQXD>xOlWT+OVHi+*Rw(n}Xy=k5>gcd%9miisML&!=7r2?=S`yLa6c=Y$Zji}{7J ziI~>7z9gg>FOw62g@Z$CeV$s}0&rSI!6zh=Q_HNN7#i5zMoDyHlxzz~&DVZ+ie<&A z@1|2f6Nm$&s{i##Cp=Z%6>mQT<(sgR^HchLpb=$$3bK!r6Jq$%&{n!(zU3nZ)PGL|l=> z&b;3fxTj18f1+27TNJ_ktaLdw^hYC5%HMd3fk!7Hgn0bHb=wRP1(!}A6#LCfEsyh; zByA+gJPtuV0qB`y#Z+@&TTxxDdyRb4I-533l)`Ou2hYL5F?gemgM@^1=8}N{*Yr$VPp<>ltTkQy zl8Eo{oNvTMpeIhtGr?k^Qf7_vhgT6}843vzKNzq{Q+Ctp#9&;;$oS|F1T!3{ub94C zwcbx_1$QT z!?~d!PDV#JyQLEi4rZsf^~6K$GlZO$uNUE(=7_7(z>tm~c6mxC@zATQ)2mf!k1X;_ zrwOFg+kG7HWVWy2F9t}bWzG+q2Qo6~i-22&&^`;D)& zKDIMj#!rQZ5ustzweApSH#fM^EF+9unG{J;QFs{{nRgcf1K4Np;_mn)G2Hs3kx_8y z%8VC$G27MJN!w59Yqv9Pg+gHF8=b-|)N3;)c4m);p8h)Hze(b>GQ7Jw{<;AkdP%@a zv|m88zNU7KJ^@$-)d0+f2e)bs*2^zWIh&Auabrf+;~i#0clAok>y$l- z)1q6ONt@{v6Mo3%OifMAWXYE#P$I!QS1SR;gBEkVvr(Y_AL4rTXfu@=?BdwX`RwZp z{2;Hr+~|+V3Na&yrPCBPHKhSvAv>FFIGI~PTOB{de|=s56i(}4v3a2N@j)s}EW~*u zM)~Ococv0~y2gMwdEJtfq%$j9#gg}<50+z*6E8RudA{aDx69j$dgG53Jb&dT&R?zmlygfL@Y8YN-__fTZrVlg*6d!%u=Wqr6F3W{!_QmO7)QkI0YG@Ia_!KNE) zWwERhSS{IH9K`?y#KT(g7XMLXUs?$r49-nR6QTMsNXYoB!C{xn^NwpEmY&w0*I=lD z@gqh~L?s-FsA|MaxLmoX!~4zm6N6d%?TAR|I-osdljUifnGF&!7!EL_m;;JR+g;HO${v z{cztmzL>CoU*GzW{S~zTzT%OQ016B9_qF#gBmHk)`p7^^0^=Qq5a);}e3w^W?Ld3M zK~92LSg@ypmm~aLIHT7NOBj3c%4Y4_bKtUTv4jj>haH*4@lFy!(hn;~KH(hu^C)j zKRWb_EF+jKP7kKlzxfPW;(XtT&gsPXViFOBN}=Ft;$1;k3Dd~#?;P5#;j~JzxJ*o(rmkp&_!k#Pl zmrIl!95?`fxw`TIjInu*n_AnR__DRGj%&K)%K#u!!Buwv?>L`NDFcwt=C3FcQImnG z#49qod%m|+n+E8TLa8RdnFDD*lb^6oi)Om~&(VDL?Q>Ujc*5o1f-}aW=}*)`a9~oD z;`SYMpvzdu=V`gau2LeW75Y1Kb2_Jkd1`z09C8-!$Q)uugOlvO_iCCsip)B9@UIC8 zcgC`MNGlgJ&CAts13^2wmlCvG6GM^@SQ6_eXn`!5mRJ(oR&Pq zt4*g)_9>Z}BW7n+K)>{pG5Y;n;CelZ5B%BWiHL}hPUeb0!DRp?B_B-lLZxDsT0$(W z_A;FgYisLY9UVRZ1KO^2qm^j2%A9Tv;qf}s0aCUBI#7{Xbx?Zx8*nFe9=9B3(?uBy zc`^tWQFGJr%zCqpPK@B_?k9be&d$zECgWtQYilM;E&PCQkcx+v0SMP}wTc38A}A+% zqv=9T_ZN_QOSlvn@sU*ide8d?7dK=%6{Ti>Cg)!5=_1t*K;vxe?FW@M*Va0EBS@bh zqN3_||G;y<-LL(+vt6>o`tdkvy{F_$<10RArb0p0p(>M!*?K!lJHrtjNVy%kxOLLUd@Y%g%ot*F6t)J*)T`l1Xf2od1~9yrR%%8Be|{s8x0I_cmWTd*1Hp_Z}<8&8-M=v@6A=tHMy{|8V&Kir?!_6!!O2lNtN3*(Z`S|V~4Cu?lN;p`r+bawPK79D_ z%a{#|kkEo*V#glPR5%zTBO@?{0P2KG2bXzwIwJVhWd3|RB1=?E3=0zz<}34p@z7_i zu6Kn~tA~eSt*wGu?#J(ISgO@NF12_(alYP&19UO&y@lu5nD`H3!Bp^=txwh^o}$0* zC9rC08%$vR4rCE3sz5+8kB4~=VZu{WEj2l4Xo6ZFZh~<&ou7PtxW7F;?jcSZurN0t zG@w_n5zY8C+UR5jqgHD<6ho^FgShs7>zGq>Rr!GOLq0FtiqC#2m}NK?02U_ z9QS{(Y%1NQNxytcTexJfzP~^8Go}zIAo(tuwze!;;$eojq5EtWzoP-gjS^ryV~#UI zCxppY{Ar%oN)yw6Cu_)tFGmvt|MY49^D$DxIr6S=>_*BAuB6hpm;LC*otjJwb9lpPC!6lr#js#E_KtlYjrJz?C0(C4C4gQz~_Jw z1GZlPmKjiZ=Rr>#ES>`FyB=A_!oNf(qjsRhg2()UN&p%U*Jb>3Fup3m~nTOc&t^-k(L) zLnmm@$Viw{v6>B#TFi}}_{n_|bSY3QzyfoD&3rbT!{Rq4F0MEL7$(1~sE3D#e*l68 zCebED43s(Jsh=-_s4|%^=R+dmk8MKOxhD18{UT_rpaKrPhJ&n2d@KLcQ~}M(^OYRT zMCWJ(Vw7CN;>28tgo9cYK0Qy$PPI&-2&fTLrgAg`-JE5V6cq9$8r(2A3>v*{KF{WW zJVh-|E_(30#zK9YvDR!hodnmbHD0e}vF0uFai6GYcQ0j(9}JiMHYTW>!Bx=n{cglt6=)?nnfKOt;T;0v;cIgVSNEo@cA)y{$&@^GqrL42}b>?d{hm3lwq) z{jV@+3E=9W&Uwl|AOaN+i56e5#CU0*+LwwDV2#gp)ee>YtXh7U)1)FmB`1La+BzbM zk_H{ue`Nt!{;J?l{=P;CVg0q*4_L62u-xhDszfkX1^bGN&xxYRkc3n|8uavE5@!{B zO4Kq+@x3Z-ycl6WeQ`k)+t}Fn*=j4+_f3)5_wi)ZXvvA_>mM6~Gm+SxYj1Wtl57=P z++DD+uq3Gxn^ON&#w62?xN_x}Mp zyUWA%7PWH8Yu9F?X3deGQ-%3pYJqu5<408-jR{bTjmb(oEu5B4n>WIy3v`mtU)y#4 z4hv`$FA4LjtU+3%w7$Et!Dlz^OdOeBr@}x(GXQ<8v#UEpkp@(ao|zF`M#fUCuxcv6 zdQ>hqc)DBIYkDn!q@^JLDIwBZvUoKl!s=6wUV>A-oEz@YF2 z{d}xgozw1Z*%&1LMaM9);Uy^5*)9_$o$RFTzzh{udlHxIY90Zy@+&O#D%~?NUolPW~UEQNk@A zm-U;rwzjLLy^eGtpNcL#9Gq^*izTQsPnqT(xj`GN0G8*?#g3GuBr?D2SvoLYeh~5V zKx_d=?Ck220|R}^$IHtL#F4?n(_pma&f^z92n5mHc-?+G8IN#kc`0N=b?jFh=C6Q& zA^QG(x%G-L^j83B1crs>fvUb;#p8%@pbI1{=y(#Opn)MrbvAhy>M+vpE*{Zu|6H}0 zgB&454DPE;9xEk0fIGUnZ=tv$tWYnGA7d2b%@yu>>kOVODCr_UE!Ux39FatHd-fqj5V~_6Q z;sR#AIq8w``R2$Q{rS()3D`hNK=y+zzIebBKL0ZX@E}iUXeb#u`O4N-&uqEg8~{Kq z9*D`X4Y>4MgjAe;qz<`D!M}-Z50ovEip(M~TE@iX8 zk(*u5Vbf)o1W?@#-e}{)3A(ORfmsc{3!^Q~xfxhmt9(HG3e1X?@f<0D<2H>USPu^m zs6e|P9sM+%%0B=AE|hT`pP$n+)x^iYD$#5~!>SiT-+DljHjob4OE;J;?T7Y2! z^J1ex;3bXXudXgXIa2Ylas_dexVcrDZqNX|-ZV8&F`l8qP*OssujU7sNm}NL1&Z7D zmpw6`X+GB&oa+7;iUIA(t~hIEcSt;c`fn*|YC1YOX$NBRN{9d2?yi}f`E|Mdt;%>< z4n!&A6I4TJZ*RZauU(133izI>rqPrT0qZb|Ts z$7qK+T(hPuRK5Vr1}iWya5=#!RR?em=~TXi@a1sfo=E^-fcyz;yBu&7R6G{=uRs@^ z_!s1u%zqA?vsEyC80~L`qu=|piPY>&?kJ$*@%aLr6&V#NAt}isODUVW4&LRCrT(sy zhjsm07!VoNa(4t^nte6NHv}36-ZKAJfQ{D1=CXP%Aaipzd$Y_qbSl~{`N<|=mN#*p z@&hX~YdW7f8~?~=0;WAd^%ir zy7Rp?wGAKuT(-_N6Ztc2cv}-l+tu3 zaS(R%QKp#@^BoIXtQTq0qWVAF(X8*E(ME zg%_+8wbdC^A~K|mA16mgzdEwEMNELIoRxn32o%KF@9GLbHzDzjiJ4~q)($X+FQyU& zk`UiTcNl@zZxTegKX-R`&Q_al0ARfbfUD5jY}ar~{2u9;%0=-3cRf`Qnm_y>AB4i)SIx0LN^0;f}-t=_VS_($ekvdMhnBhu+D_sc^oZ1{yq&{Gj^S zSphvL6^X$L%H9d-Kks^KS$!#xCqLcI&(DuUqag z=tyX+j_EMHI!T66DpHZt<~Kv{By}ci9nno#?ZvMLOv6nvP6*bR^h-5mRqMKifCV5*c2W>4Lw?$(gGv5C;z?}b&-jHPXhn&QsR$u5fvjntq4>3V7v z#_|^`U*^gXK9g6NxH@P&b-!96S@eXU5vS!w)8>&-Q^$~q)Yu%%@4xm?EYS!x7;qh{ zJC4eqk}S~-Sqb5Ew7qxL2yK8VR#!H*s3W^`(5le7KC@4GW=wr8KS{0Z}9LNaYPT#8gy63mDb9FjPlK&8@pSNPfH)Q}JE znI*~Fw~Ob(Ntali~SXhbI5e@%xKk)BrI&`(Q!|+?Y>@zKdx%)11k6obb1nSByUW}HlO_131-y% zwImoAOD{-HPJZdd^XwUcz+D-u#qXk(IV0t<>^@0>+dVS9Zg?zQr!d1IrHR}jBbZqT zRE>UGtX`^ff5STWyS#SBW_xsri?`J9aIrO>vsC>XgsxVgLanuh*lzQ6uE|8qYhK3? z1e6A&-NPBJ(uto___;C*;_u$gz}~(fryUB9-GUFtO#1|usDNQ*CssRcX;V8;yaw|` zfd$O&aeKio_(1>&ra+RqI9v*AY~%&#kA;yJBtUL14@ZE!a(%r#a<)GcC*c`&vbS4Y zZpCbQ$Ay09{uQc50mKKD!@EBclvh_9096Nyk7o=hW#W5*i1ps*DQ>4lYT%8ag8vz_hUDg`F?(jzd zpG~DZk)0AqfLlpcH_Ud1l)tLh9Bc~uPc||(Z^p*@VC?r)lETARz}hjKro%|&a2On` zNl6JEPT6HKlEIZp!S5Gnu23g*SE!(1$Nh5QT4Mm!-@eAO+4!K)<5sPHx>6B*9iOe2$SVN-5+W_b zQD(PA4CCv2CzZm3jxm)+<>}An8S2ak|q%iM7D~bkGPxmr94_IuitaCB3zk7IQh#s=Idd%*+S0B&< z2sof1%m04C)d5AL+n5ccvygzy3ufXTq&Dnc=oWZ?t+Q(iRCKq`_*&Dx4HpaHq%^c^1nTRGjOeZSYJU8 zX0*}=y^lu2K);8kNWjXBj{>MoxJDln2j^nxH4CgUfTb^KmDU&V4q5DtFVuH{umPqZ zf_BdZAt<9yVE~oY_$T^t`dkxufLkLQo{0wlAK%}cS5^aE*;LLQFt5o%<&mbnVM1QJ z;y92<1E%K)-zlokQ#9R^^*+GV(r0!hWMpc%lD4XcV^Z@)ffM6(e~$aECb;|h{FrIi zl0HkUdcEC`Ihp&E0+-=O+r%lgKl*|t!{zZcF~95N7W7x3wQ(+)cY!uLnCK85MJbMj z#li=9zy0j`>Mo4O;Wd|&C2F2*l7C2pLt&cH{fySq@O=Hk)%ncx?elu#1m*nn4X;On zcPg{SGHFCz<2lW*_+4@3jCX~1)<~?9KNn`X60;0?(EMwwnFY5e7t{x zyT@gP)zTYpp8x~iNXGoI8BIw054;pa6oQWcJ;k%_P?Z<;kS2LbDd&dogmZczo`+dz7< z3)vEavWeo_#r6_!!u{X1dTah6$-G_+kP1$#Mg7^`4j|yaE0E8WGm8h>G0+dBUkQE# z$;=)PP6`K!Tk$JDa2 z4oI7^hb&lcQ8;Ux>C{{3MowZG=xyxmNV&M;0r=m7OsnfH(VRKq{l(xOY5y%?s&JC5 z8v-BY&tySIe7WA>Dczi0uy`=Qt`&B6kbzb5;M3wYfak-tgptk7R(-!KNg$Ft%3T|F zesp(tFRQKHy2n^#EU@$g0SGGNyT|RZETv|#PvKZnh@X>xS}$)UJDI>J7OQ<8M)$>h zbUE2@gvyrJH)a5K1AjEICpHPPIF2q9$gVI%u8-Mu5z!Re|fUL-stStcd)iwWRr1}Ye6V7<|Z~CU|>%?}a_YbyA)&NS3|cKs*T?NS5?J14MC6+(t&%w`t;08(Sl-UB z*DrG%FB~sW33;MVHU{iCFD#M7Vt|;b!!8+3^-@w%GR8!HBN=*YLYi78P`t0{x*}<+YWRAXMs`)hG*&jPJ*y=lmf@v{)%BHntlG5?oFPuYq>}ygZ=ahWY*PB4#|M zu!sli26W%vT(53ye9pcg+AJ^ypuWm0Yp+ll=5PH-w>hvZwe)u@8qPSKRRB& ze+YrKqvO*z92_-@vVP_k7IU>$OCr@EEja=PDL5Jvs1BbB6fveAvnWnsEB?P?q}r)n zwciyZG()2_qy}>bk`&hr3dL&d5A_#epVwT&KVu04nGMhq#RB;*z7b%mfZ}+13zi3~ z!3j1%Q2uIJ-2(%`NG~Xk`Y6-NJRpt$x|Wrf>-6y8I{d)~Jl}r*_sbNeo|0c*5CkSY0T6(H_H6C60s~Z2*TCYN zZRRDpY?d#gN`)UKfM5laKCuIR)WSeKRW8w>9=9cLqXFXgVroQ|*m${!xLRFRx=B>7 z{lR!bjRhoe_Xj?E5R@AO84t$#vSjB^0|;6Gn++c1^TL5Wc?4ve&24Dv^v5e^Z-Cli z$_xf@D6T*1-p}mXg;3jxr-IP!$Lot2b$0+FKylaSop3hWQ@^^o9tSoXG%5G&(CgR9 z%Yi2osu7iytgMSEa=yh^+pJzsN!Cg-2eZ?a3W1_#Ty5+Bh6uypek>uO2$(?sQ{=O= zyW4j246nv=!L$U}W*F~2SR;aP1}O>2ldQ21_V)InHEXM@T>q}W5y)DKh`C+v4ON#m ztx8Et57xK?n-eH7K(%`DRlwtx=_uygrfrCrh*jz8^}8U<)v=g!eBmtr5IW6+_AZoZ zL`qqu=7IeGkkRBEx#{ccgDRo>yfqU*LwvY9m4$LvasI5aK=}ePCML{cCH3rCy8T@p zpr3QY;t1;#+#msZ0|w5${y+*$gF3q{^f9?H;MMlrJ*@Xdf&LtG6&~^VLJ1iC@CXP7 z13`b~DEf&mY%tBB3Mj6YyADGuu#zti7CJ!6vXtNa%21?aA80@;e>VHa!{T(=5{FSi zB9Ytcps9;_b4FS7c<0;|L}!L}H~wkabB!}+`Mrb?r)AK&ZBWCqfOodGvLXZ`3c%#o z?F}c%0XC14(Fa!jB+$IN!rMeXNK2D(Qzrn)=F}cIvCx*Lmk{~NvEuj2uCkWLdyqrk z8Lvkn#jHllP1Mw#`?3*KF9LjD71$C$507WEfn8{H%Dc2{F3?sc8My$7 zogokm0AU>&@L;w<&f^&dVHH=4_f0>&z_Vx1fD9}GMB=}}?pS)Q?$!6fAmjnu>$CD^ zP3;0=ue*pPug4Uio&lGIxaIC-0JvU=g{-eoxpP6wVO{Xz0fR|KK@k}p-5EhD&gHr4 zTylihjPhT_scs7H`6GQ>k9UD;A%$8oPO(yO$Z+?a z+Fw~~J+s1@4s5dcPZHCI7K@L`Oy#{=mNR8S9U6i?Ikm+fq}^GbM=+mA7?Aju)!daJ zyyka}2B_KwEG?{7!=pD)>rBT0A>@&m&%R80KJl+|Q&dFO{TTc=#8bv1fw)@E={bJ| zZBO!BK(%&bU83D*q61q@RQ%M{ISU2;$Oek{Wu3IXxx51#SpGxO=jmUA?jM`(KUs7C z&$fo+87BvHmSVyH(CXxsw1pAF5J%3-%|OF^P}lGM@ZZ~7{=-34R-Pw<+#=lp#e1m! zpF`*ktiGSKSnWAdakC&B2?F5MB^2)&a2QmmKrl$>C5ZZgILI)FR05SUk1O%THTYW` zWK*fyxFzm~3d4@O)BX)5X!z z(Y@v3RAblm@^W)@MT8s+0y3B6AcGmSAVTTuhl0amJNW@bjj6O+_;|f$k$%-CwUWPi z^A-490`}p45wWrUk~yy1j`loXYR;+i8NP;u42fKt6@VHlDG#=P6zXz|fJ)w1uCO;+ z4hO0aYik(T`v73`38xD?~c*qo13ZgU4%2O#;wH*?Dk@=I}6ycjgblwN-(sVRP> zy#rLvWTu1=*{@yWvh({3XdD?VC*zqGLIO_n^|OQt1j}b%Khr9gn@msu#{jrR&KO6A zz?81>`aL$ip@(9`{{7|%6aQ^VRtktbkjT= zW}=Xnp;YLylM^>vua{R}oWkok-Yo6}P5Wvn6)S=6Ee;|)bInGaBL3()d$Z+rZgqCs zM))5ZKyzD*K{JFz|rC0UEPid|dVjjzs{2e0K2%4VuX^ zl1T%?Sn;MU@S}o%yM+I<-C}@Q5O8Ur_O0#gAiV=L6Ic}~^?N{ZhX!1}E;hTJ&Lxh} zsm1*#RxQ=S47-^Pa!KB>yT~L&TEO?ej#X#VSNuepz~Yn+kQMYb-Trh6N~PEh}U4yxr&A z98Qrs$DdMda<*yNH$*2;y>Y`5=JUkoFjiUSRK^_$k7(2W1>&D<_{^_RVXkB^x4?Ye z1Pv#eRymZT#6qRQ83`C8uTY-?tiolt`NL%nw1gKRy!G>Q;r8J|T2&Pr>QkTh1BMq# zs!R0=tZ7~s^)8DkyiRncTW!>4jjDM7UMbXCegQ!Xa0>G!7aI!jzXM<)LjPZYnuR7p z0L7qb6}O8$kRt)xDFovOkb_phN$zeBTiHw}1C=#h5VW+kKo)TeFRe{G+zj#We8r84 z`kVP=ZhZ=H+mF}mkzglCrR4$-4!v4uGPm9K-WXoBSP03~JJ5DO`jbeThvxt!MLl zM2hbKb|&4I25e4c&L3h~-2UK%tOotqAkFU$5{bZKtEX#y1)BG(`%Po47cb;~MjKD! z8@6z=LK+Ot`~JuVo!w#y2hLOd2eLQhMD_O6dlOWpVx09X3U2N>_Y4=1lX@-anQSl+ z`_hbfw}tIXWjM&0`jVgEvy4NO$S4F70(;{F1VwtA(i2>=8Qzd_`5FwwMMNfhgSlEM zko>s_V)U=Lspv6HEJ@a2_M0U={qd&vb|Bc8WTC0qA@H8O?;WDte9->De#IijQl;|9 zs;Ls6_Q(N0G={1qSES{Yl7M}&x2TXj6hudche%SCGn*FhhL;X(k7Vn6G4cN^W%_9_ zO)=K!7MTRO0xSl;R(U}X3Put?-L^B^RWi4Dd8@2UrvdE)5sd^ZoXACMfHJ^F+;Q6f znf!iO>#k+K|I$quW}?!^4|7#0mhY06pT8OY?eqF_Nny~MG9{&?X!kLR)S+z5Ac)L& z9BOaK<@Scl?L+_rhDu=_HRBALK#8V~#FPvJTv9Y4GL``;qY4_A!8DLmQ&amc3+&p- zpQMSNVh?La!G1u;`!W=Ql#eYAPXKeLw@+Zt zU8Mj4m_U{sHvb>c)LpwDifV;E5^%UXriP~Ooq)z6t@usBV2jme1>~K_|XF@y< zmvpV$h72u@w|aSjEk}gEot9^`yvP|CLeHw~GqW=5Z8wm8o}q0%LK<(=sSfzimPZg- zdzrF7#EXVzMotL}NkvIwqV~y$2PziFzGa?^?xr(sB6oXYfaZ`|d;HGoJ*Hw!Xmq4; zQ82ZZuD;K6CxwmA`JG&E=02c0*E?g`L?lErur^6MCtRP;6TeIAh0vW1vYoj9q)RYq zxaD2?v&1*Scm@FNGZ6Z>P09MWMnd}bErRoD#l+&GW-`AU&bM#hxIm1IOhVYoiZPT} zFcehj0WY#VVB<|sKb44F7m>*Nm{b%u%z_#C;d7JA-TmG5Y2c3^q7Dx1WO49yUjb${ zpDlZWhljVlv)?vi_<;KI<;%dJ&~^|3Sm(TdBQhE|L9xpX{}lFlK@cUn-;eC3rq`ey zZXXFJTeydfil=jZ3I0|NziGJXS{O&2r(5b_QD{{5R+R>UqSyZW8)-n{*MytxuP zJ}y6)$c~~^=rHOE_R;x(K;sGs0K{`z5z)}n9?$B92_b&-_J+OgJT{)7ApLvNT8vta z+aL>Rblfq^&R zcmMulDa+fpFPXn2^KeEgNZzp?2hj&ZucN~K+7=wk1VCsS*8~ReT}8jzk|V;u7iINj zd;|GCO}k$-&z?q8QJto!B)8TxI5CO1UGO1$7z91?(xWb$5oyLpF= zi>n8^IkK|zqb~H0KIpnr#*mYcP*EYGp`lrGz2IgOhUpCK*UMc+b~t=a8%TbdxwU)0H%853)Lb|oVG~m=umMg5vZxD zRa$lUfByWbUHLn_m6sT|wJ8uqOY`0Cl3ID5F~;iJt>ufc(b2EPk4-Pp1K>c9CikLh z16}LK_#>6ylxf$67X%%qOs!bu?oLIC-8^**0H|B02QxaiNfzOR;A8@)vlzX{%7 z%Lb+rpm}+DA(E5 zJ9g0Be1HCI>AAh!2OGA1FkxGV9_TAL3|7EKSwO`)TbgfSUJ+2bX5$cwGQ=gd#-592*H``OmQj4Ozk7)T240w2ZvQXZsfWObOD) zW@aKba?E5B-eeL-r>8#5lYvPeIiF)AGX;}PEU1eAv%?FV#OIb zIq&;8LQ(#i18MGlm|&-%@Ru)ydyc#nm6dYx^4?8Nd|W9q#%%bL)6=0`r80SNFjdvn zPhbF`)nLH=4S{Sd0khxM1F$j+3yTwiTabb!@qGs*X^bvJtzoS^u2gSMFkcxtxg(nDH+9dbVvXt0Wl8*K|w)# zd&hUg*~HaP*|nh)Sb9DDIn1xAOS`WOFs02G_@0l4t>UVxc*rQoHb=`s*~QX6Vp-oN zc0|?G@NovUnDiGL&0ZbYczOyr9?X9)DPaKAVEJgJqYSbi?hBuy6ac6(kxA;>B{%aXf{+~v!JsirkjgPY&W`sgH4O=>h95NF#v@=s^iNvPMDJIfT zti#CAOk|DC*=Mm8ifv2f5INsd3GbNe!UU(t2vHtPWFxbGLEOT@p)bQL&tvv)!q#?y4dGN~?qaCQnVk(706Y{t zKJK9g+!=?uI-p95uNL!(KR3pa-=50eze|$C;hfY~LY5@u!HX`r)fYTGbTK--K#C zCl5rJ?8iLf5UQyfJ}vp$DTlnG(Y{?(HNmB$vIO+_qHsQ!l2Xsv%*Vy`a6pDSh`hKJ zmv@7%aYHP}3z0Y~o@3YW`NY)XaE6)cl?aXH z580u18MArg$MAtOsJ@74BK!`LSh+=vCl_??XjS}mkltC5gi)>=tA=ou`1R-!8kWPm zkGK^u7N>aZ(y%tjp1ydS&%RWcGexVJ#@Ly!i1OTcl3D842M4`CzAv%GER{?(gjp_^ z=iMzqb(*i~qVC?mFV?Wu(|@nJS_Ra9&K3SDhGmAHm&>(qI3*~J0lp*A>d#Jwe6D(l zHgUw(Hm48G{@6Et?KN-K@UZ&Xx+L~8G^#aHTj7VyL%)t(_Va_Uy=`*& z#q))Ul=O5_6ljPVD_9?5$!yFko!NKXfLjbgJzH}dcZ#mN&T&{d%5E0QY~NG-adRS| zmyWCKV@LzB{mK8v<5seAk*V}b#YBaC-D_sdk2TS=l1q_Eb$vcC-Q%?vJejm3!_5q2v46>hP*uhqm0gcAh7dNTky`kS#3vU_KL@VE5VuG%29kajM4IijVVXHny- zXO(8`YuB9F`+R+&F@9#T0o47Tu%PC!3B$ua=@}VXM4}thD7e}c;)?jFVAlU_(M8I2nG6ekq2GBfFliDKwjW9yZW^QHw%qRG$WNH2|T5^V+U48M*3npn z0->};s)jB0NH7o_ zPK=A=dxQx*Z@DZ%PLQ|u4{81nf6jY&D|%(2?@kW^a@pE$pw3TBE{1Nh-bS*Dh_P*$ nzn+H@i9)F_J3rifOCU1z=KW!%t}>_zp-|SxPf?0U=cE4ytl?0k literal 6027 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 Zhe-Qie@!<|g?(fPtjw>P;Y>X4{Rc-i&{6;Z diff --git a/doc/salome/gui/SMESH/images/a-startendlength.png b/doc/salome/gui/SMESH/images/a-startendlength.png index 947567d7f8d9172ab6aa8bc73ead4e13fb112f4b..ba29d91dffc017315911b93648a0da8c61e58dd7 100755 GIT binary patch literal 17193 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 diff --git a/doc/salome/gui/SMESH/images/a-transparency.png b/doc/salome/gui/SMESH/images/a-transparency.png index 370ef3026cf14bdd5fec1ef229855ad278526182..df10f7a987538b68cb0bb3ce4b35d87089283173 100755 GIT binary patch literal 9149 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 PpeZS+y)Bjd^z}agGVRjI literal 7152 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 diff --git a/doc/salome/gui/SMESH/images/add0delement.png b/doc/salome/gui/SMESH/images/add0delement.png new file mode 100644 index 0000000000000000000000000000000000000000..ed27488c9a790a605a9d12ff2b47d45062f127a3 GIT binary patch literal 15836 zcmbWe1z41CwC+73D$;GxsnXpc-5@b^NOulMgCdQ9G(&@QN;jx<3?SVh-O>%`;lKAj zXYX^qeZIZVTyQb-^2RgIJnvff{ab57-YH69puR+fKp+@0(&8!*$b)k5%Y%XhwrFE$ zpMpP69i+8j5C~@b{qKV~dQ2j)k=|KC(^=dUX6$6?;B0Ac2hp&!GqHCi7qc;h!T89P zof%;+MurX!HkQVQ_x}%4mrw?W;ol#oX6bBW3K20eA!iXMm#{IlHMMhI**emMK*%97 z;v#BpDZ6v-ns^iQPyVvYap98Nh`uXFMJZ)KBBT!^LfYdWpP8Pfud5nIWhy~CEg56_ zGp)Ycyj#*-0^LTcQ7`c*(G>al=%*gG_$i5~G!nf3s_T8U8J;W>O6y?Th&c~z0M3qg zecCV2_7huF#Ml@c0&(_C7}#y|>+GDItam<^hCniwp&Dy3C={VqTN_aj$lE*!90dZg z`Wga(=($XxR;$p1H*=Hem=nrEluww;;K^}*s))*sY41$Iu(a8!d_ z?H2e4UM|da6d9H4dA3Q@mgM7!UkRDFY2rD3WqrCN_)x8(xT(#zHZvF5*WNc##nIH*~?>(_}XqDxETbJc+^4P~a& zU_@s`QBie0dECtAyjj8Mu0(%=dARtqjeuaa->H{u0UgUjuUa{$qQLlW(oRoK4VQ`V zI-+OwStws#aq&6PM2o4pb%$AuXNypp*G0UdDV~YZ;S_WD1SVu6Ug zVjxXEz;>oan6Z$iC-rH1NxGnCCx2L;B=eln&)VN4E0syQ{_n6}VVwk8h`+&)&9dBD z8PXc*I^LQRqm0sTa$fFh7M_sHM#=1Zb{md8eRT5sg{qVz&yJn}iBY8%tGz-Si_L^a zkm6lhud{K}MZ}!2L}2T1ZP`)Gb@biU`2tu{YDL(hmKv%!qGew$^WNx(mnu-r#g4Z4 zk~De>*J=!sHrAHZIUFP@uAU~o61ox_P7aavx2W}LKHUio*K1C8|BWMRSK3_b-OxWU zaJhG*8!qf-443|z{nPyq{lh~UBrS)pG?5ODe>7)8J$T$T#&9}1IyBhR%*-ZGgBwI5 z)Zqc%x~bF}WPf!kG%^@98JyI*S^S3zq1KpWLK}bn{FyRPQ(f@K_Sg=8VQ3tvp{AC0 zb2C~5qh&j>6jxP?iGn7jVjb^hyksXt=y4~obe zNE2*ZSSV}Z3TitUCRq<9=zBBmXX1!0I^%kiKlNFqAd!{Pxue22HV%4y7WDAp6A$^c z=QgG?#n+2+eipY2TkXLgXI%HfNl$tU3GVXqi|eDJIQg90Lrq~=WJ2w=_=q�k@-# z+T4`uUEk;9D|>4V9A-wx(Fy`++hS5a>Fn(26wZGaGY>;Uk7FOUw3);A;x?m4d7;W$ z;3oiERC;PM+cnodN#2Y1TtN(4Mt!$F_2Go}<;iJdx_8Y537P$%V555J<@yNfs1rBF|4FMkP5`ljvUI>nZkmX;*g59h_Ch)qiyCy$zKOb`?xC+Aop z2(W?T?fn?_xVxz~AG|HAs`}WrRIJr}*}rezCyRFBg@y!`A*y4vY;j#^rIkx!IQyCPJF|`3yY4)};EvmU z_&H69K$^49QQlQj5{Eq%QJ*+hkQAjqOUjU5!olcMilla%G66bLr=nlxLx}mCjp5Mg zz5=)_DSaWq4MCl^0_&&com?xVHdda^>r;jr)7VvA9b+J{>pM6H_*}PE3X=}@H6J2 zaQa~6!^q5BSyS8QkT%Y9a}%`C5La*s9rgTbhNT#Bijw&hkCHQa5|vSw4#d}SgaM-5c ziHa(hr|?41ZWg6L-ecvwe*g3pUEHyOr;nm$6bc0~JI^wdG7LAw`l2m&`@)DhVFU}U zr}wc`{cs$uwEsJ*0BmqiV27-kS@w`!gffe6Ah;LYju705qSn^i85&;u>(X>|)*d{Q z4fXZVF@mPiyu3n!h^700>w9MRVX<$%rKRQ7;Oujr9~cyBDk|FV-YxP@id8<9!IsPV z211_Y=IScJDP`KcFzMNYpB}I$U4Mrw=**+qlI>egmLs1eiavXmaD9F4b0fvls)FCK zs=dub<pI!T?4D%^KKJ*QzR_y#JnlMp&a#CZGL0vEP{7*-ie9y-RX?AvYWi5Q`&2i~zf(d-~d;Wv9 zKBui{1%}I_$usYt2jbmN!QJxD1%1*z0xop9 z+}s47Iyw@DZ7=wk!vlWAcZ{b|vvDTB#)Jv78g}`dprVUC_u1&)Jezml?PGRTN)-yq za&SEp^43j}ib(1+)7f}2W00UTnZPFZ$(e0-6ksiN?-H;y6o1z7}kG>3FJa#W}G=Ob~#FfZ$$Y6fJHNqmln z9YhoqZGNA<8PA*STlq83`|4qGYa!=HceR@*(QaSSybrBj1e|5#T+CknsG@B9j4gV1 zdDc2n6821!kKAAK%kwnn>mTO_hHkTg-V4Mye?txoGjH1-`yQ;eU~H?il{gS{SIAc0 zRaw^>Yj_jijQ4A?)5a%gbawex*ciCDlI?16+06F6s3jFDBZ&8)*{*kS*gfhsuN3$u zV&cqb_j8?5i=z!u?fO~mb}Wr*YU-kvjf_#BW5j+EVoiUW<}F3^(qxcmvhv_jcz9Ib z7X{a+DW~{jN|>)}EAwYO@=Im+vVVBevkKxNzgLgkqnQY!CziNL^~5rh-Rd9!8;qX| zO}@hI3c2o@u(l4>v_bO>Y6(!P#1<8LFoGj0OJiyg7r#~L7yZlA3GUzVXOjOEhR>PK zvmnpk$*{;XV03`2ju}CTg{d)^jG@K}O~hbbP5a!%VxM39+EZ8gQj@l*i{(oj0g7!( zst4v!p_xT}03rvY`piCkVs@nW@Rfv8C4Q)+SR)ok)tP6_nOD8X!LSkrbWF48?k0q+ z*!`PE8R^)VOJnPKeO>m&!$$)HnEMQ=jQFUBM{+e@g?y&0A0h+V2;KwX{gNoBWC{=R zHeq5Oem=HWN3&H6wI0zd62@Cr@9%K*T9^potoDU3O-*VtMWoT$+sICqHoP}FZedRj z0<)*g(WBEu1o^m^(ae4jrbqu}!pC!}rnP3+q+*aih5?h1gDWs=@dc3g3e1!1sWY=; z?0)1r9+8hAL#fxz@)m>Yxw|6a;wXlHKQKamEKKAe;SYnIN53bZIq`d-Lm)$1Ur{JB z-&yn+PieJP+Cm_6YNjRP47P;@1}qq=CC5f5;<7P37S+(uC?QoFQ=`!lni{UPH}J;} zYGdsoPp9YRcAXxzd{N6*)eBBYW`y(6?iVsV@jEAlnKHVlMOOp~0Rch6ZIhaT!QHfV z*$Hfy+wqS$W{?K)a<@2@CdxGTVQED*&b(Kf@;3w`2qV8l^%)w_;Ca(iOc$F z>O)~t6m)cSOw<LYZzwIB4n zy>G^B+-bGFD)d(7ba#KgpQ ziKVfzs*X;Pj-qmGUQpiFgle8*0l&S4w7k5$oDv=iGD@atUp%8$g~?olOaC;uG9EI{ zix=>(U%$J_934&=_}0GaYU$`~$KxS8-u?7OMDOg_m}M3h%V)mx5Al%_sbg?82GJrv`O?BH{b-Pv+HyGqDVHGIt@OP)5S$3!(xL18lK9TXaup5E%ZFuysH z*Jr}!{!JH}Sykmcm?roHlQ=jcBK{`MjB`#jnwpA=YMHw|Aug`Rq#{o#UH^RHQHi=o zg3j32zEu_cxP*jPBj)Q7skFT6kdFiX9Pm2(eIdAfjY$tKkxp_{*_RXn%R~RCc%2a> z^f5gvPP`Jz`eMyqP2RNPirwAa=eMm3jmM6^BU;VJ)P%2EXP6WE;75+&1U_vZe$>A@ z1Kd5`Tlvn$8qfCFO4JAvV@&5(-9yClEzMWhUISPk?I4Zr z%n27h>ynnK2ZfZcqB6HFK(vs*rHCI`22lV9nG(WW9IvZna#UQ^x=_!U*jTQ&-!?#p z9Sm{CqxF33zt!xygtL^pRmsldy~P1`p9^}EPSDx@XA?}_q0X_|k;(zxmX~ix%_f#dzl(IJzKl4PN0p!;)?|VXx0^n+~8$0dz2S zchTL5=poJ;AT1+Xi}E(c`9F#AAn&zleSEUwM`P8-oEC%M&Y{|-zY=9Yg2;F4!n?CG zBO|d65}g97d^XWNTaET4+H%s-mjWdUtzOQBjebODpE`J`DxJ!pLrxrGC9B=*op_RWwmN zf77uim%ubO_QH#uw;xLC7JRyNeC@O4ZN$3w^VU>vDP5-1d$7wOZFu8FR$2`O^y=YAwNpy(Gw|1$?jDe3JpP}o?7#Z7#M37WgfMs~xJ;@DJzIHQ_IZ8!x5to=&t9hR zKm5;@T2e@BABUl}pC$iJw9&E=zXxD+H}dfzyu7?1N9HLeTNPq9e)lhViX8@VR?O{b z|Mlxv`*Mgf>|2@+J2SKWe9;k8p01r?eb4IZDgrLJZZ!>s*>?x$*DL?q5ub%gQtmpv z%-nw_qmc@@IV?q@*>LI9(yDeZIcRHwjKu9-wRwGUWW#kereP2s5h97Qy0)eW7YOKD zUgBZGMhV0qs$ucSEGjBe*MV|Ed*YR9j(2A?_5=H=|Igg2*!@3qYo)HHW@20%4Q>dN zK?^YsPNok0DHT(~%&$Wd{ts$uYPy$4Dj?HXmo<+MRxh-N;PDb;ieT5W*;DviSzAY5 zd##*I=nK1TWHdH5s;3uP(M6I8ce2R4ikbadazM9b0_nS*5|SDYA}=icVl}})@^N?y zueF(qN_62n$|#U>S9VuPa7Z0*E?uDBVyj6JWc;MHYy$%WrAp~SVMc^t7+?BUohFH) zrczQ}Irhxqp`p{YuIcJJ2rCb?IN8=3>75O;`NwEkO*>__Qn>d=Fu9+mzI{Ap=y+9n=)3lc&~pjQu%YR+yy$vuqvCWYeHJYSi7%x;dYrY@R}PCbx5JC;%S(^u3xqeQ2+=DQFiqdHVBR7bPYWdbk!%=7&3($^tudU|^Qp_QH(K*xZ}($i87 z!3O-nZ0mSyEKG| zUgz$2{_l*9jrH{O%*=Ab!q7xt3k#qE7Zh*eohjdJ ziVqizdD!DgfEGu(0s(=;&;3hTr?r z87@z2A6rD15FfucRk{A-)5G7te{0)v7HtwnQ@;R@R*DuNm1tuGy)Tb{-7#5I4>!&h z1_I5SmEY7PFsBDJ3%~0QN6*0%NMfiZn zF1q3mr0D+LRkIw~v)yw~b%m44yT%)*`V?NWgdUS^?&4R{ls3AFwFzfMFoR~#eWnpv z=qFySaXmYCF`;x}*S*>9U}*!gg)d*w5lg!>jaWa|uOS;I_qGX4Gfm{!>k@W-C|LT= z2zo6G@6H~}zCm+rSJ_kY* zBlfkvY~TQiO^hl(42Z+hOCAm>Bwqi*2mi@YnHtcka{b=rfZpXGFU22;{PEdnW8XaI zMZlZxcC%gIy)TA{s9JxstAXEl8(-{dcgDsX-HWYJ#PAfwy#uil>`KY?Aj z^4ebS7O0;RWo+vDv$uBIDvYJ5b?9y>Nc%?Yd?c^;mzU*g42TE?YQ4(`+T}9DhCG3I zJR-QrGG7_Mn~$ zF1c4eIy#!d=R(JxM_fs=!R{%m^>Eu~I(%!VFM2rWL_+H!F~jWatUs4%v_#FdB*rkW zt68}w17R4#ta8c*tXRHKcXr2)Py+RF_1gW}cxh_;mW$j>E3|LV0>jaCYHZ+UONXm{ z?gILqCFmjIhttCPGxacY8=K-Np^0Msk;zFn&Rx1dLpeg`i|eDoi4q+RW#w|a-G=OJ zYGL2jhK7b;zeL51S=f{WJXSQf=AwUW?)MOAF|UVA(B)z%YE^|~P5mUNG-tgLXD^(T_~ zoQo9gUY=~b3#2tS--J#QNJWxz4x5XKi5VIF>BQnjMMe@zZ?&1KFi^bq!#1j%qLx`( zSs9&}(9zPuc+T=;=y~j8d_qE@{<6Ozn%Up?e1%)LmePYNd~c-1V|jE`Fid&vm`}EL zhjplN4=%ita!*d*S3E?8un4mcfKQqZM-=Fh{<3C|6E+q1gXoSU{nTf~#KluJ=7VWq zv*#HTGd=gPxt11OG!q5msi{wnWm5ek8&lI0MD2pt&B+9yAJ2Il7C{0HyucXYhluTW z^z;lwRgkO_GDV_o@1@R%tl#32Zu|yJB$^rs_6skcef?&V9TI6jo25o~AXnip*nbbq zV^RQ3)-IkHb)@e9?(X&mxJrh2^brve zd7kd{tU4{>;6j`+%miG4!6xSaumiG_37eLw>FS%pC{;74%T$6jH8eK_o z@obCj)t627FUOXamb~u0E8#@k<6~nK{<3m%cTLByMDw$=SAGScg@*$xqOGa<-2IL` zC;Rx8CnV+j<*(=pRB;ElW&@@oDM63pzr>4EwFU;?Wt?dGBLQiv`$D?rc!-Es8(>=CjZ@amM zxW1(bFVpPCrWpPyiSfVuEH{~g+6Mqgty{Rl>y z7v^GWhWqO&QEi5H=@p`X2INs{86eA7iZg4zaFK{W-d_E(o9`n)4Tu*||5i2N291iz z2jtE>aBKirx2x0bB3A;1kw=dnZU4E8o(-j1U-`zS{)eoLr< zij$^VV_%;bGpKFpyOxdFrw$`lG>3&3ALF018i~uLg)MI`xdR%q>@q1pUtU#^o1445 zP&p^_96kiXC?bh#^-M>quVY7~lyZT>4yYugPaoays2~~iAi|ril(K%pd+^}F^QeRE zyWuH;n$(iikIWfy9J3aWGq&&dxHb!Zv|#j?9E%VQa3x&cFk%lLY~%-LN{|f?a=hdh ztuOox6PxcJ-e}toA^PvWh9IWz9=_^Z(3>C7L52uCyU$!{XOALK{F!+kW#IYA+dN+X z1%V*L^O3NuLGMMkcwG1a(Y=ZK81V!ijIBHO5F+rMsedSa` zDtiDiW**IIeSQ5%u3}$Z-0#y=wxSFB{vg2TU~E(Ml~HfoS>!9tevSJlO0f1j85$a@ ztAn84;C107F79tvE5GGfKN}wl;~_?GbEdC-B%zEflK5RrvOnwj{a`3!<^V1T<|%C^ z*kfE()&I>Q(ZvCuI6Kkh>h|NW zlJHF|wM;3dOf$8IA;-u%prGia9Ep9agoOB*e~E#C;ISIb4?uec0(!F^>sX=>DjF*1 z3@r^!K%oES<+he#szRF#D!ox8|80QbLCU{ zI4_KxTwFL+>#WA;tTRt``D^iFaDmnQmoaw#ra~4mSvN6XH}Q1$1U&>%)b8|0;~gtu z6T@ZRz;$Lv#L;U^Pfg9W!1y=CRa8`PM_!5G;!Dgm!_GkRySv%y{KcuK@Q0;{OOC&MDo%^%lAM>51IRR><-kwY zpScSAT)R0tS7|Z`xt{7?Ry&)qRfDwT+j64h|25M|Eb-EE(^7ymvTL~{SPzGcjEsn= z2)Yh@{Y0sO@uipf6hBxF8QMfd&-!$$ZDcgG=kFH1a}Pb~=V!hfiUCk9#tGM)ZEz`y znukDa)pS*6e0gv#ifoA748{5h)l9LzjGlp>}_kvjDqU7T1Dx&#U*7Z-^0re(a zz^K6edj|*2x6o|;J^DBsb6(3ryGMNwSDxZ2T__!r5EEBzvAdw6qNb#zV2c8;Z8fiYL%E~lWNpsf6RZ>|x38Q9C|Sg#wJ zb$EEFcEau3R1XwPe!c|={4ul&!-Y!GCJxenf!_smLrzw<<@%3Yt`fkw_XC@no(7H? zPv#Ynb}cujI*6KdpF>L*7Z*pzgNR*zTE&#t|3KSZzH^|cSr5F;n?L3AE6vurE<{mD z<;ug2?lXITNT&f+hXVSZqeuNyG9KA6hS%|;2iPDe(SNSmzS5NOhIeQr0$aDg;%4jf#ZI?u(yoPXdu}+P>=xdRqEK(EN+h8XjPxF_^BgojGV< zw%w!#h-+YnS7W5~WdZDO-e(kGr%s2Xw?k~w;xwn#R8@^DwnMbj)mS6yr>0*B?W80m z1W^?xW|Z^9 ze#j#E!(V|BBj@w50C#j?%VdSv{Si13#ymgU-@~2*zjZH=pT2qBEzWM8_6nCr=4&B` zNx3Gn$PXG2kBHWY##~7FeZFWV3fX!zVsz@l6O)rYX-;G%BpwAC0(W_PdwZjM1-H~` zNU|@(=285EEb^}=Ng|pWp!HM)Y&~R`H;O}!R%pQ8Ypow15<}~OaY89`2pAHu;V*a` zGUQ8d*JyK8EL-?V`xOX~sHFW&q8Qd%LEWXp2tfLwspm!fEjCfJ<6>RUyU6M5%=os3|jW17C3yLDXd%*o9~we_zwLqzwOnEMJ}t@Plbw>`!Lb+VU! z%WhEU7!Z`LwSNTtK{;jY?5fq^cHSx1VYbfk&6_tMSB#9n`X^ZfRjMx}**~$r8u-WS zfP8oFbuyWRlwx~K0B02v5_0*&x7HVdBMJmue}WfXoNT#flk=v^r?($jjzP!BcdebV zA`Ro`r@OTa^G%Mh#08ezkhKY9K3+ahd3qDq3qnMWTtbCr@y*rQLX+dj;6h}seCjC^n#z2CAxfq8q!e^JaAZ0Rc-q`g*F2 z34LVi0{Zn4%uBZY5=N*wmCdB>bAEb_IU7+=zR=#oC87#$kQPy_aaHOy$<=gKxTD}^`} zTa+gP4H?O?IWc$hvCKo!?((2-nhd#?)5F6m41`B+0#vDJ0iwAjKUByG!{D|LAo>=u zekwP};pl8C>=q_p%}inFo#^oH)#;g;g+^B;m-4d02H9=&gb=y3M_3f#er{_DYc4H~ z7+lBW`LtZ8Qec8g^ztPKk(D}dA>KYo-1l&~$)VMe}bY+^^+`8Y(%4lh8S4`~`7c=Cj zz@wr*aWFyB_*`TQ)MRspi!@3xN!b#6)FD666kuCj*MLr0j6MB}HB@32@iTF?rLriQ zJ{i#hB$15F(?CPuIY)98Ms(P1yfhCxR?1qgQQN+M`usQoi- zHTx_JDLcfuY!S-<_AdAwm8j9393P{Tkuk-|B3OMOZ#{?kmyb?Pnn6FIRsEcuVDh%$ zOtYRxV|}9*DuYb4I0`2x=h*1@KZ5l z&(`;c1os~U5BK-Fy;bH6^fC-R`XTUpXI}5r?w^oG>FDT4w9NhC`1NSRd)mhlC>$3W z91uuM ze(zpptr0x|m`SOxb-vVPU&+4=@etDF@DIfm68*mj`~TnJ-Vdyv1{gDidGj-T9?I{~ zlg6H?pth`pJ;Ti!{&`7IrSl)kmWk>AZCi_ri|cee`Fp%(>rjdmGfY1aQ?7RIpoT?` z7K9X-Gg=y2M3es9>38QD5__~t6G{o?wGBdnXT99AO^CN1a zXofW2u5sD+!BIQYqkngb?*I7m$Rul+p-dcvLf5+sJBb}sm~-iL`@e+9W<|ENakh~t z`;vhVCjHKu6_u4@VxnlMX#KzIWtL40M zM%$~JT|k%dC4ks9I3Eo79E8pVT{kl-EH^bbk9;AxCUc92i2m+q7zraIT5vWO;t5P# zCq>Iy@3jMb-BG#x-Rt7Q;)>eZJj;gaxnbF9={s6O3p`+qUn!-bqoLiCl7v1)U!Re0V>6B7L8y^n&Tq9n?rCr_;HnCR)ZFRQoN;Gl<@36xi~EfCH` z#JFB3z0i~&ZCycODH13Y1!^9*vloDb38n9^?&i2Yi;ju;{P{B=>RMV*EoCi{ukHE2 zznQ4O3oGPl8y%K@0inPX;pyh~g_sw&jn)61nc3RO_QWb1Z&2I8!O6ktspNeq(Q9zt zbQ~~F$^~;LCMtG2?_3-p)lxlu5oh(pR+u4~%T~MEY7CTJK+k^G&GpUo^^KF0lUge1 zvnkVSJXJZQAq?ZRovy+XI{!%i)PLw~!M7DKtLY?dditV`#EnO2gyjA&U%nK&-1x?x zH~0>pfRyysm+XctsLgU)am%<6W}ZuinCFl?-is&{Ra6`}|9+5MYcpT&Ev2V7xAG=N z`1-FHVVG(3C-`TrvJYwpP!$AHzwe9{x#P!7qrZbt4%O`(XaD3v2sj3|1fR~ zWJGq#=E4}_q$lQ8Jd#{!7Cvgnx1dO*>xVY}TuI556%`)aCAC7=f7^gx0SIe}XTVtw zcV%Y3>E`fn!pO*o76YM#^7=rkapjaXV6LZo6-~3Cq+wxU@tT?W?Ch)zEU}^@)z|p= z5;D}$p}nZk)k^chKhHs7(Qd2|3c757Ta(SHXlW6;FgiT89N`9YWQKStEG+!#?OWwh zU|C+%2BnUBNdWdL0|we}07&$tx#-4X-p>UfCTRLio{ zgkXGvg5DE*diWCHBDP-^>Z~V9i)Xa*22*^14TblDmnB6-SId9MeD!ZDv$BkIe${YA z3>Eug6)HTZW3A#(Qcz*ayY@Lf13;k8bA1m$HU%EK7^Ot%Wi-iqzicipSEkWo47_nLBSABgO3V4&yLNmiON(=(<`84+Z~&Vt84(E zcphlE>sKWbid|7&jsqIz`k>2(d~gm++%OaHPBBRMjt5ft$JNJ!C3zyaxwwSf53@5e z-ZI5-OTPqD8A&RX1olm6V`O3?WqJQ&uF=)jpG#Y+Uk%Ku@a~;#^;Vu3rX19iAkYw; zM0>Sxq^)7P0qw5wyVI&HX0kC=mHX9hZ47QqY;5$8r_t1)d#c^VsO8bZ970d#4b4e3 zAisyVNoVf5qzP@|AtAi(hbBZ)7A>t$#?n;qSC@CcUBZQI`9@kZ=afl^&6b+a=9j+6Tm*o~|x3k_d2ha?H)nyVKPV9zKqZi=*)O7X?5l zLNs&HsotnO5iaxMjrpaki;IGiQaY#gIPl(2FkXr)tBHwyULzy}Rs8L7#salaY!$k= z`?}j};x0hhMMNG18sbZoPuYZs``wp4)osJ-y>jc(`UP4uGc(J}mG|weTz)Mdv>TP@ z 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/add_ball.png b/doc/salome/gui/SMESH/images/add_ball.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe4f645cfb89fcaced35b165347eff9fe3af26f GIT binary patch literal 1578 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 Nh>OSwmqI^&{vRe&B;x=8 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/addedge.png b/doc/salome/gui/SMESH/images/addedge.png index f142dffb26b2e0f6427c8e9e5bac557615728ec3..7a5a030632559926267d32e1f302f4b342d13957 100755 GIT binary patch literal 14703 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 F{|A6+8jSz| literal 10602 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 O33@K~3R)#&9P(dJ))ATj diff --git a/doc/salome/gui/SMESH/images/addhexahedron.png b/doc/salome/gui/SMESH/images/addhexahedron.png index 78ea3b50f98248cf9b49a63ef8e785c7b0bd432e..0cf7fe819a0143afd9f65ff03720d0474a76cf75 100755 GIT binary patch literal 17546 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 Oh@_aDXsPh0fd2(@KlYIT literal 12169 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/addinfo_submesh.png b/doc/salome/gui/SMESH/images/addinfo_submesh.png new file mode 100644 index 0000000000000000000000000000000000000000..4811c9902d3052cd6896c47741feabf980dd6d73 GIT binary patch literal 15335 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 Q@IqcFswkApnSA~~0DmaVkpKVy literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/addnode.png b/doc/salome/gui/SMESH/images/addnode.png index 1f5375b9ae2763c4f6ad0a59235e64223cffdc85..1fd9a9f8a5ec91d375d86951ef6f66327950fb3f 100755 GIT binary patch literal 13371 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 O&>MLrxiT5!p#KBg#@A&4 diff --git a/doc/salome/gui/SMESH/images/addnode_notebook.png b/doc/salome/gui/SMESH/images/addnode_notebook.png index 4abab33589ea61a0b79c81146f571f4348eca47f..804f806df5011f49b736e619c55711d05c9d987e 100755 GIT binary patch literal 14609 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�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 D`sclZ diff --git a/doc/salome/gui/SMESH/images/addpolygon.png b/doc/salome/gui/SMESH/images/addpolygon.png index 5d02dd70dab6ebae6c7d4e9716053f9dffc55591..200999d8da6a349486ef881cff40fc2e9552a102 100755 GIT binary patch literal 16868 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

FEA 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 K#EM1UfBs)DZ^{q= literal 12475 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 JE5+Ug{XfH5j*kEU literal 13439 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 literal 11925 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 t{tpPAzFYtR diff --git a/doc/salome/gui/SMESH/images/advanced_mesh_infos.png b/doc/salome/gui/SMESH/images/advanced_mesh_infos.png index 2dc9f8f4822514c9b38701078985a9972186f256..ddd305f587eea02b9848081d71b84d483cc3c9f4 100755 GIT binary patch literal 33649 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 Sd?yM2$s9RxI6*?|+W!HODn0!G literal 24892 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

kbrQWH`@(8qoV4DC6`=dUT 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 diff --git a/doc/salome/gui/SMESH/images/aqt.png b/doc/salome/gui/SMESH/images/aqt.png index ae8e0d081751435e7c48f3e17d03128a730b6b6f..46ee94dae56e995bb06e2f26bd9265eb3778b3af 100755 GIT binary patch literal 25279 zcmdSBbyQW~+b_Bh=@yg@K|)Gtq}_BkA|Tz}UD8O2h=d>l0@5HQ-QA6Jcemu7`}aL> zoHNdQPmFQ@xMLvTg1z=ybItiYpPGa#DM(_Wlb}Noge5H{rUF3-7dr<<~ph|=p=`NX6J%C@$qCd`*FR}MWIb>sR3FH%nG zt9rd(U3~MBghbG&sWS*uh@j6h>zvfoo@}(Vv`wv<7RoRt-S01$GzwZKZv8e31*xgo z$!V@!n#XeE&L(uNcM+gV`t+?)@WaKd=sBwl3-H=zn>jzRVzffdsArr|NlEOO+NhYA zalmJptnEjMB2@k&R6o@`qG-!+b_RMq9a`ql69@Y`jWOy253~tGwLF z|3=Nc@e(vc@v6Jk!f`gA$2_l`aKB|IjlGe z7{6!?QWUx)YWPJ}ckl5~g(FR!kCQX^uxB#g&WM^;?Ohxn&GYAWYIgw z|2-ng-6-YviL{2RaeDaOoJ{H+nGa_?hQ8Zq>!k>KJ$e-W@I~XSPQ}1C!C?mG&sGI` zbm?nmIWXnKn1GbfuHHIio*8T28qUD#a+>2cQ(h+lWCwcI6dY( zb<{#z3wkd!3R~98lPL$(&*+!e*O_q%^a?_1!iOPe$E`yYsY(mF0oPkGrCbs|+}1}9 znLD0YNR4c`*shPn3N*u+2nW)ea?u8axH_-Mz1UdH2?F#ELQH0`Vb8fIdAEnltu+Vz z3}z1@hIkocq27*g*~z2+XvD68{`@i5$5r*NGsQ&1IUL(FvbZlTzD)?pL}z@6Q*iL6 zU8%VAO7-=%Vf*1l@BaJMQg<1d^(=nGZRdc+tRD2yPSrI?Rbc!13sd=9%NfOXmlhf~ zL$$^tdzXUH)zePRdJ`Ph+25JlUQ#g%MDx9hHhKzPRzIg*6XvVH6KwUo!$7S4+tS`l zL9Cee^y)%&>S`vfj%5q!DK4tMfkCv>hC(_RYDlEkm3(~SPdz{SmoHCB%Ho<@TI%FT zO&%rtWa=t0e(oCVg-z^n@Otb)yPx(m-sd2G6Np=xN+Ru2mKrMcf^qcXnBD+4$I7na4AKwWV znBRYiu4cT>O^nIM61sH9=4)7fpOhxoDM^EfOT3zn`3b?oPAUu1JBbLz5;M(_3~uI3Zdk4Lh7efjZBB{Yq4`~oRA?WU4hpVe^K-9oTvEaiU;<}QEX81z) z$aSW3pZXugc5b_QOpgYr^OF+-WLs2JG9caQZtXi>v8hR1_v6EAUe@V6^d&IS;*{w`i2dCD<0~@mmC~{Vk+mF zk5M0flzNu8W0_^f`#{U|6}q^g)NblM>-(lm7=BZMuF{A}VwY07Id@BM9Y zQ6Em#Gm)T|A~|BjU;eG)4T|zWMD%iLpMK|{gxYTJz)bS=M@|k+j;;}dqf@NAuy|qN z%`S85od2AIQ}Xf?v>z`vBO1?miTu3~>Z)>v z7?1eh*2IjkCFoYM&m3+J(-`w}2ea=y_ixP6BV5hj>n$(mT%G0#p;o-UzlNXU6@k2r zcB5~-R#HR+Mvx)}m85auNoCwpe4VL=^#{8H(qV$U;19WUj4&MwReOsl}fg_ zyBn#~n=v}3LXT2$#$x@9uQ;bWDZlE_GKbrIG#iUzt~=oPK)B&%rV1I-sdhxd{h)Rh z3^N$6_98_~*^W=mz5aX2W?x>`Uf14hzrX5$iqv$zHxnTg^!S*u(~9+Pj!pu6WImPt zuBoA(tZV2vb575V`2-(~Uo+ox!Hhc5JO)%nkyn4&P%siGn#0$$O^>jTCt}o;o(EIKGmcuMBzCTTv(q_8+bqqJVSZZU0 z4NA2B$mUa2bo7=HO4rw{FI#I7y>iF92M0zx1m!2}W}>3|zfvMO zxZBxNG7zBO+?)(D5WJ-l=EvyXido#(p-km-f$5BJMDEX2%TWe2rp=|s{Y7;Ql^_yy zOw?Uw`Ky~C^vmf)-^c`whlfX}SU;%JWx+u1ZcynV3X0M2MWYBdTL<=jg!)&%Va*(Y zwJ6PA?KcTFLs_>r*U|dNej#`DFD^u$6RgxXPhYH6-b%i7IK_=5=YPWFdxsS)gHWe; z-WJv{_tNI?l}waE_e&Z2vJ5n;FJMW5iNgR!3`L1kd3Cm0HSdSUr}H4XPv4tr$o1!* zkk=skL$GuuZI(N6HAzgb3MPnZy$OM#U{`7J_*b6w!C!6olt6CahS&%j+V(Ry3}`}+%{%vUu)#?J>h-?mew zZE;xZdCV|d2YOt@W8wXd9|rYD-SlZxn+|loQr3pHU;>_> zf>}^{26PK#>Sz~sMmlk>eLFWBQq^w8N)77ICf1D8DylB@N6dgeW_5Zv(tjQeLhk4+4;vym=I{^-C9Z<+{HFA0V8sXQkUx%j_PudKJ7kD&gAO=s~qeQk9OV;#?&A&D0 zkHalrtLAbvq)-I31U!gLD|^^oZDJ-Fdz5&#k9t#b?=~wh9jYqGFXwr}1xMh3aWpkA z+_#+|&xc^g=4};E%>fLgk{hJMDyge01etrEsv8(QJw8c8^qw?0J~r7rSqf*Lln=^lDvXmXxvh1vFEm4x zYyXlvZ zu)wgepDO4W$7AzOK@wtObnnrk!ouVp;FI9tB|W4A#ETtG&K!`XM~OpWVO`H&rsLto zO6$XDsHx+c{?iL!g}$K@V(wx#u995OIGM+Wp5o%gD>I?(t@nQonw$iT)17$S*YWn|Zm{8a5!;B`0-dVS@Kr)F{wjs{3ag+(ev(U%u$ zS1#|d*&#y9%gZH>?J~3VUW8EsBnCa7pKs3Bd&!4Fvb}kJf&PkClsdoNU+=KLn|>Z6 zG+i8>x0_bgS=awVLQEn{0$)qa{b7J;00Klfpw~1zxIryIO=dGw+5V@@4DSg^2$$8w zcljA^E@EV6X693^`|XPSc-u^4@}z`<7Vf+eUl%o(`-hCng?@Eqpqb%9F>WruAJUBehH^aZMXj$5*uG1naefF2> zP(C(`l?pmESvov95tUqso+`IcO|oIQ9eJW^Qu%hBwslbWK61X^v$ZyhCj^HyIw$CgOoPj)B0ECTOWJZ9^3W?h2!)72@Q*c71EY- zm<>=-QHkuezNDgp%m%-WUoPcYygl@4M)eSWn85lDc9Z4aWz5d3^;=54!E%k!*wl1+ zH8+meeUmDhQn*Ni5?Wqa!8_5${IpMNVZ-r?mKge&@+orlvvCw#p>911kL|38oE)Y? ziokmm911nYbc+wU>@%29XAHUIX_2{3tuNLs%4ug#ns;8MB3Qom$79lD9|VI&a}*P{ z8ZS5O8vY_Du2=gNt)e8C zx&N=MgyaEv73#ro`}Yh3E$I(&vu&b#-+R zw6(RhIa(VtFnQvB6&q%$dc>pA*+J0+id3E(mi#u|xW_wytc{J0o4UKDE0}3$5M5ne z`Klb&`-qtn8=<44qpt351PJxv19cBiPd*Y4PL=wL!$XEqi7n&oLV25!krB>wRpQ6A zn(vmG6igv~6O-VU7TEap>8UfP<>{c1#nexOYM-p^>-uBqV%BOP4EQ9v&X9wK6i1&CSZnDkUdZFos&l zo=2J%SBC%T(>ZV^S#wjnaMEa7sFmj*gN=ZvM7yK#fDHP_j z=M+l5!Yk6NjZWDmYwPSZn!R2rw^j7@6;kAR6x%$m1v@!E@2K-QdFgd-&6&W}xv8AV z<}@K!ZaFSfF)-`lRO)x{TYhXCY#EDx6u3M z@~EWPEN0*?b-370*m-wKxv*|^u%?ojfFS67F^(AiARWmJ@kKK)r^U!a1O$Z5=`5xn zD!ID&$(5Jo<7qDq9#|LHEFK)hG!&l)$Hd^Y8J2dq1O@~QHTZZ%QV5c;Ct<46DZV}s zs;{seOdhvN>;nrdkL$VEHeKQ2sfw6;(02CrNVY;M`oWiZ_ltTrrpGVU6=ARkLS*+( zedn(8b*Ha=S1#mC#eNXR3$Fc~1XJh|93Y|e;&NCPtK&E)KgHP0;Z_bhExts)2ncu( z^a4Y&YJcv7f{DiH1dDbOcti6sa~RXNGYxOc{2y0mJ2akeI3aUp`df>k?+6lgY!5s< zJT_;uI69)qwfB4|DmXrLM}|epM8yn!h|?0e_fOvc(8IP^fk1RP4ON(oOPgPx?;Ekx zP-m(#=~1@QOD8^|^wA1=V=HWCo>0e1 zo$+~|GC+IbpiX;k4ws?$3Ogc5hqKz}`H@dd6xloe3Q4`?fF8`&U*q~Qe-h1oI5J{u zI8zyAK2`FxuD;&D$Vhecs}w*PT~ug$Q^Q8)GnFjRPg)MpcD@OE!Ja*O3VO!@FuBEYqR_N}m!JZ_Yr2;j@(JQKj?_DhTNmkj; zF_L8=h)WNH64bA&&=SwY?S7{2rTO+P0_ZKY*yQE!6Lygw+DpnFe6e?MP>#pPi`{zJ z#Vje`jS21U?J;r5Fwp^y9`}}Jsq%qhnt*!L&_LQ?XrT@3v&z4R=DY6!V1Qqasi~>w z0v&)?%z<>A^3wV2-ExA8lA8pU*xTvY|)ppSO2N!T8V<^-~v)0zu z;-yXR?(VdT(*$kiNIE+sn<>M?-OqQ4joGbMH?*HFxgswwe`_i0R_%K1_`AA#Y2b4= z?_3*xE{;mN5`(2XQ+U|VO5$DVr|L<6G_}6v(Azp6MA!cL<9>JZ3TRq4HyHLezljo- zIh2a$Ri~z>GJJ2Z?46tfi;G_Xn1C58rG}^tfNF^GM~iFUJ6}{(R0z7cyFM_HFonva z2cV{&9&~bV{_pC(Ok>4*lwdf`r^|&XgzCcRldqo>bF{zC=E znm2GRR3oLN#$_klp_)~U8jp;eeEf^_vqw4&b+~>vvG3bupR{TiV!Rhoy7dna>-tH2 z^ENtOTE7N2TnzO_5e{;&J<@fGZNO7MVt0q-qxoE&HlN&VC@~PEKrk^i$wXtAlbc(o zSMvceA0{<*p7@L13jmq%;@kccSpF}I#eapa|GO8A`%X9X$!5p!hJ}UYWoe}CX&+s2-J-Y{{iY!`6PdDo2lA3p3bt~Gf~_#swo4< z^r^+>L?J1d%QbEX(H5gQOEY$j>V-5XpW<`hIKI`=BB4`A40GL^PIt+la2H8!VzNBpaq7erBU)-xwd7?f9;t=Y5xan znCP;@rJl{Xe4aXOPov9{B`GN>rFf=z8SnEyW|r4KjIU{p@FLnZD;yu++};d#R;bbqh&-R3SHNyF8y*r{5#7Y`r2&+{JdQSa*OYwK2} zcQ_n+O(yKC^Ur<$H$GtVRzw5>Ds=u)tNQ4##ft#EV_#I(cQrwmvPU1gjWx^dV+Ys} zL_nW1^o@880Cu4bUCuK^=#%QF*pyENY}w=aZ&o^@NW;Rzo5SPi4;PyQWoS9&;UEfO z@v?L!Z2obS)nsw{z5m_iX`JHtxwMAH6NPVl;i9FX_E)D{?6-vXH~l;diz92YVSibW z30GdUfaSl`+frexXs*csW=Z^0=(`$Yyp)U#1cmkI`QGM~f657UJ(!P*TCi!WOshhG z=DknkA0i_gE;hY~{9SEk3_7F953QHcpx-{%+%?Yo8CM@CHFUKyL!#_A_Q}J zb+89JzDd<2JcR5MY7%mbE#8*S&gwXy?uCI{=WAbOb?K}=*;w4(*4h_hoXf6EoUXEp zu@SZt9nF^SwJ9tAbjH=LmSp3aP_uHZm$0vmGN`I{bb&PS3Szw9GthR{A%h zpPygFU4-BM<_z9fYM$oaUJU5tw{Oux_1Bx;f-yOi;oFrh0RoT+us0)#If^_dhkn?q zq)yINx{%Hj9ffS)hT+ka*z9nt!(Okb%Of*2~V$ z4jN{TQ=i#DN^`(p3|vaen3NPU=wAk?Tvl9CC3l6itn6sb-n80heV6cJUf>zV$3I5L zz)(LJ9UUG1IV^nr!yq$c?L0m?`2)B-2;LP5(8aUP#gC*|1O#+8_{5B7Ra8{-bgHJ~ zV9r}NORb?{QBmT6_GQa`J67B*CPjXBec!azF|G68p78p&ui z7XHhb*O#uR>N~FOJV{$Y`!m1x$} zW0zdzH#gqab2X0xQE(($QXf`KmKd~_*vxRRZfqD0Ext~CKfS&mICq-U4p!X!j2XnUFV8#1j^ny?u#gDo{L(&Jyxtlaj(~-43XkndJ)m0Yef| z;)GSZ>^y-9qZg{lB`=Ro{4a>czR-}`Kityh45dK`x6HI3W+}r6F6E`wd4c0j3^rL* zOtx>(&SVh{JwFOygG3j1yoEDyX=#I_QDSNmh0n^i=W`m@rBCFvT(x2}d2R3MZN_jmv)m*80{ra`}LW7`03xD<>djt$S-eJ)wAfIi%QLE2b6VpM!; zv)mT$Znn$6NjRivlYzRWcZ79@goFgdg=pKk+Q+Fq1=&D^Vo$@0d5*jy_ zI@_6qs}|795AJer1Cg;_E=S>Y_4bNxu%@!P?oL@Aocv5xhF@kn31#|Y`U?L$1g^Qq z#Ni0VSWW(u$Uj-~6<{+}Zj^DvcoypaA|(7uE-#btVp%QsFogc5gLcl9+mW+}4{$&7 z|GpIW-+_gHv^&eax6#IA(d2@rLwC0~x0utycuA~u<$UHt-(MqdRcTiN8QrM%^%WiU zXlIZHO81(hXsC$T4>vh;myc|8oT6#ywA8oxd$O4D1F-a$&Zs1>`1wVtaMdnBMx%AL zJI?C%5hA`8xp5>7C*%*|q9VaR(1UQ%gMmE;KIakQDSIRiZBtceSfa7;^ViC$0MiXO zq8&FP-;W{pQ-Ty~tkRD!T>z7bQ7czmQ&Eq$gDZKy8ZW$dxiOr{>%&a}oF5C};N!G6 z7{#WWbD0=@{r&lm6O^cez=sr7fE0rYZRv7?&?h=@bG|Sl1R%+P4rEo>n3v)x;IRC# zXm+r@S12`B8xj~mR{8cIH}}Hfyg2aC`k6RbTX`;8CGJ5QDRnaUOiv;_$fLq~OiF^D zkdj7-V-PL@@@YQy6OT?YDUxU2y;W&xv+dn>GOEXcg8o?ezkH7N;X1s_$C(g7^w+kwtOW%Hd1MLOS4o=(qtS+VZKj6m>>L~% zt?*K^P;uX0YSgK3V-p30O{2SjzxIxf4Ixx_-AVDFO~EHn*Sl zwnm~W@9co00pb-8=@n8S$a#AVEh2%rL-s|znypW`SilTgRmHiy+9dI_^wd{Q$dvN8 zs7x9~A{zs#4)=FH1zOcToA@M5axvgKjs>Ev0S4X-> zY@_M#@5F+(La&&Q-q(-5(S3V;9^AhKPM>o8Sem5`U3qnM^qb0Ghf8=)s7jiryJr`!ZI zr^-6&$7hs}$O0yHy|BJ=+XMg=Gnvck(6{S4EFxl5lZTNJJx3vRq}nc{rjH8E=4^ZX z^0Z8GI8zb_^3Tbk*C^0QXB-?H>;#RngsEmz)Dlc`%@TVAX66sRxpP_jiTrk~4S%ml z1>6rI>5ET`pocHhMwFVKk-(4W>gsA%SUyg!GROYDDeB$*iT3g+lrlCoH3}?WR2-bp ziVBx%8%uVxbF#YoyKDEe@izvhrW)Bg+Pi~k!kwL?!V=EToYporM|Zz3#<#5^PKMG& z6NT-BfS`!==FJ9UM_S-CDZ=C7uGD*%hoy`XBO*l zqG&AqJKC)K$b8>A%CtW@{hO#@Sbv-PH~R+9LmjnJ6yMEgo-%uzd`p8mmnz4>^adi9x$O;yHOkzQl8@ZF_o#Q=PSJkPcr zcf9|uB}~=0SY=g(|72yCg7N!obGEd$_Oj^Rv>OUY`S=UR+@WQugooqP8+I?#CJLHW zTBbT}3@nNCA@K6?<$bxuyFkgv$W$uU74!xpf{KOJUhL(Eq;%SBztn<67WN7PGZA>7 z21^0Kn!n8rf^EJ`)QLNR1~0yvEmenqs*q)*S7ZE~LeMiPE=K_vf%-kG&n?d?+Fw~L zKV#PYMdWjRE)FzKjRMVd8S|QNS7RnYmA4z@yytHiZ$jk7zwPz@PEL$&lP&#yvEc^7 zKRB^TNPWi?-~00s7q{2Z>FMcFgIg>Y9w{C3X?0tfwTBm;a+QN|YZ)9=v@r(BoY?sI zU^@9^{oSe3L?Iu~V2)=SeMy1fj$3ACNTD8blWo4Mf}i&Yh{*bDJ@JX07LNe((J0gk zzPr7)vgYUGBblF{KRi7h%hk)M8KB}h7w|ZK2;|_kF9(s+;XsgV?(Rk#7#Ps}ZU5Bw z&)~;8h`GFf{U{j-t)TSvviK$5 z;Zj5isvVH>*hWg?Q05k{07RXw+x^Vq-xTYyF}2gJIg}_8ZdfZa*3#%S3J#^v*mO2s zQBe^Qo8!ZYqIcR`Q*emdl^MHT9~v5}3MW4wI4{x7kP{!-~+46IciX3In(i zZCW$qIa5=bn3O_f+LhL?LC@&<3^-f4?Hq~l%^_mL^?t46OcJ{Zdc*CxcKyPNhVDJU zFGL)sm;iTwjE#NY)Fc9|@=Cc|_`Dk*f876-XVOYXEW3{?hs=lnY8mh%A~wUI4#$7N zSGwl1aC(S}jZK*=qA(BO+Y-R0rLsmWD^zy_afC$DgnW9ui7X=K+S{|_JWQ6Yfj`b| zJB#{#({ox8hnfg-gZL2;qF|i9ITSdg>(2mxm#s?ov=tfTtq@bm?!x ze$mKSBuyhDzckW>ewp+>2?`0J1~YnZx+35H9Z*A-HV=U_lBXI)y91U=eVbOzJwWMH ze0*AaBwF>JXFX$Ih5dwpsF$5U6t;~X7&%1e&5NMNeDy#wnhXpA!v1V^V6JL*UQZ}C z@rPGwgUy+eF^7jsqJ7D)Fo4EgsF7yZc+1@aYqh6e!UlB1VQ-o%l2W*#XYcLXw+6?n z-I~9Q!>1i>N^!`!2>|fdU@DeNq4YjPfLAl19fK@Hh1fEy%j9#{YMAa)ulzQ0bJ?|& zyccR1v;O#|o|KoyXKRyw6Z4UBwB-6(%wOy5V@^L&P|K^{rgl@)vgkFC0h38mPA;;; znA>uUL8rzUEjT#%HvsGb0RfCDg86EUi8G{P7K7!yOj;-D1#4L^jFF>$L>o(5rcE-!|Ug+Ue9gs@o)0PUXmd4S}!qs=ybX zXS=}z@5Y47%iD>EUO)%P^$`il4M6~@81!rPPwFvJC-gFTzXDmYb%Pj5!b~U~6}!J? zIee{`*X`&LGYiYpClLlf8MCso8W+QTr+bA;0BCeIVU3n)Bws zojxz(KMXmjG%0H!KFe0+$B*nWC37`=fJQ^;bm@)i_9)c3O`pzg(a08A-;ZD`!f)5sn zVq)WPc)G>+x^=pPWS*s7-;}Nmv|I2zTmG~Y13BF;P9QAoG#Gw1mw*tEF^-E^Kgs;FNrZwpf@%wOlsl3a_G}1%w95B7g1&Bb zd(Do;ZX-+$x7fSXXmg&C{?G%`Xr&MTK`{G|!$W(~&_fso1_p;S>I+U`KViTI=(@i1 zXyB0XmdKpJ^)6t2Gfo!oz$XTj*r?y|p2mYsu-p5=_w7!*ca!yAZu7H>TrJ zxjI638>GajW79?5(~mA`*jd4i5aDZ`9sV!APkj0)*$zbyYRVnWIjxCibvcddq>Oc7 z1D?=lLXvV{6TtmUzGVH#76G2raev@=Za$8o^4a}G6_5a-m*zuMePB5jS`VbW3X6_L zhEn+5ibj0in2`He#}!##Uyfds8g8z{NG*VnR*=`lLHySJ{H@pcht`jNYdcOsm1ct! z@SY4<5(2ac0QppznPO4{t8U%T$xE4H)vuWY(|3Mv@NXSHOJ}!zA9VcOyQc1`UgfKP zJyu?45Ex=9T&!J%4eaHNQc0t(HAgbwnSzyz1T>}1u~K|)o9Q1~gb)e0`OgUn8ygIz z(fm8yQBO(Rsf{T%6zLoO-1z zg>`jF)d4Zze6ooA>U=Napy3)D04Tc|Ys}7tRDL(yQw7{Q-6Pm2o%~T+(>)ani@dsR z9~j66wsiFq#H>}v@0(-OMRT?GE9+R`D&>;ck>YaXId!BjtBx&dA942!x=|p9#do#g|gu=AVzg*}Bm4IeT1bw-62bU5c=u zt`YgWiRZVzKmpXOaemI{vJLn1jV=$DG#k8Mfk7}UIHFSsxI>@C%7 zvGEbT3y5gD;uj>mOcU|G?Vr@FvLy#d4d}EIDA8~N!ga2GBLqkUbhz(z(9$u1e1Oy# zdVhQ17q6S+Tnq2ykbX-LT1ja|Gym%Yeb56hmqIBIbq~Ef|Ez-to|D_C=Xa}qR+f}R zOsDuQl48!Q4i`S0#N6qRH8|4M%Y04V)qAQN-P5kvJ3Fg+HCm1{-dr@^w@+7Cfub8u z%whUnF-^#1`sG6(UAtcu744w6mb&(9K~10w=v!H7+v>F+PzrgYLcn5rufz}(5g{87 zhERx$>oGJ0h)%`v>Z)S_pjFY;Y$GBHM&+Ghn- z9_Lp5l+X@{QzZa`K_WUjWTpct;lPozv?=4>VlP0z#Wu)mQ{=0pRKP85Vgz=cQb z?pgx^DqT^;M(HPZ)nR2mUI!v;)!8mEQBjB6E0@)^we*q_r9wIC*u{xL?TQ+5+KV)y zV)#`69l2liXkU-J_&U2*Ld|&?VUJT92;y*ED-O^}7G0iVQ#e0n#{ROG`^2co6vY zD``h0aUkF?xrQ9;@M`lvHpP5LBU6w~qNb)U@ws+&=RE(ZHfI<|rvL-sI(}!;1$_Ge z8kZkbd)0V<#}6G|=4VLb(;w{^ZTsh~q9i3?!rh;S^|TV zHHDv82fPb`jzjT(^L_hww7rv8X{6=8@2bF#0C;0qIg&Ki!Au@%X=%yRahobJAmX-8 zq{z(WpD5G|1rn`j=ur#fyq~~bp6T3`3rXY41i8@k2HuJ9Xf}X~tLIG?YV-Ie;^M_7 z6JC*koS8Bkus}hsw`(6*Zb3w@LD`F_AcS_>T0+D5N)m+6eozXm0cV-+J?QfwgJ%CP zE3Mg>E?B40mmHq^*qf;`u(gFll-GgC%@Y$({MWz!Zf$Kno4AkIZ+edic)KK+`2~fA z!;OA^%mD0x_(`4%tIlu#7~wl2zndcz;N7zM-FZJmLLz=?xss{ISn9f`HCyc@xp&Fv zadY&WCXW~<>RR-XX-rpzCwE)DreIYzt~C@V6NqPtJa!b>ifPy&o-ZaLkynUMK~Z#` z;Rp0P{q3<__shelAW!lDM0p}fxMb9U(dmLo!ioCy=~F3L*;b(U;1Uv=HHAfu=BnlZ z6Qh@TgH2NH1Pt&hdi69D+jBf%7m)PIG;pG!p}m)*3k19$t}k5e6t4idS@E0eMI~IC z^}D|VN%HqV>jpSHMEL$jRsHVoMw$`me&7KSftv?2@;!VyBS%hMuCA^EBLva?q@M?i z99=opxP0_HpFis!kTvt!-=)*PfC@|lf;Q37(T8Ve;xQD0R0JfPIi!V&iA0xw|K`q9 z@;Rdd#%mAUPT-#(PFoc(0cQF>Ffb4ylasaa?uv=eZ6DLj%uF;CrbbV^$5pb0LdQ|a;p7Q=kz@6y;6$-1G_ry?8 zP%!BUhCThE^85iJq64s#Nd6QNzO}Yy4#A;}23Y?4+cMLCyQ}VP*C7g z1Q=y(`ae2oL8}0ud@(Go%U^E$^UB|ojZ&*gRxnZ0!DQ2#oOC{FV4eA8f&oe#9yxgw zaQt%PJ-obtI{K4^>9&Ej*6YGHNt)8Yz#s(`rWPT${&@u?sK7H1{rC|V9xHnu19n6J zHn+IE8~{R8yR+5!h1yj`&VAXIe~;JTYLua&;Sjhd_BGJ?+sATMOTGU(q6Wj{zH(y~ zH{R7CP;)5&ojJseou(Y53=tqW5v%lGY6%7~O(b8V5SN%Z90dKrF0gWoQDhJXipQB2 z3l0f+0BMz*r&kIvF=13zR=OY5U)W#lzpFd$Ve0Ddhiic)wsU-3^^~dz!ORV%F z0KXuHnnD1B*8up-*wK*X&C>?|*7dkd7H&8_%n!US_WXSx78&yzU#lCXQsYUZRO$AA zDo4wA`4o3-B3z)F{YMRgOX@1E(of-jIq&lzcUA+t_-R%}h=G0JOXI z7mYUopfdw7Ob7^F3s`Xo5TNgEVC|K<>}UW+51;QKTuBE^UaQ5fC`oZY2CzW^4XkhX zox&=JfP5!5Ha3yZC9k7~@P)1zn2-HPvc>^HA-g(7AM@)Q?yjuruIFoi+aEnJG%|vV zRyZAgN~ZMyr~G@Fv2tLNYXxE%yaZ28O*MgpP45>L)u;|=aeF-f5-7yVKt2TfoAg1y z{Ox&`hd|?7`Xeg~evc);e!+PtSiFJd<>ii(!rqrJYe0dHzauN4Z1s1*e!RK4Q9D4vB=XOaO)wn!Aq|EKzP7F|4(ysx zqXu{Xl*Oi{;YdnKUS3^gfEWxedc>V6@akWII~uOAdIN?DaNO5)aTeAa$7{XzE-u0K z^*%Nsil;3c+g9fA-aK7tl|Nch35fJFdioRM7*Bf;)dX6K;X;E?o{66CUt0KvHxT=S z#3%h_q8GPomQ_R0gCF2f2#Bbu5dbOurTd{i)b#PGC=g~XcNZEdp~cP3Ff@ERbL$$>T^L35;5%m*RoiG4pzC0g&_D`)+x)qmG#|O>@@Dz-|7@JQN zBw2bWDdEC_2&gb1QA+jlB`zo?Tsq$+WEcowjPcaq09=q~SpcOBYzTulLpYNMGrd(& z_*O0Z4zPdlU@_jjnViP_+VzZR{CTG#upFA`)4T)V;c4Yo3BFXvI(W6i*nwBOVNEo+ zP6ot7Q1XY1-tkxb`bRV(0oiHD9}a_oLKuR260Gd#I0AY&9s$9_(%+kJiU2wWuB&Mh z9~&DR5wE>|VY`&EOyvIO6+psDox4>EZV$A&l6#%MO%EDt2!eIinWvtQi;o`&JNm2y!I=%>e06OMQQP`vZ`ma_Kz5 z<8AKmmoJP0I;02{Zqw*!8Mi1sJFx^8Z05HE4K)o-sqd{Pr_Hol@t`O3t9Ol;W^{Fa z;Q9Xdbrhc97P>hUWqwyf04;88gu-<`&y)^ujT&K~)PraUU?bDz7Jrvh%_ zd!Q^rB7h@-G#{XYV7_{u@4n4+kZ;%^Qu{}u)ulpOQMceI(PV)Gx*wnN|=MPGwSgURNPNsxqzU*nz}bH z8$9Fh>w^jZ68*1uKhgLP+<#SN_!oAx{~7H12M@lf-mf{^IVF@LZ@YaXI14~#r#>M2 zgBqA<*u$#NTn52U4}GQ30rLX;81D;L8y5=VjxY0(|Ea<>bdiy^onO>(sQae zOPK!m8q0dXV{qA#J&>nnM@GK&1d(^OEJAg?qfkzD><44EbC)1eLt>bKcv2pOW%mcg_o+(x;r#!s3r#v2czY zsbboC>%6Z+VY@YEPc6BXdco}=CnqP6riWue(0!qD$}}-GAJEvc=-tT#Jqf7)$JbUQe>*Yzu!zG@BS>1@0kFPT{8dLpif{=D`E zWS-%{CXlI(*ceP(0U!}f+BUEgM{Vcjd%SVx%;d`Z6&zv1zC^agI>CScy8ygoUPKNK z4n%y;3_ullX>$D8QgKF6R#voFw;lw~k$^36x;2VoVPUaWO)hP!pEii_r_&2X+}e|>I~n!N*LOi~aO2P8ZQ#0ou6H`8m@C+a+$ z+B!NitR{;<0|1UD+MOahTTmX6n) zXB*Sh@zaSbjwQ^q=6*XMf+C@ztucHgEjDgeJ~?!Jd|a}Y!vguk?I7}`$7ut~3# z3`8Eo#x#YDB;G`l*Ot+>a%j~E^d5l%2&60tc+e!NawRS-Z;-*R;QaVF03^7-Fu#2O zQa|1VSFr;;8W_PasVo%`cv=R-p0@@n@p~``>w{@A0KsMgn0UFBlh!lV=y}En*n~@s z=>;{@i=?yNt?<1Wcsk7Hx5tS?((&Ex*QMq_Yn|%jk`NkBg=vXO9=rKAz!HuowC#YU zl`H4r(MgY|o~EoY)97;jgtbGj4eWpcS)4GS_iI>8`*!b;SJ^E{026Y&D1ENMr&cZ% zaER0GaoHC$W__IRFi2kgq6K6-#Gtiuu`h`r>ddr%JDOJZ?&sEFq=xuKdaAG=1u!gW z;4?A=t3okxNzeukMB`vG4ZF`=QGJUyKg)Z2*G))21&csRUOpI>-ULL85`b;~bA^8j za@cUizVMu~65c1TkGHP{+?bF`hmu(JEMzq>#&EgQ^| z%M-=^$%*X%@aTb^(F}5nc#j?h0LSF>^GR3lN6Kf!<3B!@8g)>w-r|;9O=5%S2w=sC z(02g7v=q52EyvN}7bYtkC`SDnSlYO_xZfFZFfrSr$@y_fNiiV6xYz(W8J~t*YGBr$ zm0Cy?1Aj6stO4=|R7%N4FnkpQ1PIBXmsT1)%T?D9!fObU=4Jj4I!Mwe_7gttPZL1?9BBljj zCj+ZR_hM1cK1-4$>wshQ2M|Lo8IlPlJ<7-`=8UrUE}vy=bL8P^E%^EjSLx{?imO(s zarVacbOON4t}Ee%6}1qmQI^=37xJ%?yv8gkl=6GsA>`dZ71`HEj=cTf$}>pqUrnycgxnBvM=O$AcYkvzn1Zufh)8HTuz$Ec`+9R!!E+;#(VREmf?8DMU zzmt6al&q3D&{dl)pJiAcYhq4)>{*svmvzP5iSxJf)h~o{_+0Yx7AIdiJabp_tOu1z6P}vY zr8}l2orH$+NSG!Puivd)dwjbH_wYM7U~lwT+3`vuLYy`A$ER;FnBUF@}2E%-z<`HL{?Rg(E!pNYdGO#oQr?k|ak!U@~y)=Z~i{TGFA z0^#-7=P;kQSdjZ#RWTLM3&Q)WJH1QinH*l1?a$?fEPG&@tq43ZuMA3Jfe)laIXg+7 z`(vO`hc@19=s^(agq_I0w(QUXQ7aM&gHRv58RT#F%>#L7vN&P-(*3s6{VFM^aKft6 zxcd>nkkZ9K2~*C>4baGki)V6oVY&b9ZCqy>SPV>exHvP1!(<6x7|hps@VcFo0hXz( zt{yxb6NPMl@j0Ve|KoJ8Cp89q+cAI|`kW-nuHWICkRgBW9j=}E`r7WDi~AiA;Hp4B zs5`+1E_I3ZUv^N)=hx4Hl;S{v#c2iJ=7ld%MNzy2!?>&_scw&)$w(YfK{J{vbwc?p zW0JpD>$>*=NJHMg{^AoEv>9bmiUPg{mA@VQ1X!#$a6X`YT}3aK`Fxe{6*=I!{7QS1 z;#f{#dOl0=021^(Z33+H2p|bVz-ItdZEeRO93C4BI@uVcp{JLeKHoC?)&U@IslhF> zz{x4ch^N(AUlK^vg2^*9HH8}34Atu%D0u-5MmQ$T(VD4m+R1YNkDhP@&w@KXd=sD? zT`SNvj#hx50k#l0tq;Uj%F4@&gE_J4_)6kO}}ckC-<#t-xAIm}xob{n&|orW#JfI=h1p5_$Va*VwFax0&hy9nJbygz zb@_w2#y$7%p5OglKFjw8ruW|kiJ?VX237Ns=G?U4ULhIr?r+M+jys+z-PPMJq(ZQZ zj7l&qn1h>7Wx7Pe^iJcGiTq1NBX5lZc-i;WjsG1!7!b6+S{Lhvbl9f8TK}$D!QhvE z!V}7;)J1g+SM#zur5qCEKhp7k#TJs*(cj~5Nr-vI{q)e75T2rW%>5{r4gJ#lmURCydKZ^clhqO$Y?FR zfqrFKyzuGMCzn{g?9+bS-YdtE&mW4ZK-=y+ucWl1ypNSNDu%Dm)vz)0{^HnSbz=s7 zUc?NVgth%Ek`fNbG_JoNs@>jddrvA}j!|7%IjOaDDq}>e!vh6!l6?WJ{C*IKhKjol z3V&ROHaz&c5Z9Rph8-47bj_txPdAntc%r3QHrMHenPD+BG6BAo#csB(7wlh!B;e>= z>le$0BJsGd-#1zPiG5P;kX>yfxTt zv7$it&`>2Cy19ow3T=#ZCXP@!-tm+#Pvia{@C6j%JNvSezs=Z>O|hdD9eJXoqunez z<+z?1)8w)pX1m1D+Y=WLM)6&CcQ3n{gj(!(9W)x`W+anxN;27;Oh~2Vk`)mgP-$#+<>x7@ptm=#4951knl!u z!My-Y4dQ7B>yn#S&#VZI5Z!02H!aPqJ#&1%)V3CMhwZ@X_y-1> zOn0ObN*==H1$wBrKTs9`>PxVZf8*y5?T#gef{waZGc4sghSZ#5ZP_TAs@B(Y$eiqJ zo=GPE9xrJ&!!b=m5biS5^8nJIP`U)J0tJ)QWne7hzL#p-?SG>kn8*f5p6?$1Pg?z2 zFZ*D>ey-p8ZgJb@cD5}D5~z0tCMXO==Sy$M&hg5GjEpb2ihl$pT)Cf$b=42Vu@s~_ z>DA=Owlx>0f1AFmtsMYFgAp~MOzD6@iVs9<3}zP~Jtvp@!TzcRA3BdINw01jnKQwaj>>_)|d=46?miy!W@vlj=R zG8k`55_V#oR8ciDpHBO8ecdA}D(cwz^M?UHmv{)UU%vZ_`S7q@Vp1aB&3Kw^p)enWF zo@#ALqtSkuuADIUK51cUIuuDxN+yx`Wo2cjJRa)=-{ALkFlhh`Aw|PWuW>=&y!n-H z@K4sC9{I6Fc66MD(A<`UeB8Qd2fv!Ksj0AeYocvI2tpvezrQykGE&l|`GK&o@a2@d zMMo^V)*tBRbHF~U_SxF`=LL{|1+YqD_n?@;jtw6o2zG3v{u&9ViQCymB@^n_Jm#=v zO;N#6M_AEzqkL+r=EaYfz7N*WRk*+j<;2>H746#FB9Ve=D#2ePuR<(QBWcdhBHZ3X z*+N7!7fWkT6!*_#*z?r}2g$b#+z(X?aYp%k_(1&9g6~myoFl!u>o+|D!(P9F-*a3n zt_nTy<44fj=xaB;q&S_OozKpHOQfqxM@uwAv_H#MSy#>Xp;Y?H} z?10*!D0WbemNyNC5$0pl5Zp+AHN#1&+fETe~vbB#88#|8>z@ZIL)Wby6h^P5b#F=~ek+)|)z|5F*n9Z(zoLqp?}RXev1Z5lR4?p7J{Ee2<^6`OYJFmUBvFgX&6* zwVV6d!}Gq)(h~XolzA+r|6JSTFmNoR2!Da>JLXvS*J1M0(nQqRss&l4j{X{^-g`}1 zuyeb)9o+;*P*{erzlg$4HMb7-I0_p}S&i@)z`zA#n-GQl(?n9{{QXcprT$Z!2^VCW zI%^B!8<^CxJ4f28m4AU`A_R^M$`Ye5LduJawQQbNH@F~URHTTkJ_A!e_WJWGCV7t->yBo-{s~Wth-is<7jk%chvSyS1P5tQfKl?UN?g9WL$i_0|fw6EfiIexkjAa zoJdlBpBFO=`ym`uGZ3bwq@<1+aE3vv`GCkQ(%&w+8hJ-%oxu53sD5TGAzfaC$MFOTBWkD7z%X*o+Vs+FcFhe&lj z^dXNMgo<2{jqSDC9|)Q{rjMRu-*d*v_=Hd0U;G)cdFHzxy84o`d zCNU8aW)L<@y4Tbb)Az++2t0z?<;qe|J}Q&yeT`D-ei+7R`B4_A7bKLFl%%AkZ}r7j zl#PLYhcLV1X^O#EcE}X#Mnv@D`Asx5B6>U$hr%zzJe!)yB=69Ls%?IVrV>1?ce6XS zXKD9nSFqTVyfa6U&S)otEg4ru>KN69oVYk1`}l2U=01SO>PbX~Zm|Py6Tit=+ZZ(n z3Q8Crc$YI_HYKD?wp02Up29Wj=BJXI@?6DV2k(WuzFT@K+F)s_0sZ@J1JAxUq5KSEXN>8t#Drzx@+Z@NsBIML zvWbZh$Zrp#2zFI_P@qk_w=K!?I`UlVtUw)M`TBvhb?5YMzmUZ_CUX>jAoHii)Mya z!>Q{5Sh;&c($RnX`*z}B|9D2Q#wBYsfLU#t*k1ka;e{004q1@ToY$&sOv&?GiW2&N z0VimD%sXNzzw8Y4?AMzS5s~Pu*46=hg41fD)MSDxL$-Xow`ty&8Fl!?ou8P}F(2ek zhmwG%N}uZta74t#u@DWy zK(tdq#JOUv(Pn05Fr*Li%Wi1CC1Gq0Fotz$A!@@W3vO2J5Jc-XPT!i$)kxkZLT_`*1=4pFr{cfaq}n zO1zvd+U{Jmr%I3O+<2D5OHy9kXzr>>rhw-Rsv(Oa$?-?U>05)n{SKHBNgUEMO@g#;yc=n_gg-Od`6 z2OVi`^|_UL6%hBOxxA!0&UwyAMG##D0v-Gg$aEehygZ14grd8WD&W1re(l;dmuN`9N2ZTV|C+)H+?)kcJ?OJP<_R&+ zI$M(LX2+anq292B3muJL#x2?}1m4Rp`t+U*tX_(#)4n@z6rV#DHUzQ0##&?aX$LJ= z&c}QkEJ^e0w=Z#;V-GeqHA$XxeqXm7<2*P@WRMK(olhXZdcQoM+E*aqEl90cPy{$jP)VtZ zESr&=dmT(Z$VG641wCKe@36hOiktupS*ZtmP3rQ{kRkLV_Ap`*soQ#%%`_guJT9&Y zaSWsazh}?FV`BrM6)x?`JFVoY!i}T2qV+4iT8Pe%oyQX@tj;d*DK7+ilD#@9+IDS6 ze*^r^*!Z{$^~WDT;i4JqJX{+NzC8|*(pgUHC z<~d)|&z3B{ut&fANj1&7o1Kfx+<7b4fcS#HT>)<;C&COjPqaZ5$Yl|!R6giW0NsEq zBLHL*GfGoSOHc@W69Cm2N$}_r#dYkp7y};{-1h5)sQvURCajKH0ilX3KC7r`Ff$k< zTVOHB^}L(H;Rs$A#5E^+`gEOe5V+b9ad7soOKWHKKLcVJ7 zp+HS>tg*3C0^1>6I4(Bp0@8BwYF{btF>>3Z>=%cwoBh4y~{Zn~3&u9GuFYG31 zYy*XXEu$_q2&lj@p6EDtygEbn9Y}5np0qFoHzP81Zf0iIeyQWyhC{UgxkO=@`|u(N zdE<%{jB0je$^+ZSxt(=c--|Eqri3J2h9Xrg2g?82m+C*gI?*`UtA%EF=6Xu63GRf=8*(>W9EIZ8x+1Nk zef?xgX2*}tVL%YuXHyJZIy1(roUp;m5q%ZviSD|^URkzzE&sXyw;ei3h z$TI>Wpz}s=nRtahrH3xpc{OxTQn-sROp-3e(y4td9f_MdoOv~6E0Z<@q|AbYsJq%F z+&w+wo~`V0g)_aq5JyT29BQvuu1>D{Z^XVRT^@Vn`rWI6yMF5QQ|Gf@&J_A`Rt386 zoXrmj)F&Zjen~z2+z-k4K7Nq7g31fQECp~S6#H!CRYh0|Br6tMa|K)1%5i5nhFfx3C^4kq`iq2K=in|gb! literal 20575 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/blsurf_parameters.png b/doc/salome/gui/SMESH/images/blsurf_parameters.png deleted file mode 100644 index 22d038ac596f9a4721aacd1067a367ddb1c593bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38953 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 Ye5jsQF+1Iz1pi5$y(pe2cJ1E(10xWG-v9sr diff --git a/doc/salome/gui/SMESH/images/blsurf_parameters_advanced.png b/doc/salome/gui/SMESH/images/blsurf_parameters_advanced.png deleted file mode 100644 index 0f2c07dc361f94c92a3c2419285ffb6ef8e1f8ac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25172 zcma&OWl)u2+b&Flgn*=^3P^*rNJ@9Nba!`$h_up;G)Q-Mw{&-R!=m@a^Xzx#n|b$- z?}sxiSa)4<9(CRZ%gKnMArl}&K|!I3iwP+}K|#BMe}_m2;5W18H5X7&@1VqmzACw< z9HzRs;s`GUo$&B9-+Y&OBk+p`<1b9+yPv*}SHf)qsv&`yr0a#_N+A#$T%jZ-OgI$j zuNZRtNvs9$Uu6pn?Cp5^5LIkuBMSI_x^X&+J&Z9)OH6%CotvA}DiDn%*|gspz~O@N z<9}CY;#PEF5G9jG*2M?cZ|!30elT5pR62Y|kRuY>Z3;zBe(3u>^`L=KTzB-5lxWPY z<5x*geDBG3$E7ObJ@>im%)>9YvG=W=K8=n`r7&(37irjnRx8k>j$$N+BDJcZYSX0#>pj=zxq51C^3-WZm=YBzEACA@CO=pN*f5}jb+8r<;O(Kz4;}lFKEV+S@!lf zIc%Z`p8XJk1lQ*l`WmAnXGnT%bbv&$!tX?@xQ)Oz6gdBT;_z`9-#il8RLt;krc<`0 zumox2A{AQXK$vim0F^@FEaps=gmU%jY$J>Exe&bd1>^e$=beFCjU0g-`NIAorhX2+ z?=5(;f6{3M@25=$dK(9qYFZ;vI`28^=xb=nGMnLQVZ)LAABW*wHRNfO5{7ZdC&tU> zE$iy*Gn8n`)#$Qwa-dgFb|OPVBXn~xE4yU`8r~EZyDmJD;Km?Rh-3v9sL9o1Ui z_k=qH#aT?6D@Bq2jx}ic{WnJOR3i{tRT;OsNXK4*38_%JL)WR(|Fy-no2C{IlLW8n zkr_V4iUNlgP)>$u-1Uo$;^jl#x&_p$;|0h72!`QBuC7YnOO$=ajMk&9}F%Zie%J5aYK+ z`hF`(6>H91svgm)soE0jE zG^saZ9BPWGRTuc7kK)MtnLN^C4Mr+c3jcJdD%&6)<}1HBWyC6qpyF2&}pu?&2Vbu2+o5$S#ZsghW1@f?di-Nal;+-_g zr?0L3X!6nA1=;&wS%~pe25e4HQ(+^Qbdh!q4u%U+M;#Jj8424o*>xE2B*^_m{!wfF z=4)XOW)quBJQ1$)QasZrn{n5|H>7iyjU`o$v!(5c$>O7+yv8^zlL@uD?R>h=Y1Ra3 z2X7@Ut$d2u2^-PLD-0j`)QZ?ecqZP&Dzrz?udE#t4{;ns{EjL?{9`f0AoUhAsI?RY z?+x?q%6Y4JT@<`;s7!a|lD;WE)^`DazDP~uwfU@sAr+|kUgeCzPPQ{!WrLd8Sr4k< z(rKditp8GPR`z=;OU?R5m~avq_@dx=xTcNALW;xi7XwAm(Q>sy^{AB0ThyEqBu%d6 zb3{5n9K0ntY;P>B(jRLusPg(mR4fwQZtC<`QN9IxO5~(vJS3JcE!z#YcYNVi@@c)f z9R2LjBH6IjVQ?5tk%pq7x>Wd}N6)pvf+?x^kTaogW`&r(DuuRyQ|3r{v7Ant7~Vg@ zyaev9PPbaUF3B$T=TD@+N(BO)$1AwWCxx|{b@)D*IyyQXFxXu><-cO|sivL5od5j! zv(35|Yr%-=Ugg?RV&ygv?NI#;8&q@6I#*~?()15juocb=7pVV~q2}C!pd`g+DY&AR z+~A&Td1an-^h$dCd`2oa{=LyRc#WYwEMvc~?7i~H(fic=L&@kfa;@!10e1ed25Nz~ z+ELtiRWX)(mhuzdg6`J>C2XW;vmNz2UXtNGzHLl}X9_3{w$~eZRzYg#dW_mBbp(L| z>$A%^QYoG>m)T^V7h6P0(?hFyj|A8fI0lGpUrRYo+X5mpM?ZJ|KwBAWa{ie7q;x*L zxHRNmjRT&nW0)4n*IHIqD8p2`RJ+;jc&nZWawTaznlyk#UwGz2O4{B@nSv?HA>1W4 zCOG_X=w-j?f&Atkt9bi-bB6ih?=;VPj!AM_3d=~Aa+ ztvjc+`QmWvTb6zl8$o;#l+Z6W>*JRV7i2@n@-11-ClegvV*}$Ra#jfWEjWGbdMmQO zxI@0j*GD4Lkm^Byar{DDU`D;`_{BdUfGIJh^tCdLu;1QVTO0rS#zwvK6#=g|v=H3f z?Cnjp!d%T<5f#oG^lOdbk!~$0)2mAas7rSqXlUrJ-ri_8`bx0{-=ZZqQd33fO?7y3tG9>%AScPR%gKnStX@MayvuuNY_Np_UNS!suEA=&)e zru^|&)S4HmaZhij>Ebmf!St(a^<>KWxJ2nC=Vzng8f@*ur6ev8?3mmg{9m#uI2bDexy7O1=>DyO=rc)V=stlA0trJq-50dS( zCg09h(6uwg?{~`QEbBC$DM|+6$m4B`aLJ(G;`K8vL*5S!G2taLNa~bzLU*O}318vz zAa{AxTZ9&y=yLR;_i&v^b=PWmBssc8I&kuana=t?E+A|y-9PGkG5ihBl}-O);z~&3 zsgE$#!ENMY|2W9BdAk<>$Lj)#A1Zm>`sJAu^?&53{vBNHvP!T&mGZDB3(iuAcbHdZj*d-OU0RP% zOn|3I7!u*;(R_tch4UF5r7|@-8g}+}ukzZ_(>s*zFbd9s&wQji6ptxUiei3b*r0Yt z9ZkHX?SOvM3l+D(@xT23Cx_ znz*+22h{1!GHLJ;gv7Yeze|a>{i`2&+7nK++0Uk;N!w=ckJ0eS!!i;Q$ipP=1j(BM z#t~?#GK1Bd6Rvffo{#oV3#LmCzp>4+kzZAhi;T}{Y0B*NLiYG3T77(ydaGAgP~Li; zAH9F#Eon1sc<&moSP>#+(l;|ry~ha!2M4FCT8H}+9nF?LBW={6a;|@9h`eS53qK6E z!a-`cilBrdonaa;x?jPNFefJmbMDCwRisjJ={$F^yHh%xT>RmB;zK5>H&%}1zbi)? z$K~n}I!Zhaye;}ECZFV2$FdKenq&EFrmO@(`AJf0#VV*DY1P#^^OfGPC!=MJeb+AC zk=4(iJ^uRv#+6#G+o8r>8uhfx+)*vDSRtsKet{HuRHU}T^c|H2QN zT_Vr|IE*Ux@fDG0a5UEn?_PQOIn4tXDES#78cp2)uX3SYd7~ba+S!HkdkT7#=qH+Z z1`}Ah90{C58d7jHZ`v15Z5vzGmPdDWD1_*vyVwYr&?TJEcAB8MBI7yZ`|Bg|{<1y} zu0v-+=VY|z_5mB8oQkKQ08O4}W=7P*fvz<&*SZr;|A(Y6Q^om?$4_H2R!-}dU*Z*+ z_m-Qv3ZMJh;9WRnqNvK#+zt#Y%-i-g^VU6IU0P{a5++(PB%rA@%|NR=dtlMT@pV2HEevcIh$Iivg za*L!R8w|}~pcH4?_(>BCd(Q#c`+KJ;#$m`~20`!JDSIsEN=aH`Bj3ET{)2vK--XBp9J+R|2 zuIN5ZG&5MRAAilc`alh8;&VeE*GW^S58lVsExduYV<+`WJ4TvfJk{~5d}WD_&dyH> zMKg~LV`YJX6yzosgC`~loDLsJi=-$QHbVf7XJYfZJsOp)tN&Y|*cr)Fb7$&xkbzbZ zNKEm3oG5yf5NN-E7tgT&dk%iyqDS@K&7?y}Y67qP;yZHal*oj8dzCp+&%%WZ-nY@H zeWeseXw3Auzu`~etpih1JgHnR+f`#>BE^|LGha5ZYc?HuEPOcIP13!3vg>_*+ba7d zhbHY3@IE+QN@VnQnAY?kDUX-a6-3nQ`QIl?s)i|{-3VsL=`tr0WgQ-TIQ(0#$E;os zOB4G>9Hx4n$=AU_*>-NbE-F5TW<44e{!U4I!M%C8-}(7fket}*)ubBv>YlhLK4h8+ zw;3yxm`f!Ytyo1d+p4b-Vcbq-6VvtEF+bLQI1YRF`C#z;gS~#00i{b$X8nWAo6xNv z^p$fV*>c!#Tki6r!`vG<3a5)G_tr7Zd0CObU*_~X%_}bsN?Xr6^PanBMkA$L)7G!f zI!}?TBIYcO9)?nz&ZOS8mhSpbl^AwTV>K~(e)NzAI-yRn&F4bMERPJm#u>X<<(z5<>ZLd#~Z}cGs_W_X8i=F5&&6I z7q4dU@AjXe0yR0fbSvjVrA!#IQ)g^=I=3!F@#kAHpKr_!4x{{ApX-N$(n~0wR?T}x zC$ZH>AD(tf742GL8p4M@S>M@6r8B`+F2U1kus?f8)JS)1I@$~`b+$#k(P^4N<9mhz zJDSguue-*oRi9lHmulxS5H-&}ge#6>s=SOHXVE9+bGrBe;*{$>mWmfPc;jTTJ!Mf*G2d@%UFq4Pw<& zE&4I`&la^09|ly4HRc%i5SHyb7hi{|j{Yh;(fV6`FGSLghg+gV12J&ZalbhnST>l0 z>-w@iO;&h&+L^Z`U!Vk;af=IMIVex8v_mlu)4603L+_={_f6JS7-@f7VNtHOyVYPz$Bg-SOAG{?dffgkYXu&Ty{;WltSGhoEn4%@C zvTj|pZHX;`W)1E$bYIn!lb}|vzFFc67;X7?uAkU{&0)>2?)}wYNx$#wA5#RZ^@9!} zoj5Z2s|aE)5q{lw!%1ugD{JJ&p5^JCc?vdjHN(bRUBOt8+j}fLJk{)@4kQ-3rbhUd zs>?m>?qAyFLNg^2{D&yJoFUUVVnRMRp+FA}05)!c}@&y?g znV!MHP()JCGNA=wd1{fQUi_CSL4} zI^3Qck&D|mG4|-&Nj=`(D`ttH(W!79)I1Fap*(+}rpQxUKo^4l)K59*3Px$~o!L-vWG?qWMPwTsVwHnvC}u@KuZNPY1?Z4W-_JZ@W<5R%^mX*cc;&Gp;UwY>m^wbL~a> zD0!QT{+~kvH)rgF+Gw$7w`$u_UcRGE-WtcXy{a6w;UHVvhgiuAr8_jsRBI{uaylcj z);QWWCYvk;iToH2#&go4J72xdUgrEAHT7M1V5!O3ZO)t@D6)=Uef8S=xE4xK&*}?z9a$d@Bv>VN8k~zh~2rsZ(6=>wpjLDBRK{`Uo zDJXEtaBvAk43EUVIPSJh*5=vYo^QQbco|#!{?aXEs6cYg8U-ZoMc3H&JL;fvoo5sb zEY*O7It=}FbKq2cJrVfvb=2>>v{9N5-{XlHgJx43smD_rUt2m}nomX-Hzz0O<-tNE zF8#N%=Qi(%^v@$zdlR6L(<#om4X!j;(Uz2ypptOc@h5FiMmStAs=J|}qccuUR}Wb< zU5s!MeRlOdnfB_9rB%g;r2D}SaQUop-#;z(P8KL~g6qxmEJjS^N!~mPgT>QVy7k&m zRZL7_Pqlq#B*(?Akc2RoD0lGN74lE7EN8Z#W_SXjBWx+#2a=(H(eZs_`|}JXlg1r9 zY)s2Odh4*$-`A&my4vpQ=C-!JE}*GN1PT_z%Z;L=qnoU@`K9nU)U+uBpVMicyxal;Mz|ND*$KDrcneF)LG}v%IszD%PkKknfOQHJ>V6J2@GpEp6%; z<$Kg*P$Uw``hC)JACaF=Ri$x6zW_Vi?7`urzmIJp8%6GZb37ce;v6HH2zJcVyV5u= zZ9GfFM#CWep40k+MXiz(bb?QD2|wP$ckw*0G+ml9N!2Ki$4E~gCd1es&9lM4!rtm_ zpi`&}b$17zv_7Y~k&YUiZ}w|8x&9UjCtz}zmdeb^k{r@eyM6oi?RU}dm6mfAFlGd7 zmgLVX4NewMH+#<#Rrj6fd}X@_!bNKJnO#+W(0=%+2tdO#FfdT$f^Bodd0V9kuiF{1 zv@Q3v{wXCe8T~aS-+GtVa}o=_ZBh7&qj5`gaTF2~5`W>;8+`n}Hb2wK`!ofEKWJ-VMTu(9FV0#Wetztxtc93R$KLhkM|RSHpJNQsDs zh7wucvRmTII(oqH@$qp=i;0O9q9#DAicFE%CbwnQTt|X;wY8TYY_2G*bsu--C z;9=UmWM1yE>2p3M0B!xzWv_=CBkA9X4uQ7;>ZJS)WJOeVE7jYm2*kR+|4T z)%xXXeM3WrTLZCh@bDQ5OAsjKC&DxdK>c<`EA=`N8XWfoVri7cq@)bjR8fLMLImh8iPeC^XWco?s9*sP)vtp4}$dj_is<;XRMFXX-fNFHkmE9 z=~|xdEaK?21eBCw4n3AhgBI_$H#bj5`JPYro_V)UGdew6pYECCK_qc3JMDS@7Am>c z(r9^+6P|BQmEb&Lo1#s7woeQ33v9NDt>PfL#Z(deXQ49h$2-s4sdyO zzVE-uMC~6ScDP>6KWSAb z47@L5O`#&7^3wcDzEjZ=`9Mx0<$_GeVNDdg!z|Y7Ey(7gvCZFumrWTPTKbZ5QNbFe z1&p>PeT-lIXZ~I3Ld54-DJb=!2;?-Q6itR^~GRHX)$7P0W#na;701U$gagA?Yps_CTDcfWzY#sY15E$&ZbMp@) zk&c(Xoo%^kvaA)bhSYcgGH@LT=;J#U-W5sHlD}H+x`V z;6r`pU2oQVM0mR0Aw6{FayppKIl$7AFPy3vX3%Ol1>8rEIq{!)HTTp3H4F@lT7x4s zdvdtj#WwW691Fb6%*@$F6UNrZYeK*;sFWr24Gi``oSL{$m;HNR5M5=hjZd7M!X)aq z*&pd>B=2She<(qBHvUo$7Po8XDgvC*8*8Mf&f^TM_`O?GRuJ}y%M3#Qk=~#ode~t$ zkA&->riWM|jc+kI31PP#W8-17&bN-&lU#(*h`(Rv00D-b83{Fn3&jfK0qrL)fLJ2)OU9>!(uu6N!#;G z*myXp8)!bGbY>Qo+o$`Jw`}Iv%72yG4Vmr!!xIZXg=*bEk!2k9E%hyfCrzw-GQgNaK-$eUVuBN@NVV24&zF@bxRt0f*&tdl3XD+ zYuKyx!YQA@t$9oK5@Q0}se-bFqlbnQZrH||Ugf-~JW1y#H>o$JWzIo(<{Z(h{_5-BRb_Qe(WTj)!uK`^zUO4<7=J)VQJ|y4R$< zL)rb&r{~x9kjXu!7sSeQ=b>2eY#Q7+=Lr%CCp-hrPv(@_m?;~)C-b)*&fw6 zo7*`utx2j^h3)pS6s2qWH91$Wl!SW?kxpd@BRzre>PA85Ybx?r;Tn4ww&8-8vB=^w z>0IY7Qw2z;PQp>-6&2xu(`5ff*H$WJd3_(<6UhD68=A)K|Qf*=_tr80FH(zpbUxP{W+J#h0cMrG0TP-b35cdn; zWl!wmbJ~;hJweC{Rf|7zbEo8`x&NK@zz~+7WEKw;$n;Kq=SYBKkQV9?;!&neW%hP% z)-uA)(uXvaoAKlz$Em_&?_8PHoI-xe%M2fDi0@hZ_Xel|&0O7OdJC^cG^Kh$3i)RB z>k7k_?G&}$7!4qb97Fo$`n7^;zqtC%=HT~rt3N+{dz`>zu9TLsEBRtn@Brv+pu>W7 zg+{T8^U=JpvopIvZ$xY-g}DCCNNQJC*Pk&{;CR66o&lE)Kk7DYVz&3q~P77ktRhl z7;O&~ixP^SErW5EO_!x8(SytIOM9p}DI(g*RLoMM=^IP7t>;}?u6hc82rM@3Cv{$z zVvejS>7HJCj5c88P4rEz#G5diX+q$7;tpyzZCB3gS(yn(dG*$}SqXrw zzyD$lbrKLuQL(YzmwOYbxgW2OmU=UVL-Efaei3#g^n&1sf}K6C+G6H>U6N6sIdQhi zESd<91TzA-HKLKkIpWbD!GL%_oQb{+3PCY~cog#Yty~8uhaBMU0LQu(m4q1|=rCqx z=FcA6bZCTZbc@DmpLFvyQoI2$87TWe|UwI^rN7B-gP9qhU(S{KlSvZ&@2PKQ@ zeP}r9`eKeFDng|qBv5jXTa7WLp&FLc?qlN8{pdu69orrE6BKS?c=o1L+hG6kEyQtO z7(Uu{i@-UVUN$Aw%SR_$ve6V(1jV^A^*im1VXGs7GBoK$(4!mV!&R2x?tIB>jd0*% z_tZmJOSBy{IUns-xx)_v$7&_LlqBHYvl4wq^P-}*A$pAA&wxw4tF;O-UYNG3vR+D> zueRU{blIN_2IS(%1U}KiAR`Xo=pER-@{3WBF|lvBMsJRsSN17Kp?|jAKpjX=1MRl~W2RjT3(40<3oo8s{&16C z0~E1VqqET=esAo+?pPLJ`ufBBY@U~Eq7i1{zc7GN<^tGXsK)C$i|4ry8yuEgNt7c! zC-=v>*=X{z%^TApO1J5s}oQtNf~ksB^bbp zMNvuRwY`DoK$C>*}&imfetp1J9I8BrGg?^ zZZwFS$7;|M_BD#U#(HTVJFQk5G>*y2$`Y`ewn5G|#HFxlCE;!uaA;0VMpI*J*(_$D zY#+MpM$)(jr>(w8_R-gU>+9>|FP$0u2IdGQ8-&L=+oQEB-jZO|>;ZNUYA@OQ_kLqp zqLubr5}e^LOePxfN1?#=`SHeVw(LDHCt%-kawewnc&u-21-7>GfxpLe^Kpyg7VGSq&Uy&L@R|I5eWAE{cmOpf zO!qtwPO%2mkSbP!_jVI&x=3Bn+}s@c8%QH#B?2QO(XAG0jFuXlSWW-=s_J-Mu~%jC z+uqk7Y_Ez&;0G^R@3ghUT0T2*-CrMrDST_+G-cjxUS4tCSZ%q`5}w^fZak8rzW-oE zZLyjE8xDeKySa-w?bRA_bucq%Cwt`{k|i2xzduRMX*oA;bh$eRTdeXeP1=4`w^{?( zZopiFCHia&PJ)Yz=Zn48m-mzO*4jRyjA zztZd88T1;ME!HtHHk}G&7^Qu-^M^nn0(>@cF0WQWmD~lkjGdjGkdV;nFvm)a8mV{` zWYp)W6O_%td2E-M^;`k?8$6fTw!EC2a&WefGD$xHM1!W2FmE^3LRC&aM;6yUT_`0Q z9ME&1KQ*9iG@z4IRZviHxL6)NqwRwR+8#l2AQ*%ed*R{cb_Usqtn{3-Z?ki21+L27 z&56&q`EBbD+}uQfG(9{%0-q>SQFD3B8Z;O1Jzin~3d(D3Z4FErCcAYZTt;os(|Ub4 z2Zw;LzP;V^6O-(-*Bz_F&IrY)PZ+?K5DCL?C-r%d&u6SBJM;DR{qXn*hG?$FD%sN# zTx=9PvV`&s&|ScF1Ij2z!iNJyX>z%?U$?eO-avp+0`(fYp8{~G%}ql$ohb5$hlfos zaBG|`thbWiRE}4E{lehnrDGlH@A1t;EHj^?p~FR4DZNiiVzW>HGP*+PHq8($WFl_Y zUjc~d=V!g7gKJ<3f$lChK5FV$ZDtWI=2L&|U9Hoa)(`jhLsSa8dwYAS0iDtw31FI>Kz>&Na*PCqUXYxL`0~cBjg!Cf90>v!2sZ>L<(b>xHGe{G=_4U>| z0ztw@t?ehP`Kqr`*>6P80PL5xJ|zKzHzv6iN8H*6XWjeRUiDQ|X({8&&Y7<= z+r4rJCQky3$7^6W*-CI2+@Xevkb$)_l**zS!D#g~o_%$3kp)0rDxIA;y1xr7;akHe zO8CP|(tqi1S&VHj`{dm(snkXu!@#yD=5jU!T5WT4(-yQfFMHi+A)w;RSsMX$0E|Po znMQQHMK=dfRVRJ^@LFlqX2@rWgpu-TN2t=o zf^i)KV zU%2jzQ$R>a8@U<~5b&1O6hm8En@+t#sAl8%I06{MA3peFl8MMw8cPF#CFSVoSbs;0 zp@i*ns0<%Y$id*~_+p>O>v*YKF1I}1jo0d|2yBE5>J+NbGxX@tIjO&Y|Gr4ATwwY6 zHZOgvAXT%ex%uq!vWYR*B#FZ&V#%73m4ThT<{#&ITU(nzo@~ef7BO*nyRI#AA(+A6 zTiY*>_r>K{^IenI{dHjOj43NQ`Qok*#T*{evG8#N=EG7KNG&#vet{mOp){UkFglz$ z>SssDo}QkjW0~ElwtzMug$OlS7U6?+5|BnH1PBaZC8DHWF`%?aM?{8%fwdV>f3Y1h zQ-TcV-x%Q`v6_{Ym71$&&4G!Dc{p32)d#_%lvPS#g!yXsQ}r1cc_ZzOE1(G&c2`tX z$bciX^F-8rrK3W&1CAZk8IOwJANzMAr~WYHAA?=~0K|v>55e)52*Ch{z01%3Z+Q5> zKB@_mQMLVCBYWWr$i@IQ^{i;t<%0WhT;8AGP{s~O0p~(`E3EbN;ZZaY z`2o1++f!$yxJb2gmaNv331IIvd|0B!0cAkS%Y+6BZ2rq#2Hqn2x_fGgp}@Ws9i{v> zbXRvM(C1G^N!d&733`JJ4GsSRQNp~ynmoLlh3+q*Nh zq{fwrxOViWm^1#v$s8BmJ39Zi&KP8_<|<9loiYz}SqUU9qu2KvblYJXocH!)yib;! zg+zr1YeENgw5u#;BG7pufBGOy20gZSmu4fWT)m*!zNAy>Uf1gNckifpUIo9Ru=c+X zK*n*6l@LDRZC0;v3?_<8N*VyPV#pU7cC+l|3Ef4$_#EB_JR)94dr~CsP2>)qsT9T* z&Xk-FZtFmdE*R}+0dJWtcR*}(K3U)Woi&ugsW`%7-z5eLHsWg%-TTsoxmp)ekS9P9 zk@~`+C$DFgcl`=^f5oc6FLV~~sC_15djK(0! z0s>JUlzEW|qJBIbPx%sFkmFV;t{tp`wJ_Ry!MhEF>*QbMP_Stgn%$j}xM$&ftEyN5 zjqNIJeWZ!p6%8Y5&;Om}dQr?_#MK#u&Zv7oHKQ$@HMY95)0;n*mB4MrY}0gxU}k1! zxzduF+IrG8HHGtn+O6D49F(p7-(O+L(Zm7*_R?VyPQZ40exCU?03qjZWPCgxaNa%- z#?zaeCr9@W4CsT#;-Y(Rtzb9FzlER|{%F3|U@IiH>H5wBWNOnst!bdyF0P>1n)3PS zo)JL3%+b`JudvJR2gS(LI;;HLMdVar$aoNd#bXsll5q^`1Iaec{V$95E-`Nl0{9wK z8X+X*p;)|dIAqE^b^_cMsDP=55>Y~8Vn3TmadAcL?U`5cGho5{34><tw&z$?@xq^35Vn zz0dIs{bx`|3$=NmMB|5x@IJ2Op#lVlMST4^R(`TKd}Dq6^z>BsAg-A1TT7Vt<9+PF z&iG_C_;wg6M;Z{y<@(*oAY0YSeW*>=)EgZci7Wj8ZW^Q$0*kcxXXJ(N_*@K$J@ZCV z%jPVb&d+Hby_;Bgd3o(q0D`A}anc`%q0Y|BgN?w1#L<6~sNyo{K?O?^ED=Bo;z@Xj zB?-K|yv!DA=z+l_SUM9O71fb4=dcL@{H(@m;m4e%Na57_)={8B2A%H8!UAFCoRq$P zJ66H7@6%8MQ$Utz#CVqIUYtT)Zi#ty(lJj13xK2-N2+wWc_62FAQTWc;O7ry2nKDI z2?o8E2mR>C7hhMUfOHE^UVU1PKxz`n$^ek7S!e6_^mvc=Go8nTeOY_C%4#BRzS?}^ zDA-yCMgbpWg}LbLIZlPstTxjr`MXp-k)({hqp2LARaNK_aepG$iI9NJoV<6Th@JQ0 zRB*q0nykW^ToRXYZR3^{kE(NYH1fURkJD2@bSz?=VW3#H>Hxw!oNtN)6s3zaloT}E zpU;ye^}fI2Na|%{{G`yDSVKdL5C9$B59d2(8k48fW=DqzRs(2J6a)euh&_YlCO6c# z%-Nb=7%^#4D*Zo|S#Qw)A1*^o18f*RuRxRW&9! z;3$9Nk=@**zdzmL2gM&W9Sx;&)blm;Ra`TAqap0OXj#H zXTSIW$R9*bVZKuQXMdx<2RQ!oq;F%V7|_QW}!M0j7QOT zRD>Wuyg|);2G_|FfY;8OfL@2gStQDQR$_=C zSNq*Q&-W|Lu{m;7Lao$fAESc-e7sTQ_yNwgJOyL(34gZLr&Tc_FOL!wohFvoOCM>f z5G|@Tsx;^&a>mUTDvVITuH5RUOg99eZMjer4ipJsMV$cKQL-5=S(1kzfX0*214V`V z{;)D2gvcN3S|2wQIT=Y}T( z%y|hT2z_S7+}#@#AHW^%u9WhDsL3t=NbqpExl!uu7BtEk^{u`a5*Cr?$YlUQf0rdY zY{Xvb|HJIKIaojkWhk@`x9X=wJCJakK%-)=VH#Dm+O7$F;h7F*FJAapuV?}$(`)cZ zEo~9gZno(emK*DI;7Pi%W6 zRdS`p%WdBw7?nbtK!JAP5daV7h0naWNkFc@c)f;(*_tY#8UR4f18oAVX5-(f z6?6M;9gY~)E51{vdq#oD9&h$=0bf4YES)`GR8f20TdZdRyAnVGfOqnsQuU3x%M#^6 zTp%1oGUUpf4pc}y4w-;8Cu(R&E}hJ71UwbZX7}p0pFA$DK)p;BsYe6#lqE;?GQC7` zGm${IUd$RqV&dQkM}L%dy%^>Ic;?bHEHOV24b;u4P%R|@J*Eu3j3SNd0c!8d{4rN| zH+x8*o=U0u@V;{4Aa;8CZ!!`6+67MAQ>X=>rID8Ad*A{8AhM~}DAy<|dbFlrsIeiV zW(t03e<`!PWu7!=12jv2=2**d_upFXArRV?b`v7r3ulDrJeL@yt=H~}3rEfh88D?2 zdjPUzhz}obFEkq*g8}`+-Sw2^K7-?OITp&WeZT-O3IHD+_;u^?I^JvNTZ2Hd5P~sZ z?+yj2?8Ehf&C5SswXv3|b#Pe@?*DcJ%{F?$5-YTB9>P zNI4ifIUooX0dx#8nV!B=Db`d@s86nW2E8PMUYUwJGQjL8Q@W-N;o|ilZ4N_0MyC09 z3l0S@z?_RAy)=x0Je8FIZItJR$YQ$4?#tQv_9$W8@89h}wG4pr2^bC~s6s$YmpdP6 zQpqhbhx&&Y|!IlBLl>vZy z??Q21annnJ4s>g*jnvKVTmVq#=Q}NY(7l%^{6byf)W2wy|Moq{gC)3l`NH(nTwOl{ z*?QhWE)HmO|NYa`lgwGSJi$G%dR$yw+;oan41nk`YR#^7q^xa44BsoCU!RT~@cSP!3$I6F})`Cc#_Z2;^57%0s9~pZiNqhCn z`__P=3vY?b<_~0|y_&VBe&z^*`?@D0q6?M;_!rB$Z0#m(HGZBxUY&YZrneBjbhdz+~8a zF4LALSNky9_KKdqv`f>`g#mLD4`0i2w#*OIk-qzvZ0I|j8y|ol;JjiZ2YPnqmtlqo zWW4+Q8NZF%M_)fPh0kWb<2>|e#6~!L6(@u+3j%aYnc2igDB$0n#25otjEK|zJ77kY z?pNj@s-U|WZtCSaY0)S~06G&3Y{|2YUX<)tXiiOK7GSzxqOtQD&s%%Y199Toh*KMj z*!pmevbVSQ0GWtERX-9Qx}#IpP*DaFLBnG7_dVewugRS?94MB!qrVsX3P?Ovx8Xy91-jYpgw@%_F}2dmS-s@ zEsoU+tR)Z(rRI%#ggNvRm(LWcqWgklzzbET-wen*Xj*{E>kn0)UW0Uc|<`9hrO@*!(EV%(Ib8!`2T?$K+pqV6 z32YW|ifN!wLvwI&v_RHTcponnp@6Asy3*3@8R%N8U#)I4*RxI$ih`zEI0d?P`Ipg= zk@bO;moWoH_)4L7rx&D{jIA{8G3ftlb#(5o55Ac$#?8 z+0j#~$;;^P+MmQ9$Ro}Q5m_>US;j}^zR$zoG>3;Tdi!V28%JU!spJGpxSX_GetUR#<5 zt7e_G=yLP08>B^rsPN-zXGe!4AcaW`uT(K%Nb=LZ*hS&wBiE4raW6^YarHey%qM{%QAMjE zz4;juV(;>*PJjUovb$@lI`W#IBCfTnNu=lN(&o5tyoCD$?BBwQ7C~Dh2zSyjJ*B!I z?UwE5MJ<0!NJkN=ru(M}z#?KRVSU|)x6mJ;pz07jPN8(E;z1Lc>68>AH2hC(?%8RzwYAsBONGD~Q!XIRKLLun7?414x)PDx_&DvS z04BNGdMosn4W`*mGaA6Yy}y@?0#e27>PlOkOKwFvky#i(yMxPH+sZF)sJyJK zO43QJ@{W#G>^94q+vOUu)v!?eyUMI+1g!tSjAZ`#^RBzAi;A3F2xJOl?E#2|ldqxj zvph}}`nB%>_q#f(UD1$bi3|%Xvgd-Dba7%|WyF2+CNqfCTMeMb!0>RfGZ)khs9o}* z+w_ZCJ&gJt%wkTf7}DO}E$8RwMd=+K0>qs5LI8;-LFXVCh(Dm2R)w+oGU|)-Ar*MT zU1GuSj)9R;c4ss_{_?`4sI06^Waow13ZsU~6~+}~&(kQ89TR6nM)Q@7xz!1noBury zZt68t103^m)1PulNq*Swp2TybCwyyT!{8AVPAxEM)8JvIYHf6GA&}kaZ#J<(K|!EF zvFJu=^7W((5E2asC5`0bpl$v9>}(7qp%#{R$sATHm)}j%NO?8Wj~=CTh#&$$1WbbJ zU~WmWMNO4|8}xl=Ys(Dm4w+7n8nJpmKnT*~3+s=V`$5N^(yZO&%F}i2U=16HOgfzZ zv=k1@%n`i~0s%xRwPK#Bz$Kgo#McjY(ud!Nn8$t6>;X9a<|BukcdG&DN}8HQUjqP!fh3hBh)pmdKLj)-9z)O_(A^ z7=tl0S;jJB>6}i=3|YrEmaH+;NOohH`HQk4voSt@^zSk5UvCPL{d-H6~ z^!3}sca@HWKHs#{z>YlDo^2SmS1X1^ogV)Q$tPHCjDa6KiW>gU`E^e7NTfn;PR?h= z>u_Vk@e4L^A6^+78#`Wb%ppaIYasG3T9d~UQo>?iFqkX`4KCm+moc@`au z+`VS)T0^FD7ODk4Iyp1LbOZZU)6A^ex`lBzxKLN|Yhb9|{YAiO)?hkWpE*-JTVaC> z|6Yx5(I8~a++;ZW`*+%Mzq~n?Il8V1%k)kZPm5I9J#=URT30TdNEG6mv#u$dWv!&A zV0#@WB^%~yTwR^v0wS9MJXG@BI`uJ$W9=9yw6V(Jrya6w1BUmnYCVurpAFMpC=HF1 z0A}JIK70=7HYBpphF^NK-Lg93v*OVM2kc<%pc$wb6VM2hF(HHt#O>89WYb9TJ~K0S zSS;_B?5E#D*z4D?FBUXV^DqrF?d}Ya*1dlYd<^gZsF3jTW#&BjCu=Wfq50EwQkYYN z@!}H@_9-4ex%smou2Iv|%|T@xj#`Ha76-rJl4`rC*L9s6eqY=1qWGRK<$=2B3~OL+ z)8p;g!aIzXX0i?PsF|&P%GuK1aO#ySS2$1DN1}4&jmynVBBmYv{OB+tqBmLfLI4K# z-Me??6cpeULZ`G2?6V8m%nqOa{ub1sQtS-KsAr;I{gmlx9!nxnT_0?2hc=oY0_!U6 z8x;{{fa%G*t9M;t%KZAP7eGYmwIgJGa}RZcx{=#9EktcMF4S|NBQXikf zSO}QXWp6~Dt*%g{VsHBnq7)Ga>Ku@5zH2 z%i(fuWR>(CL7WA7L+7Z|#@qhF-N~>QHZWoVvT6c|7}s&yJxsWweG`!_Sn>~Mz^~pD4VpFUo{KNMA zi$zDmCa=K>t%X7Y9wfFrkgV4WkI-7;>k<|bA$(>u-U#HBRstZ>3uZ3X|MuJS>gsBk z9!eG8)7`hPu_A1$6|jPIK_T>)e2BaxoB$CE37u6|DXMDOVE69hA#5CE6EB&Vu)1dHBX|OfmZRVqoWrV7NiS+t-4d* za>#jhF>GO&7~FF9sPuV{A3qMA$-cZ=DPcxHWnlRZ7cRVl+)FO-b=R=4FhbVEggz@dls4h~2p660wsOd8491LU*XaO9gz;Nro`r=N5bhvEJaQ51z1zn_a1;&Kp9zAMe z1q)k2=<=Bd@DdCsG1sS^?%kAeAF@7~9UT{_k)UF=Jyp8ikj4X5Tg!Dt>VpA{<#0I7 zpfpWG(Z66agmLom;9?yevtZcF77W7IH}q3R>Rw-^Q9&Gc2RQ3wZ5^jq*~3m<6z@Cd z!95DRV~vcA7HkIiP#$SHnDh0(KBWhd88PSA6Yyylr?$2UGDR5t*?oObqbgMK1ghvS7pmWo%qgH%-)b$p*@F zaS*L;G9v0Iumn(+pw<&DS;m5{hn#@mpcIKD5C~U9%ODXCDjvLYqMW-`PVSnvkGXk_ zXG5CrOpWtJTB5d3E-br?`!nf{>A$alaxEnUNfZgUK>zWASX!cvJ9H^9Z?XZ-*7Er- zy}iB3pyNU6>LrSIqm=Ja%AbJ{k^}6165I?w7-O&&T3uAqztPA^)Z)Hjvld2Dg1^3B z6$z0r@$!#o#+nWCqad0xW~YgO<+H8tKbC^w;z81rCmW@BtNW)YgpC}xM~^11tiy&| z;Gvq?Ml6w2w@a%GN=GMTy~qZspx^@lD;%QjoF{V}Ek=$9H%ZV;eyle3cNt|n04t!X zlKpyEnkJQ5+sH~cE1JR^&K~W#)fuL)p&@NNhM-%aW3BJsm&P6vy=gZt+O%W9sHC+^ z>l%}eD!ksH3^l-op>)LSZ|FpwK2Xf})h=v+ATUWK!0BQ=Tp$R_yi5bz8K8F#6q#_) z)>|POX6;kc0C_el6dpm(?LO_PHDklWWRO~OFBW-X%lzNM%*S+hP(#40Y~=u#_oW~& zO7Vb&#a&>bZ)&KVN9oO`s<7%#p1O_%zq0CPIBR-J^%)($OTQqB{l-;HWOrOw7PSFG@(Eg3os z%mq@R3_8VHD@bx(|NXS|>XqF>0{VbDYELC)2?{$=Ozw{KcT zM*Oi=za{dP2II{Hm2B3J1x-&IO zH$TI#h9M?9_(X6!?)Df&AxpcQ>8`op2joUeRsbGiyu7^1iI*UDiUq~H6(Uhm1OsZ7 z&(P-=z+Q`1{_<_he+^xV2Q`I;V>=P1M^bKj1KkmHmlr_{2Z|OYPFY) z08r9GZFTabAULM5#q5a??UegwCH7uo0z`;gK_1vs6@Fa#{an};SfhJn~sYHaW+MibC<;{f#l?jQ#+r;x!IFK`Np=?mch6-U&_Yl$g z7k?GyPUj6PP{Dj4Ze~k7kh~ALE|g79U7cee_)UkAT=uL-*|=XZMBEd7G7Dw;H~+I z%hY9Pc+RK&A5Kn9(Jq6+SORuL=3H+?!!C7pE3{MO!TlCAY>B-W)$VE8?SH__Gdp@b zR{1%&X*eF~J_2g4+^6qH7ES3V*@d&r51&{iuD7A)n7H7t==2$+*H5Z;=24J zOTsG)7?g@>V2dsPjE{C~_ruM!I-E-kYq+~tVYES4auFcC6pSk6h3ty`@D@OW52sZ# zd5g($2z7aPynDNL+>ArMAr&bd>3C!nfApFFNaek$EW}{Mx|pxXgsygj2!60zqTdy)LFN z1t!fjb#)t^z<-WKkVTY zTb(#DnwhF{Ocn;8lS?%Gc-i$StyPv0&WU6CpjczD*yINrci3mwgLx)_Hy5|OWo*Lt zeOnyr?|%#M_M$HU0PC}7d2N5u#QPRhq5s<)Rw3b;4UnFc22R?RJf7M=%N}j%A;D5y zc8a)hCcp5UhtFJEPnYCll*gOTjfWqTa>?pzy`6$f`Z6;1Ad&K#{gU5{Rq?U#{$wUh zPbfIM>cO6njKk~+a@*xDWNYs`aJsy_Ts%f0rCqyMyy$|R?EW2~GL|6SYDj50 z*w_Bpd+^|Skb09yBod7Iq}MuV2q6QK+qq^9y^$sfq8f&bdMz9KOIADa6K_;+w!LFV z7#bVP9hJ$PnH87%MDC!UV6ssDMA%k0f>fOV*`88TS9b=)?Hou?w!N!fnPGtAm~Sjf z6Ss7ml@~kRo|u~>ItFeZX`XTL{ttz5beil{${!XAeJW=sCkt)O;Wdl7Wn~@f=WNIn zvJFAw3F%Vgak+mfl%qu5op!5xd&G1IN7CA5B*At7>Rvi7i`qej6rBMT5~Zq|Sz;}* zqQ}Jm%qB~zhi_+vm56Vo64+VcQS@iTKa6BIu#cTl()VkORQ=(wvTz3&!22<~`*_eD z$pB`7UtuH0$RL+PNQL?zdL%L8Gu0SW%*ly~#-@Z!L1TgC>sgJnDW?f-)s=34{Bdi~ zeb!zBgAPq;u-b3An#E^)=(hu@5@IzvXfZgyy6mgNe$F3vD@xVLsS)pwaY$^D=TlI) z6H-%YAJ6HweX3P4+GV4|qvMQx&vfJfHx+C?yz;l$5B(1~xDTF1`0?)YICn(`3>)I$ za2Q6+=J!Vbh^)VJCmYHR2CNOK1MXKU)g7rT6?vSNxv8m^&x+x9xlUJ~BcrcCI`ZECFdhttq^{U^Q%d!cgxz6ip7_d*kH!9vA^?Ljf>l0?!iHq4f zL73+EtvE5NX7gaspYrb6W!BU)Bhb`#`}S5`1ZJ;2XF|g=?ST*i&pVq63(aENFhfpH0tu{3pbY=M5Zfgqwc(7V;UrQ|F#yE^pe zm6_2d%gk{la--8*sZY?N{>o;Mm=};)Bk0VeU}H<1{(ARYBxpl=m9NZcIbUXdO8Gpd zkMsT8o1NQraJ+;0nVxm8`Um2uHEjU(KS7#IPEj$>1Qh;O&6mN(qR`h{@1?A1sr-ga zUiv)5ot0@YtUN??p@2k37g8(FJjO;f#ZO`v$wj?+3=F4j!95*r{vSGpdQUd~nJ}KQ_?B zczUk7qMzQS!_9Ix_r0`?7^dDNJzY^%Ze?1je^YWcmYhJpH=?g2SZ)e)6%MxzAkuX} zMM`zq+~8OKfEkgANw`wcGuGAx~4)aH@tB^@iBWIId-mt&7 zCx~%q6{5xCO&zcN$>LDc0A| zPY(RKUpKAg+_3TAcOv{f?o`;Qlzl6EF4C{{{1{nVX`^4;U-qsj^1L$tR#R$zdpKYB z37)x9*Z*86s=Mc-L)B?NTRH-vm#Idy!4*?{Wl_2*sg(198&~GJtdW{pb>C-%f}Kte z-A^w+zCCDjGC`EFW28uCGeU2q%jdg7VC5}mTwre`QSbf8yXA^#zn7EoiQT71UcY&B z85Vq} z0EsCZ{DtBmq4^O2Fn<32fr+QZAOZk#KvG0V#Xap{(M<=(jST77-qjyI8HEU56wC1_ zsKmv3FINlyONrK1vDQ^#q|d&xZAqTS6)E`;w`0xWr!NNaB*;91GbkR9oU+Q~6j&0l z(4)JJl0jX&`@_U0d4@y>{kc9Inx)fue=N#jA~E^=`unmQ*b$=wRCuILmP>HJ^PETSmPIn4 ztGvNvZ(*Tguml$++HKuQ(|CC)*bdq1{WnVh$O3>K6%tH6W7r{J{n|pKZD~y}x>l9yT3sz1XtnrHIGNw@%`>D9hUnLOk>?IHVzr`$lXnSI|-R z-ZCMs{8T2CEtB8vZF#Or<;^QLJs&IuY^+#44_dBsBR~Oo`vNdU#ZhAlE3@cqBvl`y zs~%`j5MzuVPev_*nHusb*s`c#BRD>kKH2kTCEiny>c(;Yx>4y# ziikiwN#KOgwCI_z@mT)^NgJz{X6Qmqk$<&G?(Av6*O>_;tL6|A^Ii#YYksPCMQ^G3 zq|Q#gW?^vv4H>aKc{k>Uc9ppI+ot(82$67d_ul-&?NjY^P4BhS9NnMgXUisyGMZqdOc!9p3L*K6MGT4#w@X#wSbERjpKEm2eK;F8N_r+{S#~{{JjpK0 zefLcJWV8Kp+Pw9E_+?rqHonh|m)G*+B|X1~qtnK+x@*mylepYiNs_h@sjo79n$OxJ z@BLLgNxJ#IG4o3x>n(j2tyHoMH}KXZ=ouj(cp8f}UA9KZ<&JOqQ%wgUwFALF&fHKB z4!^bO#`?bH18jK)eXt*nqm|15M4uaRpQcY%nImy~RI(ffjiprgIa#KphiBbnA^KGS zEc~G7cb7Y*=O3X)R6Ebq1{*O5;&TU700XAd`NmYI*>jc9w9YsH5bXRgkaD~1e{nQP zvYrS%=rNLdmzqea=@?aO_N}Fy_Nv=*XfGm6|0iypolTY5w{(=$N8UPz4iu&^8UT}_u)Ql6=;cnf{S_x06@+)2~7=VhkC#d+<_ z1yRLzsNF*zN5Y)uuXNYvRx>CzZU!UWLd8D0iN`Lp4jJf z%xK%RQ}gJsD6=H2Ehimf9rm-^9F=8_sFn;7AZ9_M4aw)~9mOS*L_jKj`UaC#%5!xl zcMo|pt|5cy-x#0(3os{#>)x#}8)L|eP zUzmZnL)gj3^f`|+$=Fvqiqwp;i78q$EZe9}`6MalFUjs!WUMAgxC1t3t(DFm^>IzQ zV1NK2^U9ZbGKd#Ry}2;{$$5O;KLbtM8EK={bb^v0w19&L9&s5;cQ__My_uk+%#sjF zAV?5^5zii;(*JYei?NY#PAUw5-nab>0Jzh;jH>rjU5aOVQZ9&?`JV&ZvjdjXrjmv< zkWM3SUVmBvw4TA(;$$&cpuq!=E+M}fu;Sujl>Rv@nptDuVhww9bE8fvB`5GMfAlq- zXWkxtBsbcKcO?Xc&Wp=_w<#vtaVIi;Df4q8cR~Xlv+bd%amw2_Mi$lVY#PrHf%gQ? z1)F@C8H=dSo_iE%md@O4V-pLAfEK2{(auPC%2ICh%0W6@k^R#ylS^X!$Fo$ZOl%@v zWs(eg*reUH(z3j{8Flmh-Oy^`9<-sJtlg{u?pc!*= ze3`JG55(;srv<-(GPXb=3MZ6QmEC zynBJ0sL4eQR7|EJ#C2DhC0)Aq@jkeT(Pn(`Dm5F$2$Mtt5YrFGzTS1<1OY1}-;B+? z;lZVNhOZCB-+H3;{n$MmVofIJUN%TYJ7w3(5E z{y}e|H!RXTgy}!B0`l5WENXH;Tf36lE-i73lzwh}D_AhDNrxEL>0mp1chhjNp%$G< zuO`=FFjCl6p$RSoB0y(0`Ej%mIct=Q7-PiZxuSE#w#%niwMJB$&$33XiL-m$B>)2@ z@vh2(&70RL0wKys9(bvyd5?7dU1l__nF#N9)B;krIBMy8hq7C+9JRUdZs%Y^ezd-@ zq^8X&dU|<^DLW1O&Z-Y`X@sW}>HX7!+y4jCv{TtTnqk<1fzrT3+rD*XmJAPuFm!+p zadB>_RK4vga_Dg@GF^GVR(B3lzYS=x&qSfIfAVy9%F#}JIROtpY5x29OK8S&&)m<2{DT!PY(Mx17kAZy=>8bJka0`0sh+IynP-S$y@k%W zWDJxemNtk1k_E~TLly#7LI`4;88e}#Dii()e;R@V7jb!6&hnA~j-%yt8`lq9qFytm zi{s8$W03)oFyx63s|WhyFm$o~Y`SPsVY4zk=oIaS?+`uA=TD?h!*&wC66UQ}8CTE1 zwY)x53(BmI(~qd4OE9&xfdhbpkFaTx>?F&@yfFHk-m}Yf=xAc9C^g{)s+jI=& z-^mjP*^Db1f*O)-$_T=&#cH$1RNofR&@<4{4CD|hr!hwNOUV+=s&V1JeR0FJLl=QE zAE>VrMaxM4l|@5`B}|GaRKf@y80Y@%>$P5ZNS-;BybhuB_sv_!!h6lO zpX?_{84bk`x_}1e@r!Xw2bQk|OQNUxM{;l4MvxX_}#HV=h#YZ&Bgtq-~b zv2r#z`m@zayM8v1Wn`Vmydmt|C;q_K%n0`i8+zeFW-h>idQR(1 zWRAFlI~q>!58=-iGJwW04YY3uOBj84L)snBA8YnKEEXl97$7Wix77q7!a)f|KEH5k zNb~eJhe5_hrvfOx8p4x{u!0cyb1=3)*62URcOVOXwzxKLM^8Y&JQ7hsdo@P+fg{__ z5S~>AkJm0>+3lhu#y%!p-C|p9BHKzFXsw?>bHATXP~KwT*nd%|2fyMa-J-ydPc*OH zm2a}KsaB{G3ny^| z16t8u*P5oFtU|d|!KH1S@#$G!gfP*&yAtnJYt!LEf{n*@8%BqZReQ6g9bnb2s>n}IN)ldSW2-ixz)(h!A( z6rxqaqcLstEB3ka*iv_x?-I!?Ib64+1%3vGsS(Ine8Q2L4oEgmc7HUCL`58E&7*zuyx4wzbBoIc;e5SuW*9&cIHk)| z>vw+Kx*OgpDoSW3gBKc%Ngy$Jy?2;dgu63#tx`ENzPRzTGG~Id+8hysWJ2HL>>J^g zHk`s8_KJYrePvEaQMWh-*}c(yh)vmP5F>|>MbGRI$s9Lwsn^v$;mz%2Cz>HRNj<*C zjmr`71@-gdIjWL0T@u8A(f0ZcMlavJg}l9B7wf*#?JZrNRF1O>d<_q;#-rO4zlH{` z0K{`Rz)k&)c($y=2q|#Q$609HG$rR+R~F%l(*=>0mQ`iQ9b4oR&~J2joX$98h!9Q% zXKxMv3;;!}_CwzY4jC}OvEYZt!+SBYzcF&UNGG80`DEvoI0F(hs)+9W&lKXd*P>IJ$F61uB4h9=Jc6+VQsy4 zRT-MS#hA|PsB_hYcChb@zEtnNdS5@P2rfyp??vRwGN#2fCGjwR@sWHzQdj-k;QFE_`tl`?P`dlWI8D+Wo~Y6=*H8xl(kj;h`)Mk zfP};Kys9(okIO@R&4`BeXpU8Rc^z9Ld*+YI9+&{2$b8v(Vb6i=jYVU+U zAU(5Mm>~gdY;E!BHC^|n1h|wOOIVnhheFcl>mHyO5fQ-3;o<7`dR%fcfh&XaL9ncM zD_3N9CU^onv8v5D#mXB^v-4MuCzO>EX-pQ!BSB}Iu!2@r?*L9N9%Y>G-@oVjHPe0l zR9Z@Z;or{|-D|>}{B6N0EiJ8>E$5UfHz$R_dgR)_=u>DY3cYsi?#{II+2)F-zg+9f ztBp#kkVq1)Z}%@06g%jS#B>g~_I8Z;FKvhMf@P0tkv80ox7d)rc?8d-2lW5`W?Y-%a z$s+cJqeYZxkV$m7|PE`Ws^1VqIpN$|xdC0Wp70YFFxB4Fvu z&&JQkr+UWRPydF7MzWtB4IUPdkdwbOLNJI(T~GS%u5d^fp@YkQ#DKdyD-(zP z3*s7ahQzR#m^%AAxie-2VZ$p@zWDx|oo=!ko9dCFGVA(7Z4P_7cXR^-0}HB?;d88{ zK%M>kyXT>(jijP8u2j3+GHPF76c;G9m_HmFFbtZmfis)o!Vue*B0Yn4J?r1Jrgy_BlD1 zB-h84TyNboWvH*t9rh;5RJ-MLTQyq{0ZyI6jO9NfiMhA;$&+{u7ZP#as5V^k336ook$1{3E8H z4$I`6xzpten!Yz)?u=g@K6L*PxfK;RHZ~@^H!UVA+PlsyFF)LJhY8~!7PdFhMgT@O zI$%9jikZajGk6ODV~d`S&T4aVzaJ&`aIq!Fqeb8dMXT1xfAg*Y1P2J^bf>BnESky`R%ts!lMKIJQNgUUq zIcLx2??*G!hFwKA&FyrHPL=g3r_5%}&-zmT`N~EcBC>vwR$w9=jEbFjXg#KwNpYkX zKSXStx}m0p_~n~~?J3e+feS}JHZ32oyUu)*>86LLUMgEZDrJa0yhFFJiPel3zaECj zXN#Br6HxcpL42ZZIU$(0S9`XM6;6&=u18}h-ZNYe-|#7NlldvLvdI0;Kd-_eVTMY} zgi1@z-i+H7+hi~lI#nW5knB*w8W|gbD?u%bvEsn#%-te8U#HvTQjk$BguS;W9$905 zuxDdW8{4~EL(M4sYD66Vdz079%*;Rv|G}@I3YAKYiz31om5*zEd8uxPoo_;uWz5p2 z)SC8B-QJCq6WdxGNqqOyW=iu!mT>l48hRR9 z+8isVV(T};7ehVS57ryhm9=V8ChH@QxZ-LyUt0n?)IV;a6I|02PJFtw*+huPt=x2I z$Z9pIsG=^HN7LVsFqPHg@$#Z43RP(N$h|LROJB@hf>P9<`!g_bB&wQ9EW*jbd0=pG z7HV&w%5E!^hjD(WACPx*gBzy7s8uDC7W1|2_rdHHj`?}K?vj$Oh}pYGdqazRn{4ke zq^0|7CMJ4%ru+U#bGNn)>>C}g_3w@(J>QvJ6tY!xMoV03>H+NB{0(|Brr)6}@YlfkH!Z{eX;8^v>St@o+KiB@DdcuLMI^6$ zxg9px!fB_83Yb2of+5}Dl-9E04E}?)Y1y<4Syf`iFYYHsD+NBr&qata#Op72B@bM~ z*bB>~hlh-r(T_>9TIO~MT$)f}eZ&A4bz?x`?Tfx>up1`KS#|AMt+%sVY(AOnPke^h zVqH&aJ#C*=j9D~ZrAn)4CFYW9Uf7b0xG2n4Yun^HZMT%2|BW%kxpv~axs2>!oU!Nm zIgjh9^CCx`(*>pHxevYI#YQe7+rlm}P^QGWdL~ zdO5O5rSm*KnhiR*IXbx1q=J8|$nsC2{U`Li)Z}G8;_nM$h~4$KH7FQ!aDc$k+-!4l zNK0Ga{J76_fuj)o??D@<95 zpNxG548L0CJ8{e-+5+`yQO_A~bmtv{!FP*#R+%X1T=NYdw2`u1jxuzq^r4OJ6yK~1 zE-ztep@-=?OCoAqcmLGAsM^^@2)P#TGPiNS_66wQ2&S7vZy(i+{uOL_dj z-Y$YzU}tmc`cEj0Zeq0(9vFBXPn+;qwN)Qi^DRU^%VBr}Jxz*C7&EK^V-Ug6HUNNv!yxRYDV*HfGuQDgv1-)U*w@U>4KW3XV z>bGF0M(r;ybo&kTL{*zJ=r!OaCjA<3cYB=vSe(YUuhTP0wCWg2qqj7;HTclC(5%=p zQxtwH{bOK22EamwFQ&4!MF4SJ-w#|`_0*IUl)(0&u*3{b-){^1hFikZb~8V>QqSD) zxjt^C^Lr2j%>M%Ks@P5b6)>yQ>)E{8cD~K0pLcz{{!k^6OPrjLLg>4UKNMbG#2$aNEn#=dFIcVa1x3TA%Lmy5alS5scXLlLIT`HMM^(}EOdmNe?8V;Ok zp|Sa|s9$|suF&MVUO&lsXRcrE`XNUQ!bv=o+Y2+C$yYk_er;bBPKQqk%)~J6l!BVN zIz<2Tb7%EwKZfJnf&%bFX^_C(z6gndfnmqevbeh7RoDwuQGF_{d=wROdAVsy>Y&fqgH$Pw4 z)JMs#dAz$vd0OXMG^1m&xDQ*C|6`^J%Ch zGB4lVq06$jhl1<{?eIM=YS8I*0spkB=cnl(qm`D*!{9|#5f4t|TbsViWGT%3DY7eM zWc2lvEeko&6*yiwsdkU%)O#f-A%|OFKEC73rIt~gnYpijyS4^ z5VEMb=zS7=?=TWIS)yiQZVo5-8+@j60Bv1eX4@63{fP{C1-XEr%z&4oQcQd;F7UIn zQ|YX$yL++GBPTaEu+u0x`Ghr2cS;w%QMAa>_-cp6+;+C+6X6TwAif7r+qsG|u<-2S z_&qsJT!UBTUH8-Z8YWr3`nc%r!td&kELAkW#GSI}&l7)njt>Lk7|rAFD!#N&G~I=% z)@^M+kTA#|T!tC#&rTpzCnm1?_taa?KEdfGP3Uftu_ zm|0C_L&{^*4_06@MOuAjAx|FiQ6b{7hq~OHv$GLIJls4y{hggIVA9LqUAnr|%-_wI zgWY=SRQZL2i&ITCm&@tQ(nH8@mNZ~BM_-&kr=5Lkhx4LYo;ubG)U~Xpi;W8ymcQwJ zfGp$Gs8dF}v$~SBA50>mW@8yTwg(iTNp)kIbW#dves zIo(Llh@Sz3%)NhfLvW;7z0s9TYtNjdmHDTbCTK6tdt+N$8VgJ}sx-c7>ANo$?b#JU zE6m>&pMK71Rh>L0LT%8&01_E>-?weFHTvS}w|W~Mttdbv{MypB`Sz2!Cmnp{rIOi< z=OEVV^^aRnP=*%1`0ydjU;pynY@3=~CG+Kld;pfP%m0z$`@LVw%0n?!T5x%V3#I0s z7Skinuiy+Pn|@FQvKF2)^wvt0`%x)uq_<-UXDtgv`~=y`|6&enZb@Jw$prF8Eks2T zn@VJC*;Zz&&3Q@7!9H3|D=6yn85Ych;dryh$BmaoDt&7w7OYyG+T}QnyvBk5=>_;I zo64?KRMQ$KEQTKXV+eNL=_W^L{VJVguv9W1q6n)^x{%P%sTwGdO^5$O#r~LUD+o)1wHh8>1J~37A0xqbrmRtq@86M19 zccg&Ju8O6lrMLHeliShl#a@B8RbL#bn_DU|8k2L2Q6)hXc*py5DY$#)AfV-`s(Ux z?K&GShuhxRH%djUtgLaxZuw;}a&mH8!pT4(#`_zUSo#w_9v%iU=lfT$ zOdf78q!StP#1aw`KyGr}qGo&B!s=HTKI7cX%-a`V*4IrC11hbj8A3(AUVl!Z<>${J zZ1%ZhkL^_{RJ8lW<8}!6OT1FTP&zBN7Sz%r1bF=(bvl?Q*;cwBMAX#Og!$^1l6lrb zx!D-pi;>1+AgxNqQj>5xkW*4pva+gpRLPRWlJL0ZBI2Tog6pWMnGCjw z;HkSFE;M-GIO*#zg9~}JI~kTDrs)ZyvkzS2)XE30G;cPY8a5D%JSN%5xwr^`6jsZx zhRMa~oF6v_>TKtiT775}`}_O)P|?teexb+9mFvU>|9L%c@>Y&lJM0&}j}|IYP1+;v z@9zWT%*;xZ(Gss>Kw7B1UAWk3?K3u&-BNRMbo9wiL1qLYTXBR|UKtDq1_p5a_~2!W z7ZVf1Y&MW|bGB8Go4dZg9vB?_hbuI)Y)T7jES-YNzkfpy>if_WO;t=4B)nQ-*pWb| z8519m02ozX&(&IklnIg7!5mxhE2>EKkkQ^Eo&hOH^fIvh50&LK0dyF2(SBbR&ugtwb|gccWY)Qnaf~vO^x$p zu_`+|J1^-GIDdB}S$UJ1>Kp*f)Z6Qi=gVr>T6J!ng&42Vc7;7vzgVF%( z$!;~Vg$uRLU%*^HMn*Zd|D_21JNcsaay)C?P~FN}5t*W;C>^6%P}c)6l3ekgmaI%-A!WM5Y)#$A+<1V@T(7TV1zgarfp2 zI61lLwHvz1-SmN;0f|h4_t~P#Ti{$%-*mLJv|!1W%NWx!A12B_esl{leDD&7hBkZN z7(FfNwh+x@-uyhWb&hkYS`)``JsKG+2p#|BQC;MA?R0cGVc_aIl{Ab9BRIc#$WI!9 zbcFyX*s9X#{3DHh>5x9PfgyvVsRGLnHvF^l7IxG-JSfDI<-8g@o}b6wl^o#p#(ogL z*2g@0x>>f>j+57IbU0jZ=pW88;%jLxkUNLI#B-ut{}r~)S-#0nn>e5@=Xy5<5okYJ zzCGL7Q^#JmUo0#m*z`Wbp}>b(u{0UjyZVX}TL9Y@a|8C>uiQvmulIVb9i>j)Qm3ZI z#Hdv4Cp_RyOw9m)2*kFtU~GKsa1lkn*{HUts0dQJQc{ke!S5(?vwy0(N61!XP|fwY z_U22>^Tcw!a$@hx{ZOYW1ZlOTloXI7()Ve3zdnF+K$MUk?{Q-^b2@45;oV9Q$zi*h z_R(B^uOb_HQo8oWTsXd}2E?@k1J9IMs-5KX3JPHT19EWKwzt<~qBF|8r*4g{95{)A zipokwj6PH51{pE*;?x$YUvV#jgqgez^sN-K6+o~HqTSJ*YkCw)iS(mGSJ%r7y(L2m z^LPBF*0k{cCW_D5lIWyrDofW*gjgnLizdL&4k$!1=5-a&e*7eMH$8^{3w$ShB&X!& zr{sRzm=7%EWFmd~%z8VsHf=k1c{cP!di_xEUGxI@Nz?l@TlIKZx>pA)G=0eTbhnt_ z-rhbCexB*KaB}nZX_w*NZ`n^>K|yek|Ncx7SlK-M!NDQ$ol%1!5k%;|x67_pQqt12 zfW+s>97J0igoj(qg(|~Zkr?!ef|&VwEAs&CVgWEj)0#Y#m>|ZFnVW(k;Fws zr*>VbR4_*{Nci{-Rtx}x_b+#;t5zJ>em)<(Lin0r^nh4=b=p~N2zM*e8Cn#E;Ih@5 zgOn4D-qwVe?YVwC=rG7rs>6Wf52E4~asB^bLUuuVuyJj^SQgwPv$a1dJOuw;Kvhdk zgsWUf6uyuv6$K8Kp>5*GlW4VgFxP8bfxA@Apd9;8^6KBq&Yv1Ap#ePaU$spG62(AP zkUdlA&ILtGMT<4^8dC1Ur_`QlPYwHiwNg+5;$G63P=JU_2%Nj&Vc}%Hk{bLRDs45E zyR#muziZo4`&EyFlmh+yLCD|Qh}mjjCh6*^@W=W2Im%iW_dFV}4oM<|o`#YVSTWqg z!oCz*#=W_m2>6&yK{8)khp%z5JNo+cg4T-AQL1mN1Ca;0w8_=#X?xXYk^mgdI*>&7 z9G#3g3S`O`1(a#W@=l73DhrzWLyg6YEX{_W5px#`xOA8WhDWthk^lcI*AHk#0a?G)*^Yg*S$JPu1k9N*QqU92|%-N5dzk3fh zwu|FR9N6rNe4k*2^pbSs@4hU}V<%wSLqi7jA?sds3PVMMYF65;HaCERV_tqftbc4Z z3)`o#FlkRMt@ypF2My1-e-@tt0^rz|6bHlEEWAP9iDdbyz$YXEY`=;a#C63fRCK9A zE)ylfob}~O(-Fdn=b*oOzR0epB!v^R-BxD@0D^B{zzQlEeKluc1SDi+a*H3FIBkHa z&8?5$b*?o6bON0EC8easss>>{0YFS_Y$~fID$q+Ay}46s$qRsGYA}tz_R0SfCK*4c zv%^y&4mC}pf+Zt3F=#)XnEGmU7U0jGtfHgieK}wmNi7Q_NdDsTe6QBBqj3?6D1CPJ zH|fTFG{*E-T-?EY>v*#asPnXl%WpXO-ku=Q6VcUF@6DCoO$`#cJ=E|(vkcT#S**t0 zftgm@5{Xxh9(RY;1@aH#kv7w1Ep8vlc8jz^Z3C^M9~qDuo4C19ZBxE;K_L-yZYx-9 z%>P3<#Zu=z(3X+a@;vlsPUYD#1)O+fsaVh{D6uGa8o6;4WCz^(@f*E>h?Sg=rCks)h=`}bl+pqul!}}mLFp7Ut&UAH;qr-YHHWVhE%f+el^!4b` zuV;%`fyD|N*dg&jwHNH%@3?o-fmg47`ayl>kB7xxHi{zesPZw;YSI2(U-&UXFDy|m z5$TUX!o%lx(KS*k*kd5^MDCBZ`d8P+zbPw!9fW>A92kD4=hNkFj{xfx-X^Oy*+vBb zV(3H$+tl}A!A+bmQU2HaR@c}7fEv_3B6}S&+WJ&NS<#KWV1lZL1{y^EG1X)$@@Myx zc0~(i9UYtPU~;NFIsxnL;0YGbFa99(CDr9Ay){dA8aT{!yJ5c?=ZY7rpBNKDy6(2GPcU6X&`?pSgA5kuy+2?k^Zqci)`>Y;R1=4wHT(GpR4rkd;FG|Aj+Z$c2>=`Rg_m;SJ zWpkaXeto6pqg~5+>9ydXs$Q|e#P^HYoB-$sh zIh-qc$O*ZMUz@zhW#2zIfP;f;X>P7bo^f~0AROPT^(UxqwVE0z(cZe0p$>Ql&2wfr z-`^$BVez zjENwoBVM+gY0xTYvFLR^(9`pBPatE^tc1VT?tvN&4GklN1ymA2{pNy*ofavR*H^_+ z(R#|xVxkZmbStoOt=v=>Xt8HGu62Q7fE_9VQXjrx2R4w*0wee?9@Zc99I&BBcE^HE z&29GQ2;B+4wyZA5BFyh*4J>wG>y*`XwYzA3nbxq@6`@z>t~j#v0PYiw=sbRp_m~3r z$Nh{@|69`WkMdyfEMY)v|I?uH9`s*KnzNmo48D9CQfBdl_=DQ?W3}zxJwjOPd=(81 zli~%9wzl>zOh^X;KolB>ls*CUjXjBq`>-? zU*SDXyp#toDOlA;sduHi8BqU)3xRhPGl>v{StC`dC14sA&lg8UMS&f62A@|Q$VFte zcAqcAf=C-!S>KmI=jP%n;xEa5W8HP_mXR{54-Y!CkiayszD#4=jGcx8mPQC5oH*c%Ex>Cw$3pWJ^Fc8(~bWf>IYZLkx z8a{6Qfq=%K+c?qU<+dT=!k5350rI6_+%41?>KY0Of%L4rz+;xTRCfm5yLazsXkh$* z{rdIwE2h9S$7y;&7uf8A`qkll)7A8f4;>yFc+DO&mM`sQn}xc=ZYEv879WpZ>)5>+ zB*E*gCas1u_$IEwr#xUCqf37WPFNl|TMG8p|!6e}H{3gJrbd|x%#I!S2N(3Cc z^Wxy(SmlB_)|f@Y?dc8jx4dk~&U^SzhNDzw2YvZ&a}NB@f3cM*GJG^ADTi)bSBX66iauNnD2DeBu*Z zp{8X!Mt;dv1IFu2xC^ObvU25@~pPfB%=S4#cljpx#e|+%!GEBnjJ~BU_I=sch!xIu1 zsA63?NiS5A9HFZsK4F`~q>eXL`DSm8dQVs$dnDVq;06s(Hj_QCumTHEiiyP^N zLT7k5w+#;j9v%H)+ymjW@jD$%V7!m>}C@d^&W3ykPUe3$Q`>nL};o+gW zs;V%wKqfgRJ)MMzC>1PPVD@_7oD$mDH$1PI8>tN2t@&2*Zmpc|+wThH&fYM!bK#U# zQZnB~uRRO`+`IY~kZ|cc@qA0`4X#gfbMyB0HfSC~K*Iw7mo|`(hI-#Hb8~|j-=!;@ z0Emi?cHA6DK5D%iT3ARM-eS}OdC}3F$+-b3=@Bc*M26y~s`ibEu)g+P&nmNUli0mK zTA|n$qhDVGHOH3O+)4tbK`?FO;~zn$?e6k`_O~gay-Xjv1X9>tU7M-)tql&Vu)t># zO3;*2X)F$IQs6(3b}Lem!p&3BTODss_&rm37Z^LHLA1sA@j*he@>Ai_Hcq;QWcsL6 zlnpvVCd@m|jj5?bnZxR>T75{auC4$U0RaujU_O@w31SdwrOP1!JK~dCIlfCcyM^Bz z^1`824y7zHvSnx@B5fCY(?!CS;GU;hYc=`ZaSibAHId8U^;m3l>Fn$TiE8b7I}&!A zmjH55yJ1H#y=GP5Z7w-pSh;RfzFqHJVpgfOi=tYRk|PlWT2&Dd?i#xXi;Z1jGs|pk zZA}`<;eNaV1BbXjR~tqM1sV9UF(o7kR zcadH10)TQ`lX`ky>Owc!QULtS{+a~ z@?KG6>)v3LU(v9J=Quz1I{jWOI2E`_Cwy;6} zuaYC^Iy#d=@n5a@yFV=P%ot$?hT~fWl6b1JcP`yY_O~oW&enm|k zOlbcqRmT`Sn4(qiF0>*{eTH}_pMH6#6lQlIm1B5(T-hZ&+p3e$5Z|&a!-iK^J%WKx zbB7wN@E9O;?unv!9xC$c`Rv?W1Tj~PXP*RnL)Wu2A#%`~?03GU44Xfc#)rh8(LU+o zvnrL&=LK#H16#jb*%$63b1+jG2$g%I${m~ z?bd8o-*1V>gMFf}PbzayRf!(m`{Y+AKBFEoJSu0Q9ta8-BeRnLfh@EQ#WheJhSam^kT4H$47P= zF#(+1+)fQTU}x`h_f4}pEj@kK<`R5zu12eN#h^5W&+y+(gK%VfX!QrXl)^NaQ&|lHo?c~GQom;Mug3Cf3nm{wo`L&dnMS42 zS{DR#s)2}3Jks~tvs1xK+V)3Z`)zQ1Pzf3>ntEV-5VmkF+~04FAbf0rlVl*&W`Vza z`6>0=L>ra=F$fO?|AYs{ze@|KK{>Cj=w{sf6Vaboge>3;rM@p{?#2X|932Al9t@n`e*>REs}5# zdCeDq5TZ7HAc#y5sD>StuCDRtbNGL=FXc~iz$SRdHrjX^y=`aKG+Uhc&^x;SJ`--IT=Pf zkFP_#ZE#P$K%PLrSE((Gqw*>fYPp#IP?-GyT4BI%4uEU={k;HdYewjc41u2wu90VI zTsP7@!UTwzQhu1EBqe#X*&CL(KDgeSe{AUex-4?JJ+ph2wTNi>N|-H{F`hy9eF3E2 z3ls*zwgwsK?x!PNi_U`nB5hqf?X`z|#&b?(D7uKm8|h8Ic&g|HkLzFMZ^}Ur{<5u>wqpu7`X1Z&X)04*ANu9mcTbG3 zexwZ>q@GX!_Bzm9m6Hyngn_5s>19kmZi9-}`H(1A?wWI(*@5`mEdi&u zY|K2gXQ!PN8zs}H*WN6o_HzSVTSFOToJW7Wrj?PEwMF=&yjAa|(`KMAkzTq0PBEIA z+7u9#9BsGztC%<${B3BNN4#*;aXBm&Zns$10y78BKq}h2;lMeumdKcaPBel9N`EtUQ~*lTH`HJ9Gq(YMeb`>N5Z?CmBPZFFXPTf zvwOW9bm$y!v~^u35N1GRd#ALev5E=M?;KiYKA{uCTzrkh8p~=u z>OhPDzhPo?z8LZY=jU-E9JVlG;D5i@?x!MsjuNE*lZF!<+k2D4F+ASGoeJ6(PuBv( zS_nYb5JdQr+0AnITAd_7AC*!V``TDx<7BzMp6p^zaD*8ad7mzj@a5#%@*mxU1HA=Z zYa4=rgso29&|PyF(4y4=|anDkeytilJz2$byB2V-ELg zwZBKwNBaGKW3}k?x*IG6Y#MXKPsP))j}30*!k<||KT=yYc%z_Lf0P8+q8n_Rkq16A e0Gs?LB#z~ceJ%$B3h+BW07+3fkuqU}KmI@Ont`7H literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/bnd_box_preview.png b/doc/salome/gui/SMESH/images/bnd_box_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..902101cf35a053647287e8906d6a5d67fc169964 GIT binary patch literal 56083 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�MNla{1Y|k9qTx$1T7x+UKTDlGaag9r1EvuX_HTS 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 hMcK3dA4ostg);a@43H_qEF1(}x|#+Wh~S;q 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/cartesian3D_sphere.png b/doc/salome/gui/SMESH/images/cartesian3D_sphere.png new file mode 100644 index 0000000000000000000000000000000000000000..45bffdabeb856605446234ae1626c6c56ca658e2 GIT binary patch literal 12475 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 a2OkkWYF=q2VEo^|r5$XXt!u4<(*6%|`EF+b literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/colors_size.png b/doc/salome/gui/SMESH/images/colors_size.png new file mode 100755 index 0000000000000000000000000000000000000000..15060edfef697143200dba9de8c262936adb2ebb GIT binary patch literal 20715 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/controls_popup.png b/doc/salome/gui/SMESH/images/controls_popup.png new file mode 100755 index 0000000000000000000000000000000000000000..c32f9eada9a7e9cdf1b919c1994cb27529f93997 GIT binary patch literal 3441 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/convert.png b/doc/salome/gui/SMESH/images/convert.png index 4a71cac99697cf6c4437a115396bf008b6b69906..2ebbedc3a08524912321224a89967f2501b3859f 100644 GIT binary patch literal 13963 zcmc(GWmuGL-{&B=fPtWNi=;|OODjl8sdOXVBi*2aAT8YtCEeYj(%s!KL)TD44103l z@4LJ29?!A+?0(rV9&>ojbzSG2@&DDIfOiT~xOd6#LLd-a8R<74AP|gZ@Oc&o3;etI zS1~69@)#oX=C!g*($1W#ha3XkaxlewK zFkR?v@TEh0qCU65U%`^Nq_{P9n5=glz4a7rcGw7AZm02Wz6&lQwK0BwF|NV=ZK75s z8#nM&TxFE-JvDA)nk9jmGWM+Frz_&ih&$NqEg`@1Li_8&TlWpgDa&ndIU2LUAv%Z{xKCYcdW9_qwql)iqZ23Uh^Pu#r79&^InsHag#Cy#bhlSA}EmHy% zER9zun0GuY{5&ew5A1lBH!S|BITIqirvg0Y=G378dTV}H>!ztFywUK0PPL>91f_h4Cs)>_T}tpoY1oF~(_27i$# zWQs2k4`yMQaqqvN=h;gOV0HZaxXV zPRST;>J^vmm9z*aRB8%^&C~H2osPyMPT4J=A=-U$^2<*$tP8(ypI5wFFsrc`tK)-x zIK_Nl-X~wh!M?mJd2Js##`#L#C8l_140n@1^G_#xpK0p(pVU;Bwogxtg)f|KXmh$0 zxvgzhE!ufbyxoJ#C$0|MJsurvp033B3u5uH+tiJ>Gs9@canO~c1oKO>RSUGjBdWq0 z*T%2}oD?s@6bsyZZ)F6<7pcwMV(4a?Cq#%WGsOWfJC~{IT-QSEwi!ukN^0@!J3Z0U z9Jgg{H)&xR(RoVAif_%H`JpVqIZW*;qO&rLD}wm}>;BwZ9Ns7{?m{IyGIwhXo;!Qz z67uWcupE8MQKQMIpKXRvird&N=Y|#^Xol~mSm%!)R71xyFD<0ZHnJICTl!V{E!cnD z@gAc;u+nm@TkDjA9yIKqENE3{>oj6mdVN^Y^k@kx^<;0cKDR^*B<_IYR>7y~Vw zE4+HkUS1*c;#QzU5}j6lmmR8lli}1YWs?G@7B3B6KH={?(*gCuz2}xYz)G-?s3%y} zbT4PG>h16sExPp=$@zu4v^&+s2Qs?-dvEW})?ZRxOHvKhjHTnhXtq_g5q(oh@=o^3 z{b_XtT20D6#PWCd-X05bk1&qlfsMd%L~3hJ=p!~nF0%Q8D#fOd@k@{Er`5X^NPi`= zO|vp_h?Q_*%tVWn6q9givjvev5dvJtUIulPhZ1AkLjCtOhZbdZAFH9z1G zzT>mg1N+!*Xz{DF@#lEBnv1Dtq2!hORZLxfYmjqy&(qJD3!`3LPiu6%ta&ivAI*%9 z?7PLLuaWFwYZ=FkD+a#yjEquB-SuJh2s&vl6d~g2P-GeiR~l@XMN;v6WD2(N>c4dB zA_wbu)yA@!wF%73ojbl!4vq;588K;TM#3=%FY7{BCOAmP9FP12M+kic z(#yxQ+^ByPx3FF%qb6)7K3ZfvRT%L=F3b$fVCTd>-g3>6>6*J;1kXiof{)J@(mj-B zV`fTRL?@q(XwrN>&+6nZOv$dpwYy9C5<7srLl>s{2G)GpQv0M>ichp-dLcouqUheN zYpgms?*3!Z!7?<@3FR5Np})FAN|SMzvNDAla#Z$HGDYi8m~8(-HulN9%?p0n-`=E5rDo1ES)Af3pn zdxj{+C>-Us+-v7^Gg6ouMs*vFTm_UG3nnvir%yFQG@I%bt}gqI_~mp?!Qw-G!u)qQ z3y)tPnDgJZ=sgzNYxszNis#;u-H1AAnA}c6q5KhviF#lX_(g;kW_QmuYmlKs)4z#? z`-1&93TEUQA>Ix5W4nY%!y=9;*m%byrH;MKcuEfb^5yeEYz82ZYQ zyfX9zLqh6xThG9AU#v>2>f$p{$3M*ACX9W9aRs&)r5VkztmJ;T&K$|XHaMtb**X(e z+acjg7I?NhlWs;vVn0gZ;6TokcI39$?}3-B>ExE8smQ8!j76&C36`rTJ{(WIV4=Yb z)i$GF+&g?OWB6X79ryS{iShM?dv2_E{`uLDjyd7&H6nN9jxYu#$b88TlN}x5QVB0t6@It&gB?|?NNKW^&oNnjHZzBoaD5@OkRZ@tEa;o z`A4?ln+yZ1$W2^kfx`32gL^_VSZKm5uXfWqTghydYh#UNjkrMx`Oe#cZU{K0i1Q{_ z^n%E%WSE^w<9(}V%`P6HQRbDfKm%J~|4yZ=Ukb5%gM>^GmVTTFkU$J)$w3 zh5xM~%9n2Ar0WQuuxe~R`|>;iMsU}1%Kp&_gCHNQ;>c=M<*3O=nsY~noB77CG zx;!76KYCtdtXD2@)q=*1Cill*Mh~$TXvI(5T-Mgk8(W{%i?q#CsL3A6Xljx(DJHWxzsk+cJqq3RR5OkmYxTj77xfmIurJCO zv)~}TFPF?4{RI9_qw7TSLt1l13-$LVc`9&g#INV*Bq);JfNZ;uEXyZP)P-lbYWuD7; zu^v<*#XAAbWME(bkrsx%H8P@azC4!=&tu8XPEq`6kLVXVU(YbB)I17IA;zoJzw|wD z*qxn98x%cXryFl-CiZE4_4+jiwBZ2D$NxUoV?MrEbERzCqxJXqu`oF}IC|YIV?6wx zz9D*c+F9(hGo2*O-x@-}w|{uJe0H$1RyV2=Pbl$}WB4gE^Ws{puF3CazQHC>cZi*x zU1x7^K_LF~qOa9BF?<@uI|6B_Z{OQ!zy*}Z!&59`DA*o^isiaR>_z2mk-J>MW~ zS_ugW^|{HZ*k<134<*nDBtP&m(4Wm;nJoE;nUu;uHbBM|9fU85fgjYV{Un?xY=a1 zCqup7iS^^hkGw0oI8AS`-SNx0YZpR@g$0@GhyZY;rv#v|HfrRC)4!7UO&Bo7}x{KykJ zI;w0U2W#DHz79=ic6hMUI|FT&FVw8;rAs5x_4smiaWayvNW-X*pl)bn2q0h252>t1vqxy(Wru48f#OipB%-;a;}a4R zG8;_&;+p(ZHnz-hOZD>V3d3bJTq=&olDH?9ok>7|9DIwL9Cvq@ArfX@S805Hyw#xv zZEO?VYY;EiZM14{bU82pdrzX#?YMVDNLU!-1?hbcGzyXQ()O0Kv-9lSoa@!`1gb|5 z{mb_b!9PocC=x+Z2skj?dWRbB|7hKwD);BvD|{Mp+1@A+D%x;@P3?VY3lX_GQp%D` z`PBjQkTWr13?gP-o;)xFyNTqTVi`X_|LW>0J|$)EsJYTqgE<#+Aw_TVj>mRf>HQF@ zZm=8P5q`^6%Ho9X3K6qu;^bu1q^3Te_dv?$Z%q{I!QN*`4wXwO3OMe*NgTI!5!$q^ zbKct>6!qt6RGoEb(; zb8qq!|+zx#!dFE4GphYuj+7S^~JUU_?aONLR)gVo)bu6#~QOMA4pu#gm7q+Khf zs;U|w8#!0+YBbl{BCEyteSkO>+O|u+MVr^WpDfg zO2PV22KGG?wm%&m{<5)bAGyiRrz>(@lfg{%rwCh?Yt#N|bX4mqHtvZrGBPT$p5YY} z@FR3ZBqb;JfqL21-EYwP^)`f*5Hm0xvbeG$F5Q8DvqbyfKh^m?kenw zG-9md4}SrtBmdac#3Ujj;@7yf7RE6=q|~s3({5E7$yroXbf1j#kBK0>Ke2}fy^sCC z)BQW0ROkzIVK0oqzX$ef!d$Hu6WD?fO>L>gv>e1#cvj%>zk|$NTALYUr=21+O&w z3n}yY2L8ST-rpS^*}rysgM)%rjwkdG7kkZ!&TzV$mk(t~5s;Ah51BO}_*y2PFX+Gc z0fQ;-Ojn|I>-U&hSd6AC%!A2!0>PZlk4*aFuQ^Yc=6vzNI&2T-`yLj?&N*ksojztE zBQ0HW{y;5%tjxHFIFyqASC}Ze^Xu(P3QzYhpSHC)}?1RM-s zztV^JVBs1JroR4j6!h_EeMk<*G3S99Ec5#Hmyqr~p{wok0rg4?BFKR7Ddo`6P)&V( z;>yT|1A}hjv_||)|NPIch5oB{`D)1BhJ=HyHcEwE&Cru=Rjebc-{N?}zCl6vz>dmL zEz}e`8WiWYnI&acWKT4XQPJ0bTv}Rs^XqBF{qQL$WVN*^F)%RB&d&B1uF(^huBbj_DNZ3WIt7U57V;v01-=Hg@nvN=i^$QdgH+Mn(qyo~R|9 zPReXru2F6*^)=>^o3Lqr5{7;coK<3cV(WN&`loK*Y?HNMnD7ZHFIwW zbT;TSTBI~%;%Xco8{09V=dF_oeLy49>w_m^<|XWT?!c&!)LCh{3H3`6bPjbz!a1y` zE9|WCA~Q4T;r+>4n`?Fig{nP+Y2s52u3V{2wo?E$!4Zd}6h*JNgGku1M!ha)7SQeA zAO6&%#TT1(cNRXh98AS1ZahgCb3Mpki5E0w3x611Q&*eLK8oxu9~i8Yw=B|Y?q64v zv|nk*tq@>k6CfdG)m4U{tF;vyv>%ZbXjMOG)KxSyV=hPZI=Oge40TRdTE_F+Gg9wT z&(=8x`BS@nU+am*+!RDj*_&{AOnxHgGnX@ZV-$Y8HJbZ7&BtH8!KD=C7aRDFZ05ms z*d?{gB8E2dT6{X%!J(33d1#e`4R-OBQ9faiBa^PuVoa=fB7^y^uC6YTTSxJb*=pnI z{E9ak;k|!!lvz^pLyFqf*Kun+J~m#+l~Xb!e0Q!su^GF@sOu5Z^k=Hq_;jWW0dmXj zc=KQ_R*yJ$3!sg|`XC0Nj713KfM~YmM3K{qc7qEC0i&FLe-i)d+S*W+wHBZPmMP5+ z8y|E%k5x3QZ9-c0^;kD?;%iD+ zC6ll)m8fX*qlL8k1#M8?MLf@=;^r$Y)zB!#Ej`PJV#-iJ*OTZnIM~_Q0sHjHuP34L zsv=NSR3zuKk-IuSwsmxD+>>%ZqtTZ~BZ?|ie)#FPWDf1^LcM4P1_r`X=IWPqdLjYX zBlmyfAs2n{y1g~=#YUy=;C6PQq02(xtFHO z7V&uBl3*2(JT~B=1`$@i`2BC2HXr|c>PG4xQDm>SfD|mb{yb+~aCJcrgi89eXh9T@ z#bl0L&$ZXa>7gpajfCNx9&D;@6v*OZitB@qr z+NH$Ae~iri&?6%+|MUHC$zU4Mrp}|z@6um2Z$~q61G&(*rK?ci`=eF7TSY;oBv>a( zPeI|qb97UB-f!?0L+9gom}CZrXoY6s7~%wt1mvcYKiuvs1)mM%)8&~hP&9jtEajhD zAiU6A>Smp%&ju_0TNbc?P(X=lRLUiGCf3)ujQQ zxS>C>#tjFxU1@)K>_P|*bg+jPy}7wL9JSziN(Fc?K!GKnN}9NAt!3>Ei4*<-of5snu3Li+I0czo! zGv?~*>SXAgIoA`*%EZFbD|9{QyuixSK5S4z;%&R&b#X(qkHR(vE>XxJD<$^>0djvj zPyPI&T&3)Y#6(#K2X25kNuZ*os=j7XFZqn2cwc`lF#2+Z=34)7wF`cDh(wxCR8aw; z^|ML`$M(8rYt@wfV2T2Tai2;c3c#ho3)0DQQ@lp^Q#MX-AQwnQJYKptO^bi9dxVLN zYt+5^6po5ylsAC)#&1v7X3Soz2r)7F^IFgRD53kJQEqb3ouh_hLnHSq^e>bCBpGe( zq&`Ox2K>9g29wsYD zft~UC^=qq{rjUS0#=}|rf$%ffo3gSp$Yg~r0l+bCP~}If?$PejI6GEn(*+EKuTGR1 z%K{ZNS!GQ@OC=si|7CmI>H)Qow5k*FJz^F}ODbt^Y|-$@NbL`|FL80C5UdB3oq&nx z!K(nWk5ohTAYW&&4PTJ<77O}1EP{pMo!FVnu6ETJS*Ha*Cn}1t%4X(n&NTsad$Kf` zl7HA{`(%3OUy(BggK`p|!m}W*Hp^)?#rrN_c`V0Y5*BCJz8uCr*(_@x8;hnD-}OLi zWh+o&;ZpzUxIT|NAZSO@6LXhAQ79@+kJ-Ok2s^0@q&QmS`8tC;?vOR8PyG$ zy~!Vo`0?X&VqzlZYYkYz-=Z2mar^dd33xrAD28J<6on$B{X>3*-1tdMWvwua$&_Wa zYCd3_td#tAzBJyK;kWM*<4?Z$5&c{(M%U{pM-&5kII0%*HsE)-2(zM2N=ga?TWc7Z zu%Bs)tKW}MTEIkw-Htxxu+YBgQ%j2#E^Kt-MhPE}D#@L#{PnBOpD2EJYfBd3o$P{! zg@W$4Z{IR06){>soy+P=D6@<`k6${mo+*r3_^*6n&B9(|G+!-@*Lu2S_qQX2^gh6YgIhlynOHAws zEP6gYA5|`&Jt80o2>wprVEU5u<4MvK993mdJ+=#E4icQ5b)u=DoE&C&e|%4RTTnNV zxJE9xdv5@V4h9=wTniXwgtUKz;Tt=$PBtRu8&hi+y#q&)#-a2gQta-52}@ucy)aQ& zA|LcawStanK|#D8BLFqivyvrnOpslEig^N*v~L)mPjup~BKiP6jIsd6gehYJa$5^F zRIKFR`RzY0+JE2--T%gXTRTtD``zLP<^iO@@7ZF9*`i&O?Yz4hg`$Buhfnl0M+N`J zQ$o7gO)33fS$O}(%m0=0_ix|t-7T_KK={|{uGMdteeZFd8qmUGKH^+`_gv_AgI%4T4$Y*Y^8^$z}+q7t^h zWkVpuk~h5ovkz&mw+w;iXq(0xFEbsWjyPrHG9RV~TGlR%&N(TaS-ajTgy{KOMVk}2 znsZ?KWRhNkC&m{HtNAjNu0OzgCl$>@u8?yU0zE+8&4D@!r>}9|)2+{FW{{03F*|s2 z3lp<%M8MxEON;L~!ra2*8C?q1Yoj;x!tO^#0Ga?bj7QwAylc?4%m5%YP1cMAkU0$X zDl0OstbrG?8tUrOpX&AdlLfI~NlId^w1?1b9xy+ldqRe~lm<#BFsfd^^%j;8$LHwh zs+^9S%69hl0bs%e1nSLE2b2tq$wqfRR;{W`K;R(3?v9kd{qal()ZC@tDi|!Levyk8`YB!eKM=0$f5AUB zdId}5PMhv*rc7iUkPU)PyDf8bbMUJ2qZ=#)2n`@tm*Sed+@#Mgr+3-9LM5j0B#eqH zrbwqgqJb}7*!|>%aW^5LbezhYxa?Jrv^Ch=rz7IyfvtQ$V0L^EqjK76|#SuaVO}BBDc9bN=`7{9QIn5e*0@Qn(#m*GaeSI%8H5I zvazvI-2w&iO@85ItHZ&hHM<8QS2e#)|3#c02_az|46(Mx0eldvxvJlwS{KX+0n z4>RXD(~Ky3lw$xbC5U#KD%VoyJK0;%GdhZe!StRS0C+n9nSfOck88KljVrlU(E~U4 z$D@IR>iIA6Z0B8)=lRjnXs!~AMw#zxA1qM(G5%*sp8Jm^|2bL)T*J) z(hGoe6&gx_3*T-52-=4v=QjJSTa4(9=Ym*GHM9xsjTdSGL-o}w2n67IskpJGVLpr2 z>$CTZT z38_xh90;^JIF0~~9!SOsca$bR1Vv7=0_GWqd%u+*9z{Rc9zg{wTU$L~ zcdw6R<6)+dzFW34H2-_-?mJ;&VWtaw7wc0QOn?NlT+DDLI^LmuBk{jPK9ridAN9rA ze(F3^`<5lh4}4iIgFo1lRL23sKeMui1?%_#ZWL_3183mFUzK<5&XN%S9rdEG@3CjC z5>r##IMxB&u`O1ag1Wyg%Xg7aH3 z>Ij?=6~X{85L2a%u7Ri{F)^{h4F`A!`sxBh<|}~O-Nv_#+phZ87~slVdJz&55^z`~ zfPN?yH>7%IInpijeV}Bb*+HDP?0}T=9zxgU~6AoJaY4Ax&aav9n zJO==iG#g59YrH@AF);np&NPo|fqJX2s6t6y?^wPX3IH3wC!8CAqXpS`)&l!>W%1d` zJ)Hac$e6xpq7?jP5mj?K(UF`1)n#C@ZO_$D?5;z*A{baS8#Bh5Z^1L|g7u-{4&0yKtpc#R&rRhk&eQU6!M=~s$C7PBjj)Gq|J$2Ob`oBac zLa3g(G0}n-qCffhqu;<-bvbAw3BU<^3>Iyw+%$Gtf!4P~8PJFw1`#Pq?~RQO;295s zYV{$OOwfd-k$O)8XgZlL8B$zHcwe$<3M?{G{L`lw05cZ*_!f-NGe``V^NsAOI>+ZI z6biCE-B8KB{TQw3TyEjk=U2e_B=zE7E~P|?ruQK*4Fl? zO}FCwQ zE|5)hG~j;2|CMsHzpDcV9>&MVXJ%%WIL@frCmVKz8n1SS!;L}Au`!p;2cZ%Z_>qus z_Z9F~lLZ_NE>3m;c`jRu#1#VY00DH{et+?GSy{Pyi9QAdtL>c;H)3hW=v9mxgQf#e=q5=z@r9k$M-< z*dTJr%+3~6&Z@Ll2E#WpHnv^ue8_7(Zxj7FFI%LcMhutCyD-xAl0G zPQ4RGiRX+HvH|1`sS{iL_VD`w_H|B7Z# z1xuh~RBfYq|HH#r0jW^Rt{gSVgXJLhM?^#t#htn!Qltc#4n(!B_zCcyfd}==-OuNt z$$;&)367eYnt`FAMzdEVDk59va?^!Pw?P_k#`o{vzmbxNY~ZUME&CsWOn2EYwyY-c zWU?H%oXgA0>og+^ml{k!eNzBy*954d`;;$BIt7}Vn&c9=1HjC@SPZ~mVB?EX&El!* zE~~y;URjws?Gtf3S(gD-xd&ZU(2)2VXUT{gJ2Zs{>m0SXveVi(%i^zUK|1+E);zXb zC2d2YAJ{fAqz4#IQl886H-?(C1IV-mG{(|^1QG{wfZwD4@IPA}w!5!WU{7nm`TJ`n zgMeRb)>7qUCM9A!50)jL*qx(@vqZSsEC6o&*R2l!hT!=>Bi6s&Sn*%qX8Uw6@O9zW zXPggi+{fXl=O~@G^?!hs_*{Va=6^o?XE(_IV;9FRdjsX9cPi&H{@N&L(FiafHXTbm z(&(e}R7I+9amhZoYM26SUf@~u~3y0Y}E z^h0Ubn9zS*VCdQtG2SenZ**6)rN6Z(dW{O3GhTg}@0vMa!6xH%`m{%6rq*E*JIGsU1V-E8;<3 zLpcBJP4UD?g}D;EH`ymKQ7gJ#SKE;iH3z+r)fgQO*OQ-G$baj&%~j%M6}9G)E7PIs zI;V1Y0bqJ#vXl-Rk8%|_FTiG*Q@M%%Ogv8$8qfJW%Dxx6-I8bAmSB-hf6DL}Nyxn#UqnU>J4ZzLvJUn0uCKJTEhIrp3 z%r}t%giX7qh2RRN4|*B47n*6n*+kcF!nmy_2jN0W!hz`}ZpRi1Fp<7r-|k^Jjt*wH z&F(_wn%XKm{ySY~uv72|WHL6v|eFwd=natdTJ!W>MD{LA?Y`NX?rS&B|2? zahDduXkC>&Em*udHN{W)^2#07*hENW2Gv&2(e<=gk`c9gqYlpW?Rplu+X1 zud_S%UG2WRy2;7i2dGh^nGH^qru(yt_j4z)-5!K{VP%x2D#tmY7w#%2dh+Y#%U6-H zu~OIQtG=*UJ-1rv-m0ob9;^$h)Mh7gUJS)ke-{H zP~trxyh7-+6=t_@fP!&?pGnwg;GB{x0^(HA;WN44h1DKRb`w?}v{OLULvD{QOldW` z@$lKqf~K`Rrq&tby83#8htyxv)yzh+a7oy-UkyD69O~23uWy2-M;kp-TOwte{^kkk zYM{T00ipFLB|5sTE0PIBg+8v0B)TqNKx&Y-PSjxON&p%kG6N#l=Nkp71f4R){T~>a z7##w0KO98&%Mw9-YvlL+KFsV$!=fGM+|%lI*rWTxU*^dcyhEOx=6%pvQ6~JRS!q$~ zI%0yX0KOxqQ71lO<1Y|oSlPJup#RD?p6TVfJP0~Ic|NZ4zF~I+vFa*w?8CB6ApR^K)$OEh#v^A z8c^^=5HX9ORtW$Pu{!x89Mco)I2Wi27CLWHnsSUmHOCdKa&D2W<`zMZxm)8ryo$Zh zdncL6G6Dogvmk>2DK+4aW%dY#n-&SM8$ol=DiHIV_1F@$!0(rMKUrxRi=sD$rfhtE zPx!4pgkmIJ;@-T=3T}!kJpO}!4d{sj^-QVR%ac%I2?Ubt5sNH66&^a(HaZ|~)21ix zF%A-K52fm9a79YX%5o`Q9CXm2PG+D%Q8y+#nDI8iCxX+Nfy_Q!@~3%3O%2+JKoiA} zm>9`-@9+WPLEw3xVFC@G1t!zi*LNTEAAC?({|>%|Ktx7Fe1NAlR#MgEQIA=B=Wa_K zpg$eG%-@u9oLR^(yS8`>i>Gc23t1l7ws!9^wx;qKJ6`&37joHjwtNrl(LlLkAdf+w zWeCDg&}o1Ph5j94*Y7n2<>x<_jbY)yHnG$_IQM%%)eQ>!+fmb8FpOYo;RL|W2}nt$ zfw~Raj*BrSz7}-e)drc(?*typ_1W6;u@lYq<=x$W(Bm_>b-d{hx=*-_yQ3bhp^uBb zjnS7VkMrE~DOEs-Z`y(06COwWt}q{|aYZtM{zRY_=iRng!GQ)xfkn3=4m2bzZf*wZ z7MphdpcT5@{~hWO9dArQIY+h|y;;?)d|S#OSK&acZ~6v3wzvo^J0uUx!BJi-s|syR zkh5Q0*Dy?l;fc&s&-V5`D{Z;SZ%##~vGkr(=bTr=h= zmg$RwFoHt#%!3}0#Iue7mg|ed?gxc(yQf!eWq6buT<;rSy2GN6Nedpl-QrS&>Xq3e zWn>4M_(Y++W`mC*VuJoLan|vd05_7rie!^p#Cjfxo~sZ^dz*l>?l5?LtoxrF=CcXmjq0&8e zj`JpuuL~QG7Q`}DOOAjwUSYglU;^( literal 11849 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/copy_mesh_icon.png b/doc/salome/gui/SMESH/images/copy_mesh_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..263479f3bf79e868ee99d860c0c3b13ffb9b3d55 GIT binary patch literal 1117 zcmV-j1fu(iP)z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ<(@8`@RCwB)lUr<5RTPH5b7tyfCZ{tgB%Kzfz+kJ` zCMd1en)pI!gMuY32oDl47)4P)kpwS^3Ni5-13{%CUYdBp*ceQOM3NGV_`;HPHLIX%$eEe?7e(|Hbo#3SF&DK*8cXt{{|=j5h&glS z$g+(;V%s*BWmQ|2<=m|IKY{b+&66cBe>1)`7pVAOzy%8y$inqsA_C)vs|IeW(7KTi z0Bvn;(z<3lJKtW4>$=yZ)*7W0N-12|rC2OlqvwJ*g0;7|OY7S0c)pO!<#_bfAMmu` z`GPiA(l>(E!u!jUqpDLt_PUWQC@|LHWYS$OuKxpnQX;4W2J!aHWdZ`Faho zxw%>9uh~J-6AGT8;2AQR49b@=kn#;md8k4T$2m_8Fr>pUFmdsiB(=+E>CYgj0pHf5P|oWC0X~y zNsQ5G-$VO~(}#cJj)UK^DUslVREp0oT>@ZJRTYaJhZSefaxkB7v25EGV+;VLrKLn7 z5k!Q$Tb|@x|F7hRQ%srJ!tIT19P9a5AgTsmb1o~(G=7lGr2LSgrx!Qia=a5zyP4zo9#GYpZl{WN?TKfnBYDE2m~U2uB!MF1j0ZA zKSO+6Amuk^_$mm*3VN<6|H?aKd)mi8DNn`JMnW3c&~DVbQFv*aA~K zuU!i3H;Q1L%(at~79%Hh-%E`Wrb{C{M;sd)z-!P_U1vV;;b;{?E-rYm94q5f)o0J1 zktF@dUOnwzqPNcamFXiqwmMDgk+pg0zj{8fl)QU-KA;$!(AA}kqo}C(1Zq_4Kt9}! zdn|8Zk$15Dy*0wJFBA>cQtyYu;V(19Rd#=9K(NL@-l3k$w6{zLa3Y_dpT}QQkduEE z6jjj=+@HnpSDC1?ieY^sCid7X@bWDeNcwuZIsvGj6fW=F#_<|T#`qrlk)nJ=iK;V? zvcw18@#%^8gCye*D;%`qi{4&Tv&>*1KMg^M5+u4j$)t_ES5x6|3e_a+`spk|3LJWx zWG3rmYUOsEFl>BG5dM2sg`{mVD=F;u2&kW!a^^$D$+yE_9h~BQH-fC)sc`e!gXhV= zc}XFGR+7S>ztn!x8O?1irJnRn!PaYh-d$qev++SRoBBh_s;Vk;;c+Nc=KE>GHJ08` z-bfOq={{x@L{u#f7mMg&9yv0P1fMKN4mz4NMji9G@~p!Z)TWg^9oG)k%wR|KS_p%_ zhHFx3xF}E%J4>2KOwW?m@gm<;$|Z5s8}lEXo??Lr2niB1VakP;k$=DD9M)jhF*a;< zNYipaLZ^4y=H@__N}rr3?Ba`l*a_c~dHNVXbk%Y96Xa1&fl>Fj?Wv40)((v4?(B~UdwMCfg{`PkGceWDQzIW-f_!~n z??K&2NopEcpT2g{RJbP@uGPlI_>;T)=p+A0M+C*xP2u5Q>tIvJ?M->)?~$2Ult3fn zm`M=(=Tjzy&XV()0OBNk{R%S?wv=?(Gb=;ptn2mZ`jc9gPUC?et4oVKb;j_xpozG~ z-9Rco&z3mp82ap{mDk3eowM`DSSs3*ZdtQJ`vpPLhQE~Sr-g6!if`M9y>1x)64+pZ z24bgOKmNMtas?;LbCiFxwD&?-3W2qLmQ?XtrW4aK({~k&MEe{LsCamY#wxQ@DTN@o zf?4H$(Q3bX6&o6gxi#HLYn^;g=i5?l!+6w8AYO#Z@_Q@N?5C7f4nq>03sqqMgJCx; zZ(fJyQwM6UP>}pSi>eH_Ryh8n*bc7zdYNZFL(pHPML%o)u+gcdcGxkr+YZVQ)xzXw zhjau#=Fj&;NlUnDAroQk@ct0ndzjAGkL>o-Wm}@k0|Pnv8?FxPUDr*>h?TDv!ka7L z;Ij#=(}nXf66k)o6(~3e=F30>d=3*)+QZdUIjUgNlgytDw6YP%bzG2o<;T4bMyq*6 zY@`qK-i4`L?jNq6B7%NLlm}ST6D7f0VK=f%nmnCOW|Ilic)sspV()l$HdpL7_Jhh> z64Ld}qAzf=JHC#AL${_dGipfY_syE3bWHjs>Yxv9uG8ZdH)LUIB#x~u7{uShol(Lw z*A;|=4$`1BqpsLL(pQ(_4EqyKtsCI9UCHD9+vcIrLpwr2PI1Ei?u$xCWcLiq8c*u4 zpM5+7@}}|RnrX32ehvG@#Ft&*$nqdJdXDEm=aGCpnR?M?t!F|z*HaxIH!kx$q+C#a zzEsPrInDNTvVNZ8kJS#G7f0N*jo*jG??A7PPFZrRSJi)wiIi9HJ4k6%eq1me(_SSy z@|Egslo__LKH-ewCn6z2 zR8>)k*bMx<+RP&KnJQ;4Q1DZcIygLpKsXS2-QhbrwePL;_4V_BV$p~k=<@E~&EeSP z_HD0}81$0m_8vPsyRE$w@@V~USSoQ_dS0|v_Xr5YuBSyuK$e3~fZf?@#L)E7W0A7ppMv!;(1JvxL~P2hIQ(yJQWV(>?H-W|CvRG#FaRx&ECl&9G^Z$ zpp`jOrEc+b@QUVzU1WtzBdFIrlq2e_*QpMAUEYEhCLL^cv%qca7DX=`xWd8gePB{@ zt;cR>XJ`ItgPuRL?keVNAL$jr`&Zw`gVc2!wo+GHM+4 z=Q9uVdn4xPZE0%@Uhzo8p}>uw0bcx@zxlVwg-K}BMF@5=m;tnR`F-roh=~9usKhhP zYkHcr6$3(55!B7%a(gP%wZ0Q@wk?p|hH%5(TQzyOq*Z6$??HU_%Oqi;(`K}Tt5;&T z3D=$YvrD^`*9Dk#`};>fM{vt=cpajwoQt5K$FZIE#!AGq`TE7xDsr|xQ`|V-B9p}b zhC$1GOlotlhqNVbB+WE4`|l$+op}|A-5_*TI1<=W1i9VtJQJdODbl{+lax1 z*pYjYF#J_xe+JiYgFo}P9iSj*WeqRjA*3qRIUvLu7KTf`?uGS!DNrF7}kx6CIZMf}C~}_dW!*U?yCjnix2rZ@;&qf4akNP<0?hR%O$+ zU6p5>6L59>z459{$)hvnG36OR*WSUZ|*jQN`7PZ%@~oVY8dty_x-& zB|idWJRNU{XiN{C)yG7-9f&403CpkEOl>wSnvZEQt1-R(1q0%2{IjV^UgJ^c$b4!8$psg!p(&?Y-4+`g4?K zQ(MG_@*vZz@=jYPr%2>HzEQ1jEaJ?+1J7>!wF!u7@OKp(=o!ahV)Y!=D#HE3Y%kDD zbSibiLY8#lP+@*}9RiYrK**_t@WdK}evm{GR+Z$mwz9bC8DphSN6~ZAfZ7CtE?BF0 z!^src6(PZpq@e%@hQ|W+07h^jOL@}r5FE5sn5u|i;}6yFxrzM~Pe zEN0sw`UBf*^J=Ne%eLoNf0?w{##*ohn$J`m&KENcJi2e=i-UY1B%rxGrt;i*%3F+Y zMvD5`3|~1r*X(@oNV;j8S0S6L&Kbl;kw_dR3|~J#q06e)u>KuItyXHzPNg-;D=|&6 zso+cI0H(SrS3(*A!)SIG*4c!c-`o2 zC8pOL?7J2ib`Hx#R4J4%65@(gzKk6<2;53rN*PwQGM+Z97!o}gTzB?=JGIIE+XFjT zwmE=t_V&+x=^s081|G-Pw%d$9jmocRR)ZGs7R&BI9&&xm!b%>(PVQa0ClQI|SjQk1 z$zU@O+hI<|iW1)P#4FL`@x4%K?!F(ZOc$4xutl|kTAWd6v1*)lbebsAxSfV;%`DS5 zUv4W9uSt#I$&;BsxsUwEBxJ=GgD@a8HdBCo?_s*ETkmPujQVh+)c7J)>b`8?*(AWWIPliN4zae|$_dbTZJMqG( zkyB^+t?T`XXUrwe3W+h=fdd*K&^H*Fr_tWUV8&C*XsBd~n0UUH(Bi9GdWn)SI%bz= zWZ0@nOW^V$Rv)~_4kYxUpjW!O$gc)-RX-qtd}zO>k(DkpuM_AZs!?eCf_SQO<@MC< zhw_E!pg@Wr;m3i4NbA#9n%pw4ii->8yipZCeX=;`K5Ge> zbM7&M>HUKAadeU*M8*BMKD=ap7ZF083+J}+HnxWHj+!(e^Wl6sZ7RT@(b1z}RET%k zeK1>v2h%fviPf9fxb0={T!Rpx9)i73yW7a}AVFFX>+_f6Ram`U2IG_6zAAkNn30=f zYN4@*YMOJDqAYA^X&j#}jB(9Y9F-Hcdg60vL@xX3c)92N{O%NKcV26Isx_9eBYn)u z2ZW0}PFBEqu$OE)^3YPsPh~HyRAp#*_#F#4=stTqBbDZ0*Oc4A{ursIoE{Y&G24v) zzO2yK1G`_w(k!#htA8q4k_db?ji66dKMLisEeMZX`-88BFTJnKS}_!MaGP4B2*Ck) z6$Vu@XY-N!Jq?=bd|ch1HwTar`krm?MX6OJafKut zIv>fNKd%|j6r?AvU!HW!t^FuTzSfQx6r8_$At>5PRcGSGEY?)T6j4%Gu|(&xvxVu9 zCLpJGBW?L%L@wkVJ31(|q1A6{_vv`%MgSR(SR~p>*x<`Cv3qQ-@u1}GHFm$sAlr{D zlW))JfH@^Toy0!^CX{*PqQBI8?sB#>W8>{vr;cpLaNi58mKL~hdla)&ku{YZm68M_ ziPo+gFPWRVFe8aL6$m#BTOJgvJV;9NA|9SxMO6KiFw{~`r4BZA*;D6GJhGvG!Z$mC z1px(KYhz)9vTt@|N_@;^0}q|CsJH;aT2-YE7*>DTvBmR=Ey~)eQ@jjU&wN*HO^_5C zRarfK7>@V8>!ug9aE62~rmMJAd9ds>Ewf#r>uBW275&}kI0CPRW7ZreaOPNYLCzOf zpv#j<*je9W5wYt*jS2(TA#qzIVfhX+`{w9D#=znJp4M!;eR)eLhCK}xU4@4}UfFA5 zN`l}l9d?5{sQAgw1o4o0msemDb-5Fa*f9U=+Lg<3puL}z-t4}PzsoVNwA&#y;IPqR zAw9RI#-cXIq_G>PcbW^!OK$@-jP}a2xw}SX%ib9!R;>41Uu_1M$(x?P8F%2Hx?D5c zU$aPPFSJC)@itW+OYj>R%yV~hBKdf{S2Ml#4s;S&TXR6=EpBwTyDg3H^;_twO6KAj zRu(G#(&SS(YCK8i2b``+K||#Ls2`kLwXM7R+5A-2nU(9dMIDzU2aNVEng%5rYkkup zL2LRBi%xB7T>46SzTuh!5cW7o=3RNfg>q8`l)V9cI-h}(6?bGos1Hp^#xBz`2*EDg z-90_p9jhNmp+%k_rCy%mf(NXt<*AgY(o#Bh-dEnP&DEbma;o~RMF0a6Xf* zFnH}OBXc-CEok#eNa6`ytwWov%E-XC-{a#28G_}X_QHq$t`#=ZN!Q*xnfH_fRptNc zsxcgs6V_;t+LmoAq1L+G7HEX6$zEY9`*}Wdkz)Ov&sy$JpWlD*i=V-6HnzQ1ob^+_ z9GRl!>R{)(1w-@l5tA>8jbt4NM6fzT@?u{JiX!?ctoJ7DT2KfW?%@M+?fx1FwyrFuf}Gz8}ikj&Pr zzmqXJmMrv*W6mRmgM9aTKHFvScCOO=Rd#a|As`^EwCqjF zvBG-^HA=e=eUbP{2RzMCJIS(?a^=|2Kw?&(i*fv2m&>jmYi0d-o|H{Z2R97v=a`-# zFyoD(nZ45;&m?6o{7KMlMSYi6t6+HIXFp#012xRD`UACkD|Y6K3D2f8Wz?5cCe?75 zNo-II9KK*T_#(S=4~}Kla{3%Ug7M?GG0p}x#e2)1DDRtNnqS2{9e>WL4Qg!3PlB3Q zU@+K6VJiYxH@93af(*?^xOjL&V`GurI{D`9p*TI?m*%~KCKKwsYVdf`%Lggbg5U5_ z{0Aw`UW2AJH8tjwHE(-Rs4!OinJPbkyLfE8GWOr^grZ0y!00EG4h3mRW}9d)g(sGI zYfXb94pCjk+GeqSPG-Sh4^i{3PlqNaqmv(L0S0vNl(x}0%g81?{396f?Hq9gntiZk zP3tJ9zbPxE1_YXr?_5qjbDHQ*U{Ue%5(gIlU7s;AF%Tmo!Zf4y7^j-K{n;%7qgnwH zc1m){pLCO_)Z!*4k(;uRI|orBQxo8!pe*Q2Mg}7s0k>cZJ45pHd(R&uCBPCl_Ee7= z9jN~iNvv{E0~;o zNby(0#%dr6x9w0P2GkTpP*7d$Mo|ptohdSXsJ`yyqDKy2du~7&wWtf=!IP4otQrO)PWc?N6;3?UXV{O zw%MKN+M|ATC#GhJ>32sYhF1>oW2M32wg`VW-3ZB8UV^MaOc$BC{R? zUY?8D-)-b@IgS7jCR_h`e)7W(1fnOPGSq+qALKJWtn5C>{L40JRLS2jK3?d?d*Iuo zuP-Qk1A$D&yRO>OWW2zDzg-c~eR;OILVT}q+|uy7o$j-NM*nlylGzHG2dfX+5WU~$ ze|*CLIXMJU%K98cRa1Tu4(CNhm32r&?YK~qTOoQGVZZ7tJQ}B?%AU#LgyWEP;hy5i z@A;LOs31)YA@>HzwEN$J} z!Uvm;>WYemoQoZLp52~4;qFV=HiC2u z%(c3@y0$P_oc$+7#U+8S$l9h7#!Qn;2oojBVQCv(XA#uRWhY>OTTHpeg3%BWyt6|& z{yj*fhzF%@WF%GTSJZ;+Nx~muePfE02DJ@Ys62R10SJ27@^Xf6=|MTgjXWoZ=Ym#B zr!k?vH{PQmaIVaCn%ZTnWso30QCMj zg9%1VaQGiXwNnQR@^x(PG*oyhyUN}-jq-UpG$?iYg6zcovBaOD$bq~|!Qom;b{24$ zRT^L0-Y4id(@B9Jyv2WaOzqD^1$49aEU7Wl5GaM(P6O$vI^W6kiXpFRtbYj;3<|AzdyQI(i z(oQX8EKVaipJRgP;*@s-pxZ%l%IqM33izh+(ZtMFT}ZsQJsJ7D)@;XyP=CLdRVv1X zLiJ(cx)&_6Pt(FnY%V(pH!HUn`FRbmHO5`-T`EdxVV5({x<@@Kh%VAE{CnN-8X=w>W#}ZqlU7a$rcWiRE-SYg#FCuIz%!#PW zCigPeeik_^G^@OhZ?k(G0=9V=pGDUZGR7R~?Z94U`Z~d|+FCw|gNT^8{Y_;KAPy>p zD+#Jb)Du0>Qmh9ZPxIqr0w82d!D%umHg#ekv$M!ErCrWSP%)V)@pP$cT||?sR_0gP z=a7pF-&X@Aih+SL4q^-$B#$0Her!G8>QT|sE>2yzB_=0h)uD+EQ=ww%59Cy%O<_5*uW}-s zDvm1;__P9k02G6&J{^p=(kVTV^rqzs(Bt7Sm z2`A9rBu>HmaWfOGi7Ddbk2Evz8#EXkb36Dy!xG;;4Eq4*l57Zv6=)AT##ql9pdjH4 z2Oo$i^h)Y6odRy-ZuWW%d&FaM$d#q>xU%G@USL|Fj!dz*ieJWjZJ+K=P39?45Xd~@`AYjJT<-2xxK>$Ei--|a_XX|H)^TJQxZ-{b1@?DQhe4` z1X!SVgJS_kuuv;n=aY82HBISmmfZV5;#G?V$45cT&f$@ML7L2s-K zpTq2BhPUcU&$X+)SUl1_pE6rz%I;nTZ(@D;>d~FHV-AdT{T;NJ95$|}4;c6OI_6XU zvHrqCeylwslA53b*nFSlj4xxiQNrbiJU;Q4UP0TAxcuaaBy_d+TNEno&I%_6oao)Q^F699XI%NMwbHqvaapvG=@dTJP%9!DbO1D~{?}dT@(;Oz>nwHCo&rNA;2map5NCw%k7=QWd4ZZSA z1B_v2_6J_l(mf-?rFg2PfO;%2o@i@3g#OBlnIHKqd-uUZ?lACR^!8K3Ek1LDsRu zO!bfs%01EU{=ww8Q8j&PA zdRLK2-@VOJ;1nTHh7j9OG2>nFVb5df4VewGZP)s$;p3KWoX19&O`)0H`W%#BaB*?> zHj2v^@=}>uZ;gMox={Nn$4S&%&}!B>boTTrNc{al>lVXTP+Hh|^!6|G37tijKd_s& z=xSZ4xHtl^_@+EPObV{qxp}nT+a-`boH|kUtrcjhU`EUAbR><;zT>N-!9)8F)$a1S zJDA|1+wQ4$@zTK_xy*-;<$S5ZR_atD;}qIq;dvS9WYMf&)VzD&uGJuc6Y;C9^@5UJ zbn4mhD=ZxSC+M(k%D0B|eevPwZIbhHAdu2u+Wax-IBov3Bpx36!o?QlW;=02*7wCR zW%Y;9q#L&Ah={hKVN_L>FhBw1QkUN^cc~E@Z?`&Qlw?jVHEJ3;R$Ml>#}jvX1M1S2cM|)QT(A&OrrJj+KFkBh zEb)A#N%F(xB9=0`l(f3oI;W+D`GW{gVs*%>k)S7sdt z|Ca06rd?~XFIf;(ko@fXZZjk`HRTzj58gER8|(6i2aT>mnzc2E`g*$6(d-z&Ijo;{ z>;WXS2_lSV${Mi*Vo!dV@7j&$Eg;VT@&G7`_x6nZhrr>AsJr7Ds1IEa^*asg@c!nf;r0q3af_^FscQi zd)$BSX;t!eH#j}%AU;>ay~m?x98%YE%NkLf`lhB1Cl4mI=jlY_*icCt9v*)0I-iRT zp=dsCU2hv%#ZG^~$IJDTxLALFuyHKUYy4frXIJB4?Xa0F)1~E=2RuAHqZ<_DUpXRR zxG|O=4SAA!)e#(Th7d|*U#7?Feehx@l&Pp_+LG);N_sNLloaVBq3P{!=wHXX3nzEJ zkw;ER0%|M3v>|%0BNh;ERFhI5ln6Nd4#K$qm=>}Ua6RUK262I?cT)4TnFwd$34I zg(orXPW*Qb%YQLs;ho0B&CLx;z(%n33DQ&70v1MM++T2L{MJ=P}rP%F#do`1rU;vEVQ~(uHLWfyK`s#yA=tLxD1=Hh^8b5*fu=bjQyF zSr*5~#U9*26MwA68({WReMSM-b^tYjBw38xIT*BJ+|ec)egk~anfWSSAexrnK*G_a z1~|#|(7kE2zi#T+U3+i-lT)3SihNREJRz*%zfTF4d5<2Q>bfiOlE{d7*3{-O*Lt|W zP~k9lCFc^jIYj4Ak9jpV@Qp5F!A}2!N^w4jD2;F;D@xd08J_pv+;`b5E-u1oYb0$CcV8SrQiW8(7C7V$pCz)%W$1YE9 zcYd5^rJMl-0FvKRkOeb*Yxt`>HSL2RQZNfpXTlsew>FN@%>CyhZ7@5#&)U~;xUh!4 zm;My$S3w2Nn@0HL@0f4S7#oe?&u#jVNypwp0Lv=AXz(?;q2h^5WT)bF4j9vL_iNhA z;v<2Q=zTHB;&e5KbsJ)RElZ}`q_VHe3+;T_&lDW3>H#+d*p zk7pxO@G865hQ)`AN`~3v$%F-LDdp*WKJniwKK`3zMGmFkvVHG^S_MAAxh4c1M6&`n z-U6Xs4MZ{dcaPTBc)$v=c@<2Vxks3QlM3R5C9 z$iJ29&GL61O5^n3Vz8;7cge0qXx8Dj=^7UuL@@;gK(V;ag&Y*j-;pNvZy<904CKdpp#_SFWI5^^Qvkf&{`w^Ntl zkiB>SkoK|{*Kf8bcBZRox5Fq_jK}f+Zz7R|^AXl7{nXrshDibQz*9W5+4wuHfUox+4dT*1Q>7mr=3Zbo}+yg%r?L@1`<6QS`HDQo6^TXSoRS0Sg6y0>Y_4`0hp!Tj12ldIHQ z(k+NjtjnH_E=OxZO($vME?@kpP_l>k|Eu zV6lWQ zJ|gDYUepFpIHR%nRCM$Tt}Uq7!RR;!ap+|`^S{+=+9%@(^^M~I{&9EE#X;;6M@M>R z0=LoZK7a7Y;coK4G0op>h~ch^e@GJnt@r+uAvc`+rX-J&pi5+k6$npaSK1zeyEjTMd}?`)o=12%ePNAL`RkskNfQxTV=pJXIWClX&4 z)15UIjeli<8Im}l&dy{%e*E|-YEJ{eTOw?L#E+j1@v5^TPOic)C*&Pk;CH~7L8pC$ z`;%k%7aRL0_n~g}&aORie{C{?n^{Dwtr5Ao7$U{ih@2ddSUNFDB$n=oZcjgo*g#WK z^F4v*ZEEvt#1Zhu+7-E@a;F+$(~-nA;L!>qFMigliO!)bHVj42!CF^qPNLENwVkIp z+~q_o^l5WTm7Xd3DT^J10`EHCgrJr~c~LzB1I(vZG`W46IJu$8xoRn&=xQNwj`t6R z(c)7}1qP3_lwOvrDhbCLo9e|A+C^8somHmdZ>0PWMpo~~Quf+~TK%Dp`YIrE=&p-v zsz9PVxr|a2cV)M$fEYilOZH|_jV__}-TcWk5EBz~U}Bm3&e!PTJNJZ&3nPJ0SBxC; z*kVvDOiB1x3HAD_gUzd7ZM2TJyji!K%10*{gahf}R8+NnK4!@>CCNt@)b%m*k$)w;<5YjL zN;%ZHdIZ}3yx>=ke@Y0P@sO}Nd0Oz<5GU+GRT>i@&ZqF-5v^^a`Jl7NAo$t%>doHP zy8*z_D7s6Z&7JOda?~`}0M1I|0l?IeYr=+DyK^WRCpp=onSrE(KQ9FSFFYhSYJ~>; zL(h`VBENmKtKm75evv<|EJ=@f58F& zrBlS5?M}2bZkvENmCP)Fnid{|K%3Ekzdzdz3J(|*Hi*21_c zUP4+Q2mADTJas(`_DSa1_pf@dpqrkJ7DZ&7EVWh_=0^FXz$KdB!K#Jq4-Q&5pn~r& z9&k~juB|c}EoOew`#-A!ml$LZeKgR@zfxKxVl#68Gm~dMGLevw&}Cz|@VTs0(`F*? zpu&ejq;Bsh$^QF3*r)hyA@ahh)S;fAo(X6{*XPggf##NalPQZKDXW?4^BcLQSRYPa zbD$1ENyYpjmK~eirb*Z5U;bRO4ZWOyosyD*?Qe!_#G?LKddxfe5^MzKH?Ml4R^XeG zmX=pt{nyFl`SYmR21_4iIoA=+6!$=I=L=!k>{otf9IuOvW6Y0Z^KS|dj2AiiPaSw{ zUSWbJr6wV!rtv57o~^KLrtO)4;cK?(9Y0f#S-DkWy+>xb1I2vo+5ykNwR6;8_XNysvL*Sxt9K5&T2~->XkpxM^)`ty$WCDzp0a_(1KG3NjMse9v#)& z_1q5pAo3W9eG-5Qii!@5;w>AUd?u>`28&I0d8T~6d+hNk1dzY;y-A$1hnxWLoG87&YADN+zO8o^}=P>~1y(OI$?r668D?b&H7*O~2RLb|L zz-xLaPesV@#Gy5y14gRr`9*2qr+B{nL&JQd7Y|=nwPqzf7an{1 zPm_<2b&;=lMr+k0YYA;SkUif;zXzg8>KMDWCaPWPmz{eSLy6hLCwGi$PntpsE){)e zY&WR-Yi&{b+28t3Ut{i}Y5LC}Roz-XUm4S>Qd^PyHhDK=Ut1>aNxf$pTaHP`m?_VF z(xVr|VoQi?wG})lr_1=o#l`VTbxcf{KBuR5m&SaVQoU=`pzpNQ52jH%TXo{2Bm6fNsDUDt3s!Gs3ohvpZd}&>~Sw?W^H%a>!h1(38^$j+Yo7}paWw6~-S#Mc+(dW#)GpUI!DtR!JWzfvNIVf1) z)Q?HqRHc5F4{^P{*u1@~RAcsKxoFv{!%5!vMVCEY!WhaAJ&URz(53hpcLv*S3BWJHA*V@;;<%OyL6O;TGA_kFL zdLWSO_0V-zg|51ic8=T=1_kB}$85hoO8^7g{{@8)mQ&^FJ$%!MLBD6fL|nm7M0u9l z*Ttx%k}Lm(ZUD%27Y6cVVCn4w-AXnLf-v7xuGBpyartrwhyN25*S6~x)OYc7yd#o` zLC%O(B5aJ052GHCt`I)>519O?Aq<`iEnms1HiPk#BclggAl}^F37NfXyrVJ}4T}G4 zP6BMHqIJtf<$a)M>)`O};#(jFLGZ!E-^%k|-LR9l0eCTXnYEC{luCF^sePOAXY0<{u4lnuB$ZcdHgRyo2YGEo!dL< z^pj0iyOC4Nhu>;|%<{F!h{c;3k96Y7w;C5D$e;wmWX#iaaNzF9I+dKuEZ=&uO--0} zOPM1F2|f?soy*0$)!4nD1iCBQ4Y-}%n=eGM;s;}51S5K%u~_`z<|5$=4dq=ln40YB z5JPjwlY=gEh$CgT4tN!US-lB#u?xQK@y2HUIaSXSOAiG25S&ZCs(QFpxj1&Y1cX1c zcI>Ye4&v%Fcr^To0q4v=EamBo`{=)o$Gl1y*xA%AoR<~x27pSR;wVjDc(5MgtB=$7 z{8pGK$p0pxem{Z%T69xnlfE$VVjp+I0Q1t*2`u$&?`dx4oq<bB^?T@w{2>BC!?l9EyjH`u@= zQ1shMNR2#C51H|Qb$GC)KvX9k!WVRz9z1yavo#MrKE8I_&I<6Q=-$+);`h!v^+N`4 cv2R%reX%s|7iHGK-|9fml{6L0o|%XK4=Wv4W&i*H literal 17338 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 Uo*NwePY#HTgaWKY?7iRr00c?MoB#j- diff --git a/doc/salome/gui/SMESH/images/creategroup.png b/doc/salome/gui/SMESH/images/creategroup.png index 2fb825cc865d820a61def6629daac7a63d338869..ec24a4feda84816946bebe69c6b4f02441fa5c7f 100755 GIT binary patch literal 33751 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 literal 30825 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/custom_point_marker.png b/doc/salome/gui/SMESH/images/custom_point_marker.png new file mode 100755 index 0000000000000000000000000000000000000000..a46e33ed99610ecc67111b6c23bad8f3cb2b4be2 GIT binary patch literal 8145 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/dialog.png b/doc/salome/gui/SMESH/images/dialog.png index 1fa2c42635471fabd424ff507c3283580ff3abbf..c9cc22cab9dc31c410d31e42d77a6e256511261b 100755 GIT binary patch literal 9281 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

    ~{1@*}(t+&X<>UA; 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�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

    ABD_FrF8aZco}ENwYj>wf|GhO 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 V8-;w}KO6x%*Yz|@uUS9&{{Zd^GB^ML diff --git a/doc/salome/gui/SMESH/images/distributionwithtabledensity.png b/doc/salome/gui/SMESH/images/distributionwithtabledensity.png index 3bd40bc458fa3f23233ee829ee152edadb3e1a7b..60847cde5ffe148afd4d31f4ac30c4125a6d3aa6 100755 GIT binary patch literal 38836 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 G@qYkS3VJ{Q literal 16346 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/double_nodes.png b/doc/salome/gui/SMESH/images/double_nodes.png new file mode 100644 index 0000000000000000000000000000000000000000..27a209b7415f36bde1dad8c2abc5b713e2cae14b GIT binary patch literal 6636 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/duplicate02.png b/doc/salome/gui/SMESH/images/duplicate02.png new file mode 100644 index 0000000000000000000000000000000000000000..0e501412539a1f79c17ede8fc4730506219c9430 GIT binary patch literal 27801 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/duplicate_nodes.png b/doc/salome/gui/SMESH/images/duplicate_nodes.png new file mode 100644 index 0000000000000000000000000000000000000000..61ff32bcd1d9b265fafe281e81c5717b58684df7 GIT binary patch literal 1057 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 diff --git a/doc/salome/gui/SMESH/images/elem_info.png b/doc/salome/gui/SMESH/images/elem_info.png new file mode 100644 index 0000000000000000000000000000000000000000..9937d6d88586b9f0b8f62db1f680167ffcf779bd GIT binary patch literal 907 zcmV;619bd}P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igi8 z69p{^#?W>E00Rq2L_t(I%dM16Xk1kk$A5R`<$DaRDQ|)rD|QwJDo#74?T{3!bTP?` zn~uQW-qG>>_~>X`Jb*p`Gz`P6t*z}?SQsS| ziJbI2?^dZ)Dpr1C{Hvx;kSzgdF4!X)jfTQumDu}o%UhPU^|jX4dAXO}*E^h^y_+8S zv;W4=e|-bkJfyK3fHupzEWnn^W;7a&9ysvg#reB^;ujB!@AZi9b%~SPEg!u9teB>` zeYM7R06P>i2xbS{yQ`}&aeaD>ufD!aHamk~`h$J1YU=Ab*4WrswVG1w zp`k>3clYFY07b<2zdOJ1_lKvJIex;)9y*-8Id(qJ?b}6W=ZaKS$&yI|ORnp}Bg~-zXzT8ttkhDa zQWpg1d}Z&`mwx+Ym;5^SjJ)^mCW*(JmTPKi4g>X%G6x5+wHAP8%bF->GJ9f%VQgw? zsf&sf`RRt{+&Qu{GfuzfdEWw_v4%A5vuC%rTh_$5)*e8!*4n#zHD5S!(j}WMGdriR z=kxBTz$~EGvM&d)MKqbro<1OE7{*dE8B!me4s-mN;Ijm`$!Q2OAnwpxPDwRraXsw^uTL0*}?kB*##{s?s h^aOex2z39@xDUtgVtTen16BY4002ovPDHLkV1klprFZ}U literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/eleminfo1.png b/doc/salome/gui/SMESH/images/eleminfo1.png index 3aae250c8c7472a4e458aa41b29546c34d1ecdd9..3cd439b76df0733f11f26e61b02a9d9a971e318d 100755 GIT binary patch literal 21263 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 SErdV1L^g$P4XIwg@6=y~k>S4p literal 14708 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 VAC2IHu2cv__NfA_NK)_Z{{j5Ia-IMH diff --git a/doc/salome/gui/SMESH/images/eleminfo2.png b/doc/salome/gui/SMESH/images/eleminfo2.png index 9ead335b823ad79ad2ec0fb962d99fb765c7b13f..54fe12df8603d679a5549f3e4e7f1b5f1fb434c5 100755 GIT binary patch literal 42074 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 literal 16004 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 ReqD}`7Jn|5_eAIY{{gYxKn4H+ diff --git a/doc/salome/gui/SMESH/images/extr_along_wire_after.png b/doc/salome/gui/SMESH/images/extr_along_wire_after.png new file mode 100644 index 0000000000000000000000000000000000000000..eb39bdd08b47d6d55eac9939fa6d26cbb3c62bdd GIT binary patch literal 7221 zcmdUU`9G9h`2Q_Ep=E@!Bzs6iDQQfoXtRf;MU;e;C5;ir(xXNwo@6()+P6ZCbqe9Z z%tMx9W*EtY%-F`58N>J7^?5$8&+{jIf4E=g+~+>$eXi?$u5+DpUH2n9n^Rjh?$`)H z(3Uf&&Fvv*9TS3t@>56?Zv9q=p=7P~_HHAxh8X)7H-@Le<5v+rporah63avlutXM#@W;Mc&#d^J&gR}-N*R^STH%Ccv zg);4`Hg*1bsP4D5ZWY6i3eP3;R0Yp&6U7A$WfAy%5$9-sk=t|ey{rz8B2cW4{QyW9G;97$Bm(}xs%5*BxmJaqewt0iW^ut%*5&Lvf+jBo~~ zE4R%lE9CYNVH#ocxOt6mk#aB2`%38G6isC`DoYge{9@f(-9BV$q{tm*O4QWlw*Jm) zr7d5=m+k7s-O#31L}4v4%=3*@G}iD-osta8>Z*$n3$Us}iF&DUaqz0x^u}Q2DpY|h zL_`j~fBgRTt9xAb*&bT`N=Ri>xX-8aPPmCGjClBV`z2VIgQhDpJ$j4l{k74H+NG`CNa7eFU9Nt_j5L6vZf8n$d2B zXw8wbrZxvFtJ4wJJthq^V>#c7nW<8|m}Es3W5sVv21ZF~&3Ghae@4V=b*56>a!0bnER&)FM6uFkXIlFl zIMiZ$VtE_lL|AlUlcVA2LOe!!JNTH}f)*qaoH)pV*}hr{C6)2KcMTBiUuVr zj&gCZX|<%gZFPR`=9qDtLD!J6B#g4dgW}1KKAB+JDTg&Ad=E(ez zlALFNPEl{fFm8!2J~V1qvm5k!tE0kCFVONb11086HmR6qN8-?uOyV&tXWHz~2JL=c zxXtUp;V=9pEm1DL_mBEOxzoU{gg&8`xn3(}3;GH+d7z}{Rl}RtiBAI?@?m5*#|KRE zd{49;7#EkCwKJq6bR>V)JuGIa;vKU37Z*-sJ-7L&;B>Q)&$hff$f4!POvdjT_sitO z9VP_gfNaj`1m>W~8rS$Q+L4eQGk?}y!0~qE=p>CjE^v^VCwV^C0K-*Vkyb6?`GyJE zx(gy^2?n_ONZh;_CZF>3L%5eU?y|xr?h<39@d-6UG9(4WQc8L+})bnnA;;m;l(_*K(k4TmAIdd_+yGz?iU_b&8XnzJf^Al(m%cMVilsX zei5rPUg7g~F^4s2R6?_TG-}(}fV@qno@z!Da+T{EPT;(ym{IYlg3XVXN(_owDVX*o ze_R(etNHFVEQ8fcJH0j>C zO;0e)AMorNYAkxlZV1(=@CmL*rADcXV%ocW%d2NnB?9i{8!ClKfHnA<(@A`)`)H{& zV-VAc8aJyk(^wku=UJJD5o!&9ESBkehN^EmkBCdK5=H0;v6)XH(G@bicSgbqPX{>e zp89nysMng5tH!S7@G6$UhLUM!1-Mq9Lo z`#qe87^X)5P@kpKM?yRbbMt&BPnUKT`4Qirq1IQzoIX38b zEDBK=*fyJI<7rC$29eA;b4*cY*kyRxt!nS?JR#2I_ayuiBRtA ze*B7a|6rc?j_J^oEvT%#M={N=TZDtgD+^P%G1ap=E6Yrh1Yj5pYLfDGzR00Y62K3 z(@ixSV>$Lt9D8X_mLGLk1mh?8;yT~;q9u~GX{mwSafGHusc+Vj#yH0Wrq4wiX+3*S z#E`E=){0@44vvIr4Venwru8?xzJz%qH8sAA6RGs>9R`^m1$^OXih7Q&TA$W2U)L7z zTjiKXnBAH3n9{1-^4*56wcLPXMWNA`I9lCkhHjsh%M=@1Sdm8gX8f@MwFs^z6%tc@ z8vWcZb?wjs9agz*O`x53HSJBRR?-H9ok zF=@lH7@ICqEyXx*NFK$Li8x;(BUswOqW)Ac2{nB^0Qa}^XBF4##*IEVdJyB}F;bO7 zTxfJZ7Li)Y9JssNp>)m{jSIU#d7=Dm;O?3W0_l^X(HV?kA-MB~sKJLiObI7Wfx<-! zzMg0_F3XYtuJ*51e2lE(jFlhXiqnw5w-j|!8T{U`D+M#~am>a%UK~@z_lPzs*KP~e zyNynmW!X>N2*9PYPx2aM3{5%XZ=0@1#ogRI&(M-6ByP3#x+whEq}9^p5bqx|pp3o5BzO{+Ei_*gMaDKeG7s~1zl zvN+N+#G_+^_UXf0X9i96Xa&z4P)z1l`|9M%2mkyyDUp<%j2MGX5Zfho2&c$ zryCzHmQJj4UJ8B)JgylZFBeYwdoRtQnCYGx#L(|+A0zEGEDD`Ef9Im>^HaA}jpaTd zx|FNGV<(f+14-`1%)Zk#pIPLl9aXht>~y%-3Gxa4>xIMm5oe$S&OY^Z-Z=S>cw z)1ifbS(~ThsXu^ryGm%~Q_N8m4GQws_>_qgQ=50}zQXC=p5A{e5ai}-KI?I zJV(c`hph4?%BoM1l@j!WQJ}x`Zim#3T6+;*tEc{bg?2~XImP|dq#F}4LAGfxq@^*4 z+HtW11@3}tc;|$zKaXbLxk!uRQbCNdo0t=k%-gQRNHR6&j|WuFRP-F|W}8BUn;9BI zIKS87a+yi#)vkmeoV0248==#PH&V=aqq{!t}_6u%aO|V_HEEX<$8d!g6 zhY9>O&Fsq>(F`Er7@p6@1p%oLn0Pz`B{Oo zk=g~R9OhKE(a?*#(wLJSOtAw4=t$L=pqeay#*v8Cb1jCAnLiW2fHfZfzQSnD8=D)5 zz5MPXmO1}Jf~TOO&0p87AR6`R&1Gu!2d3+L=$n-S3;P$Xo=2jauI-_fFkwH=O}R+L zH$zrqS;407U#50V**uCyd0x1N#vp|EHMq~21f38`QKuk>t?Qc(QvGhah@?@MMn*!F za*(}&sMNQ$4m;>J(#6wIXRV^E&XnC|I_ZS23$@|vHpoquP8tJW?wx*H3$7#!Vi?^+ zchS|Q!cdlN%>g~XLw~eqT*4HgzHen(jqzMpI88DVq^rv=Ynehq0n56U~^)TqlK*j838 zW+tLQSnc0BWkUjit^e(ge&e=4x%PB0;zxOHk~a8A85>ZT-$fCMyq?ub^at_%mx`r+ zDq%BH^53v|SMoF=z8_Bk0rvC3A+m=u2wb62s0f7wku~v{WeJmz-FejJeC{7C zGC3x7T<77FpAQyXk<^B@(=jM(l$sGSeKJ99>$qa(S`NoC(BVrZMRjhPr$ANNccg4n z(49W>Xoj2+B$E!dF#qxd1>JyMKpHZ2{vJa>#ZLoeEz|&5T=Jc1lkZ5Y1)68JPV-A3 zL1L9wwywKN-Ufh_X8r_#=7Asr%8$)!w$rvv7J@WBzpt*i_BV)U2v)IZYLbWZ(^k;6 zi%sb29EppGS!(8pM=;ILvUWbK0>lw-y%!o`^yUM%VSM4CIub@0bfJ=Y>{BnpwNoDe zel5Wn3VlUx6U^XPo>D){E8!>^vdhW!-cT~}YbAeWYJR6G(jElLfh(5?6q`6L?@*l( z9Zk4bxEb1)N!4q2Oj_z@*f*_rItf!ar(pPv zb5n$PXU$(05bLbz39IYEED9Z=8jZuli>|xm9R!We=7^#?=e+B1)sa(Fper$F*%Gqw zV{*dOBI8ENh>|KGt-L(<-j2Ddh!~)L$G7e_4McFKkpZwf;JlPE{FWa z!$N$}rppBU_HgYsXFnwhd&2RCM*!F@m|N%(K7}M_{p16}l90sv1A?@dI#>=(>vlf` zT0R9Q9>$?*^RJiQceTyRK#$Y8x&2svzr$5s_=P-$(1iC`K?b;rCbb<~2i^O^y;-R4 zt+XsIJ^`{tk>j6^3>u5ny`Zh|;=0>hLp54Eud2hQk`RBM<6G=!rjYW>Fvn|ZA%6wAI=EX%jH&xXAy~GwYJi?4~+mlmF0)~v1gpn?|P)BU4Q`XBnlu{Q(p60 zgC*eo!dGxMeX5g^eW$NFPzTR~*BUeaUPya?`(SsB>Z@?iPa&y5vqJj%hs9P~e?#;p z_8T*#WQ+I~);oWKm+aht!)YXcKiZdsCBy995~ueGZ$t%lix57cYd8oaYFQhf}+ z)zTQGm5B8a@y)89>b&chOxK8jg`K=IsxALo+Noh~-sDOJ6bEmNZLZI}_c(DZbn1(h zt^xAX;`$pdi>0#7U@04(%1qBp*$C;!vvq;```%8_s(r)V(Bw-KZ)x8@DL`&Fe5nVL zI=Xqz(ikX`aDn<++v|d93)qcE8>E*%p0{DLDzuo{9K$vS^yky;eRy_G9N`w;S@_k)_eEpdRJd8OILHS^ zj(I3ei4+H+Wu`rWct-;)wy;Z@&kwqt5Ys(%^+>=W=+0D>NRfkR&e7;1dS zZL&vfPtdBj(b_%~V4c_%>pHx5`U0FzJ+MP^#(Kd!+|Pdd^w)qr!qC2?50-pEH6vob z+2KDsGJqR$>(-fWEeFzcx&3tEkNMeG70a{%iub*}H4=Z*MK1$Lk=DKxkjyyA*MV{R zVPG|dknAv!*y(BHu5g>?1l4L|bZ%3)W%2nl0BUEC%w%_Hjz+M$W(<1@=3E>5uK$K|%Ga zM;yQVF2z!S?A>;cw0W{KIB_svWx|8k!El?6^vco#9eNh-?R!!ip*#T5v^b0P_Ui z59t!Ki@!?A*}E|FU1GtozdgEt{R#-{pvaHG>$*WU0A$K9j0~Ov3~aL^mf^s(`H_)j zIdB;hSq8g{{KUwz1h`C#{Po>gC?Qb??%*so9r_hO68{!m)`yXjC1*Y0DYZ36JHh_} zC`ME$2flj{_huhW(u6CD)1l#D1SBH190rJlY$RtV!ANL>)a)eueo|_-6aEvB)h*?f z0!~?|CWeib18PN{5na9q_ta1yWxzT6vsM9KhU#}`YQ|G;wRHnc|h!Fbw|M{{Brn;J+i~7uyt7xUcX^oK}7kK7kDHnev<1X})b) zqachC=C>)z2rI*w)UXNkJY_AR15RjVol^Ax3j1T z-5`$(5uvs*!Ids07El5zU0G})V{APecT~G$nMra$LFjxoVx^wS7Cazv81u<;5q{vO zjEy0UrO2(nT_2&y|6tPQa0ABOxo_84&SOC^YZJ$^>1GiiDN;M4fw#m83HOzs!wQYa zun zR+`9Xeiy8^lzjjW212biGTilca#xtDO)hlPgqXE>0SL)tFtDg1i+`-B=-q8>+QO|1 z;D4Q^)&&`YCD`|86A&YUyu!x5FwvmFtF(;>_Q~&?&i}de{C`zl6K^pqXNa~x+Y0`+ PL1#|bn3o=RyZ`?HoleSS literal 0 HcmV?d00001 diff --git a/doc/salome/gui/SMESH/images/extr_along_wire_before.png b/doc/salome/gui/SMESH/images/extr_along_wire_before.png new file mode 100644 index 0000000000000000000000000000000000000000..2f30ceeaba466d84936bf6f2ce36b051c43bb3b7 GIT binary patch literal 7120 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
    uR|{5!Q3u1sm$oX(Uu-75{`ttCt&>vA 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 aT3>xy`f_K_$r|wF0JoE#C&(^gSN;OY>XO6& diff --git a/doc/salome/gui/SMESH/images/extrusion2.png b/doc/salome/gui/SMESH/images/extrusion2.png deleted file mode 100755 index 527b67a171af95c1162c6acfa649e816d4e0b7c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21556 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 F`~@yQv+V!? diff --git a/doc/salome/gui/SMESH/images/extrusionalongaline1.png b/doc/salome/gui/SMESH/images/extrusionalongaline1.png index 76a60bd345e80fe5a9d903e0ce070b27c3e766d9..95a26ca9006e56be549e600d6452d929cc0dfa5c 100755 GIT binary patch literal 25647 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 LY9R~Hm<9e1Dm*!~ literal 14477 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 I5qTzmiVAq!AkdjdnRvIiNM zfKC!1j1$(K{+0^p_vgQszt+fTa;*S_-R?Lp5Geacmw4lq#l`~8BRRSsfH0kj1Tsv{r>=JU0=xM?{zf1y`};`K0WF&h;B?_P wT#1$A2OG{IAgN=~0t0L@dusH}ow-A%opH8ZZuJ>Q;C7JK1O5A@_nw6OFNr1E(EtDd diff --git a/doc/salome/gui/SMESH/images/extrusionalongaline2.png b/doc/salome/gui/SMESH/images/extrusionalongaline2.png index fb04562508b66ae0b3a8ae645907a6d12c4600bd..8c16623966f6e6251bfb19976d5fab41984b63d1 100755 GIT binary patch literal 28621 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 MDQjOXQZf(te=7dwj{pDw literal 14477 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 diff --git a/doc/salome/gui/SMESH/images/findelement1.png b/doc/salome/gui/SMESH/images/findelement1.png new file mode 100644 index 0000000000000000000000000000000000000000..1c11a031a59673f38517e19e5b645ac5c2daa5a3 GIT binary patch literal 24077 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

See Also sample TUI Scripts of @@ -43,10 +65,29 @@ created: \image html add_node.png -\note You can also use variables defined in the SALOME \b NoteBook -to specify coordinates of the node: +
+\anchor adding_0delems_anchor +

Adding 0D elements

+ +\image html add0delement.png + +In this dialog box specify the node which will form your 0d element by +selecting it in the 3D viewer and click the \b Apply or +Apply and Close button. Your 0D element will be created: + +\image html add_0delement.png + +\anchor adding_balls_anchor +

Adding ball elements

-\image html addnode_notebook.png +\image html addball.png + +In this dialog box specify the node which will form your ball element +either by selecting it in the 3D viewer or by manual entering its ID, +specify a ball diameter and click the \b Apply or Apply and +Close button. Your ball element will be created: + +\image html add_ball.png
\anchor adding_edges_anchor @@ -120,6 +161,16 @@ the \b Apply or Apply and Close button. Your hexahedron will be created: \image html image71.jpg +
+\anchor adding_octahedrons_anchor +

Adding hexagonal prism

+ +In the Add Hexagonal Prism dialog box specify the nodes which will +form your hexagonal prism by selecting them in the 3D viewer with pressed Shift button and click +the \b Apply or Apply and Close button. Your hexagonal prism will be created: + +\image html image_octa12.png +
\anchor adding_polyhedrons_anchor

Adding polyhedrons

@@ -142,4 +193,4 @@ button. If you've managed to obtain the necessary result, click the \image html add_polyhedron.png -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/adding_quadratic_elements.doc b/doc/salome/gui/SMESH/input/adding_quadratic_elements.doc index afe9b77b5..921948630 100644 --- a/doc/salome/gui/SMESH/input/adding_quadratic_elements.doc +++ b/doc/salome/gui/SMESH/input/adding_quadratic_elements.doc @@ -2,7 +2,7 @@ \page adding_quadratic_elements_page Adding Quadratic Elements -\n MESH modules allows you to work with Quadratic Elements. +\n MESH module allows you to work with Quadratic Elements. Quadratic Edge is not a straight but a broken line and can be defined by three points: first, middle and last. All more complex \b Quadratic @@ -18,6 +18,25 @@ one of the following: \image html image152.png +\note All dialogs for quadratic element adding to the mesh +provide the possibility to automatically add an element +to the specified group or to create the group anew using +Add to group box, that allows choosing an existing group for +the created node or element or giving the name to a new group. By +default, the Add to group check box is switched off. If the user +swiches this check box on, the combo box listing all currently +existing groups of the corresponding type becomes available. By +default, no group is selected. In this case, when the user presses +Apply or Apply & Close button, the warning message box +informs the user about the necessity to input a new group name. The +combo box lists both \ref standalone_group "standalone groups" +and \ref group_on_geom "groups on geometry". If the user chooses a +group on geometry, he is warned and proposed to +\ref convert_to_standalone "convert this group to standalone". +If the user rejects conversion operation, it is cancelled and +a new quadratic element is not created. + + To create any Quadratic Element specify the nodes which will form your triangle by selecting them in the 3D viewer with pressed Shift button. Their numbers will appear in the dialog box as Corner Nodes diff --git a/doc/salome/gui/SMESH/input/additional_hypo.doc b/doc/salome/gui/SMESH/input/additional_hypo.doc index 9687bdf2d..61ac4800c 100644 --- a/doc/salome/gui/SMESH/input/additional_hypo.doc +++ b/doc/salome/gui/SMESH/input/additional_hypo.doc @@ -9,18 +9,21 @@ To define an Additional Hypothesis simply select it in Create Mesh menu. These hypotheses are actually changes in the rules of mesh creation and as such don't possess adjustable values. +\anchor non_conform_allowed_anchor

Non Conform mesh allowed hypothesis

Non Conform mesh allowed hypothesis allows to generate non-conform meshes (that is, meshes having some edges ending on an edge or face of adjacent elements). +\anchor quadratic_mesh_anchor

Quadratic Mesh

Quadratic Mesh hypothesis allows to build a quadratic mesh (whose edges are not straight but broken lines and can be defined by three points: first, middle and last) instead of an ordinary one. +\anchor propagation_anchor

Propagation of 1D Hypothesis on opposite edges

Propagation of 1D Hypothesis on opposite edges allows to propagate a @@ -28,8 +31,49 @@ hypothesis onto an opposite edge. If a local hypothesis and propagation are defined on an edge of a quadrangular face, the opposite edge will have the same hypothesis, unless another hypothesis has been locally defined on the opposite edge. - +
See Also a sample TUI Script of a -\ref tui_propagation "Propagation hypothesis" operation. +\ref tui_propagation "Propagation hypothesis" operation + +\anchor quadrangle_preference_anchor +

Quadrangle Preference

+ +This additional hypothesis can be used together with 2D triangulation algorithms. +It allows 2D triangulation algorithms to build quadrangular meshes. + +When used with "Quadrangle (Mapping)" meshing algorithm, that is obsolete + since introducing \ref hypo_quad_params_anchor "Quadrangle parameters" +hypothesis, this hypothesis has one restriction on its work: the total quantity of +segments on all four sides of the face must be even (divisible by 2). + +\anchor viscous_layers_anchor +

Viscous Layers

+ +Viscous Layers additional hypothesis can be used together with +some 3D algorithms, Hexahedron(i,j,k) for example. This +hypothesis allows creation of layers of highly stretched prisms near +mesh boundary, which is beneficial for high quality viscous +computations. The prisms constructed on the quadrangular mesh faces are +actually the hexahedrons. + + +\image html viscous_layers_hyp.png + +
    +
  • Name - allows to define the name of the hypothesis.
  • +
  • Total thicknes - gives the total thickness of prism layers.
  • +
  • Number of layers - defines the number of prism layers.
  • +
  • Stretch factor - defines the growth factor of prism height +from the mesh boundary inwards.
  • +
  • Faces without layers - defines geometrical faces on which +prism layers should not be constructed. By default the prism layers +are not constructed on geometrical faces shared by solids.
  • +
+ +\image html viscous_layers_mesh.png A group containing viscous layer prisms. + +
See also a sample TUI script of a \ref tui_viscous_layers +"Viscous layers construction". + -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/area.doc b/doc/salome/gui/SMESH/input/area.doc index bef0c0f8a..f650a3fd6 100644 --- a/doc/salome/gui/SMESH/input/area.doc +++ b/doc/salome/gui/SMESH/input/area.doc @@ -11,7 +11,8 @@ quadrangles).
  1. Display your mesh in the viewer.
  2. -
  3. Choose Controls > Area or click "Area" button. +
  4. Choose Controls > Face Controls > Area or click +"Area" button. \image html image35.png
    "Area" button
    diff --git a/doc/salome/gui/SMESH/input/aspect_ratio.doc b/doc/salome/gui/SMESH/input/aspect_ratio.doc index 070d377cc..af1057ddd 100644 --- a/doc/salome/gui/SMESH/input/aspect_ratio.doc +++ b/doc/salome/gui/SMESH/input/aspect_ratio.doc @@ -13,22 +13,23 @@ nodes is calculated by the formula: \image html formula4.png -- The Aspect Ratio of a \b quadrangle 2D element consisting of - 4 nodes is the worst (i.e. the greatest) value from all triangles - which can be built taking three nodes of the quadrangle. There are - four triangles to consider: +- The Aspect Ratio of a \b quadrangle 2D element consisting of 4 +nodes is calculated using The Verdict Geometric Quality Library +available within VTK. The calculation formula is: -\image html image138.gif +\image html formula5.png To apply the Aspect Ratio quality criterion to your mesh:
    1. Display your mesh in the viewer.
    2. -
    3. Choose Controls > Aspect Ratio or click "Aspect -Ratio" button in the toolbar. +
    4. Choose Controls > Face Controls > Aspect Ratio or click +"Aspect Ratio" button in the toolbar. +
      \image html image37.png -
      "Aspect Ratio" button
      +"Aspect Ratio" button +
      Your mesh will be displayed in the viewer with its elements colored according to the applied mesh quality control criterion: diff --git a/doc/salome/gui/SMESH/input/aspect_ratio_3d.doc b/doc/salome/gui/SMESH/input/aspect_ratio_3d.doc index 751e41102..2ea62f1f8 100644 --- a/doc/salome/gui/SMESH/input/aspect_ratio_3d.doc +++ b/doc/salome/gui/SMESH/input/aspect_ratio_3d.doc @@ -7,8 +7,8 @@ parameter as the \ref aspect_ratio_page "Aspect ratio" criterion, but it is applied to 3D mesh elements: tetrahedrons, pentahedrons, hexahedrons, etc. -- The Aspect Ratio of a \b tetrahedron 3D element is calculated -by the formula: +- The Aspect Ratio of a \b tetrahedron 3D element defined by +vertices {a,b,c,d } is calculated by the formula: \image html formula1.png @@ -21,8 +21,8 @@ by the formula:
      1. Display your mesh in the viewer.
      2. -
      3. Choose Controls > Aspect Ratio 3D or click "Aspect Ratio 3D" -button of the toolbar. +
      4. Choose Controls > Volume Controls > Aspect Ratio 3D or click +"Aspect Ratio 3D" button of the toolbar. \image html image144.png
        "Aspect Ratio 3D" button
        diff --git a/doc/salome/gui/SMESH/input/bare_border_face.doc b/doc/salome/gui/SMESH/input/bare_border_face.doc new file mode 100644 index 000000000..2115d3249 --- /dev/null +++ b/doc/salome/gui/SMESH/input/bare_border_face.doc @@ -0,0 +1,15 @@ +/*! + +\page bare_border_faces_page Bare border faces + +This mesh quality control highlights the faces having the border not +shared with other faces (free border) and missing an edge based on +nodes of the free border. The faces with bare border are shown with a +color different from the color of shared faces. + +\image html bare_border_faces_smpl.png + +\sa A sample TUI Script making a group of faces highlighted in the +picture is \ref tui_bare_border_faces "Bare border faces Control". + +*/ diff --git a/doc/salome/gui/SMESH/input/bare_border_volumes.doc b/doc/salome/gui/SMESH/input/bare_border_volumes.doc new file mode 100644 index 000000000..d0dd894ba --- /dev/null +++ b/doc/salome/gui/SMESH/input/bare_border_volumes.doc @@ -0,0 +1,15 @@ +/*! + +\page bare_border_volumes_page Bare border volumes + +This mesh quality control highlights the volumes having the border not +shared with other volumes (free border) and missing a face based on +nodes of the free border. The volumes with bare border are shown with a +color different from the color of shared volumes. + +\image html bare_border_volumes_smpl.png + +\sa A sample TUI Script making a group of volumes highlighted in the +picture is \ref tui_bare_border_volumes "Bare border volumes Control". + +*/ diff --git a/doc/salome/gui/SMESH/input/basic_meshing_algos.doc b/doc/salome/gui/SMESH/input/basic_meshing_algos.doc index 1fd311d84..a4ebe1c8a 100644 --- a/doc/salome/gui/SMESH/input/basic_meshing_algos.doc +++ b/doc/salome/gui/SMESH/input/basic_meshing_algos.doc @@ -21,9 +21,8 @@ shape of a mesh.
      5. For meshing of 2D entities (faces):
        • -
        • Triangle meshing algorithms (Mefisto, Netgen 1D-2D and BLSUFR ) - Faces -are split into triangular elements.
        • -
        • Quadrangle meshing algorithm (Mapping) - Faces are split into +
        • Triangle meshing algorithms (Mefisto) - Faces are split into triangular elements.
        • +
        • Quadrangle meshing algorithm (Mapping) - quadrilateral Faces are split into quadrangular elements.
        @@ -34,10 +33,12 @@ quadrangular elements.
      6. For meshing of 3D entities (volume objects):
        • -
        • Hexahedron meshing algorithm (i,j,k) - Volumes are split into +
        • Hexahedron meshing algorithm (i,j,k) - 6-sided Volumes are split into hexahedral (cubic) elements.
        • -
        • Tetrahedron (Netgen and GHS3D) meshing algorithms - Volumes are split into -tetrahedral (pyramidal) elements.
        • +
        • \subpage cartesian_algo_page
        • +internal parts of Volumes are split into hexahedral elements forming a +Cartesian grid; polyhedra and other types of elements are generated +where the geometrical boundary intersects Cartesian cells.
        \image html image125.gif "Example of a tetrahedral 3D mesh" @@ -45,16 +46,25 @@ tetrahedral (pyramidal) elements. \image html image126.gif "Example of a hexahedral 3D mesh" -\Note that BLSURF and GHS3D are commercial meshers and require a -license to be used within the Mesh module. +Some of 3D meshing algorithms also can generate 3D meshes from 2D meshes, working without +geometrical objects. Such algorithms are +
          +
        • Hexahedron meshing algorithm (i,j,k),
        • + +
        There is also a number of more specific algorithms:
        • \subpage projection_algos_page "for meshing by projection of another mesh"
        • +
        • \subpage import_algos_page "for meshing by importing elements from another mesh"
        • \subpage radial_prism_algo_page "for meshing geometrical objects with cavities"
        • \subpage segments_around_vertex_algo_page "for defining the local size of elements around a certain node"
        • \subpage prism_3d_algo_page "for meshing prismatic shapes"
        • +
        • \subpage radial_quadrangle_1D2D_algo_page "for meshing special 2d faces (circles and part of circles)"
        +\ref use_existing_anchor "Use existing edges" and +\ref use_existing_anchor "Use existing faces" algorithms can be +used to create an 1D or a 2D mesh in a python script. \ref constructing_meshes_page "Constructing meshes" page describes in detail how to apply meshing algorithms. @@ -62,4 +72,4 @@ detail how to apply meshing algorithms.
        See Also a sample TUI Script of a \ref tui_defining_meshing_algos "Define Meshing Algorithm" operation. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/blsurf_hypo.doc b/doc/salome/gui/SMESH/input/blsurf_hypo.doc deleted file mode 100644 index b392f3b72..000000000 --- a/doc/salome/gui/SMESH/input/blsurf_hypo.doc +++ /dev/null @@ -1,222 +0,0 @@ -/*! - -\page blsurf_hypo_page BLSURF Parameters hypothesis - -\n BLSURF Parameters hypothesis works only with BLSURF 2d -algorithm. This algorithm is a commercial software. - -\image html blsurf_parameters.png - -
          -
        • Name - allows defining the name of the hypothesis (BLSURF -Parameters_n by default).
        • - -
        • Physical Mesh - if set to "Custom", allows user input in te -in User size, Max Physical Size and Min Physical -Size fields. -
        • - -
        • User size - defines the size of the generated mesh elements.
        • - -
        • Max Physical Size - defines the upper limit of mesh element size.
        • - -
        • Min Physical Size - defines the lower limit of mesh element size.
        • - -
        • Geometrical mesh - if set to "Custom", allows user input in - Angle Mesh S, Angle Mesh C and -Gradation fields. These fields control -computation of the element size, so called geometrical size, conform to -the surface geometry considering local curvatures. \n -If both the User size and the geometrical size are defined, the - eventual element size correspond to the least of the two.
        • - -
        • Angle Mesh S - maximum angle between the mesh face and the -tangent to the geometrical surface at each mesh node, in degrees.
        • - -
        • Angle Mesh C - maximum angle between the mesh edge and the -tangent to the geometrical curve at each mesh node, in degrees.
        • - -
        • Max Geometrical Size - defines the upper limit of the geometrical size.
        • - -
        • Min Geometrical Size - defines the lower limit of the geometrical size.
        • - -
        • Gradation - maximum ratio between the lengths of -two adjacent edges.
        • - -
        • Allow Quadrangles - if checked, allows the creation of quadrilateral elements.
        • - -
        • Patch independent - if checked, geometrical -edges are not respected and all geometrical faces are meshed as one -hyper-face.
        • - -\image html blsurf_parameters_advanced.png - -
        • Topology - allows creation of a conform mesh on a shell of -not sewed faces. -
            -
          • "From CAD" means that mesh conformity is assured by conformity - of a shape.
          • -
          • "Pre-process" and "Pre-process++" allow the BLSURF software to - pre-process the geometrical model to eventually produce a conform - mesh.
          • -
          - -
        • Verbosity level - Defines the percentage of "verbosity" of -BLSURF [0-100].
        • - -
        • Add option - provides the choice of multiple advanced -options, which appear, if selected, in a table where it is possible to -input the value of the option and to edit it later.
        • - -
        • Clear option - removes the option selected in the table. - -
        - -\n -The following options are commonly usable. The notion of diag -used in the descriptions means -the diagonal of the bounding box of the geometrical object to mesh. - -
          -
        • topo_eps1 (real) - is the tolerance level inside a CAD -patch. By default is equal to diag × 10-4. This tolerance is used to -identify nodes to merge within one geometrical face when \b Topology -option is to pre-process. Default is diag/10.0.
        • - -
        • topo_eps2 (real) - is the tolerance level between two CAD -patches. By default is equal to diag × 10-4. This tolerance is used to -identify nodes to merge over different geometrical faces when -\b Topology option is to pre-process. Default is diag/10.0.
        • - -
        • \b LSS (real) - is an abbreviation for "length of sub-segment". It is -a maximal allowed length of a mesh edge. Default is 0.5.
        • - -
        • \b frontal (integer) -
            -
          • 1 - the mesh generator inserts points with an advancing front method.
          • -
          • 0 - it inserts them with an algebraic method (on internal edges). This method is -slightly faster but generates less regular meshes.
          • -
          -Default is 0.
        • - -
        • \b hinterpol_flag (integer) - determines the computation of an -interpolated value v between two points P1 and P2 on a -curve. Let h1 be the value at point P1, h2 be the value at point -P2, and t be a parameter varying from 0 to 1 when moving from P1 -to P2 . -
            -
          • 0 - the interpolation is linear: v = h1 + t (h2 - h1 )
          • -
          • 1 - the interpolation is geometric: v = h1 * pow( h2/h1, t)
          • -
          • 2 - the interpolation is sinusoidal: v = (h1+h2)/2 + -(h1-h2)/2*cos(PI*t)
          • -
          -Default is 0.
        • - -
        • \b hmean_flag (integer) - determines the computation of the average of several -values:
            -
          • -1 - the minimum is computed.
          • -
          • 0 or 2 - the arithmetic average computed. -
          • 1 - the geometric average is computed.
          • -
          -Default is 0.
        • - -
        • \b CheckAdjacentEdges, \b CheckCloseEdges and \b CheckWellDefined -(integers) - gives the number of calls of equally named subroutines the -purpose of which is to improve the mesh of domains having narrow -parts. At each iteration,\b CheckCloseEdges decreases the sizes of the -edges when two boundary curves are neighboring,\b CheckAdjacentEdges -balances the sizes of adjacent edges, and \b CheckWellDefined checks if -the parametric domain is well defined. Default values are 0.
        • - - -
        • \b CoefRectangle (real)- defines the relative thickness of the rectangles -used by subroutine \b CheckCloseEdges (see above). Default is 0.25.
        • - -
        • \b eps_collapse (real) - if more than 0.0, BLSURF removes -curves whose lengths are less than \b eps_collapse. To obtain an -approximate value of the length of a curve, it is arbitrarily -split into 20 edges. Default is 0.0.
        • - -
        • \b eps_ends (real) - is used to detect the curves whose lengths are very -small, which sometimes constitutes an error. A message is printed -if fabs(P2-P1) < eps_ends, where P1 and P2 are the -extremities of a curve. Default is diag/500.0.
        • - -
        • \b prefix (char) - is a prefix of the files generated by -BLSURF. Default is "x".
        • - -
        • \b refs (integer) - reference of a surface, used when exporting -files. Default is 1.
        • -
        - -\n -The following advanced options are not documented and you can use them -at your own risk. -\n\n Interger variables: -
          -
        • addsurf_ivertex
        • -
        • background
        • -
        • coiter
        • -
        • communication
        • -
        • decim
        • -
        • export_flag
        • -
        • file_h
        • -
        • gridnu
        • -
        • gridnv
        • -
        • intermedfile
        • -
        • memory
        • -
        • normals
        • -
        • optim
        • -
        • pardom_flag
        • -
        • pinch
        • -
        • rigid
        • -
        • surforient
        • -
        • tconf
        • -
        • topo_collapse
        • -
        -Real variables: -
          -
        • addsurf_angle
        • -
        • addsurf_R
        • -
        • addsurf_H
        • -
        • addsurf_FG
        • -
        • addsurf_r
        • -
        • addsurf_PA
        • -
        • angle_compcurv
        • -
        • angle_ridge
        • -
        • eps_pardom
        • -
        -String variables: -
          -
        • export_format
        • -
        • export_option
        • -
        • import_option
        • -
        - - -\n -Currently BLSURF plugin has the following limitations. -
          -
        • The created mesh will contain inverted elements if it is based on a shape, - consisting of more than one face (box, cone, torus...) and if - the option "Allow Quadrangles (Test)" has been checked before - computation.
        • - -
        • SIGFPE exception is raised at the attempt to compute the mesh - based on a box when the option "Patch independent" is checked.
        • - -
        • BLSURF algorithm cannot be used as a local algorithm (on - sub-meshes) or as a provider of a low-level - mesh for some 3D algorithms, because the BLSURF mesher (and - consequently plugin) does not provide the information on node - parameters on edges (U) and faces (U,V). For example the - following combinations of algorithms are impossible: -
            -
          • global MEFISTO or Quadrangle(mapping) + local BLSURF;
          • -
          • BLSUFR + Projection 2D from faces meshed by BLSURF;
          • -
          • local BLSURF + Extrusion 3D;
          • -
          -
        • -
        - -*/ diff --git a/doc/salome/gui/SMESH/input/building_compounds.doc b/doc/salome/gui/SMESH/input/building_compounds.doc index faf636b82..98a20c470 100644 --- a/doc/salome/gui/SMESH/input/building_compounds.doc +++ b/doc/salome/gui/SMESH/input/building_compounds.doc @@ -47,4 +47,6 @@ for this operation. \image html image160.gif "Example of a compound of two meshed cubes" +See Also a sample +\ref tui_building_compound "TUI Example of building compounds." */ diff --git a/doc/salome/gui/SMESH/input/cartesian_algo.doc b/doc/salome/gui/SMESH/input/cartesian_algo.doc new file mode 100644 index 000000000..3240150c7 --- /dev/null +++ b/doc/salome/gui/SMESH/input/cartesian_algo.doc @@ -0,0 +1,75 @@ +/*! + +\page cartesian_algo_page Body Fitting 3D meshing algorithm + +Body Fitting algorithm generates hexahedrons of a Cartesian grid in +the internal part of geometry and polyhedrons and other types of +elements at the intersection of Cartesian cells with the geometrical +boundary. + +\image html cartesian3D_sphere.png "A shpere meshed by Body Fitting algorithm" + +The meshing algorithm is as follows. +
          +
        1. Lines of a Cartesian structured grid defined by +\ref cartesian_hyp_anchor "Body Fitting Parameters" hypothesis are +intersected with the geometry boundary, thus nodes lying on the +boundary are found. This step also allows finding out for each node of +the Cartesian grid if it is inside or outside the geometry.
        2. +
        3. For each cell of the grid, check how many of its nodes are outside +of the geometry boundary. Depending on a result of this check +
            +
          • skip a cell, if all its nodes are outside
          • +
          • skip a cell, if it is too small according to Size + Threshold parameter
          • +
          • add a hexahedron in the mesh, if all nodes are inside
          • +
          • add a polyhedron or another cell type in the mesh, if some +nodes are inside and some outside.
          • +
          +
        4. +
        +To apply this algorithm when you define your mesh, select Body + Fitting in the list of 3D algorithms and click "Add + Hypothesis" button and "Body Fitting Parameters"" menu + item. Dialog of Body Fitting Parameters + hypothesis will appear. + +
        +\anchor cartesian_hyp_anchor +

        Body Fitting Parameters hypothesis

        + +\image html cartesian3D_hyp.png "Body Fitting Parameters hypothesis dialog" + +This dialog allows to define +
          +
        • \b Name of the algorithm
        • +
        • Minimal size of a cell truncated by the geometry boundary. If the + size of a truncated grid cell is \b Threshold times less than a + initial cell size, then a mesh element is not created.
        • +
        • Cartesian structured grid. Each grid axis is defined + individually. Definition mode chooses a way of grid + definition:
            +
          • You can specify the \b Coordinates of grid nodes. \b Insert button + inserts a node at distance \b Step (negative or positive) from a + selected node. \b Delete button removes a selected node. Double + click on a coordinate in the list enables its edition. A grid + defined by \b Coordinates should enclose the geometry, else the + algorithm will fail.
          • +
          • You can define the \b Spacing of a grid as an algebraic formula + f(t) where \a t is a position along a grid axis + normalized at [0.0,1.0]. The whole range of geometry can be + divided into sub-ranges with their own spacing formulas to apply; + \a t varies between 0.0 and 1.0 within each sub-range. \b Insert button + divides a selected range into two ones. \b Delete button adds the + selected sub-range to the previous one. Double click on a range in + the list enables edition of its right boundary. Double click on a + function in the list enables its edition. +
          +
        • +
        + +
        +See Also a sample TUI Script of a +\ref tui_cartesian_algo "Usage of Body Fitting algorithm". + +*/ diff --git a/doc/salome/gui/SMESH/input/changing_orientation_of_elements.doc b/doc/salome/gui/SMESH/input/changing_orientation_of_elements.doc index 410245b04..416f9514c 100644 --- a/doc/salome/gui/SMESH/input/changing_orientation_of_elements.doc +++ b/doc/salome/gui/SMESH/input/changing_orientation_of_elements.doc @@ -2,8 +2,7 @@ \page changing_orientation_of_elements_page Changing orientation of elements -\n Orientation of an element is changed by reverting the order of -nodes of the selected elements. +\n Orientation of an element is changed by reverting the order of its nodes. To change orientation of elements:
          @@ -11,13 +10,16 @@ nodes of the selected elements.
        1. In the \b Modification menu select the \b Orientation item or click Orientation button in the toolbar. +
          \image html image79.png -
          "Orientation" button
          +"Orientation" button +
          The following dialog box will appear: +
          \image html orientaation1.png - +
          • The main list shall contain the elements which will be reoriented. You can click on an element in the 3D viewer and it will diff --git a/doc/salome/gui/SMESH/input/clipping.doc b/doc/salome/gui/SMESH/input/clipping.doc index f3b19f374..2bfa8292b 100644 --- a/doc/salome/gui/SMESH/input/clipping.doc +++ b/doc/salome/gui/SMESH/input/clipping.doc @@ -9,12 +9,20 @@ To start, click on the \em New button. \image html a-clipping2.png -Now you can define the parameters of your cross-section: \b Orientation -(X-Y, X-Z or Y-Z); \b Distance between the opposite extremities of the -object, if it is set to 0.5 the object is split in two halves; and -\b Rotation (in angle degrees) around X (Y to Z) and around Y (X to -Z). If the Show preview button is on, you can see the clipping plane -in the 3D Viewer. +Now you can define the parameters of cross-section: +
              +
            • List of meshes, sub-meshes and groups to which the cross-section will be applied. +/n Select all button allows to select and deselect all available +objects at once).
            • +
            • \b Orientation (X-Y, X-Z or Y-Z).
            • +
            • \b Distance between the opposite extremities of the boundary box +of selected objects, if it is set +to 0.5 the boundary box is split in two halves.
            • +
            • \b Rotation (in angle +degrees) around X (Y to Z) and around Y (X to Z).
            • +
            • If the Show preview button is on, you can see the clipping plane +in the 3D Viewer.
            • +
            \image html image79.jpg "The plane and the cut object" diff --git a/doc/salome/gui/SMESH/input/colors_size.doc b/doc/salome/gui/SMESH/input/colors_size.doc new file mode 100644 index 000000000..f953b37c6 --- /dev/null +++ b/doc/salome/gui/SMESH/input/colors_size.doc @@ -0,0 +1,37 @@ +/*! + +\page colors_size_page Properties + +\image html colors_size.png + +Using this dialog you can define the following set of mesh visualization +parameters: +
              +
            • Elements
            • +
                +
              • Surface color - surface color of elements (seen in Shading mode).
              • +
              • Back surface color - interior surface color of elements. Use slider to select this color +generated on base of the Surface color by changing its brightness and saturation.
              • +
              • Outline color - color of element borders.
              • +
              • Wireframe color - color of element borders in wireframe mode.
              • +
              • 0D slements - color of 0D elements.
              • +
              • Size of 0D slements - size of 0D elements.
              • +
              • Line width - width of lines (edges and borders of elements).
              • +
              • Shrink coef. - relative space of elements compared to gaps between + them in shrink mode.
              • +
              +
            • Nodes
            • +
                +
              • Color - color of nodes.
              • +
              • Marker - group of options allowing to change the representation of + points (see \subpage point_marker_page "Point Marker" page).
              • +
              +
            • Orientation of faces
            • +
                +
              • Color - color of orientation vertors.
              • +
              • Scale - size of orientation vectors.
              • +
              • 3D vectors - allows to choose between 2D planar and 3D vectors.
              • +
              +
            + +*/ diff --git a/doc/salome/gui/SMESH/input/constructing_meshes.doc b/doc/salome/gui/SMESH/input/constructing_meshes.doc index d62feac7c..adf63c6a1 100644 --- a/doc/salome/gui/SMESH/input/constructing_meshes.doc +++ b/doc/salome/gui/SMESH/input/constructing_meshes.doc @@ -2,83 +2,250 @@ \page constructing_meshes_page Constructing meshes -\n Construction of a mesh consists of: +\n Construction of a mesh on some geometry consists of:
              -
            • Selecting a geometrical object for meshing
            • -
            • Applying \subpage basic_meshing_algos_page "meshing algorithms" and -\subpage about_hypo_page "hypotheses" which will be used at computation of -this mesh.
            • +
            • \ref create_mesh_anchor "Creating of a mesh object"
            • +
            • \ref evaluate_anchor "Evaluating mesh size"
            • +
            • \ref preview_anchor "Previewing the mesh"
            • +
            • \ref submesh_order_anchor "Changing submesh priority"
            • +
            • \ref compute_anchor "Computing the mesh"
            +Mesh can be \ref use_existing_anchor "computed using your own meshing algorithms" +written in Python. + +\anchor create_mesh_anchor +

            Creation of a mesh object

            To construct a mesh:
              -
            1. In the \b Mesh menu select Create Mesh or click "Create -Mesh" button in the toolbar. +
            2. Select a geometrical object for meshing.
            3. +
            4. In the \b Mesh menu select Create Mesh or click "Create + Mesh" button in the toolbar. + + \image html image32.png + "Create Mesh" button + + The following dialog box will appear: + + \image html createmesh-inv.png +
              +
            5. +
            6. Apply \subpage basic_meshing_algos_page "meshing algorithms" and + \subpage about_hypo_page "hypotheses" which will be used at computation of + this mesh. + + For example, you need to mesh a 3D object. + + First, type the name for your mesh in the \b Name box, by default, + it is "Mesh_1". Then select the geometrical object you wish to + mesh in the Object Browser and click "Select" button near \b Geometry + field (if name of the object not yet appeared in \b Geometry field). + + \image html image120.png + "Select" button + + Now you can define 3D Algorithm and 3D Hypotheses, which will be + applied to solids of your geometrical object. Click the "Add + Hypothesis" button to add a hypothesis. + + \image html image121.png + "Add Hypothesis" button + + Click the "Edit Hypothesis" button to change values for the + current hypothesis. + + \image html image122.png + "Edit Hypothesis" button + + Most standard 2D and 3D algorithms can work without hypotheses + using some default parameters. The use of additional hypotheses + is optional (i.e. you may leave "None" in this box). + + Proceed in the same way with 2D and 1D Algorithms and Hypotheses that + will be used to mesh faces and edges of your geometry. (Note + that any object has edges, even if their existence is not + apparent, for example, a sphere has 4 edges). Note that the + choice of hypotheses and of an algorithm of lower dimension depends on + the algorithm. + + Some algorithms generate mesh of several dimensions while others, of + only one dimension. In the latter case there must be one Algorithm and zero or several + Hypotheses for each dimension of your object, otherwise you will + not get any mesh at all. Of course, if you wish to mesh a face, + which is a 2D object, you don't need to define 3D Algorithm and + Hypotheses. + + In the Object Browser the structure of the new mesh will be + displayed as follows: + + \image html image88.jpg + + It contains: +
                +
              • a reference to the geometrical object on the basis of + which the mesh has been constructed;
              • +
              • Applied hypotheses folder containing the references + to the hypotheses applied at the construction of the mesh;
              • +
              • Applied algorithms folder containing the references + to the algorithms applied at the construction of the mesh.
              • +
              + + There is an alternative way to assign Algorithms and Hypotheses by + clicking Assign a set of hypotheses button and selecting among + pre-defined sets of hypotheses. In addition to the standard + sets of hypotheses, it is possible to create custom sets by editing + CustomMeshers.xml file located in the home directory. CustomMeshers.xml + file must describe sets of hypotheses in the + same way as ${SMESH_ROOT_DIR}/share/salome/resources/smesh/StdMeshers.xml + file does (sets of hypotheses are enclosed between + tags). + + \image html hypo_sets.png + List of sets of hypotheses: [custom] + automatically added to the sets defined by the user +
            7. +
            + +Consider trying a sample script for construction of a mesh from our +\ref tui_creating_meshes_page "TUI Scripts" section. + +\anchor evaluate_anchor +

            Evaluating mesh size

            + +After the mesh object is created and all hypotheses are assigned and +before \ref compute_anchor "Compute" operation, it is possible to +calculate the eventual mesh size. For this, select the mesh in +the Object Browser and from the \b Mesh menu select \b +Evaluate. The result of evaluation will be displayed in the following +information box: + +\image html mesh_evaluation_succeed.png + +\anchor preview_anchor +

            Previewing the mesh

            + +Before \ref compute_anchor "the mesh computation", it is also possible +to see the mesh preview. -\image html image32.png -
            "Create Mesh" button
            +For this, select the mesh in the Object Browser. From the \b Mesh menu +select \b Preview or click "Preview" button in the toolbar or activate +"Preview" item from the pop-up menu. -The following dialog box will appear: +\image html mesh_precompute.png +"Preview" button -\image html createmesh-inv.png -
          • -
          • For example, you need to mesh a 3d object. -\n First, type the name for your mesh in the "Name" box, by default, -it is "Mesh_1". Then select the object you wish to mesh in the Object -Browser and click the "Add" button. +Select 1D mesh or 2D mesh preview mode in the Preview dialog. -\image html image120.png -
            "Add" button
            +\image html preview_mesh_1D.png "1D mesh preview shows nodes computed on geometry edges" +
            +\image html preview_mesh_2D.png "2D mesh preview shows edge mesh elements, computed on geometry faces" -Now you can define 1d Algorithm and 1d Hypotheses, which will be -applied to the edges of your object. (Note that any object has edges, -even if their existence is not apparent, for example, a sphere has 4 -edges). Click the "Add Hypothesis" button to add a hypothesis. +Compute button computes the whole mesh. -\image html image121.png -
            "Add Hypothesis" button
            +When the Preview dialog is closed, the question about the storage of temporarily +created mesh elements appers: -Click the "Edit Hypothesis" button to define values for the -current hypothesis. +\image html preview_tmp_data.png -\image html image122.png -
            "Edit Hypothesis" button
            +These elements can be kept in the mesh. -The use of additional hypotheses is optional (i.e. you may leave -"None" in this box). -Proceed in the same way with 2d and 3d Algorithms and Hypotheses, note -that the choice of hypotheses depends on the algorithm. There must be -one Algorithm and one or several Hypotheses for each dimension of your -object, otherwise you will not get any mesh at all. Of course, if you -wish to mesh a face, which is a 2d object, you don't need to define 3d -Algorithm and Hypotheses. -\n In the Object Browser the structure of the new mesh will be -displayed as follows: +\anchor submesh_order_anchor +

            Changing submesh priority

            -\image html image88.jpg +If the mesh contains concurrent \ref constructing_submeshes_page "submeshes", +it is possible to change the priority of their computation, i.e. to +change the priority of applying algorithms to the shared sub-shapes of +the Mesh shape. -It contains: +To change submesh priority: + +Choose "Change submesh priority" from the Mesh menu or a pop-up +menu. The opened dialog shows a list of submeshes in the order of +their priority. + +There is an example of submesh order modifications of the Mesh created on a Box +shape. The main Mesh object: +
              +
            • 1D Wire discretisation with Number of Segments=20
            • +
            • 2D Triangle (Mefisto) with HypothesisMax Element Area +
            • +
            +The first submesh object Submesh_1 created on Face_1 is: +
              +
            • 1D Wire discretisation with Number of Segments=4
            • +
            • 2D Triangle (Mefisto) with HypothesisMaxElementArea=1200
            • +
            +The second submesh object Submesh_2 created on Face_2 is: +
              +
            • 1D Wire discretisation with Number of Segments=8
            • +
            • 2D Triangle (Mefisto) with HypothesisMaxElementArea=1200
            • +
            + +And the last submesh object Submesh_3 created on Face_3 is:
              -
            • a reference to the geometrical object on the basis of which the mesh has been constructed;
            • -
            • Applied hypotheses folder containing the references to the -hypotheses applied to the construction of the mesh;
            • -
            • Applied algorithms folder containing the references to the -algorithms applied to the construction of the mesh.
            • +
            • 1D Wire discretisation with Number of Segments=12
            • +
            • 2D Triangle (Mefisto) with HypothesisMaxElementArea=1200
            -There is an alternative way to create a mesh on an object simply by -clicking Assign a set of hypotheses button and selecting between -Automatic Tetrahedralization or Hexahedralization. The program will -automatically generate a 3D mesh with the most appropriate -settings. In the same way you can apply this functionality for meshing -2D objects, in which case 3D algorithms are not applied.
          • -
          • Now, when everything is ready, select your mesh in the Object -Browser. From the \b Mesh menu select \b Compute or click "Compute" button of the -toolbar. +The sub-meshes become concurrent if they share sub-shapes that can be +meshed with different algorithms (or different hypothesises). In the +example, we have three submeshes with concurrent algorithms, because +they have different hypotheses. + +The first mesh computation is made with: +
            +\image html mesh_order_123.png +"Mesh order SubMesh_1, SubMesh_2, SubMesh_3"
            +
            +\image html mesh_order_123_res.png +"Result mesh with order SubMesh_1, SubMesh_2, SubMesh_3 "
            + +The next mesh computation is made with: +
            +\image html mesh_order_213.png +"Mesh order SubMesh_2, SubMesh_1, SubMesh_3"
            +
            +\image html mesh_order_213_res.png +"Result mesh with order SubMesh_2, SubMesh_1, SubMesh_3 "
            + +And the last mesh computation is made with: +
            +\image html mesh_order_321.png +"Mesh order SubMesh_3, SubMesh_2, SubMesh_1"
            +
            \image html mesh_order_321_res.png +"Result mesh with order SubMesh_3, SubMesh_2, SubMesh_1 "
            + +As we can see, each mesh computation has a different number of result +elements and a different mesh discretisation on the shared edges (the edges +that are shared between Face_1, Face_2 and Face_3) + +Additionally, submesh priority (the order of applied algorithms) can +be modified not only in a separate dialog box, but also in +the Preview. This helps to preview different mesh results, +modifying the order of submeshes. +
            +\image html mesh_order_preview.png +"Preview with submesh priority list box"
            + +If there are no concurrent submeshes under the Mesh object, the user +will see the following information. +
            +\image html mesh_order_no_concurrent.png +"No concurrent submeshes detected"
            + + +\anchor compute_anchor +

            Computing the mesh

            + +It is equally possible to skip \ref evaluate_anchor "the Evaluation" +and \ref preview_anchor "the Preview" and to \b Compute the mesh after +the hypotheses are assigned. For this, select your mesh in +the Object Browser. From the \b Mesh menu select \b Compute or +click "Compute" button of the toolbar. \image html image28.png -
            "Compute" button
            +"Compute" button The Mesh Computation information box appears. @@ -89,33 +256,58 @@ failure is provided. \image html meshcomputationfail.png -After you select the error, Show Subshape button allows -visualizing the geometrical entity that causes it. +After you select the error, Show Sub-shape button allows +visualizing in magenta the geometrical entity that causes it. -\image html failed_computation.png "Example of the invalid input mesh" +\image html failed_computation.png +3D algorithm failed to compute mesh on a box shown using Show + Sub-shape button -\Note Mesh Computation Information box does not appear if you set + +\note Mesh Computation Information box does not appear if you set "Mesh computation/Show a computation result notification" preference to the "Never" value. This option gives the possibility to control mesh computation reporting. There are the following possibilities: always show information box, only if an error occurs or never. By default, the information box is always shown after mesh computation operation. -Publish Subshape button publishes the subshape, whose meshing +Publish Sub-shape button publishes the sub-shape, whose meshing failed, in GEOM component as a child of the mesh geometry, which allows analyzing the problem geometry and creating a submesh on it in order to locally tune hypotheses. -NOTE It is possible to define a 1D or a 2D mesh in a -python script and then use such submeshes in the construction of a 3D -mesh. For this, there exist two algorithms: Use existing edges and Use -existing faces. They are not entirely usable from the GUI, so a -mesh created using these algorithms should be exported into a python -script, edited and then imported into the GUi. +If a cause of failure is an invalid input mesh and the algorithm has +provided information on what mesh entities are bad Show bad Mesh +button appears in the dialog. Clicked, it shows bad mesh entities in +the Viewer in magenta. Sometimes the shown mesh entities are too small +or/and hidden by other mesh elements, to see them it can be helpful to +switch the mesh to Wireframe visualization mode or to switch off +visualization of faces and volumes (if any). -Consider trying a sample script for construction of a mesh from our -\ref tui_creating_meshes_page "TUI Scripts" section. -
          • -
        +\anchor use_existing_anchor +

        "Use existing edges" and "Use existing faces" algorithms

        + +It is possible to create an 1D or a 2D mesh in a python script +(using AddNode, AddEdge and AddFace commands) and +then use such sub-meshes in the construction of a 2D or a 3D mesh. For +this, there exist two algorithms: Use existing edges and Use + existing faces. Scenario of their usage is following. For +example, you want to use standard algorithms to generate 1D and 3D +meshes and to create 2D mesh by your python code. Then you +
          +
        • create a mesh object, assign an 1D algorithm,
        • +
        • invoke \b Compute command, which computes an 1D mesh,
        • +
        • assign Use existing faces and a 3D algorithm,
        • +
        • run your python code, which creates a 2D mesh,
        • +
        • invoke \b Compute command, which computes a 3D mesh.
        • +
        + +Consider trying a sample script demonstrating usage of +\ref tui_use_existing_faces "Use existing faces" algorithm for +construction of a 2D mesh using Python commands. + +\image html use_existing_face_sample_mesh.png + Mesh computed by \ref tui_use_existing_faces "the sample script" + shown in a Shrink mode. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/constructing_submeshes.doc b/doc/salome/gui/SMESH/input/constructing_submeshes.doc index a17569f7c..f300d6f50 100644 --- a/doc/salome/gui/SMESH/input/constructing_submeshes.doc +++ b/doc/salome/gui/SMESH/input/constructing_submeshes.doc @@ -1,41 +1,43 @@ /*! -\page constructing_submeshes_page Constructing submeshes +\page constructing_submeshes_page Constructing sub-meshes -Submesh is a mesh on a geometrical subobject created with algorithms +Sub-mesh is a mesh on a geometrical sub-object created with algorithms and/or hypotheses other than the algorithms and hypotheses assigned to the parent mesh on the parent object.

        -If a geometrical subobject belongs to several geometrical objects -having different meshes or submeshes, it will be meshed with the -hypotheses of a submesh of a lower dimension.
        -For example, a face of a box is meshed with a submesh using algorithms +If a geometrical sub-object belongs to several geometrical objects +having different meshes or sub-meshes, it will be meshed with the +hypotheses of a sub-mesh of a lower dimension.
        +For example, a face of a box is meshed with a sub-mesh using algorithms and hypotheses other than the parent mesh on the whole box. The face and the box share four edges, which will be meshed with algorithms and -hypotheses of the submesh on the face, because the face is a 2D object +hypotheses of the sub-mesh on the face, because the face is a 2D object while the box is a 3D object.
        If the dimensions are the same, an arbitrary algorithm/hypothesis will be used. This means that an edge shared by two faces each having - its own different submesh, will be meshed using algorithms and - hypotheses of any of the two, chosen randomly.
        + its own different sub-mesh, will be meshed using algorithms and + hypotheses of any of the two, chosen randomly. This indeterminacy can + be fixed by defining \ref submesh_order_anchor "Sub-mesh priority". +
        -\n Construction of a submesh consists of: +\n Construction of a sub-mesh consists of:
          -
        • Selecting a mesh which will encapsulate your submesh
        • +
        • Selecting a mesh which will encapsulate your sub-mesh
        • Selecting a geometrical object for meshing
        • Applying one or several previously described \ref about_hypo_page "hypotheses" and \ref basic_meshing_algos_page "meshing algorithms" which will be used -at computation of this submesh
        • +at computation of this sub-mesh
        -
        To construct a submesh: +
        To construct a sub-mesh: \par -From the \b Mesh menu select Create Submesh or click "Create +From the \b Mesh menu select Create Sub-mesh or click "Create Sum-mesh" button in the toolbar. \image html image33.gif -
        "Create Submesh" button
        +
        "Create Sub-mesh" button
        \par The following dialog box will appear: @@ -45,11 +47,11 @@ The following dialog box will appear: \par It allows to define the \b Name, the parent \b Mesh and the \b Geometry (e.g. a face if the parent mesh has been built on box) of the -submesh. You can define algorithms and hypotheses in the same way as +sub-mesh. You can define algorithms and hypotheses in the same way as in \ref constructing_meshes_page "Create mesh" menu. \par -In the Object Browser the structure of the new submesh will be +In the Object Browser the structure of the new sub-mesh will be displayed as follows: \image html image10.jpg @@ -57,14 +59,14 @@ displayed as follows: \par It contains:
          -
        • a reference to the geometrical object on the basis of which the submesh has been constructed;
        • +
        • a reference to the geometrical object on the basis of which the sub-mesh has been constructed;
        • Applied hypotheses folder containing the references to the -hypotheses applied to the construction of the submesh;
        • +hypotheses applied to the construction of the sub-mesh;
        • Applied algorithms folder containing the references to the -algorithms applied to the construction of the submesh.
        • +algorithms applied to the construction of the sub-mesh.

        See Also a sample TUI Script of a -\ref tui_construction_submesh "Construct Submesh" operation. +\ref tui_construction_submesh "Construct Sub-mesh" operation. */ diff --git a/doc/salome/gui/SMESH/input/convert_to_from_quadratic_mesh.doc b/doc/salome/gui/SMESH/input/convert_to_from_quadratic_mesh.doc index 072a91f7a..66f850664 100644 --- a/doc/salome/gui/SMESH/input/convert_to_from_quadratic_mesh.doc +++ b/doc/salome/gui/SMESH/input/convert_to_from_quadratic_mesh.doc @@ -2,14 +2,20 @@ \page convert_to_from_quadratic_mesh_page Convert to/from Quadratic Mesh -\n This functionality allows you to transtorm standard meshes to -quadratic and vice versa. See \ref adding_quadratic_elements_page "Adding quadratic elements" +\n This functionality allows transforming standard meshes (or +sub-meshes) to quadratic and vice versa. +See \ref adding_quadratic_elements_page "Adding quadratic elements" for more information about quadratic meshes. +Note that conversion of a sub-mesh most probably will +produce a non-conformal mesh. Elements on the boundary between +quadratic and linear sub-meshes become (or remain) quadratic. To produce a conversion:
          -
        1. From the Modification menu choose the Convert to/from Quadratic -Mesh item, or click "Convert to/from quadratic" button in the +
        2. Select a mesh or a sub-mesh in the Object Browser or in the +Viewer.
        3. +
        4. From the Modification menu choose Convert to/from Quadratic +Mesh item , or click "Convert to/from quadratic" button in the toolbar. \image html image154.png @@ -20,11 +26,17 @@ The following dialog box will appear: \image html convert.png
        5. -
        6. In this dialog box you should select: +
        7. In this dialog box specify:
            -
          • if you wish to convert standard mesh to quadratic or quadratic to standard;
          • -
          • if you wish to place medium nodes of the quadratic mesh on the geometry (meshed object).
          • +
          • if it is necessary to convert a standard mesh to quadratic or a quadratic +mesh to standard. Note that the choice is available only if the selected mesh +(or sub-mesh) contains both quadratic and linear elements, else the +direction of conversion is selected automatically.
          • + +
          • if it is necessary to place medium nodes of the quadratic mesh on the +geometry (meshed object). This option is relevant for conversion to +quadratic provided that the mesh is based on a geometry (not imported from file).
          \image html image156.gif @@ -37,4 +49,6 @@ The following dialog box will appear:
        8. Click the \b Apply or \b OK button.
        -*/ \ No newline at end of file +
        See Also a sample TUI Script of a \ref tui_quadratic "Convert to/from quadratic" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/copy_mesh.doc b/doc/salome/gui/SMESH/input/copy_mesh.doc new file mode 100644 index 000000000..2c65ab8d0 --- /dev/null +++ b/doc/salome/gui/SMESH/input/copy_mesh.doc @@ -0,0 +1,56 @@ +/*! + +\page copy_mesh_page Copy Mesh + +\n A mesh can be created by copying a part of or the whole other mesh. + +To make a copy of a mesh: + +\par +From the \b Mesh menu select Copy Mesh or click "Copy Mesh" +button in the toolbar. + +\image html copy_mesh_icon.png +
        "Copy Mesh" button
        + +\par +The following dialog box will appear: + +\image html copy_mesh_dlg.png + +\par +In the dialog: +
          +
        • specify the part of mesh to copy: + +
            +
          • Select the whole mesh, submesh or group by mouse activating +this checkbox; or
          • +
          • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
          • +
          • input the Source Element IDs directly in this field. The +selected elements will be highlighted in the viewer; or
          • +
          • apply Filters. Set filter button allows to apply a filter +to the selection of elements. See more about filters in the \ref +selection_filter_library_page "Selection filter library" page.
          • +
          +
        • + +
        • specify the New Mesh Name;
        • + +
        • specify the conditions of copying: +
            +
          • activate Generate groups checkbox to copy the groups of +elements of the source mesh to the newly created mesh.
          • +
          • activate Preserve IDs of elements checkbox to keep +the IDs of new nodes and elements the same as the IDs of source nodes +and elements.
          • +
          +
        • + +
        • Click \b Apply or Apply and Close button to confirm the operation.
        • +
        + +See Also a sample +\ref tui_copy_mesh "TUI Example of mesh copying." +*/ diff --git a/doc/salome/gui/SMESH/input/create_groups_from_geometry.doc b/doc/salome/gui/SMESH/input/create_groups_from_geometry.doc new file mode 100755 index 000000000..a09272c98 --- /dev/null +++ b/doc/salome/gui/SMESH/input/create_groups_from_geometry.doc @@ -0,0 +1,17 @@ +/*! + +\page create_groups_from_geometry_page Create Groups from Geometry + +To use this operation, select in the \b Mesh menu Create Groups from Geometry. + +\image html create_groups_from_geometry.png + +This operation allows creating on a selected geometry several groups consisting of +elements of all types. + +The group names will be the same as the names of geometrical objects. +The Type of group of mesh elements is defined automatically by the nature of +the Geometric object. + + +*/ diff --git a/doc/salome/gui/SMESH/input/creating_groups.doc b/doc/salome/gui/SMESH/input/creating_groups.doc index 0e89b980d..966e091fe 100644 --- a/doc/salome/gui/SMESH/input/creating_groups.doc +++ b/doc/salome/gui/SMESH/input/creating_groups.doc @@ -2,12 +2,13 @@ \page creating_groups_page Creating groups -\n In MESH you can create groups of elements of different types. To -create a group of elements in the \b Mesh menu select Create -Group. -\n To create any group you should define the following: +\n In MESH you can create a group of elements of a certain type. The +contents of the group can be defined in different ways. To create a group, in the \b +Mesh menu select Create Group item (also available in the +context menu of the mesh).
        +To create a group of any type you should define the following:
          -
        • Mesh - the name of the mesh whose elements will form your +
        • Mesh - the mesh whose elements will form your group. You can select your mesh in the Objet Browser or in the 3D viewer.
        • Elements Type - set of radio buttons allows to select the type of @@ -19,131 +20,121 @@ elements which will form your group:
        • Volumes
      7. Name field allows to enter the name of your new group.
      8. +
      9. Color - allows to assign to the group a certain color, for +example, defining boundary conditions. The chosen color is used to +display the elements of the group. The color attribute of the group is +not persistent, it is lost if you save and then load the study from +the file.
      10. -SALOME Platform distinguishes between the two Group types: -Standalone Group and Group on Geometry. +SALOME Platform distinguishes between the three Group types: +Standalone Group, Group on Geometry and Group on Filter. -

        Standalone Group

        +\anchor standalone_group

        "Standalone Group"

        -Standalone Group consists of mesh elements, which you can define in -two possible ways. +Standalone Group contains a list of mesh elements, which you can define in +the following ways:
          -
        • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted. After -that click the \b Add button and the ID of this element will be added to -the list.
        • -
        • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements of your group. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
        • +
        • By adding all entities of the chosen type existing in the + mesh. For this, turn on the Select All check box. In this mode + all controls, which allow selecting the entities in other ways, are + disabled.
        • +
        • By applying the Filter. The Set filter button allows to + define the filter for selection of the elements for your group. See more + about filters on the + \ref selection_filter_library_page "Selection filter library" page.
          + If the Enable manual edition check box is turned off, the + filter entirely defines the group contents. In this mode, the filter is + applied to all elements of the mesh. If there are no entities + corresponding to the filter, the \b Apply button is disabled.
          + If the Enable manual edition check box is turned on, the defined + filter can be used to for selection of entities for the group.
        • +
        • By choosing entities manually with the mouse in the 3D Viewer. For + this, turn on the Enable manual edition check box. You can + click on an element in the 3D viewer and it will be highlighted. After + that click the \b Add button and the ID of this element will be + added to the list.
        • +
        • By adding entities from either a submesh or an existing + group. For this, turn on the Enable manual edition check + box. Select from set of fields allows to select a submesh or + a group of the appropriate type.
        • +
        +In the manual edition mode you can +
          +
        • click the \b Remove button to remove the selected elements from the list
        • +
        • click the Sort List button to sort the list of IDs of +mesh elements.
        -To remove a selected element or elements from the list click the -\b Remove button. The Sort List button allows to sort the list of IDs of -mesh elements. -\n Select from set of fields allows to choose a submesh or an existing -group whose elements of the previously defined type will be added to -the list of elements which will form your group. -\n Color - allows to assign to the group a certain color, for -example, defining boundary conditions. This feature introduces a -useful element of preprocessing in Mesh module. Note that Color attribute defines -the colors used for the display of the elements of the group. -\n Warning The Med Color group interface may change in future versions of Salome. - \image html creategroup.png + +For example, to create a new group containing all faces of an +existing group and some other faces selected in the viewer: +
          +
        • Select the \b Face type of entities and input the name of the new group.
        • +
        • Check the \b Group checkbox in Select From group.
        • +
        • Select the existing group of faces in the object browser or in the viewer
        • +
        • Click \b Add in \b Content group. Id Elements list will be filled +with IDs of faces belonging to the exising group.
        • +
        • Select other faces in the viewer.
        • +
        • Click \b Add in \b Content group.
        • +
        • Click \b Apply button to create the new group.
        • +
        + +Please note that the new group does not have references to the source +group. It contains only the list of face IDs. So if the source group +is changed, the new one is not updated accordingly. + + \image html image130.gif -
        In this picture the brown cells belong to a group defined manually.
        +
        In this picture the brown cells belong to a group defined + manually.
        \image html image131.gif -
        In this picture the brown cells belong to the group defined by the criterion -Taper > 0.
        +
        In this picture the brown cells belong to the group defined by + the criterion Taper > 0.
        See Also a sample TUI Script of a \ref tui_create_standalone_group "Create a Standalone Group" operation. -

        Group on Geometry

        - -To create a group on geometry check Group on geometry in the \b Group -\b type field. Group on geometry contains the elements of a certain type -belonging to the selected geometrical object. To define a group select -in the Objet Browser or in the 3D viewer a geometrical object from -which the elements will be taken. After confirmation of the operation -a new group of mesh elements will be created. -\image html a-creategroup.png +\anchor group_on_geom

        "Group on Geometry"

        -

        -To create multiple groups on geometry of both nodes and elements of -any type at once, in the \b Mesh menu select Create Groups from -Geometry.
        -Group names are same as those of geometrical objects. -Type of group of mesh elements is defined automatically by -Geometrical object nature. +To create a group on geometry check Group on geometry in the \b +Group \b type field. The group on geometry contains the elements +of a certain type generated on the selected geometrical object. Group +contents are dynamically updated if the mesh is modified.
        +To define a group, select in the Objet Browser or in the 3D viewer a +geometrical object from which the elements will be taken. After +confirmation of the operation a new group of mesh elements will be +created. -\image html create_groups_from_geometry.png +\image html a-creategroup.png \image html image132.gif -
        In this picture the cells which belong to a certain face are -selected in green.
        +
        In this picture the cells which belong to a certain + geometrical face are selected in green.
        See Also a sample TUI Script of a \ref tui_create_group_on_geometry "Create a Group on Geometry" operation. -

        Creation of groups using existing groups and sub-meshes.

        - -Application provides possibility to create new standalone groups using existing standalone groups, groups on geometry and sub-meshes. This functionality is implemented using "Select from" group box of "Create group" dialog box described above. - -This functionality is described on the example of creating new group from existing standalone groups and groups on geometry. - -Imagine there are group G1. It can be standalone group or group on geometry. -To create group G2 containing all entities of group G1 and a faces graphically selected in 3D view following steps can be performed: -
          -
        • User opens "Create group" dialog box.
        • -
        • The user specifies "Face" type of entities and "G2" name of group.
        • -
        • The user checks "Group" check-box of "Select From" group box.
        • -
        • The user selects G1 group in object browser or 3D view.
        • -
        • The user presses "Add" push button of "Content" group box. "Id Elements" list-box is filled with identifiers of faces belonging to group G1.
        • -
        • The user selects other faces in 3D view.
        • -
        • The user presses "Apply" button. System creates group G2.
        • -
        - -Please note that group G2 does not have a references to source group G1. It contains list of faces identifiers only. So if G1 group will be changed group G2 will remain unmodified. - -
        -\anchor gui_create_dim_group -

        Creating groups of entities from existing groups of superior dimensions

        - -Application provides possibility for creating groups of entities from existing groups of superior dimensions. For example, it is possible to create group of nodes using list of existing groups of faces. - -To create groups of entities from existing groups of superior dimensions, in the \b Mesh menu select Group of underlying entities.
        - -The following dialog box will appear: - -\image html dimgroup_dlg.png +\anchor group_on_filter

        "Group on Filter"

        -In this dialog box you should specify the name of the resulting group, types of entities and set of source groups. +To create a group on filter check Group on filter in the +Group type field. The group on filter contains the elements +of a certain type satisfying the defined filter. Group contents are +dynamically updated if the mesh is modified.
        To define a group, +click the Set filter button and define criteria of the +filter in the opened dialog. After confirmation of the operation a +new group of mesh elements will be created. See more about filters on +the \ref selection_filter_library_page "Selection filter library" page. -For example, we have two source Volume groups illustrated on the figure below - -\image html dimgroup_src.png -
        Source groups
        - -In this case we obtain following results for Faces, Edges and Nodes. - -\image html dimgroup_2d.png -
        Faces
        - -\image html dimgroup_1d.png -
        Edges
        - -\image html dimgroup_0d.png -
        Nodes
        +\image html creategroup_on_filter.png See Also a sample TUI Script of a -\ref tui_create_dim_group "Creating groups of entities from existing groups of superior dimensions" -operation. -*/ \ No newline at end of file +\ref tui_create_group_on_filter "Create a Group on Filter" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/cut_mesh_by_plane.doc b/doc/salome/gui/SMESH/input/cut_mesh_by_plane.doc new file mode 100644 index 000000000..9befdbbeb --- /dev/null +++ b/doc/salome/gui/SMESH/input/cut_mesh_by_plane.doc @@ -0,0 +1,55 @@ +/*! + +\page cut_mesh_by_plane_page Cut a tetrahedron mesh by a plane + +\n MeshCut works only with MED files and produces MED files, and is a standalone program. +It can be used either directly from a command shell outside SALOME, or with a GUI interface in SMESH, +provided in a python plugin that needs to be installed in your SALOME application. + +\n MeshCut allows to cut a mesh constituted of linear tetrahedrons by a plane. +\n The tetrahedrons intersected by the plane are cut and replaced by elements of various types, +(tetrahedron, pyramid, pentahedron). + +
        +\anchor meshcut_standalone +

        Using MeshCut as a standalone program, outside SALOME

        + +\n MeshCut is a standalone program, reading and producing med files. +\n +\n Syntax: +\code +MeshCut input.med output.med resuMeshName aboveGroup belowGroup nx ny nz px py pz T +\endcode + +\n +\n where: +\n input.med = name of the original mesh file in med format +\n output.med = name of the result mesh file in med format +\n resuMeshName = name of the result mesh +\n aboveGroup = name of the group of volumes above the cut plane +\n belowGroups = name of the group of volumes below the cut plane +\n nx ny nz = vector normal to the cut plane +\n px py pz = a point of the cut plane +\n T = 0 < T < 1 : vertices of a tetrahedron are considered as belonging to +\n the cut plane if their distance from the plane is inferior to L*T, +\n where L is the mean edge size of the tetrahedron + +
        +\anchor meshcut_plugin +

        Using MeshCut inside SALOME

        + +When the MeshCut plugin is installed, it can be found in the Mesh menu, sub-menu SMESH_plugins. +\n If the plugin is not installed, the file meshcut_plugin.py is in +SMESH installation in subdirectory bin/salome/meshcut_plugin.py. + +\n If there are already plugins defined in a smesh_plugins.py file, + this file should be added at the end. + if not, copied as ${HOME}/Plugins/smesh_plugins.py or ${APPLI}/Plugins/smesh_plugins.py + or in ${PLUGINPATH} Directory. + +From the Mesh menu, sub-menu SMESH_plugins, choose "MeshCut" item +The following dialog box will appear: +\image html meshcut_plugin.png "MeshCut Plugin dialog box" + +See above for the meaning of the parameters. +*/ diff --git a/doc/salome/gui/SMESH/input/deleting_groups.doc b/doc/salome/gui/SMESH/input/deleting_groups.doc index 21eea88df..86addcf5d 100644 --- a/doc/salome/gui/SMESH/input/deleting_groups.doc +++ b/doc/salome/gui/SMESH/input/deleting_groups.doc @@ -1,8 +1,8 @@ /*! -\page deleting_groups_page Deleting Groups +\page deleting_groups_page Deleting groups with content -\n To delete a group in the Main Menu select Mesh -> Delete Groups and +\n To delete groups and their content in the Main Menu select Modification -> Remove -> Delete groups with Contents and select one or several groups you wish to delete in the 3D viewer or in the Object Browser. \n The selected groups will be listed in Delete groups with contents @@ -16,4 +16,4 @@ elements
        . To delete a group and leave its elements intact, right-click on the group in the Object Browser and select \b Delete in the pop-up menu or select the group and choose Edit -> Delete in the Main Menu. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/double_elements_control.doc b/doc/salome/gui/SMESH/input/double_elements_control.doc new file mode 100644 index 000000000..63ee0e762 --- /dev/null +++ b/doc/salome/gui/SMESH/input/double_elements_control.doc @@ -0,0 +1,14 @@ +/*! + +\page double_elements_page Double edge, Double faces and Double volumes + +These mesh quality controls highlight the mesh elements basing on the same set of nodes. + +\image html double_faces.png + +In this picture some faces are coincident after copying all elements +with translation with subsequent Merge of nodes. + +\sa A sample TUI Script of a \ref filter_double_elements "Filters of Double Elements". + +*/ diff --git a/doc/salome/gui/SMESH/input/double_nodes_control.doc b/doc/salome/gui/SMESH/input/double_nodes_control.doc new file mode 100644 index 000000000..75fd2928b --- /dev/null +++ b/doc/salome/gui/SMESH/input/double_nodes_control.doc @@ -0,0 +1,16 @@ +/*! + +\page double_nodes_control_page Double nodes + +This mesh quality control highlights the nodes which are coincident +with other nodes (within a given tolerance). Distance at which two +nodes are considered coincident is defined by "Quality Controls/Double +nodes tolerance" preference. + +\image html double_nodes.png + +In this picture some nodes are coincident after copying all elements with translation. + +\sa A sample TUI Script of a \ref tui_double_nodes_control "Double Nodes" filter. + +*/ diff --git a/doc/salome/gui/SMESH/input/double_nodes_page.doc b/doc/salome/gui/SMESH/input/double_nodes_page.doc new file mode 100644 index 000000000..a8a93d1f9 --- /dev/null +++ b/doc/salome/gui/SMESH/input/double_nodes_page.doc @@ -0,0 +1,72 @@ +/*! + +\page double_nodes_page Duplicate Nodes + +\n This operation allows to duplicate nodes of your mesh, which can be +useful to emulate a crack in the model. +Duplication consists in replacement of an existing mesh element by another one. +Lower level elements of the duplicated ones are cloned automatically. + +To duplicate nodes: +
          +
        1. From the \b Modification menu choose \b Transformation -> \b Duplicate +\b Nodes item or click "Duplicate Nodes" button in the toolbar. +
          +\image html duplicate_nodes.png "Duplicate Nodes button" +
        2. +
        3. Check in the dialog box one of two radio buttons corresponding to +the type of nodes duplication operation you would like to perform.
        4. +
        5. Fill the other fields available in the dialog box (depends on the chosen + operation mode).
        6. +
        7. Click the \b Apply or Apply and Close button to perform the operation of nodes + duplication.
        8. +
        + +\n "Duplicate Nodes" dialog has two working modes: +
          +
        • \ref mode_without_elem_anchor "Without the duplication of border elements"
        • +
        • \ref mode_with_elem_anchor "With the duplication of border elements"
        • +
        + +
        +\anchor mode_without_elem_anchor +

        Without duplication of border elements

        + +In this mode the dialog looks like: + +\image html duplicate01.png + +Parameters to be defined in this mode: +
          +
        • Group of nodes to duplicate (mandatory): these nodes will be duplicated.
        • +
        • Group of elements to replace nodes with new ones (optional): the duplicated nodes + will be associated with these elements.
        • +
        • Construct group with newly created nodes option (checked by default): + if checked - the group with just created nodes will be built.
        • +
        + +
        +\anchor mode_with_elem_anchor +

        With duplication of border elements

        + +In this mode the dialog looks like: + +\image html duplicate02.png + +Parameters to be defined in this mode: +
          +
        • Group of elements to duplicate (mandatory): these elements will be duplicated.
        • +
        • Group of nodes at not to duplicate (optional): group of nodes at crack bottom + which will not be duplicated.
        • +
        • Group of elements to replace nodes with new ones (mandatory): the duplicated nodes + will be associated with these elements.
        • +
        • Construct group with newly created elements option (checked by default): + if checked - the group of just created elements will be built.
        • +
        • Construct group with newly created nodes option (checked by default): + if checked - the group of just created nodes will be built.
        • +
        + + +
        See Also a sample TUI Script of a \ref tui_duplicate_nodes "Duplicate nodes" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/editing_groups.doc b/doc/salome/gui/SMESH/input/editing_groups.doc index b5031468a..ca9b7a222 100644 --- a/doc/salome/gui/SMESH/input/editing_groups.doc +++ b/doc/salome/gui/SMESH/input/editing_groups.doc @@ -2,7 +2,7 @@ \page editing_groups_page Editing groups -\n To edit an existing group of elements: +To edit an existing group of elements:
        1. Select your group in the Object Browser and in the \b Mesh menu click the Edit Group item or "Edit Group" button in the toolbar.
        2. @@ -14,28 +14,34 @@ The following dialog box will appear: \image html editgroup.png -In this dialog box you can modify the name of your group and add or -remove the elements forming it. For more information see +In this dialog box you can modify the name and the color of your group +despite of its type. You can add or remove the elements forming a +standalone group. You can change criteria of the filter of +the group on filter. For more information see \ref creating_groups_page "Creating Groups" page. -
        3. Click the \b Apply or Apply and Close button to confirm modification of the -group.
        4. + +
        5. Click the \b Apply or Apply and Close button to confirm +modification of the group.
        -\n To convert an existing group on geometry into standalone group -of elements and modify: +\anchor convert_to_standalone +To convert an existing group on geometry or a group on filer into +a standalone group of elements and modify:
          -
        1. Select your group on geometry in the Object Browser and in the \b Mesh menu click -the Edit Group as Standalone item.
        2. +
        3. Select your group on geometry (or your group on filter) in the +Object Browser and in the \b Mesh menu click the Edit Group as +Standalone item.
        4. \image html image74.gif
          "Edit Group as Standalone" button
          -The group on geometry will be converted into standalone group and can -be modified as group of elements +The selected group will be converted into a standalone group and +its contents can be modified. +
        5. Click the \b Apply or Apply and Close button to confirm modification of the group.
        6. +
        -
        See Also a sample TUI Script of an -\ref tui_edit_group "Edit Group" operation. +\sa A sample TUI Script of an \ref tui_edit_group "Edit Group" operation. */ diff --git a/doc/salome/gui/SMESH/input/extrusion.doc b/doc/salome/gui/SMESH/input/extrusion.doc index ea25158f1..1b3438084 100644 --- a/doc/salome/gui/SMESH/input/extrusion.doc +++ b/doc/salome/gui/SMESH/input/extrusion.doc @@ -16,38 +16,45 @@ and Hexahedron solids respectively. \image html image91.png
        "Extrusion" button
        -The following dialog box will appear: +The following dialog common for line and planar elements will appear: \image html extrusionalongaline1.png \image html extrusionalongaline2.png + -
      11. In this dialog box you should select: +
      12. In this dialog:
          -
        • the type of elements which will be extruded (1D or 2D),
        • -
        • specify the IDs of the elements which will be extruded by -selecting them in the 3D viewer or select the whole mesh or -submesh, +
        • select the type of elements which will be extruded (0D, 1D or 2D),
        • +
        • specify the IDs of the elements which will be extruded:
            -
          • Check on Select the whole mesh, submesh or group option -
          • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
          • -
          • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
          • +
          • Select the whole mesh, submesh or group activating this +checkbox; or
          • +
          • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
          • +
          • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
          • +
          • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
        • +
        • If the Extrude to Distance radio button is selected
        • +
          • specify the distance at which the elements will be extruded,
          • -
          • specify the vector along which the elements will be extruded,
          • -
          • number of steps.
          -
        • Generate Groups checkbox allows copying the groups of +
        • If the Extrude Along Vector radio button is selected
        • +
            +
          • specify the coordinates of the vector along which the elements will be extruded, or select the face (the normal to the face will define the vector)
          • +
          • specify the distance of extrusion along the vector,
          • +
          +
        • specify the number of steps;
        • +
        • activate Generate Groups checkbox if it is necessary to copy the groups of elements of the source mesh to the newly created one.
        • +
        -
      13. Click the \b Apply or \b OK button.
      14. +
      15. Click \b Apply or Apply and Close button to confirm the operation.
      \image html image77.jpg "The mesh with an edge selected for extrusion" diff --git a/doc/salome/gui/SMESH/input/extrusion_along_path.doc b/doc/salome/gui/SMESH/input/extrusion_along_path.doc index d35d23315..e391111d8 100644 --- a/doc/salome/gui/SMESH/input/extrusion_along_path.doc +++ b/doc/salome/gui/SMESH/input/extrusion_along_path.doc @@ -4,13 +4,14 @@ \n In principle, Extrusion along a path works in the same way as \b Extrusion, the main difference is that we define not a vector, -but a path of extrusion which must be a meshed edge. To get an idea of -how this algorithm works, examine several examples, starting from the -most simple case of extrusion along a straight edge. In all examples -the same mesh will be extruded along different paths and with -different parameters. This sample 2D mesh has two quadrangle faces and -seven edges. Look at the picture, where white digits are the node -numbers and green are the element numbers: +but a path of extrusion which must be a 1D mesh or 1D sub-mesh. +To get an idea of how this algorithm works, examine several examples, +starting from the most simple case of extrusion along a straight edge. +In the examples the sample mesh will be extruded along different +paths and with different parameters. +This 2D mesh has two quadrangle faces and seven edges. Look +at the picture, where white digits are the node numbers and green +are the element numbers: \image html mesh_for_extr_along_path.png @@ -40,29 +41,17 @@ been selected as Start node. \image html curvi_angles_after.png
      The same, but using angles {45, 45, 45, 0, -45, -45, -45}
      -

      Extrusion along a sub-mesh

      +

      Extrusion of a 2D face along a mesh built on a wire

      -In this example the path mesh has been built on a wire (polyline with -six edges). The first edge of the wire was used as Shape (edge), node -#1 as Start node. The angles have been defined as {10, 10, 10}. The -middle edge (#4) of the initial mesh has been extruded. +In this example the path mesh has been built on a wire containing 3 +edges. Node 1 is a start node. Linear angle variation by 180 degrees +has also been applied. -\image html edge_wire_before.png +\image html extr_along_wire_before.png +
      Meshed wire
      -\image html edge_wire_after.png - -

      Extrusion of 2d elements along a sub-mesh

      - -This extrusion bases on the same path mesh as in the previous example -but the third edge of the wire was set as Shape (edge) and node -#4 as Start node. Please note, that the extrusion has been done -in direction from node #4 to node #3, i.e. against the wire -direction. In this example both faces of the initial mesh have been -extruded. - -\image html edge_wire_3d_before.png - -\image html edge_wire_3d_after.png +\image html extr_along_wire_after.png +
      The resulting extrusion

      Extrusion of 2d elements along a closed path

      @@ -88,36 +77,36 @@ path
      item or click "Extrusion along a path" button in the toolbar. \image html image101.png
      "Extrusion along a path" button
      -The following dialog box will appear: +The following dialog common for line and planar elements will appear: \image html extrusion1.png - -\image html extrusion2.png
    5. -
    6. In the dialog box you should: +
    7. In this dialog:
      • select the type of elements which will be extruded (1D or 2D),
      • specify the IDs of the elements which will be extruded +
          -
        • Check on Select the whole mesh, submesh or group option -
        • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
        • -
        • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
        • +
        • Select the whole mesh, submesh or group activating this +checkbox; or
        • +
        • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
        • +
        • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
        • +
        • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
        +
      • define the Path along which the elements will be extruded, \n Path definition consists of several elements:
          -
        • \b Mesh - containing a 1D sub-mesh on the edge, along which proceeds the extrusion
        • -
        • Shape (edge) - as the mesh can be complex, the edge is used to define the sub-mesh for the path
        • -
        • Start node - the first or the last node on the edge. It is used to define the direction of extrusion
        • +
        • Mesh or submesh - 1D mesh or sub-mesh, along which proceeds the extrusion
        • +
        • Start node - the start node. It is used to define the direction of extrusion
      • -
      • Generate Groups checkbox allows copying the groups of +
      • activate Generate Groups checkbox if it is necessary to copy the groups of elements of the source mesh to the newly created one.
    8. @@ -149,7 +138,9 @@ At each step the shape will be rotated by angle/nb. of steps. -
    9. Click the \b Apply or \b OK button. Mesh edges will be extruded into + +
    10. Click \b Apply or Apply and Close button to confirm the operation. +Mesh edges will be extruded into faces, faces into volumes. The external surface of the resulting 3d mesh (if faces have been extruded) is covered with faces, and corners with edges. If the path is closed, the resulting mesh can contain diff --git a/doc/salome/gui/SMESH/input/find_element_by_point.doc b/doc/salome/gui/SMESH/input/find_element_by_point.doc new file mode 100644 index 000000000..191aa4307 --- /dev/null +++ b/doc/salome/gui/SMESH/input/find_element_by_point.doc @@ -0,0 +1,41 @@ +/*! + +\page find_element_by_point_page Find Element by Point + +\n This functionality allows you to find all mesh elements to which +belongs a certain point. + +To find the elements: +
        +
      1. Select a mesh or a group
      2. +
      3. Select from the Mesh menu or from the context menu the Find +Element by Point item. + +\image html findelement3.png +
        "Find Element by Point" button
        + +The following dialog box will appear: + +\image html findelement2.png + +
      4. +
      5. In this dialog box you should select: + +
          +
        • the coordinates of the point;
        • +
        • the type of elements to be found; it is also possible to find elements +of all types related to the reference point. Choose type "All" to find +elements of any type except for nodes and 0D elements.
        • +
        + +
      6. +
      7. Click the \b Find button. +
      + +\image html findelement1.png +
      The reference point and the related elements.
      + + +
      See Also a sample TUI Script of a \ref tui_find_element_by_point "Find Element by Point" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/free_faces.doc b/doc/salome/gui/SMESH/input/free_faces.doc index 4a87b992a..d35caa9ac 100644 --- a/doc/salome/gui/SMESH/input/free_faces.doc +++ b/doc/salome/gui/SMESH/input/free_faces.doc @@ -2,16 +2,17 @@ \page free_faces_page Free faces -\n This mesh quality control highlights faces which are connected -less than to two mesh volume elements. Free faces are shown with a color differs from -the color of shared faces. +This mesh quality control highlights the faces connected to +less than two mesh volume elements. The free faces are shown with a +color different from the color of shared faces. \image html free_faces.png -
      In this picture some volume mesh element are removed as -a result some faces become connected only to one -volume. i.e. become free. -
      See Also a sample TUI Script of a -\ref tui_free_faces "Free Faces quality control" operation. +In this picture some volume mesh elements have been removed, as +a result some faces became connected only to one +volume. i.e. became free. + +\sa A sample TUI Script of a \ref tui_free_faces "Free Faces quality control" +operation. */ diff --git a/doc/salome/gui/SMESH/input/free_nodes.doc b/doc/salome/gui/SMESH/input/free_nodes.doc index 13f314309..8a06fe3de 100644 --- a/doc/salome/gui/SMESH/input/free_nodes.doc +++ b/doc/salome/gui/SMESH/input/free_nodes.doc @@ -2,15 +2,15 @@ \page free_nodes_page Free nodes -\n This mesh quality control highlights nodes which are not connected -to any mesh element. Free nodes are shown with a color differs from -the color of nodes. +This mesh quality control highlights the nodes which are not connected +to any mesh element. \image html free_nodes.png -
      In this picture some nodes don't connected to a mesh element as -a result of deleting elements and adding several isolated nodes. -
      See Also a sample TUI Script of a -\ref tui_free_nodes "Free Nodes quality control" operation. +In this picture some nodes are not connected to any mesh +element after deleting some elements and adding several isolated nodes. + +\sa A sample TUI Script of a \ref tui_free_nodes "Free Nodes quality control" +operation. */ diff --git a/doc/salome/gui/SMESH/input/generate_flat_elements.doc b/doc/salome/gui/SMESH/input/generate_flat_elements.doc new file mode 100644 index 000000000..7cfdbf90b --- /dev/null +++ b/doc/salome/gui/SMESH/input/generate_flat_elements.doc @@ -0,0 +1,12 @@ +/*! + +\page generate_flat_elements_page Generate flat elements on group boundaries or on faces + +\n These functionalities, used in some mechanics calculations, +allow to generate flat volume elements on the boundaries of a list +of groups of volumes, or on a list of groups of faces. +\n These functionalities are only available in python scripts. + +
      See a sample TUI Script of \ref tui_double_nodes_on_group_boundaries "Generate flat elements" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/ghs3d_hypo.doc b/doc/salome/gui/SMESH/input/ghs3d_hypo.doc deleted file mode 100644 index 414e342fa..000000000 --- a/doc/salome/gui/SMESH/input/ghs3d_hypo.doc +++ /dev/null @@ -1,69 +0,0 @@ -/*! - -\page ghs3d_hypo_page GHS3D Parameters hypothesis - -\n GHS3D Parameters hypothesis works only with Tetrahedron (GHS3D) -algorithm. This algorithm is a commercial software. - -\image html ghs3d_parameters_basic.png - -
        -
      • Name - allows to define the name of the hypothesis (GHS3D -Parameters by default).
      • - -
      • To mesh holes - if checked, the algorithm will -create mesh in the holes inside a solid shape, else only the outermost -shape will be meshed. Volumic elements created within holes are bound -to the solid.
      • - -
      • Optimization level - allows choosing the required -optimization level: none, light, medium or strong. Higher level of -optimisation provides better mesh, but can be time-consuming. -
      • - -\image html ghs3d_parameters_advanced.png - -
      • Maximum memory size - launches ghs3d software with -work space limited to the specified amount of RAM, in Mbytes. If this option is -checked off, the software will be launched with 7O% of the total RAM space.
      • - -
      • Initial memory size - starts ghs3d software with -the specified amount of work space, in Mbytes. If this option is checked off, the -software will be started with 100 Megabytes of working space.
      • - -
      • Working directory - allows defining the folder for input and output -files of ghs3d software, which are the files starting with "GHS3D_" prefix.
      • - -
      • Keep working files - allows checking input and output files -of ghs3d software, while usually these files are removed after the -launch of the mesher.
      • - -
      • Verbose level - to choose verbosity level in the range from -0 to 10. -
        • 0, no standard output, -
        • 2, prints the data, quality statistics of the skin and final -meshes and indicates when the final mesh is being saved. In addition -the software gives indication regarding the CPU time. -
        • 10, same as 2 plus the main steps in the computation, quality -statistics histogram of the skin mesh, quality statistics histogram -together with the characteristics of the final mesh. -
      • - -
      • To create new nodes - if this option is checked off, ghs3d -tries to create tetrahedrons using only the nodes of the 2D mesh.
      • - -
      • To use boundary recovery version - enables using a -boundary recovery module which tries to -create volume meshes starting from very poor quality surface meshes -(almost flat triangles on the surface, high density propagation, -extreme aspect ratios, etc.) which fails with the standard version. The -resulting volume mesh will however most likely have a very poor -quality (poor aspect ratio of elements, tetrahedra with a very small -positive volume).
      • - -
      • Option as text - allows input of any text as command line -for ghs3d. This allows the input of advanced options in a free from.
      • - -
      - -*/ \ No newline at end of file diff --git a/doc/salome/gui/SMESH/input/ghs3dprl_hypo.doc b/doc/salome/gui/SMESH/input/ghs3dprl_hypo.doc deleted file mode 100644 index 40ffb918c..000000000 --- a/doc/salome/gui/SMESH/input/ghs3dprl_hypo.doc +++ /dev/null @@ -1,392 +0,0 @@ -/*! - -\page ghs3dprl_hypo_page GHS3DPRL Parameters hypothesis - -\n GHS3DPRL Parameters hypothesis works only with Tetrahedron (Tepal with TetMesh-GHS3D) algorithm. -\n -\n This algorithm is a commercial software, its use requires a licence (http://www.distene.com/fr/build/offer.html). -\n The advantage of Tepal is the possibility to generate (for example) a partitioned -200 million tetrahedra mesh on a not-so-big memory computer (2Go RAM) -...in something like 50 hours of one CPU (Xeon, 2008). -This is an alternative to Pluging GHS3D where you should need something like a not-so-common CPU with 64Go RAM -to try to do a one-partitionned 200 million tetrahedra mesh ...in a much less time indeed. -\n -\n Notes: -\n This Plugin doesn't load in Memory the supposed plentiful big resulting meshes. -It's user choice: (in GUI Mesh mode) menu File-Import-MED Files. -\n Beware, to load one 5 millions tetrahedra MED file, GUI Salome needs 2Go RAM. -\n A new true parallel faster version of Tepal, using MPI, is expected in 2009. - -\image html ghs3dprl_parameters_basic.png - -
        -
      • -Name - allows to define the name of the hypothesis (GHS3DPRL Parameters by default). -
      • -
      • -MED_Name - allows to define the path and the basename of the -generated resulted MED files ("DOMAIN" by default). -Undefined path means environment variable $SALOME_TMP_DIR (or $TMP by default). -
      • -
      • -Nb_Part - allows to define the number of MED files generated, -the initial skin (triangles) will be meshed (tetrahedra) and partitioned -in Nb_Part by the elementary algorithm implemented in Tepal.
        -Beware, the (expected) number of total tetrahedra versus this parameter -involves the maximum tepal RAM use. -
      • -
      • -Keep_Files - if this box is checked, input files of Tepal -(GHS3DPRL.points and GHS3DPRL.faces) are deleted after use (...if no backgrounding). -
      • -
      • -Tepal_in_Background - if this box is checked, for big meshes, -launch Tepal execution and MED file generation in background, -allows user exiting of Salome. In this case, beware of the -job Tepal is "killSalome.py" independent, sometimes on other host. -
      • -
      • -To_Mesh_Holes - if this box is checked, force parameter component -of tetmesh-ghs3d to mesh holes. -
      • - -

        Modifying GHS3DPRL Advanced Parameters


        -GHS3DPRL Plugin launches standalone binary executable tepal2med which launches binary executable tepal.
        -tepal2med launches tepal, wait for the end of computation, and converts resulting output tepal files in expected MED files.
        -Some advanced optional parameters are accessibles as arguments.
        -If keep_files checked you a posteriori can always re-launch tepal2med in a Terminal as a command with yours parameters.
        Idem for tepal.

        -
      • -Advanced tepal2med Parameters - type "tepal2med --help" in a Terminal.

        - -\verbatim -myname@myhost > /export/home/myname/salome_5/GHS3DPRLPLUGIN_5/bin/salome/tepal2med --help -Available options: - --help : produces this help message - --casename : path and name of input tepal2med files which are - - output files of tepal .msg .noboite .faces .points .glo - - output file of GHS3DPRL_Plugin casename_skin.med (optional) - with initial skin and its initial groups - --number : number of partitions - --medname : path and name of output MED files - --limitswap : max size of working cpu memory (Mo) (before swapping on .temp files) - --verbose : trace of execution (0->6) - --test : more tests about joints, before generation of output files - --menu : a GUI menu for option number - --launchtepal : also launch tepal on files casename.faces and casename.points and option number - --meshholes : force parameter component of tetmesh-ghs3d to mesh holes - --background : force background mode from launch tepal and generation of final MED files (big meshes) - --deletegroups : regular expression (see QRegExp) which matches unwanted groups in final MED files - (try --deletegroups="(\bAll_Nodes|\bAll_Faces)" - (try --deletegroups="((\bAll_|\bNew_)(N|F|T))" -example: - tepal2med --casename=/tmp/GHS3DPRL --number=2 --medname=DOMAIN --limitswap=1000 - --verbose=0 --test=yes --menu=no --launchtepal=no - -\endverbatim - -

      • -
      • -

        -Advanced tepal Parameters - type "tepal" in a Terminal.

        - -\verbatim -myname@myhost > tepal - ===================================== - GHS3D-TEPAL 1.4.2 (Dec, 2006) 10-Dec-2008 AT 12:59:48 - ===================================== - - Distene SAS - Pole Teratec - BARD-1 - Domaine du Grand Rue - 91680 Bruyeres le Chatel - FRANCE - Phone: +33(0)1-69-26-62-10 Fax: +33(0)1-69-26-90-33 - EMail: support@distene.com - - COPYRIGHT (C)2006 DISTENE ALL RIGHTS RESERVED - - -USAGE : tepal options - -With options in : - --filename name (-f name) : - Basename of the input case (MANDATORY) - - --ndom n (-n n) : - Number of subdomains to make (MANDATORY) - - --ghs3d ghs3d options (-g ghs3d options) : - Run temesh ghs3d on a previously generated subdomain. (ghs3d options must be "quoted") - - --memory m (-m m) : - Max amount of memory (megabytes) allowed for ghs in the cutting process. (default is 0 : unlimited) - - --mesh_only (-Z ) : - Only (re)mesh all subdomains and update communications messages - - --mesh_call command (-c command) : - Call the user specified command for meshing all the subomains after their skin was generated - - --stats_only (-S ) : - Only compute and show some statistics on subdomains - - --rebuild (-r ) : - Merge final subdomains skins - - --rebuild_tetra (-R ) : - Merge final subdomains skins and tetraedra - - --rebuild_iface (-i ) : - Include interfaces in final subdomains merge - - --rebuild_retag (-t ) : - Tag vertices, faces (and tetra if selected) with their subdomain number in final subdomains merge (keeps the lowest tag for shared elements) - - --rebuild_ensight_parts (-e ) : - Build ensight geom file with parts - - --tetmesh_args str (-G str) : - Arguments to pass to Tetmesh during cutting process - - ============================================================================== - GHS3D-TEPAL SOFTWARE 1.4.2 (Dec, 2006) - END OF SESSION - COPYRIGHT (C)2006 DISTENE ALL RIGHTS RESERVED - ============================================================================== - ( Distene SAS - Phone: +33(0)1-69-26-62-10 Fax: +33(0)1-69-26-90-33 - EMail: support@distene.com ) - -\endverbatim - -

      • -
      • -

        -Advanced ghs3d Parameters (through tepal's --tetmesh_args) - type "ghs3d -h" in a Terminal.

        - -\verbatim -myname@myhost > ghs3d -h - -USE - /export/home/myname/ghs3d-4.0/DISTENE/Tools/TetMesh-GHS3D4.0/bin/Linux/ghs3dV4.0 - [-u] [-m memory>] [-M MEMORY] [-f prefix] [-v verbose] - [-c component] [-p0] [-C] [-E count] [-t] [-o level] - [-I filetype] [-a/-b] [-O n m] - -DESCRIPTION - - -u (-h) : prints this message. - - -m memory : launches the software with memory Megabytes of work space. - The default value of this parameter is 64 Megabytes and its - minimum value is 10 Megabytes. - It is also possible to set this parameter with the - environment variable GHS3D_MEMORY by means of an operation - equivalent to: - setenv GHS3D_MEMORY memory, - the value specified in the command line has the priority on - the environment variable. - - -M MEMORY : uses the automatic memory adjustment feature. - If MEMORY is zero, the size of the work space is initially - guessed from the input. If MEMORY is not zero, the - software starts with MEMORY Megabytes of work space. - The software then reallocates more and more memory as - needed. - The starting value when MEMORY equals 0 is 64 Megabytes, - the maximum is given with memory of the -m option if used - and the actual memory available. - - -f prefix : defines the generic prefix of the files. - - -v verbose : sets the output level parameter (the verbose parameter - must be in the range 0 to 10). - - -c component : chooses the meshed component. If component is - 0, all components to be meshed - 1, only the main (outermost) component to be meshed - - -p0 : disables creation of internal points. - - -C : uses alternate boundary recovery version. To be used only - when the boundary recovery of the standard version fails. - - -E count : sets the extended output for error messages. If -E is used, - the error messages found will be printed, up to a maximum - count of errors between 1 and 100. - - -t : generates an error file prefix.Log - - -o level : sets the desired optimisation level. - Valid optimisation levels are: - none, light, medium or standard, strong, - in increasing order of "quality vs speed" ratio. - - -I filetype : defines the input mesh format as follows: - -IP input files are ascii files, named prefix.points - and prefix.faces - this is the default - -IPb input files are binary files, named prefix.pointsb - and prefix.facesb - -IM input file is ascii file, named prefix.mesh - where prefix is given with the -f option - - -a/-b : selects the output file type: - -a for ascii (the default) and - -b for binary. - - -On : saves a NOPO file in addition. NOPO is the mesh data - structure of the Simail and Modulef software packages. - -Om : saves a mesh file in addition. - -Omn : saves both NOPO and mesh files. - - ============================================================================== - TETMESH-GHS3D SOFTWARE 4.0-3 (December, 2006) - END OF SESSION - COPYRIGHT (C)1989-2006 INRIA ALL RIGHTS RESERVED - ============================================================================== - ( Distene SAS - Phone: +33(0)1-69-26-62-10 Fax: +33(0)1-69-26-90-33 - EMail: support@distene.com ) - -\endverbatim - -

      • -

        Saving user's preferred GHS3DPRL Advanced Parameters


        -GHS3DPRL Plugin launches standalone binary executable tepal2med.
        -you may rename tepal2med as tepal2med.exe for example, and replace tepal2med by a shell script at your convenience to overriding parameters.
        ... or else $PATH modification... .
        Idem for tepal.

        -
      • -Advanced tepal2med Parameters - overriding parameter deletegroups

        - -\code -#!/bin/bash -#script tepal2med overriding parameter deletegroups -#we have renamed binary executable tepal2med as tepal2med.exe -#echo tepal2med initial parameters are $1 $2 $3 $4 ... or $* -#$0 is ignored - -tepal2med.exe $* --deletegroups="(\bAll_Nodes|\bAll_Faces)" - -\endcode - -

      • -
      • -

        -Advanced tepal Parameters - overriding parameter component of ghs3d (to mesh holes).

        - -\code -#!/bin/bash -#script tepal overriding parameter component of tetmesh-ghs3d -#we have renamed binary executable tepal as tepal.exe - -#optionnaly we could set licence only for us -DISTENE_LICENSE_FILE="Use global envvar: DLIM8VAR" -DLIM8VAR="dlim8 1:1:29030@is142356/0016175ef08c::a1ba1...etc...e19" -SIMULOGD_LICENSE_FILE=29029@is142356 - -tepal.exe $* --tetmesh_args "-c 0" - -\endcode - -

      • -
      • -

        -Advanced tepal Parameters - overriding launching tepal on other host.

        - -\code -#!/bin/bash -#script tepal overriding launching tepal on other host (tepal run 64 bits only) -#we have renamed binary executable tepal as tepal.exe -#common file system (same path) otherwise scp... on input or result files -#ssh -keygen -t rsa done and files id_rsa et id-rsa.pub move in ~/.ssh - -#example of typical command -#tepal -f /home/myname/tmp/GHS3DPRL -n 4 > /home/myname/tmp/tepal.log -#echo parameters $1 $2 $3 $4 ... or $* - -#tepal licence ought to be known on otherhost -ssh otherhost "tepal.exe $* > /home/myname/tmp/tepal.log" - -#or more and more -#ssh otherhost "tepal.exe $* --tetmesh_args \"-c 0\"" > /home/myname/tmp/tepal.log - -\endcode - -

      • - -

        TUI use.


        - -
      • -

        -example ex30_tepal.py.

        - -\code - -#!/bin/python -import os - -import geompy -import smesh - -# Parameters -# ---------- - -results = "/tmp/ZZ" - -radius = 50 -height = 200 - -# Build a cylinder -# ---------------- - -base = geompy.MakeVertex(0, 0, 0) -direction = geompy.MakeVectorDXDYDZ(0, 0, 1) - -cylinder = geompy.MakeCylinder(base, direction, radius, height) - -geompy.addToStudy(cylinder, "Cylinder") - -# Define a mesh on a geometry -# --------------------------- - -m = smesh.Mesh(cylinder) - -# 2D mesh with BLSURF -# ------------------- - -algo2d = m.Triangle(smesh.BLSURF) - -algo2d.SetPhysicalMesh(1) -algo2d.SetPhySize(5) - -algo2d.SetGeometricMesh(0) - -# 3D mesh with tepal -# ------------------ - -algo3d = m.Tetrahedron(smesh.GHS3DPRL) - -algo3d.SetMEDName(results) -algo3d.SetNbPart(4) -algo3d.SetBackground(False) -algo3d.SetKeepFiles(False) -algo3d.SetToMeshHoles(True) - -# Launch meshers -# -------------- - -status = m.Compute() - -# Test if ok -# ---------- - -if os.access(results+".xml", os.F_OK): - print "Ok: tepal" -else: - print "KO: tepal" -\endcode - -

      • -
      - - -*/ diff --git a/doc/salome/gui/SMESH/input/group_of_underlying_elements.doc b/doc/salome/gui/SMESH/input/group_of_underlying_elements.doc new file mode 100755 index 000000000..a6a1b91ff --- /dev/null +++ b/doc/salome/gui/SMESH/input/group_of_underlying_elements.doc @@ -0,0 +1,34 @@ +/*! + +\page group_of_underlying_elements_page Create Group of Underlying Elements + + +To create groups of entities from existing groups of superior dimensions, in the \b Mesh menu select Group of underlying entities.
      + +The following dialog box will appear: + +\image html dimgroup_dlg.png + +In this dialog box specify the name of the resulting group, types of entities and set of source groups. + +In the figure below, there are two source Volume groups: + +\image html dimgroup_src.png +
      Source groups
      + +In this case the following results for Faces, Edges and Nodes are obtained: + +\image html dimgroup_2d.png +
      Faces
      + +\image html dimgroup_1d.png +
      Edges
      + +\image html dimgroup_0d.png +
      Nodes
      + +See Also a sample TUI Script of a +\ref tui_create_dim_group "Creating groups of entities from existing groups of superior dimensions" +operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/grouping_elements.doc b/doc/salome/gui/SMESH/input/grouping_elements.doc index 56fe14c0d..9615ad4c3 100644 --- a/doc/salome/gui/SMESH/input/grouping_elements.doc +++ b/doc/salome/gui/SMESH/input/grouping_elements.doc @@ -2,30 +2,37 @@ \page grouping_elements_page Grouping elements -In Mesh module it is possible to \subpage creating_groups_page "create groups of mesh elements": -nodes, edges, faces or volumes: -
        -
      • by selecting the elements of the specified kind by their IDs or -directly on the presentation in the VTK viewer - Standalone group tab of Create group -dialog.
      • -
      • by creating a group of elements of the selected type from all -such elements of the chosen geometrical object - Group on -geometry tab of Create group dialog.
      • -
      • by creating several groups of elements (nodes, -edges, faces and volumes) from the chosen submesh - using Mesh -> Construct -Group Menu item. In this case groups of elements are created automatically.
      • -
      +In Mesh module it is possible to create groups of mesh elements: +nodes, edges, faces or volumes. One group contains elements of only +one type. The following ways of creation are possible: + +- by selecting the elements using filters and/or directly on the + presentation in the VTK viewer, and/or by using elements of other + mesh objects - \ref standalone_group "Standalone group" + tab of \ref creating_groups_page "Create group" dialog. +- by creating a group of elements generated on the chosen geometrical + object - \ref group_on_geom "Group on geometry" tab of + \subpage creating_groups_page "Create group" dialog and + \subpage create_groups_from_geometry_page "Create Groups from Geometry" + dialog. +- by creating a group of elements satisfying to certain criteria - + \ref group_on_filter "Group on filter" tab of + \subpage creating_groups_page "Create group" dialog. +- by creating groups of nodes and elements from the chosen submesh + (type of elements depends on dimension of submesh geometry) - + using Mesh -> Construct Group menu item (available in context + menu as well). +- by creating groups of entities from existing groups of superior + dimensions - using \subpage group_of_underlying_elements_page + "Create Group of Underlying Elements" dialog. The created groups can be later: -
        -
      • \subpage editing_groups_page "Edited"
      • -
      • \subpage using_operations_on_groups_page "Subjected to Boolean operations", or
      • -
      • \subpage deleting_groups_page "Deleted"
      • -
      +- \subpage editing_groups_page "Edited" +- \subpage using_operations_on_groups_page "Subjected to Boolean operations" +- \subpage deleting_groups_page "Deleted" An important tool, providing filters for creation of \b Standalone -groups is \subpage selection_filter_library_page
    11. . - +groups is \ref selection_filter_library_page. */ diff --git a/doc/salome/gui/SMESH/input/importing_exporting_meshes.doc b/doc/salome/gui/SMESH/input/importing_exporting_meshes.doc index dbf96ab89..52ff261e3 100644 --- a/doc/salome/gui/SMESH/input/importing_exporting_meshes.doc +++ b/doc/salome/gui/SMESH/input/importing_exporting_meshes.doc @@ -3,15 +3,15 @@ \page importing_exporting_meshes_page Importing and exporting meshes \n In MESH there is a functionality allowing importation/exportation -of meshes from \b MED, \b UNV (I-DEAS 10), \b DAT (Nastran) and STL -format files. +of meshes from/to \b MED, \b UNV (I-DEAS 10), \b DAT (Nastran), \b STL +and \b CGNS format files. You can also export a group as a whole mesh. To import a mesh:
      1. From the \b File menu choose the \b Import item, from its sub-menu -select the corresponding format (MED, UNV and DAT) of the file containing +select the corresponding format (MED, UNV, DAT, STL and CGNS) of the file containing your mesh.
      2. In the standard Search File dialog box find the file for importation. It is possible to select multiple files to be imported all at once.
      3. @@ -21,13 +21,13 @@ importation. It is possible to select multiple files to be imported all at once. \image html meshimportmesh.png -To export a mesh: +To export a mesh or a group:
        1. Select the object you wish to export.
        2. From the \b File menu choose the \b Export item, from its sub-menu -select the format (MED, UNV, DAT and STL) of the file which will contain your -exported mesh.
        3. +select the format (MED, UNV, DAT, STL and CGNS) of the file which will +contain your exported mesh.
        4. In the standard Search File select a location for the exported file and enter its name.
        5. Click the \b OK button.
        6. diff --git a/doc/salome/gui/SMESH/input/index.doc b/doc/salome/gui/SMESH/input/index.doc index 2de722d16..798237b4b 100644 --- a/doc/salome/gui/SMESH/input/index.doc +++ b/doc/salome/gui/SMESH/input/index.doc @@ -6,22 +6,31 @@ \n \b MESH module of SALOME is destined for:
            -
          • \ref importing_exporting_meshes_page "import and export of meshes in MED format";
          • +
          • \ref importing_exporting_meshes_page "import and export of meshes in different formats";
          • \subpage about_meshes_page "meshing geometrical models" previously created or imported by the Geometry component;
          • \subpage viewing_meshes_overview_page "viewing created meshes" in the VTK viewer;
          • \subpage grouping_elements_page "creating groups of mesh elements";
          • -
          • applying to meshes \subpage quality_page "Quality Controls" , -allowing to highlight important elements: +
          • applying to meshes \subpage quality_page "Quality Controls", +allowing to highlight important elements; +
          • filtering sub-sets of mesh entities (nodes elements) using +\subpage filters_page "Filters" functionality;
          • \subpage modifying_meshes_page "modifying meshes" with a vast -array of dedicated operations.
          • -
          • \subpage using_notebook_mesh_page.
          • +array of dedicated operations; +
          • different \subpage measurements_page "measurements" of the mesh objects; +
          • easily setting parameters via the variables predefined in +\subpage using_notebook_mesh_page "Salome notebook".
          +Mesh module preferences are described in the \subpage mesh_preferences_page section of SALOME Mesh Help. + Almost all mesh module functionalities are accessible via \subpage smeshpy_interface_page "Mesh module Python interface". +Other functions are available in
          salome.smesh python package. + + \image html image7.jpg "Example of MESH module usage for engineering tasks" */ diff --git a/doc/salome/gui/SMESH/input/length_2d.doc b/doc/salome/gui/SMESH/input/length_2d.doc index def8afa95..4ed5b69e6 100644 --- a/doc/salome/gui/SMESH/input/length_2d.doc +++ b/doc/salome/gui/SMESH/input/length_2d.doc @@ -10,8 +10,8 @@ of your mesh.
          1. Display your mesh in the viewer.
          2. -
          3. Choose Controls > Length 2D or click "Length 2D" -button in the toolbar. +
          4. Choose Controls > Face Controls > Length 2D or click +"Length 2D" button in the toolbar. \image html image34.png
            "Length 2D" button
            diff --git a/doc/salome/gui/SMESH/input/make_2dmesh_from_3d.doc b/doc/salome/gui/SMESH/input/make_2dmesh_from_3d.doc new file mode 100644 index 000000000..a53257eef --- /dev/null +++ b/doc/salome/gui/SMESH/input/make_2dmesh_from_3d.doc @@ -0,0 +1,52 @@ +/*! + +\page make_2dmesh_from_3d_page Generate boundary elements + +\n This functionality allows to generate mesh elements on the borders of +elements of a higher dimension. + +To generate border elements: +
              +
            1. Select a mesh or group in the Object Browser or in the 3D Viewer
            2. +
            3. From the Modification menu choose "Create boundary elements" +item, or click "Create boundary elements" button in the toolbar + +\image html 2d_from_3d_ico.png "Create boundary elements icon" + +The following dialog box will appear: +\image html 2d_from_3d_dlg.png "Create boundary elements dialog box". +
            4. +
            5. Check in the dialog box one of two radio buttons corresponding to +the type of operation you would like to perform.
            6. +
            7. Fill the other fields available in the dialog box.
            8. +
            9. Click the \b Apply or Apply and Close button to perform the operation.
            10. +
            + +\n "Create boundary elements" dialog allows creation of boundary elements +of two types. +
              +
            • 2D from 3D creates mesh faces on free facets of volume elements
            • +
            • 1D from 2D creates mesh edges on free edges of mesh faces
            • +
            +Here a free facet means a facet shared by only one volume, a free edge +means an edge shared by only one mesh face. + +In this dialog: +
              +
            • specify the Target mesh, where the boundary elements will + be created. +
                +
              • This mesh adds elements in the selected mesh.
              • +
              • New mesh adds elements to a new mesh. The new mesh appears + in the Object Browser with the name that you can change in the adjacent box.
              • +
            • +
            • activate Copy source mesh checkbox to copy all elements of +the selected mesh to the new mesh, else the new mesh will contain only +boundary elements (old and created by this operation).
            • +
            • activate Create group checkbox to create a group to which + all the boundary elements (old and new) are added. The new group appears + in the Object Browser with the name that you can change in the adjacent box.
            • +
            +
            See Also a sample TUI Script of a \ref tui_make_2dmesh_from_3d "Create boundary elements" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/max_element_length_2d.doc b/doc/salome/gui/SMESH/input/max_element_length_2d.doc new file mode 100644 index 000000000..6ec4f7116 --- /dev/null +++ b/doc/salome/gui/SMESH/input/max_element_length_2d.doc @@ -0,0 +1,28 @@ +/*! + +\page max_element_length_2d_page Element Diameter 2D + +\n This quality control criterion consists in calculation of the length of +edges and diagonals combining 2D mesh elements (triangles and quadrangles). + +To apply the Element Diameter 2D quality criterion to your mesh: +
              +
            1. Display your mesh in the viewer.
            2. + +
            3. Choose Controls > Face Controls > Element Diameter 2D or click +"Element Diameter 2D" button in the toolbar. + +\image html image42.png +
              "Element Diameter 2D" button
              + +Your mesh will be displayed in the viewer with its elements colored according to the +applied mesh quality control criterion: + +\image html max_element_length_2d.png +
            4. +
            + +
            See Also a sample TUI Script of a +\ref tui_max_element_length_2d "Element Diameter 2D quality control" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/max_element_length_3d.doc b/doc/salome/gui/SMESH/input/max_element_length_3d.doc new file mode 100644 index 000000000..7aa70ad7b --- /dev/null +++ b/doc/salome/gui/SMESH/input/max_element_length_3d.doc @@ -0,0 +1,29 @@ +/*! + +\page max_element_length_3d_page Element Diameter 3D + +\n This quality control criterion consists in calculation of the length of +edges and diagonals combining 3D mesh elements +(tetrahedrons, pyramids, pentahendrons, hexahedrons and polyhedrons). + +To apply the Element Diameter 3D quality criterion to your mesh: +
              +
            1. Display your mesh in the viewer.
            2. + +
            3. Choose Controls > Volume Controls > Element Diameter 3D or click +"Element Diameter 3D" button in the toolbar. + +\image html image43.png +
              "Element Diameter 3D" button
              + +Your mesh will be displayed in the viewer with its elements colored according to the +applied mesh quality control criterion: + +\image html max_element_length_3d.png +
            4. +
            + +
            See Also a sample TUI Script of a +\ref tui_max_element_length_3d "Element Diameter 3D quality control" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/measurements.doc b/doc/salome/gui/SMESH/input/measurements.doc new file mode 100644 index 000000000..6514cf287 --- /dev/null +++ b/doc/salome/gui/SMESH/input/measurements.doc @@ -0,0 +1,70 @@ +/*! + +\page measurements_page Measurements + +Mesh module provides the possibility to perform different measurements +of the selected mesh data. + +All measurement operations are available via \b Measurements +top-level menu. Access to the measurements operations is +implemented via a single dialog box, where each operation is represented +as a separate tab page. + +\section min_distance_anchor Minimum Distance + +This operation allows measuring the distance between two objects. +Currently only node-to-node and node-to-origin operations are +available, but this operation will be extended in the future to support +other mesh objects - elements, meshes, sub-meshes and groups. + +To start Minimum Distance operation, select Minimum Distance +item from \b Measurements menu. + +\image html min_distance.png + +In the dialog box choose the first target and the second target mode by +switching the corresponding radio buttons, then select the objects the distance +between which is to be calculated (or input their IDs directly +in case of nodes/elements) and press \em Compute button. + +The following types of targets are supported: +- \em Node: single mesh node; +- \em Element: single mesh element (not available in this version); +- \em Object: mesh, sub-mesh or group object (not available in this +version); +- \em Origin: origin of the global co-ordinate system. + +The result will +be shown in the bottom area of the dialog box. In addition, a simple +preview will be shown in the 3D viewer. + +\image html min_distance_preview.png + +\section bounding_box_anchor Bounding Box + +This operation allows to calculate the bounding box of the selected +object(s). + +To start Bounding Box operation, select Bounding Box +item from \b Measurements menu. + +\image html bnd_box.png + +In the dialog box choose the required type of the object by switching the +corresponding radio button, select the object(s) and press \em Compute button. + +The following types of input are available: +- \em Objects: select one or several mesh, sub-mesh or group objects; +- \em Nodes: select a set of mesh nodes; +- \em Elements: select a set of mesh elements. + +The result of calculation will be shown in the bottom area of the +dialog box. In addition, a simple preview will be shown in the 3D +viewer. + +\image html bnd_box_preview.png + +See Also a sample TUI Script of a +\ref tui_measurements_page "Measurement operations". + +*/ diff --git a/doc/salome/gui/SMESH/input/merging_elements.doc b/doc/salome/gui/SMESH/input/merging_elements.doc index 821f6f9f2..50513d58f 100644 --- a/doc/salome/gui/SMESH/input/merging_elements.doc +++ b/doc/salome/gui/SMESH/input/merging_elements.doc @@ -5,42 +5,58 @@ \n This functionality allows to merge coincident elements of a mesh selectable in the dialog box. - \image html mergeelems_ico.png "Merge elements button" +
              +
            1. Choose in the main menu \b Modification -> \b Transformation -> Merge elements item. The following dialog box +shall appear:
            2. -\image html mergeelems.png +\image html mergeelems_auto.png +
              +
                +
              • \b Name is the name of the mesh whose elements will be merged.
              • +
              • \b Automatic or \b Manual Mode allows choosing how the elements +are processed. +
              + +
            3. Automatic mode: +
                +
              • In the \b Automatic Mode the elements created on the same nodes will be merged.
              • +
              +
            4. + +
            5. If the \b Manual Mode is selected, additional controls are +available: +\image html mergeelems.png +
              +
                +
              • \b Detect button generates the list of coincident elements for the given \b Tolerance.
              • +
              • Coincident elements is a list of groups of elements for +merging. All elements of each group will form one after the operation.
                  -
                • \b Name is the name of the mesh whose elements will be merged.
                • -
                • \b Tolerance is a maximum distance between elements sufficient for merging. -
                    -
                  • \b Detect button generates the list of coincident elements for the given \b Tolerance.
                  • -
                • -
                • Coincident elements is a list of groupes of elements for - merging. All elements of each group will form one after the operation. -
                    -
                  • \b Remove button deletes the selected group from the list.
                  • -
                  • \b Add button adds to the list a group of elements selected in the - viewer with pressed "Shift" key.
                  • -
                  • Select all checkbox selects all groups.
                  • -
                • -
                • Edit selected group list allows editing the selected group: -

                  - \image html add.png -
                  adds to the group the element selected in the viewer.
                  -
                  - \image html remove.png -
                  removes from the group the selected element.
                  -
                  - \image html sort.png -
                  moves the selected element to the first position in the - group. This means that all other elements will be merged into this - one.
                  -
                  -
                • -
                • To confirm your choice click \b Apply or Apply and Close button.
                • +
                • \b Remove button deletes the selected group from the list.
                • +
                • \b Add button adds to the list a group of elements selected in the +viewer with pressed "Shift" key.
                • +
                • Select all checkbox selects all groups.
                • +
              • +
              • Edit selected group list allows editing the selected group: +

                +\image html add.png +
                adds to the group the element selected in the viewer.
                +
                +\image html remove.png +
                removes from the group the selected element.
                +
                +\image html sort.png +
                moves the selected element to the first position in the +group. This means that all other elements will be merged into this +one.
                +
                +
              • +
              • To confirm your choice click \b Apply or Apply and Close button.
              +
            In this picture you see a triangle which coincides with one of the elements of the mesh. After we apply Merge Elements functionality, the diff --git a/doc/salome/gui/SMESH/input/merging_nodes.doc b/doc/salome/gui/SMESH/input/merging_nodes.doc index fb88fb89e..3b1bbeaaf 100644 --- a/doc/salome/gui/SMESH/input/merging_nodes.doc +++ b/doc/salome/gui/SMESH/input/merging_nodes.doc @@ -2,29 +2,43 @@ \page merging_nodes_page Merging nodes -\n This functionality allows user to detect groups of coincident nodes -with desirable tolerance, edit these groups and merge. +This functionality allows user to detect groups of coincident nodes +with specified tolerance; each group of the coincident nodes can be +then converted to the single node. \image html mergenodes_ico.png "Merge nodes button" To merge nodes of your mesh:
              -
            1. From the \b Modification choose \b Transformation and from its +
            2. From the \b Modification choose \b Transformation and from its sub-menu select the Merge nodes item. The following dialog box -shall appear: - -\image html mergenodes.png - +shall appear:
            3. +
              +\image html mergenodes_auto.png +
              • \b Name is the name of the mesh whose nodes will be merged.
              • +
              • \b Automatic or \b Manual Mode allows choosing how the nodes are +processed.
              • \b Tolerance is a maximum distance between nodes sufficient for -merging. +merging.
              • +
              • Exclude Groups group box allows to ignore the nodes which +belong to the specified mesh groups. +
              + +
            4. Automatic mode: +
              +
                +
              • In the \b Automatic Mode all Nodes within the indicated tolerance +will be merged. The nodes which belong to the groups specified in the +Exclude Groups will be not taken into account.
              • +
              +

            5. +
            6. If the \b Manual Mode is selected, additional controls are available:
              • \b Detect button generates the list of coincident nodes for the given \b Tolerance.
              • -
              -
            7. -
            8. Coincident nodes is a list of groupes of nodes for +
            9. Coincident nodes is a list of groups of nodes for merging. All nodes of each group will form one after the operation.
                @@ -33,6 +47,10 @@ operation. viewer with pressed "Shift" key.
              • Select all checkbox selects all groups.
              + +
              +\image html mergenodes.png +
            10. Edit selected group list allows editing the selected group: @@ -60,4 +78,4 @@ one.

              See Also a sample TUI Script of a \ref tui_merging_nodes "Merge Nodes" operation. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/mesh_infos.doc b/doc/salome/gui/SMESH/input/mesh_infos.doc index 37a3deaf4..06a02d2cc 100644 --- a/doc/salome/gui/SMESH/input/mesh_infos.doc +++ b/doc/salome/gui/SMESH/input/mesh_infos.doc @@ -1,65 +1,105 @@ /*! -\page mesh_infos_page Mesh infos +\page mesh_infos_page Mesh Information -\n There are three information boxes: Standard Mesh -Infos, Advanced Mesh Infos and Mesh Element Info. +The user can obtain information about the selected mesh object +(mesh, sub-mesh or group) using Mesh Information dialog box. -
              -\anchor standard_mesh_infos_anchor -

              Standard Mesh Infos

              - -The Standard Mesh Infos box gives only the information on the -number of elements of maximum dimension and the number of nodes in the -mesh. However, from this Info you can learn about groups selected on -this mesh. -\n To view the Standard Mesh Infos, select your mesh or submesh -in the Object Browser and select Standard Mesh Infos -from the \b Mesh menu or click "Standard Mesh Infos" button +To view the Mesh Information, select your mesh, sub-mesh or +group in the Object Browser and invoke Mesh Information +item from the \b Mesh menu or click "Mesh Information" button in the toolbar. \image html image49.png -
              "Standard Mesh Infos" button
              +
              "Mesh Information" button
              + +The Mesh Information dialog box provides three tab pages: +- \ref advanced_mesh_infos_anchor "Base Info" - to show base +information about the selected mesh object +- \ref mesh_element_info_anchor "Element Info" - to show +detailed information about the selected mesh node or element. +- \ref mesh_addition_info_anchor "Additional Info" - to show additional information available +for the selected mesh, sub-mesh or group object. -The following information will be displayed: +\anchor advanced_mesh_infos_anchor +

              Base Information

              -\image html a-standmeshinfo.png +The Base Info tab page of the dialog box provides general +information on the selected object - mesh, sub-mesh or mesh group: +name, type, total number of nodes and elements separately for each +type: 0D elements, edges, faces and volumes. + +\image html advanced_mesh_infos.png +
              "Base Info" page
              +\anchor mesh_element_info_anchor +

              Mesh Element Information

              + +The Element Info tab page of the dialog box gives basic +information about the type, coordinates and connectivity of the +selected mesh node or element. + +\image html eleminfo1.png +
              "Element Info" page, node information

              -\anchor advanced_mesh_infos_anchor -

              Advanced Mesh Infos

              +\image html eleminfo2.png +
              "Element Info" page, element information
              -The Advanced Mesh Infos box gives more information about the mesh, -including the total number of faces and volumes and their geometrical -types. -\n To view the Advanced Mesh Infos, select your mesh or submesh -in the Object Browser and select Advanced Mesh Infos -from the \b Mesh menu or click "Advanced Mesh Infos" button -in the toolbar. +The use can either input the ID of a node or element he wants to +analyze directly in the dialog box or select the node or element in +the 3D viewer. -\image html image50.gif -
              "Advanced Mesh Infos" button
              +\anchor mesh_addition_info_anchor +

              Additional Information

              -The following information will be displayed: +The Additional Info tab page of the dialog box provides an +additional information on the selected object: mesh, sub-mesh or +group. -\image html advanced_mesh_infos.png +For a mesh object, the following information is shown: +- Name +- Type: based on geomerty, imported, standalone +- Shape (if mesh is based on geometry) +- File (if mesh is imported from the file) +- Groups +- Sub-meshes -In case you get Mesh Infos via a \ref tui_viewing_mesh_infos "TUI script", -the information is displayed in Python Console. +\image html addinfo_mesh.png +
              "Additional Info" page, mesh information
              +
              -\image html b-mesh_infos.png +For a sub-mesh object, the following information is shown: +- Name +- Parent mesh +- Shape +\image html addinfo_submesh.png +
              "Additional Info" page, sub-mesh information

              -\anchor mesh_element_info_anchor -

              Mesh Element Info

              -The Mesh Element Info box gives basic information about the -type and the coordinates of the selected mesh element. -\n It is possible to input the Element ID or to select the Element in -the Viewer. +For a group object, the following information is shown: +- Name +- Parent mesh +- Type: standalone, group on geometry, group on filter +- Entity type: node, edge, face, volume +- Size +- Color +- Number of underlying nodes (for non-nodal groups) + +\image html addinfo_group.png +
              "Additional Info" page, group information
              +
              -\image html eleminfo1.png +\note For the performance reasons, the number of underlying nodes is +computed only by demand. For this, the user should press the "Compute" +button (see picture). Also, the number of underlying nodes is +automatically calculated if the size of the group does not exceed +the "Automatic nodes compute limit" set via the "Mesh information" +preferences (zero value means no limit). -\image html eleminfo2.png +In case you get Mesh Information via a TUI script, the information is +displayed in the Python Console. +See the \ref tui_viewing_mesh_infos "TUI Example". -*/ \ No newline at end of file +*/ + diff --git a/doc/salome/gui/SMESH/input/mesh_preferences.doc b/doc/salome/gui/SMESH/input/mesh_preferences.doc new file mode 100644 index 000000000..1b1b2f96c --- /dev/null +++ b/doc/salome/gui/SMESH/input/mesh_preferences.doc @@ -0,0 +1,203 @@ +/*! + +\page mesh_preferences_page Mesh preferences + +In the Mesh module you can set mesh preferences, which can be used in +later sessions with this module. + +

              General Preferences

              + +\image html pref21.png + +
                +
              • Automatic Update
              • +
                  +
                • If you toggle Automatic Update checkbox, the model in your +viewer automatically updated whenever you make changes in it.
                • +
                • Size limit (elements) - allows to specify the maximum +number of elements in the resulting mesh for which the automatic updating +of the presentation is performed. This option affects only +Compute operation. Zero value means "no limit". Default value +is 500 000 mesh elements. +
                +
              • Quality Controls
              • +
                  +
                • If you toggle Display entity, both faces and edges of an +object will be displayed in the viewer by default.
                • +
                • If you toggle Use precision checkbox, you can display numbers in +Quality Control diagrams at the necessary level of precision.
                • +
                • Number of digits after point - defines precision for Quality Controls. By default, numbers in Quality Control +diagrams are presented as integers.
                • +
                • Double nodes tolerance defines the maximal distance between two +mesh nodes, at which they are considered coincident by Double nodes +quality control. +
                +
              • Display mode - allows to set Wireframe, Shading, Nodes or Shrink +presentation mode as default.
              • +
              • Representation of the 2D quadratic elements
              • +
                  +
                • Representation of the 2D quadratic elements combobox - allows +to select lines or arcs for representation of quadratic elements.
                • +
                • Maximum Angle - maximum deviation angle used by the +application to build arcs.
                • +
                +
              • Mesh export
              • +
                  +
                • If you toggle Automatically create groups for MED export checkbox, +this operation will be carried out automatically.
                • +
                • If you toggle Automatic renumbering checkbox, the exported +mesh will be renumbered automatically
                • +
                +
              • Mesh computation
              • +
                  +
                • Show a computation result notification combobox allows to +select the notification mode about a mesh computation result. +There are 3 possible modes:
                • +
                    +
                  • Never - do not show the result dialog at all;
                  • +
                  • Errors only - the result dialog will be shown if there were +some errors during a mesh computation;
                  • +
                  • Always - show the result dialog after each mesh +computation. This is a default mode.
                  • +
                +
              • Mesh information
              • +
                  +
                • Mesh element information
                • - Change the way mesh element +information is shown: +
                    +
                  • Simple - as a plain text
                  • +
                  • Tree - in a tree-like form
                  • +
                  +
                • Automatic nodes compute limit
                • - allows to define the size limit for the +mesh groups for which the number of underlying nodes is calculated +automatically. If the group size exceeds the value set in the preferences, +the user will have to press \em Compute button explicitly. Zero value +means "no limit". By default the value is set to 100 000 mesh elements. +
                +
              • Automatic Parameters
              • +
                  +
                • Ratio Bounding Box Diagonal / Max Size - this parameter is +used for automatic meshing: ratio between the bounding box of the +meshed object and the Max Size of segments.
                • +
                • Default Number of Segments - allows defining the default +number of segments on each edge
                • +
                +
              • Mesh loading
              • +
                  +
                • If No mesh loading from study file at hypothesis modification + checkbox is on, the mesh data will not be loaded from the study file + when a hypothesis is modified. This allows saving time by omitting + loading data of a large mesh that is planned to be recomputed with other parameters.
                • +
                +
              • Input fields precision
              • +
                  +
                • Length precision - allows to adjust input precision of coordinates and dimensions.
                • +
                • Angular precision - allows to adjust input precision of angles.
                • +
                • Length tolerance precision - allows to adjust input precision of tolerance of coordinates and dimensions.
                • +
                • Parametric precision - allows to adjust input precision of parametric values.
                • +
                • Area precision - allows to adjust input precision of mesh element area.
                • +
                • Volume precision - allows to adjust input precision of mesh element volume.
                • +
                +
              • Python Dump
              • +
                  +
                • Historical python dump checkbox allows switching between + \a Historical and \a Snapshot dump mode. In \a + Historical mode, Python Dump script includes all commands + performed by SMESH engine. In \a Snapshot mode, the commands + relating to objects removed from the Study as well as the commands + not influencing the current state of meshes are excluded from the script.
                • +
                +
              + +

              Mesh Preferences

              + +\image html pref22.png + +
                +
              • Nodes
              • +
                  +
                • Color - allows to select the color of nodes. Click on the +colored line to access to the Select Color dialog box.
                • +
                • Type of marker - allows to define the shape of nodes.
                • +
                • Scale of marker - allows to define the size of nodes.
                • +
                +
              • Elements
              • +
                  +
                • Surface color - allows to select the surface color of elements +(seen in Shading mode). Click on the colored line to access to the Select Color dialog box.
                • +
                • Back surface color - allows to select the interior surface color +of elements. Use the slider to select the color generated basing on +the Surface color by changing its brightness and saturation.
                • +
                • Outline color - allows to select the color of element +borders. Click on the colored line to access to the Select Color dialog box.
                • +
                • Wireframe color - allows to select the color of borders of +elements in the wireframe mode. Click on the colored line to access to the Select Color dialog box.
                • +
                • Width - allows to define the width of lines (edges and borders of elements).
                • +
                • Shrink coef. - allows to define relative space of elements +compared to gaps between them in shrink mode.
                • +
                +
              • Orientation of Faces - allows to define the behavior of +Orientation of faces functionality
              • +
                  +
                • \b Color - allows to define the color of orientation vertors;
                • +
                • \b Scale - allows to define the size of orientation vectors;
                • +
                • 3D Vector checkbox allows to choose between 2D planar +and 3D vectors.
                • +
                +
              + +

              Selection Preferences

              + +\image html pref23.png + +
                +
              • Selection - performed with mouse-indexing (preselection) +and left-clicking on an object, whose appearance changes as defined in +the Preferences.
              • +
                  +
                • Object Color - allows to select the color of mesh (edges and +borders of meshes) of the selected entity. Click on the colored line +to access to the Select Color dialog +box.
                • +
                • Element color - allows to select the color of surface of selected +elements (seen in Shading mode). Click on the colored line to access +to the Select Color dialog box.
                • +
                +
              • Preselection - performed with mouse-indexing on an object, +whose appearance changes as defined in the Preferences.
              • +
                  +
                • Highlight Color - allows to select the color of mesh (edges and +borders of meshes) of the entity . Click on the colored line to access +to the Select Color dialog box.
                • +
                +
              • Precision - in this menu you can set the value of precision +used for Nodes, Elements and Objects.
              • +
              + +

              Scalar Bar Preferences

              + +\image html pref24.png + +
                +
              • Font - in this menu you can set type, face and color for +the font of Title and Labels.
              • +
              • Colors & Labels - in this menu you can set the number of +colors and the number of labels in use.
              • +
              • Orientation - here you can choose between vertical and +horizontal orientation of the Scalar Bar
              • . +
              • Origin & Size Vertical & Horizontal - allows to define +placement (X and Y) and lookout (Width and +Height) of Scalar Bars
              • +
                  +
                • X: abscissa of the point of origin (from the left +side)
                • +
                • Y: ordinate of the origin of the bar (from the bottom)
                • +
                +
              • Distribution in this menu you can Show/Hide distribution histogram of the values of the Scalar Bar and specify the Coloring Type of the histogram
              • +
                  +
                • Multicolor the histogram is colored as Scalar Bar
                • +
                • Monocolor the histogram is colored as selected with Distribution color selector
                • +
                +
              + +*/ diff --git a/doc/salome/gui/SMESH/input/mesh_through_point.doc b/doc/salome/gui/SMESH/input/mesh_through_point.doc index cd0d68ea9..968bfbc0b 100644 --- a/doc/salome/gui/SMESH/input/mesh_through_point.doc +++ b/doc/salome/gui/SMESH/input/mesh_through_point.doc @@ -1,34 +1,37 @@ /*! -\page mesh_through_point_page Mesh through point +\page mesh_through_point_page Moving nodes -\n In mesh you can define a node at a certain point either by creation -of a new node, by movement of the node closest to the point or by +\n In mesh you can define a node at a certain point either +by movement of the node closest to the point or by movement of any node to the point. -To create a mesh passing through a point: +To displace a node:
                -
              1. From the \b Modification menu choose the Mesh through point item or -click "Mesh to pass through a point" button in the toolbar. +
              2. From the \b Modification menu choose the Move node item or +click "Move Node" button in the toolbar. -\image html mesh_node_to_point.png -
                "Mesh to pass through a point" button
                +\image html image67.png +
                "Move Node" button
                The following dialog box shall appear: \image html meshtopass.png
              3. -
              4. Enter the coordinates of the point.
              5. -
              6. Choose one of several methods: you can either \b Create a new node at -the indicated point or Move the existing node to the point. In the -latter case you can check in Automatic search of the closest node or -select the necessary node manually. \b Preview check-box allows to see -the results of the operation.
              7. -
              8. Click the \b Apply or \b OK button.
              9. +
              10. Enter the coordinates of the destination point.
              11. +
              12. Check in Find closest to destination option or +select the necessary node manually (X, Y, Z, dX, dY, dZ fields allow +to see original coordinates and displacement of the node to move). +\b Preview check-box allows to see the results of the operation.
              13. +
              14. Click the \b Apply or Apply and Close button.
              +\image html moving_nodes1.png "The initial mesh" + +\image html moving_nodes2.png "The modified mesh" +
              See Also a sample TUI Script of a -\ref tui_mesh_through_point "Mesh through point" operation. +\ref tui_moving_nodes "Moving Nodes" operation. */ \ No newline at end of file diff --git a/doc/salome/gui/SMESH/input/minimum_angle.doc b/doc/salome/gui/SMESH/input/minimum_angle.doc index af631b0ec..4236d6562 100644 --- a/doc/salome/gui/SMESH/input/minimum_angle.doc +++ b/doc/salome/gui/SMESH/input/minimum_angle.doc @@ -10,7 +10,8 @@ element (triangle or quadrangle).
              1. Display your mesh in the viewer.
              2. -
              3. Choose Controls > Minimum angle or click "Minimum Angle" button. +
              4. Choose Controls > Face Controls > Minimum angle or click +"Minimum Angle" button. \image html image38.png
                "Minimum Angle" button
                diff --git a/doc/salome/gui/SMESH/input/modifying_meshes.doc b/doc/salome/gui/SMESH/input/modifying_meshes.doc index d3149b730..36c4e0abb 100644 --- a/doc/salome/gui/SMESH/input/modifying_meshes.doc +++ b/doc/salome/gui/SMESH/input/modifying_meshes.doc @@ -18,24 +18,27 @@ elements of the mesh.
              5. its elements.
              6. \subpage rotation_page "Rotate" by the indicated axis and angle the mesh or some of its elements.
              7. -
              8. Create a \subpage symmetry_page "symmetrical copy" of the mesh +
              9. \subpage scale_page "Scale" the mesh or some of its +elements.
              10. +
              11. \subpage symmetry_page "Mirror" the mesh through a point or a vector of symmetry.
              12. +
              13. \subpage double_nodes_page "Duplicate nodes" to emulate a crack in the model.
              14. Unite meshes by \subpage sewing_meshes_page "sewing" free borders, conform free borders, border to side or side elements.
              15. -
              16. \subpage merging_nodes_page "Merge Notes", considered coincident +
              17. \subpage merging_nodes_page "Merge Nodes", considered coincident within the indicated tolerance.
              18. \subpage merging_elements_page "Merge Elements", considered coincident within the indicated tolerance.
              19. -
              20. \subpage moving_nodes_page "Move Nodes" to an arbitrary location +
              21. \subpage mesh_through_point_page "Move Nodes" to an arbitrary location with consequent transformation of all adjacent elements and edges.
              22. -
              23. \subpage mesh_through_point_page "Make node at a point", existing -or created anew.
              24. \subpage diagonal_inversion_of_elements_page "Invert an edge" between neighboring triangles.
              25. \subpage uniting_two_triangles_page "Unite two triangles".
              26. \subpage uniting_set_of_triangles_page "Unite several adjacent triangles".
              27. \subpage changing_orientation_of_elements_page "Change orientation" of the selected elements.
              28. +
              29. \subpage reorient_faces_page "Reorient faces by vector".
              30. \subpage cutting_quadrangles_page "Cut a quadrangle" into two triangles.
              31. +
              32. \subpage split_to_tetra_page "Split" volumic elements into tetrahedra.
              33. \subpage smoothing_page "Smooth" elements, reducung distortions in them by adjusting the locations of element corners.
              34. Create an \subpage extrusion_page "extrusion" along a vector.
              35. @@ -45,6 +48,12 @@ of the selected node or edge.
              36. Apply \subpage pattern_mapping_page "pattern mapping".
              37. \subpage convert_to_from_quadratic_mesh_page "Convert regular mesh to quadratic", or vice versa.
              38. +
              39. \subpage make_2dmesh_from_3d_page "Generate boundary elements".
              40. +
              41. \subpage generate_flat_elements_page "Generate flat elements on group boundaries or on faces".
              42. +
              43. \subpage cut_mesh_by_plane_page "Cut a tetrahedron mesh by a plane".
              44. -*/ \ No newline at end of file +\note It is possible to use the variables defined in the SALOME \b NoteBook + to specify the numerical parameters used for modification of any object. + +*/ diff --git a/doc/salome/gui/SMESH/input/moving_nodes.doc b/doc/salome/gui/SMESH/input/moving_nodes.doc deleted file mode 100644 index d44ddc249..000000000 --- a/doc/salome/gui/SMESH/input/moving_nodes.doc +++ /dev/null @@ -1,36 +0,0 @@ -/*! - -\page moving_nodes_page Moving nodes - -\n In MESH you can change the location of any node of your mesh. In -this case all adjacent elements (edges) will be also transformed right -after the displaced node. - -To displace a node: -
                  -
                1. From the \b Modification menu choose the Move node item or -click "Move Node" button in the toolbar. - -\image html image67.png -
                  "Move Node" button
                  - -The following dialog box shall appear: - -\image html movenodes.png - -
                2. -
                3. Enter the ID of the required node in the Node ID field or -select this node in the 3D viewer. The coordinates of your node will -be automatically displayed in the \b Coordinates set of fields.
                4. -
                5. Set new coordinates for your node in the \b Coordinates set of fields.
                6. -
                7. Click the \b Apply or Apply and Close button.
                8. -
                - -\image html moving_nodes1.png "The initial mesh" - -\image html moving_nodes2.png "The node has been moved, transforming all adjacent edges" - -
                See Also a sample TUI Script of a -\ref tui_moving_nodes "Moving Nodes" operation. - -*/ \ No newline at end of file diff --git a/doc/salome/gui/SMESH/input/netgen_2d_3d_hypo.doc b/doc/salome/gui/SMESH/input/netgen_2d_3d_hypo.doc deleted file mode 100644 index f81ed2618..000000000 --- a/doc/salome/gui/SMESH/input/netgen_2d_3d_hypo.doc +++ /dev/null @@ -1,67 +0,0 @@ -/*! - -\page netgen_2d_3d_hypo_page Netgen 2D and 3D hypotheses - -\n Netgen 2D and Netgen 3D hypotheses work only with Netgen 1D-2D and -Netgen 1D-2D-3D algorithms. These algorithms do not require -definition of lower-level hypotheses and algorithms (2D and 1D for -meshing 3D objects and 1D for meshing 2D objects). They prove to be -useful if lower-level meshing is homogeneous for all wires and faces -of the meshed object. - -\image html netgen2d.png - -
                  -
                • Name - allows to define the name for the algorithm (Netgen -2D (or 3D) Parameters by default).
                • -
                • Max Size - maximum linear dimensions for mesh cells.
                • -
                • Second Order - if this box is checked in, the algorithm will -create second order nodes on the mesh, which actually will become -\ref adding_quadratic_elements_page "Quadratic".
                • -
                • Fineness - ranging from Very Coarse to Very Fine allows to set the -level of meshing detalization using the three parameters below. You -can select Custom to define them manually.
                • -
                • Growth rate - allows to define how much the linear dimensions of -two adjacent cells can differ (i.e. 0.3 means 30%).
                • -
                • Nb. Segs per Edge and Nb Segs per Radius - allows to define the -minimum number of mesh segments in which edges and radiuses will be -split.
                • -
                • Allow Quadrangles - allows to use quadrangle elements in a -triangle 2D mesh. This checkbox is not present in Netgen 3D parameters -because currently building a tetrahedral mesh with quadrangle faces is -not possible.
                • -
                • Optimize - if this box is checked in, the algorithm will try to -create regular (possessing even sides) elements.
                • -
                - -\image html netgen3d_simple.png - -Netgen 2D simple parameters and Netgen 3D simple parameters allow defining the size of elements for each dimension. Note that Netgen algorithm does not strictly follow the input parameters. The actual mesh can be more or less dense than required.
                - -\b 1D group allows defining the size of 1D elements in either of two ways: -
                  -
                • Number of Segments has the same sense as \ref -number_of_segments_anchor "Number of segments" hypothesis with -equidistant distribution.
                • -
                • Average Length has the same sense as \ref -average_length_anchor "Average Length" hypothesis.
                • -
                - -\b 2D group allows defining the size of 2D elements -
                  -
                • Length from edges if checked in, acts like \ref -length_from_edges_anchor "Length from Edges" hypothesis, else
                • -
                • Max. Element Area defines the maximum element area like \ref -max_element_area_anchor "Max Element Area" hypothesis.
                • -
                - -\b 3D groups allows defining the size of 3D elements. -
                  -
                • Length from faces if checked in, the area of sides of -volumic elements will be equal to an average area of 2D elements, else
                • -
                • Max. Element Volume defines the maximum element volume like -\ref max_element_volume_hypo_page "Max Element Volume" -hypothesis.
                • -
                    - -*/ \ No newline at end of file diff --git a/doc/salome/gui/SMESH/input/over_constrained_faces.doc b/doc/salome/gui/SMESH/input/over_constrained_faces.doc new file mode 100644 index 000000000..6c026f84a --- /dev/null +++ b/doc/salome/gui/SMESH/input/over_constrained_faces.doc @@ -0,0 +1,19 @@ +/*! + +\page over_constrained_faces_page Over-constrained faces + +\n This mesh quality control highlights faces sharing only one border +with other faces. In other words, the faces having all thier nodes on +the external border of the mesh are highlighted. + +\note The highlighted faces are actually over-constrained only if, at the computation time, +the boundary conditions on the borders where the nodes are located are all Dirichlet boundary conditions. + +\image html over_constrained_faces.png + +In this picture the over-constrained face is displayed in red. + +
                    See Also a sample TUI Script of a +\ref tui_over_constrained_faces "Over-constrained faces" filter. + +*/ diff --git a/doc/salome/gui/SMESH/input/over_constrained_volumes.doc b/doc/salome/gui/SMESH/input/over_constrained_volumes.doc new file mode 100644 index 000000000..7f1ba280d --- /dev/null +++ b/doc/salome/gui/SMESH/input/over_constrained_volumes.doc @@ -0,0 +1,18 @@ +/*! + +\page over_constrained_volumes_page Over-constrained volumes + +\n This mesh quality control highlights volumes sharing only one border with other volumes. +In other words, the volumes having all their nodes on the external border of the mesh are highlighted. + +\note The highlighted volumes are actually over-constrained only if, at the computation time, +the boundary conditions on the borders where the nodes are located are all Dirichlet boundary conditions. + +\image html over_constrained_volumes.png + +In this picture the over-constrained volume is displayed in red. + +
                    See Also a sample TUI Script of a +\ref tui_over_constrained_volumes "Over-constrained volumes" filter. + +*/ diff --git a/doc/salome/gui/SMESH/input/pattern_mapping.doc b/doc/salome/gui/SMESH/input/pattern_mapping.doc index cf556d51b..ef7f8ba66 100644 --- a/doc/salome/gui/SMESH/input/pattern_mapping.doc +++ b/doc/salome/gui/SMESH/input/pattern_mapping.doc @@ -5,36 +5,87 @@

                    About patterns

                    The pattern describes a mesh to generate: positions of nodes within a -geometrical domain and nodal connectivity of elements. As well, a -pattern specifies the so-called key-points, i.e. nodes that will be -located at geometrical vertices. Pattern description is stored in +geometrical domain and nodal connectivity of elements. A +pattern also specifies the so-called key-points, i.e. the nodes that will be +located at geometrical vertices. The pattern description is stored in \.smp file. The smp file contains 4 sections: -
                      -
                    1. The first line holds the number of nodes (N).
                    2. -
                    3. The next N lines describe nodes coordinates. Each line holds 2 -coordinates of a node.
                    4. - -
                    5. A key-points line: indices of nodes to be mapped on geometrical -vertices. An index n refers to a node described on an n-th line of -section 2. The first node index is zero.
                    6. - -
                    7. The rest lines describe nodal connectivity of elements, one line -for an element. A line holds indices of nodes forming an element. An -index n refers to a node described on an n-th line of the section -2. The first node index is zero. There must be 3 or 4 indices on a -line: only 2d elements are allowed.
                    8. -
                    - -The 2D pattern must contain at least one element and at least one -key-point. All key-points must lay on boundaries. - -An example of a simple smp file and a preview of a pattern described -in this file: - -\image html image94.gif +-# The first line indicates the total number of pattern nodes (N). +-# The next N lines describe nodes coordinates. Each line contains 2 +node coordinates for a 2D pattern or 3 node cordinates for a 3D pattern. +Note, that node coordinates of a 3D pattern can be defined only by relative values in range [0;1]. +-# The key-points line contains the indices of the nodes to be mapped on geometrical +vertices (for a 2D pattern only). Index n refers to the node described +on the n-th line of section 2. The index of the first node zero. For a 3D pattern the key points are not specified. +-# The remaining lines describe nodal connectivity of elements, one line +for each element. Each line holds indices of nodes forming an element. +Index n refers to the node described on the n-th line of section 2. +The first node index is zero. There must be 3 or 4 indices on each +line for a 2D pattern (only 2d elements are allowed) and 4, 5, 6 or 8 +indices for a 3D pattern (only 3d elements are allowed). + +A 2D pattern must contain at least one element and at least one +key-point. All key-points must lie on boundaries. + +A 3D pattern must contain at least one element. + +An example of a simple 2D pattern smp file: + +\code +!!! SALOME 2D mesh pattern file +!!! +!!! Nb of points: +9 + 200 0 !- 0 + 100 0 !- 1 + 0 0 !- 2 + 0 -100 !- 3 + 0 -200 !- 4 + 100 -200 !- 5 + 200 -200 !- 6 + 200 -100 !- 7 + 100 -100 !- 8 +!!! Indices of 4 key-points + 2 0 4 6 +!!! Indices of points of 6 elements + 0 1 8 + 8 5 6 7 + 2 3 8 + 8 3 4 5 + 8 7 0 + 8 1 2 +\endcode + +The image below provides a preview of the above pattern: + +\image html pattern2d.png + +An example of a simple 3D pattern smp file: + +\code +!!! SALOME 3D mesh pattern file +!!! +!!! Nb of points: +9 + 0 0 0 !- 0 + 1 0 0 !- 1 + 0 1 0 !- 2 + 1 1 0 !- 3 + 0 0 1 !- 4 + 1 0 1 !- 5 + 0 1 1 !- 6 + 1 1 1 !- 7 + 0.5 0.5 0.5 !- 8 +!!! Indices of points of 6 elements: + 0 1 5 4 8 + 7 5 1 3 8 + 3 2 6 7 8 + 2 0 4 6 8 + 0 2 3 1 8 + 4 5 7 6 8 +\endcode

                    Application of pattern mapping

                    @@ -46,90 +97,122 @@ From the \b Modification menu choose the Pattern Mapping item or click \image html image98.png
                    "Pattern mapping" button
                    -The following dialog box shall appear: +The following dialog box will appear: + +\n For a 2D pattern \image html patternmapping1.png +In this dialog you should specify: + +
                      +
                    • \b Pattern, which can be loaded from .smp pattern file previously +created manually or generated automatically from an existing mesh or submesh.
                    • +
                    • \b Face with the number of vertices equal to the number of + key-points in the pattern; the number of key-points on internal + boundaries of the pattern must also be equal to the number of vertices + on internal boundaries of the face;
                    • +
                    • \b Vertex to which the first key-point should be mapped;
                    • +Alternatively, it is possible to select Refine selected mesh elements +checkbox and apply the pattern to +
                    • Mesh Face instead of a geometric Face
                    • +
                    • and select \b Node instead of vertex.
                    • + +Additionally it is possible to: +
                    • Reverse the order of key-points By default, the vertices of + a face are ordered counterclockwise.
                    • +
                    • Enable to Create polygons near boundary
                    • +
                    • and Create polyhedrons near boundary
                    • +
                    + +\n For a 3D pattern + \image html patternmapping2.png -To apply a pattern to a geometrical object, you should specify: +In this dialog you should specify:
                      -
                    • a face having the number of vertices equal to the number of -key-points in the pattern; the number of key-points on internal -boundaries of a pattern must also be equal to the number of vertices -on internal boundaries of a face;
                    • -
                    • a vertex to which the first key-point should be mapped;
                    • -
                    • reverse or not the order of key-points. (The order of vertices of -a face is counterclockwise looking from outside).
                    • +
                    • \b Pattern, which can be loaded from .smp pattern file previously +created manually or generated automatically from an existing mesh or submesh.
                    • +
                    • A 3D block (Solid) object;
                    • +
                    • Two vertices that specify the order of nodes in the resulting mesh.
                    • +Alternatively, it is possible to select Refine selected mesh elements +checkbox and apply the pattern to +
                    • One or several Mesh volumes instead of a geometric 3D +object
                    • +
                    • and select two /b Nodes instead of vertices.
                    • +Additionally it is possible to: +
                    • Enable to Create polygons near boundary
                    • +
                    • and Create polyhedrons near boundary
                    -Then you either load a .smp pattern file previously created manually -by clicking on the "Load pattern" button, or click on the \b -New button for automatic generation. -\n For an automatic generation you just specify a geometrical face -having a mesh built on it. Mesh nodes lying on face vertices become -key-points. Additionally, you may choose the way of getting nodes -coordinates by projecting nodes on the face instead of using -"positions on face" generated by mesher (if there is any). Faces -having a seam edge can?t be used for automatic pattern creation. - -When creating a pattern from an existing mesh, there are two possible -cases: -
                      -
                    1. A sub-mesh on face is selected. A pattern is created from the 2d -elements bound to a face by mesher. Node coordinates are either -"positions on face" computed by mesher, or coordinates got by node -projection on a geometrical surface, according to your choice.
                    2. -
                    3. A mesh where the main shape is a face, is selected. A pattern is -created from all the 2d elements in a mesh. If all mesh elements are -build by mesher, the user can select the way of getting nodes -coordinates, else all nodes are projected on a face surface.
                    4. -
                    - -\image html a-patterntype.png +\n Automatic Generation + +To generate a pattern automatically from an existing mesh or submesh, +click \b New button. + +The following dialog box will appear: \image html a-patterntype1.png -

                    Mapping algorithm

                    +In this dialog you should specify: -The mapping algorithm is as follows: -
                      -
                    1. Key-points are set in the order that they are encountered when -walking along a pattern boundary so that elements are on the left. The -first key-point is preserved. -
                    2. +
                        +
                      • Mesh or Submesh, which is a meshed geometrical face (for a +2D pattern) or a meshed solid (for a 3D pattern). Mesh nodes lying on +the face vertices become key-points of the pattern.
                      • +
                      • A custom Pattern Name
                      • +
                      • Additionally, for a 2D pattern you may choose to +Project nodes on the face to get node coordinates instead of using +"positions on face" generated by the mesher (if there is any). The faces +having a seam edge cannot be used for automatic pattern creation.
                      • +
                      -
                    3. Find geometrical vertices corresponding to key-points by vertices -order in a face boundary; here, "Reverse order of key-points" flag is -taken into account. +When a pattern is created from an existing mesh, two cases are possible: -\image html image95.gif -
                    4. +- A sub-mesh on a face/solid is selected. The pattern is created from the 2d/3d +elements bound to the face/solid by the mesher. For a 2D pattern, the node coordinates are either +"positions on face" computed by the mesher, or coordinates got by node +projection on a geometrical surface, according to the user choice. For +a 3D pattern, the node coordinates correspond to the nodes computed by +the mesher. +- A mesh, where the main shape is a face/solid, is selected. The pattern is +created from all 2d/3d elements in a mesh. In addition, if all mesh +elements of a 2D pattern are built by the mesher, the user can select +how to get node coordinates, otherwise all nodes are projected on +a face surface. -
                    5. Boundary nodes of a pattern are mapped onto edges of a face: a -node located between certain key-points on a pattern boundary is -mapped on a geometrical edge limited by corresponding geometrical -vertices. Node position on an edge reflects its distance from two -key-points. -\image html image96.gif -
                    6. - -
                    7. Coordinates of a non-boundary node in a parametric space of a face -are defined as following. In a parametric space of a pattern, a node -lays at the intersection of two iso-lines, each of which intersects a -pattern boundary at least at two points. Knowing mapped positions of -boundary nodes, we find where isoline-boundary intersection points are -mapped to, and hence we can find mapped isolines direction and then, -two node positions on two mapped isolines. The eventual mapped -position of a node is found as an average of positions on mapped -isolines. +

                      Mapping algorithm

                      + +The mapping algorithm for a 2D case is as follows: +- The key-points are set counterclockwise in the order corresponding + to their location on the pattern boundary. The first key-point is preserved. +- The geometrical vertices corresponding to the key-points are found + on face boundary. Here, "Reverse order of key-points" flag is set. +\image html image95.gif +- The boundary nodes of the pattern are mapped onto the edges of the face: a + node located between two key-points on the pattern boundary is + mapped on the geometrical edge limited by the corresponding geometrical + vertices. The node position on the edge depends on its distance from the + key-points. +\image html image96.gif +- The cordinates of a non-boundary node in the parametric space of the face + are defined in the following way. In the parametric space of the + pattern, the node lies at the intersection of two iso-lines. Both + of them intersect the pattern boundary at two + points at least. If the mapped positions of boundary nodes are known, it is + possible to find, where the points at the intersection of isolines + and boundaries are mapped. Then it is possible to find + the direction of mapped isolinesection and, filally, the poitions of + two nodes on two mapped isolines. The eventual mapped + position of the node is found as an average of the positions on mapped + isolines. \image html image97.gif -
                    8. -
                    -
                    See Also a sample TUI Script of a +The 3D algorithm is similar. + +See Also a sample TUI Script of a \ref tui_pattern_mapping "Pattern Mapping" operation. -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/point_marker.doc b/doc/salome/gui/SMESH/input/point_marker.doc new file mode 100644 index 000000000..40c2b932b --- /dev/null +++ b/doc/salome/gui/SMESH/input/point_marker.doc @@ -0,0 +1,53 @@ +/*! + +\page point_marker_page Point Marker + +\n You can change the representation of points in +the 3D viewer either by selecting one of the predefined +shapes or by loading a custom texture from an external file. + +- Standard point markers + +The Mesh module provides a set of predefined point marker shapes +which can be used to display points in the 3D viewer. +Each standard point marker has two attributes: type (defines shape +form) and scale factor (defines shape size). + +\image html point_marker_widget1.png + +
                    + +\image html std_point_marker.png "Mesh presentation with standard point markers" + +- Custom point markers + +It is also possible to load a point marker shape from an external file. +This file should provide a description of the point texture as a set +of lines; each line is represented as a sequence of "0" and "1" symbols, +where "1" symbol means an opaque pixel and "0" symbol means a +transparent pixel. The width of the texture corresponds to the length +of the longest line in the file, expanded to the nearest byte-aligned +value. The height of the texture is equal to the number of non-empty +lines in the file. Note that missing symbols are replaced by "0". + +Here is a texture file sample: + +
                    +00111100
                    +00111100
                    +11111111
                    +11111111
                    +11111111
                    +11111111
                    +00111100
                    +00111100
                    +
                    + +\image html point_marker_widget2.png + +
                    + +\image html custom_point_marker.png "Mesh presentation with custom point markers" + +*/ + diff --git a/doc/salome/gui/SMESH/input/preview_meshes.doc b/doc/salome/gui/SMESH/input/preview_meshes.doc deleted file mode 100644 index 92a4cca27..000000000 --- a/doc/salome/gui/SMESH/input/preview_meshes.doc +++ /dev/null @@ -1,36 +0,0 @@ -/*! - -\page preview_meshes_page Preview and Compute meshes - -Before whole mesh computation it is allowed to see the mesh preview. -When mesh object is already created and all hypotheses assigned, -select your mesh in the Object Browser. From the -\b Mesh menu select \b Preview or click "Preview" button of the -toolbar or activate "Preview" item from pop-up menu. - -\image html mesh_precompute.png -
                    "Preview" button
                    - -The Mesh Preview dialog box appears. In this dialog box you can select -preview mode 1D mesh or 2D mesh depending on assigned -hypotheses to mesh. - -The 1D mesh preview shows as nodes computed on geometry edges - -\image html preview_mesh_1D.png - -The 2D mesh preview shows edge mesh elements, computed on geometry faces - -\image html preview_mesh_2D.png - -Pressing Compute button leads to whole mesh computation -process. -During exit from Preview dialog box, the question about storage temporary -created mesh elements appers: - -\image html preview_tmp_data.png - -Note, that computed temporary mesh elements can be reused during next -mesh computation process. - -*/ diff --git a/doc/salome/gui/SMESH/input/prism_3d_algo.doc b/doc/salome/gui/SMESH/input/prism_3d_algo.doc index 837923ac1..5589ba3a7 100644 --- a/doc/salome/gui/SMESH/input/prism_3d_algo.doc +++ b/doc/salome/gui/SMESH/input/prism_3d_algo.doc @@ -4,9 +4,9 @@ 3D extrusion algorithm can be used for meshing prisms, i.e. 3D Shapes defined by two opposing faces having the same number of vertices and -edges and meshed using the \ref projection_algos_page "2D Projection" -algorithm. These two faces should be connected by quadrangle "side" -faces. +edges and meshed using, for example, the \ref projection_algos_page +"2D Projection" algorithm. These two faces should be connected by +quadrangle "side" faces. The opposing faces can be meshed with either quadrangles or triangles, while the side faces should be meshed with quadrangles only. @@ -17,4 +17,8 @@ As you can see, the 3D extrusion algorithm permits to build and to have in the same 3D mesh such elements as hexahedrons, prisms and polyhedrons. -*/ \ No newline at end of file +\note This algorithm works correctly only if the opposing faces have +the same (or similar) meshing topography. Otherwise, 3D extrusion +algorithm can fail to build mesh volumes. + +*/ diff --git a/doc/salome/gui/SMESH/input/projection_algos.doc b/doc/salome/gui/SMESH/input/projection_algos.doc index 422b40993..dcbae8db4 100644 --- a/doc/salome/gui/SMESH/input/projection_algos.doc +++ b/doc/salome/gui/SMESH/input/projection_algos.doc @@ -17,14 +17,18 @@ The following dialog box will appear: \image html projection_1d.png In this menu you can define the \b Name of the algorithm, the already -meshed source \b Edge and the \b Mesh (optional, use it if there are several -different meshes on the same edge). It could also be necessary to -define the orientation of edges, which is done by indicating the -Source Vertex being the first point of the Source Edge and the -Target Vertex being the first point of the created \b Edge. For -a group of edges, Source and Target vertices should be -shared by only one edge of the group. If Source and -Target vertices are specified, the elements of the group must be ajacent. +meshed source \b Edge and the \b Mesh (It can be omitted only when +projecting a submesh on another one from the same global Mesh). +It could also be necessary to define the orientation of edges, +which is done by indicating the Source Vertex being the first point +of the Source Edge and the Target Vertex being the first point of +the created \b Edge. +
                    +For a group of edges, Source and Target vertices should be +shared by one edge of the group. If Source and Target +vertices are specified, the elements of the group must be adjacent. +The source and target groups must contain equal number of edges +and they must form topologically equal structures. \n Projection 2D algorithm allows to define the mesh of a face (or group of faces) by the @@ -42,17 +46,26 @@ following dialog box will appear: \image html projection_2d.png In this menu you can define the \b Name of the algorithm, the already -meshed source \b Face and the \b Mesh (optional, use it if there are several -different meshes on the same face). It could also be necessary to -define the orientation of mesh on the face, which is done by -indicating two Source Vertices, which belong to the same edge of the -source face, and two Target Vertices, which belong to the same edge of -the created \b Face. +meshed source \b Face and the \b Mesh (It can be omitted only when +projecting a submesh on another one from the same global Mesh). +It could also be necessary to define the orientation of mesh on the +face, which is done by indicating two Source Vertices, which +belong to the same edge of the source face, and two Target +Vertices, which belong to the same edge of the created \b Face. +For groups of face, they must contain equal number of faces +and they must form topologically equal structures. + +\n Projection 1D-2D algorithm differs from Projection 2D +algorithm in one aspect: it generates mesh segments on edges of +the face according to the projected 2D elements; thus it does not +require the edges to be meshed by any other 1D algorithm; moreover it +does not allow to mesh edges of the face using another algorithm via +definition of sub-meshes. \n Projection 3D algorithm allows to define the mesh of a shape by the projection of another already meshed shape. This algorithm works -only if all faces and edges of the target face have been meshed as 1D -Projections of the faces and edges of the source face. Another +only if all faces and edges of the target shape have been meshed as 1D-2D +Projections of the faces and edges of the source shape. Another limitation is that this algorithm currently works only on boxes. To apply this algorithm select the solid to be meshed (indicated in @@ -63,12 +76,15 @@ following dialog box will appear: \image html projection_3d.png In this menu you can define the \b Name of the algorithm, the already -meshed source 3D shape and the \b Mesh (optional, use it if there are -several different meshes on the same shape). It could also be -necessary to define the orientation of mesh on the shape, which is +meshed source 3D shape and the \b Mesh (It can be omitted only when +projecting a submesh on another one from the same global Mesh). +It could also be necessary to define the orientation of mesh on the shape, which is done by indicating two Source Vertices, which belong to the same edge of the source 3D Shape, and two Target Vertices, which belong to the same edge of the source 3D Shape. +
                    See Also a sample TUI Script of a +\ref tui_projection "Projection Algorithms". + */ diff --git a/doc/salome/gui/SMESH/input/radial_quadrangle_1D2D_algo.doc b/doc/salome/gui/SMESH/input/radial_quadrangle_1D2D_algo.doc new file mode 100644 index 000000000..0854941ea --- /dev/null +++ b/doc/salome/gui/SMESH/input/radial_quadrangle_1D2D_algo.doc @@ -0,0 +1,29 @@ +/*! + +\page radial_quadrangle_1D2D_algo_page Radial Quadrangle 1D2D + +\n This algorithm applies to the meshing of 2D shapes under the +following conditions: the face must be a full circle or a part of circle +(i.e. the number of edges is less or equal to 3 and one of them is a circle curve). +The resulting mesh consists of triangles (near the center point) and +quadrangles. + +This algorithm is optionally parametrized by the hypothesis indicating the number +of mesh layers along the radius. The distribution of layers can be set with any 1D Hypothesis. + +If no own hypothesis of the algorithm is assigned, any local or global hypothesis is used +by the algorithm to discretize edges. Note that if the geometrical face has two radial edges, +they must be meshed with equal number of segments. + +If no 1D hypothesis is assigned to an edge, "Default Number of Segments" preferences parameter +is used to discretize the edge. + +\image html hypo_radquad_dlg.png + +\image html mesh_radquad_01.png "Radial Quadrangle 2D mesh on the top and the bottom faces of a cylinder" + +\image html mesh_radquad_02.png "Radial Quadrangle 2D mesh on a part of circle" + +\sa A sample \ref tui_radial_quadrangle "TUI Script". + +*/ diff --git a/doc/salome/gui/SMESH/input/removing_nodes_and_elements.doc b/doc/salome/gui/SMESH/input/removing_nodes_and_elements.doc index 6e0f7f804..0a48ee65e 100644 --- a/doc/salome/gui/SMESH/input/removing_nodes_and_elements.doc +++ b/doc/salome/gui/SMESH/input/removing_nodes_and_elements.doc @@ -6,6 +6,7 @@
                    • \ref removing_nodes_anchor "Nodes"
                    • +
                    • \ref removing_orphan_nodes_anchor "Orphan Nodes"
                    • \ref removing_elements_anchor "Elements"
                    • \ref clear_mesh_anchor "Clear Mesh Data"
                    @@ -18,32 +19,58 @@
                    1. Select your mesh in the Object Browser or in the 3D viewer.
                    2. -
                    3. From the Modification menu choose Remove and from the associated -submenu select the Remove nodes, or just click "Remove nodes" +
                    4. From the Modification menu choose Remove and from the associated +submenu select the Nodes, or just click "Remove nodes" button in the toolbar. -\image html image88.gif +\image html remove_nodes_icon.png
                      "Remove nodes" button
                      The following dialog box will appear: \image html removenodes.png -In this dialog box you can specify one or several nodes +In this dialog box you can specify one or several nodes:
                        -
                      • Choosing them manually with the mouse in the 3D Viewer. You can -click on a node in the 3D viewer and it will be highlighted
                      • -
                      • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the nodes. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                      • +
                      • choose mesh nodes with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                      • +
                      • input the node IDs directly in ID Elements field. The selected nodes will be highlighted in the +viewer; or
                      • +
                      • apply Filters. Set filter button allows to apply a filter to the selection of nodes. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                      + +
                    \note Be careful while removing nodes because if you remove a definite node of your mesh all adjacent elements will be also deleted. +
                    +\anchor removing_orphan_nodes_anchor +

                    Removing orphan nodes

                    + +There is a quick way to remove all orphan (free) nodes. + +To remove orphan nodes: +
                      +
                    1. Select your mesh in the Object Browser or in the 3D viewer.
                    2. + +
                    3. From the Modification menu choose Remove and from the associated +submenu select Orphan Nodes, or just click "Remove orphan nodes" +button in the toolbar. + +\image html remove_orphan_nodes_icon.png +
                      "Remove orphan nodes" button
                      + +The following Warning message box will appear: + +\image html removeorphannodes.png + +Confirm nodes removal by pressing "Yes" button. +
                    +
                    \anchor removing_elements_anchor

                    Removing elements

                    @@ -52,8 +79,8 @@ node of your mesh all adjacent elements will be also deleted.
                    1. Select your mesh in the Object Browser or in the 3D viewer.
                    2. -
                    3. From the \b Modification menu choose \b Remove and from the -associated submenu select the Remove elements, or just click +
                    4. From the Modification menu choose Remove and from the +associated submenu select the Elements, or just click "Remove elements" button in the toolbar. \image html remove_elements_icon.png @@ -65,13 +92,14 @@ The following dialog box will appear: In this dialog box you can specify one or several elements
                        -
                      • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                      • -
                      • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                      • +
                      • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                      • +
                      • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                      • +
                      • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                      +
                    5. Click \b Apply or Apply and Close to confirm deletion of the specified elements.
                    @@ -91,7 +119,7 @@ about filters on the
                  • From the Modification menu choose Remove and from the associated submenu select the Clear Mesh Data, or just click "Clear Mesh Data" button in the toolbar. You can also right-click on the mesh in the -Object Browser and select Clear Mesh Data in the pop-up menu. +Object Browser and select Clear Mesh Data in the pop-up menu.
              \image html mesh_clear.png diff --git a/doc/salome/gui/SMESH/input/reorient_faces.doc b/doc/salome/gui/SMESH/input/reorient_faces.doc new file mode 100644 index 000000000..f637aecfd --- /dev/null +++ b/doc/salome/gui/SMESH/input/reorient_faces.doc @@ -0,0 +1,61 @@ +/*! + +\page reorient_faces_page Reorient faces by vector + +\n This operation allows changing orientation of a set of neighboring +faces. The desired orientation is defined by a vector. Since direction +of face normals in the set can be even opposite, it is necessary to +specify a control face whose normal will be compared with the vector. This +face can be specified either explicitly or can be found by closeness to +a given point. + +Orientation of a face is changed by reverting the order of its nodes. + +To change orientation of faces: +
                +
              1. In the \b Modification menu select the Reorient faces by + vector item or click Reorient faces by + vector button in the toolbar. + +
                +\image html reorient_faces_face.png +"Reorient faces by vector" button +
                + +The following dialog box will appear: + +
                +\image html reorient_2d_point.png +\image html reorient_2d_face.png +
                + +
              2. In this dialog +
                  +
                • Specify a way of selection of the control face: by point or + explicitely.
                • +
                • Select an \b Object containing faces to reorient, either in the Object + Browser or in the 3D Viewer; it can be either
                    +
                  • group of faces,
                  • +
                  • sub-mesh of faces or
                  • +
                  • mesh.
                  • +
                • +
                • Specify either coordinates of a \b Point by which the control face + will be found or the control \b Face it-self. You can easy specify the \b + Point by either picking a node in the 3D Viewer or by selecting a vertex + in the Object Browser. The \b Face can be either picked by mouse in + the 3D Viewer or its ID can be entered by typing.
                • +
                • Set up a \b Direction to be compared with the normal of the + control face. You can either pick a node in the 3D Viewer then a \b Direction + from the coordinate system origin to the selected node will be set, + or you can pick two nodes (holding Shift button) then a \b Direction + from the first to the second node will be set.
                • +
                +
              3. + +
              4. Click the \b Apply or Apply and Close button to confirm the operation.
              5. +
              + +
              See Also a sample TUI Script of a +\ref tui_reorient_faces "Reorient faces by vector" operation. + +*/ diff --git a/doc/salome/gui/SMESH/input/revolution.doc b/doc/salome/gui/SMESH/input/revolution.doc index 67dfdb374..1d13bb23a 100644 --- a/doc/salome/gui/SMESH/input/revolution.doc +++ b/doc/salome/gui/SMESH/input/revolution.doc @@ -16,31 +16,42 @@ on the revolution axis). \image html image92.png
              "Revolution" button
              -The following dialog box shall appear: +The following dialog common for line and planar elements will appear: \image html revolution1.png -\image html revolution2.png
            11. -In this dialog box you should specify: +In this dialog you should specify:
              • the type of elements which will be extruded (1D or 2D),
              • -
              • specify the IDs of the elements which will be revolved +
              • specify the IDs of the elements which will be revolved: + + +
                  +
                • Select the whole mesh, submesh or group activating this +checkbox; or
                • +
                • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                • +
                • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                • +
                • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                • +
                +
              • + +
              • specify the axis of revolution:
                  -
                • Check on Select the whole mesh, submesh or group option -
                • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                • -
                • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                • +
                • specify the cooordinates of the start \b Point of the vector of revolution;
                • +
                • specify the \b Vector of revolution through the coordinates of its +end point with respect to the coordinates of the start +point. Alternatively, it is possible to specify the vector through the +normal to the selected face.
              • -
              • specify the axis (point and vector) around which the elements will -be revolved,
              • -
              • angle of rotation and number of steps,
              • + +
              • specify the angle of revolution and the number of revolution steps,
                • Angle by Step - the elements are extruded by the specified angle at each step (i.e. for Angle=30 and Number of Steps=2, the elements will be extruded by 30 degrees twice for a total of 30*2=60)
                • @@ -52,15 +63,18 @@ be revolved, \image html revolutionsn1.png "Example of Revolution with Total Angle"
                -
              • tolerance of rotation
              • -
              +
            12. -
            13. Preview checkbox allows showing the results of parameter-setting in the viewer
            14. -
            15. Generate Groups checkbox allows copying the groups of +
            16. specify the tolerance for the operation
            17. + +
            18. activate Preview checkbox to show the parameter-setting in the viewer
            19. +
            20. activate Generate Groups checkbox to copy the groups of elements of the source mesh to the newly created one.
            21. + -
            22. Click the \b Apply or \b OK button.
            23. +
            24. Click \b Apply or Apply and Close button to confirm the +operation.
            diff --git a/doc/salome/gui/SMESH/input/rotation.doc b/doc/salome/gui/SMESH/input/rotation.doc index e64357276..cb46359fd 100644 --- a/doc/salome/gui/SMESH/input/rotation.doc +++ b/doc/salome/gui/SMESH/input/rotation.doc @@ -2,41 +2,70 @@ \page rotation_page Rotation -\n This operation allows to rotate in space your mesh or +\n This operation allows to rotate in space the mesh or some of its elements. +To rotate the mesh: + +
              +
            1. From the \b Modification menu choose \b Transformation -> \b Rotation item or click +"Rotation" button in the toolbar. + \image html rotation_ico.png "Rotation button" -To rotate your mesh: -\par -From the \b Modification choose \b Transformation and from its sub-menu -select the \b Rotation item. The following dialog box shall appear: +The following dialog will appear: \image html rotation.png -\par -In this dialog box you can specify the elements which should be -rotated + +
            2. + +
            3. +In this dialog: +
                +
              • specify the IDs of the elements which will be rotated: +
                  -
                • Check on Select the whole mesh, submesh or group option -
                • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                • -
                • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                • +
                • Select the whole mesh, submesh or group activating this +checkbox; or
                • +
                • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                • +
                • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                • +
                • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                - and the rotation parameters: +
              • + +
              • specify the axis of rotation:
                  -
                • \b Axis: point and vector
                • -
                • \b Angle of rotation
                • +
                • specify the cooordinates of the start \b Point of the vector of rotation;
                • +
                • specify the \b Vector of rotation through the coordinates of its +end point with respect to the coordinates of the start point;
                +
              • +
              • specify the \b Angle of rotation
              • -\n Toggle the corresponding checkbox to Select whole mesh, submesh or group. -\n When Move elements radio button is selected, the source mesh (or elements) is created at the new location and erased from its previous location -\n When Copy elements radio button is selected,the source mesh (or elements) is created at the new location, but it also remains at its previous location and is considered one and single mesh with the result of the rotation. -\n When Create as new mesh radio button is selected, the source mesh (or elements) remains at its previous location and a new mesh is created at the new location and appears in the Object Browser with the default name MeshName_rotated (you can change this name in the adjacent box). -\n Copy groups checkbox allows copying the groups of elements of the source mesh to the newly created one. -\par +
              • specify the conditions of rotation: +
                  +
                • activate Move elements radio button to create the source +mesh (or elements) at the new location and erase it from the previous location;
                • +
                • activate Copy elements radio button to create the source +mesh (or elements) at the new location, but leave it at the previous +location, the source mesh will be considered one and single mesh with the result of the rotation;
                • +
                • activate Create as new mesh radio button to leave the +source mesh (or elements) at its previous location and create a new +mesh at the new location, the new mesh appears in the Object Browser +with the default name MeshName_rotated (it is possible to change this +name in the adjacent box);
                • +
                • activate Copy groups checkbox to copy the groups of elements of the source mesh to the newly created mesh.
                • +
                +
              • + +
              • activate Preview checkbox to show the result of transformation in the viewer
              • +
              • click \b Apply or Apply and Close button to confirm the +operation.
              • +
              +
            \image html rotation1.png "The initial mesh" diff --git a/doc/salome/gui/SMESH/input/scalar_bar.doc b/doc/salome/gui/SMESH/input/scalar_bar.doc new file mode 100755 index 000000000..146a7fa78 --- /dev/null +++ b/doc/salome/gui/SMESH/input/scalar_bar.doc @@ -0,0 +1,40 @@ +/*! + +\page scalar_bar_dlg Scalar Bar properties + +In this dialog you can specify the properties of the scalar bar + +\image html scalar_bar_dlg.png + +
              +
            • Scalar Range in this menu you can specify +Min value and Max value of the Scalar Bar
            • + +
            • Font - in this menu you can set type, face and color for +the font of Title and Labels of the Scalar +Bar
            • + +
            • Colors & Labels - in this menu you can set the number of +colors and the number of labels of the Scalar +Bar
            • + +
            • Orientation - allows choosing between vertical and +horizontal orientation of the Scalar Bar
            • . + +
            • Origin & Size Vertical & Horizontal - allows defining the +location (X and Y) and size (Width and +Height) of Scalar Bar
            • +
                +
              • X: abscissa of the origin (from the left +side)
              • +
              • Y: ordinate of the origin (from the bottom)
              • +
              +
            • Distribution in this menu you can Show/Hide distribution histogram of the values of the Scalar Bar and specify histogram properties
            • +
                +
              • Multicolor the histogram is colored as Scalar Bar
              • +
              • Monocolor the histogram is colored as selected with Distribution color selector
              • +
              +
            + + +*/ diff --git a/doc/salome/gui/SMESH/input/scale.doc b/doc/salome/gui/SMESH/input/scale.doc new file mode 100644 index 000000000..57626fd78 --- /dev/null +++ b/doc/salome/gui/SMESH/input/scale.doc @@ -0,0 +1,134 @@ +/*! + +\page scale_page Scale + +\n This geometrical operation allows to scale in space your mesh +or some of its elements. + +To scale a mesh: + +
              +
            1. From the \b Modification menu choose \b Transformation -> \b Scale +\b Transform item. + +One of the following dialogs will appear: + +With one scale factor: +\image html scale01.png + +Or with different scale factors for axes: +\image html scale02.png + +
            2. + +
            3. +In the dialog: +
                +
              • specify the IDs of the translated elements: + +
                  +
                • Select the whole mesh, submesh or group activating this +checkbox; or
                • +
                • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                • +
                • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                • +
                • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                • +
                +
              • + +
              • specify the base point for scale
              • + +
              • specify the scale factor
              • + +
              • specify the conditions of scale: +
                  +
                • activate Move elements radio button to scale the selected +mesh (or elements) without creating a copy;
                • +
                • activate Copy elements radio button to duplicate the selected +mesh (or elements) and to apply scaling to the copy within the same mesh;
                • +
                • activate Create as new mesh radio button to leave the +selected mesh (or elements) at its previous location and create a new +mesh of the scaled copy of the selected elements; the new mesh appears in the Object Browser +with the default name MeshName_scaled (it is possible to change this +name in the adjacent box);
                • +
                • activate Copy groups checkbox to copy the groups of +elements existing in the source mesh to the newly created mesh.
                • +
                +
              • + + + +
              • activate Preview checkbox to show the result of transformation in the viewer
              • +
              • click \b Apply or Apply and Close button to confirm the operation.
              • +
              +
            + + + +Example of using: + +1. Create quandrangle mesh 3x3 on a simple planar face (200x200) + +\image html scaleinit01.png + +and union 3 faces (along axis Z) to group "gr_faces" + +\image html scaleinit02.png + + + +2. Perform scale operation for the whole mesh and create a new mesh: + +\image html scale03.png + +result after operation: + +\image html scaleres03.png + + + +3. Perform scale operation for the whole mesh and copy elements: + +\image html scale04.png + +result after operation: + +\image html scaleres04.png + + + +4. Perform scale operation for a group of faces and copy elements: + +\image html scale06.png + +result after operation: + +\image html scaleres06.png + + + +5. Perform scale operation for two edges and move elements: + +\image html scale07.png + +result after operation: + +\image html scaleres07.png + + + +6. Perform scale operation for one face and move elements: + +\image html scale09.png + +result after operation: + +\image html scaleres09.png + + +
            See Also a sample TUI Script of a \ref tui_scale "Scale" operation. + + +*/ diff --git a/doc/salome/gui/SMESH/input/selection_filter_library.doc b/doc/salome/gui/SMESH/input/selection_filter_library.doc index 66c72a0e8..4e58dcee0 100644 --- a/doc/salome/gui/SMESH/input/selection_filter_library.doc +++ b/doc/salome/gui/SMESH/input/selection_filter_library.doc @@ -9,8 +9,8 @@ via Tools / Selection filter library. \image html selectionfilterlibrary.png Library file name shows the path and the file name where your -filters will be stored. By clicking the Browse button you can load an -existing filter library. +filters will be stored. By clicking the Browse button you can +load an existing filter library. \n Names of filters lists the filters created or uploaded for the current study. You can \b Add or \b Delete filters. \n In Filter name box you can specify the name for your @@ -31,18 +31,28 @@ specify logical relations between criteria using \b Binary operators Or and And. \n Some criteria should have the additional parameter of \b Tolerance. -When we create a Standalone Group using filters (for this click -Set Filters button in the Create Group menu), the menu -for setting filters looks a bit differently. Toggling Insert filter -in viewer checkbox enables to preview the group selected with your -current filter in the viewer. -\n In the \b Source field you choose if the filter will be applied to -the whole \b Mesh, the Initial Selection or the Current Group. -\n Copy from... button gives you a possibility to load an +When we create a group using filters (for this click +Set Filters button in the Create Group dialog), the menu +for setting filters looks a bit differently (see below). Switching +on Insert filter in viewer checkbox limits selection of elements +in the Viewer using your current filter. +
            +In the \b Source field you choose if the filter will be applied to +the whole \b Mesh, the Initial Selection or the Current +Group. If \b Mesh is chosen, the elements satisfying the filter +will be selected in the 3D Viewer. If Initial Selection is +chosen, the filter will be applied to the selected elements and the +elements rejected by the filter will be deseleced. If Current +Group is chosen, the filter will be applied to the list of +elements in the Greate Croup dialog and the elements rejected +by the filter will be removed from the list. +
            +Copy from... button gives you a possibility to load an existing filter from Selection filter library and Add to... button gives you a possibility to save your current filter in the Library. -\n Note: If the button Apply and Close is disabled, there +
            +Note: If the button Apply and Close is disabled, there is no selected mesh in the Object Browser and the filter can not be created. You have to select the mesh and the button will be enabled. @@ -66,20 +76,19 @@ shape, the algorithm works slower. IDs. Threshold Value can be, for example: "1,2,3,50-60,63,67,70-78"
          5. -Color of Group allows selection of entities belonging to Group with -specified color defined by the Threshold Value. +Color of Group allows selection of entities belonging to the Group with +the color defined by the Threshold Value.
          6. -Some criteria are applicable to all Entity types: except for +Some criteria are applicable to all Entity types, except for Nodes
            • -Linear allows selection of Linear or Quadratic (in case of -Unary set to "Not" state) +Linear allows selection of Linear or Quadratic elements (if Unary is set to "Not")
            • Geometry type allows selection of elements by their geometric type defined by the Threshold Value. The list of available geometric -types depends on element entity type defined by the Threshold Value. +types depends on the element entity type defined by the Threshold Value.
            @@ -97,12 +106,25 @@ specified arbitrary surface within a given Tolerance. -Additional criteria to select mesh Edges are the following: +The following criteria allow selecting mesh Nodes: +
            • +Free nodes selects nodes not belonging to any mesh element. +
            • +Double nodes selects a node coincident with other nodes +(within a given Tolerance). +See also \ref tui_double_nodes_control "Double Nodes quality control". +
            • +
            + +The following criteria allow selecting mesh Edges:
            • Free Borders selects free 1D mesh elements, i.e. edges belonging to one face only. See also a \ref free_borders_page "Free Borders quality control".
            • +Double edges selects 1D mesh elements basing on the same set of nodes. +See also \ref filter_double_elements "Double Elements quality control". +
            • Borders at Multi-Connections selects edges belonging to several faces. The number of faces should be more, less or equal (within a given Tolerance) to the predefined Threshold Value. See also a @@ -115,7 +137,7 @@ See also a
            -Additional criteria to select mesh Faces are the following: +The following criteria allow selecting mesh Faces:
            • Aspect ratio selects 2D mesh elements with an aspect ratio (see also an \ref aspect_ratio_page "Aspect Ratio quality control"), which is more, less or equal @@ -145,8 +167,17 @@ Additional criteria to select mesh Faces are the following: one element of mesh only. See also a \ref free_edges_page "Free Edges quality control".
            • -Free faces selects 3D mesh elements wich belong less than to -two volumes. +Free faces selects 2D mesh elements wich belong to less than two volumes. +
            • +Double faces selects 2D mesh elements basing on the same set of nodes. +See also \ref filter_double_elements "Double Elements quality control". +
            • +Faces with bare border selects 2D mesh elements having a free border without an edge on it. +See also \ref bare_border_faces_page "Bare border faces quality control". +
            • +Over-constrained faces selects 2D mesh elements having only one border shared +with other 2D elements. +See also \ref over_constrained_faces_page "Over-constrained faces quality control".
            • Borders at Multi-Connections 2D selects cells consisting of edges belonging to several elements of mesh. The number of mesh elements should be more, less or equal @@ -158,10 +189,21 @@ See also a length, which is more, less or equal (within a given Tolerance) to the predefined Threshold Value. See also a \ref length_2d_page "Length 2D quality control". +
            • +Coplanar faces selects mesh faces neighboring the one selected +by ID in Threshold Value field, if the angle between the +normal to the neighboring face and the normal to the selected face is less then the +angular tolerance (defined in degrees). Selection continues among all neighbor faces of already +selected ones.
              +
            • +Element Diameter 2D selects triangles and quadrangles composed of the edges and +diagonals with a value of length, which is more, less or equal +(within a given Tolerance) to the predefined Threshold Value. See also a +\ref max_element_length_2d_page "Element Diameter 2D quality control".
            -Additional criteria to select mesh Volumes are the following: +The following criteria allow selecting mesh Volumes:
            • Aspect ratio 3D selects 3D mesh elements with an aspect ratio (see also an \ref aspect_ratio_3d_page "Aspect Ratio 3D quality control"), which is more, less or equal @@ -171,8 +213,23 @@ Additional criteria to select mesh Volumes are the following: \ref volume_page "Volume quality control"), which is more, less or equal (within a given Tolerance) to the predefined Threshold Value.
            • +Element Diameter 3D selects 3D mesh elements composed of the edges and +diagonals with a value of length, which is more, less or equal +(within a given Tolerance) to the predefined Threshold Value. See also a +\ref max_element_length_3d_page "Element Diameter 3D quality control". +
            • +Double volumes selects 3D mesh elements basing on the same set of nodes. +See also \ref filter_double_elements "Double Elements quality control". +
            • Bad oriented volume selects mesh volumes, which are incorrectly oriented from the point of view of MED convention. +
            • +Over-constrained volumes selects mesh volumes having only one border shared +with other volumes. +See also \ref over_constrained_volumes_page "Over-constrained volumes quality control". +
            • +Volumes with bare border selects 3D mesh elements having a free border without a face on it. +See also \ref bare_border_volumes_page "Bare border volumes quality control".
            diff --git a/doc/salome/gui/SMESH/input/skew.doc b/doc/salome/gui/SMESH/input/skew.doc index 5235b4ddd..036c70d83 100644 --- a/doc/salome/gui/SMESH/input/skew.doc +++ b/doc/salome/gui/SMESH/input/skew.doc @@ -14,7 +14,8 @@ criterion can be applied to elements composed of 4 and 3 nodes
            1. Display your mesh in the viewer.
            2. -
            3. Choose Controls > Skew or click "Skew" button of the toolbar. +
            4. Choose Controls > Face Controls > Skew or click +"Skew" button of the toolbar. \image html image40.png
              "Skew" button
              diff --git a/doc/salome/gui/SMESH/input/smeshpy_interface.doc b/doc/salome/gui/SMESH/input/smeshpy_interface.doc index 17255212c..e725ef99c 100644 --- a/doc/salome/gui/SMESH/input/smeshpy_interface.doc +++ b/doc/salome/gui/SMESH/input/smeshpy_interface.doc @@ -2,128 +2,137 @@ \page smeshpy_interface_page Python interface -\n Python package smesh defines several classes, destined for easy and +Python package smesh defines several classes, destined for easy and clear mesh creation and edition. -\n Documentation for smesh package is available in two forms: - -\n The structured - documentation for smesh package, where all methods and - classes are grouped by their functionality, like it is done in the GUI documentation -\n and the \ref smeshDC "linear documentation for smesh package" - grouped only by classes, declared in the smesh.py file. - -\n The main page of the \ref smeshDC "linear documentation for smesh package" - contains a list of data structures and a list of - functions, provided by the package smesh.py. The first item in - the list of data structures (\ref smeshDC::smeshDC "class smesh") - also represents documentation for the methods of the package smesh.py itself. - -\n The package smesh.py provides an interface to create and handle - meshes. Use it to create an empty mesh or to import it from the data file. - -\n Once a mesh has been created, it is possible to manage it via its own - methods, described at \ref smeshDC::Mesh "class Mesh" documentation - (it is also accessible by the second item "class Mesh" in the list of data structures). - -\n Class Mesh allows assigning algorithms to a mesh. -\n Please note, that some algorithms, - included in the standard Salome installation are always available: - - REGULAR(1D), COMPOSITE(1D), MEFISTO(2D), Quadrangle(2D), Hexa(3D), etc. - -\n There are also some algorithms, which can be installed optionally, -\n some of them are based on open-source meshers: - - NETGEN(1D-2D,2D,1D-2D-3D,3D), - -\n others are based on commercial meshers: - - GHS3D(3D), BLSURF(2D). - -\n To add hypotheses, use the interfaces, provided by the assigned +Documentation for smesh package is available in two forms: + +The structured +documentation for smesh package, where all methods and +classes are grouped by their functionality, like it is done in the GUI documentation +and the \ref smeshDC "linear documentation for smesh package" +grouped only by classes, declared in the smesh.py file. + +The main page of the \ref smeshDC "linear documentation for smesh package" +contains a list of data structures and a list of +functions, provided by the package smesh.py. The first item in +the list of data structures (\ref smeshDC::smeshDC "class smesh") +also represents documentation for the methods of the package smesh.py itself. + +The package smesh.py provides an interface to create and handle +meshes. Use it to create an empty mesh or to import it from the data file. + +Once a mesh has been created, it is possible to manage it via its own +methods, described at \ref smeshDC::Mesh "class Mesh" documentation +(it is also accessible by the second item "class Mesh" in the list of data structures). + +Class \b Mesh allows assigning algorithms to a mesh. +Please note, that some algorithms, included in the standard SALOME +distribution are always available: +- REGULAR (1D) +- COMPOSITE (1D) +- MEFISTO (2D) +- Quadrangle (2D) +- Hexa(3D) +- etc... + +To add hypotheses, use the interfaces, provided by the assigned algorithms. -\n Below you can see an example of usage of the package smesh for 3d mesh generation. +Below you can see an example of usage of the package smesh for 3d mesh generation. -

              Example of 3d mesh generation with NETGEN:

              +\anchor example_3d_mesh +

              Example of 3d mesh generation:

              -\n from geompy import * -\n import smesh +\code +from geompy import * +import smesh -# Geometry -\n # an assembly of a box, a cylinder and a truncated cone meshed with tetrahedral. +### +# Geometry: an assembly of a box, a cylinder and a truncated cone +# meshed with tetrahedral +### -# Define values -\n name = "ex21_lamp" -\n cote = 60 -\n section = 20 -\n size = 200 -\n radius_1 = 80 -\n radius_2 = 40 -\n height = 100 +# Define values +name = "ex21_lamp" +cote = 60 +section = 20 +size = 200 +radius_1 = 80 +radius_2 = 40 +height = 100 -# Build a box -\n box = MakeBox(-cote, -cote, -cote, +cote, +cote, +cote) +# Build a box +box = MakeBox(-cote, -cote, -cote, +cote, +cote, +cote) -# Build a cylinder -\n pt1 = MakeVertex(0, 0, cote/3) -\n di1 = MakeVectorDXDYDZ(0, 0, 1) -\n cyl = MakeCylinder(pt1, di1, section, size) +# Build a cylinder +pt1 = MakeVertex(0, 0, cote/3) +di1 = MakeVectorDXDYDZ(0, 0, 1) +cyl = MakeCylinder(pt1, di1, section, size) -# Build a truncated cone -\n pt2 = MakeVertex(0, 0, size) -\n cone = MakeCone(pt2, di1, radius_1, radius_2, height) +# Build a truncated cone +pt2 = MakeVertex(0, 0, size) +cone = MakeCone(pt2, di1, radius_1, radius_2, height) -# Fuse -\n box_cyl = MakeFuse(box, cyl) -\n piece = MakeFuse(box_cyl, cone) +# Fuse +box_cyl = MakeFuse(box, cyl) +piece = MakeFuse(box_cyl, cone) -# Add in study -\n addToStudy(piece, name) +# Add to the study +addToStudy(piece, name) -# Create a group of faces -\n group = CreateGroup(piece, ShapeType["FACE"]) -\n group_name = name + "_grp" -\n addToStudy(group, group_name) -\n group.SetName(group_name) +# Create a group of faces +group = CreateGroup(piece, ShapeType["FACE"]) +group_name = name + "_grp" +addToStudy(group, group_name) +group.SetName(group_name) -# Add faces in the group -\n faces = SubShapeAllIDs(piece, ShapeType["FACE"]) -\n UnionIDs(group, faces) +# Add faces to the group +faces = SubShapeAllIDs(piece, ShapeType["FACE"]) +UnionIDs(group, faces) -# Create a mesh +### +# Create a mesh +### -# Define a mesh on a geometry -\n tetra = smesh.Mesh(piece, name) +# Define a mesh on a geometry +tetra = smesh.Mesh(piece, name) -# Define 1D hypothesis -\n algo1d = tetra.Segment() -\n algo1d.LocalLength(10) +# Define 1D hypothesis +algo1d = tetra.Segment() +algo1d.LocalLength(10) -# Define 2D hypothesis -\n algo2d = tetra.Triangle() -\n algo2d.LengthFromEdges() +# Define 2D hypothesis +algo2d = tetra.Triangle() +algo2d.LengthFromEdges() -# Define 3D hypothesis -\n algo3d = tetra.Tetrahedron(smesh.NETGEN) -\n algo3d.MaxElementVolume(100) +# Define 3D hypothesis +algo3d = tetra.Tetrahedron() +algo3d.MaxElementVolume(100) -# Compute the mesh -\n tetra.Compute() +# Compute the mesh +tetra.Compute() -# Create a groupe of faces -\n tetra.Group(group) +# Create a groupe of faces +tetra.Group(group) -\n Examples of Python scripts for all Mesh operations are available by -the following links: +\endcode -
                -
              • \subpage tui_creating_meshes_page
              • -
              • \subpage tui_viewing_meshes_page
              • -
              • \subpage tui_defining_hypotheses_page
              • -
              • \subpage tui_quality_controls_page
              • -
              • \subpage tui_grouping_elements_page
              • -
              • \subpage tui_modifying_meshes_page
              • -
              • \subpage tui_transforming_meshes_page
              • -
              +Examples of Python scripts for all Mesh operations are available by +the following links: +- \subpage tui_creating_meshes_page +- \subpage tui_cartesian_algo +- \subpage tui_viewing_meshes_page +- \subpage tui_defining_hypotheses_page +- \subpage tui_quality_controls_page +- \subpage tui_filters_page +- \subpage tui_grouping_elements_page +- \subpage tui_modifying_meshes_page +- \subpage tui_transforming_meshes_page +- \subpage tui_notebook_smesh_page +- \subpage tui_measurements_page +- \subpage tui_generate_flat_elements_page +- \subpage tui_work_on_objects_from_gui */ diff --git a/doc/salome/gui/SMESH/input/smoothing.doc b/doc/salome/gui/SMESH/input/smoothing.doc index 77a59f880..dc15270a8 100644 --- a/doc/salome/gui/SMESH/input/smoothing.doc +++ b/doc/salome/gui/SMESH/input/smoothing.doc @@ -7,38 +7,40 @@ To apply smoothing to the elements of your mesh:
                -
              1. Display a mesh or a submesh in the 3D viewer.
              2. +
              3. In the \b Modification menu select the \b Smoothing item or click "Smoothing" button in the toolbar. \image html image84.png
                "Smoothing" button
                -The dialog box contains the following fields which should be -specified: +The following dialog will appear: \image html smoothing.png +
              4. +
              5. In this dialog:
                  -
                • Id Elements field allows to specify the elements which -should be smoothed +
                • specify the IDs of the elements which will be smoothed:
                    -
                  • Check on Select the whole mesh, submesh or group option -
                  • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                  • -
                  • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                  • +
                  • Select the whole mesh, submesh or group activating this +checkbox; or
                  • +
                  • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                  • +
                  • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                  • +
                  • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                • -Fixed nodes ids: some nodes keep their location during -smoothing. If a mesh is built on a geometry shape, the nodes built on -geometrical edges are always fixed. If smoothing is applied to a part -of a mesh then the boundary nodes of an elements set are also -fixed. Any other nodes may be additionally fixed. Fixed nodes can be -selected manually or by filters too. -
                • Smoothing Method: + +
                • define the Fixed nodes ids that should remain at their location during +smoothing. If a mesh is built on a shape, the nodes built on its +geometric edges are always fixed. If the smoothing is applied to a part +of the mesh, the nodes of boundary elements are also +fixed. It is possible to additionally fix any other nodes. The fixed nodes can be +selected manually or by filters, just as the smoothed elements.
                • +
                • choose the Smoothing Method:
                  • \b Laplacian smoothing pulls a node toward the center of surrounding nodes directly connected to that node along an element @@ -47,32 +49,29 @@ edge. element-area-weighted centroid of the surrounding elements.
                  • Typically, the Laplacian method will produce the mesh with the least element -distortion. It is also the faster method.Centroidal smoothing usually -produces a mesh that has more uniform element sizes. Both methods +distortion. It is also the fastest method. Centroidal smoothing usually +produces a mesh with more uniform element sizes. Both methods produce good results with "free" meshes.
                  \image html image83.gif
                • -
                • Iteration limit: both of the smoothing methods use an -iterative procedure to converge toward a smoothed mesh. All nodes are -smoothed according to one of the techniques shown above. Then the -smoothing is reevaluated with the updated nodal locations. This -process continues until the maximum number of iterations has been -exceeded, or all elements has aspect ratio less or equal than the +
                • specify the Iteration limit. Both smoothing methods +iterate through a number of steps to produce the resulting smoothed +mesh. At each new step the smoothing is reevaluated with the updated nodal locations. This +process continues till the limit of iterations has been +exceeded, or till the aspect ratio of all element is less than or equal to the specified one.
                • -
                • Max. aspect ratio allows to define the quality at which the -smoothing algorithm should stop the iterations as the target of the -operation has been reached.
                • -
                • When in parametric space radio button is checked, the -algorithm tries to improve the shape of faces in the parametric space +
                • specify the Max. aspect ratio - the target mesh quality at which the +smoothing algorithm should stop the iterations.
                • +
                • activate in parametric space checkbox if it is necessary to +to improve the shape of faces in the parametric space of geometrical surfaces on which they are generated, else the shape of faces in the 3D space is improved.
                -
              6. -
              7. Click the \b Apply or \b OK button to confirm the operation.
              8. +
              9. Click \b Apply or Apply and Close button to confirm the operation.
              \image html smoothing1.png "The initial mesh" diff --git a/doc/salome/gui/SMESH/input/split_to_tetra.doc b/doc/salome/gui/SMESH/input/split_to_tetra.doc new file mode 100644 index 000000000..325c30c11 --- /dev/null +++ b/doc/salome/gui/SMESH/input/split_to_tetra.doc @@ -0,0 +1,55 @@ +/*! + +\page split_to_tetra_page Splitting volumes into tetrahedra + +\n This operation allows to split volumic elements into tetrahedra. +2D mesh is modified accordingly. + +To split volumes: +
                +
              1. Display a mesh or a submesh in the 3D viewer.
              2. +
              3. In the \b Modification menu select the Split into Tetrahedra item or +click "Split into Tetrahedra" button in the toolbar. + +\image html split_into_tetra_icon.png +
                "Split into Tetrahedra" button
                + +The following dialog box will appear: + +\image html split_into_tetra.png + +\par +
                  +
                • The main list contains the list of volumes. You can click on +a volume in the 3D viewer and it will be highlighted (lock Shift +keyboard button to select several volumes). Click \b Add button and +the ID of this volume will be added to the list. To remove the +selected element or elements from the list click \b Remove button. Sort +list button allows to sort the list of IDs. \b Filter button allows to +apply a definite filter to the selection of volumes. +
                  Note: If you split not all adjacent non-tetrahedral volumes, your mesh becomes +non-conform.
                • +
                • Apply to all radio button allows to split all +volumes of the currently displayed mesh or submesh.
                • +
                + +
                  +
                • \b Split hexahedron + +
                    +
                  • Into 5 tetrahedra, Into 6 tetrahedra and Into 24 tetrahedra allows to +specify the number of tetrahedra a hexahedron will be split into. If the specified method does +not allow to get a conform mesh, a generic solution is applied: an additional node +is created at the gravity center of a hexahedron, serving an apex of tetrahedra, all quadrangle sides of the hexahedron are split into two triangles each serving a base of a new tetrahedron.
                  • +
                  + +
                • + +
                • Select from a set of fields allows to choose a submesh or an +existing group whose elements will be automatically added to the +list.
                • +
                + +
              4. Click the \b Apply or Apply and Close button to confirm the operation.
              5. +
              +*/ diff --git a/doc/salome/gui/SMESH/input/symmetry.doc b/doc/salome/gui/SMESH/input/symmetry.doc index 67068e981..924e4eeb0 100644 --- a/doc/salome/gui/SMESH/input/symmetry.doc +++ b/doc/salome/gui/SMESH/input/symmetry.doc @@ -5,48 +5,81 @@ \n This geometrical operation allows to perform a symmetrical copy of your mesh or some of its elements. +To create a symmetrical copy of the mesh: + +
                +
              1. From the \b Modification menu choose \b Transformation -> \b Symmetry item or click +"Symmetry" button in the toolbar. + \image html symmetry.png "Symmetry button" -To apply symmetry to your mesh: -\par -From the \b Modification choose \b Transformation and from its -sub-menu select the \b Symmetry item. The following dialog box shall -appear: +One of the following dialogs will appear: -\image html symmetry1.png +It is possible to mirror a mesh or some of its elements through: +\image html symmetry1.png "a point (defined by a point and a vector)" +\image html symmetry2.png "an axis" +\image html symmetry3.png "a plane (defined by a point and a normal to the plane)" -\image html symmetry2.png +
              2. -\image html symmetry3.png +
              3. In the dialog: +
                  +
                • specify the IDs of the elements for the symmetry operation: + +
                    +
                  • Select the whole mesh, submesh or group activating this +checkbox; or
                  • +
                  • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                  • +
                  • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                  • +
                  • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                  • +
                  +
                • -\par -This operation has three options, you can symmetrically copy your mesh -or some of its elements specifying: +
                • depending on the nature of the mirror object: +
                    +
                  • if the mesh is mirrored through a point, specify the coordinates +of the point
                  • +
                  • if the mesh is mirrored through an axis:
                      -
                    • one point
                    • -
                    • one axis (point and vector)
                    • -
                    • one plane (point and normal)
                    • +
                    • specify the cooordinates of the start \b Point of the axis vector;
                    • +
                    • specify the axis \b Vector through the coordinates of its +end point with respect to the coordinates of the start point;
                    +
                  • +
                  • if the mesh is mirrored through a plane:
                      -
                    • Select elements for the symmetry operation +
                    • specify the cooordinates of the \b Point lying on the plane;
                    • +
                    • specify the axis \b Vector through the coordinates of its +end point with respect to the coordinates of the start point.
                    • +
                    +
                  • + +
                  • specify the conditions of symmetry operation:
                      -
                    • Check on Select the whole mesh, submesh or group option -
                    • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                    • -
                    • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                    • +
                    • activate Move elements radio button to create the source +mesh (or elements) at the new location and erase it from the previous location;
                    • +
                    • activate Copy elements radio button to create the source +mesh (or elements) at the new location, but leave it at the previous +location, the source mesh will be considered one and single mesh with the result of the rotation;
                    • +
                    • activate Create as new mesh radio button to leave the +source mesh (or elements) at its previous location and create a new +mesh at the new location, the new mesh appears in the Object Browser +with the default name MeshName_rotated (it is possible to change this +name in the adjacent box);
                    • +
                    • activate Copy groups checkbox to copy the groups of elements of the source mesh to the newly created mesh.
                  • -
                  • When Move elements radio button is selected, the source mesh (or elements) is created at the new location and erased from its previous location
                  • -
                  • When Copy elements radio button is selected,the source mesh (or elements) is created at the new location, but it also remains at its previous location and is considered one and single mesh with the result of the translation.
                  • -
                  • When Create as new mesh radio button is selected, the source mesh (or elements) remains at its previous location and a new mesh is created at the new location and appears in the Object Browser with the default name MeshName_mirrored (you can change this name in the adjacent box).
                  • -
                  • Copy groups checkbox allows copying the groups of elements of the source mesh to the newly created one.
                  • +
                  +
                • activate Preview checkbox to show the result of transformation in the viewer
                • +
                • click \b Apply or Apply and Close button to confirm the +operation.
                -\par +

              See Also a sample TUI Script of a \ref tui_symmetry "Symmetry" operation. diff --git a/doc/salome/gui/SMESH/input/taper.doc b/doc/salome/gui/SMESH/input/taper.doc index d2c4dd2b4..86472eabe 100644 --- a/doc/salome/gui/SMESH/input/taper.doc +++ b/doc/salome/gui/SMESH/input/taper.doc @@ -13,8 +13,8 @@ for elements consisting of 4 nodes.
              1. Display your mesh in the viewer.
              2. -
              3. Choose Controls > Taper or click "Taper" button in -the toolbar. +
              4. Choose Controls > Face Controls > Taper or click +"Taper" button in the toolbar. \image html image36.png
                "Taper" button
                diff --git a/doc/salome/gui/SMESH/input/translation.doc b/doc/salome/gui/SMESH/input/translation.doc index 45565acaa..56c1dfb05 100644 --- a/doc/salome/gui/SMESH/input/translation.doc +++ b/doc/salome/gui/SMESH/input/translation.doc @@ -5,45 +5,70 @@ \n This geometrical operation allows to translate in space your mesh or some of its elements. +To translate a mesh: + +
                  +
                1. From the \b Modification menu choose \b Transformation -> \b Translation item or click +"Translation" button in the toolbar. + \image html translation.png "Translation button" -To translate your mesh: -\par -From the \b Modification choose \b Transformation and from its -sub-menu select the \b Translation item. The following dialog box -shall appear: +One of the following dialogs will appear: + +It is possible to define the vector of thanslation: +\image html translation1.png "by two points" -\image html translation1.png +\image html translation2.png "by the vector from the origin of coordinates" -\image html translation2.png +
                2. + +
                3. +In the dialog: +
                    +
                  • specify the IDs of the translated elements: -\par -This operation has two options, you can translate in space your mesh -or some of its elements specifying:
                      -
                    • two points (starting and ending)
                    • -
                    • one vector
                    • +
                    • Select the whole mesh, submesh or group activating this +checkbox; or
                    • +
                    • choose mesh elements with the mouse in the 3D Viewer. It is +possible to select a whole area with a mouse frame; or
                    • +
                    • input the element IDs directly in ID Elements field. The selected elements will be highlighted in the +viewer; or
                    • +
                    • apply Filters. Set filter button allows to apply a filter to the selection of elements. See more +about filters in the \ref selection_filter_library_page "Selection filter library" page.
                    +
                  • +
                  • specify the vector of translation:
                      -
                    • Select elements for the translation operation +
                    • specify the cooordinates of the start and end \b Points of the +vector of translation; or
                    • +
                    • specify the end point of the \b Vector of rotation starting at the +origin of coordinates.
                    • +
                    +
                  • + +
                  • specify the conditions of translation:
                      -
                    • Check on Select the whole mesh, submesh or group option -
                    • Choosing them manually with the mouse in the 3D Viewer. You can -click on an element in the 3D viewer and it will be highlighted
                    • -
                    • Applying Filters. The Set filter button allows to apply a -definite filter to selection of the elements. See more -about filters on the -\ref selection_filter_library_page "Selection filter library" page.
                    • +
                    • activate Move elements radio button to create the source +mesh (or elements) at the new location and erase it from the previous location;
                    • +
                    • activate Copy elements radio button to create the source +mesh (or elements) at the new location, but leave it at the previous +location, the source mesh will be considered one and single mesh with the result of the rotation;
                    • +
                    • activate Create as new mesh radio button to leave the +source mesh (or elements) at its previous location and create a new +mesh at the new location, the new mesh appears in the Object Browser +with the default name MeshName_rotated (it is possible to change this +name in the adjacent box);
                    • +
                    • activate Copy groups checkbox to copy the groups of elements of the source mesh to the newly created mesh.
                  • -
                  • When Move elements radio button is selected, the source mesh (or elements) is created at the new location and erased from its previous location
                  • -
                  • When Copy elements radio button is selected,the source mesh (or elements) is created at the new location, but it also remains at its previous location and is considered one and single mesh with the result of the translation.
                  • -
                  • When Create as new mesh radio button is selected, the source mesh (or elements) remains at its previous location and a new mesh is created at the new location and appears in the Object Browser with the default name MeshName_translated (you can change this name in the adjacent box).
                  • -
                  • Copy groups checkbox allows copying the groups of elements of the source mesh to the newly created one.
                  • + +
                  • activate Preview checkbox to show the result of transformation in the viewer
                  • +
                  • click \b Apply or Apply and Close button to confirm the operation.
                  +
                -
                See Also a sample TUI Script of a -\ref tui_translation "Translation" operation. +
                See Also a sample TUI Script of a \ref tui_translation "Translation" operation. */ diff --git a/doc/salome/gui/SMESH/input/tui_cartesian_algo.doc b/doc/salome/gui/SMESH/input/tui_cartesian_algo.doc new file mode 100644 index 000000000..f1218439b --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_cartesian_algo.doc @@ -0,0 +1,49 @@ +/*! + +\page tui_cartesian_algo Usage of Body Fitting algorithm + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +# create a sphere +sphere = geompy.MakeSphereR( 50 ) +geompy.addToStudy( sphere, "sphere" ) + +# create a mesh and assign a "Body Fitting" algo +mesh = Mesh( sphere ) +cartAlgo = mesh.BodyFitted() + +# define a cartesian grid using Coordinates +coords = range(-100,100,10) +cartHyp = cartAlgo.SetGrid( coords,coords,coords, 1000000) + +# compute the mesh +mesh.Compute() +print "nb hexahedra",mesh.NbHexas() +print "nb tetrahedra",mesh.NbTetras() +print "nb polyhedra",mesh.NbPolyhedrons() +print + +# define the grid by sitting constant spacing +cartHyp = cartAlgo.SetGrid( "10","10","10", 1000000) + +mesh.Compute() +print "nb hexahedra",mesh.NbHexas() +print "nb tetrahedra",mesh.NbTetras() +print "nb polyhedra",mesh.NbPolyhedrons() + + +# define the grid by sitting different spacing in 2 sub-ranges of geometry +spaceFuns = ["5","10+10*t"] +cartAlgo.SetGrid( [spaceFuns, [0.5]], [spaceFuns, [0.5]], [spaceFuns, [0.25]], 2 ) + +mesh.Compute() +print "nb hexahedra",mesh.NbHexas() +print "nb tetrahedra",mesh.NbTetras() +print "nb polyhedra",mesh.NbPolyhedrons() +print + +\endcode + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_creating_meshes.doc b/doc/salome/gui/SMESH/input/tui_creating_meshes.doc index 9048afef1..24161d483 100644 --- a/doc/salome/gui/SMESH/input/tui_creating_meshes.doc +++ b/doc/salome/gui/SMESH/input/tui_creating_meshes.doc @@ -2,7 +2,7 @@ \page tui_creating_meshes_page Creating Meshes -\n First of all see \ref introduction_to_mesh_python_page "Example of 3d mesh generation", +\n First of all see \ref example_3d_mesh "Example of 3d mesh generation", which is an example of good python script style for Mesh module.
                @@ -25,7 +25,7 @@ algo1D.NumberOfSegments(7) algo2D = tetra.Triangle() algo2D.MaxElementArea(800.) -algo3D = tetra.Tetrahedron(smesh.NETGEN) +algo3D = tetra.Tetrahedron() algo3D.MaxElementVolume(900.) # compute the mesh @@ -82,6 +82,68 @@ quadra.Compute() \endcode +
                +

                Change priority of submeshes in Mesh

                + +\code +import salome +import geompy +import smesh +import SMESH + +Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) +[Face_1,Face_2,Face_3,Face_4,Face_5,Face_6] = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"]) + +# create Mesh object on Box shape +Mesh_1 = smesh.Mesh(Box_1) + +# assign mesh algorithms +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(20) +Nb_Segments_1.SetDistrType( 0 ) +MEFISTO_2D = Mesh_1.Triangle() +Max_Element_Area_1 = MEFISTO_2D.MaxElementArea(1200) +Tetrahedron = Mesh_1.Tetrahedron() +Max_Element_Volume_1 = Tetrahedron.MaxElementVolume(40000) + +# create submesh and assign algorithms on Face_1 +Regular_1D_1 = Mesh_1.Segment(geom=Face_1) +Nb_Segments_2 = Regular_1D_1.NumberOfSegments(4) +Nb_Segments_2.SetDistrType( 0 ) +MEFISTO_2D_1 = Mesh_1.Triangle(algo=smesh.MEFISTO,geom=Face_1) +Length_From_Edges_2D = MEFISTO_2D_1.LengthFromEdges() +SubMesh_1 = MEFISTO_2D_1.GetSubMesh() + +# create submesh and assign algorithms on Face_2 +Regular_1D_2 = Mesh_1.Segment(geom=Face_2) +Nb_Segments_3 = Regular_1D_2.NumberOfSegments(8) +Nb_Segments_3.SetDistrType( 0 ) +MEFISTO_2D_2 = Mesh_1.Triangle(algo=smesh.MEFISTO,geom=Face_2) +Length_From_Edges_2D_1 = MEFISTO_2D_2.LengthFromEdges() +SubMesh_2 = MEFISTO_2D_2.GetSubMesh() + +# create submesh and assign algorithms on Face_3 +Regular_1D_3 = Mesh_1.Segment(geom=Face_3) +Nb_Segments_4 = Regular_1D_3.NumberOfSegments(12) +Nb_Segments_4.SetDistrType( 0 ) +MEFISTO_2D_3 = Mesh_1.Triangle(algo=smesh.MEFISTO,geom=Face_3) +Length_From_Edges_2D_2 = MEFISTO_2D_3.LengthFromEdges() +SubMesh_3 = MEFISTO_2D_3.GetSubMesh() + +# check exisiting submesh priority order +[ [ SubMesh_1, SubMesh_3, SubMesh_2 ] ] = Mesh_1.GetMeshOrder() +# set new submesh order +isDone = Mesh_1.SetMeshOrder( [ [ SubMesh_1, SubMesh_2, SubMesh_3 ] ]) +# compute mesh +isDone = Mesh_1.Compute() + +# clear mesh result and compute with other submesh order +Mesh_1.Clear() +isDone = Mesh_1.SetMeshOrder( [ [ SubMesh_2, SubMesh_1, SubMesh_3 ] ]) +isDone = Mesh_1.Compute() + +\endcode +
                \anchor tui_editing_mesh

                Editing of a mesh

                @@ -162,7 +224,7 @@ algo1D.NumberOfSegments(7) algo2D = tetra.Triangle() algo2D.MaxElementArea(800.) -algo3D = tetra.Tetrahedron(smesh.NETGEN) +algo3D = tetra.Tetrahedron() algo3D.MaxElementVolume(900.) # compute the mesh @@ -170,6 +232,11 @@ tetra.Compute() # export the mesh in a MED file tetra.ExportMED("/tmp/meshMED.med", 0) + +# export a group in a MED file +face = geompy.SubShapeAll( box, geompy.ShapeType["FACE"])[0] # a box side +group = tetra.GroupOnGeom( face, "face group" ) # group of 2D elements on the +tetra.ExportMED("/tmp/groupMED.med", meshPart=group) \endcode
                @@ -188,4 +255,48 @@ demonstrating the resulting mesh. \skipline import geompy \until #end +
                +\anchor tui_copy_mesh +

                Mesh Copying

                +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +# make geometry of a box +box = geompy.MakeBoxDXDYDZ(100,100,100) +face = geompy.SubShapeAllSorted(box, geompy.ShapeType["FACE"])[0] + +# generate 3D mesh +mesh = Mesh(box) +localAlgo = mesh.Triangle(face) +mesh.AutomaticHexahedralization() + +# objects to copy +fGroup = mesh.GroupOnGeom( face, "2D on face") +nGroup = mesh.GroupOnGeom( face, "nodes on face", NODE) +subMesh = localAlgo.GetSubMesh() + +# make a new mesh by copying different parts of the mesh + +# 1. copy the whole mesh +newMesh = CopyMesh( mesh, "whole mesh copy") + +# 2. copy a group of 2D elements along with groups +newMesh = CopyMesh( fGroup, "face group copy with groups",toCopyGroups=True) + +# 3. copy a group of nodes with preseving their ids +newMesh = CopyMesh( nGroup, "node group copy", toKeepIDs=True) + +# 4. copy some faces +faceIds = fGroup.GetIDs()[-10:] +newMesh = CopyMesh( mesh.GetIDSource( faceIds, FACE ), "some faces copy") + +# 5. copy some nodes +nodeIds = nGroup.GetIDs()[-10:] +newMesh = CopyMesh( mesh.GetIDSource( nodeIds, NODE), "some nodes copy") + +# 6. copy a sub-mesh +newMesh = CopyMesh( subMesh, "submesh copy" ) +\endcode + */ diff --git a/doc/salome/gui/SMESH/input/tui_defining_hypotheses.doc b/doc/salome/gui/SMESH/input/tui_defining_hypotheses.doc index bd9fbbb10..e98eca6b4 100644 --- a/doc/salome/gui/SMESH/input/tui_defining_hypotheses.doc +++ b/doc/salome/gui/SMESH/input/tui_defining_hypotheses.doc @@ -22,8 +22,12 @@ hexa = smesh.Mesh(box, "Box : hexahedrical mesh") # create a Regular 1D algorithm for edges algo1D = hexa.Segment() +# optionally reverse node distribution on certain edges +allEdges = geompy.SubShapeAllSortedIDs( box, geompy.ShapeType["EDGE"]) +reversedEdges = [ allEdges[0], allEdges[4] ] + # define "Arithmetic1D" hypothesis to cut all edges in several segments with increasing arithmetic length -algo1D.Arithmetic1D(1, 4) +algo1D.Arithmetic1D(1, 4, reversedEdges) # create a quadrangle 2D algorithm for faces hexa.Quadrangle() @@ -124,7 +128,7 @@ hexa.Compute()
                \anchor tui_average_length -

                Average Length

                +

                Local Length

                \code from geompy import * @@ -227,7 +231,7 @@ tetra = smesh.Mesh(cyl, "Cylinder : tetrahedrical mesh") # assign algorithms algo1D = tetra.Segment() algo2D = tetra.Triangle() -algo3D = tetra.Tetrahedron(smesh.NETGEN) +algo3D = tetra.Tetrahedron() # assign 1D and 2D hypotheses algo1D.NumberOfSegments(7) @@ -359,8 +363,8 @@ algo1D = tetra.Segment() # create a Mefisto 2D algorithm for faces algo2D = tetra.Triangle() -# create a Netgen 3D algorithm for solids -algo3D = tetra.Tetrahedron(smesh.NETGEN) +# create a 3D algorithm for solids +algo3D = tetra.Tetrahedron() # define hypotheses algo1D.Arithmetic1D(1, 4) @@ -369,17 +373,338 @@ algo2D.LengthFromEdges() # compute the mesh tetra.Compute() -# 3. Create a tetrahedral mesh on the box with NETGEN_2D3D algorithm -tetraN = smesh.Mesh(box, "Box : tetrahedrical mesh by NETGEN_2D3D") +\endcode -# create a Netgen_2D3D algorithm for solids -algo3D = tetraN.Tetrahedron(smesh.FULL_NETGEN) +
                +\anchor tui_projection +

                Projection Algorithms

                -# define hypotheses -n23_params = algo3D.Parameters() +\code +# Project prisms from one meshed box to another mesh on the same box + +from smesh import * + +# Prepare geometry + +# Create a parallelepiped +box = geompy.MakeBoxDXDYDZ(200, 100, 70) +geompy.addToStudy( box, "box" ) + +# Get geom faces to mesh with triangles in the 1ts and 2nd meshes +faces = geompy.SubShapeAll(box, geompy.ShapeType["FACE"]) +# 2 adjacent faces of the box +f1 = faces[2] +f2 = faces[0] +# face opposite to f2 +f2opp = faces[1] + +# Get vertices used to specify how to associate sides of faces at projection +[v1F1, v2F1] = geompy.SubShapeAll(f1, geompy.ShapeType["VERTEX"])[:2] +[v1F2, v2F2] = geompy.SubShapeAll(f2, geompy.ShapeType["VERTEX"])[:2] +geompy.addToStudyInFather( box, v1F1, "v1F1" ) +geompy.addToStudyInFather( box, v2F1, "v2F1" ) +geompy.addToStudyInFather( box, v1F2, "v1F2" ) +geompy.addToStudyInFather( box, v2F2, "v2F2" ) + +# Make group of 3 edges of f1 and f2 +edgesF1 = geompy.CreateGroup(f1, geompy.ShapeType["EDGE"]) +geompy.UnionList( edgesF1, geompy.SubShapeAll(f1, geompy.ShapeType["EDGE"])[:3]) +edgesF2 = geompy.CreateGroup(f2, geompy.ShapeType["EDGE"]) +geompy.UnionList( edgesF2, geompy.SubShapeAll(f2, geompy.ShapeType["EDGE"])[:3]) +geompy.addToStudyInFather( box, edgesF1, "edgesF1" ) +geompy.addToStudyInFather( box, edgesF2, "edgesF2" ) + + +# Make the source mesh with prisms +src_mesh = Mesh(box, "Source mesh") +src_mesh.Segment().NumberOfSegments(9,10) +src_mesh.Quadrangle() +src_mesh.Hexahedron() +src_mesh.Triangle(f1) # triangular sumbesh +src_mesh.Compute() + + +# Mesh the box using projection algoritms + +# Define the same global 1D and 2D hypotheses +tgt_mesh = Mesh(box, "Target mesh") +tgt_mesh.Segment().NumberOfSegments(9,10,UseExisting=True) +tgt_mesh.Quadrangle() + +# Define Projection 1D algorithm to project 1d mesh elements from group edgesF2 to edgesF1 +# It is actually not needed, just a demonstration +proj1D = tgt_mesh.Projection1D( edgesF1 ) +# each vertex must be at the end of a connected group of edges (or a sole edge) +proj1D.SourceEdge( edgesF2, src_mesh, v2F1, v2F2 ) + +# Define 2D hypotheses to project triangles from f1 face of the source mesh to +# f2 face in the target mesh. Vertices specify how to associate sides of faces +proj2D = tgt_mesh.Projection2D( f2 ) +proj2D.SourceFace( f1, src_mesh, v1F1, v1F2, v2F1, v2F2 ) + +# 2D hypotheses to project triangles from f2 of target mesh to the face opposite to f2. +# Association of face sides is default +proj2D = tgt_mesh.Projection2D( f2opp ) +proj2D.SourceFace( f2 ) + +# 3D hypotheses to project prisms from the source to the target mesh +proj3D = tgt_mesh.Projection3D() +proj3D.SourceShape3D( box, src_mesh, v1F1, v1F2, v2F1, v2F2 ) +tgt_mesh.Compute() + +# Move the source mesh to visualy compare the two meshes +src_mesh.TranslateObject( src_mesh, MakeDirStruct( 210, 0, 0 ), Copy=False) + +\endcode + +
                + +\anchor tui_fixed_points + +

                1D Mesh with Fixed Points example

                + +\code +import salome +import geompy +import smesh +import StdMeshers + +# Create face and explode it on edges +face = geompy.MakeFaceHW(100, 100, 1) +edges = geompy.SubShapeAllSorted(face, geompy.ShapeType["EDGE"]) +geompy.addToStudy( face, "Face" ) + +# get the first edge from exploded result +edge1 = geompy.GetSubShapeID(face, edges[0]) + +# Define Mesh on previously created face +Mesh_1 = smesh.Mesh(face) + +# Create Fixed Point 1D hypothesis and define parameters. +# Note: values greater than 1.0 and less than 0.0 are not taken into account; +# duplicated values are removed. Also, if not specified explicitly, values 0.0 and 1.0 +# add added automatically. +# The number of segments should correspond to the number of points (NbSeg = NbPnt-1); +# extra values of segments splitting parameter are not taken into account, +# while missing values are considered to be equal to 1. +Fixed_points_1D_1 = smesh.CreateHypothesis('FixedPoints1D') +Fixed_points_1D_1.SetPoints( [ 1.1, 0.9, 0.5, 0.0, 0.5, -0.3 ] ) +Fixed_points_1D_1.SetNbSegments( [ 3, 1, 2 ] ) +Fixed_points_1D_1.SetReversedEdges( [edge1] ) + +# Add hypothesis to mesh and define 2D parameters +Mesh_1.AddHypothesis(Fixed_points_1D_1) +Regular_1D = Mesh_1.Segment() +Quadrangle_2D = Mesh_1.Quadrangle() +# Compute mesh +Mesh_1.Compute() +\endcode + +\anchor tui_radial_quadrangle +

                Radial Quadrangle 1D2D example

                +\code +from smesh import * + +SetCurrentStudy(salome.myStudy) + +# Create face from the wire and add to study +Face = geompy.MakeSketcher("Sketcher:F 0 0:TT 20 0:R 90:C 20 90:WF", [0, 0, 0, 1, 0, 0, 0, 0, 1]) +geompy.addToStudy(Face,"Face") +edges = geompy.SubShapeAllSorted(Face, geompy.ShapeType["EDGE"]) +circle, radius1, radius2 = edges +geompy.addToStudyInFather(Face, radius1,"radius1") +geompy.addToStudyInFather(Face, radius2,"radius2") +geompy.addToStudyInFather(Face, circle,"circle") + + +# Define geometry for mesh, and Radial Quadrange algorithm +mesh = smesh.Mesh(Face) +radial_Quad_algo = mesh.Quadrangle(algo=RADIAL_QUAD) + +# The Radial Quadrange algorithm can work without any hypothesis +# In this case it uses "Default Nb of Segments" preferences parameter to discretize edges +mesh.Compute() + +# The Radial Quadrange uses global or local 1d hypotheses if it does +# not have its own hypotheses. +# Define global hypotheses to discretize radial edges and a local one for circular edge +global_Nb_Segments = mesh.Segment().NumberOfSegments(5) +local_Nb_Segments = mesh.Segment(circle).NumberOfSegments(10) +mesh.Compute() + +# Define own parameters of Radial Quadrange algorithm +radial_Quad_algo.NumberOfLayers( 4 ) +mesh.Compute() +\endcode + +\anchor tui_quadrangle_parameters +

                Quadrangle Parameters example 1 (meshing a face with 3 edges)

                +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +# Get 1/4 part from the disk face. +Box_1 = geompy.MakeBoxDXDYDZ(100, 100, 100) +Disk_1 = geompy.MakeDiskR(100, 1) +Common_1 = geompy.MakeCommon(Disk_1, Box_1) +geompy.addToStudy( Disk_1, "Disk_1" ) +geompy.addToStudy( Box_1, "Box_1" ) +geompy.addToStudy( Common_1, "Common_1" ) + +# Set the Geometry for meshing +Mesh_1 = smesh.Mesh(Common_1) + + +# Define 1D hypothesis and compute the mesh +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(10) +Nb_Segments_1.SetDistrType( 0 ) + +# Create Quadrangle parameters and define the Base Vertex. +Quadrangle_2D = Mesh_1.Quadrangle().TriangleVertex( 8 ) + +Mesh_1.Compute() +\endcode + +

                Quadrangle Parameters example 2 (using different types)

                +\code +import geompy +import smesh +import StdMeshers + +# Make quadrangle face and explode it on edges. +Vertex_1 = geompy.MakeVertex(0, 0, 0) +Vertex_2 = geompy.MakeVertex(40, 0, 0) +Vertex_3 = geompy.MakeVertex(40, 30, 0) +Vertex_4 = geompy.MakeVertex(0, 30, 0) +Quadrangle_Face_1 = geompy.MakeQuad4Vertices(Vertex_1, Vertex_4, Vertex_3, Vertex_2) +[Edge_1,Edge_2,Edge_3,Edge_4] = geompy.SubShapeAllSorted(Quadrangle_Face_1, geompy.ShapeType["EDGE"]) +geompy.addToStudy( Vertex_1, "Vertex_1" ) +geompy.addToStudy( Vertex_2, "Vertex_2" ) +geompy.addToStudy( Vertex_3, "Vertex_3" ) +geompy.addToStudy( Vertex_4, "Vertex_4" ) +geompy.addToStudy( Quadrangle_Face_1, "Quadrangle Face_1" ) +geompy.addToStudyInFather( Quadrangle_Face_1, Edge_2, "Edge_2" ) + +# Set the Geometry for meshing +Mesh_1 = smesh.Mesh(Quadrangle_Face_1) + +# Create Quadrangle parameters and +# define the Type as Quadrangle Preference +Quadrangle_Parameters_1 = smesh.CreateHypothesis('QuadrangleParams') +Quadrangle_Parameters_1.SetQuadType( StdMeshers.QUAD_QUADRANGLE_PREF ) + +# Define other hypotheses and algorithms +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(4) +Nb_Segments_1.SetDistrType( 0 ) +status = Mesh_1.AddHypothesis(Quadrangle_Parameters_1) +Quadrangle_2D = Mesh_1.Quadrangle() + +# Define submesh on one edge to provide different number of segments +Regular_1D_1 = Mesh_1.Segment(geom=Edge_2) +Nb_Segments_2 = Regular_1D_1.NumberOfSegments(10) +Nb_Segments_2.SetDistrType( 0 ) +SubMesh_1 = Regular_1D_1.GetSubMesh() + +# Compute mesh (with Quadrangle Preference type) +isDone = Mesh_1.Compute() + +# Change type to Reduced and compute again +Quadrangle_Parameters_1.SetQuadType( StdMeshers.QUAD_REDUCED ) +isDone = Mesh_1.Compute() +\endcode + +\anchor tui_import +

                "Use Existing Elements" example

                +\code + +from smesh import * +SetCurrentStudy(salome.myStudy) + +# Make a patritioned box + +box = geompy.MakeBoxDXDYDZ(100,100,100) + +N = geompy.MakeVectorDXDYDZ( 1,0,0 ) +O = geompy.MakeVertex( 50,0,0 ) +plane = geompy.MakePlane( O, N, 200 ) # plane YOZ + +shape2boxes = geompy.MakeHalfPartition( box, plane ) +boxes = geompy.SubShapeAllSorted(shape2boxes, geompy.ShapeType["SOLID"]) + +geompy.addToStudy( boxes[0], "boxes[0]") +geompy.addToStudy( boxes[1], "boxes[1]") +midFace0 = geompy.SubShapeAllSorted(boxes[0], geompy.ShapeType["FACE"])[5] +geompy.addToStudyInFather( boxes[0], midFace0, "middle Face") +midFace1 = geompy.SubShapeAllSorted(boxes[1], geompy.ShapeType["FACE"])[0] +geompy.addToStudyInFather( boxes[1], midFace1, "middle Face") + +# Mesh one of boxes with quadrangles. It is a source mesh + +srcMesh = Mesh(boxes[0], "source mesh") # box coloser to CS origin +nSeg1 = srcMesh.Segment().NumberOfSegments(4) +srcMesh.Quadrangle() +srcMesh.Compute() +srcFaceGroup = srcMesh.GroupOnGeom( midFace0, "src faces", FACE ) + +# Import faces from midFace0 to the target mesh + +tgtMesh = Mesh(boxes[1], "target mesh") +importAlgo = tgtMesh.UseExisting2DElements(midFace1) +import2hyp = importAlgo.SourceFaces( [srcFaceGroup] ) +tgtMesh.Segment().NumberOfSegments(3) +tgtMesh.Quadrangle() +tgtMesh.Compute() + +# Import the whole source mesh with groups +import2hyp.SetCopySourceMesh(True,True) +tgtMesh.Compute() +\endcode + +\anchor tui_viscous_layers +

                Viscous layers construction

                + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +X = geompy.MakeVectorDXDYDZ( 1,0,0 ) +O = geompy.MakeVertex( 100,50,50 ) +plane = geompy.MakePlane( O, X, 200 ) # plane YZ + +box = geompy.MakeBoxDXDYDZ(200,100,100) + +shape = geompy.MakeHalfPartition( box, plane ) + +faces = geompy.SubShapeAllSorted(shape, geompy.ShapeType["FACE"]) +face1 = faces[1] +ignoreFaces = [ faces[0], faces[-1]] + +geompy.addToStudy( shape, "shape" ) +geompy.addToStudyInFather( shape, face1, "face1") + + +mesh = Mesh(shape, "CFD") + +mesh.Segment().NumberOfSegments( 4 ) + +mesh.Triangle() +mesh.Quadrangle(face1) +mesh.Compute() +algo3D = mesh.Tetrahedron() + +thickness = 20 +numberOfLayers = 10 +stretchFactor = 1.5 +layersHyp = algo3D.ViscousLayers(thickness,numberOfLayers,stretchFactor,ignoreFaces) + +mesh.Compute() + +mesh.MakeGroup("Tetras",VOLUME,FT_ElemGeomType,"=",Geom_TETRA) +mesh.MakeGroup("Pyras",VOLUME,FT_ElemGeomType,"=",Geom_PYRAMID) +mesh.MakeGroup("Prims",VOLUME,FT_ElemGeomType,"=",Geom_PENTA) -# compute the mesh -tetraN.Compute() \endcode -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/tui_filters.doc b/doc/salome/gui/SMESH/input/tui_filters.doc new file mode 100755 index 000000000..0eb41dd28 --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_filters.doc @@ -0,0 +1,770 @@ +/*! + +\page tui_filters_page Filters usage + +Filters allow picking only the mesh elements satisfying to a +specific condition or a set of conditions. Filters can be used to create +or edit mesh groups, remove elements from the mesh object, control +mesh quality by different parameters, etc. + +Several filters can be combined together by using logical operators \a +AND and \a OR. In addition, applied filter criterion can be reverted +using logical operator \a NOT. + +Mesh filters use the functionality of mesh quality controls to filter +mesh nodes / elements by a specific characteristic (Area, Length, etc). + +This page provides a short description of the existing mesh filters, +describes required parameters and gives simple examples of usage in +Python scripts. + +\sa \ref tui_quality_controls_page + +\section filter_aspect_ratio Aspect ratio + +Filter 2D mesh elements (faces) according to the aspect ratio value: +- element type should be \a smesh.FACE +- functor type should be \a smesh.FT_AspectRatio +- threshold is floating point value (aspect ratio) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with aspect ratio > 6.5 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_AspectRatio, smesh.FT_MoreThan, 6.5) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with aspect ratio > 6.5:", len(ids) +\endcode + +\sa \ref tui_aspect_ratio + +\section filter_aspect_ratio_3d Aspect ratio 3D + +Filter 3D mesh elements (volumes) according to the aspect ratio value: +- element type is \a smesh.VOLUME +- functor type is \a smesh.FT_AspectRatio3D +- threshold is floating point value (aspect ratio) + +\code +# create mesh with volumes +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get volumes with aspect ratio < 2.0 +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_AspectRatio3D, smesh.FT_LessThan, 2.0) +ids = mesh.GetIdsFromFilter(filter) +print "Number of volumes with aspect ratio < 2.0:", len(ids) +\endcode + +\sa \ref tui_aspect_ratio_3d + +\section filter_warping_angle Warping angle + +Filter 2D mesh elements (faces) according to the warping angle value: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_Warping +- threshold is floating point value (warping angle) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with warping angle = 2.0e-13 with tolerance 5.0e-14 +criterion = smesh.GetCriterion(smesh.FACE, smesh.FT_Warping, smesh.FT_EqualTo, 2.0e-13) +criterion.Tolerance = 5.0e-14 +filter = smesh.CreateFilterManager().CreateFilter() +filter.SetCriteria([criterion]) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with warping angle = 2.0e-13 (tolerance 5.0e-14):", len(ids) +\endcode + +\sa \ref tui_warping + +\section filter_minimum_angle Minimum angle + +Filter 2D mesh elements (faces) according to the minimum angle value: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_MinimumAngle +- threshold is floating point value (minimum angle) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with minimum angle > 75 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_MinimumAngle,">", 75) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with minimum angle > 75:", len(ids) +\endcode + +\sa \ref tui_minimum_angle + +\section filter_taper Taper + +Filter 2D mesh elements (faces) according to the taper value: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_Taper +- threshold is floating point value (taper) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with taper < 1.e-15 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_Taper, smesh.FT_LessThan, 1.e-15) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with taper < 1.e-15:", len(ids) +\endcode + +\sa \ref tui_taper + +\section filter_skew Skew + +Filter 2D mesh elements (faces) according to the skew value: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_Skew +- threshold is floating point value (skew) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with skew > 50 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_Skew, smesh.FT_MoreThan, 50) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with skew > 50:", len(ids) +\endcode + +\sa \ref tui_skew + +\section filter_area Area + +Filter 2D mesh elements (faces) according to the area value: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_Area +- threshold is floating point value (area) + +\code +# create mesh +from SMESH_mechanic import * +# get faces with area > 60 and < 90 +criterion1 = smesh.GetCriterion(smesh.FACE, smesh.FT_Area, smesh.FT_MoreThan, 60,\ + smesh.FT_Undefined, smesh.FT_LogicalAND) +criterion2 = smesh.GetCriterion(smesh.FACE, smesh.FT_Area, smesh.FT_LessThan, 90) +filter = smesh.CreateFilterManager().CreateFilter() +filter.SetCriteria([criterion1,criterion2]) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with area in range (60,90):", len(ids) +\endcode + +\sa \ref tui_area + +\section filter_volume Volume + +Filter 3D mesh elements (volumes) according to the volume value: +- element type is \a smesh.VOLUME +- functor type is \a smesh.FT_Volume3D +- threshold is floating point value (volume) + +\code +# create mesh with volumes +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get volumes faces with volume > 100 +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_Volume3D, smesh.FT_MoreThan, 100) +ids = mesh.GetIdsFromFilter(filter) +print "Number of volumes with volume > 100:", len(ids) +\endcode + +\sa \ref tui_volume + +\section filter_free_borders Free borders + +Filter 1D mesh elements (edges) which represent free borders of a mesh: +- element type is \a smesh.EDGE +- functor type is \a smesh.FT_FreeBorders +- threshold value is not required + +\code +# create mesh +import geompy, smesh, StdMeshers +face = geompy.MakeFaceHW(100, 100, 1) +geompy.addToStudy( face, "quadrangle" ) +mesh = smesh.Mesh(face) +mesh.Segment().NumberOfSegments(10) +mesh.Triangle().MaxElementArea(25) +mesh.Compute() +# get all free borders +filter = smesh.GetFilter(smesh.EDGE, smesh.FT_FreeBorders) +ids = mesh.GetIdsFromFilter(filter) +print "Number of edges on free borders:", len(ids) +\endcode + +\sa \ref tui_free_borders + +\section filter_free_edges Free edges + +Filter 2D mesh elements (faces) consisting of edges belonging to one +element of mesh only: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_FreeEdges +- threshold value is not required + +\code +# create mesh +import geompy, smesh, StdMeshers +face = geompy.MakeFaceHW(100, 100, 1) +geompy.addToStudy( face, "quadrangle" ) +mesh = smesh.Mesh(face) +mesh.Segment().NumberOfSegments(10) +mesh.Triangle().MaxElementArea(25) +mesh.Compute() +# get all faces with free edges +filter = smesh.GetFilter(smesh.FACE, smesh.FT_FreeEdges) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with free edges:", len(ids) +\endcode + +\sa \ref tui_free_edges + +\section filter_free_nodes Free nodes + +Filter free nodes: +- element type is \a smesh.NODE +- functor type is \a smesh.FT_FreeNodes +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +# add node +mesh.AddNode(0,0,0) +# get all free nodes +filter = smesh.GetFilter(smesh.NODE, smesh.FT_FreeNodes) +ids = mesh.GetIdsFromFilter(filter) +print "Number of free nodes:", len(ids) +\endcode + +\sa \ref tui_free_nodes + +\section filter_free_faces Free faces + +Filter free faces: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_FreeFaces +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +# get all free faces +filter = smesh.GetFilter(smesh.FACE, smesh.FT_FreeFaces) +ids = mesh.GetIdsFromFilter(filter) +print "Number of free faces:", len(ids) +\endcode + +\sa \ref tui_free_faces + +\section filter_bare_border_faces Bare border faces + +Filter faces with bare borders: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_BareBorderFace +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +# remove some faces to have faces with bare borders +mesh.RemoveElements( mesh.GetElementsByType(FACE)[0:5] ) +# get all faces bare borders +filter = smesh.GetFilter(smesh.FACE, smesh.FT_BareBorderFace) +ids = mesh.GetIdsFromFilter(filter) +print "Faces with bare borders:", ids +\endcode + +\sa \ref tui_bare_border_faces + +\section filter_coplanar_faces Coplanar faces + +Filter faces with bare borders: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_CoplanarFaces +- threshold value is the face ID +- tolerance is in degrees + +\code +# create mesh +from SMESH_mechanic import * +faceID = mesh.GetElementsByType(FACE)[0] +# get all faces co-planar to the first face with tolerance 5 degrees +filter = smesh.GetFilter(smesh.FACE, smesh.FT_CoplanarFaces,faceID,Tolerance=5.0) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces coplanar with the first one:", len(ids) +\endcode + +\section filter_over_constrained_faces Over-constrained faces + +Filter over-constrained faces: +- element type is \a smesh.FACE +- functor type is \a smesh.FT_OverConstrainedFace +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +# get all over-constrained faces +filter = smesh.GetFilter(smesh.FACE, smesh.FT_OverConstrainedFace) +ids = mesh.GetIdsFromFilter(filter) +print "Over-constrained faces:", ids +\endcode + +\sa \ref tui_over_constrained_faces + +\section filter_double_elements Double edges, Double faces, Double volumes + +filter mesh elements basing on the same set of nodes: +- element type is either \a smesh.EGDE, \a smesh.FACE or \a smesh.VOLUME +- functor type is either \a smesh.FT_EqualEdges, \a + smesh.FT_EqualFaces or \a smesh.FT_EqualVolumes, +- threshold value is not required + +\code +from smesh import * +# make a mesh on a box +box = geompy.MakeBoxDXDYDZ(100,100,100) +mesh = Mesh( box, "Box" ) +mesh.Segment().NumberOfSegments(10) +mesh.Quadrangle() +mesh.Hexahedron() +mesh.Compute() +# copy all elements with translation and Merge nodes +mesh.TranslateObject( mesh, MakeDirStruct( 10,0,0), Copy=True ) +mesh.MergeNodes( mesh.FindCoincidentNodes(1e-7) ) +# create filters to find equal elements +equalEdgesFilter = GetFilter(SMESH.EDGE, FT_EqualEdges) +equalFacesFilter = GetFilter(SMESH.FACE, FT_EqualFaces) +equalVolumesFilter = GetFilter(SMESH.VOLUME, FT_EqualVolumes) +# get equal elements +print "Number of equal edges:", len( mesh.GetIdsFromFilter( equalEdgesFilter )) +print "Number of equal faces:", len( mesh.GetIdsFromFilter( equalFacesFilter )) +print "Number of equal volumes:", len( mesh.GetIdsFromFilter( equalVolumesFilter )) +\endcode + + +\section tui_double_nodes_control Double nodes + +filters mesh nodes which are coincident with other nodes (within a given tolerance): +- element type is \a smesh.NODE +- functor type is \a smesh.FT_EqualNodes +- threshold value is not required +- default tolerance is 1.0e-7 + +\code +from smesh import * +# make a mesh on a box +box = geompy.MakeBoxDXDYDZ(100,100,100) +mesh = Mesh( box, "Box" ) +mesh.Segment().NumberOfSegments(10) +mesh.Quadrangle() +mesh.Hexahedron() +mesh.Compute() +# copy all elements with translation +mesh.TranslateObject( mesh, MakeDirStruct( 10,0,0), Copy=True ) +# create filters to find nodes equal within tolerance of 1e-5 +filter = GetFilter(SMESH.NODE, FT_EqualNodes, Tolerance=1e-5) +# get equal nodes +print "Number of equal nodes:", len( mesh.GetIdsFromFilter( filter )) +\endcode + + +\section filter_borders_multiconnection Borders at multi-connection + +Filter border 1D mesh elements (edges) according to the specified number of +connections (faces belonging the border edges) +- element type is \a smesh.EDGE +- functor type is \a smesh.FT_MultiConnection +- threshold is integer value (number of connections) + +\code +# create mesh +from SMESH_mechanic import * +# get border edges with number of connected faces = 5 +filter = smesh.GetFilter(smesh.EDGE, smesh.FT_MultiConnection, 5) +ids = mesh.GetIdsFromFilter(filter) +print "Number of border edges with 5 faces connected:", len(ids) +\endcode + +\sa \ref tui_borders_at_multiconnection + +\section filter_borders_multiconnection_2d Borders at multi-connection 2D + +Filter 2D mesh elements (faces) which consist of edges belonging +to the specified number of mesh elements +- element type is \a smesh.FACE +- functor type is \a smesh.FT_MultiConnection2D +- threshold is integer value (number of connections) + +\code +# create mesh +from SMESH_mechanic import * +# get faces which consist of edges belonging to 2 mesh elements +filter = smesh.GetFilter(smesh.FACE, smesh.FT_MultiConnection2D, 2) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces consisting of edges belonging to 2 faces:", len(ids) +\endcode + +\sa \ref tui_borders_at_multiconnection_2d + +\section filter_length Length + +Filter 1D mesh elements (edges) according to the edge length value: +- element type should be \a smesh.EDGE +- functor type should be \a smesh.FT_Length +- threshold is floating point value (length) + +\code +# create mesh +from SMESH_mechanic import * +# get edges with length > 14 +filter = smesh.GetFilter(smesh.EDGE, smesh.FT_Length, smesh.FT_MoreThan, 14) +ids = mesh.GetIdsFromFilter(filter) +print "Number of edges with length > 14:", len(ids) +\endcode + +\sa \ref tui_length_1d + +\section filter_length_2d Length 2D + +Filter 2D mesh elements (faces) corresponding to the maximum length. +value of its edges: +- element type should be \a smesh.FACE +- functor type should be \a smesh.FT_Length2D +- threshold is floating point value (edge length) + +\code +# create mesh +from SMESH_mechanic import * +# get all faces that have edges with length > 14 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_Length2D, smesh.FT_MoreThan, 14) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with maximum edge length > 14:", len(ids) +\endcode + +\sa \ref tui_length_2d + +\section filter_max_element_length_2d Element Diameter 2D + +Filter 2D mesh elements (faces) corresponding to the maximum length +value of its edges and diagonals: +- element type should be \a smesh.FACE +- functor type should be \a smesh.FT_MaxElementLength2D +- threshold is floating point value (edge/diagonal length) + +\code +# create mesh +from SMESH_mechanic import * +# get all faces that have elements with length > 10 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_MaxElementLength2D, smesh.FT_MoreThan, 10) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces with maximum element length > 10:", len(ids) +\endcode + +\sa \ref tui_max_element_length_2d + +\section filter_max_element_length_3d Element Diameter 3D + +Filter 3D mesh elements (volumes) corresponding to the maximum length +value of its edges and diagonals: +- element type should be \a smesh.VOLUME +- functor type should be \a smesh.FT_MaxElementLength3D +- threshold is floating point value (edge/diagonal length) + +\code +# create mesh with volumes +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get all volumes that have elements with length > 10 +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_MaxElementLength3D, smesh.FT_MoreThan, 10) +ids = mesh.GetIdsFromFilter(filter) +print "Number of volumes with maximum element length > 10:", len(ids) +\endcode + +\sa \ref tui_max_element_length_3d + +\section filter_bare_border_volumes Bare border volumes + +Filter 3D mesh elements with bare borders: +- element type is \a smesh.VOLUME +- functor type is \a smesh.FT_BareBorderVolume +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# remove some volumes to have volumes with bare borders +mesh.RemoveElements( mesh.GetElementsByType(VOLUME)[0:5] ) +# get all volumes with bare borders +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_BareBorderVolume) +ids = mesh.GetIdsFromFilter(filter) +print "Volumes with bare borders:", ids +\endcode + +\sa \ref tui_bare_border_volumes + +\section filter_over_constrained_volumes Over-constrained volumes + +Filter over-constrained volumes: +- element type is \a smesh.VOLUME +- functor type is \a smesh.FT_OverConstrainedVolume +- threshold value is not required + +\code +# create mesh +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get all over-constrained volumes +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_OverConstrainedVolume) +ids = mesh.GetIdsFromFilter(filter) +print "Over-constrained volumes:", ids +\endcode + +\sa \ref tui_over_constrained_faces + +\section filter_belong_to_geom Belong to Geom + +Filter mesh entities (nodes or elements) which all nodes lie on the +shape defined by threshold value: +- element type can be any entity type, from \a smesh.NODE to \a smesh.VOLUME +- functor type should be \a smesh.FT_BelongToGeom +- threshold is geometrical object + +\code +# create mesh +from SMESH_mechanic import * +# get all faces which nodes lie on the face sub_face3 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_BelongToGeom, sub_face3) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces which nodes lie on sub_face3:", len(ids) +\endcode + +\section filter_lying_on_geom Lying on Geom + +Filter mesh entities (nodes or elements) at least one node of which lies on the +shape defined by threshold value: +- element type can be any entity type, from \a smesh.NODE to \a smesh.VOLUME +- functor type should be \a smesh.FT_LyingOnGeom +- threshold is geometrical object + +\code +# create mesh +from SMESH_mechanic import * +# get all faces at least one node of each lies on the face sub_face3 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_LyingOnGeom, sub_face3) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces at least one node of each lies on sub_face3:", len(ids) +\endcode + +\section filter_belong_to_plane Belong to Plane + +Filter mesh entities (nodes or elements) which all nodes belong to the +plane defined by threshold value with the given tolerance: +- element type can be: \a smesh.NODE, \a smesh.EDGE, \a smesh.FACE +- functor type should be \a smesh.FT_BelongToPlane +- threshold is geometrical object (plane) +- default tolerance is 1.0e-7 + +\code +# create mesh +from SMESH_mechanic import * +# create plane +import geompy +plane_1 = geompy.MakePlane(p3,seg1,2000) +geompy.addToStudy(plane_1, "plane_1") +# get all nodes which lie on the plane \a plane_1 +filter = smesh.GetFilter(smesh.NODE, smesh.FT_BelongToPlane, plane_1) +ids = mesh.GetIdsFromFilter(filter) +print "Number of nodes which lie on the plane plane_1:", len(ids) +\endcode + +\section filter_belong_to_cylinder Belong to Cylinder + +Filter mesh entities (nodes or elements) which all nodes belong to the +cylindrical face defined by threshold value with the given tolerance: +- element type can be: \a smesh.NODE, \a smesh.EDGE, \a smesh.FACE +- functor type should be \a smesh.FT_BelongToCylinder +- threshold is geometrical object (cylindrical face) +- default tolerance is 1.0e-7 + +\code +# create mesh +from SMESH_mechanic import * +# get all faces which lie on the cylindrical face \a sub_face1 +filter = smesh.GetFilter(smesh.FACE, smesh.FT_BelongToCylinder, sub_face1) +ids = mesh.GetIdsFromFilter(filter) +print "Number of faces which lie on the cylindrical surface sub_face1:", len(ids) +\endcode + +\section filter_belong_to_surface Belong to Surface + +Filter mesh entities (nodes or elements) which all nodes belong to the +arbitrary surface defined by threshold value with the given tolerance: +- element type can be: \a smesh.NODE, \a smesh.EDGE, \a smesh.FACE +- functor type should be \a smesh.FT_BelongToGenSurface +- threshold is geometrical object (arbitrary surface) +- default tolerance is 1.0e-7 + +\code +# create mesh +from SMESH_mechanic import * +# create b-spline +spline_1 = geompy.MakeInterpol([p4,p6,p3,p1]) +surface_1 = geompy.MakePrismVecH( spline_1, vz, 70.0 ) +geompy.addToStudy(surface_1, "surface_1") +# get all nodes which lie on the surface \a surface_1 +filter = smesh.GetFilter(smesh.NODE, smesh.FT_BelongToGenSurface, surface_1) +ids = mesh.GetIdsFromFilter(filter) +print "Number of nodes which lie on the surface surface_1:", len(ids) +\endcode + +\section filter_range_of_ids Range of IDs + +Filter mesh entities elements (nodes or elements) according to the +specified identifiers range: +- element type can be any entity type, from \a smesh.NODE to \a smesh.VOLUME +- functor type is \a smesh.FT_RangeOfIds +- threshold is string listing required IDs and/or ranges of IDs, e.g."1,2,3,50-60,63,67,70-78" + +\code +# create mesh +from SMESH_mechanic import * +# get nodes with identifiers [5-10] and [15-30] +criterion1 = smesh.GetCriterion(smesh.NODE, smesh.FT_RangeOfIds, Treshold="5-10",\ + BinaryOp=smesh.FT_LogicalOR) +criterion2 = smesh.GetCriterion(smesh.NODE, smesh.FT_RangeOfIds, Treshold="15-30") +filter = smesh.CreateFilterManager().CreateFilter() +filter.SetCriteria([criterion1,criterion2]) +ids = mesh.GetIdsFromFilter(filter) +print "Number of nodes in ranges [5-10] and [15-30]:", len(ids) +\endcode + +\section filter_bad_oriented_volume Badly oriented volume + +Filter 3D mesh elements (volumes), which are incorrectly oriented from +the point of view of MED convention. +- element type should be \a smesh.VOLUME +- functor type is \a smesh.FT_BadOrientedVolume +- threshold is not required + +\code +# create mesh with volumes +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get all badly oriented volumes +filter = smesh.GetFilter(smesh.VOLUME, smesh.FT_BadOrientedVolume) +ids = mesh.GetIdsFromFilter(filter) +print "Number of badly oriented volumes:", len(ids) +\endcode + +\section filter_linear_or_quadratic Linear / quadratic + +Filter linear / quadratic mesh elements: +- element type should be any element type, e.g.: \a smesh.EDGE, \a smesh.FACE, \a smesh.VOLUME +- functor type is \a smesh.FT_LinearOrQuadratic +- threshold is not required +- if unary operator is set to smesh.FT_LogicalNOT, the quadratic +elements are selected, otherwise (by default) linear elements are selected + +\code +# create mesh +from SMESH_mechanic import * +# get number of linear and quadratic edges +filter_linear = smesh.GetFilter(smesh.EDGE, smesh.FT_LinearOrQuadratic) +filter_quadratic = smesh.GetFilter(smesh.EDGE, smesh.FT_LinearOrQuadratic, smesh.FT_LogicalNOT) +ids_linear = mesh.GetIdsFromFilter(filter_linear) +ids_quadratic = mesh.GetIdsFromFilter(filter_quadratic) +print "Number of linear edges:", len(ids_linear), "; number of quadratic edges:", len(ids_quadratic) +# convert mesh to quadratic +print "Convert to quadratic..." +mesh.ConvertToQuadratic(True) +# get number of linear and quadratic edges +ids_linear = mesh.GetIdsFromFilter(filter_linear) +ids_quadratic = mesh.GetIdsFromFilter(filter_quadratic) +print "Number of linear edges:", len(ids_linear), "; number of quadratic edges:", len(ids_quadratic) +\endcode + +\section filter_group_color Group color + +Filter mesh entities, belonging to the group with the color defined by the threshold value. +- element type can be any entity type, from \a smesh.NODE to \a smesh.VOLUME +- functor type is \a smesh.FT_GroupColor +- threshold should be of SALOMEDS.Color type + +\code +# create mesh +from SMESH_mechanic import * +# create group of edges +all_edges = mesh.GetElementsByType(smesh.EDGE) +grp = mesh.MakeGroupByIds("edges group", smesh.EDGE, all_edges[:len(all_edges)/4]) +import SALOMEDS +c = SALOMEDS.Color(0.1, 0.5, 1.0) +grp.SetColor(c) +# get number of the edges not belonging to the group with the given color +filter = smesh.GetFilter(smesh.EDGE, smesh.FT_GroupColor, c, smesh.FT_LogicalNOT) +ids = mesh.GetIdsFromFilter(filter) +print "Number of edges not beloging to the group with color (0.1, 0.5, 1.0):", len(ids) +\endcode + +\section filter_geom_type Geometry type + +Filter mesh elements by the geometric type defined with the threshold +value. The list of available geometric types depends on the element +entity type. +- element type should be any element type, e.g.: \a smesh.EDGE, \a smesh.FACE, \a smesh.VOLUME +- functor type should be \a smesh.FT_ElemGeomType +- threshold is of smesh.GeometryType value + +\code +# create mesh with volumes +from SMESH_mechanic import * +mesh.Tetrahedron() +mesh.Compute() +# get all triangles, quadrangles, tetrahedrons, pyramids +filter_tri = smesh.GetFilter(smesh.FACE, smesh.FT_ElemGeomType, smesh.Geom_TRIANGLE) +filter_qua = smesh.GetFilter(smesh.FACE, smesh.FT_ElemGeomType, smesh.Geom_QUADRANGLE) +filter_tet = smesh.GetFilter(smesh.VOLUME, smesh.FT_ElemGeomType, smesh.Geom_TETRA) +filter_pyr = smesh.GetFilter(smesh.VOLUME, smesh.FT_ElemGeomType, smesh.Geom_PYRAMID) +ids_tri = mesh.GetIdsFromFilter(filter_tri) +ids_qua = mesh.GetIdsFromFilter(filter_qua) +ids_tet = mesh.GetIdsFromFilter(filter_tet) +ids_pyr = mesh.GetIdsFromFilter(filter_pyr) +print "Number of triangles:", len(ids_tri) +print "Number of quadrangles:", len(ids_qua) +print "Number of tetrahedrons:", len(ids_tet) +print "Number of pyramids:", len(ids_pyr) +\endcode + +\section combining_filters How to combine filters with Criterion structures? + +Filters can be combined by making use of "criteria". + +Example : + +\code +# create mesh +from SMESH_mechanic import * +# get all the quadrangle faces ... +criterion1 = smesh.GetCriterion(smesh.FACE, smesh.FT_ElemGeomType, smesh.Geom_QUADRANGLE, smesh.FT_LogicalAND) +# ... AND do NOT get those from sub_face3 +criterion2 = smesh.GetCriterion(smesh.FACE, smesh.FT_BelongToGeom, sub_face3, smesh.FT_LogicalNOT) +filter = smesh.CreateFilterManager().CreateFilter() +filter.SetCriteria([criterion1,criterion2]) +ids = mesh.GetIdsFromFilter(filter) + +myGroup = mesh.MakeGroupByIds("Quads_on_cylindrical_faces",smesh.FACE,ids) +\endcode + + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_generate_flat_elements.doc b/doc/salome/gui/SMESH/input/tui_generate_flat_elements.doc new file mode 100644 index 000000000..eaf544556 --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_generate_flat_elements.doc @@ -0,0 +1,82 @@ +/*! + +\page tui_generate_flat_elements_page Generate flat elements + +
                +\anchor tui_double_nodes_on_group_boundaries +

                Double nodes on groups boundaries

                + +\n Double nodes on shared faces between groups of volumes and create flat elements on demand. +\n The list of groups must describe a partition of the mesh volumes. The nodes of the internal +faces at the boundaries of the groups are doubled. Optionally, the internal faces are replaced +by flat elements. +\n Triangles are transformed into prisms, and quadrangles into hexahedrons. +\n The flat elements are stored in groups of volumes. + +\n +\n This example represents an iron cable (a thin cylinder) in a concrete bloc (a big cylinder). +The big cylinder is defined by two geometric volumes. + +\code +import geompy +import smesh +import SMESH +# geometry + +O = geompy.MakeVertex(0, 0, 0) +OX = geompy.MakeVectorDXDYDZ(1, 0, 0) +OY = geompy.MakeVectorDXDYDZ(0, 1, 0) +OZ = geompy.MakeVectorDXDYDZ(0, 0, 1) +Vertex_1 = geompy.MakeVertex(50, 0, 0) +Cylinder_1 = geompy.MakeCylinder(O, OX, 10, 500) +Cylinder_2 = geompy.MakeCylinder(Vertex_1, OX, 100, 400) +Vertex_2 = geompy.MakeVertex(-200, -200, -200) +Vertex_3 = geompy.MakeVertex(250, 200, 200) +Box_1 = geompy.MakeBoxTwoPnt(Vertex_2, Vertex_3) +Fuse_1 = geompy.MakeFuse(Cylinder_1, Cylinder_2) +Partition_1 = geompy.MakePartition([Fuse_1], [Cylinder_1, Box_1], [], [], geompy.ShapeType["SOLID"], 0, [], 0) +[Solid_1,Solid_2] = geompy.GetShapesOnShape(Cylinder_1, Partition_1, geompy.ShapeType["SOLID"], geompy.GEOM.ST_IN) +[Solid_3,Solid_4] = geompy.GetShapesOnShape(Cylinder_2, Partition_1, geompy.ShapeType["SOLID"], geompy.GEOM.ST_IN) +Vertex_4 = geompy.MakeVertex(450, 0, 0) +Vertex_5 = geompy.MakeVertex(500, 0, 0) +Vertex_6 = geompy.MakeVertex(550, 0, 0) +vec1 = geompy.MakeVector(Vertex_4, Vertex_5) +vec2 = geompy.MakeVector(Vertex_5, Vertex_6) +[Face_1] = geompy.GetShapesOnPlane(Partition_1, geompy.ShapeType["FACE"], vec1, geompy.GEOM.ST_ON) +[Face_2] = geompy.GetShapesOnPlane(Partition_1, geompy.ShapeType["FACE"], vec2, geompy.GEOM.ST_ON) + +# meshing (we have linear tetrahedrons here, but other elements are OK) + +Mesh_1 = smesh.Mesh(Partition_1) +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(15) +MEFISTO_2D = Mesh_1.Triangle(algo=smesh.MEFISTO) +Length_From_Edges_2D = MEFISTO_2D.LengthFromEdges() +ALGO3D = Mesh_1.Tetrahedron() +isDone = Mesh_1.Compute() + +# relevant groups of volumes and faces + +Solid_1_1 = Mesh_1.GroupOnGeom(Solid_1,'Solid_1',SMESH.VOLUME) +Solid_2_1 = Mesh_1.GroupOnGeom(Solid_2,'Solid_2',SMESH.VOLUME) +Solid_3_1 = Mesh_1.GroupOnGeom(Solid_3,'Solid_3',SMESH.VOLUME) +Solid_4_1 = Mesh_1.GroupOnGeom(Solid_4,'Solid_4',SMESH.VOLUME) +Face_1_1 = Mesh_1.GroupOnGeom(Face_1,'Face_1',SMESH.FACE) +Face_2_1 = Mesh_1.GroupOnGeom(Face_2,'Face_2',SMESH.FACE) + +\endcode + +\n Here, the 4 groups of volumes [Solid_1_1, Solid_2_1, Solid_3_1, Solid_4_1] constitute a partition of the mesh. +The flat elements on group boundaries and on faces are built with the following code. +\n If the last argument (Boolean) in DoubleNodesOnGroupBoundaries is set to 1, +the flat elements are built, otherwise, there is only a duplication of the nodes. + +\code +Mesh_1.DoubleNodesOnGroupBoundaries([Solid_1_1, Solid_2_1, Solid_3_1, Solid_4_1], 1) + +Mesh_1.CreateFlatElementsOnFacesGroups([Face_1_1, Face_2_1]) +\endcode + +\n To observe flat element groups, save the resulting mesh on a MED file and reload it. + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_grouping_elements.doc b/doc/salome/gui/SMESH/input/tui_grouping_elements.doc index e03433f1c..a163d13f5 100644 --- a/doc/salome/gui/SMESH/input/tui_grouping_elements.doc +++ b/doc/salome/gui/SMESH/input/tui_grouping_elements.doc @@ -19,7 +19,11 @@ aFilter = smesh.GetFilter(smesh.FACE, smesh.FT_Area, smesh.FT_MoreThan, 100.) anIds = mesh.GetIdsFromFilter(aFilter) # create a group consisting of faces with area > 100 -aGroup = mesh.MakeGroupByIds("Area > 100", smesh.FACE, anIds) +aGroup1 = mesh.MakeGroupByIds("Area > 100", smesh.FACE, anIds) + +# create a group that contains all nodes from the mesh +aGroup2 = mesh.CreateEmptyGroup(smesh.NODE, "all nodes") +aGroup2.AddFrom(mesh.mesh) salome.sg.updateObjBrowser(1) \endcode @@ -70,6 +74,51 @@ aSmeshGroup2 = quadra.GroupOnGeom(aGeomGroupE) salome.sg.updateObjBrowser(1) \endcode +
                +\anchor tui_create_group_on_filter + +

                Create a Group on Filter

                + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +box = geompy.MakeBoxDXDYDZ(10,10,10) + +# make a mesh with quadrangles of different area in range [1,16] +mesh = Mesh(box,"Quad mesh") +hyp1D = mesh.Segment().StartEndLength( 1, 4 ) +mesh.Quadrangle() +mesh.Compute() + +# create a group on filter selecting faces of medium size +critaria = [ \ + GetCriterion(FACE, FT_Area, ">", 1.1, BinaryOp=FT_LogicalAND ), + GetCriterion(FACE, FT_Area, "<", 15.0 ) + ] +filt = GetFilterFromCriteria( critaria ) +filtGroup = mesh.GroupOnFilter( FACE, "group on filter", filt ) +print "Group on filter conatains %s elemens" % filtGroup.Size() + +# group on filter is updated if the mesh is modified +hyp1D.SetStartLength( 2.5 ) +hyp1D.SetEndLength( 2.5 ) +mesh.Compute() +print "After mesh change, group on filter conatains %s elemens" % filtGroup.Size() + +# set a new filter defining the group +filt2 = GetFilter( FACE, FT_RangeOfIds, "1-50" ) +filtGroup.SetFilter( filt2 ) +print "With a new filter, group on filter conatains %s elemens" % filtGroup.Size() + +# group is updated at modification of the filter +filt2.SetCriteria( [ GetCriterion( FACE, FT_RangeOfIds, "1-70" )]) +filtIDs3 = filtGroup.GetIDs() +print "After filter modification, group on filter conatains %s elemens" % filtGroup.Size() + +salome.sg.updateObjBrowser(1) +\endcode +
                \anchor tui_edit_group

                Edit a Group

                @@ -318,13 +367,13 @@ salome.sg.updateObjBrowser(1) \endcode \image html dimgroup_tui1.png -
                Source groups of faces<\center> +
                Source groups of faces
                \image html dimgroup_tui2.png -
                Result groups of edges and nodes<\center> +
                Result groups of edges and nodes
                -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/tui_measurements.doc b/doc/salome/gui/SMESH/input/tui_measurements.doc new file mode 100644 index 000000000..aabf0b317 --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_measurements.doc @@ -0,0 +1,84 @@ +/*! + +\page tui_measurements_page Measurements + +\section tui_min_distance Minimum Distance + +\code + +import smesh +from SMESH_mechanic import mesh as mesh1 +from SMESH_test1 import mesh as mesh2 + +mesh1.Compute() +mesh2.Compute() + +# compute min distance from mesh1 to the origin (not available yet) +smesh.MinDistance(mesh1) + +# compute min distance from node 10 of mesh1 to the origin +smesh.MinDistance(mesh1, id1=10) +# ... or +mesh1.MinDistance(10) + +# compute min distance between nodes 10 and 20 of mesh1 +smesh.MinDistance(mesh1, id1=10, id2=20) +# ... or +mesh1.MinDistance(10, 20) + +# compute min distance from element 100 of mesh1 to the origin (not available yet) +smesh.MinDistance(mesh1, id1=100, isElem1=True) +# ... or +mesh1.MinDistance(100, isElem1=True) + +# compute min distance between elements 100 and 200 of mesh1 (not available yet) +smesh.MinDistance(mesh1, id1=100, id2=200, isElem1=True, isElem2=True) +# ... or +mesh1.MinDistance(100, 200, True, True) + +# compute min distance from element 100 to node 20 of mesh1 (not available yet) +smesh.MinDistance(mesh1, id1=100, id2=20, isElem1=True) +# ... or +mesh1.MinDistance(100, 20, True) + +# compute min distance from mesh1 to mesh2 (not available yet) +smesh.MinDistance(mesh1, mesh2) + +# compute min distance from node 10 of mesh1 to node 20 of mesh2 +smesh.MinDistance(mesh1, mesh2, 10, 20) + +# compute min distance from node 10 of mesh1 to element 200 of mesh2 (not available yet) +smesh.MinDistance(mesh1, mesh2, 10, 200, isElem2=True) + +# etc... + +\endcode + +\section tui_bounding_box Bounding Box + +\code + +import smesh +from SMESH_mechanic import mesh as mesh1 +from SMESH_test1 import mesh as mesh2 + +mesh1.Compute() +mesh2.Compute() + +# compute bounding box for mesh1 +mesh1.BoundingBox() + +# compute bounding box for list of nodes of mesh1 +mesh1.BoundingBox([363, 364, 370, 371, 372, 373, 379, 380, 381]) + +# compute bounding box for list of elements of mesh1 +mesh1.BoundingBox([363, 364, 370, 371, 372, 373, 379, 380, 381], isElem=True) + +# compute common bounding box of mesh1 and mesh2 +smesh.BoundingBox([mesh1, mesh2]) + +# etc... + +\endcode + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_modifying_meshes.doc b/doc/salome/gui/SMESH/input/tui_modifying_meshes.doc index bc7b78c89..922d15969 100644 --- a/doc/salome/gui/SMESH/input/tui_modifying_meshes.doc +++ b/doc/salome/gui/SMESH/input/tui_modifying_meshes.doc @@ -22,6 +22,26 @@ if new_id == 0: print "KO node addition." else: print "New Node has been added with ID ", new_id \endcode +
                +\anchor tui_add_0DElement +

                Add 0D Element

                + +\code +import SMESH_mechanic + +mesh = SMESH_mechanic.mesh + +# add node +node_id = mesh.AddNode(50, 10, 0) + +# add 0D Element +new_id = mesh.Add0DElement(node_id) + +print "" +if new_id == 0: print "KO node addition." +else: print "New 0D Element has been added with ID ", new_id +\endcode +
                \anchor tui_add_edge

                Add Edge

                @@ -262,35 +282,40 @@ else: print "KO Elements removing." \endcode
                -\anchor tui_renumbering_nodes_and_elements -

                Renumbering Nodes and Elements

                +\anchor tui_removing_orphan_nodes +

                Removing Orphan Nodes

                \code import SMESH_mechanic mesh = SMESH_mechanic.mesh -mesh.RenumberNodes() - -mesh.RenumberElements() +# add orphan nodes +mesh.AddNode(0,0,0) +mesh.AddNode(1,1,1) +# remove just created orphan nodes +res = mesh.RemoveOrphanNodes() +if res == 1: print "Removed %d nodes!" % res +else: print "KO nodes removing." \endcode
                -\anchor tui_moving_nodes -

                Moving Nodes

                +\anchor tui_renumbering_nodes_and_elements +

                Renumbering Nodes and Elements

                \code import SMESH_mechanic mesh = SMESH_mechanic.mesh -# move node #38 -mesh.MoveNode(38, 20., 10., 0.) +mesh.RenumberNodes() + +mesh.RenumberElements() \endcode
                -\anchor tui_mesh_through_point -

                Mesh through point

                +\anchor tui_moving_nodes +

                Moving Nodes

                \code from geompy import * @@ -324,16 +349,11 @@ n = mesh.FindNodeClosestTo( -1,-1,-1 ) if not n == node000: raise "FindNodeClosestTo() returns " + str( n ) + " != " + str( node000 ) -# check if any node will be found for a point inside a box -n = mesh.FindNodeClosestTo( 100, 100, 100 ) -if not n > 0: - raise "FindNodeClosestTo( 100, 100, 100 ) fails" - # move node000 to a new location x,y,z = -10, -10, -10 -n = mesh.MeshToPassThroughAPoint( x,y,z ) -if not n == node000: - raise "FindNodeClosestTo() returns " + str( n ) + " != " + str( node000 ) +n = mesh.MoveNode( n,x,y,z ) +if not n: + raise "MoveNode() returns " + n # check the coordinates of the node000 xyz = mesh.GetNodeXYZ( node000 ) @@ -768,7 +788,6 @@ mesh.RotationSweepObject(GroupRotate, axisXYZ, angle45, 4, 1e-5) \code import geompy - import smesh # define the geometry @@ -802,17 +821,151 @@ algo2D.MaxElementArea(240) isDone = Mesh_2.Compute() if not isDone: print 'Mesh Mesh_2 : computation failed' -# create a pattern +# create a 2d pattern pattern = smesh.GetPattern() isDone = pattern.LoadFromFace(Mesh_2.GetMesh(), Face_2, 0) if (isDone != 1): print 'LoadFromFace :', pattern.GetErrorCode() # apply the pattern to a face of the first mesh -pattern.ApplyToMeshFaces(Mesh_1.GetMesh(), [17], 0, 0) - +facesToSplit = Mesh_1.GetElementsByType(smesh.SMESH.FACE) +print "Splitting %d rectangular face(s) to %d triangles..."%(len(facesToSplit), 2*len(facesToSplit)) +pattern.ApplyToMeshFaces(Mesh_1.GetMesh(), facesToSplit, 0, 0) isDone = pattern.MakeMesh(Mesh_1.GetMesh(), 0, 0) if (isDone != 1): print 'MakeMesh :', pattern.GetErrorCode() + +# create quadrangle mesh +Mesh_3 = smesh.Mesh(Box_1) +Mesh_3.Segment().NumberOfSegments(1) +Mesh_3.Quadrangle() +Mesh_3.Hexahedron() +isDone = Mesh_3.Compute() +if not isDone: print 'Mesh Mesh_3 : computation failed' + +# create a 3d pattern (hexahedrons) +pattern_hexa = smesh.GetPattern() + +smp_hexa = """!!! Nb of points: +15 + 0 0 0 !- 0 + 1 0 0 !- 1 + 0 1 0 !- 2 + 1 1 0 !- 3 + 0 0 1 !- 4 + 1 0 1 !- 5 + 0 1 1 !- 6 + 1 1 1 !- 7 + 0.5 0 0.5 !- 8 + 0.5 0 1 !- 9 + 0.5 0.5 0.5 !- 10 + 0.5 0.5 1 !- 11 + 1 0 0.5 !- 12 + 1 0.5 0.5 !- 13 + 1 0.5 1 !- 14 + !!! Indices of points of 4 elements: + 8 12 5 9 10 13 14 11 + 0 8 9 4 2 10 11 6 + 2 10 11 6 3 13 14 7 + 0 1 12 8 2 3 13 10""" + +pattern_hexa.LoadFromFile(smp_hexa) + +# apply the pattern to a mesh +volsToSplit = Mesh_3.GetElementsByType(smesh.SMESH.VOLUME) +print "Splitting %d hexa volume(s) to %d hexas..."%(len(volsToSplit), 4*len(volsToSplit)) +pattern_hexa.ApplyToHexahedrons(Mesh_3.GetMesh(), volsToSplit,0,3) +isDone = pattern_hexa.MakeMesh(Mesh_3.GetMesh(), True, True) +if (isDone != 1): print 'MakeMesh :', pattern_hexa.GetErrorCode() + +# create one more quadrangle mesh +Mesh_4 = smesh.Mesh(Box_1) +Mesh_4.Segment().NumberOfSegments(1) +Mesh_4.Quadrangle() +Mesh_4.Hexahedron() +isDone = Mesh_4.Compute() +if not isDone: print 'Mesh Mesh_4 : computation failed' + +# create another 3d pattern (pyramids) +pattern_pyra = smesh.GetPattern() + +smp_pyra = """!!! Nb of points: +9 + 0 0 0 !- 0 + 1 0 0 !- 1 + 0 1 0 !- 2 + 1 1 0 !- 3 + 0 0 1 !- 4 + 1 0 1 !- 5 + 0 1 1 !- 6 + 1 1 1 !- 7 + 0.5 0.5 0.5 !- 8 + !!! Indices of points of 6 elements: + 0 1 5 4 8 + 7 5 1 3 8 + 3 2 6 7 8 + 2 0 4 6 8 + 0 2 3 1 8 + 4 5 7 6 8""" + +pattern_pyra.LoadFromFile(smp_pyra) + +# apply the pattern to a face mesh +volsToSplit = Mesh_4.GetElementsByType(smesh.SMESH.VOLUME) +print "Splitting %d hexa volume(s) to %d hexas..."%(len(volsToSplit), 6*len(volsToSplit)) +pattern_pyra.ApplyToHexahedrons(Mesh_4.GetMesh(), volsToSplit,1,0) +isDone = pattern_pyra.MakeMesh(Mesh_4.GetMesh(), True, True) +if (isDone != 1): print 'MakeMesh :', pattern_pyra.GetErrorCode() +\endcode + +
                +\anchor tui_quadratic +

                Convert mesh to/from quadratic

                + +\code +import geompy +import smesh + +# create sphere of radius 100 + +Sphere = geompy.MakeSphereR( 100 ) +geompy.addToStudy( Sphere, "Sphere" ) + +# create simple trihedral mesh + +Mesh = smesh.Mesh(Sphere) +Regular_1D = Mesh.Segment() +Nb_Segments = Regular_1D.NumberOfSegments(5) +MEFISTO_2D = Mesh.Triangle() +Tetrahedron = Mesh.Tetrahedron() + +# compute mesh + +isDone = Mesh.Compute() + +# convert to quadratic +# theForce3d = 1; this results in the medium node lying at the +# middle of the line segments connecting start and end node of a mesh +# element + +Mesh.ConvertToQuadratic( theForce3d=1 ) + +# revert back to the non-quadratic mesh + +Mesh.ConvertFromQuadratic() + +# convert to quadratic +# theForce3d = 0; this results in the medium node lying at the +# geometrical edge from which the mesh element is built + +Mesh.ConvertToQuadratic( theForce3d=0 ) + +# to convert not the whole mesh but a sub-mesh, provide it as +# an additional argument to the functions: +# Mesh.ConvertToQuadratic( 0, subMesh ) +# Mesh.ConvertFromQuadratic( subMesh ) +# +# Note that the mesh becomes non-conformal at conversion of sub-mesh. + \endcode -*/ \ No newline at end of file +*/ diff --git a/doc/salome/gui/SMESH/input/tui_notebook_smesh.doc b/doc/salome/gui/SMESH/input/tui_notebook_smesh.doc new file mode 100644 index 000000000..a300ee633 --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_notebook_smesh.doc @@ -0,0 +1,47 @@ +/*! + +\page tui_notebook_smesh_page Using SALOME NoteBook + +\anchor tui_notebook_smesh + +\code +import geompy +import smesh +import salome_notebook + +# set variables +notebook = salome_notebook.notebook +notebook.set("Length", 100) +notebook.set("Width", 200) +notebook.set("Offset", 50) + +notebook.set("NbSegments", 7) +notebook.set("MaxElementArea", 800) +notebook.set("MaxElementVolume", 900) + +# create a box +box = geompy.MakeBoxDXDYDZ("Length", "Width", 300) +idbox = geompy.addToStudy(box, "Box") + +# create a mesh +tetra = smesh.Mesh(box, "MeshBox") + +algo1D = tetra.Segment() +algo1D.NumberOfSegments("NbSegments") + +algo2D = tetra.Triangle() +algo2D.MaxElementArea("MaxElementArea") + +algo3D = tetra.Tetrahedron() +algo3D.MaxElementVolume("MaxElementVolume") + +# compute the mesh +ret = tetra.Compute() + +# translate the mesh +point = smesh.PointStruct("Offset", 0., 0.) +vector = smesh.DirStruct(point) +tetra.TranslateObject(tetra, vector, 0) +\endcode + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_quality_controls.doc b/doc/salome/gui/SMESH/input/tui_quality_controls.doc index 57d52c69e..bc99172aa 100644 --- a/doc/salome/gui/SMESH/input/tui_quality_controls.doc +++ b/doc/salome/gui/SMESH/input/tui_quality_controls.doc @@ -2,9 +2,7 @@ \page tui_quality_controls_page Quality Controls -
                -\anchor tui_free_borders -

                Free Borders

                +\section tui_free_borders Free Borders \code import salome @@ -48,9 +46,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_borders_at_multiconnection -

                Borders at Multiconnection

                +\section tui_borders_at_multiconnection Borders at Multiconnection \code import salome @@ -97,9 +93,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_length_1d -

                Length 1D

                +\section tui_length_1d Length 1D \code import salome @@ -145,9 +139,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_free_edges -

                Free Edges

                +\section tui_free_edges Free Edges \code import SMESH_mechanic @@ -189,9 +181,7 @@ for i in range(len(aBorders)): salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_free_nodes -

                Free Nodes

                +\section tui_free_nodes Free Nodes \code import salome @@ -242,10 +232,7 @@ print "" salome.sg.updateObjBrowser(1) \endcode - -
                -\anchor tui_free_faces -

                Free Faces

                +\section tui_free_faces Free Faces \code import salome @@ -281,13 +268,12 @@ geompy.addToStudy( Plane_2, "Plane_2" ) import smesh import StdMeshers -import NETGENPlugin Mesh_1 = smesh.Mesh(Partition_1) Regular_1D = Mesh_1.Segment() Max_Size_1 = Regular_1D.MaxSize(34.641) MEFISTO_2D = Mesh_1.Triangle() -Tetrahedron_Netgen = Mesh_1.Tetrahedron(algo=smesh.NETGEN) +Tetrahedronn = Mesh_1.Tetrahedron() isDone = Mesh_1.Compute() # create a group of free faces @@ -298,11 +284,11 @@ aGroup = Mesh_1.CreateEmptyGroup(smesh.FACE, "Free_faces") aGroup.Add(aFaceIds) # print the result -print "Criterion: Free nodes Nb = ", len(anNodeIds) +print "Criterion: Free faces Nb = ", len(aFaceIds) j = 1 for i in range(len(aFaceIds)): if j > 20: j = 1; print "" - print anNodeIds[i], + print aFaceIds[i], j = j + 1 pass print "" @@ -322,10 +308,91 @@ aGroup.Add(aFaceIds) salome.sg.updateObjBrowser(1) \endcode +\section tui_bare_border_faces Bare border faces + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +box = geompy.MakeBoxDXDYDZ(100, 100, 100) +geompy.addToStudy( box, "box" ) + +mesh = smesh.Mesh(box) +mesh.Segment().NumberOfSegments(3) +mesh.Quadrangle() +mesh.Compute() + +# remove 2 faces +allFaces = mesh.GetElementsByType(FACE) +mesh.RemoveElements( allFaces[0:2]) + +bareGroup = mesh.MakeGroup("bare faces", FACE, FT_BareBorderFace) +assert(bareGroup.Size() == 3) +\endcode + +\section tui_bare_border_volumes Bare border volumes + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +box = geompy.MakeBoxDXDYDZ(100, 30, 10) +# the smallest face of the box +face = geompy.SubShapeAllSorted( box, geompy.ShapeType["FACE"])[0] + +geompy.addToStudy( box, "box" ) +geompy.addToStudyInFather( box, face, "face" ) + +mesh = Mesh(box) +mesh.AutomaticHexahedralization(); + +# remove half of mesh faces from the smallest face +faceFaces = mesh.GetSubMeshElementsId(face) +faceToRemove = faceFaces[: len(faceFaces)/2] +mesh.RemoveElements( faceToRemove ) + +# make a group of volumes missing the removed faces +bareGroup = mesh.MakeGroup("bare volumes", VOLUME, FT_BareBorderVolume) +assert(bareGroup.Size() == len( faceToRemove)) +\endcode + +\section tui_over_constrained_faces Over-constrained faces +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +mesh = Mesh() +faceFilter = GetFilter(FACE,FT_OverConstrainedFace) + +#make an edge +n1 = mesh.AddNode(0,0,0) +n2 = mesh.AddNode(10,0,0) +edge = mesh.AddEdge([n1,n2]) +assert( not mesh.GetIdsFromFilter( faceFilter )) -
                -\anchor tui_length_2d -

                Length 2D

                +# make faces +mesh.ExtrusionSweep([edge], MakeDirStruct(0,7,0), 5) +assert( 2 == len( mesh.GetIdsFromFilter( faceFilter ))) +\endcode + +\section tui_over_constrained_volumes Over-constrained volumes +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +mesh = Mesh() +volumeFilter = GetFilter(VOLUME,FT_OverConstrainedVolume) + +# make volumes by extrusion of one face +n1 = mesh.AddNode(0,0,0) +n2 = mesh.AddNode(10,0,0) +edge = mesh.AddEdge([n1,n2]) +mesh.ExtrusionSweep([edge], MakeDirStruct(0,7,0), 1) +mesh.ExtrusionSweep( mesh.GetElementsByType(FACE), MakeDirStruct(0,0,5), 7) +assert( 2 == len( mesh.GetIdsFromFilter( volumeFilter ))) +\endcode + +\section tui_length_2d Length 2D \code import salome @@ -372,9 +439,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_borders_at_multiconnection_2d -

                Borders at Multiconnection 2D

                +\section tui_borders_at_multiconnection_2d Borders at Multiconnection 2D \code import salome @@ -421,9 +486,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_area -

                Area

                +\section tui_area Area \code import SMESH_mechanic @@ -456,9 +519,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_taper -

                Taper

                +\section tui_taper Taper \code import SMESH_mechanic @@ -491,9 +552,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_aspect_ratio -

                Aspect Ratio

                +\section tui_aspect_ratio Aspect Ratio \code import SMESH_mechanic @@ -526,9 +585,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_minimum_angle -

                Minimum Angle

                +\section tui_minimum_angle Minimum Angle \code import SMESH_mechanic @@ -562,9 +619,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_warping -

                Warping

                +\section tui_warping Warping \code import SMESH_mechanic @@ -598,9 +653,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_skew -

                Skew

                +\section tui_skew Skew \code import SMESH_mechanic @@ -633,9 +686,40 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_aspect_ratio_3d -

                Aspect Ratio 3D

                +\section tui_max_element_length_2d Element Diameter 2D + +\code +import SMESH_mechanic + +smesh = SMESH_mechanic.smesh +mesh = SMESH_mechanic.mesh +salome = SMESH_mechanic.salome + +# Criterion : ELEMENT DIAMETER 2D > 10 +mel_2d_margin = 10 + +aFilter = smesh.GetFilter(smesh.FACE, smesh.FT_MaxElementLength2D, smesh.FT_MoreThan, mel_2d_margin) + +anIds = mesh.GetIdsFromFilter(aFilter) + +# print the result +print "Criterion: Element Diameter 2D Ratio > ", mel_2d_margin, " Nb = ", len(anIds) +j = 1 +for i in range(len(anIds)): + if j > 20: j = 1; print "" + print anIds[i], + j = j + 1 + pass +print "" + +# create a group +aGroup = mesh.CreateEmptyGroup(smesh.FACE, "Element Diameter 2D > " + `mel_2d_margin`) +aGroup.Add(anIds) + +salome.sg.updateObjBrowser(1) +\endcode + +\section tui_aspect_ratio_3d Aspect Ratio 3D \code import SMESH_mechanic_tetra @@ -669,9 +753,7 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode -
                -\anchor tui_volume -

                Volume

                +\section tui_volume Volume \code import SMESH_mechanic_tetra @@ -706,4 +788,37 @@ aGroup.Add(anIds) salome.sg.updateObjBrowser(1) \endcode +\section tui_max_element_length_3d Element Diameter 3D + +\code +import SMESH_mechanic_tetra + +smesh = SMESH_mechanic_tetra.smesh +mesh = SMESH_mechanic_tetra.mesh +salome = SMESH_mechanic_tetra.salome + +# Criterion : ELEMENT DIAMETER 3D > 10 +mel_3d_margin = 10 + +aFilter = smesh.GetFilter(smesh.FACE, smesh.FT_MaxElementLength3D, smesh.FT_MoreThan, mel_3d_margin) + +anIds = mesh.GetIdsFromFilter(aFilter) + +# print the result +print "Criterion: Element Diameter 3D Ratio > ", mel_3d_margin, " Nb = ", len(anIds) +j = 1 +for i in range(len(anIds)): + if j > 20: j = 1; print "" + print anIds[i], + j = j + 1 + pass +print "" + +# create a group +aGroup = mesh.CreateEmptyGroup(smesh.FACE, "Element Diameter 3D > " + `mel_3d_margin`) +aGroup.Add(anIds) + +salome.sg.updateObjBrowser(1) +\endcode + */ diff --git a/doc/salome/gui/SMESH/input/tui_transforming_meshes.doc b/doc/salome/gui/SMESH/input/tui_transforming_meshes.doc index 163afe0e4..a4c6df87d 100644 --- a/doc/salome/gui/SMESH/input/tui_transforming_meshes.doc +++ b/doc/salome/gui/SMESH/input/tui_transforming_meshes.doc @@ -44,6 +44,37 @@ angle270 = 1.5 * math.pi mesh.Rotate([], axisXYZ, angle270, 1) \endcode +
                +\anchor tui_scale +

                Scale

                + +\code +import geompy +Box = geompy.MakeBoxDXDYDZ(200, 200, 200) +f = geompy.SubShapeAllSorted(Box, geompy.ShapeType["FACE"]) + +import smesh,SMESH +import StdMeshers +Mesh1 = smesh.Mesh(f[0]) +Regular_1D = Mesh1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(3) +Nb_Segments_1.SetDistrType( 0 ) +Quadrangle_2D = Mesh1.Quadrangle() +isDone = Mesh1.Compute() + +#Perform scale opration for the whole mesh and creation of a new mesh +newMesh = Mesh1.ScaleMakeMesh(Mesh1,SMESH.PointStruct(100,100,200),[0.5,0.3,0.7],True,"ScaledMesh") + +#Perform scale operation for the whole mesh and copy elements +Mesh1.Scale(Mesh1,SMESH.PointStruct(200,100,100),[0.5,0.5,0.5],True,True) + +#Perform scale opration for two edges and move elements +Mesh1.Scale([1,2],SMESH.PointStruct(-100,100,100),[0.8,1.0,0.7],False) + +#Perform scale opration for one face and move elements +Mesh1.Scale([21],SMESH.PointStruct(0,200,200),[0.7,0.7,0.7],False) +\endcode +
                \anchor tui_symmetry

                Symmetry

                @@ -319,4 +350,237 @@ mesh.Compute() mesh.SewSideElements([69, 70, 71, 72], [91, 92, 89, 90], 8, 38, 23, 58) \endcode -*/ \ No newline at end of file +
                +\anchor tui_duplicate_nodes +

                Duplicate nodes

                + +\code +import salome +import smesh +import SMESH_test1 + +mesh = SMESH_test1.mesh + +# Compute mesh +mesh.Compute() + +# Without the duplication of border elements + +# Nodes to duplicate +nodes1 = mesh.CreateEmptyGroup( smesh.NODE, 'nodes1' ) +nodes1.Add( [ 289, 278, 302, 285 ] ) + +# Group of faces to replace nodes with new ones +faces1 = mesh.CreateEmptyGroup( smesh.FACE, 'faces1' ) +faces1.Add( [ 519, 556, 557 ] ) + +# Duplicate nodes +print "\nMesh before the first nodes duplication:" +print "Nodes : ", mesh.NbNodes() +print "Edges : ", mesh.NbEdges() +print "Triangles : ", mesh.NbTriangles() + +groupOfCreatedNodes = mesh.DoubleNodeGroup(nodes1, faces1, theMakeGroup=True) +print "New nodes:", groupOfCreatedNodes.GetIDs() + +print "\nMesh after the first nodes duplication:" +print "Nodes : ", mesh.NbNodes() +print "Edges : ", mesh.NbEdges() +print "Triangles : ", mesh.NbTriangles() + +# With the duplication of border elements + +# Edges to duplicate +edges = mesh.CreateEmptyGroup( smesh.EDGE, 'edges' ) +edges.Add( [ 29, 30, 31 ] ) + +# Nodes not to duplicate +nodes2 = mesh.CreateEmptyGroup( smesh.NODE, 'nodes2' ) +nodes2.Add( [ 32, 5 ] ) + +# Group of faces to replace nodes with new ones +faces2 = mesh.CreateEmptyGroup( smesh.FACE, 'faces2' ) +faces2.Add( [ 576, 578, 580 ] ) + +# Duplicate nodes +print "\nMesh before the second nodes duplication:" +print "Nodes : ", mesh.NbNodes() +print "Edges : ", mesh.NbEdges() +print "Triangles : ", mesh.NbTriangles() + +groupOfNewEdges = mesh.DoubleNodeElemGroup( edges, nodes2, faces2, theMakeGroup=True ) +print "New edges:", groupOfNewEdges.GetIDs() + +print "\nMesh after the second nodes duplication:" +print "Nodes : ", mesh.NbNodes() +print "Edges : ", mesh.NbEdges() +print "Triangles : ", mesh.NbTriangles() + +# Update object browser +if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser(0) +\endcode + +
                +\anchor tui_make_2dmesh_from_3d +

                Create boundary elements

                + +\code +from smesh import * +SetCurrentStudy(salome.myStudy) + +box = geompy.MakeBoxDXDYDZ(100, 100, 100) +gFaces = geompy.SubShapeAllSorted(box, geompy.ShapeType["FACE"]) +f1,f2 = gFaces[0],gFaces[1] +geompy.addToStudy(box,"box") +geompy.addToStudyInFather(box,f1,"face1") +geompy.addToStudyInFather(box,f2,"face2") + +twoFaces = geompy.MakeCompound([f1,f2]) + +## ----------- +## +## 2D from 3D +## +## ----------- +dim = SMESH.BND_2DFROM3D + +init_mesh = Mesh(box, "box") +init_mesh.AutomaticHexahedralization() # it makes 3 x 3 x 3 hexahedrons + +# remove some faces +faces = init_mesh.GetElementsByType( SMESH.FACE ) +nb_faces = len( faces ) +rm_face = faces[ : nb_faces/2] +init_mesh.RemoveElements( rm_face ) + +# restore boundary in this mesh +mesh = CopyMesh( init_mesh, "2D from 3D") +groupName = "bnd 2D" +nb, new_mesh, new_group = mesh.MakeBoundaryElements(dim, groupName) + +# restore boundary (only) in other mesh +meshName = "2D boundary of " + init_mesh.GetName() +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName) + +# restore boundary in mesh copy +meshName = init_mesh.GetName() + " + boundary" +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName, toCopyAll=True) + + +## ----------- +## +## 1D from 2D +## +## ----------- +dim = SMESH.BND_1DFROM2D + +init_mesh = Mesh(f1, "2D mesh") +init_mesh.AutomaticHexahedralization() + +# remove some edges +edges = init_mesh.GetElementsByType( SMESH.EDGE ) +nb_edges = len( edges ) +rm_edge = edges[ : nb_edges/2] +init_mesh.RemoveElements( rm_edge ) + + +# restore boundary edges in this mesh +mesh = CopyMesh( init_mesh, "1D from 2D") +groupName = "bnd 1D" +nb, new_mesh, new_group = mesh.MakeBoundaryElements(dim, groupName) + +# restore boundary edges (only) in other mesh +meshName = "1D boundary of " + init_mesh.GetName() +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName) + +# restore boundary edges in mesh copy +meshName = init_mesh.GetName() + " + boundary" +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName, toCopyAll=True) + + + +## ------------------ +## +## 1D from 2D GROUPS +## +## ------------------ +dim = SMESH.BND_1DFROM3D + +init_mesh = Mesh(box, "box") +init_mesh.AutomaticHexahedralization() # it makes 3 x 3 x 3 hexahedrons +# remove all edges +rm_edges = init_mesh.GetElementsByType( SMESH.EDGE ) +init_mesh.RemoveElements( rm_edges ) + +# make groups of faces +fGroup1 = init_mesh.Group( f1, "f1" ) +fGroup2 = init_mesh.Group( f2, "f2" ) + +# make 1D boundary around groups in this mesh +mesh = CopyMesh( init_mesh, "1D from 2D groups", toCopyGroups=True) +groups = mesh.GetGroups() +nb, new_mesh, new_group = mesh.MakeBoundaryElements(dim, groupName,groups=groups) + +# make 1D boundary (only) in other mesh +meshName = "boundary from groups of " + init_mesh.GetName() +groups = init_mesh.GetGroups() +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName,groups=groups) + +# make 1D boundary in mesh copy +meshName = init_mesh.GetName() + " + boundary from groups" +nb, new_mesh, new_group = init_mesh.MakeBoundaryElements(dim, groupName, meshName, + groups=groups, toCopyAll=True) + +\endcode + +
                +\anchor tui_reorient_faces +

                Reorient faces by vector

                + +\code +import smesh, geompy, SMESH + +# create a geometry consisting of two faces +box = geompy.MakeBoxDXDYDZ( 10, 10, 10 ) +faces = geompy.SubShapeAllSorted( box, geompy.ShapeType["FACE"]) + +shape = geompy.MakeCompound( faces[:2] ) +faces = geompy.SubShapeAll( shape, geompy.ShapeType["FACE"] ) +geompy.addToStudy( shape, "shape") +geompy.addToStudyInFather( shape, faces[0], "faces[0]") +geompy.addToStudyInFather( shape, faces[1], "faces[1]") + +# create a 2D mesh +mesh = smesh.Mesh( shape, "test_Reorient2D") +mesh.AutomaticHexahedralization(0.5) +localAlgo = mesh.Segment(faces[0]) +localAlgo.NumberOfSegments( 11 ) +mesh.Compute() +group = mesh.Group( faces[1] ) + +vec = geompy.MakeVectorDXDYDZ( 1, 1, 1 ) + +# Each of arguments of Reorient2D() function can be of different types: +# +# 2DObject - the whole mesh +# Direction - a GEOM object (vector) +# FaceOrPoint - an ID of face +mesh.Reorient2D( mesh, vec, mesh.NbElements() ) +# +# 2DObject - a sub-mesh +# Direction - components of a vector +# FaceOrPoint - a GEOM object (vertex) +mesh.Reorient2D( localAlgo.GetSubMesh(), [ 1, -1, 1 ], geompy.GetFirstVertex( vec )) +# +# 2DObject - a group of faces +# Direction - a SMESH.DirStruct structure +# FaceOrPoint - coordinates of a point +mesh.Reorient2D( group, smesh.MakeDirStruct( -10, 1, 10 ), [0,0,0]) +# +# FaceOrPoint - a SMESH.PointStruct structure +mesh.Reorient2D( localAlgo.GetSubMesh().GetIDs(), [10,1,0], SMESH.PointStruct(0,0,0)) + +\endcode + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_use_existing_faces.doc b/doc/salome/gui/SMESH/input/tui_use_existing_faces.doc new file mode 100644 index 000000000..8b9c7325a --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_use_existing_faces.doc @@ -0,0 +1,124 @@ +/*! + +\page tui_use_existing_faces Use existing faces + +This sample demonstrates how to use Use existing faces algorithm, +which is actulally just a stub allowing to use your own 2D algoritm +implemented in Python. + + +\code +import smesh, geompy +import numpy as np + +# define my 2D algorithm +def my2DMeshing( geomFace ): + + # find gravity center of geomFace + gcXYZ = geompy.PointCoordinates( geompy.MakeCDG( geomFace )) + + # define order and orientation of edges + sortedEdges = [] + geomEdges = geompy.SubShapeAll( geomFace, geompy.ShapeType["EDGE"]) + sortedEdges.append(( geomEdges.pop(0), True )) + while geomEdges: + prevEdge_rev = sortedEdges[ -1 ] + prevVV = geompy.SubShapeAll( prevEdge_rev[0], geompy.ShapeType["VERTEX"]) + prevV2 = prevVV[ prevEdge_rev[1] ] + found = False + for iE in range( len( geomEdges )): + v1,v2 = geompy.SubShapeAll( geomEdges[ iE ], geompy.ShapeType["VERTEX"]) + same1,same2 = [( geompy.MinDistance( prevV2, v ) < 1e-7 ) for v in [v1,v2] ] + if not same1 and not same2: continue + sortedEdges.append(( geomEdges.pop( iE ), same1 )) + found = True + break + assert found + sortedEdges.reverse() + + # put nodes on edges in a right order + nodes = [] + for edge, isForward in sortedEdges: + v1,v2 = geompy.SubShapeAll( edge, geompy.ShapeType["VERTEX"]) + edgeNodes = mesh.GetSubMeshNodesId( v2, all=False ) + \ + mesh.GetSubMeshNodesId( edge, all=False ) + \ + mesh.GetSubMeshNodesId( v1, all=False ) + if not isForward: edgeNodes.reverse() + nodes.extend( edgeNodes[:-1] ) + + # create nodes inside the geomFace + r1 = 0.6 + r2 = 1 - r1 + nodesInside = [] + for n in nodes: + nXYZ = mesh.GetNodeXYZ( n ) + newXYZ = np.add( np.multiply( r1, gcXYZ ), np.multiply( r2, nXYZ )) + nodesInside.append( mesh.AddNode( newXYZ[0], newXYZ[1], newXYZ[2] )) + mesh.SetNodeOnFace( nodesInside[-1], geomFace, 0, 0 ) + + # find out orientation of faces to create + # geomFace normal + faceNorm = geompy.GetNormal( geomFace ) + v1,v2 = [ geompy.PointCoordinates( v ) \ + for v in geompy.SubShapeAll( faceNorm, geompy.ShapeType["VERTEX"]) ] + faceNormXYZ = np.subtract( v2, v1 ) + outDirXYZ = np.subtract( v1, [ 50, 50, 50 ] ) + if np.dot( faceNormXYZ, outDirXYZ ) < 0: # reversed face + faceNormXYZ = np.multiply( -1., faceNormXYZ ) + # mesh face normal + e1 = np.subtract( mesh.GetNodeXYZ( nodes[0] ), mesh.GetNodeXYZ( nodes[1] )) + e2 = np.subtract( mesh.GetNodeXYZ( nodes[0] ), mesh.GetNodeXYZ( nodesInside[0] )) + meshNorm = np.cross( e1, e2 ) + # faces orientation + reverse = ( np.dot( faceNormXYZ, meshNorm ) < 0 ) + + # create mesh faces + iN = len( nodes ) + while iN: + n1, n2, n3, n4 = nodes[iN-1], nodes[iN-2], nodesInside[iN-2], nodesInside[iN-1] + iN -= 1 + if reverse: + f = mesh.AddFace( [n1, n2, n3, n4] ) + else: + f = mesh.AddFace( [n4, n3, n2, n1] ) + # new faces must be assigned to geometry to allow 3D algorithm finding them + mesh.SetMeshElementOnShape( f, geomFace ) + + if reverse: + nodesInside.reverse() + polygon = mesh.AddPolygonalFace( nodesInside ) + mesh.SetMeshElementOnShape( polygon, geomFace ) + + return + +# create geometry and get faces to mesh with my2DMeshing() +box = geompy.MakeBoxDXDYDZ( 100, 100, 100 ) +f1 = geompy.SubShapeAll( box, geompy.ShapeType["FACE"])[0] +f2 = geompy.GetOppositeFace( box, f1 ) +geompy.addToStudy( box, "box" ) +geompy.addToStudy( f1, "f1" ) +geompy.addToStudy( f2, "f2" ) + +# compute 1D mesh +mesh = smesh.Mesh( box ) +mesh.Segment().NumberOfSegments( 5 ) +mesh.Compute() + +# compute 2D mesh +mesh.Quadrangle() +mesh.UseExistingFaces(f1) # UseExistingFaces() allows using my2DMeshing() +mesh.UseExistingFaces(f2) +my2DMeshing( f1 ) +my2DMeshing( f2 ) +assert mesh.Compute() + +# compute 3D mesh +mesh.Prism() +assert mesh.Compute() + +\endcode + +Resulting mesh: +\image html use_existing_face_sample_mesh.png + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_viewing_meshes.doc b/doc/salome/gui/SMESH/input/tui_viewing_meshes.doc index 59681c16b..fc0094e4b 100644 --- a/doc/salome/gui/SMESH/input/tui_viewing_meshes.doc +++ b/doc/salome/gui/SMESH/input/tui_viewing_meshes.doc @@ -9,10 +9,12 @@ \code import geompy import smesh +import SMESH # create a box box = geompy.MakeBox(0., 0., 0., 20., 20., 20.) geompy.addToStudy(box, "box") +[Face_1,Face_2,Face_3,Face_4,Face_5,Face_5] = geompy.SubShapeAll(box, geompy.ShapeType["FACE"]) # create a mesh tetra = smesh.Mesh(box, "MeshBox") @@ -23,13 +25,25 @@ algo1D.NumberOfSegments(3) algo2D = tetra.Triangle() algo2D.MaxElementArea(10.) -algo3D = tetra.Tetrahedron(smesh.NETGEN) +algo3D = tetra.Tetrahedron() algo3D.MaxElementVolume(900.) +# Creation of SubMesh +Regular_1D_1_1 = tetra.Segment(geom=Face_1) +Nb_Segments_1 = Regular_1D_1_1.NumberOfSegments(5) +Nb_Segments_1.SetDistrType( 0 ) +Quadrangle_2D = tetra.Quadrangle(geom=Face_1) +isDone = tetra.Compute() +submesh = Regular_1D_1_1.GetSubMesh() + # compute the mesh tetra.Compute() -# print information about the mesh +# Creation of group +group = tetra.CreateEmptyGroup( SMESH.FACE, 'Group' ) +nbAdd = group.Add( [ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 ] ) + +# Print information about the mesh print "Information about mesh:" print "Number of nodes : ", tetra.NbNodes() print "Number of edges : ", tetra.NbEdges() @@ -43,5 +57,83 @@ print " hexahedrons : ", tetra.NbHexas() print " prisms : ", tetra.NbPrisms() print " pyramids : ", tetra.NbPyramids() print " polyhedrons : ", tetra.NbPolyhedrons() + +# Get Information About Mesh by GetMeshInfo +print "\nInformation about mesh by GetMeshInfo:" +info = smesh.GetMeshInfo(tetra) +keys = info.keys(); keys.sort() +for i in keys: + print " %s : %d" % ( i, info[i] ) + pass + +# Get Information About Group by GetMeshInfo +print "\nInformation about group by GetMeshInfo:" +info = smesh.GetMeshInfo(group) +keys = info.keys(); keys.sort() +for i in keys: + print " %s : %d" % ( i, info[i] ) + pass + +# Get Information About SubMesh by GetMeshInfo +print "\nInformation about Submesh by GetMeshInfo:" +info = smesh.GetMeshInfo(submesh) +keys = info.keys(); keys.sort() +for i in keys: + print " %s : %d" % ( i, info[i] ) + pass +\endcode + + + +
                +\anchor tui_find_element_by_point +

                Find Element by Point

                + +\code +import geompy +import smesh +import SMESH + +# Create a geometry to mesh +box = geompy.MakeBoxDXDYDZ(100,100,100) + +# Create a mesh +mesh = Mesh(box,"Mesh") +mesh.AutomaticHexahedralization() +mesh.Compute() + +# Create a point +x,y,z = 0, 0, 1 + +# Find all elements (except 0D ones) located at the point +all_elems_except_0D = mesh.FindElementsByPoint(x,y,z) +assert( len(all_elems_except_0D) == 4) + +# Find nodes at the point +nodes = mesh.FindElementsByPoint(x,y,z, SMESH.NODE ) +assert( len(nodes) == 0) +assert( len( mesh.FindElementsByPoint(x,y,0, SMESH.NODE)) == 1) + +# Find an edge at the point +edges = mesh.FindElementsByPoint(x,y,z, SMESH.EDGE ) +assert( len(edges) == 1) + +# Find faces at the point +edges = mesh.FindElementsByPoint(x,y,z, SMESH.FACE ) +assert( len(edges) == 2) + +# Find a volume at the point +vols = mesh.FindElementsByPoint(x,y,z, SMESH.VOLUME ) +assert( len(vols) == 1) + +# Find 0D elements at the point +elems0d = mesh.FindElementsByPoint(x,y,z, SMESH.ELEM0D ) +assert( len(elems0d) == 0) + +# Find edges within a group +group1D = mesh.MakeGroupByIds("1D", SMESH.EDGE, [1,2] ) +edges = mesh.FindElementsByPoint(x,y,z, SMESH.EDGE, group1D ) + \endcode -*/ \ No newline at end of file + +*/ diff --git a/doc/salome/gui/SMESH/input/tui_work_on_objects_from_gui.doc b/doc/salome/gui/SMESH/input/tui_work_on_objects_from_gui.doc new file mode 100644 index 000000000..abc51d95b --- /dev/null +++ b/doc/salome/gui/SMESH/input/tui_work_on_objects_from_gui.doc @@ -0,0 +1,27 @@ +/*! + +\page tui_work_on_objects_from_gui How to work with objects from the GUI ? + +It is sometimes useful to work alternatively in the GUI of SALOME and in the Python Console. To fetch an object from the TUI simply type: + +\code +myMesh_ref = salome.IDToObject("ID") +// were ID is the number that appears in the object browser in the Entry column +// ( If hidden show it by right clicking and checking the checkbox Entry) +myMesh = smesh.Mesh(myMesh_ref) +\endcode +or +\code +myMesh_ref = salome.myStudy.FindObjectByPath("/Mesh/myMesh").GetObject() +// "/Mesh/myMesh" is the path to the desired object in the object browser +myMesh = smesh.Mesh(myMesh_ref) +\endcode + +All the methods documented in these pages can then be used on myMesh + +\note The first statement only gives you access to a reference to the object created via the GUI. +\n But the methods available on this reference are not exactly the same as those documented in these help pages. +This Python API is meant to be used on smesh.Mesh instances. +\n That's why you'll have to create such an instance with the second statement. + +*/ diff --git a/doc/salome/gui/SMESH/input/use_existing_algos.doc b/doc/salome/gui/SMESH/input/use_existing_algos.doc new file mode 100644 index 000000000..3634ccf60 --- /dev/null +++ b/doc/salome/gui/SMESH/input/use_existing_algos.doc @@ -0,0 +1,66 @@ +/*! + +\page import_algos_page Use Existing Elements Algorithms + +\n Use Existing Elements algorithms allow to define the mesh of a geometrical +object by importing suitably located mesh elements from another +mesh. The mesh elements to import from the other mesh should be contained in +groups. If several groups are used to mesh the same geometry, validity of +nodal connectivity of result mesh must be assured by connectivity of +the source mesh; no geometrical checks are performed to merge +different nodes at same locations. +
                The source elements must totally cover the meshed geometry. +The source elements lying partially over the geometry will not be used. +
                +These algorithms can be used to mesh a very complex geometry part by +part, by storing meshes of parts in files and then fusing them +together using these algorithms. +
                + +Use Existing 1D Elements algorithm allows to define the mesh of +a geometrical edge (or group of edges) +by importing mesh edges contained in a group (or groups) from another mesh. +\n To apply this algorithm select the edge to be meshed (indicated in +the field \b Geometry of Create mesh dialog box), +Use existing 1D elements in the list of 1D algorithms and click the +"Add Hypothesis" button. +The following dialog box will appear: + +\image html hyp_source_edges.png + +In this dialog box you can define +
                  +
                • The \b Name of the algorithm.
                • +
                • The Groups of Edges to import 1D elements from. +
                • To copy mesh checkbox allows to import not only the edges of +the selected Groups of Edges, but the whole source +mesh. In this case To copy groups checkbox allows to create +the same groups as in the imported source mesh.
                • +
                + +Use Existing 2D Elements algorithm allows to define the mesh of +a geometrical face (or group of faces) +by importing mesh faces contained in a group (or groups) from another mesh. +\n To apply this algorithm select the geometrical face to be meshed (indicated in +the field \b Geometry of Create mesh dialog box), +Use existing 2D elements in the list of 2D algorithms and click the +"Add Hypothesis" button. +The following dialog box will appear: + +\image html hyp_source_faces.png + +In this dialog box you can define +
                  +
                • The \b Name of the algorithm.
                • +
                • The Groups of Edges to import 1D elements from. +
                • To copy mesh checkbox allows to import not only the edges of +the selected Groups of Edges, but the whole source +mesh. In this case To copy groups checkbox allows to create +the same groups as in the imported source mesh.
                • +
                + +
                See Also a sample TUI Script of a +\ref tui_import "Use Existing Elements Algorithms". + +*/ + diff --git a/doc/salome/gui/SMESH/input/using_notebook_smesh_page.doc b/doc/salome/gui/SMESH/input/using_notebook_smesh_page.doc index 2968a27be..aac0c9052 100644 --- a/doc/salome/gui/SMESH/input/using_notebook_smesh_page.doc +++ b/doc/salome/gui/SMESH/input/using_notebook_smesh_page.doc @@ -2,9 +2,14 @@ \page using_notebook_mesh_page Using SALOME NoteBook -It is possible to use variables defined through SALOME NoteBook for -creation and modification of objects in the MESH module with the following -limitations: +SALOME NoteBook allows defining variables to be used for +creation and modification of objects. + +\image html using_notebook_smesh.png "Setting of variables in SALOME NoteBook" + +\image html addnode_notebook.png "Use of variables to add a node in MESH module" + +The following limitations on the use of variables still exist:
                • \ref radial_prism_algo_page "Distribution of Layers" hypothesis - @@ -28,6 +33,10 @@ limitations: values (indices of nodes) are correctly applied, but they are not restored after "Update study" operation.
                • \ref clipping_page "Clipping" dialog box. -
                • Colors / Size dialog box. +
                • Properties dialog box.
                + +Our TUI Scripts provide you with useful examples of +\ref tui_notebook_smesh "Using SALOME NoteBook". + */ diff --git a/doc/salome/gui/SMESH/input/viewing_meshes_overview.doc b/doc/salome/gui/SMESH/input/viewing_meshes_overview.doc index 01a272de2..376da99b0 100644 --- a/doc/salome/gui/SMESH/input/viewing_meshes_overview.doc +++ b/doc/salome/gui/SMESH/input/viewing_meshes_overview.doc @@ -17,13 +17,16 @@ right-clicking on the selected mesh. \image html dialog.png
                  +
                • Rename - allows to rename the object in the Object browser.
                • Hide all - allows to hide all objects in the viewer.
                • Update - refreshes the presentation of your mesh in the Object Browser, applying all recent changes.
                • -
                • \subpage mesh_infos_page "Mesh Infos" - provides +
                • \subpage mesh_infos_page "Mesh Information" - provides information about the mesh.
                • -
                • \ref mesh_element_info_anchor "Mesh Element Info" - provides basic -information about the selected element of the mesh.
                • +
                • \subpage find_element_by_point_page "Find Element by Point" - +allows to find all mesh elements, to which belongs a point with the +given coordinates.
                • +
                • Auto Color - switch on / off auto-assigning colors for the groups.
                • \subpage numbering_page "Numbering" - allows to display the ID numbers of all meshing elements or nodes composing your mesh in the viewer.
                • @@ -31,19 +34,27 @@ viewer. Wireframe, Shading and Nodes presentation.
                • \subpage display_entity_page "Display Entity" - allows to display Faces, Edges or both.
                • +
                • 2D Quadratic - allows to select between the representation +of quadratic edges as broken lines or as arcs
                • Orientation of faces - shows vectors of orientation of -faces of the selected mesh
                • -
                • Colors / Size - allows to select color and size of -meshes.
                • +faces of the selected mesh. The orientation vector is shown for each 2D mesh element +and for each free face of a 3D mesh element. the vector direction is calculated by +the first three nodes of the face produced by vectors n1-n2 and n1-n3. +
                • \subpage colors_size_page "Properties" - allows to define several properties, including color of elements, shrink size, ....
                • \subpage transparency_page "Transparency" - allows to change the transparency of mesh elements.
                • -
                • \subpage clipping_page "Clipping" - allows to create cross-sections of the selected objects.
                • -
                • \ref about_quality_controls_page "Controls" - graphically +
                • \ref quality_page "Controls" - graphically presents various information about meshes.
                • Hide - allows to hide the selected mesh from the viewer.
                • Show Only -allows to display only the selected mesh, hiding all other from the viewer.
                • +
                • \subpage clipping_page "Clipping" - allows to create cross-sections of the selected objects.
                • Dump view - exports an object from the viewer in bmp, png, jpg or jpeg image format.
                • -
                • Change background - allows to redefine the background color. By default it is black.
                • +
                • Change background - allows to redefine the background +color. By default it is black.
                • +
                • View Operations checkbox - allows to show/hide the +visualization toolbar in the viewer window.
                • +
                • Recording Operations - allows to show/hide the recording +toolbar in the viewer window.
                */ diff --git a/doc/salome/gui/SMESH/input/volume.doc b/doc/salome/gui/SMESH/input/volume.doc index ba99c193e..4d74fcbf0 100644 --- a/doc/salome/gui/SMESH/input/volume.doc +++ b/doc/salome/gui/SMESH/input/volume.doc @@ -9,8 +9,8 @@
                1. Display your mesh in the viewer.
                2. -
                3. Choose Controls > Volume or click "Volume" button -in the toolbar. +
                4. Choose Controls > Volume Controls > Volume or click +"Volume" button in the toolbar. \image html image145.png
                  "Volume" button
                  diff --git a/doc/salome/gui/SMESH/input/warping.doc b/doc/salome/gui/SMESH/input/warping.doc index 83bfaa3ac..86ba0c905 100644 --- a/doc/salome/gui/SMESH/input/warping.doc +++ b/doc/salome/gui/SMESH/input/warping.doc @@ -24,8 +24,8 @@ projection height ?h? to the half edge length ?l?.
                  1. Display your mesh in the viewer.
                  2. -
                  3. Choose Controls > Warping Angle or click "Warping angle" -button of the toolbar. +
                  4. Choose Controls > Face Controls > Warping Angle or click +"Warping angle" button of the toolbar. \image html image39.png
                    "Warping angle" button
                    diff --git a/doc/salome/gui/SMESH/static/doxygen.css b/doc/salome/gui/SMESH/static/doxygen.css index 88e613d23..7a2dcbde8 100755 --- a/doc/salome/gui/SMESH/static/doxygen.css +++ b/doc/salome/gui/SMESH/static/doxygen.css @@ -1,170 +1,836 @@ -H1 { - text-align: center; +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 12px; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; +} + +dt { + font-weight: bold; +} + +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + padding: 2px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code { + color: #4665A2; +} + +a.codeRef { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.version { + border:1px solid #0000FF; + color: #CCCCCC; + font-family: Arial, Helvetica, sans-serif; + font-size: 9pt; + text-align: center; + width:100px; + -moz-border-radius: 8px; + margin: 5px; +} + +div.footer1 { + background-color: #DFE5F1; + border: 1px solid #AAAAAA; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; + padding: 10px; + margin-top: 15px; +} + + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 10px; + margin-right: 10px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +*/ + +td.tiny { + font-size: 75%; +} + +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; +} + +th.dirtab { + background: #EBEFF6; + font-weight: bold; +} + +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; +} + +hr.footer { + height: 1px; +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; +} + +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; +} + +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; +} + +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.memitem { + padding: 0; + margin-bottom: 10px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; } -CAPTION { - font-weight: bold +.paramname { + color: #602020; + white-space: nowrap; } +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + -/* Link in the top navbar */ -A.qindex {} +/* @end */ -A.qindexRef {} +/* @group Directory (tree) */ -/* Link to any cross-referenced Doxygen element */ -A.el { - text-decoration: none; - font-weight: bold +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; } -A.elRef { - font-weight: bold +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; } -/* Link to any cross-referenced Doxygen element inside a code section - (ex: header) +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} */ -A.code { - text-decoration: none; - font-weight: normal; - color: #4444ee + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ + +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0D000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; } -A.codeRef { - font-weight: normal; - color: #4444ee +dl.todo +{ + border-color: #00C0E0; } -A:hover { - text-decoration: none; - background-color: lightblue +dl.test +{ + border-color: #3030E0; } -DL.el { - margin-left: -1cm +dl.bug +{ + border-color: #C08050; } -/* A code fragment (ex: header) */ -DIV.fragment { - width: 100%; - border: none; - background-color: #CCCCCC +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + background-color: #175783; + border: 1px solid; + height: 80px; + background-repeat: no-repeat; +/* font: 300% arial,sans-serif;*/ + margin: 0px; + padding: 0px; +} + +#projectbrief +{ + font: 120% arial,sans-serif; + margin: 0px; + padding: 0px; } -/* In the alpha list (coumpound index), style of an alphabetical index letter */ -DIV.ah { - background-color: #CCCCCC; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px +#projectnumber +{ + font: 50% arial,sans-serif; + margin: 0px; + padding: 0px; } -/* Method name (+ type) */ -TD.md { - background-color: lightblue; - font-weight: bold; +#titlearea +{ + background: url("head.png"); + background-color: #175783; + border: 1px solid; + height: 80px; + background-repeat: no-repeat; + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; } -/* Method parameter (some of them) */ -TD.mdname1 { - background-color: lightblue; - font-weight: bold; color: #602020; -} - -/* Method parameter (some of them) */ -TD.mdname { - background-color: lightblue; - font-weight: bold; - color: #602020; - width: 600px; -} - -/* Separator between methods group (usually empty, seems not supported by IE) */ -DIV.groupHeader { - margin-left: 16px; - margin-top: 12px; - margin-bottom: 6px; - font-weight: bold -} - -DIV.groupText { - margin-left: 16px; - font-style: italic; - font-size: smaller -} - -BODY { - background: #FFFFFF; -} - -/*div.div-page { - background-color: #FFFFFF; - margin-left: 1em; - margin-right: 1em; - margin-top: 1em; - margin-bottom: 0.1em; - - padding-left: 1em; - padding-right: 1em; - padding-top: 0.5em; - padding-bottom: 0.5em; - - border: 2px solid #0D299A; - border-width: 2px; - border-color: #0D299A; -}*/ - -div.tabs { - text-align: justify; - margin-left : 2px; - margin-right : 2px; - margin-top : 2px; - margin-bottom : 2px - font-weight: bold; - color: #FFFFFF; -} - -DIV.div-footer { - margin-left: 1em; - margin-right: 1em; - margin-bottom: 0.2em; - text-align: right; - font-size: 9pt; -} - -/* In File List, Coumpound List, etc, 1st column of the index */ -TD.indexkey { - background-color: #CCCCCC; - font-weight: bold; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px -} - -/* In File List, Coumpound List, etc, 2nd column of the index */ -TD.indexvalue { - background-color: #CCCCCC; - font-style: italic; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px -} - -span.keyword { color: #008000 } -span.keywordtype { color: #604020 } -span.keywordflow { color: #e08000 } -span.comment { color: #800000 } -span.preprocessor { color: #806020 } -span.stringliteral { color: #002080 } -span.charliteral { color: #008080 } diff --git a/doc/salome/gui/SMESH/static/footer.html b/doc/salome/gui/SMESH/static/footer.html index 9211d570f..4c89a2ba1 100755 --- a/doc/salome/gui/SMESH/static/footer.html +++ b/doc/salome/gui/SMESH/static/footer.html @@ -1,10 +1,12 @@ - - - - - -
                    -
                    Copyright © 2003-2009 CEA, EDF
                    -
                    +
                  5. + + +
                    +
                    + Copyright © 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
                    + Copyright © 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
                    +
                    +
                    + \ No newline at end of file diff --git a/doc/salome/gui/SMESH/static/header.html b/doc/salome/gui/SMESH/static/header.html deleted file mode 100755 index a70a95e30..000000000 --- a/doc/salome/gui/SMESH/static/header.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - $title - - -
                    -
                    -SALOME documentation central -
                    -
                    diff --git a/doc/salome/gui/SMESH/static/header.html.in b/doc/salome/gui/SMESH/static/header.html.in new file mode 100755 index 000000000..4571b4363 --- /dev/null +++ b/doc/salome/gui/SMESH/static/header.html.in @@ -0,0 +1,20 @@ + + + + + +$title + +$treeview +$search +$mathjax + + + + +
                    +
                    Version: @VERSION@
                    + +
                    diff --git a/doc/salome/gui/SMESH/static/header_py.html.in b/doc/salome/gui/SMESH/static/header_py.html.in new file mode 100644 index 000000000..61414bb57 --- /dev/null +++ b/doc/salome/gui/SMESH/static/header_py.html.in @@ -0,0 +1,21 @@ + + + + + +$title + +$treeview +$search +$mathjax + + + + +
                    +
                    Version: @VERSION@
                    + + +
                    diff --git a/doc/salome/tui/Makefile.am b/doc/salome/tui/Makefile.am index b8cd5eee5..987d26a43 100644 --- a/doc/salome/tui/Makefile.am +++ b/doc/salome/tui/Makefile.am @@ -1,39 +1,32 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : Makefile.in # Author : Vasily Rusyaev (Open Cascade NN) # Module : doc # include $(top_srcdir)/adm_local/unix/make_common_starter.am -EXTRA_DIST += images static - -dist_doc_DATA = \ - extra/AddNetgenInSalome2.pdf \ - extra/AddNetgenInSalome2.ps \ - extra/AddNetgenInSalome2.sxw \ - extra/PluginMeshers.html +EXTRA_DIST += input images static/doxygen.css static/footer.html -EXTRA_DIST += extra/PluginMeshers.txt +tuidocdir = $(docdir)/tui/SMESH +tuidoc_DATA = images/head.png images/smeshscreen.png dev_docs: doxyfile echo "Running doxygen in directory: "`pwd`; \ @@ -49,4 +42,13 @@ install-data-local: fi; uninstall-local: - rm -rf $(DESTDIR)$(docdir)/tui/SMESH + @test -d $(DESTDIR)$(tuidocdir) && chmod -R +w $(DESTDIR)$(tuidocdir) ; \ + for filen in `find $(DESTDIR)$(tuidocdir) -mindepth 1 -maxdepth 1` dummy ; do \ + case $${filen} in \ + dummy ) ;; \ + $(DESTDIR)$(tuidocdir)/docutils ) ;; \ + $(DESTDIR)$(tuidocdir)/head.png ) ;; \ + $(DESTDIR)$(tuidocdir)/smeshscreen.png ) ;; \ + * ) echo "removing $${filen}" && rm -rf $${filen} ;; \ + esac ; \ + done diff --git a/doc/salome/tui/doxyfile.in b/doc/salome/tui/doxyfile.in index bd927280e..a4143bfee 100755 --- a/doc/salome/tui/doxyfile.in +++ b/doc/salome/tui/doxyfile.in @@ -1,37 +1,36 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # -# Doxyfile 1.4.6 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- -PROJECT_NAME = "Mesh Module Programming Guide v.@VERSION@" +PROJECT_NAME = "SALOME Mesh Module Developer" PROJECT_NUMBER = OUTPUT_DIRECTORY = SMESH CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English USE_WINDOWS_ENCODING = NO BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = NO +REPEAT_BRIEF = YES ABBREVIATE_BRIEF = ALWAYS_DETAILED_SEC = YES INLINE_INHERITED_MEMB = YES @@ -40,6 +39,7 @@ STRIP_FROM_PATH = @top_srcdir@ @top_builddir@ STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO DETAILS_AT_TOP = NO INHERIT_DOCS = YES @@ -69,7 +69,7 @@ CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES -SORT_MEMBER_DOCS = NO +SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES @@ -91,7 +91,7 @@ WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = +WARN_LOGFILE = log.txt #--------------------------------------------------------------------------- # configuration options related to the input files @@ -99,8 +99,9 @@ WARN_LOGFILE = INPUT = @top_srcdir@/src \ @top_srcdir@/bin \ @top_srcdir@/idl \ - @top_builddir@/bin -FILE_PATTERNS = *.idl *.hxx *.cxx *.h *.c *.hh *.cc @DOXYGEN_PYTHON_EXTENSION@ + @top_builddir@/bin \ + @srcdir@/input +FILE_PATTERNS = *.idl *.hxx *.cxx *.h *.c *.hh *.cc @DOXYGEN_PYTHON_EXTENSION@ *.doc RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO @@ -116,19 +117,19 @@ FILTER_SOURCE_FILES = YES #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO +SOURCE_BROWSER = YES +INLINE_SOURCES = YES STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO +REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES -USE_HTAGS = NO +#USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES -COLS_IN_ALPHA_INDEX = 3 +COLS_IN_ALPHA_INDEX = 2 IGNORE_PREFIX = #--------------------------------------------------------------------------- @@ -137,7 +138,7 @@ IGNORE_PREFIX = GENERATE_HTML = YES HTML_OUTPUT = . HTML_FILE_EXTENSION = .html -HTML_HEADER = @srcdir@/static/myheader.html +HTML_HEADER = @builddir@/static/header.html HTML_FOOTER = @srcdir@/static/footer.html HTML_STYLESHEET = @srcdir@/static/doxygen.css HTML_ALIGN_MEMBERS = YES @@ -145,8 +146,8 @@ GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO -BINARY_TOC = YES -TOC_EXPAND = YES +BINARY_TOC = NO +TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO @@ -212,14 +213,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = NO +SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references @@ -238,26 +239,29 @@ HIDE_UNDOC_RELATIONS = NO HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = NO -GROUP_GRAPHS = NO +GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = NO +INCLUDED_BY_GRAPH = YES CALL_GRAPH = NO GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = jpg +DOT_IMAGE_FORMAT = png +DOT_FONTNAME = Arial DOT_PATH = DOTFILE_DIRS = MAX_DOT_GRAPH_WIDTH = 1024 -MAX_DOT_GRAPH_HEIGHT = 1200 -MAX_DOT_GRAPH_DEPTH = 0 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = NO +GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO + + diff --git a/doc/salome/tui/extra/AddNetgenInSalome2.pdf b/doc/salome/tui/extra/AddNetgenInSalome2.pdf deleted file mode 100644 index d736a6ded9ab54cd3b0cccc654a7869ab7d8ece4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70677 zcmdqJRX`rux&;{AfzP4wZ~!C>t&AN^@R;99_T?az}Lw#!lLoP08dj~s1T}x<}^bF-OyA>vc_G7X;BX(qd0>S6}!a7;x ziNPBEVs3~D6Fj6G1pg1PKB2LnM~-(?ZeiVyf%6EL?JVkNeRuR;?^iUBaMTEW5}y?B zSDvCodZkNZG6w&z zz6Dtdy1uISdMRD*KKbeHYjzF$$p)z(M^b zh``o~lX`|A3Z>F%nt!N?4$%=)NfgPT`%jv(L@gU2%-8#Ltfs*C;C`oR$(so{EYfS5 z$0R?{pt;^V0cG|7;1^pNH7tg&;6&7g;#%?1zippKSD(2vzgi&}Hr`7p0ZgSRc zcjqBgY{Vm7S0IP0V2b^K6#wuZ+@I<&0zMzRK{R-;r(K$MI)GO>pP8+6)OEKuec3Q> zL;*#}2BCQ7QnZ(<0^-)@>aUa(;AK=0Q|R%XnEejL&flK+Hhg=>TRNU~}o;0b1k zys=eEXgU;=kHXW$v5Pl?ebw|tt=T4-96YW8*)Y}VS-C2GzPWA4li-BnsNX}cZB zu63zr9@^nuS&tH>~jl2aJggO-=ZLb72-JmO*6YxNbXHDex>zTfeh8bKd)P z>>oJg-;=8yzk6=91mlblI=jvOLXMoSNlHj7iFNe?E%n9Dk|26Yr}5n+3eB#l691V` z&5&`vckP@F)$gllFQv|06o<^F%vLNG?pvz~Yb~uj%-XeS^l-Pf8?6yeGSiqVduYCM z8TyIFo|?a%0Vl2Bj0>RAU^U_8m;A`_4Oz5{F#saL5tgv2b)~7H%8KlvOY2ClAQiv9 z2C0d<2`Kfrs1=zidK| z?s=Qx+7r~v+e_6}7zL+37h^?NBQ;euMP1*`o=P7XrEiSthtludeT42so?F~2#a6TY zSiLSD49n#+@TzwG=@Gy`B_bsYX}84b_NB};(;CD+ie=ce8a(20w`Ql})caQ| zWDECtn4xmD9lH~F9_r&R7WTu{O;Udvxwhu7*5Y=fadkogPm5ruvXeWr7I~XD&)3o^4iFyCZELQIfn%P4auo zo+EpYZ!bqjl2QPNZYtf!&P1W%*!r71d#voWVFOlU54A#>C@jO>`W=ysH(ys}?}Q`@ zoE}S+mS{v%Sm#&2E!NHxduy~-;_RDo)_&v_zXDiwH;Ay`bg~Q4c@V1U-l0u7Y2Fxy zfN3YO-@J-aE&Vk4W}dF-Dc=_iP%2F}l_H6Kx6e4*uTZyfIHq_WP2sHPb~ z4u2_g8p|FxIEH=1r@*a+%#ctE`qIN67(yJDILjD)Y({MtT<*%0Dp3CHWkd5gMQ0)D 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 diff --git a/doc/salome/tui/extra/AddNetgenInSalome2.ps b/doc/salome/tui/extra/AddNetgenInSalome2.ps deleted file mode 100644 index a81aa9658..000000000 --- a/doc/salome/tui/extra/AddNetgenInSalome2.ps +++ /dev/null @@ -1,13935 +0,0 @@ -%!PS-Adobe-3.0 -%%BoundingBox: 0 0 595 842 -%%Creator: OpenOffice.org 1.0.2 -%%For: nadir -%%CreationDate: Tue Dec 9 10:49:18 2003 -%%Title: Proc?dure de rajout de DATA dans le produit SalomePro -%%LanguageLevel: 2 -%%DocumentData: Clean7Bit -%%Pages: (atend) -%%PageOrder: Ascend -%%EndComments -%%BeginProlog -/ISO1252Encoding [ -/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef -/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef -/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef -/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef -/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle -/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash -/zero /one /two /three /four /five /six /seven -/eight /nine /colon /semicolon /less /equal /greater /question -/at /A /B /C /D /E /F /G -/H /I /J /K /L /M /N /O -/P /Q /R /S /T /U /V /W -/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore -/grave /a /b /c /d /e /f /g -/h /i /j /k /l /m /n /o -/p /q /r /s /t /u /v /w -/x /y /z /braceleft /bar /braceright /asciitilde /unused -/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl -/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused -/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash -/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis -/space /exclamdown /cent /sterling /currency /yen /brokenbar /section -/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron -/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered -/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown -/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla -/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis -/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply -/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls -/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla -/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis -/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide -/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def - -/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne -{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def -currentdict end exch pop definefont pop } def - -/pathdict dup 8 dict def load begin -/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit } -{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1 -add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10 -eq 3 1 roll exch } def -/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0 -get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3 --1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul } -for 256 div exch pop exch { neg } if } def -/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add -1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end -/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def - -systemdict /languagelevel known not { -/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get -exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1 -roll show moveto 0 rmoveto } for pop pop } def -/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0 -rlineto closepath } def -/rectfill { rectangle fill } def -/rectstroke { rectangle stroke } def } if - -/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def -/psp_ascii85filter { currentfile /ASCII85Decode filter } def -/psp_lzwstring { psp_lzwfilter 1024 string readstring } def -/psp_ascii85string { psp_ascii85filter 1024 string readstring } def -/psp_imagedict { -/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def -/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get } -def 7 dict dup -/ImageType 1 put dup -/Width 7 -1 roll put dup -/Height 5 index put dup -/BitsPerComponent 4 index psp_bitspercomponent put dup -/Decode 5 -1 roll psp_decodearray put dup -/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup -/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put -} def -%%EndProlog -%%Page: 0 0 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%BeginFeature: *PageSize A4 -<> setpagedevice -%%EndFeature -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/Times-Bold-iso1252 /Times-Bold ISO1252Encoding psp_definefont -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -253 283 moveto -0 0 0 setrgbcolor -/Times-Bold-iso1252 findfont 67 -67 matrix scale makefont setfont -<50726F63E9647572652064652072616A6F75742065742064652074657374206475206D61696C6C -6575722074E974726168E9647269717565204E657467656E> -show -403 361 moveto -<64616E73206C65206D6F64756C6520534D455348206465206C27656E7669726F6E6E656D656E74 -2053616C6F6D652032> -show -220 439 moveto -<202020202020> -show -220 508 moveto -/Times-Bold-iso1252 findfont 58 -58 matrix scale makefont setfont -<20202020205072E9616C61626C65733A> -show -295 566 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4F6E20737570706F736520717565204E657467656E206120E974E920696E7374616C6CE9206461 -6E73206C6120636F6E66696775726174696F6E2073756976616E7465> -show -1647 566 moveto -<20> -show -1659 566 moveto -<3A> -show -312 683 moveto -<6C732020206E657467656E5F696E7374616C6C6174696F6E5F706174682F696E636C756465> -show -312 739 moveto -<6E676C69622E68> -show -312 795 moveto -<6C73206E657467656E5F696E7374616C6C6174696F6E5F706174682F6C69622F4C494E5558> -show -312 851 moveto -<6C69626373672E61202020206C6962677072696D2E61202020206C69626D6573682E6120202020 -202020202020202020206C69626F7074692E61202020202020202020206C69627669732E61> -show -312 907 moveto -<6C696267656E2E61202020206C69626C612E61202020202020202020206C69626E67696E746572 -666163652E61202020206C696273746C67656F6D2E61> -show -312 963 moveto -<6C73206E657467656E5F696E7374616C6C6174696F6E5F706174682F62696E2F4C494E5558> -show -312 1019 moveto -<6469616C6F672E74636C20202020202020206D656E75737461742E74636C202020206E6768656C -702E74636C202020206E672E74636C2020202020202020202020202020706172616D65746572732E -74636C202020207661726961626C65732E74636C> -show -312 1075 moveto -<64726177696E672E74636C202020206E6720202020202020202020202020202020202020206E67 -69636F6E2E74636C202020206E6776697375616C2E74636C20202020737461727475702E74636C> -show -295 1191 moveto -<6FF9206E657467656E5F696E7374616C6C6174696F6E5F7061746820657374206C612064697265 -63746F7279206427696E7374616C6C6174696F6E206465204E657467656E2E204C65732066696368 -69657273> -show -295 1247 moveto -<6E657467656E5F696E7374616C6C6174696F6E5F706174682F62696E2F4C494E55582F2A2E7463 -6C20736F6E74206C657320666963686965727320646520636F6D6D616E642074636C20706F757220 -70696C6F746572206C65> -show -295 1303 moveto -<6D61696C6C657572204E657467656E20E0207472617665727320736F6E2049484D2E206E657467 -656E5F696E7374616C6C6174696F6E5F706174682F62696E2F4C494E55582F6E6720657374> -show -295 1359 moveto -<6C276578E963757461626C65206465204E657467656E206176656320736F6E2049484D20656D62 -61727175E9652E204C6573206C696272616972696573202A2E612064616E73> -show -295 1415 moveto -<6E657467656E5F696E7374616C6C6174696F6E5F706174682F6C69622F4C494E55582F20646F69 -76656E7420EA74726520636F6D70696CE97320656E20656E6C6576616E74206C276F7074696F6E> -show -295 1471 moveto -<2D444F50454E474C20717569206E27657374207574696C652071756520706F7572206C27484D20 -6465204E657467656E2E204C61206C6962726169726965206C69626E67696E746572666163652E61 -20646F6974> -show -295 1528 moveto -<636F6E74656E6972206C276F626A6574206E676C69622E6F206574206E6520646F697420706173 -20636F6E74656E6972206E676E657764656C6574652E6F2E> -show -295 1640 moveto -<4C612070726F63E96475726520E02061646F707465722065737420746F757420642761626F7264 -20646520636F6D70696C6572204E657467656E2028766F6972206C6520524541444D452E494E5354 -414C4C> -show -295 1696 moveto -<76656E616E742061766563206C6120646973747269627574696F6E293B20636520717569207072 -6F6475697261206C276578E963757461626C65206E6720206C696E6BE92073746174697175656D65 -6E742061766563206C6573> -show -295 1752 moveto -<6C696272616972696573202A2E612E205075697320617072E87320696C20666175647261697420 -6D6F646966696572206C657320646966666572656E7473204D616B6566696C6520706F757220656E -6C65766572206C276F7074696F6E20> -show -295 1808 moveto -<2D444F50454E474C2C202072616A6F75746572206C276F626A6574206E676C69622E6F2C206578 -636C757265206C276F626A657420206E676E657764656C6574652E6F20E0206C61206C6962726169 -726965> -show -295 1864 moveto -<6C69626E67696E746572666163652E6120657420656E66696E207265636F6D70696C6572207365 -756C656D656E74206C6573206C6962726169726965732E> -show -343 1984 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -418 1984 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4F6E206120E0206E6F74726520646973706F736974696F6E20286465206C612070617274206465 -204E616469722920756E652061726368697665204E657467656E2E74677A20636F6E74656E616E74 -206C6573> -show -295 2044 moveto -<2020202020202020202020736F757263657320646520534D45534820717569207772617070656E -74206C657320617070656C732061757820726F7574696E6573206465204E657467656E> -show -1779 2044 moveto -<20> -show -1791 2044 moveto -<706F7572206C65> -show -295 2100 moveto -<20202020202020202020206D61696C6C6575722074E974726168E96472697175653A> -show -294 2201 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<63642053414C4F4D45325F524F4F54> -show -294 2245 moveto -<746172207A787666204E657467656E2E74677A> -show -294 2289 moveto -<2E2F534D4553485F5352432F7372632F4E455447454E2F4D616B6566696C652E696E> -show -294 2333 moveto -<2E2F534D4553485F5352432F7372632F534D4553482F534D4553485F4E455447454E5F33442E63 -7878> -show -294 2377 moveto -<2E2F534D4553485F5352432F7372632F534D4553482F534D4553485F4E455447454E5F33442E68 -7878> -show -294 2421 moveto -<2E2F534D4553485F5352432F7372632F534D4553482F534D4553485F4D6178456C656D656E7456 -6F6C756D652E637878> -show -294 2465 moveto -<2E2F534D4553485F5352432F7372632F534D4553482F534D4553485F4D6178456C656D656E7456 -6F6C756D652E687878> -show -294 2509 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4E455447454E5F3344 -5F692E637878> -show -294 2553 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4E455447454E5F3344 -5F692E687878> -show -294 2597 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4D6178456C656D656E -74566F6C756D655F692E637878> -show -294 2641 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4D6178456C656D656E -74566F6C756D655F692E687878> -show -294 2685 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4C656E67746846726F -6D45646765735F692E637878> -show -294 2729 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F492F534D4553485F4C656E67746846726F -6D45646765735F692E687878> -show -294 2773 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F626F785F7465 -7472612E7079> -show -294 2816 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F626F78325F74 -657472612E7079> -show -294 2860 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F626F78335F74 -657472612E7079> -show -294 2904 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F666978617469 -6F6E5F74657472612E7079> -show -294 2948 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F666978617469 -6F6E5F686578612E7079> -show -294 2992 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F506172746974 -696F6E315F74657472612E7079> -show -294 3036 moveto -<2E2F534D4553485F5352432F7372632F534D4553485F535749472F534D4553485F666C69676874 -5F736B696E2E7079> -show -294 3080 moveto -<2E2F> -show -344 3080 moveto -<534D4553485F5352432F> -show -596 3080 moveto -<61646D5F6C6F63616C2F756E69782F636F6E6669675F66696C65732F636865636B5F4E65746765 -6E2E6D34> -show -296 634 1 457 rectfill -2109 634 1 457 rectfill -296 634 1814 1 rectfill -296 1090 1814 1 rectfill -280 2167 1 924 rectfill -2125 2167 1 924 rectfill -280 2167 1846 1 rectfill -280 3090 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 1 1 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-ReguObli -%!PS-AdobeFont-1.0: NimbusMonL-ReguObli 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular Oblique) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle -12.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-ReguObli def -/PaintType 0 def -/WMode 0 def -/FontBBox {-61 -237 774 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020947 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A208511C6D0C255B9A5BB2FDEDB4D399C6CF1 -94FFAC236883767C0F68F4EF84EE696B677DE704EC3B097384F2E673A1F51692B7B260693738C211 -9F7D90FFDB21EB715FD5B8134FC87DBA320EE54C2CEC6A4D6BB350555EAFF2EC4F84365CCC0802DB -B3BD0E3F0D9F858647DD637725C2CAF9557FDF842A0DA6A0CA0F1B442EF8EE6CBF2B03858468A466 -AC5883CBBD3815B283343B39205803C02C917D06825C09E2BB14609FA32C28D720C0E14A4B12D4F1 -25FF6281FF324DA33A56FC49987AC7D3AA206540F8127273FFE9A3DACFFE2B1C269D3DB9A811578A -C7D532C2EFC18376F473FBB2B32EF642B19CDEC1D6DE83643723E3C6DFC87F97A7007B6081894BBC -45C955B7001EB36211B26AD7A3D07459CFB33F9C54A40A360CB802FD202C8E93D4DB888B325CE246 -D02D1220ABF55CE646DFB45F07CB848406E470362F80CE4C02D98DD845189877732744CC16C7F566 -9F77EF096EA55AFF98AA103EEAEFB971731EBF3782E6AB725D4E9E35B2968689E8007C038CF25B6A -E69451A4731E79AC22BD268F56942A233E52D71873E83E00A1874E04D3B22E72FB2D0671AF81C698 -53C389B51F4A257373AEBF4DE2DA1E4DA5E2CA88941F81EAE0E32D982064C8AFDD7A9A600D56D736 -05B9463C6240606B3361BAF22AF74EF89AC804A5793BD512DA2D13F4BB1B73EFCA1E621ED2A65D66 -5AAD0AD228B3B7E3D90DBDB6061E172B686E92355A7C7459D83199040A368B5697DDC3B81DDAD341 -6FF4405E1096B1240EDC18A0E9985CA55A0D697972BB11E9F1BC30765D6775BB68C69704BE200EEF -4E11B78ADDB6229D8FA49A6B1525ADADF17122C0FFF51A08AA7AED158724AC4352EBB91ED0C157E2 -4281BDC1FD610195F495E87062A8C38E0D046DA4067EE16E81BC5F87E583315B973184E474064482 -9B2A52E0D37E249BAB31988B906F891AC904D1BB8901F0673AECE60ACEDE97B8DB7935C6488ADE8D -FD898027424AA85A11A3DA494498B084133B857017A6D507D70A3421235486EB3CF7613C59139FD4 -DCB92EADC60BB6225D9CD0599779217BDAF4813A453989B2E56903F4DBB83D83DF4837C86BB4C3D3 -CCF98F07A23EBBF7AB5687C3E1E6792E40F92A7A466DE352294064537505EEF3F9C308C9EB94506D -B02CFAE289F10005A6E42D2DCE43731A7AE368564B2983038DAD6987F67062199018395BC0FCAF28 -7A2B040C71F7325FA1E9A9808979B2FEF19096B98B8A0A728EB98F2BA3D33B49E3C20BE992822C7A -1BCCA5B4E4D1099D456D8D7D83C57ECBA0FF21428024F7572A1470317CB8CBC8679A974E13D88C68 -1338C68C9AC9557F97784F4E1C8C2E61F26023ACF46232CBBDF3C0BCC5583B935FE9FA09A562129A -8927AE73988DB0F7E733C6561CA7C9716DCA9B88208A715166F2FAE6D5EFF289A9B2EDCE813403A4 -16F243F1B57EEDE7D81E10C2DA4065A3082BC92A38B2457368EEC9C3C17296CB09819E9E642D7365 -F9A6EF430FC7DD611EA5FDBDEDFA72634AB599EB666A5DC178B0A0BD1FAB042792115EF3B6222C12 -41DCE36CB38B738F68B1B3CB489FED9E53315553F3C5C3BBCE40451E47B7EA53FD3D3ABA6CE0AD22 -5DAEE734BDFA3BF1D81C1B42C6D856A05D0924E03F7627C5EB24D7FBEA3BD85716207F961B56803D -BE046E81ED5FDC378F9CA52C14FD8544CA7C539201BEE06487EBDC30FF3B28E8264EC7FD5DA7E080 -65B0A9147344CE28DA5182335875E9F8B2347A44E33DFAA167232A5C3E69E8C5B58B7C7216537827 -C936F5741B87FC68753EB0D4A466961D0050DB59DF3195BD3379F5647F8CFED35DA952D7CF2DED45 -EB442DBFE992711D22EB228BDDF36B8D7DBA27062D60D2271EA8E8412F4290B58F5BE26FF06F0559 -872F9DE4DEAABA015EAB4904BA1F509F6D517C6E897312DDD571D769BC474FD378AF4360E8B1F103 -AA75F48721B9E0BA589319E15D74AC0B03D730C3EF708C7C504787483F134EA6297097B46D2680FF -8AA50B7A255563C88D594B912F5574564A1371463674793E4834AF11D14C7991E7FDB3A6ABF8529E -1A4F10CAE79C60D37429579093DBD041ECAF03824DF9C007E96F45595A524B27EF8774A83AEEBD3A -7134AB4435C80944DEFF5C1CBA921B0A41B9651968581DA4834B3C0E6D4DE13C1E792FCEED26A72A -DC4D9E3903661D8803DDB58EB2B929CE31FC9F50A694116B00AC9F3EEF53FFDB1ACA3394BF111610 -38F39917B022394C75A0D467D64B89A44E5505DED7D9C6B8BA6BA098F140C9C00E09200EB4828356 -A2D6BE9EC1D5524B09C06D9C6FCB5E2808050A339B5E5FD4DD6C2035A48FE9674520901EDCAD107F -67AC8C8E508E6003011978D77ED225F361BC0F86A98B6120EEAFB73F7377DB1E7213E02D12C330F5 -492511B4DDE08558D75D5B8AA2D56A3111DCCD257EE96E3446EF1C76F000C8916C4CE261425ED9D1 -5B58CED128DAA6C1300466E7B152BCFB5E6FAAB2519B8A98F26B29F98133AF886A0AA7E586A090BD -A1DC6120DBB5640885C609A8BDADEEFE5DE0DA5B75A8A29E92515E86E7E66BB29581E5AFF8CB6551 -D8D1103DF60D558E7987E6F56126A13DB2C9A04886C655064E68A0A20D1B7DE24DAD22BBFEE1B7C3 -C208D4FD6A58DE78D6A0A6126EFDEE3B1A9713DEE94069A9F0A2B392A2F391C4C75327803B53F252 -CC9EF0323F84929BA4716C50385681FF5B4ED54929821594F9026B7C1297941B178C3F8A704CE097 -60533DBC6CF4B18AFBCBAD039ECB2EBDC7838A9410E7B227924BED7123944675A5DBCA388B710F8A -F6048B03DFB713F881EA0F3B191A5CD989EA150B979059C8AADE403855815D8F7980CE6288F47EAA -37C1097D33F13776F08779063C5217D7408D9835AACBE5C071EA40C9AE6DF685F4A9827B828815D8 -F3A672E73A418E5CB15684EB6C6FE0998A386E124D76620446907F993BE16FE5AFCEC681F585601E -18182EDCFD3024062A3082AF97E803C47D32229D0A24596CF7E03F18229FA631175699E2F0D60FC0 -9C4F1954C5D12D03BFB4395F0E5EB6C6877083807D91D93CA4177A6B5A8D2AA500131FCB670E7118 -73F8A3C77575EC93A3ACBA37EA117DB268CF10D04AD0F079484DB124F6DC14A50AD3B0294F7157D0 -837D8F9A6060FBCB385606066401708C041594E0396A0BE4B8B66FEA141CCE4BD29366A986ADB98D -9A6935C49C57F8CD415E93FF8AE0DF75E463E02AAC68DF064C1B789B685F84E15E512404E065A39E -9E8F5568A7D97671AE1602605FC7E4933975189837586FB1A55007FBB0E91382A629277C36A190BC -85AF49EF3F0F38D4ADD2B5DEE09916B79690EC83473C63E92CF617617A66DF472A49641DA10654E3 -AD3880D060B02A4A6C75B51E4E9917A2B6D8EFDA12D59DE5A8E222DC7E82F02F23A9D3DBF637154F -719B14114DBB102BE5EB76B441D7E9990EF6420C2E80942C8AED5A1D0B19BCE115B5929AB9E145F1 -496753DD6B1798324F5EC1D0C7F26FC3045D7BB46A14110C99BA07A45EC16002CB754C0BAE7A1A88 -EB387BB345FA70B0A38AB4D532C2DE49274D4F86F2582728A2CC54B4C09D26C0CDEB8FEE6A42885C -6207D74953CFCC583ED82DD7C0F29D35BDAE5BB251B8A2D4B1DC97E2264DCE035E359DFBADDE84F7 -37EA6A59C23D1A64D963E635769233624F7682EA34636B595CCD064AAFF3887D916867475731BFCB -F7F96D5E5E1FBE6AABF454C2F504EA4E8EB382911560195295C87793D5F7739AD7EC7176E126413C -D4D1058EBD7D6EBEE14BB94A1ECF28B686411D91E07373E891F78C4C0A05D2E8D90A8AE2614F7FC2 -63A762D0F43485473A54C31726F8547701D4A38D20565ED1707847AED9C805780F062B847E668E15 -565CBA07A72B0BA99F03FB57D26FA26FF579C30EED0AAB6FEC1B5DBEA81AA88F16F0C9BE869505BE -18C1CB79657D91D6706E2A3F0BE9920655B93EBBAE2B4D0B5DF6BE622C951F2CFA42AEDBF7AE649E -2150FE87CDBF5C2685EF36051080BF39D864573A45AE2648AD97662B1F69787031B9BC43511FB841 -55ECDC3D91E2475D072BDE6A5207ACEA1E0D2ECB1DA8A1BC4BEEC335A5C7102963E84B97BE741C44 -58ACC3D72A7E53B1F08C955F33EDC3A0DC3E7308270C0F7FF814B111459985733C62E8863625A551 -837952F3CBF32ADCFD9F345E14B585B23ECC440775310654DAF7F41E56FF45F89701292019A94BF3 -0EB2D65E14B1A1D6BF89D4CC43187ADADF3F6E03A90ED01E5D876BD3AA56E5EE84DBAA4DAD9824DE -9984BD45AF96FB8A56C010B3C3A3C6139D58E9D69D9109DB18561B55EAD6452497840B9AE90C749C -155B6329716F0152A7AD52DBD0B8A25B9995E1416681F38FDBDFA443879B5C4C25AA29E0DCC07DE8 -BB161C36D76EF286EC88D57C74BF44DBCB4FEFF771D3BD82C8F4E233357C48E516EFE3DB9E60EF16 -8E2C45B54651DF9A5ACB5F1790F7929BCB16CE5E9F6A43919AD287DBC8E12D9F9E97E5DBAA592879 -1A5A02D39D259F3CE273A870906A643CC18D86E23F115D2A35DE6926053D8C84B940B362E7DB183C -4905060316B269223DAD309EB5AC96DEBA757BEA45FA3100F77F4765334EDF3D659E09BD1A5552DA -492BE9174DD406F8353A059ECFEE3709422940A8C369919EE1F22F7C02412C995FE93DC4559D32A3 -155DD22D3526D89B16D9ADDC30CB7ADA6E52D62C5F2DFD142D4D7B6E066671EBAD08F54917E31704 -1F410CFD8A3243F8B39459C418B7B7C6494551C6F6753A94072D09E0D812351D62916383C6E061F3 -5ED864923002007E626089772D269B298DCA2CC1F25D9BE43FD8AD62D554C16AFEB7EF6E5DDA66D0 -5A810F003CDDCFD2C02FFF02BB61344968091F67D3862C1499409ECCA137B9A2A9BE314995B818AC -CDAE27ED4AD583BE29DDE4E8C2400C5F8152C85709AD2A4737BAC768FEB70CE81A92C9657DDDB2D0 -BCF9169D272A063C75C150ADDFCBC2F5F2503DE3D13231AA8CFB396DB38E80197A605F6BC20EFA1E -DE40CF424CF221218D51BEACE64A3DC88377E4F3EFE43DB4F4FC0803BF61764104CFF0B618C90311 -98B094E20B0FACFB94240B438B67BA298E31D3F4E31FD190E48BFCE27B1BE29D36E765E7D295E96E -DCE09094FAC43B87E294818FDE9363FC7DC5EA36A1497EE25762D02DFA00A9BE53F87ABE62E52ED6 -F59818FDFCA643042EC13D670DED1980413950EE43372D31AE2694B83DDA42E1FBB049F7E7B7E69C -93FFA3195A2462423DD2C022E5141783FFA07E192AEBC5070F08B23AEC9142EED56DA74F93BDB504 -78DA55DDD0A9987FEA131E4CCA0EFC51064E4B37632728261369C3FEDACA100F1AA78FB718ECE7A9 -F56296C5FB43781E63F36B0E1D34BB748EFF35E1953941F94D1A9B0FA474FD68B47183F2AC53A63F -9F1D30B9B89C5FE54C3765B43DB403D57994701C133E42B950D9BB1CA202F15B5E590EE75598FAE4 -3D5CF1546572770BBA9A6373F100CDC61DB4E5EBBE0A93E0E51C86005E333F69110B1C8E492F2BF2 -52CADD5B73E7D3EBB53E759353F1EF3C9B8B39C230D13AB7158A5D92EE4C452F81F6DFC18803280A -A023832FD0DCB482CE5AF615C952BC3F7E58F6417D69775FC7C0D5B405AAC632857736ACF32B2EE0 -F2A2C0F3B3CAD483C614505BE94706322F2A2830FC5AB592907D0291ED1873377E7A6158140C2CDB -1B0E27EEC9CA50176102200992308045CCB5A169B61EA0546778B8D280737319046716604945A21F -2A1CB9E15E3A5DB31E0FB5A3B0AFDFDF6F3424B7536D473F9756CA3694DEE4301FB1AB1AE47128F8 -D2B461C051C1B999DBB010E78DD13AFCBBA6F7D5226D540527F17881A18F551B3EEF76A7E28B4FDD -879381A2217EF2FF9F9982E9EA70AD2003B862D7C36D57C5FF9FBEAAB56040FEE973EFC3B34D8319 -1960010110BA10694C17B7635AE03CC1CD087C0B05522A7A791F0CA34022A3F5860B536D9551BDFD -BF560A07F63AA4E687407E5E48584E689591F1B52671213E430A708C06A34D2E1D51CFA6B328A122 -007C81B5EB263B967746961BCFC8772F8502DD95898724ABF369B0877F3313A167F3F714023C229C -5757D4D46FCD9B4AFECD093DCABE52B78132CE9AB6225C9A344C4BF8D96F2C50C4272CB9AA0D606F -013B2642F8C880E08EA2822C8CF5097D2CDB64932FE195ABD5FDF36D3BE123AEDD8BA2F82A8A628D -BE3ED6129DC0FDC4BE50D5574AE4FECC65062E70F4703BFECB35EADE196294FE173EA57938679DBA -6D15448FF44C0D1A903B202439DA93C0B0E612110068F8079219AA89F435E44D0464F54833BEB338 -670BD820D941DF4B31F51B895BEDF833F9C43CB7616DB80F988CE72FD3C12C7D49F740CF85B4766C -0ED398EB837695D102DEC16E24B7475A0F5DDE88FBF2D6B94F126417C811E8362B9CCC52D8891C13 -C10937AACC228D621D4712CB9DE0BAB60EDE2A97E9292BE04E42E6D3425594DF56931A61E1F96172 -6AF6E6891D63B240E6E79E5BF30C052091D681BA1102409874CFD8EDC3EE2BE331676E31AC00F807 -91D1019BB789CA4F5907F4823B002AF3581448C352BB67D80FDFFCD1C5BEEF60523330AA2C045600 -8F62DEB55E69AC2F86369FAB1ECC90D2487954E61117A90D9269A65DFBDF297EBD29C3DD1F62755F -8F289C42A534F59650685F8576EA2FC5D26B99B8E3DCD3F1FEEC73131000F99AA9868EA9BAC0B56D -AE2CF46DA6CC1D18C0AB8D77BECFF7B89992175CBA2E22779C13DB9DF53FF5B1C8FE95E164997D94 -202C37175E562C8622989B075CDCDE173452C064274354D5DB8F7D5A78D48AD4A103B9E47500D08E -DC7C51C1F3CFA7F43C3686A3C24A7EB5018B0F419961564F87E212CE0A0741AC68D6822C7AB9FD68 -85F5D0B2AC249CB7F50E2353CC4B0A6A24562F564FBBC7090C3FDF1284AB0EC615E0B3FBE132F315 -70C8A65C814F93910AA4BB80D516CB70D2E1D11969238E6F022D628FA2F33A0A15C4EF0CE7F753DF -80A8AD9494885A1B9ADAE6C38AC9DA6FB0A61696AD3A502630252AD7B574C841117D34BD20BD6581 -217D977B35F5D04E02B933E1E84F5C090F6615AF484D63265D28517BA74BEA8876FDA332A84AEA12 -E6CD82B94AE10A778CD3A216ABC08495EF319F06AD6FF8ADD237D911F846A514FDBFAA8A1EC8E0AA -9F80F11F1CE615519A4B044F3D1CF1A17D7F3D2174222A5FFA8B39F20197FF6CAF250B6ADBDBF519 -1C525070C8D38220FB501C223F493D80F498621A02EBCCD6EFE914F16B2A435D60C0A1A453E288A5 -3D818FE1EDCA7D55A26A017F2EE47A816E90D6C3FCDF0035EEA307DFB06D2BCCE43458A67354A4ED -B6E5C57233DE4FBE41ED07EE5EC77A5DFADC4032138DA9E1B74428CAD02A913E40152F8063A774D4 -FDD4070E4B8A6C089F199AF7C529C277E902195DB760D81EC655DFFD1BB283F3C5AA8BB58F2476BC -797B2892E94414ABBE96D4DB93E280CF7DE23EB852E7CA954D6682A6F1A4BE0507884C2A05AC863D -2BA73F3B54668397B6C54DC2F4183130AB414875F3C3D8792BF7E5FC4D228DF87748BF0B14178DB7 -E3FFB7891D700A1E9520D778B095DA80E4801F53442D5C073EDEB706A5DB8466FFE7E701ABA9C364 -A37169F585C883A83713A61C9C3BD9336A667EA4E3DB5F4DF6BC6A552BE8D3EF093639EC67E5FF71 -8959F9902477F5AA894ED2D1CD312ED82EE417D95C49C96671B23FB0E1738E892ADFFE62EC1C3D4C -BEB6CD089C98DE8D247DF7ED17DFA2959D3662F105E8386D75AD308480536959F8E6CF8F2C6937B0 -9F2E8137C811327D6B165ABE46C51834A955FE8306D10033F8C2A34667F13A8BA831CCF52C7A21C1 -3DB92F3E77B55CE291F6190BB1D194A33FD73151C3F61ABD2D8A0C9BDE90E796BD996D2D0094DB2B -E98657E751BDEEFE8A43EE4501B98F0CC6D80805189438872A60047A8CAA9039893530A3E5F6BD75 -BB466B25165737C939AFF3EA59BFF4A7DB09C2A5B36B8A1F0C6C5E5870C7C9412589877EF44F8428 -4B8A53B5B74315CE72D2EAFC631BC4CC2E5B71DC958B5A6350CB5F615C3A4502E973622E3E18193B -69572DEF1D02303A375ED60ABA1BC8A179FAA0F221A49078FE15AE13383585FB45FF4D5F3BB3D0F6 -D8BF62E9BD6BAB3C9A7D38C8A5AB0BE57ACDADCBD02B1DC7952D73AEF702D406F62719922BEA96B8 -FDC9B879708E794891C7A0A42F2CCD6812C3F4DB030B5178E3A627C3E77621D312CE4EBE815CD387 -7208FAD92761A5396B67E835222609F823728B1C987857CFEAAE21F2AD5EA9D841212993508091A4 -A2C268BF1D8DA1C650F6AB93995E7C13A3F84DB55748C626FD09C0DA1E3325CCB0BF091E996245BF -51EB486680162BAE63B6513C74CE83B92359938439921950D713C69324A87BCE67B45A030C9CF10A -DFA0A82781D49FF224AC57A23C6CB321F95915C5E14E41FA852F66E1E2044A9E7B1DC3BE9E818515 -D28B2C4D2F2210098C39557067062BA4239F2AAE28816D999955910298A450741947A9A1AABCBD8A -FF3530626089978C87DFC73618C044731B6DB8007739A9699ABC354A6F985E03C11D750B8B9E9AE0 -5436205FAAD1B895B159E2C90562B82A62EA1A7FFB501767DCE2B11C51D55A17529EF5ADF0A0EE9A -96D0E7E89F68E50EED813836531B4B46E9071E84AA413F4135CC882CE832BF78ECFA7CAB0C9F64EB -92C86DFCD1152BB7D4AB33831AA0C139B555967F6346068D5C3351A7A4368EEBD2933E6B9F789DAF -37EF536FCF965C397AF1B7F98AF864B301F3F440B7ACF704B59540453678FD6C1504519481893812 -3E2F47B265EC4F5CF2172D394543D84CD4281165CBEB11349B315A85DEB2D1699507B0C8C110C726 -62EA2959C4962FF093AA5EE6F21F89B3CCB0149CEFEF1855B9A48D28BB363416C015A1F4EA1975C3 -D8807F616C5817C8162536176F464A198EBEE6C97029F15F414275A39B8219128B8C8542E9483550 -7FC2D3908BB0EC375771280B9EBE87E827811418EF93E52EF70546891BFC0FB34969FD7DEA4CE752 -4D9EEFF2B46BED908C0FB2E02EFC1D1624642EAEA1CAC1EB4841E020532E88E59AC890E6C3F44734 -B99722E9816402D1D0FDF8045C5481EC055100836EBFB48E9FBC392143032C909853C9BA38A19363 -141BED09DAF02FDF4E7CC9808321CD0708A1B45270BFFCC3A0D7C27F7E781713D5DECE82C72ED303 -86B02D14575A1A6447547ECC7FAAC1BDFF332C92984758E242256C054656CDD2C45D46E67AEC6F83 -9F95D74E222A6EAE12EFAAB723A7C816D4E42D4ED2725A794743F67597F3DB8CCDDE45BAABC25726 -B851E02E56341EBE69E4D91F2A233583EC816F18A1DECBDA4AB69320F55E730617360FCFB8AC2D2B -737675B406297F7F8C4BC370CB084C22BFEC5FEF02E9AB290282F7B153F0A4B1AE569F1E52371A43 -46A748DDE09336CAD1F5337FC3D7CF0677091E59480AB15021E023E356B0E1BAC6C6471AD53625C7 -0206C338536F4D0D40733AB217E2297F86B593717C61458B6C93A16027CC886A8CFDC01EF19C34C9 -A608B95A84B6A2E31454BC03C10FA55CDCB7B1EB7DC16AC1E93981A46DECD7E7F00638DCAC568744 -69A2D9B45CBC81398727E4ED3DB5DB31965F358D8179CBF934EE2C4D652C9CC211807F070C80E3A8 -222B4C31FFEC8DFB9EE07A94C973462254BC1B1581903EE6F9AD91524A787129A63FCE048B45BBE6 -855826750C586B6B23B805FEC3E7AAAC079576949A06F422FC2C826BDB78AE96135E9E2C20C2B2EF -F6171D610B2EB8635ACAB7C5C5ED9C9FFC26CD54D2FD4CB9E4294E178CECA1E16CC8E3FC06518BD1 -6F4D63AE2B435753538834CDD9D8AE7DE624006CE688938031336351A6578C304C2E5480A3FCB43A -8BEE4953DABC30558B7790C6E7A6F0F9FFA557C50417407AC6A0DDA1E736F7070BC89455FC293453 -3DB004AA9070734C8C2608A07330E421A0220DAB99F8A77489132F6413ADB9EA637F3B75948050E6 -67276A55BEB09D4153DC126BBDBE0DB9298AC799A943D72AFB769BFA1488D311BEB86A907EC9385A -AE4F77835DFFE4389E3D9ADED1B08BBC2B1ED6084B3D1074A326CCBF38E06BD026919107BD03BD9C -30470DB779508DFE0DC82DFFD2DED749E872EB7EB9DDF509D5319865070DD76846C34E4E43691AF4 -29AA40DB4BF2CDD50B275589987D8081F7C5A0461AA5D1455A660178A94A0BA0DCB69C3CEBF5EE04 -26D6534F6F919D9795AD6A0E1A1F452AF3B4CB2EA54D6011FA809132421D111EFC51174E223AB6A1 -3596411A9723079231B050CEDAE7659CF168C39AEA9C6902C2CD37D25492CEE00096EDD63DC7643B -667FDFDE5B595DC54F0A72C2650E1E46990584C78A5CEF9BFC3C5F88CFB0C49CD6CADD9DBA675177 -D601927D75C6902B55AAED0E9E3CB52A577C887D581B3CE6201A1C77C9546CEE5A13B92963337F17 -070E2BF9F5C5E86B84225863874618AA50F4DE855DE567BF2AB7163944ED43DBD7F4BBC0E1623180 -7C43DCB47B2EB694E6FEDCFBE26194D2D9943A1BFE32AA1E5305F5E341EA021F91532162978DD1B8 -C5295A5E7551E2DEE46DC2347C6B32197AF430AF3BB676A53BCA9BD1EA88678377DC0A9A86E2AB6D -E29E3E261BFD5573C66FB5687BA9C0544D894A759866B066E1DB5C66E60AE071CC3A1C4AE40197CD -E4EC723F7B80137619DEDC99AF57A5497D6E03C1C9E672E74F48F6C213A3CFACF2699CAE72345A51 -C71C1D69348DE5BC5F443EC0EADE1E76A8A33066922CF3869E3C1D26A3B34E540DC08EA4DA2DDE3E -EB17C16790DA4EF1A3A76D71D34B788A87838BF2A5A3DB8176F9C097D2320050A79EA6C4A94926DA -11ABCDCD26DBA09FD33F30AEED977E8B5AD928F3967F607628859429DCB4ECEC7DA3411BE35A0385 -1017B535985632639D378CDCD13B00FE537A49FD9EB6DF1E3AAF5C41EBE35721FA6833C2FE08AA3C -FFC3477E7FCEBF9EF9F4DAE62FF78F319481C3F1E72999C8A493EC6EE295316B58A5CD62FFAB62C8 -96E521B678342F04BCE1613CF7F6778CBF5227BA20504500D743270771953ACBD5C6586432F3FA6C -0987BAD33B88BC6C15D29C4B3CC54A9DD72A2357AA5BAEB2CB057CDCE72DC80CC98C62B16AC50B4C -6A7641379B766CDDF990DBB2FC7F9CDBBA755B6E3DEA438FD6699C30A99A8B3178E6D613AA938120 -835E517431D28114BCA1AB745C11FE6E52ADB82B9D3D53A33BCC49740C93017D9531ECF43831359C -5C93CB0E926DB440B139E3125CC2E069B1CF6D96EF68407F32DB517242C3AE0BC6723E560B0F45FC -7F87A5E44E1751C8B7F9F669C24AD5CF16F84FB03BA121B86B0694234D8F2C9C947269AF96FCA08A -78F736E4E04ACEA44C5BAAFDE360FCD8BA6A59724CA86160A5527FD564468123D302DB45173C1B21 -6B01DC5B6D3415B13FBDBBD3121A5493374B3357EFB131CABFE5087AA1D2C7472B0377066B3632C8 -2073C6A846285CC953A8F28E131CF587B35217EE498D9A1DB57B063CE068DAF55D8CC1771C0C3099 -9CA4FDC5D67BE4E7E69418F6334BC6149000821B89A7437CCDF9A6A0ED702D5968F1E04F7E4FE9FE -C9D1E994885CB624035BBC5426CB8EDF0456828F8EEE75BE491B45FAC192A405EBA25CAA4F4C66C0 -DC234D7B417628DA5276C08260BE512B2432256C401A66E3B583E69D23E9FD278CD5F2178544D054 -16B9B4F61A88A4728AF2CEED07C08E207F31D644E8E3BA1E4E2F9D8E30936BCB9C6AEB54E37DB46B -D64F2ECC1021336D0564DF0F18E5A6B6BA470233D8D41FDD9D1079706EA685B6D8A740570BFB78E3 -984BB155C3155C69BCCCB41CB51975EEA1C1B4294CB546CFB03DC31BF86EC3BCB1977E8F94A771CA -B09DE12A82F1D6C791FA7800E5A21DF81C9C8FCDA78622ABE75B54AEEA747AA4F26D563200992E33 -7231A430137C720A17D44F3AD6CFFE63B2DE12D3184BD3E151F955786B8DDCCCB290C42718F3A219 -1759DF76371C2FC177544A6C425CAB14AAAB31628A9CF9D71B5257AFF0D59843989CF0D747375A26 -DC9ED29B66AC2147DA0168306C48C2484C70CA92F33C0C138F92F276F5EAF5EA3082A8A1CB12DB66 -1633C2F71E3B69918F509060AC949FCD52C36498A2ABB77D139DF1EB33E3B846A7C1BBDCEF5DEECA -4EF0AD250CEA9C2751E13EF7681E8FAE0491CFA6C144DBAC1FC39D39E76EB12D3EE9CA159AA77D27 -94F0C433345B135BA632F544082BBDC9471E9FA3AED3A7D465AB7158E8AC97F68B1FBC8D368E2350 -45C18EFCCADEE98778D894D96301F903283C5AE355A863BB0DC5809158F7E108662D04A5C1234915 -E7BD5B4C30F9EFA55E702E54F87FCA06FB321507BC57A1E55CC117E21AA4E3A4DFB77C1A949EFE36 -6D93F2BD827EF8CC16D387CA82AC039F77FE995BE6D9AEFC87F8D809E90C1017803BCFA1C737DAD5 -F1A631EBE6894AD20C70791665E7BC71F21C2C3F4462F60FDE75C8A377CF49BE99314663C6ECB538 -B1BF021B2F2174D2B22CF6FAD115EB0ECE8A2E64097A5FB0A2AF666E1EE13276AEC59FD0C9D4BFF2 -3F71E835984E5EEEE36490C54E077AD7355DBC98BDD37DF29B3DDF8C55480B7349C4D17322418705 -796A8C521FFF920DD11773FC44FC631C7D6E9B420D7965D7F62EC7385F2BE30A51E2D796483134F8 -40AEC71FA19ED1272C27F98F2CDC9C7E54DAB585AC1703ED08F5F9E825564902EFD08EDF99DFD494 -44C21FA6BE16CB8A1B6D0C8A5ABF80A50BB8D055483176FD0AA07EBAEAD88FD694F96FEBD60751E5 -C4D8F9BC747D4F4030BCDF9B0370B7A5E0A6923FF60DEA16EF47F886F10CCEE6956ECF41A21F7C59 -6F3BC78299A9657266807E01762B2B2878E551914CA312C2A68D34CD91E4F5115EA1FBE801346E14 -AE529049089B6B0273E258785773A9CE8E4B6C4211CB7C2767319576758F811CBAF3A3FFB41B3130 -6C49F3798B698A47BFA2E3CA0251C4D90C0B02ACA28C611744526906791D9E157E54CE4E1BCF5B68 -6990BA8AB7897D624EF00EAB92CBAC255AE9177DA9F0D86447D35B452CD2F337147B5D3EBBF2B952 -35778A72914EB3707EA78294B3A3BC4ACB19FE87C72AA1D982E4B822F07B115CADF4D3E7EE3D1BA7 -08653BEC6F0A352A0C33252ED0630E7274961896D461EE8BF523D5911BAC1C8AC763E5FB11FDD217 -4E1F129675969C195476C7A5E18A81BF9A11ED9F2336D5301E3BD32174ED5C933E8C85D6272EA218 -52A6F7E2AAB174E0965F73E0EF89E906BAFB181DBCF8B1F5AA0C12D12C6272753C016AFEC2EC9F95 -41B8757874D6F2E061ABBE8B29281677246305B3C41E90418426C575BAA216CEE3C5EC29B2FDEE1C -77C14FDF940792F48A56AE80AA33E370B037CB28A7373F882022AF378F26B6006A049FD3B35074A8 -65C97D153352ACC156992C00DE26AD21C982C71F0EDCFEB61593BB40FA5F2CEBF23C4FF34A4F4BDB -73CA273C269242D1C6117262B7C47771F2619FE5710855134A80FA8F92BB2425CF88940CA3450F81 -234ABF2B11775929B12CFF86442B2AA0F4243D324A5983E5D1829775B3C7A111D5622D1C4E2B2A2F -982FC8A95F789881416DCB34950A393F4F1720D2212F3D343A17683060182355DE9E4718506D76C9 -184F8DAC55788D7E603CFAF4907DDE965A49C323DFF425FE88C09AA4A4D16283F9B14AB9EF1BB885 -A954034710B4A9DA4C88A8A0932B18D139A687303EE562EC9F656F12F3E8F27DAA9C75DB0FA946FD -0E1A982BB58E040BFC0A49A4AD8CD668493FCB573C849EC5474049A693CBEBD4D79AC7515047CC34 -7A9A7570C90861F3ECFB57B9F53AB9C0D6B05C8C570A8F3C04D58555A45524C98FF091B8F8A422F2 -E0E9E5A7B7FF69F1CEFC13E42F1CA276BCD584516D266BA6838D5E9CA9E9854F50C7D92CAED61AAC -AF758A7C7BE59C3BAA82BF32B691ACA3E8EB171E08AD22C39FBE586A54E6E4DE2CD86B31138546BB -8DA5834B2C6E4838547A1B67E651964E43988C8036931088904BBB589CA901E7EBBC094C0DA81E09 -1915D9E46828AD8596FD0FCA39FF12A6C27A359337F973809E81B2E9E3D43B3146F2516667E607FF -EB9AC80FC95A7B7D4DED551FEE0F3561C70DB2D69ABA96673E39E3397F1C3F8FE5F48BAB8AD6E0ED -8901F90F6CFF24E80CB5DCAC498506C4D01033E497C1241E413B022227A3264DA68BC3F91B35781F -A2D018475C199F43CBA7D3A0D5697B45321BAD2C394B207136E1E16B41794975E8903EF2B2E1C33F -87CF72C325C11EC0B92FD3890ACDF60B521DA32596763BDFCDCA837ADC6F26F129B23CA32F9CD39B -33E64576970DF3C05B8DCA4BFE2F17E6C5678B84D69494F1DBA9FE0446AE6AFEAA1FF245C07916C7 -B7569E6267C42B459435A1D116CEC665B311E404171774C0ACC8DDE96B0D9167C8CC7D99C4240559 -2D745C4428755500EB4719340D2FC6BC215B67823F69FA949C08B5EC985D7AA87C9AC1F9BCC8994C -6CBCE6027B7D1E0C22A83A5DE61DBA05D4AF6884C95F46BA7F253E0B2337E312916E163CAF9DB2EC -56C5425990FE73EE53E42B3BCCA1CF642F02B0C5ABD529B568E9ADFF865B9DC190240AD78AD226ED -884BED3C285B4CB0E3929E805C67F1318D186504D92085764B70DE6AB5AB6990F181BDA50FC31262 -348D980EC76608CF08176C2502E065AC2D8EA5CF9E2D44E2B70A7DDC7B922047C471DF8A0B2087D1 -106B5BD8A830EC0E53223CE3C96EF56E5541191167860EEA58D696EC357EC55799438C90156BBF2B -13A0D5C9EE93227746654ED73EA5B9CAB61DAC5BC690F89C87FECAF9AD03BD39E438F43B81D39E07 -E0422F94E8B096AB38C88BC2E1A043811D8141C1A35DD3A6DBE41620E83C8ED3A379CD80D4F9BC30 -41BB44B933DACA7C5D4427AE94A176829F24B5968B713431CB8BD9F53080832C6B784CEA9B515687 -F121983EB9D9C9CE8BD4FA3BEC48AFE64E643B7BD86D8383D07521FE5D091392BE124CCC91113604 -3824B686988E7C83AEBF406D2DA88FD952D0FA9327F4AD04C55FEDBFBFA76ECAE8A176C516479AE1 -467125B7EB3C9E7C5B103BC0C470946346DF271F8EE19DF7E3FF7478C35EE059297F4BF21A5C7B95 -993BE6202E897776952A7ED0613A5CACAFA731FFC633CAB62963150E86EDAC796026CE02EB235B9F -7A54E0B0C5281567138A612BAFE409A818C216DA8EAC5EDF9D1E3A1E3514AE50735A111B4D2AA083 -4EC6C11E290D58FF340F82F0E079F1C7B3566F2336EAA45BF72BCF88569988DB5F65D4C1E59B50F3 -41E45A899656A0B522847ED567B49CD5284FE50E5F8652CDAC1C076804F2B2185F6A51ED19DD4941 -2E65A0D2DBC844B75E2DF71B009776D9F97A4C6F786EFFEB87A307FB6B912BB659DC2BCC6D509A9F -BDE87DE8D716040A8551B6CCFB7743978AD992D14D2B85CA052E87326138DB196C24593F8F7ECD6F -486F85D1666B9DE2ACA6C7900044EE369D223524664A2790B773F9EA26E0A4CDFD709942A44298B8 -249506EB9B77BC887DC0EF947DDDC7CB3CFC6B48F060DBF032A11884E6C226D9D447A5A458CBA325 -D57E144C6DC295262763E7BB8FF6A0CA473EB7661C12E0E8E23EA37E8AB3387B9E54686F3E57765D -4067E521BC1AFAE52394227793C737C19208803F2F2DA920B553E2AAF94EB992AB17E31B58C15CC4 -AA8A1B444DF5B3E7CD937CF03E1F7FAC63342731B4589F16939D16E8E497A74CDE5686F529E9495E -1603D74875288CF53271DB9313A4511B104F80B179FCF213558970A002E945281BF3AE51E668DD6D -13D9E85152747F562CA0B75DDEC8FE9FE31F8D05B0F59E802888A7A4F19B29954A31108D2F041367 -DEBD6AA1CAD856BDD1427E9EFE89956FE28D500CDC6A0CB80A76902A08D0BC6705583243F1DD8020 -749B257EDF4803BCAA653F7FD6D8B91690995BA5EA3EE92FCD367C11601C6B8ADCEDCE67B16C596C -5D200693AC5FA15D4CC6CE9DF7A71C8A925E99F5085313D60FAD25C1BBAAD28D4AC2B69062D68F39 -0530A976319A3904CEE44DC9451E441AAB4780425440F8C499B81460B5D3E268974145117ED843B1 -71BB14AA84C3A084A7D8E07B9979260675D5CE6534DC176DDB60DDE90F6A3674F67462EF78195F8D -FF74FB5882B079DEE31FE92816F16CE1A70D07752EA25FAF5000ADF79BBE7D17EB1BD2F9BF6CDBB6 -F078CAF97986442680A8FC4121866F9CE86C385DE34E30D8B9768A0136D9EEF79A4B38EE99CBB9A4 -D32316564C9D56996E2595753EA71BEF684834FD030D38BB100E2332B026B046316A53270A96DAB2 -182E994E91262FB03D1AFFBAD623F1689228409884F91DBA153030870A7BEB2C7EE2DEC51875B137 -33B7929041F8D23A94904BD54DD4BC9B432DD0C78DD81639F46D686FFAD39AAFBD1B6C1A37E248CE -48F23E12464D5379B4AED0D50B5A41577E6ECB75270E9AD3EA7D0FC09DAB271FB18B51DCFC0069F1 -5D72546E6C51049F3425AD005F88FD7F02042DABE9F097F9D6A076B30D8CD777B1EC12BD163FDABA -5972EAA61E3C87E9AC007A052B1A3FFE14D7D43C7A0ADC89B1DD4CB4F9C762A84A6C0701494B2D8C -4E4E1A9245738BE4111805C2F153A20ED9FECF2DCF4C8F7C3BAF84D60454A7403D4F5F81C6404173 -A7BA81BB0CEAECFD493D877465DC5735D43E3102CEC57B8A589182FC65A4704661A9E351FCCBC731 -5A87E62F65D24EEB9CEE979C6E10DBCF5C162ADB926EC8CC9BFFE381F6B8A3AC0A19D1631BEA2938 -731AFC99E8EAA39BC75DDB3A39D01AD8F0BC1838F4D674B9BEE9F6F7BE4D9C8BD97E8D171EFF330C -15B76614A1FFD25B3BE19E4A201BCC850F926ED51616318C965AD2F0E56F9433B1247C6D5B72EDF3 -D408A3E0674A509BF30BE813A5E669D72B978794683CA8B85E3469EACB167C30F7666DB5E081B81E -E99ECFBC1704B9646B1A29E4A4CE5654CA8409ADD60145DFC54225BDB8485E39CC98CBC3F38FD0A7 -97E5DFC2099452A2418C6636BD2D5F6B24345ACFA65F4E7DBD2D0AA0C1776A4920B4466C509BB5BC -7D6627946C4DCB38A27098B7B5BEEDC2B3BA18F927077F71E38644597719652037621BB350BB5369 -DCCC073954026E6438FD8393DDB3630C4473F06D9FB9E422E435566C396B12FDCD5605DFEA232171 -CD8EF298786806E9159B84599C26D4C7D8C3BB064665CDD072E2083190372AA808B2268B3FEC8878 -B6420CA829BCF995DC20E067EE6B8E44D2869D51BA3AEDD1763F7F8D2CFB8EC41E6E9E0129DE5343 -1457960CC51D546B10B8B6CE08A1C2B79FBA448DF9783D815608A16C55E589DCD8EF6B04C66232F4 -7A473973A35618000D79B8173258B7365C9691DDFE47B16EEB08B28F881828B946FB5D6FE10ECC6A -FC4EA1F762E90B3320403382E42AF4885B183AA48DB5E4DFC9A54E0B4FFBF7C26EB17A4F13B4BB93 -12234434FFF05549E7587BA0373ACB3E31418BFAF400D8938FC6466B94273D1735306AB912AAB13E -31DA3541C1733E2A7E4DA5B82767D37F3084AA7A7C488CDCA7ABEF77D19E42B4448ABBD346E9BC28 -8ABC4540C0A1CFD0BF46C5BC7454B25E27E9906A3E6CBF678BFECAD1B19B4E42398A210CD567EC35 -FB115D5C0DF0EEECE593982056B0E1D14C292F70B3E049984F8881C8B477956AD3140B4AA22256DA -AC0D11C4126808B5B9F922BCC5F24A77FF352E2C621A3941AC07A20E550A69C49B1B87D116EE6F2F -970918F0F1A501166AC4423FC212E4EC8039AC7F9C212D864F418CBB92948FBD588228108FAC1AD1 -837070512305C110F0FC3FAFE6E1529C2BD0DDE868A9EBE5137DFDFC5C12A3D08014BF0EE27B1080 -02AAD6B607F5C5C0F1B1EED3C552919C9A2E97204A8127F97B1066607ECFB47BA95EF2B51F007C29 -3B2F6A63041A9C1120D9CFCD5357222E5B02DFC73CF94CF9B5CB00EAF073E9BF253E30E09B50341E -57BF245A746EA31BFFD0B00201C34CF0881BBD1006BC9BA7D420A48E53686B598BEDB3449924EBA5 -8D5DB1B1B01AE2BA281D5758C99EFE38ADCE18F7B182FBD0D0622A6EA497A4E7C00C7D17299A2765 -EFD8DE376C214D01A21819451FC04A0277EC84A151FF93903D61C78AB7886911E36E12526ED855AB -43F6289C1890222602B8EFBF15782B374AC1E580B6E963403D6D15A051DB8558F2E61C0B9476C6DE -5D4861585CF515CE951732F20D32969F39192FBF1690D242AC04D47E0C53D467D0FE4656B9526C0F -7F852348B0437737CB0F29ECF9B54A5E17185236DD0C16349C3496F3ABA569EA20E343F6D771210C -39DC932DC65ECEF94575C6E76902CDF6C8C8361F9C757A2577DA535187FD526699917CFE0AD438C2 -A758727B306BC7979547E68B94E87ED820614BDBC649D469EF6B4E4E3DD2EAEB5F80B22FE576CED2 -56495467C76A75F589460061E03F3A1B065121A5ABE3E2C51148B3DDC9F624C97889AAF7FB84B158 -C015EDA5670746C6359D27B0C2BD65144F2B88A64331816DA904572BE398E015A9924218B3EEF951 -23AABFC3AC8217B7B4F691219A1C9DD0A3EDD5C04E63ACBDE71B423522532561F4B71B7028415C34 -37E346BE728A415596AB749015C1D59BD8328E39A850CB98085B34B57FB52DD1D154F98FEC49B3AE -BFCB1672762E4D2A1ECF02787F59DF1EBF2625C3631BED849B298C6D226BE4E6EA2AB66A287D2BA9 -2A6C9C612A5F849B3CB3C25F17164BE286F6E4F5E7E4C9EB17BC68AA5EF0190B64696A570442E1D9 -BDD1A30E7692524E30E4B4C3DF84481DCEC6E10E7308E65DE9D90099F3FABB3F4F766BB86CC98594 -6D2003E21287761A7386CD8461615B570BDA015F5EFA23D18E83C325EE444EC166A1A32D9818C2A6 -5A092D44156C06D3FD079B92450B8A491CBB3529DDAC7D95AFE8EAF33777FBB265FEB8A4B9AFF2CE -CEFFF49AFBDCF6C4197497D3B448866D70EF28D8E4B17E7CE95F43F64BB48C4A73EB84B26650F62D -3E5199D64DB0B5B87702650ED0B850FD5D16C848D096E4C7E61BC63B2A3ECFC099CD713E12C91A65 -77A88D6F55D348617C7A49890A86EA8FE2045704B5ED529DB128C9B19EE129E5FE6498CC97087F6B -DE96007C9D01CE9CAF75646E5A5B32BFEAD9362A52223D746943A2D09C536CFAF78E601BC2D2F0B7 -63AD722E3A7AE7069D65F9F2BDED7278511D0120F5EA071D41A69F8C2A2D720D3B24B4BE61C83FFB -EFFAE21B0560A6FD1A44E53E42E0D10E0E93F421A8A7E167BB65F0D7F1DDE2809FA3CDFD931CCC69 -B119C83238C1C00EC100D8E7AB1C7FB02EDE97073C8A5860371A8132BE391EB1C397B61F93876FEB -438C288EF2E38DDCD182A5CFBBA994A94A1BF818312CD8234215FCCD7C240A15AC01A885E1179E5D -7D6305DC2F534BAA141F25EA6A5F356486E5FA0AE3C6980A9F5E8E99E7AE5B95AC42977510970245 -4FC951E4319AE4B1DDC9B07D0998372C0A95ABA6985A4DBE6DC633154FAA30ACE689D36A7F17011B -F29CEDC58A6692A8B3B0A5742E6CEC2F69B255BCEDA762DEE72F125EBA98891CFF4D88AAC14188A1 -8D81424979C9079E44890D94EE094D4CADDC1C7AC5F6791FAB8849CC0240A579ABD800EFE3AA4EE2 -F78119A3C2806C05C2B1F17940BE73984982D1C0065433A9BD658EA31AC819DA9A11B87475BB565C -C294B6F302FE3F7752ED9B963C5279B5F1196762D0E12E6DA46FF9A0CADE3876D7DF695D8965CB4B -47B351FA3F759811269376B2C3134403633FDE27C9B024F6BA81F3E1699CF64A426618428BA6C326 -6BF016C5DAA5FA4CC82FB6DC23FF2D742160518CD3A65ADB38E53F1067076CA1625466E0C64670A1 -564A54CE14DC5C57D24A12283FBCBFFD0FD594AC2A56EE58B552F7586825E4FB1EC23F8221711692 -C8C56F42272B87EBFF3865191F1C11943BB76D8C0CFC53ED452AE49404D2C8193ECC2A7BB8CFBF24 -870ABA38D2CCF7869E9363DC0AD94FACAED5922B324DC3B6FE83E7B34FE29ABC1EAD62B49FFBCB81 -1ADBB5148D5AC2743E3A058386036FADAB6FF071BC1C3B8023F908B6FF48DB0AB1C9C67487C35211 -D40995E1892C8B66AD6C9C6203F6F8B513B11117B10DA8725AB45B4437B5A88A96AF3178D856D601 -196E8162868A83DA64E408FDDEBD14D6591881EA652032CF2F88B3FD6C0479C8F89AC68D14D01AF0 -CEAFD95AD146E68FAE01A07F39E7A0C5E4FFA6D6A91D710827CA5ACFE7D1F946A8D7B67621D60F53 -41F32C12A6EFB03AE5AC5373A382C044A276F6B41C173D0AAAAE0C1DE4C3CC71EC2637225CCBFBD4 -5EAB92BF39357C57195B410F74283585B12B926438AC72AFADAAD2D0FA2CCA728C8E86BD3FE75D47 -B8BEB96AB13B5480F7A3D5741EB51E3E40C21FF2ED7D9221D9877C7D1A8CECF394E4023FCF8C4EFD -B38B839499FF5CD96A46AB4FDB46F35D3B48B91757C0159328120E93CF1F2739E936E28908FB1947 -1D3AD7F6F1AD2BD1EC364986A411CC1B547D0CA104FBC10B1CA7B638A60E75485574034561DB345D -DA68415146AAC632DFA34769B6ED7D7D4694E92CBFF4EFB16B55495908102E85E827FC623CF1BBE6 -A13CBF64E878E1A2A159948B5529B75E071744A5F0E50DF18C110B0AF117CE7F33F8C959D4C98CED -5A9D492AE6F56DA57B0F17495DACB130660BCEFB064FD8309D965ABE8D2BE98F6898C1B7A39CBBE3 -E75DA0FFEF6CC3945CE76DA3BE915546FE8A5310130AE0ACAA9AB73C7E041C00533B4BC7724657AA -649B9388B791AAC5EABFCDDDEA2CC67A0FD0AE9BE37DF9AD40636538EE55A83F60E9E026C64FBD8B -220CEB46E67410144A520FCEACA252E8165448F84D8EA083C793AD09B90B3EE83B73FEFC3365C729 -E3C738894B8C01C2F8AEE0CC8B114E1175EFB44CC4C6CEF5C8754B1CC7CEC200AD8BF1189D741CB7 -5BCA4E88BE959E32216AD33F674F49AB20A354CF3969F1611A95D3934E148831AE7C81A7EBE3C524 -4F743E66A82E10D16CC09F8194EA7A596BC5981D833318AB4F7DBF2ABCE543E410B649D18D146F01 -486159683DF61A3F880F9B21EBFAB77E908C6CFC79F89BA5F51114F0BF7C3CCEC7BF0F3B057C3195 -CFBA6908E31E0DF10DF69163C9DA7BABC00E9A580FA7FAC202910615BD479BBF76FB8068630D1EC2 -1CD2926D351E869E16C2CF1E023CF04D4FC61607DAEFEEEDFF5593E6023492F00029E2AE4B4A2C14 -50954EFA2792F32B4934A768F892171245A1E2F034E2B9F39833F1B331A19A386BAACFEC8C929BA6 -B67CD8922BBC9DC005EC3976575D5B0508D0717C6BF11123EA36D8FD37FA77A6F1F5AA84D4AD8D25 -B2C11D1877A6E2F9B74F3B5829FAEFD4F7209CE9785AA6FDE68672554A6F29D8BF03FE108ED90A7F -58690FAC399A8AD3A26899072B832874DDB629581A51B3325CD9EDFD49E890EA8959DB937DAB83C7 -77F2A426B967AF5888C33A3635B78D647AD6BA441E222C958EA58D61945F781D7EF409771B89B202 -42AD7D07C2EF592CBF413C5FC89EC30FC9EBEE4BC63709AE33B65EE3091CECBE610B847E12C556A2 -79C8B114C3E460822D3330ADFD72BD69F54C08A81848C2002A08326CF3B09B1305490D35AEE59179 -08E1604ECE75BBE811A715AE8AF7EA9C371B322D0428EDF4C893FDEA607E70E1B6F6614947326101 -EAEF18E29BE0557D2A92CF1FC1505E8B434BC368CE07CCAABC0774F8A63E1073FBBCEB3F4052462A -A9008A1E53F188C9EAE339FABA74AFD6D60F47282CD9FF721F64BD51787F3C13B5A6C5A5F7861171 -0111F5E0471E206D72520F1DFA465F4A23C71DCF99A04CEEF11B0E3BDFC35B7461A60753D3AC26DC -50A5956C9195A4F5226388E0953DDD03AF128A98F03BDFA0602CBBAA20AB9ECCDF7255962A332E16 -D4380762E498FDA4885C64FF5F9B480DA487C58E78943DF62616E6E2C69EEC8836DFCFA9EBF58938 -A878F3E792E8BD8C5D6DF557A5D82018DBAE1CA9C64BA5AF8E21BE1B6680FC5DB22422220B776E9B -A0BF1ED2B7212F8BF111EC8C8C77B223C05EB5E5F1CFABD2D037F4BA0F9503E2CD83F4519D180476 -63F09E308883F5DA5228F83045FF41214D2273B2FE0A9017D5E0557BC2A198C35D1E7E81F7965444 -5760CBA1D3F05EA4B90658E53FDF0823BDB1501ED51DA75C47395073D8980D1E3504E3F67DB3259E -4EE73A87CFD96F84E221796573958D364A51E635FC55478C9CBF9AEA16B7D8C25F2115CFE4B7F598 -54E24968833BA0D64D1D332A666DFA2A3FD71B05A26BAB7DA382907B13DE0B80871DF184D3622B62 -3D7E09BC32A4F6EA2E6DA450A906EAD36D53FDEC7F83E101FEF32F4FAEC581B000686D86A0D3861C -1E67F18A4C4647F51F978484D9E3100B37BE9D20AE84C085461C1FBF929C669E936659050C2627AC -1B019837BAA75757F5B0A82E8AE9CF2111931A38BFC94744E2FDE3F8710342AC615286E4ACE7F269 -743AA05463AF537D9416230ECCA859D8C99B7C6E70BE7FE11DB698589BE9E11900C8E9582A4EF5EA -94B5F62820C90DBC022A620EC536E06CB8BE7526A789996D0E741AAD980880A33800A6FE92286CCD -02C9CB407EB31FB95D9C9F4AFF38B37087AC582C1F7B64A7C3D2202BDD62E9AEB31BCA85C4CF323F -03DA9D318B91F78FDC0D266630F7444ED068B55C05461C97552366A82C2E743CEC353D51028FDCF5 -403B3B74D379B82EB69C4380ED40239E15A86B2E5C860891E26781CC111FB5705E3B7C7AF1946006 -54B5FA1B5FC54FD0BA43666E7BABD2C91C859F393ED49F7123EDFB648A3D6152F2C17F7E438C0A63 -8968AC06B4FB3F77F64F358AE063820BD33F0213C85C40E4D97ED100EC2DA1C2E1EA258BF107AF67 -5A9D995F60BFA37222B9C2B325C0052BB8537D2B27DD43A129C7E8FF42757B3AC9B447703D382108 -DA520B8B3BB3E8C7295B776B44ED28F863B8E1F81B0BD1DAEE8A171525D09D2620C04DD3219D880C -2ECC79282DD7B1772A9CBBCA706909AE8BC7798E6EC7375189B6CFCE8A875849176E5913B85A18FB -197A33CA4B5B4058603CF1FA79A56856B43D538E9ECE117D99AFA73B57E307364F553644DE01EDB4 -6234EFAC13046B6E047ECC8F63942F20097AD7ACF0A45C0501A95263DE9439A880D6B5C5214D2918 -0A54D7FE9B2E627EF49E189B59FCC78745E878E45B46C0A648955D3EA8C935113D94F92EC963F66C -F3CF3A526BA71CDF3CD4CA69EFAB08B7389E3390716892A4872BD29DC1E0889A42D7FFB4190E9A8D -05D84EB9C5741BE6B02716BC75E0106F5F94BD3778BE985E03860D27E44088C3CB2A059DEBC420DC -E3A8F4087A9548485E616C409AC400DD1C411CE4B6A229D091B253EB68F06E43511EC5AA6ECA4D6E -4818D6AA2068DA1AEFCA377611BFA816B5215182432D5683294D67A7C1FD76C52233087CA44943EC -7280005E93145F5E7AE50100C18364E1B36741E9647C4DC1F68A58EC44095920FDCF05532F603717 -80F78420077EF5C24D63E26040CDDFF8DFD65D871DB943F50CDE84900C1372EF33FD8AB9889C82F9 -4F61A0E6842219A0F39EC7B232CBF802C4A744F33159432E827006C7CA77E480A48A9B0E6A876158 -8A3102E3F98A77BBD62A3A23150FD140D3941773BF7CBBA2338FF37B9EB640558A2313E8824E8E62 -0331568A9B76F4897198A709F9313F4AC40827D8C3A71F2ABFF02BFD57D30D0B14012FB5C39B85AF -540DDA0ADC27A85B31694E8D7B61F9D9B476571022D98F2D768246550A877293F3FF6ED918A498D6 -A600223E1A61890C49ACFB60265867CE9464F9C32C59E94F7641C3873FB4FA6EB237F8ED94579957 -270D6FD640BD9543E683F2372CCD7B60AAD269E03A72C5CDB732B128818D41A6DDD2BC139F7D3911 -F48E1B1D263DD4AE8E4CE1A686F3A00A2CBF48978631CD243566E22E68F8D7397134A3530EA3745E -4F1EACB4D6A5FD84C3011094F37573F7F9902305020C53926716D4780C6B0A257BF711AD94C83F1D -41A02C1C7DD203A3E6E4B14EDA2FDBB36B063A3E074495F626B0EEA146D22AC33457F44F41675967 -6D2A0566EC2B726D2F0540ABF225339F02F406D4E7A62E5233DDF20AE7C86CA0CDD561F33C422654 -BF2DC3685CA91BB9D4B09AC8B15A24A99FF56E2894F11F7BB4728FE8F0F5B799F74F475D2D01F61B -7E9E0E541F7FEB8A557486D7DF2CE50927515D833BCAA1CD9BF7A650BEE9E003A5951C98ED147C4C -52F64F692AB281984EE65A47E44A4A5FA93D6F18D276D3B01C5E5F6135AC6940524CD713DF4077FB -4943E8AC927A68489EA52ACF7A854393CD027EB52EA2DC6234EF034F3DC742D6DB5A67FC21D22B97 -146B9C268BA97C30161CE01EDC69A6A1F05EFB0E06F22644E1A368F0E2C0C6C1C832878E0614B74B -D645F5CB293CFDB7618B837FFF14A1210AA061C8C81867244305B80DAA73CB25A417228E9559E7BD -52C119B0CCDB7C4DCE7E1B9F7E8EBBCB575E5BD213BDD6DB88769DACB05E5870232F0EF82F448559 -187423409EEF756BA6247493BE24CB1879B5DD822E03D0ADEA1EDBDD83D3FC46759C679B921F0616 -F27212903F728AB44C1784E8A7DCED0DF5625A7D3F48A20FCA34008184CECD145CCD98E31B79E174 -CF107E8F35C40C19D86B40BAEE6164353408801EDF75A619FFC5B6FAF3F3A95F64795CC40C1F8963 -4FD8C13852D265FBCEF834C800AB46E3E8167476B23CDD8AFF6E2F997C99A86A9CB30EF8C853154D -0D89EEE9B9CDC1B4F27BDA32432A4173B55CA8D9FB50ACB2D886AD8E5862FFD5DFF224BA13C8B8A5 -4A7F1A9F987FBBDBC5A3C3D762A5BE309D5D926AE5093C40AA47B3B1BD828797CBB9BC9FEC9D19EE -A73D2A39764816113A8EDC6CFA6E605AD578FC8E30ABD600658A49ABCD5AC54655D29C50FDB72070 -169D1B389F114B7C71EF95A80D82AB537AC8C165D47371FC142A51625029A990A577EB1618480D72 -6DA93C98E5C5F24F622A850CDD94BADAEA91D4BC32CD50CE69E9F00E77DEA8EC1D37916398FB7092 -402605359DF08AFE7B99C76C2A7C70383F28A7C000C696F45291BB8F074791798197CAFF1544C76C -EEA8C9E6D76EDCBD92A86DF889481F3BBFF0865442264F0EA40D3CAA69AE467A08003F9C30FF7F2B -77E767580575398462D5B1171DD441D8986F33BC7BDA17D413EBB6B7A32642E33F20B284BF3EDED0 -02352FC66C6F7741A542155F4A159CD778BE56B9492CD95115C1A06189A216CFD2E6725965A13DE9 -73765A05114D9A5A4BE0615AF8BF6A5EAFF84468B849954D15BEAE1CDD57C435788B331905C01421 -B50F20B184506A0BEF746330BC98E9C89AAA8F9D102F158043BEB6A682059A1C8B8CF67B2F3D7AF4 -D8BBE086254CDE53765E3226BA2F95AE8063649F9F94BD9519411DAF8A0287307335668190638806 -E29484A4FFBC1E46B1800E03B162C23B1DC0B4C0DD3C7ABED2F00762972EF06EEB9BCDC7B3F39C70 -BE32789D366F073AC3280C273DFF2979507671B3E1E7685A9A4F0FD3867F96DD675BF05F25ED986A -79249B75F182FD73CDA2A6A66D693E4CC5AFE3402431B2C816DA1486C34BC9DCA4E2D51C868688A7 -787CD10ABB9ACA14B7181369DE89913CD8FAB58FC84519EA2AA14E54B7A8CE474F213E07CF2DE2E8 -88093DEEC937526816B71C96ED75FA9E2EDC0F9E6E84569C12BB8E39AAEDBF546630745553D6084F -F9524FEC6A7264F88CEB7EC3358E923B392474E3A48865564431662988FEA768CE555AB0DA48BD52 -6A84B0CB17B4584066C1640C1023D91F7869EF0C4D701BE121A6E3C832010427490758AED7A2B30D -6028F2215AA44E86D852FDC67DA5CCBA79EEA863BAC9EDC2535B66AB0E54EC4D4411390FDEB8D1FB -C1743F15C3B68DC92A8659E7A892D5E53872EA51EE8CA7EF51103E87C29A2714E907C79DB9CF3744 -1785D2F73A1EE58550111A4D9BCCBEBF2E39CD3B93DCA300FAC3ED1ADD8215301E5766C30C8CF296 -75746C5A77BF1FE3CD75D25CF193DE8D9AF02AF8F7A6E8F84B548058CDD3C6998ED13463FADE7391 -26D83D3CE2C7201F955382832E32C10DCBCCA35835985B9A93F8E3B0208BE6E92428787C47D3808A -0F77B8F1D76E6BF6A17FF81CDB065180E03809D03638307BD7BF5CEDBF64904E918FC805AC905379 -928B816480F6E3BDEE47042CBA98539DA0E113B1A5F23EAF1A3210BD18561985E6436EAB90395DA4 -77C7A6D7888D2377B3FC4169368357D880CE041E1F7C875E956600DB7D9B35D1EE66BE476E9DD806 -4CC02230276829C2C0A098F051502E828A0CC505AFD8C3DF293DA1508AC4D25866BEEE6BBD5A230E -9C2DCDD4F06883936381F476DDCD86CCFE15C2CE3C3243E148CBE603B8513A7CE7A6910A66A90B70 -89E5CCD4368BEFFF2BCF8E918BFE0A1B069AB2A914CA7BB91A0AC3B3C0B060FA1A0316F6135E890E -E549315897C8464496CC6DEA0F7E3AF43FFA4C3281156067582CA255B1D2E80F999A3AC0402BBD17 -01824C3BB524130F5B82A45275807BC2F3A0655EA208F968B297F98C369192C8ACA26BEBA7DC4506 -FBD1305E2EFA4DBE5375281A88EE2D6FC88FC0A755E72934B4B58F6DD3BDAF7171A4A3C776576735 -2492BFA9A7758504750AB7F38754683B70E9E293CB1CD7B23BA62BD7397ABB84D7EDB22EF6C3F58B -3EEAF656E361747ED04020163253D1CF3F905B5E85F83FFF30AB2778CAE43781667C0F65C8FD404D -6B9202A99EA76AF9AE1236631550B66B063847180B6DCA832EA8DC4A6EFDB674B5A26552A7C7D54C -2799C7D4E03C24F661A91103086DE3A90A774A6988347656344CFBA06065AB22476BB09FB68F9928 -C0045F2764AF643CFEF0516D87FDE6DBF93BAE2829B176CB507BB99835E01BAD5E55C2F8798C93FA -35EB3FEF02CFA31D3D21B030547F86D27B9448D68E2B155A65C742BD2999DAA0C3AED64447B9CC67 -F7AF33B63AFAF25F3CF7EF86657FE8F952288CA4B691D369E8F1935CDA44A180A6767560C2ED3F2F -CC38B6BD7991D4170C7C566D690A8A25BE03212A80871108D18CCEFF246623E653107631F29227D6 -4754B2208D19F84E547799E691CA473780DDD56AE620CD953D5133D135E3D51F237078FEEBB73714 -54EE633CFE238AEA63F9999E32850E6C197687A0EC4E5908D2A18C5349627E336AB5E3185B218228 -603A4B1852069F5EE849D571B8387DCE1F8F8E9FE94FADEF128BA83BDD245F8C1C27C11F2ED1A8AB -2D6D601726842CEE744EE7AAC6B6FA16CCAA39DBF5B3B1D47339F31DFA562671A9CF7DDE6915FEF9 -F19B3E068A464DD350A3AD146D1A241673B5112A4A8768F976723E6E184790C0604506C46591BEF2 -106C40789B733331A80740D59ABED39868F80BECC2AA21C400A0BD0CC326D186FFF9EB37680F1EDC -32AC78F9059280D07B5FF2E354FED545129FA5FA8F3D4317FF21E027602FDB2522F049BB545FF4DA -60248130F81F4E348373142F3148DED038AFBA818F26D5B49FC02DE9800D894E9239C88EE0EDE431 -F8083697CB0BE3B497473473E5714717C914A1A926730C249413FEA2615EF72BDB0906933387A892 -370F77EEBF62D26CD583EE643B02E323821379C0DC966407D36AE3CDF646B95DEDC7D7FD0F28E950 -78F12DFC0D6400B327B743C548A0A3517A175A7ED963ED756B1E107AE7087E2446BA702CD4E26E2D -CDC1A8B697108B5B5E81E9F03105F220C72D4AEBC57665887C8C7964089FBE9424120EFDB14D76EE -F8C6F7A30B13E1AE90CB9D93D2E14BDE47F4A1D05ED5B18D32AA39911B92D24C93976ACEB7EF597A -75161923A73B2CC761785493D0EEDC08B5AFE95F3C006B41438A0785C962B070DE2BD096CB63B847 -C87539880AA3D3FC5C345E0992D7BE77C6CFF4948617FDDA784CC55652192B0ED775129C4EA4245A -41BCF3875BE319DA0EE2DAFEFAE920CD2B6C6C2001762F88C0C5C05053025C0349DB17104360FCE1 -5D7F3A8E30ED13155A74FAF91DC77B8AABDD6FBD5A1EAF255DB209D7F2B90822296B5603FB5E2CC9 -5CBC5F7A6044058B8044ADCE73ACFD896177F1F70EAD2F6534DC3AD755AB2BA87126D63CA2E9C441 -DF0965BDDD6BE494E58D6B5057A561D1E31BD38E92CB73C1465AF6B9C001F7229059BCA4104847D1 -639E124E082F7364B56548BF8112D0EB461B316B2449049F6A476D36D6B7C0C1126C08F2E9A1246A -3B5B21E7C8FAC6E23B82E33A7783E4F31F0240E96E69C9444E7D7A928636CFD086475DF1E0A28464 -81387BB2010655B9F81A0744121699B4905AAEDCC84BC5D5AB3674601DBBB651EDE7B5DF05C8A463 -DAB41F79706D285C4F9063997F7AC8CEF35CAD51FBE5F5BB1B3FA6DA2C3ABF2B3E925581349728D6 -DA0D59C1EF6444539742EE9A23A5727F20CF9377F4F84DEA420607015A30FB14632D084A2DD181BB -02FC3A84FC499B318156B675B9CA3CCABD87FDB2497C6705FA70EBA43ADDB6CF961B30E8F6AB9F84 -E1DD8D6DB3314B34B7F7AA3BBE19D5BDC75ECADFD8EAE19E07B387A1FC586F0F30DB695926764B54 -0D89F1D854B0FF86528AD9523CAF56371E29498C11AFB2F4D5202670C834E930103F039D13348824 -16A49BF93B84FD3CF1209EEF7D4994C8302436C0794497461C11F5B8BA152BACBCC08AF8A15F4A4D -F3EFFB7227CA97FC21D2D0356C93390C749CBE9750B821F1A7BCFAE2C8BC6D9A27F844D8AD088320 -79ABF0EAD8ECD4EA72846DFEED021857F33C1ACE4C07BEC90398B629814C498D33BEB375B9A53DA0 -F926FE6E89E70322C72CB2DDBFB16B13EF7A4F50DF783316584C6AC2BD7D9029124933133B2229BF -74A228868AB30EA5C3E87C78C3F0962199480DBCADBEF53BDDE45849DA857A4FD85B96682F1EDEB8 -5384929DEE4AFAF84C51A09F5D572705673D885070303FDB47DC898F874E103A9E7C1E894115DFDD -AD81549C7375D4AEDCCE2E52C13E5130B47F206F7C5AFAF1F9EE83DA8188D70B473269CA280A6A02 -DE85300B93D8A4F6B402FB5DF58F1327470CE11CC63ECEF2EFAA396A6680A6746A20382D9529B58E -7CE684B39AC00F7086BCB47C2230DF0343BED9B9152A61C9826AEF9E00A1452D91305CF05490D4BC -0BADC9C6FCBFA93FAD52C3A80705A1956890497557C0873EBDCF61CCDD2219354A4F5621AB33B119 -32065C1D990A9B68858331EE7875CAC855F98563B14EF9E1060BEA90F195AFFF94728AE935453438 -DAB35123D0E2699475884DDAFC7307A5CC06920F35341728D85965F5BA86F261CFFCB1E29B429F97 -6970D42D10E6AF6C4B792B4384122AEF2448E22A58D3AA007743C71324EA08D06819FED14AC1F22A -4F0BE4787BC8738E1CEF240677571C65804ED3E748D72E89C94B6F310BE748FAEA31EE246859CAF7 -A1EA17CCB5B246C87EAB771E2AC5D378650191081514DDC2C66878E3766CB20DC49F630F2743A7FA -ECBE9DBE9E815A3CB57DADF2BFF5EF2FCE23A56298A30A2E052FEAEFBD698101F9DB992613706693 -CB0EFAF6F60C8BB5E7D0A50B3392B9831EF3A304A846CD4AF431E9F018FCD3A5B16387552D55DAEA -683D36257418AAA0E7BF8A03ED7BAB114D7C15119E6C71C1946BD7903C1C42E115E954619051B853 -BF05AE316E15E619A7DEE498F771E809D9435969C1056402725EF40C0200E083F3EC6E0EC27B8ED3 -8DFE32EA0E5E156AC36C4BB9AC5ED111A11678339703F1B9299345AEB1F251FCEFA11FB3101CC499 -907DC862B4463D5523B9B25C5B69F70AB6B29CFC1DF1ECAB8227EB3ED1F882E90B12080EE003714D -403EC43B7B54491446B6A3DD6EB641EFBFEF060C45E873E7398025B1CB7065441F1753028F6F8C49 -A96801C0D598E098EADC96A21117F817B6FD6E6947642F93E22425A00E8F6B592AD50B317B69C0F9 -4047386A45E5EBC9504FE55451A01EB29DDF9A41D4BAD85FC84CE280971E834F06CEF49C8C20ED2C -EAC889F158CB14A8C070900478804CFF1D1637CC880C81AA287D8382837FFA8F41FF3C9DF2F22CB2 -0044C171E4815D0D0F6C22D19A52114E780CECD71DAF63427782E85E463DCB333789F496340E8CFF -885A9D9A4250118B439C71C6BE51A9338BE29251AA794EDC67DEEC6337FA63CA9B03C1C9F75E733A -4A918646E7BC9792486CB5A4BCC5F84FBABDFE338C3792254A3EEA3D88903C2C47B91E076259DCCC -8BD3DCA90ECCC832C09C45141C6242026BFE309029A562C3EE0FCCDCD40E5CF265ED9C3DE582884E -0E14819DB98B3AF734B1B3276AC41D43384EBE73003D15CE39FFCC04109583390E470F431B4407F9 -8550E138F96C4564B494E5480F47C853BDD237E27301F55E42A3BED18FADA152572B7B465A581DBF -E7DB2619365CF16D71BF8F091862B9FCF04BF8D0859A76F46E7B5712F2757EDCE332D3213B8A30AC -2CE7D7797EEF6F30904906B0805DFA7CA36D32A20D989858497A66CE72491393DD79332003D55C09 -5A5AB5DF761C4BE5C041FA8407263D604E53091F7B6B15496245DBBEE96A63F10FC2978D99E65731 -28689366FE8B0BADA48B50185B861BAD03E3600F22BAD4274F2542B635F6C7944BEFC3BC741BDEF1 -1A8DD659038CB40FEF2E16AD1AE7EBEDB7D9BA15FDCF26355331505A386DD7399FB999535D6061EA -BC61DD76EF3EB457446F29D0BB6EC2FC0AABAC20B27A3C123C27BC27A76336D0A0A6D456DA070367 -4D959A4AFE428E2206A511BFC80039ECD56E75F69786DA0A8084D81A66644DD98B6018681F1D70AD -E09BD9BF3D16D68DD5D0A03AE26DCF1552549E459FE190B310A8776B2C8468C14CA8B1B9A7AF2956 -507A3B705AD75A17A0EEA7FE089273353CECD07BB8563465EC8DECA0EB42F43FE3664EB5F31E1D13 -24185539B28D508BCD065ED576D8814ED3FD637D576F027927162344AFB0255A91FFC616948E4E35 -8867E9FC76A9AFFACAEBFFE110808C1532A2BBB0DBEF3F010E45FFC73F228D28F12E98478B27397D -8F456781ED9E19711DF2E9EECBC3FE61F7493FDF1A59124668A91BE51F122F93DCA4BBD22DEEA339 -E6EDA3D6EBEE03DF958113E1CA49C8398D2C59DA6764882EE3663F62A55AE50A7E91B4FEAD1B11FE -0D50ACCC5D75F1A515F0C53616A500F1491381DFD0E2477E402AB0CF9F67D501A442629C8593ED5D -25A72EDB9746B02F2B0F0759CC9CDCB4C9D8B4519C8C617E569B432F0CF6890372AA879CA7DE46E1 -10D95E230A4F0E52CF65811C54365DF4A3E40D819E2FD379B47DA3233D0DEF0EFBCE04AD8BAA3888 -4F6A69FE5C373E38AE0FD0241480F2BE7CCD18AF85916D2703A049779FE7398FC47D348454CF03F2 -2EB3FECC064606957898B5643464845445C25C0C7D685C8DB042AF5D5882174374ACE90081C68678 -9BCA96AC602EB41D317BD652293EE628951875641661EC86A2C40A42E8F0813A861D41A0F5178E55 -43651CA0E99150462DB5EE0010F00DE6D55B0D7FD7EC5BAEA24ED3E90A7D6A0589761922B91A6A91 -3A7FEDDD3B68254D89ECF767CE8E27F966426A8B4FB1B4085384FD09D63E288405B78A646F44C87E -EE22C8596B13188085479F75F63D3D97A28F9C8306FD207DBFD38DEDF0FFEB7DD80B2A3292DFBF1E -D605ADF1B33E85B010309E3EC058FCD922B1325FEE71EFF2DBBC2E68DB52D513E024C01D47CF657B -B61C9734649A4AB63C0AF4720EC3EFCD82DD3CA6E80BB63BCF1B8DE810A0C6C517C63B76FE68C0B2 -86867BE102424FC31C4937048B6F323D039618586FC21731005D949E7D802A430DF8D2F0CE99F2A2 -376C2953EFC4184355E4D12F422C9E1E25C4DF38DEA334DBC89B540E14C61A7769D77115CE8968FB -76B27D0863CEA2496783114C24D4CC816DA884D953DA3F9B9D3AF8938BC607BF26A071956CA07E6A -5509EA2F5D80E5CBEB98041B197FAC760976EE75B470DC20AA023BA3F63C2876EB281FF5173BB490 -D6815604517AA1B1FA0631401B3C1A04CA103E2CA4ECCD83874D9CFC8ABC134CC0F9141D9AFA5684 -8BF222342016C556C14B3482482DCE5D0B6EF1AB522AA1812BDD8DD3397E05327EC12748FC480842 -9B97202E24E1DE0C7C0D272C046BA73B37D30930C5DE5A47D96955CB0F5DED8F3AD929A8B42D2839 -0458F5910A0F93610F79EDDB27078943DFE17C716D65F96589769349F3B66AB7B8C004CCC59EF688 -1F745EC7129865A76F9C2D029D4660CCFB4D5F9D412BA3372A27CB175E9D65F759575CF14A5899A8 -D31FF039AC02DBD8391C3397428AC0D5717C005200790785354813C8859BE90E0E17914F6CB9C674 -F1E9A9648657B54E5E1F52756C4F982DF74E73F6E4D40718C71D1D0E2420FB7462FEC9E457C0414A -96E475C6BE2C10437096FCA0C942E995A9ADA789AB637B648781D32DFB68E62E91C2CE7E13680F8D -31ECF8C824885FA7618981CD05FB335AA111B409C59EE337DF4E5F9DCC920A5FC0D620DC07F20DAD -63F4FF5E0EE5A2F390AF1C32122BA7780F210229E5A5E3ED97BC1C3CDDDD456E739CA782EDBF4B81 -0552368E9C734B0C78B0B8E3F8B2DD782862B74318871BB1EF087828CC173D7B049811FCF598B8EF -DE4D9BC5447F4848C98029C854F3AE461B9D46DDAD8CE67A521F3C811A81A396CB0F80F3C8D8EC88 -30532FB7F9624F7CAE0F8C6DF875073333DEB28AAA90AAF486AB8C932553CE697B885E71EC8E40C7 -835CD5D59A2C695DB9E51216FF9B77A15B0DA63717FF25B05B939E45CF7FBE490E51E9344213B32E -115C2DE14D76DFD5845088DE645B0E75042A61D82FB1753C445AD0A956A1263E5A096B681D3BC51A -9FF32EBAFFF7ECA8B59D40F0937EEFF38312AE57462C7BF3B1FE24D2BA8DFE84515270E09063CE3C -80DF4935E409F62EB4F54AF16A186D4329972B9BDF15FB08461B688ED49928429226CAD9F67C9D63 -6D1375CBB7B08A5631956B7FE29CC9EFA8D75C9E4919C8C2C54F401D2E0D7BFBA40C50CAE214D210 -C6F3EA5802339F63FC4C1C1995787617F3EC2C806CE44CF8E29F76606CD5836F6E5A2E423CD791BE -CD3F112F25657DFED9366FC4ADF90B685CCE4A5698E5FE16D7542B913FBC01B288DD13F43DB2B1ED -8CCB80159DBDC90A8132125DF8DF547C4851CA609D1F6F4D647741260E845B457937787827A89E37 -CDA06BB191669AC84B8608EAE132D10177F3FC384980F3A6E439B048A38D0D6B9CEF09F3F2D732AA -71BD058169D6D0F8C9D146D9DA046774027559A8B3843F6116B418427E78476AD8F0F81E8A6B1209 -8060FF7DD686503F972D6C42FD6CC29C083AC3D72E3751F21D2E44A572EEC80E81EE44C90FAA7AFA -BCD3ECEB98FD4068F6C3A4DED0E6CEC523C9A0054D1FC2A8D61A4A26F9BC250B8F302416924AB22E -722297888B85B9C12F8DFD2A744CBD143F9B2514C1CBE988D9CB4E77D90B2EFD5C2A528355A35F7C -4AF039C7D1D756305967B847D4ACBB81263D4992C001E2A262B9FEE2D1F5022BE5B15E1D8F1D67BC -52227344EE912C018CB73E5F47CED54FD202627777BB77AACF3EE6B22706FB2FA9062BEE87E22CD2 -802E7706322648DAA0C624EA885430175F746E1F536F9A8E1C610C4A761D07248426DB63C9319A88 -A3FA449C3FB8AC94C6003C745E6BAD717A3B2EA3862D1E08512A98E57772A62F85F1E2FFBA40E2EE -43AEC11203DA9CE5AFBF673436F2DB6AF85BBE89D802F7A9E5FA25A408DB69E51F0577DD26F94CF2 -BA2FC53EDDD6FBEB534AF15F74F66EF8D14E7FF77D8A5D284C8202DD5A6053CEAA606BF925992382 -5EF4EFFAA8D878652A4CAF2EE43ED26BF3590402686C876F86C1AE95046E527617CDD3C429BD4CC3 -F9654D2C76DD4102471FF746FA9FA379B16DF96BFE3836D43FCC0B8E95120C27370049ACA4AC313E -1D50D72D1814F2566B8B29FA9C9C20D0488743722A766436776783B939171FFFA00E04805A8B5821 -4D4F114F7B9C3C17CE7486AEA2BCC895ECDE809502BDE57981318A93F23016F056A421B733C4590E -34AB08BB348DA4A48F19B6BEFAA1DDD2A49A6C440443028333CDD48C85CD698ADAF3FD8676739E44 -400A98B575BE02350576F96CFA54D4184BA47555B8D12374B86D038D085F7FA51FF4BE2FF5981408 -999B48B2FAF305212ED54B2E371F5A0074CF68D1B0E5CD279BBC8BBAEF694A89A6C43F518D01BB4E -8402AADF34E96E9B3FCCAB4CBEA2741D3FD9ADF7AF32388F7771845AF99965A6078F4DA335EFA436 -BE36903E33A743C112C0267309F266DD44FA998C9A139704E400B89DAB952EECFE2AC09C82D9F497 -5371CCC27DA37890EC84123193314D8A7A707C217FFC951A547EE5B6D1B7C8ED85BEBD9D3F4B9B09 -6A78E5F7DF88C931E3F396973974454E59340CA51DBFEA1A00DE084B64630E26C6D6A3593B828814 -E27DB0186BF2A87EEF268AA1B135AC09B52CFE53051CBCC88CEC5657BD47F603C8E1A6249161684F -D9084AC279F57A4F9BBD0A546A87E147B62AC860911969A29B8AA20E3AAAD0079D64E6BF1B0F2CE8 -F0C54C9019207E1B403358253C2FA93A662F63B9380B65C5173C198D86A3D0DC1800D1F5378DA39C -E8523EB62C6AFAD8A0D7AD1629F2CECAD82B8FDE38975303768C7D3A08B91478EDB3C45A8C6B7725 -EA8596A8ED50B8355FB852FB8966479D12E1086223B1E6523A65FBA81DD106FE254F7309718768AB -009FF7714A8C363B09DDA73CD3F81BF9C0CD3B0C806CF3B7BBFAB73E46FACAD2480EEBA97AE68EC9 -4D3D79AA01ECC22067858EFFA9D7B7F997ABD2CE5AAA8781E5499E8580C405681CC63EEA53BB47E5 -5ECC5BA2A7A3C5472DF034B022F455C60FFF971B01583A29E211A87F7163187B190B0C1083D696B5 -86E9438FD8BAA45101A5EDCD1BE5AB9A585511089DDAC8DF1B1FDBE582ABD945E67F99ADC4452988 -A9859E39C90EF794C5C4E62997085B7A16A0D90107D08610BA175AD66377345662DA7DA4D8FEF847 -EE5D57E3AC54B928A0957CC1C944E7FF14658FE4A641CD26C61105C0F136A75950764B69CA17509E -3C19351D456B22C87C55E8DCC4ACD3E150D936333FF36499AD6B02B6403DE0F12901301ECB2EBA10 -324BA72B58206A13B8F37B0AEB12115D0C12879C8EA8A2EB70E85C95434564BA3DFF481C8972587E -FF74EEBBBAB14FB32B8A84B8FC42EBECA65D25E8C32C19CA5962832BF45DFDA4E871508AEC318495 -0D6DBE89019CEA29E40484C36E33D76B756255531ADD1DB24C03B2A64A47BD8FBA3FDCB1F5B96F8E -ECB60D5834AB001A70740498720AFB6EC03445CC35B51F7987109618C6C78CBE3041BEDC69B6FB12 -8142CEC5C8683B558AFE3024EFF7A12D04EF59A72E156DF11D33ABA08A8EEB16259DD9529CD003AD -4EF4137B6FF1654236473DFB93F597331A5E26C7796F528F65C94FE07B3B4F4DD49034FA0CC189DF -CDFF70C2F1C6D3DF30AE103E2AC5CFF20664AB934CE5C19693292071C93BD590383E0A1931E04D1D -DD18071DAFB628F5D7472E457BF81D6064EDFA8DEBFF91701C5038CB30865D6122076A336732DBCD -B0A625548773D0013648A76F07BBDC9C16284D158EC7A105AE37A62279419C3A2F360D0C7A74D6FD -D0E36DCA2A8BD59945A4196598F690878F84C894852C1811AFEA4BE3B9F6A5219E6628C66669DBD8 -FA9A0CFC2DDE7716A356FC4FB271D8A2CDDC8D4684DE447355BC7A287DC56852A638C5777826EB6E -B72FACCC86F80BEDDD0D649A883CFEEF4D74750172A90B5DD8252592FCFE19FFAAD868E99562DAEA -E70514F5DE296EF7B57E6F193737ABB6AA317956584423817E11664A67389197AD9F8F771EA59551 -98C9EE40A0761639E638CE9D890DF468642670235F1373D3AC6B1F43B5777FC0A91A96E095E89BB9 -FD62614DE456CE7AFD6B855112367573FD9FCBBD4A4F9C676E672D62DDD34A9BFE8311B6175A003C -D143C0DF15E4C0B48C735404086E48AEED6B6FA21FD9F40B84215DFF287F0677904E2DDFDA774A40 -19DF45CC877F553E95A1C65DF1D67BC0C60E0BBA4D205C0DA3DA80229FDD71859F65AD04506B308C -2B783839F31CFE4425263224F08C5C7E98A2C9D3DC8EA5AC1920F4E395413262E0836BC019A092A0 -DECA104EB2DF6B63392AE8E2136379140DE5FC98B0B69860FE8E31DAB5C5DF7807D19BEA34AC14E0 -ABC6F6519C51247B104DE7D912C5BF6EF11B48FC6DF84512E9F5FEBB48F72FF1B722BDC3BB2E835B -2E7CC6324BEE84893996B8DC2D4DC2793A4F69C18E63DAF04A7BB5C0A9076E2D5A343E134CC3C89C -4712900656FFC202E1988526D80C7FD9281FE47FBA8AB5D025E63A84051F6B13167BEC15B346212C -BD051AFE7A98BE3A2491F3C469718A58E783ED91F90E274FB4978F8719E92A99A1E8F142EA7E1F2C -46AFF0A2FB50F4D105130CE8EA309B0E480DC8F80D506172B609EA4BB4E2BBAE98D8882814FB273E -690DA990B60A9CDA20A2418246BD10AE67D846A0FA815AC25858145ADDA106A6778A11877FE59A2A -BE300D7DB9BBAB31CB5B960B7E4EF91D4600886D8795DC361CBDDDDE05EBD54B1941F426F7FA8399 -270D2F54C998BE92D146227270A8E89AF90C48BAFC4ECCCA01E6322AFC165743475E752F39BDAEC4 -9297290510FFA264342A0AFE2985F85DEEC66C36EB4A1D46683EE7C591A89B81569A8566AFBCA268 -10DDB0970577A76EC8A066622606B08315DB0F2E6C671F3259C73637D773D1A180AAD66ADADA2A65 -95B5F481E5F59E51CBA876FA06D21E1D674CFAB46A02D267E20234324D0891E7847C13C69BFCEEA3 -AC55F2EAF753726BCEB0DE1EECF42ADA964BF9E475953302C2FCA804B70B779482DC9319B40381E0 -9C0096460AE113C19A2DC9157FA138CF0E7758F71008E71D0F7599744D647B09B16E3C795C56EE5B -D14D8D63E7A512900D67487975EC9CEAEF69572FC3C2342AC5D365E8A4BCF462006B5268ECC15754 -94CAD9A9E7A9E8D9AFFE49AF647C017743EC7CFD5E66F4E4D845A6BBC836849274FBD270CBF263F1 -67DF7E26BA91F21C60F96257C07523AC37A2193010E976965CBD75751E312817C0564E1C5AE0CBA8 -BD12B01122D07020A0852120680985A8AC987BC33BE863EEC52AF13435B6E4048D951F5BCE36526E -07A8661CF2538F69D1F223BC53BF5896437D1BD46F57D9698F642F0E99C7392D8EE47134E34DCE94 -D392949B418D9821E12CAFA8337323E8469DAC24DADC6AAD4A0DADD7FF65694BA3A27964D28D8EB4 -1179458F91CD3F83B8F119BF5E76184DD29CC4C0718CF7945DCECC993A7A78739363136CEC7F2FB4 -95EEA8CEDB3EBF14373A058758C442939D36774435554851E9519B6F09C31EF26B6CD997DAFA11DA -91FA9759F17B7079164C5B47B99CCB7A876FBAB1D0D5D1E1A2683CD6914E6B3B755939CEF1C9168D -30B2738C4349650CF86C90D2542FC9B90F36A494C035A1C86DD716014AA16E6B9EC7AA03B16554BE -C436511DD3097FAB1FD0CD49EDAB96F74E8FD26400FC748CBD9EE1EEAEE24DA30DB6F8734B52818B -3A5E510AA5C14E42060898033E7E36CBA9A64042CF94A74E4B52E37AC027C0DC69BAC4944CCE12E7 -AD81AEDCE642EC34CA23E3FF07B8CD35DFF19F33C8D4DBB56A52534F8A827BE47AD4AEDCAD83B273 -38409FD1101C4DFF3F12D3DF79AD1FCE65B2F419451DD059C88BF066413E23DE27D3621DAC2DCC8F -9F3620DAD0F4B1A6E8C9E6E8ADB552E1EB2C4B2A3B73986AD53ED9ED8911F82F750DF05CD2EBA3E1 -B0DF208A87FB5ED44C3296B803881C1D9776D13350CD29C3F716F0B5A8B8557812024BA70069BE65 -89AA579EADB1F657712DF2570843D7C5FF7F4009D4D232D3547DC8B92ED5C4DB77B76255E661FF8B -163C6F3856DE5651B597EC7C78B84F0C6C1D6EA3A82286F1D3BB45F708D564E139E81F473C705AB2 -56346328DAA64D1EA8645DC10FD449092E0634D9D7344B2AEC3C75F6B6CD8B3F3867FF3CBB0F556B -186EE9A7C26BD2D17C8A773055D9D5013BD2F937D697A770C57BDB36D922CB911CD14E7FA14160BE -19C1A052E297B1A2D682D4BBC9F1D2493BCD7CAD2FA75D904C5F5479179DAF7DC6A4E0D269BACA2C -4F2430B4C8CF1572FBDC750A05DCD5B09FA3A9CD6F2F2A386E2B3D4D8E257BD43A783B38E63BCEE5 -03EA96FF2C373181744A607F0CB8D281D7DB1A6F4076AA3E2C61914BD796EF8A0873F79F964FDE28 -B792BA99A20C3F1F5ED1FD189FB1867C84DCD6AF43D49420C8B1F3DCE7DBAE71DEB17FE45644DB24 -4F44B1011C7C768EBB7254F4DACA64E9BA87AA7CD0F0C4B2228FFB9EBDCF3DDE4DCED39399FFEB34 -8811547D025320A88B480943A339E2CD2FA3605AAAE87939B1D7901465A1879BCB4C5BE1A179E7E3 -71F1BA2E0844F88AFBAE9B78DCCA47AE8AEDF5BD3D458C7D4A7A08ACCBF880D1F1DC69C636628DF1 -EBDC5C42FF88FF8B66351F3F72D703E52F3CE91E4E00759753A599FDD863788E99858498B66B93E5 -083BC3501C39A9BA928B0D763C28826FD237E949EF0BA85CCA9AA20C405DB6D5612DB718F7B4AD31 -D253AE306E4D7CB615C59AE668D347A4E60FFF7B103F8BD0E7CBDB142A763BE88AB40EEF6B8FC200 -458D728930AD0F94FE52ACBF0657C4907CC7942710AB1FD8BD149A9C9DEF6B8DCA7DB9062AA7B1B0 -11ABB5AAE8B77893A023F9EEEED4A20FBC30F922282A7AE2F1ACFF64151013D6B8AC2EAAE58171A1 -0F80BC18C3BBB5DE1E22EBE6033BF83040629023D74CCBAB3F1923CFA4A6735E1DFA8A1B261FBF1C -397E26F3BA9C2629CFDA84DFA3D1087EBB19DDA7E2D76E30DC2E15B8821D5291DA1DFD73940E5560 -A8A6DC91BE0075E3ED8D9E8CAC85AC20768D868CD2DC45DEADCC8B59AABE6EE5B2F891E0D7CBAE82 -0F83479332BF9707486698FE196C72EF72B52F54314329FC498171782BF160E1110A19B8208FC591 -EF0F0DA71AF657B43A7CC649A8488B759F7B69134B4F9DCF79DAEBC1CE52CC8015F324C9D46320F4 -4E1551EDA6D86139DFD1DB814CF38A22A89FABB4F75FB896B00E769820F763486E86668253CC466C -1529A5A924CC337C48448851A381DCEF63A0A302B65203D6571A1DD1FB9DC0C3BD6AEF4891497033 -109CEB5A481BFE442249940EC54096F1D0F2436D9E60495D0ACFF967A741B30467D24AC6B0032213 -18666B951EFD45324987B10BEF4AAA0FF1DF6887377A7F70F555DFB9FF1001C67438A167A00B05D2 -C37065655173A7ED9AE342DFA1497FB1F2FED6098901249A085D31B66DBB6AC25EF16C106B0A6FF3 -47CDF66434DC3F0012DAADE80B942D522CD59AF4C31C1C062157B3D000B9CB86E2AA7B4A5BF31605 -8A0D5A148EAA2C67977FAA0966E4C3454E08DF14C2498AD76E389AF65D2C139A6D8675298C46ACEB -7DBE6904C373C06E5F71399B2EDA0B40AB96E8BE991DDC39F92F1D24797F9EC9F2FAE25669B43754 -E2498E8EA5C44B176C3FB3E8F7A7A1481275A461F2549AFC4CC73E28417BD8C5212C13105EAB967D -AA679AE822B9B75B372A99C7E82D6BD83AA2BA00314DA4AC51B9CAA30D80507505BE24BAD0A87C5D -5D7336EDF60CCA4CEC8201D243C3932F74D171E2409D789AAD0D04A7BB22FB6DC3AB92AE33FFEA89 -7C484D741039F38C317EA396A0FBB9F15A27D87FCBE007558799BAB73212B6E5FAF2080BA074724E -AC87D88166DBC1464CF5D41B99428851FF1D99246944511CF42C3F9248513E9E51593F253D89C604 -388AD7132D6A169E9DD888E020AC1F8BA606F2E1EBB97977E505D8C40853653D8F398F71CC9F8F9C -540C22A1E6195BA578AE7262FC845FCCF77B33F33EEF266489AF8B81A615D6A13464BCA58BEC16C2 -3F31D678F14A938BEC31272DAC3CCB1B2DAE577A26BED852FC59843176A5FCFCFA0AB7FB00D2309D -E55C82CB9049F44FA61F1E313205A76317C4CF529A4456019D970624129681F46A9CD7950B8B5C40 -61853040113C8115319E68B37F88D864C6957DF813B305D09E6A1716B10F26F2EF5C727FC77AABBA -73E12B5AE6416AB19F6563CE14046B715BD4CB2B1E4D315F42D10F74CDEDE82BCDD524A1A5460921 -9084CF1CDABFE72CC8375478B41614BC18A914903596D6FC2F361EE519F875385F4ECB50F7053127 -4EBDEB14A5DBD906A60817246042E3799BB3AC647CDA7244B7998AE4F3BFBE5C767FD2142E48518A -4217599E0EC2CF5E86C8C270FF8B02F949EE001D6A439BCB4BC7D7F7C8167C3AE0A7E59687FB8BF6 -F37BEAA164541B8EAFD92E9D152E3FD0F413C99CCC34FCD8AA455A0B55DEC846A5874B94FC95CFF1 -BB386B2A1E22CD1C3914264B6D5BD1746972857C9235052D77A6C0DD3019F8A307FBEE63A3EF12B0 -39B224108276FFA84021F1AC5B745C54690B3FF587B4B1710AC3533A67BCEFC503ADF1F4B62B2910 -B31965E364EEC9CC437CC40181A7320CD52BE9C546B8F1DC824312216C2FD8232E2BB8D40EE2E314 -54C09772A387F9520E331456C269F51A078E6ABD9FB6A68BFD5F557215B0BBD2227B8959CBD1BD4A -EEAB094DD18E891C61FB00933C0A0D76174D169C0B6445D34C00DC9E06D85EB086C18F3BE27DF734 -EBB9CF078AFF6514438549CBE92A0C0D25EFE4A527D86F158B4E9D8870C7AC5D6C059643A3298079 -CC20398324CA87273B86ED801057D797D91BC3CF2F96C650EE1566CD3CF8656CC577D38B830201BE -718DC9A494268177A5019546EEEDBF101996BE593631654B638C75A6BAA648CD1E7AA9AC1EA60F4C -D604071C89DCCFF8B3E430A57ED6DE11C5837E78956ED991058F3646219BEAE94E4D9381A33D48CA -9B8FF12B54A73FF869D0EEED7E098D80152295E6016CDD809173C57D1F5FCE908A37010AD4C4471A -53451DE9B4363B63437C374C598F548F145D3D288F42531FCF36A9CDF72521F1C0868FCEEEB1857E -A983F6B75CE245D875BEAD1BCB8819E5464518E04717B78BD6E335F0AD77B832AF5682062A1E2AC7 -7CD5EDD5DC372EE456C96D38BF8BF348DAC2B4EBBB2440F2CE97B4B337F2E23247E3E8423BFA9237 -CA6CEB6FB93F960CAD894A96F0371168A33222052DE9B3BE04B022AB95C0C243486E35197721FC55 -311DC55F87BC72D09B6C940CA36E6640AEB66C394A5949A604E7F15DCE3A008BB41B0EEF2840A357 -F348443B4DCE064B4C15E5EC52E448C985FAA1C3D6526270B1CC691009959A7620C9A6202619A19B -E410FF7BD535A8B2640AAA459DFDCB8F2BB35112626497E8A397D4F9E04788322A738DC8907CB643 -15CF63C95809E90D06EF02F72AB04AA61FE02ECCF7E9049FF9F3EF2258A75656178AAAC9F3C2A26C -001341862D526CC14E92A81BD63502F959066E0BCD659CB9B5A45606153DD77039B8C5D5B13565F0 -0D95A41937CF97089F3938E39659A64DC3D6046D0E9EF66544CAF8A206635DF49926A3EEF3FDBC9D -CCEA2886EC855F1821C4B9CE1D02A19A11BBBEF43A7D4D536715548A62802F64AF30BBCBEA8C7E55 -AD56C801D8A569C8183615A78CD393CA42C103F155941E845712C335F4ACFC7807202B92A983111A -ED241BBB8501F15560E8F2157C29752BDCDB274008137277920053D6D7DCDC626A574A82A8A34F1E -77B2FC8CF7C1A7322F22DFCB450259EB450C52B70DF3584A7C54C813DB41E3DD81253A03B02BC252 -346AF0160716355797B6F8210C453DD7E1E756FF08C7E6A5F4F87605E1DFF35A130D79148A57B7AD -12D94A129FE3F055CF974EBA09A2B13DEECA2E02EA818A58B81E8743004646C7746110BC61B86ADF -2D5D8C45A6A5461EB34497FCCD09E711F47BFA742C73F87B257B53F30CB68D151424DC3C210D3E8A -C67C2495A8236EA2D7985A5E1DEAC699D7B700E6D38EEE2E93B191BAA5A8A2C916D206C63FE63427 -AAAFED2B5784276FC21EEFF2D70E47C8540DCCC3E00134642B703795CD3702631AE2A90E063A218B -61E5B89BBCFFF84F567E37A31A9B349717A8CDB9C9377215BA838FF7469BC486B64EF2B6D92519C0 -BF0826E3652903F40E400689F5749DF86FE3DE178E21E20EDF9053081F6510D8F19ACD021CBA481C -484D30EAD3B84ED0190087EE478A17154B243346C3938FDD5340CF6E47B185E64ABDF44F8CBCDB82 -94492B91929BFEB9DA2B033C3ACEE554F0F1A7F8A56DF7C06A3583C1E9C5CA458D40E550FDF3E2F2 -E7BE8312D5FEE98543388EDC8A04CA29F1B82B7AB4ADABBA3F2C331EFF3521B2B92F99C4377AB827 -A989B423750D36ADDD2E286E7F3B694E29B8BC403693C6F7CAB5FE34F1E48C8D41B47831E8C3F5BE -5ED5142E3C44ACF5180CD41FDA149B1F4AED36812E42BC184227F5034220F74F67830255E1CAEC12 -66DEFA358A87D2E3B4B4E7EF30181570D0B2B43072EE0311C2C157D32EE2BEA8EA4251B59F6B61D2 -B4FDEB654DEB67AA3DFF4AD65B727F0D6B7D61523E4B44D99BA5CD33540F340A35DDD466ABEA4E72 -E504FC9BAAE51D231C33A8CE7DC2970DE4C1FB5B096A3D9C641EF77DC9039886831DDD01C4F21E6E -168E38BBDDA5F4308C959C7BBF36A42D042DA6862937EB20D4FA2E5927741A58DA5CBFFD9553BEFF -BD92E6D64871D8B25D9049F4E71970A8FF5557D1DE83DD24286D6C3E4770EE00F9A1A0B0063C9999 -4AEC75E84D6F9C488434D1F3DCFD0A8BEE9ED8257CA97E75E8B1285747184D6D2228EF95D4A0B8DA -252318ABD35C8398FC6568B294D90AB308A7675F9F160140F0A08C88AD0CA1CA2CF85E4D031CFA3B -87635F1398EB7DBC666A259F02DB6741D13E11B230025DD6DD64C438409AF109090058151E4DFB8C -0E9CD65935C4CC063CC6100FDE70896E23E3661C7FC1B8228B26A55903E997F80207EDD8863FA074 -EE4FF23BE585BAF708040C9F8CFDEB42FB8EB71D4CB6D7757E973E4D8C9DDD082712C23F868E1135 -ECD91250BB4335958B07C12FDA75EEB56BE19D1644C1F76A8811C021122619F751CBBFEB1D3DC912 -999017FA163672A1EF754C5CB78962BAAB76EC48461B492FA88F9897170DE857CC8374C8BAE417D4 -C78A56047024731F4A45145F0393A27CAB614A7FF747BBC28E6880D4D01C0A6CF317A1DE5BB5ADFA -4B5FBFE0C57598C79F25AE57BB797A489D51F85A9B9CF8BEA64293F8FCC43B0D5484DF99DBE19152 -692CE756F6FBE8CE5831CF4B8A5AF47524E272C45C62ACBFBDFE7E60B05BB1A1A6AF0E9210012014 -69B3DBB49EC7B23A363FA68417B7118DCEA71D4ACA2E36F88C6DDEFB70205DF3AB7C74CF65CFD01F -F85FAF99F172689737331D4C6CFF7A29029772F487FBF625F17BDAD89B4AC076948277B4ED687840 -301016C2B7AD4C6D02F81E88C75B7A04D724E234E38A38269351582245E361A42C75B8256AFD5624 -B558ADA2190F960A896BBAE7A8C57E76DA10DC29E69BBF3AA86214C001A27B39C1D17C548DA5601E -86A5CF53E7B1896BF003AAE9387ABA9B102EB1E9002DD3754A378F3E49F2C6EECF47EB1BAC2CFCE1 -1AC0C5CB063672D32733563F3E1E891B6073739BC53AAA0043FC45E90E413DFBD4548DD320B681ED -70A7443A233D79E3F038D26975586E5CDD2115AA614727B1F6DD4024B85CCCFC79D10B7B6AFA789D -B37BD0E8C423C1A4A8681B5FF3A9FA1F61A46E46C4B1836D1AA41A89264A7F4B1C259E4B10ECDF37 -5BD26A1F412FE01FBDC03368FCAF48AA0EC28B1BD603A6A0D0DADE66D14C9B7285569230FAB76803 -35BE104305E4B748FA99FA31F23991608DFDD2097DA292551136F255051C9F7EEF3FB7C7FDB4E651 -C3D03A4CA357B587245236F4FF3252563F6BE08EF8A3EC09BE2BF27B9120F7D37801F6999EFB1C8A -D1A08698CC59CEAE2CFCDBF6BD8F94DEC94F7EBF33AF05F52C85760C63950B455510C6AB9398D09A -C288EFA09E8F631A59B03FBBC75BBDAFD675FFACCCF8ADF71E815A4A49F14BF70E42DB0B7347B528 -4E234C24010E2177DBBD57648E398FA6B54571A37BA8C989503594D03C6E60871A7F964599022154 -02BA168B8D1D2685F5CF8645D5E11A1769473027F42564C2966C10C0DEE1EE1B6975852A4870D492 -83A470E623337544A7CDA5C16FE2855BA2A548511FB4D4FF2E3E78D108E4C734F64EE2F12CC9562C -BDF363EFAF5201B673AD00583FF108AFF6B68055A5F299452D176EAAFB92C84F114C8C22A05EAD65 -64A3371420EA9E646308DE97D40705E1638DF08704FC90249CBC0D2D3E884A4562CC27370B1A9738 -9D8EFD237E644A7370B8B38ED1C377F522C75F981D878A5E87101E621DF9D85C7207BBE5A87CCB60 -7F93A2E52F660E05C83A7A6CE6D01AB4B62A1EF8DA47CF97D4BBA0FA8EFFA9C0F61A325A97ADA694 -45F23AB1FE27A66C271639F839203040D44B11ECC6E805FBE88843B34C4FD52D1D3C6C70FFED433F -C04501FC20536ABDFFA429B8DC8192B2D45DD9D646049CBF40719C3D674773F9676F9FCF32817DCB -55402A72C56D74AA4CE4035687C730B6B44A9CC614BCA5A3FD17C170ED949E588EE45E89E18B0766 -2A6327FB9E8475C43E5DA1B0AF07C23774B19C9EF59281F5D884990D6194170D8293A86DB52A0FE1 -7E88DA82209A00A16BD29B8B2F13FD60AA25FCFA9745F57C8216283C1D6EA1C119CB9B8D57C00419 -5210FFBD56395A3EC2D3098ED38F389EFC0324FD0E55EA339B3892568229D8D3E205A821E8219FCB -1A7713FCF3450F8BEF976CA0BECA47376A8CA73DF85B340C67EFE4534D459617996526B5E5D3D19E -17CC5449E5EF2B82B2C4C2131FF8A19FCFE6A186A9840D872D85C40665A7A04E67EE26B8BC9206C3 -5B44C8F8A1AFC3867D96DC6D48BD45063BE25B882E9BC0D0948C18DC870E6925818E1FE17D336217 -F174EB4481F5C0ED37A3BEAFAF4D46F857811B6728BEC461AE6468D87A736572F4FF95B58B04564A -9D3C22754587DF15495A319D822B838461764B73483C1F7CB930EECC6F7424841EE10E4087E95120 -2FE88A391375C96BEC4480328A54740213F741105B12A39F19808F3823507B88115D468C61B212A8 -ABAE7480E39BA52390A1892C7EC50271156B4E8076FC3ADA222695DF372385DA7B117A29E04CD2B8 -0A320F186D61C963FBDAFE9224E537057C49E82E405196AAB621B5FE4011E1782A747EF935ED8BB1 -1BDA39A141CC0BA42D04AE123383BC95A1D03A85A9440010C3B9613064FFECA76197E10919BA5006 -F35837ED9BCD7DE5E6D968AACB6FC91178091FA467EF6FDEB728E17293DC89DDE5A5261FAA95A2B0 -000FC750E7073900D4D88247DA464613ADC2B3903A6132D96AC0E1C564385FFBF6249DEA76BEA2A9 -9160632DD2FC2B99133E9F2F470F72B45D6F18B45020F604B06CD9174BA3805DB60EB9C5E6A9C789 -ACE76AE9C79C1BD34434E95E501BC968633AF93FF4883C6A596776254C0C74993710327086B2886B -02FD3E42A725A03459CB36EE34A094139AF5FCF487D3DFE63FAD20BF0DFB60DEEDA2ACCA3510E963 -189D1256EABD81253F7FF9D11263FDBC1DCFDA3D1EA2E52005CE3C605C993231258A717423F64BFE -EBC34684EFA676358B9B543C2042BEF954829FE3246A879845B30EBACB43D8DD7A20FCFEDF763AD2 -C5D20A798B69E08722DCE6A5762E249ACE3055B650D9E110599EA30DE5C4FE7200D5A8DA9E1FE268 -6350D0DF334877D0B9F6524C552D0B6DFFAE125EC4C18F7547BD51C14288E4ABB7F8A1A00458596C -390AEEE6FA308AC1F788FAE30D7F8928AFC91D4DE6352D20B19D8D8AB122B7378CB379C5BE7E3CE2 -922FE667EA057B5D7B3F0B51C7BF0C85F87AC2F360D82C38964F4DABCC9104B32F0FB8802235E8E8 -D9A5997D392259074C00AF2CE1D2BF7B8E90E2E2AC34185C68A03BAB8B267778292B227245D7FF86 -70786E3F746F86B9D4D17190DB859A0E144B2A61E6AC9254DE5DBAEF20E2E9DB0B2FF654B996E962 -F55E465DD238BD1643CE59DC2B5A58B1E6E4AE2DDC2D74D79AFF3C34E4E593E051FDA236B79CC0DB -268D2A89B1878051223BB8F33FF99BA87A4811C0B3BCC01171D0A731EB732ECD8749D27952C27886 -B252F9C3D190419FD2900987A0A255B9753FB7AA70C37462134C467A2C4B7920BED9F9E86F8F98B9 -6D00AF8B05A4BD5F14C2A0D914A9A84160D554FD0718F50ECB5DF5E76623065852DAA74C9AD6DA07 -A119DF12C3577FE276AE551D48B1C5CD8A50E84DEC9CB0840520D78FA7F9A7C2071E28CD20EC7649 -B991F3818CDE295CDB6085F24FCF93147E9F4DD084FBD32525326D2EA147ECD5B6C9D9F4A7166663 -AD18BF234E9CB92FF72138A8A49E73E527E9A6488A4CA808AECABC94D693CD2C0C357D285F65006F -A2F9197F61FBCA6EF07B013E2B558AB531D2FD270CEE7FA8E467FAB885E90C5884843AA08E2BBFEA -0AA575643727BA18ACC499FF34E3438645BE2AA71EA491E54687CD305E12BBC94FAEC848311AE816 -495B013BC5075A2D2AE54A7AD7C9105B64356CB51F18C2C28E3A83B9D81A4554DBEC9BEA9A660CF7 -E1BA89E6D4DFB3EEC6A3DE3FCDED9B2D614156EDAE8CFDAD5FF0EFEE31DA3E6A54D94CE9453A1CAA -D9756D91BE85315F6514BAFBC821EE810BB5D8E1B8F05F64F3F72C4B35D424F7E4DC3AB581B74ADE -B6D6297CDE7AA8278909F269FED79B7DFD39B1C0338E01D556C4DB9CA3A8578ACE3EC3D743ED4B9C -0145E4525E8C315F7A1B98584B975C70F0D415708C8CCC13F848B1D36AC8249B73638F95DE0CD27C -7EFB52BED4339EBDA4812564D7A77416DDF4CC88CFB52D07A252D89353C6826CA1832A153242979B -6CEE783ABDE65C8B40CF4EA7B42B8DBCC0E02423DD693108006F6A4AEBF053B666C3CB63D1861F86 -EAACD43BB9BB6F2C3A17293C189331D253B447757EE7CBF4518BABB73A1D44874D7F0625E6A013C6 -08E991B4AD17A9ADB36740D25E3E35B459B422F7370B134CDFFF3F3BCC4C32B4E9EBF6A2478013F6 -6933A1FA9403A2F1161EC632F1F04EDF95ED0F33DAD9665D54DD9DB2564E51DA7B65978CAB50D6DC -1568976E83B056EB0E3A6758518B6E17E9EBFE49B72EB148B472BA144BDC2AC95744C9BF1258F0A2 -E47470AB0EFF90E190A41108914AB8C1ED6B11E0681778521870E80C16AF2AFC723CAD8719ADB62D -3939D3BC8CC1D8A4E07E9D734F54ECA33D936D2C39D5C8055739C33E53359BD40E576C11E93B4B4C -122BDBC9B1BBF44243AF4F0BCDBDFADE68C526B5CD74E29CE3F70D62BA83C489034111FE8E4DAEA2 -F01F9D938ABB532DEEAC0E329F42453FF5C15DEC2AEA8C198323C9E8FEA55B3F5DC4751D2E2E16B6 -154E7F2ADD46860E9CA71DC114C99D80E7EA1DAB51E925DE161CEDD678EE6282AFF38E3CD0E65954 -9C970613209955A3F581E1ABE485E56402A3DB0D1E9B8A9DFD05C4B0B7F97FC6D0EED0B69AD6F182 -B1D028ADD2F24463834B13F5C1307F91D363891824E81108E57CFD5211F86400D3E96B107F3B1FE8 -9C4908649D04A46DC3CEE0DE66AF03A7FF9F4DAFECDD6DF4D93784CC899B527784DBE0718050FCE1 -85BDE3F39DEBCDD660B2488D23AB1CFF87B0546D02B48E7B7724C9E87B71BF34B5D6640E0F6ECE47 -B182D41C89461F712849C6CFDB7E3F5EBC1ACDD12D65A422BA362A8FD6CAAC5104CCC5AB5FC04A46 -E4309ACAC83D659DDDA256CCDDD1BFF9AB3622450C4FBC89C82214F00C42FB0311BCB1B722A691ED -839CAF9024FB1671F18E4639C96D84718C663A4341DEC037175C6BBD288BBF5A0478298CA726567A -9B74C32A527339C666A294A17F6821CBF243D13EA4B1603C292953308B566653423E7301A032E5D5 -E2B93F1C1434893633DD19501AD12728B5A1D9D36635B589FA2E151140B543D7C5E469AFAE8E80C4 -FC1D9CB6C3823CC1BB7EE40AECB58CBC1465792226B19E0FE79235115F6A3AFE19F98C5DB63D372D -D7C041CD940F4F79F2474D9CEEA0334FA04A97DC9773064895CF11CF73F11B4684F06E48F4469F6A -1AEB2CBBC52994DFAB3319DCE3A0C8C2EFA9627496F8CC84D3DF3BDC4FFCB61672780F294F453278 -AEB9262E66486856D37B7647141A82E049364ED3D03F925284A3F1FA3DDF4C0B48B3FE22E7DF9ABA -239D33CD300FFA8FD4B96192BD568FB18D325CAA8E1F1FD4B27527417B034841FD49E4A77F217062 -3CC8B22101166D80361EB15FA9020D24F61007B0A8274DF9DFCD8E97C85568E76D34AD5DB1779B02 -F034A69CCF9D4EBAA188EB3017EEF5B22A0A552696A574907F695098BD8A4849D5C8311F129447CD -7A3CF88B8191AEC0AFF30A38A9AB8135608A7829207A7D242F6E1FA7DDA19F5E4C28560D42DB4405 -77CC0C5F5803EEE897103ECA0BD944E320AC26553BEE7852EAA733BD13DF760056B2F5BD1243BEDA -BC3C1EA0531017D74B47E18F801A60074D6DF849FD0532234545E5B5E112D1E7385341D39A89551C -80DC2DEAED5D5DA2A4BE5015D297324E92BE64C68428132E6EC654DD4BDCC6640C68835FF8A05E09 -9604B8CD43D3AF2B2FE10C8AFEDEC5A70AF8509D12F662338CBF166D9452CD36331758AC4F4CBD7E -DD52139AD27DC52569877FE709F297444C4F31899D1945C81B14ABDECBF31DC463A4148F04EC4FB9 -703C158216C0FBE65CCD450043ABFD4E65BF8B28CC148252E9F3E797EA0B57B8721C94CBC2EA602D -F2C57E87938C887A382D2659226463BC7D6A1DA87F4A341A59BEA458177D3F18D1213539DC0E301F -6EFE0111FCF6921368BE17CCBB7428127E0C059C2C5ADB2A3F0197F0CEAB77FF7F3C027A8EC3EE76 -CF5C986EB47CB60561C773B3A2DA47B5A35394E29373DBD5C3FF4C9213A89AED77CC4F3FCFC49EF6 -EC7557C521979A546983C106B3627B5FD2D71CC5F08A32BF49332A89C5DA71AFBFB94C949A91220A -B1F885C981423AF93F73BC1CA4D92D9DBAE3EFE6A76E2DE3D0F74FD3255820636E3F1A6B7C185306 -23C12AF90CDCD2C0A728521E9B639EB6345D1DE8FFFC3B19C72E7A93823DFE3115E9E7BBBEB28CB7 -3DB121AED8920D47D8CC08EA2E472E39A4CAD5881B5C4204F2B732AF9D5189D25ABF413CC78714CB -01B1D8CA5565169A919DC481F6D2E67F1D490AEBC5CC62A8F62C1A323EBB55ED35AA5C8D6F8B970E -93205C2701CF4817BDA994FC16197B469ECC5F5E9DDF0FA05640C2E571849571CBD26402B1EB1E80 -3FCF423345007B9B52B13E3B034E8CB3984B925EBFFE719ED4F39F3D0E3343316A6FDC26BDBEA88C -4366D3B2F851D2B244CC4408251AE2C77348CCE9DD8BB9C89800B572D38C5D1CC34C740BEEBB5DDB -0A8BB251655FB989840D23205D16311A9FCCF7C85F6DFFEA9704492A4E7A8F6C0BDC29745AAC2ABF -AEBA02B0E7AEFEB92BA63AB0DF844EB09D505C3DFC1058CE42CDD8043B76398401E1DB862FF9F76C -05E8BC6260A4443CF494BC1755913D51745BF45ADF2F8C7A9546D7EF4FB11E9D94E4539632C2A396 -06D04480EE459408D7A2A869807A4C01881C1BB21C296A402B5E6E07093D833C3DFF075F4DD426EB -87B1B8DE16C146DE79F52F5943015331EEB852809CBB8E1D6460AC4D176FE96F8D19F6CCB22ABBBA -A27C4497D91312C3CFB5BB913B314E43D2EC6AB6897BA7C34CF2CAA6DB4BD69EB5DFCEE0AA917D69 -50E36A68A4C22A60DCC69379D47544A58D640EB10DFE120FCA843B588CA8B94F7869F97609A6FE03 -AC86EC1F7CEAD2EC8E81977D1B946E459DFCFEFE65A7BFF67E66F5F78A45D8DF65AF0146DF74E024 -FC042328886CC1DD7779F49CDBB750345CF83CD678A6A8897577299DEB38AD665DC4F21CE1892A18 -C256F318107DD3E9245C1AD3BC93CEF7B7BF057E33EC9A3F953251261AA3D1A8347261E70A46F777 -3A84F3D4D1A0DF6DD22A96429349DE0D180310E17955B10FBF53220EF6483D03C650A8D5C16D63DA -F65C21ADCD6C2D0B5D4ADEB2F5526AACF7CF42F9A8BF4832FB2D4F73F3D5FFD984B572232F87BD3E -59133ED3D2FA19F7856AD812515C74F7D851574019C532C25F8E163E595FC9C83E3E820C3CBF690D -A62578A980FC0803EB6DB9B1E90E3256BD4650816ABE5EA86CE65C2EB418D0ADDA5F3EA04E17AA8C -4536CC471AC20236E66ECA3619F161DFEFA485386C30EBB86A7AD930FD0AADF2DA69DCAF26C0F677 -206E2030E3B15B3662C0AD03DBC1636EBFAD1F2F2C37F5FA9856B0198C5B1D80B69C5EFFD94CE071 -5135C649C26B9BA1266B0A5B270CD08A706166C0B320915C87B27DE21DEB5D7E4806F6E700B7A06A -4E29B629CB40761983E9CA8E34E869ABD04DDA190BFE5A6EE8B22D7E511B84EA584A84211F27AF89 -18DC5AF8A1FF2D360B6BE3CA8E66BA4CD2CE6A25E7E89406684DA83FFBCCCCBFD0844FE3BECD7DE6 -7764C59C022DB1168D585FE25073FE00E30218D1DFE115CA1FC606AFCB04F2A082EF91788B6BD096 -84DEA31F20034A91AB9D971366F97B5009FEFBF1EF0AD941654081B1E8F0B2EA495069A1DDF11DC5 -6857D29533DC857958B49D1A0779732819FD22E437084BD9F3C4F2CDA4D12CA14431937AB63A03F9 -C040AF1D801F367ABDCA7302E18A9050D6026FBA5A5A7FAD44E31593173CDF277CD737D1CEF59FE9 -684252BC0DDD00A80E023B88222494C1C8C0884230AB11D1083225AFDCDBC1E24D4AD5FAB396D2E3 -70E44A7571B230660D510A5076D8E35F7DB72C0566DFC119EE1B8AC3C0406950A3C4A4DA36BDE297 -040A27F70753A87E6CD593DC6BE9962261A99AE5949340C5D45C94A9AA3DD636CE8B497BBB812345 -7C824F443A53B3EE595C38983FE3E07DBDC6ACD55CAE8BE1081AFD4857A5F52A3C925143507A3C37 -F1992CF72ED0D4C48D94AE6CADDC3BC87AC3A3EF035E02181F78449E4B063B0835E827644051551C -1603E2EAB5875F28FC77BEBA6923428D5521C698C6B7F133B0F689F105FDBAC30A8ED2F29F0255DD -F8A037B81F04EDF004CBE639C8DB0F94D0C5DB92D34D66C2FED66CF8B895AFC4E659D08388EA44EA -E83CE459E5BE306750A682B627802990037157339BF142BCB9C08FAFDC3C3FB16DC3544F62C6C7E3 -3E20CC4FC7CA21E2C3F6C546CD78DEE348F1A4C8CB548EF20C049678916771D83ACC9B7B22784AD8 -580134471A3C79BC86B5D6D0D305C32E6204274351C94F9DF45D9B2AD5B5087A89F90D6AA033E4B1 -D1BED022F36147C7ABD2B73134DFFD50907258E610C3B20949E141172B1C6A76DB238C375021CBA6 -645CDC26B717428B5A9B4D3F32A4B1E22FEFF3BB93FD889E1DEF8087718D5E3E650FE4A3330DA9C3 -7E9EB499DF5A342D8BA4C0A033C3347CB25A31BE143ECBF91384F2381E323E7FD3A82A3197C18905 -3200AE2C86B9D01AB0B289841EA7E9E9A26966E0DEF54DE0B85D8DF084B8C590081E444BAF1E1F60 -670FA12AB97159318624F2AF1B5EC7DD83C1073A99398D2143A52D10A13C201FB356BC9E90C63BB0 -BC2D4C42AF4A8B9C8C4D58A1B32E0597C63B3F8B3E893BD3BE8C60231838F1BC78E73A6C8CDD5E7F -2907F897FC8EE99BFFDA7338BCEFB5AEF950E5549ADFD207AEB15846B509FC57989883642498A381 -1B8E5CDE69C05924EFAEC232FA4CEF302EE3251366ECAEF57D25CFA3B4A9E6397D996F421C900BEB -CF73B038FE7B16FD0A1172AC2F40D19CE0B02FCEB8BC47DA5344CB933C7FEC950184F78ACB32D3E5 -E290E84BE753B9E7A7BFC4416CCF29D023760C06CDDEF2505806A65E1508990529245059AFD301DB -669D41BD72BF7A80A9DF66B876B3553FDF4DD38D15289AF7A1AFBC53FFFF135A6348DD784AB42A6C -0D6AA330B069607E2DF3CBEFCE79D6F63E274C9E73A33EB85246D5EBB986BFA923DF68B2B8CF82AF -6C33E785F35B25B1D1D614DE85A4F4510ADFE42D75B5FA5408A59ABE53859E28B3D000EB9C6A7D2F -67C91DD14C895BA87B9CB57B851E5193FCC2A443AF85FE28DF6F39537F23A058BCF81DD8C04CB2C2 -5040300F4C55975E856DCB4E21E2B5481BDCC05601942FB25BB8A6B6F93E2C2A33CD478B44655657 -C557EBB080179EE5D98C5CEBE0B25BFDD952FFEB258014D7A5BC4BCA4F1A23BBA73C454B12960451 -CE1752401B0151CB2E01D5C72595095EAE91D8D3BD55A54A2AEA89239FA176FA7CD6F16BB0733EF6 -CE6E77763A23AAC77DA88C8EFA7BBB2991E472FF2075FB25A75ACFA70A04C28764F4AE4C12051B25 -B120CAD2E3044DA35C1F94135DBD69B10DE147321CBBDC814CE99982AC1D76CE3D3330E41AB31F3C -76BF89B95EAB81AF3464C732D5B1411D97DB36C9063537F64756F205B16ED7058E2CB1D6946C00A1 -A0CDA9EBBE924BDA6C7D7B605C514A98133907B793C74CA858E82DA3519188CD974B34DAA74265DB -5BC8550D5F0B1173ACEB87458BCE2AB1F96996C811699A0FE4A9B849D39023725E2B1EE7E426D30A -6C5C75AE6BCEA6DB41E4EB2035F7F924E6B9F0DCD00EB2BB014222E55FE387FBF5B9B7C04F4688D5 -AE3529FDACB38B5EB0AF5C3A874C1AA6B17CDA8D1E22EEE05A3DA88449200D3D0D002DB86F6C51B3 -37C8E19F338E7BFA01E1202612D50E210140947D5F350E84F790286C3F679A5D7E43BCDC337265C2 -631527FD62D598B7CA1F5835C0441881B97F5197901ECDC4F195BC665A846823D2E41417373F8639 -567B228FE7B73D781F07A361AA49C3E9D80FE5B2A32C4C1E575D194E841967B08D10405FA44EEE28 -47DB9372C5CC931E50469532F1BAF577F680BAB4E30B7E1CFFA8574ABB679789F69A8A1BAC07B7C6 -4EF5CE5EB00E97B36FBEACA9BBA4A13B0293D34BDBC77AD1FF88E5744AF009823BC262511C4724DD -585E7E17D90F230F7A5861B0DFC42F0B4E49A04EE0EE4DADB908479DEF8372F334C53D2BA5D855CB -39DC7C9550F9D0F7F77E82D5A59FBBF34BFFE92DC9E6668B68FEEAA4F20053433D6749162BBAC5D0 -D428DCF2D58D49B127FA2E674EDC7D3613B1342F4D0ABD7F4C5B049FBF78E804D5F16505AE7EDCBF -4D6FA08D72890F5D55199034572AB4B0C9A7E7F6F5A403198864ADF113CAFF5BF9D4AB5B16F81D0F -C2188FC80875E10034D12E30C0364F8F72797F1AED525A2712A40D44210B813DF5A29C84E9F6D51B -1D60A5F6F938FAABF878D29E6AB252D95D05FC1ADF5D4CE1C9E585219112112BC6CD5C766411FBD2 -2731794B5DE0A27AC57D3C57926807469C360372BE529098C350EFE2154B87F1205A57A0B04C5206 -CC4FA66B8793BBBE492CC3271FB4F90A28D0066E0D7F63B8DD01549A05AFA5482C29560ABD628568 -75CAC16100087540162473498C14087B29B86B7BFAD693E81765CEC781F3FC80E9C7B410E9B55B88 -114191A1703C638DFBB469ED1DD8254B1407003A319CE74AD419B077F17047A01F0BC0AC8507191B -F72D77D9333C9DA8C9DA733EFB5305F49CB8C7BC451321ADD7D896395D269DCDFDD084EB3AA70338 -6C0697E962929651164135C094D9BB1C9B949D5EEBD3BB17F02C98C813CCBFB23C2C26218A2F4C63 -9A8B9DFF2C29406037F91938A5E1227310728428B56F48108CDEB33BD3191ECA89F947271983DB77 -6B2BC897A30EECF2601EE3B2A6F0E135397622AAC1F2DF523CE6E6BC720E13CB530CEF4AB9C8273B -D3D81563AC8A8E6C44A195112DAF824BC7A72FCDC4E129A480717BEB01085DEE65EE4344D0B41EC0 -BCDF842566B1D9F5353B1F6A063FFA6CDB06EF634C8BD5A7A63F991D178F56EACA653DD67685CE49 -E98C7554745A4AC533217662D23E1D6937135D13BC2208EB8D50560A2BAAC319DFAE478B6BA4CA5E -DA20222F0E9BDB0806320ED1665B54A347DE0C42E9F77842DE4D188E7E824EB2F0D7AD163F05480A -7FA99C5A603BBC5DBC843774CA66E889B945054C0ED0B1A4BB14324EF901B023C208CB95DFCE9284 -89789690CC45BAB97BE449F8E2F5AA9276C0571303E9788C46E7F789555BFCDC3FA9ED8DA8AD9BA4 -8B3AE09404664391E63A989EF1E24BB464043AA099E4F2D796E352EB277106D8D81BAF2F8562EF46 -BCFD1E0047E8018CBD973021DC1C1D821AF03F083F0B088A62EBCF2BF6C5B0FCFA441AAD1625FDB8 -34F943DD47A5A42EB3E9A5B49641F797C288B799A64897F1346070461B6D535E0C4ED099199C387A -3176AEDC7DA7E7D9E118E55565092A36F7C74ABF281720C0147F4E4F37D49436466C61FF12764E30 -43D8A6D027E70537164F0E7942F4ACA42BB2CB136177EF7197E76F49AB403F741C0EF902FEBC471A -D6C627424320A8C3A1F04C310C511B3F91C3937D9ACF459999C18A33F2C852EC38CA806599C728C5 -43714018C65E2C5F430F6270AF52AD71ED38813B60440779455F9529A4A1623CB9F5422B9216F9CD -BA913B9A1CD95DA225E254E8101216085020660509D03A034B5D7E32E3DB5E5962A9A27711D4C3E2 -9CD84057F7D0D7E8000947AFE896F8523253391D2E11FFFE523366B05C532D5629A90741EAB3D4A7 -31D3F6D4F03FF93233DDF88BB1913ABA22EB9AA6311E3144381DAE29BCC8639958EEE59ACCFA06F3 -5DCCC63E0609F542F3EE5DFB1CF718CA3F328455726F8F65E23ACD970E4049225998371B63E35AE9 -8DC54D8329B8DB0901FAA63129EDE21B158776981D4D094013C096E9CD020315D123C03DEBA21E97 -E4B584B4BC0AF25F5DCE53C2DC0F3E61F99BECAB40799478BE7F5AFD7F68E23EF50AD6645C967EE1 -1206B6E791769428ACDC370D64E4F2B3972E0E4F442297199350663D6E772FC6777A9B9DE215273D -082CCE4E8678FE9948DC8D5B0E459CD02F1645AC5620F3571A40B4D5A17DF5CFF48B6C843DDEAB5E -BF58FE13D7DA08E8AA7902119248B3B151DA583101CF80853B0150FE05BDEDBFB50A7FB0F65728C9 -3B9DF48CE8AF1DF1FAC25C1D58E1AD30274A00EB54CF2F16029E1AC0A0919C0655474B9A6936AEE0 -FB74BD185FE7D70BB84786997D34A40326A74356A4AFAEE67B6B26D1C1A7BCFF8697B55C816CCD77 -312C332A55315DC54F9BC0A0F12500E0A76B3936292A3DA2DDF5AA8CBB9B5DC32EDACC4827D684D2 -74E65B8B76FB2C2B19F7D5607523FA953E34BB39032C05B1C1244304606C55660D3CA8607E764EA5 -B03DB7FCAB5CF7788C6E60EC8C449BCAFD90BCABA4132B6CBCCFF16784FB59B36B77CF0A9EA572E4 -CA0A01C725A6CF2E4500CDDF5BACCB9094D48925434F044118CFDC2696AF5FC0CAB3884107ED17B9 -BDE0C0104B1292A1F8C99B06FC4A6360B24480BD59DF0488641899B0F42B1311B582717BA7ECFEE1 -4143654B5371C8B9B2D80685AD38D897AD1E64875C28C7020A84FBB3A3BBEE16617DCB9BC822B7C5 -9C5A18C0CF7E80163ADFB7AA03B7CDE8497C1697D90F2ED90F813095C5B91657FC294EF0E341DB33 -92ED860CB2E0AA09293D0F99AE9EB54C761CA2DB1E51E1CEAEAB276C7BD916C68510D72D9A67468B -09B3C39A7815628FB126CDFD5EFF59CC8184C0D35A5B5960F824BD175495DD3EB12A4E96008CB13B -8C5745303E66CF8608FF27C4709C1D854EB79608E52F068FEC0151A74C125EDEAEA555C198FC0802 -7BBBB802835E1D435077AE4B1CCDBF722354F6C572BEB1376D3E342195FA80AC9722EB2F46E44DE0 -5F5A227B731B8D4A4B6EDEF04AF2C5DEC2EEF8FF48C5B18710ADE3DBFA0C956505B6DA9CCB7CBB83 -4DB6CC754948855D833670FF0AC42A4773FEA8322BECEE04CA74AC2D66855132D11A51524488C547 -71B5B7A512796D7D7AE0F9C1FBC9CBDBA0831074F4D200349D0CA40537B92496692766F020AC43AC -01DB8B2AA2EFA9D21732BE3A315F6CAA402BB2E61D40DDEBDE11276D90C2C601A935C168BE600464 -76ADED15087D54A14C68EECBBBB590927C1E10D291C9285334CB0C80EDBD392BDE4D535EB61F8E76 -41F58AC1DF5B1C5A5D91E3E27E05CAF7EC97ECF0C85B6425197AA856521ED701E5AEB82A7F52A8BD -7DC97D5B3FB5C99A5DF84D1BAFF89072922509D76BC6EDB15CE5F9EB8F4154BEE1E82020240283BD -C83A8E49AA9A2649B7955D5C058F2818A63BD0BFE7EACED4A49063C489A626277AE1246F721C9926 -E2A2B6C31045FBCD235F3CC58BC4DD6C57FE998EBD1E9FA5154652BE3A1685BCD2EFAA079A3293F7 -8142A6473822FAB627927EACCD61B3E99C3077103D2D19382BC7EE15BAD0FDE489602D055A01DBBC -F91A566974559D1B477C209416887053169C3F8F59955BE4DE82B60558CC9AE15602A93F029F6B43 -29E0E62A03982DB32F5229714EFA1491A7B24AEFE18FEBC2C93DFE50B3F641B51BDD33DA38871BF5 -243C17502D00AEA2D9E9734E80A96788D4CF5BC12A42BC386162FC88A7435EE13200C1C2C6CCC5D2 -1A03941007B4C4291BDB711446CEAF27148104BB240357D5EDA0EA5A5CE27D4A83909D75BFC05D75 -F10AA74A6DE37D7DE15C1DDA3AC3045DA6CD48323D904E716B445E5E096FCB379353ED70CF4B6FAC -102C762711079EFAF13FB74C9B47AF75F3F6BDA2A4647D2AB47ECAB64DA6CC01479F618E8D2D0A36 -45445E8744683CBBC560D47C98078B84206E90EB839B02D37C852B8E284463D4E4D890203C3D5B20 -352110034EAD6BD7F41456B807E1DB1631A9D499E52E9D9853D86728B1A2E511F40F8CA1E4724A0D -17ECD640B52FF6C66E28693D89765FC391612E5889E77423EC85CBD0A038B6BA98B607701DC0C4B6 -6B3B28C7790A1F1EB8D051DC98276DD9CFEFAB3F65C1C928E48A060C992B392A43E56EAA6DED896D -EBCE71F8245BE4687F2F1B8FC0F43ECE8DB0BD0AB0811C5CE73CBE336023A0D66168B34A95B4B0A7 -50B3BF1D197E3C042C7914FA731D7831AF798E9429571CBB977E6258244E84701E5FF91D608F98FC -3D68A4EE5B81D5FF38B6C184F6118B875F022B4CE207DC7B37E1452DFDC591A3E506AE82C7E7BFF0 -011B0A3DBD616A993FBF878FB03B6C9F2055A2B095D29361F8253C2623653687FE0AB98078F6AEE5 -FC2C2BDE0405EABEDB3A33EB7F04CB6837176245F190C6BBBCD64522B12FE7F9CDCF201A1AA8A19A -7BBC4AC064B4958F44AA0F8DDA23835AD28A1FD0EA105DE2F395385DCCFBE2261DC5A89A23AF606A -3985E5038706B1FE0910400E16BF008F250F3BDE3AD806C735495D499F16F99275010478FD2127BF -7CEDD6B5BD505FBE9BD0065B4A7090C9D27CD5B36C3AD33E1B31EB6D44E375003B51B909DA50BD18 -218418B3CD22B43278B144BE78406EAF16C7DF6B6C1C6238004AAB73736B38E168441DC16F9A5CF6 -0793A18633BC43D78674D12D38CC979F7CAADA6EFE807CEA499CB9FE616496682A66E04BBDACE1DC -112B2156B9B0B20A58A8CB43FF0EEDB99805234B9A5789762AC7D65F5A319C33F4F7438CD15E06BB -80A7A97E976E8CEC23F4C646A5821880A82B2F1DC27767F090997E91488BFA15064B702F864FCE65 -05D6CEF87D2A0A12B55BA189AF269811E3B8B850C8401F3906C080D32618D9698A766732A40A9FC5 -A94E5BDDA3D028D823D6B603B6D17DD046DE181FD989EA0F80B4CA62F7973E4DF5E032A31FE6BC8F -5CDA678D4A72787EB8253EA5882C337CDF9AA3E1E7D9536DD09B047CD8962E773F72F6418A3AEF5A -289B3406C152A50CE7BD4B493FFFC27F6AA52F79EA67E362FD92559AA4F94A2F787F6C735DFADCF2 -F08AAF98B80C53CA5607A94F25F04AA65A70A75937840E73055B3D65FB054C63E2E48E68488C9315 -A13EE949E03E46723C11CC759D222CBFAD2E1A87CAD779B23D38F7E2F660DE1388EAF1CF4D18994D -75C6CC63F187FDB949940C18B537A0AFB12AC5F67B0283CA5EFE2E764C4369104B9D3B06490D1244 -C41D6085C85F1106082EC9DB84586230511C05C82412D2CDF3DAFBF4759A775628878F997415296B -C416AC8352A6C6988691FCB831CF95C10BAE691ADB3BA2918B35924BD5C3ACAD8B137397B10AF82B -479800FE16D472CD0CDBDAAB4F882A0649CF561004B8CB7CA32EC129D0A415BE6CB91DA2B65F44E8 -0D138808A127E851A7FCF927E99DAA0EA2D626B77A16C72E37F058A3B882FC4955DC8CB6312434BD -3BCED75780B13590BF4FE8D64ACF0371F9FB1D361B05025852AAB9EDA1A0C997CFA58052C454FD45 -1E6C1F194F4D363114E312F6DC35BBAF357A32CD200A3DD9654155134259887D677ACC44F89AA401 -CA27282DF7DC3F2F04A108CBEF2558DCCE28BAC2D87B8D5B7181EA927F61977764F882626D4AB338 -D95C9477C54E9C36012A3CFFBE199EC8120A99D2D70A21F9D9A0354E4EAC7947990E8A6E0601796A -AF6F14E758CABCABDFBD8204A8E748A3E5FEBA570D36E2BF474C0083229A63F96114182321B2EBE1 -BC76DD193724C4588C1D39D184C332FAEAF4C629F2B3B2F49996E46AA6C9F497428BEA52D58876B0 -DC07B460248BC85CC16773A5DAC36CDE8B152D96057F4EFAAF8B1DC10022038577368057699B3A37 -178A9F1F6C6CC60BAE820B7ADD0717911BD23A6DCDADAFA32473491AA80CFE90F2A77E24CE2826FF -77B18B869C33FA292FE01D6477765044C7D14A548B28B1360125C6933F05C58B0889390537CDD16F -8E967E0B38579449DFC1E07389B7069AA8594C5103465D5041CC929268DE863FADB6925B350AA94A -27D421FB7FCC81C6B35F906F12246B7A5140511A97211BA9BD6831A508E963FE8BE961332F557808 -488F06EAD75E86D60DE3FA2425AE8439ECB9112BC3E4D73747C1C8E87A649919827049832DB0BF6D -A8C85C9A2592AC002809070900ECAD52A56F1BFD456AFE066509694EAC075788456B0B0BDD7C192D -321E9FB6AADCAEF00F570F22CD4A5322FBCE8FA98FAEB681940895426270BB4319C11DA67D88552A -7373398AEC5DA7C9CAA9F3B34581C6E968DAAAB2751CC012199DD897B448986CFFBAE4D412BF9ECB -F46742715A9569932516259D3B3A5431CD7028E42FC751C434E2B714C718202BF02CAF9B8A2075DE -922322EA7CFA605C8376FA958B8FBE43031E1026FBE6126A3775F643EA67EBBD97F239FB3C435526 -75CD08B19CA5EBF53B40D728556B4481C7F73EC71CAB0F89E34D60C69B272FADC22E8E7BDC6210DB -09FDD913E209F49FD28E8712B8508904620250746CA3B21B026EDAE60A2822F59E912E626B93E0D2 -BFB3230DFD0E54E91A1DBA25A609B64D41ABD897A5D21764C351E85F9E87BEAB9E645149AD32AEEB -B3B1161032C701647115F98C1C2AAECE871862D91D321AB90F3E923B1FDEE00D927F897AA9812373 -6536E2E0700F10053D7E6C589BF66029D794883EAE4C8228941CE96565B50D48887B5314A2E55379 -59638222A6CA54C77CBABD460DAC11B063519AE4F50D93DE41763BA7CFBF4C7724360E750478EB62 -8921DAA065858341958E4F3EB5966C6DD77C05EEECDF4B5F6CF19AB507589B4219377959BD258EC9 -21C34FE1DB003F7D0FEA3E2FD6F5DDB0A2D62CA5A2CD3C7AB457DFF25094EFE04A9E1B9CE7AE3F30 -026B1CB039228D309A22899F6E9B9BFF922E117123347967D7C62C670E2C74579C35989925603022 -C17B1DCE378031ABC9B4B437C7B6E64620932E93189754C01D4B280B8B08699B2CA953AE4823BB9E -E34133C5C95B3290E1BF010705AD852C72BE87291E1034B09F44A95B6A2F83FEE8841DCF661770AF -44D0AC7F9CDB280939FC5D953D525E0B41B7BE188D5C794687330CD770D24D9CD53B895A253004E1 -8A31BE4E82B384 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/NimbusMonL-ReguObli-iso1252 /NimbusMonL-ReguObli ISO1252Encoding psp_definefont -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -/Times-Italic-iso1252 /Times-Italic ISO1252Encoding psp_definefont -295 321 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4C61> -show -362 321 moveto -<6469726563746F7279> -show -556 321 moveto -<53414C4F4D45325F524F4F54> -show -962 321 moveto -<657374> -show -1032 321 moveto -<737570706F73E965> -show -1230 321 moveto - -show -1318 321 moveto -<6C61> -show -1369 321 moveto -<6469726563746F7279> -show -1564 321 moveto -<636F6E74656E616E74> -show -1772 321 moveto -<746F7573> -show -1870 321 moveto -<6C6573> -show -1940 321 moveto -<6D6F64756C6573> -show -295 377 moveto -<6465> -show -358 377 moveto -<6C61> -show -411 377 moveto -<706C617465666F726D65> -show -644 377 moveto -<53414C4F4D45> -show -866 377 moveto -<322E> -show -920 377 moveto -<4C6573> -show -1009 377 moveto -<6669636869657273> -show -1175 377 moveto -<2A4D6178456C656D656E74566F6C756D652A> -show -1661 377 moveto -<736F6E74> -show -1761 377 moveto -<64E96AE0> -show -1861 377 moveto -<64616E73> -show -1969 377 moveto -<6C61> -show -2021 377 moveto -<62617365> -show -295 433 moveto -<6D616973206E6520636F6E7469656E6E656E74207269656E206465207369676E696669616E742E> -show -295 551 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 551 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -659 551 moveto -<6475> -show -738 551 moveto -<66696368696572> -show -896 551 moveto -1 0 0 setrgbcolor -/NimbusMonL-ReguObli-iso1252 findfont 50 -50 matrix scale makefont setfont -<636F6E6669677572652E696E2E62617365> -show -1434 551 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1553 551 moveto -<72616A6F75746572> -show -1737 551 moveto -<6C61> -show -1801 551 moveto -<76E972696669636174696F6E> -show -2060 551 moveto -<6475> -show -370 611 moveto -<66696368696572> -show -535 611 moveto -<696E636C756465> -show -716 611 moveto -<6E676C69622E68> -show -891 611 moveto -<6574> -show -962 611 moveto -<646573> -show -1063 611 moveto -<64696666E972656E746573> -show -1310 611 moveto -<6C696272616972696573> -show -1522 611 moveto -<4E657467656E2C> -show -1714 611 moveto -<766961> -show -1809 611 moveto -<6C65> -show -1880 611 moveto -<66696368696572> -show -2045 611 moveto -<6D34> -show -370 668 moveto -<636865636B5F4E657467656E2E6D342E> -show -309 769 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<6563686F2074657374696E67206E657467656E> -show -309 813 moveto -<6563686F202D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D -2D2D2D2D2D2D2D2D2D> -show -309 857 moveto -<6563686F> -show -309 901 moveto -<434845434B5F4E455447454E> -show -309 945 moveto -<6563686F> -show -309 989 moveto -<6563686F202D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D2D -2D2D2D2D2D2D2D2D2D> -show -295 1106 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1106 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E206475206669636869657220> -show -849 1106 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D616B6566696C652E696E20> -show -1087 1106 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<20> -show -1099 1106 moveto -0 0 0 setrgbcolor -<706F757220706F75766F6972207574696C69736572206C652064657373696E> -show -370 1166 moveto -<6D6573685F747265655F616C676F5F74657472612E706E672064616E73206C2749484D20646520 -534D4553482028766F6972206C61206D6F64696620E020666169726520737572> -show -370 1222 moveto -<20534D45534847554929206574206C61204272657020666C696768745F736F6C69642E62726570 -206461616E73206C65207465737420534D4553485F666C696768745F736B696E2E70792E> -show -294 1323 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<5245534F55524345535F46494C4553203D205C> -show -294 1367 moveto -<20202020202E> -show -294 1411 moveto -<20202020202E> -show -294 1455 moveto -<20202020202E> -show -294 1499 moveto -<6D6573685F747265655F616C676F5F74657472612E706E67205C> -show -294 1543 moveto -<666C696768745F736F6C69642E62726570> -show -295 1660 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1660 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4C65206669636869657220> -show -578 1660 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<61646D> -show -664 1660 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -677 1660 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<756E69782F6D616B655F636F6D6D656E63652E696E202020> -show -1207 1660 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<646F697420617573736920EA747265206D6F64696669E920E02063657474652066696E> -show -1862 1660 moveto -<20> -show -1874 1660 moveto -<3A> -show -309 1765 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<69666571202840574954484E455447454E402C79657329> -show -309 1809 moveto -<202041434C4F43414C5F535243202B3D20636865636B5F6E657467656E2E6D34> -show -309 1853 moveto -<656E646966> -show -295 1971 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1971 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -662 1971 moveto -<6475> -show -744 1971 moveto -<66696368696572> -show -905 1971 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F535243> -show -1178 1971 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1190 1971 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<737263> -show -1251 1971 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1264 1971 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D616B6566696C652E696E> -show -1552 1971 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1675 1971 moveto -<72616A6F75746572> -show -1861 1971 moveto -<6C61> -show -1928 1971 moveto -<6469726563746F7279> -show -370 2031 moveto -<4E455447454E> -show -592 2031 moveto -<6FF9> -show -664 2031 moveto -<6C61> -show -723 2031 moveto -<6C6962726169726965> -show -904 2031 moveto -<64796E616D69717565> -show -1149 2031 moveto -<6C69624E455447454E2E736F> -show -1481 2031 moveto -<7661> -show -1550 2031 moveto - -show -1648 2031 moveto -<636F6E73747275697465> -show -1868 2031 moveto - -show -1912 2031 moveto -<706172746972> -show -2043 2031 moveto -<646573> -show -370 2087 moveto -<6C69627261697269657320737461746963206465204E657467656E2E> -show -294 2188 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<69666571202840574954484E455447454E402C79657329> -show -294 2232 moveto -<2020534244495253203D204F424A45435420534D445320534D4553484453204472697665722044 -72697665724D45442044726976657244415420447269766572554E56205C> -show -294 2276 moveto -<20202020202020202020204D45464953544F204E455447454E20534D45534820534D4553485F49 -20534D45534846696C7465727353656C656374696F6E20534D455348475549205C> -show -294 2320 moveto -<2020202020202020202020534D4553485F53574947> -show -294 2364 moveto -<656E646966> -show -295 2481 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 2481 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -665 2481 moveto -<6475> -show -750 2481 moveto -<66696368696572> -show -915 2481 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F535243> -show -1187 2481 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1200 2481 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<737263> -show -1261 2481 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1274 2481 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D455348> -show -1432 2481 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1445 2481 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D616B6566696C652E696E> -show -1738 2481 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1864 2481 moveto -<72616A6F75746572> -show -2054 2481 moveto -<6C6573> -show -370 2541 moveto -<6669636869657273> -show -541 2541 moveto -<717569> -show -626 2541 moveto -<7772617070656E74> -show -834 2541 moveto -<6C6573> -show -910 2541 moveto -<617070656C73> -show -1059 2541 moveto -<6175> -show -1128 2541 moveto -<6D61696C6C657572> -show -1315 2541 moveto -<74E974726168E9647269717565> -show -1600 2541 moveto -<6465> -show -1669 2541 moveto -<4E657467656E> -show -1835 2541 moveto -<6574> -show -1893 2541 moveto -<6C6573> -show -1969 2541 moveto -<626F6E6E6573> -show -370 2597 moveto -<6F7074696F6E7320646520636F6D70696C6174696F6E2F6C696E6B6167652E> -show -294 2698 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<69666571202840574954484E455447454E402C79657329> -show -294 2742 moveto -<20204558504F52545F48454144455253202B3D20534D4553485F4E455447454E5F33442E687878> -show -294 2786 moveto -<20204C49425F535243202B3D20534D4553485F4E455447454E5F33442E637878> -show -294 2830 moveto -<20204E455447454E5F494E434C554445533D404E455447454E5F494E434C5544455340> -show -294 2874 moveto -<2020435050464C414753202B3D2024284E455447454E5F494E434C5544455329> -show -294 2918 moveto -<2020435858464C414753202B3D2024284E455447454E5F494E434C5544455329> -show -294 2962 moveto -<20204C44464C414753202B3D202D6C4E455447454E> -show -294 3006 moveto -<656E646966> -show -295 735 1 264 rectfill -2125 735 1 264 rectfill -295 735 1831 1 rectfill -295 998 1831 1 rectfill -280 1289 1 265 rectfill -2125 1289 1 265 rectfill -280 1289 1846 1 rectfill -280 1553 1846 1 rectfill -295 1732 1 132 rectfill -2125 1732 1 132 rectfill -295 1732 1831 1 rectfill -295 1863 1831 1 rectfill -280 2154 1 220 rectfill -2125 2154 1 220 rectfill -280 2154 1846 1 rectfill -280 2373 1846 1 rectfill -280 2665 1 352 rectfill -2125 2665 1 352 rectfill -280 2665 1846 1 rectfill -280 3016 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 2 2 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/Times-Italic-iso1252 /Times-Italic ISO1252Encoding psp_definefont -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -295 271 moveto -0 0 0 setrgbcolor -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 271 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -653 271 moveto -<6475> -show -725 271 moveto -<66696368696572> -show -877 271 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F535243> -show -1149 271 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1162 271 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<737263> -show -1223 271 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1236 271 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F49> -show -1435 271 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1448 271 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D616B6566696C652E696E2C> -show -1708 271 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont - -show -1938 271 moveto -<434F524241> -show -370 331 moveto -<6465206C61207072E963E964656E7465206D6F64696669636174696F6E2E> -show -294 433 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<69666571202840574954484E455447454E402C79657329> -show -294 476 moveto -<20204C49425F535243202B3D20534D4553485F4E455447454E5F33445F692E637878> -show -294 520 moveto -<2020> -show -344 520 moveto -<4C44464C414753202B3D202D6C4E455447454E> -show -294 564 moveto -<656E646966> -show -295 682 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 682 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -657 682 moveto -<6475> -show -733 682 moveto -<66696368696572> -show -890 682 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<69646C2F534D4553485F42617369634879706F7468657369732E69646C> -show -1563 682 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1681 682 moveto -<72616A6F75746572> -show -1863 682 moveto -<6C92616C676F726974686D65> -show -370 742 moveto -<4E455447454E5F33442071756920636F72726573706F6E64206175206D61696C6C6575722074E9 -74726168E9647269717565206465204E657467656E2E> -show -294 887 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<2020> -show -344 887 moveto -<696E7465726661636520534D4553485F4E455447454E5F3344> -show -974 887 moveto -<20> -show -999 887 moveto -<3A20534D4553485F33445F416C676F> -show -294 931 moveto -<20207B> -show -294 975 moveto -<20207D3B> -show -295 1092 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1092 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -667 1092 moveto -<6475> -show -754 1092 moveto -<66696368696572> -show -921 1092 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F534D4553484755492F534D4553484755495F69636F6E732E70 -6F> -show -2019 1092 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -370 1152 moveto -<72616A6F75746572> -show -556 1152 moveto -<6C92616C676F726974686D65> -show -832 1152 moveto -<4E455447454E5F3344> -show -1149 1152 moveto -<717569> -show -1242 1152 moveto -<636F72726573706F6E64> -show -1495 1152 moveto -<6175> -show -1572 1152 moveto -<6D61696C6C657572> -show -1769 1152 moveto -<74E974726168E9647269717565> -show -2062 1152 moveto -<6465> -show -370 1208 moveto -<4E657467656E2064616E73206C612047554920646520534D4553482028626F75746F6E20646520 -73E96C656374696F6E292E> -show -294 1309 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<236D6573685F747265655F616C676F5F7465747261> -show -294 1353 moveto -<6D736769642049434F4E5F534D4553485F545245455F414C474F5F54657472615F3344> -show -294 1397 moveto -<6D7367737472206D6573685F747265655F616C676F5F74657472612E706E67> -show -295 1515 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1515 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -651 1515 moveto -<646573> -show -739 1515 moveto -<6669636869657273> -show -910 1515 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F726573736F75726365732F534D4553485F5B656E2C66725D2E786D6C> -show -1843 1515 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1955 1515 moveto -<72616A6F75746572> -show -370 1575 moveto -<6C6573> -show -445 1575 moveto -<636F6D6D656E746169726573> -show -744 1575 moveto -<737572> -show -824 1575 moveto -<6C6573> -show -898 1575 moveto -<626F75746F6E73> -show -1075 1575 moveto -<6465> -show -1142 1575 moveto -<6C61> -show -1196 1575 moveto -<475549> -show -1305 1575 moveto -<6465> -show -1370 1575 moveto -<534D4553482E> -show -1569 1575 moveto -<436573> -show -1663 1575 moveto -<626F75746F6E73> -show -1840 1575 moveto -<7065726D657474656E74> -show -2073 1575 moveto -<6C65> -show -370 1631 moveto -<63686F6978206475206D61696C6C6575722074E974726168E9647269717565206465204E657467 -656E206574206465206C61207461696C6C652064657320E96CE96D656E747320766F6C756D697175 -65732E> -show -294 1732 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<20202020> -show -394 1732 moveto -<3C> -show -419 1732 moveto -<706F7075702D6974656D206974656D2D69643D93353033339420706F732D69643D9494206C6162 -656C2D69643D944D61782E2048657861686564726F6E206F72> -show -294 1776 moveto -<5465747261686564726F6E20566F6C756D65942069636F6E2D69643D946D6573685F6879706F5F -766F6C756D652E706E679420746F6F6C7469702D69643D949420616363656C2D> -show -294 1820 moveto -<69643D949420746F67676C652D69643D949420657865637574652D616374696F6E3D9494202F> -show -1251 1820 moveto -<3E> -show -294 1864 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1908 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1952 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1996 moveto -<202020203C> -show -420 1996 moveto -<706F7075702D6974656D206974656D2D69643D93353032309420706F732D69643D9494206C6162 -656C2D69643D9448657861686564726F6E2028692C6A2C6B2994> -show -294 2040 moveto -<69636F6E2D69643D946D6573685F616C676F5F686578612E706E679420746F6F6C7469702D6964 -3D949420616363656C2D69643D949420746F67676C652D69643D9494> -show -294 2084 moveto -<657865637574652D616374696F6E3D9494202F> -show -772 2084 moveto -<3E> -show -294 2128 moveto -<202020203C> -show -420 2128 moveto -<706F7075702D6974656D206974656D2D69643D93353032319420706F732D69643D9494206C6162 -656C2D69643D945465747261686564726F6E20284E657467656E2994> -show -294 2171 moveto -<69636F6E2D69643D946D6573685F616C676F5F686578612E706E679420746F6F6C7469702D6964 -3D949420616363656C2D69643D949420746F67676C652D69643D9494> -show -294 2215 moveto -<657865637574652D616374696F6E3D9494202F> -show -772 2215 moveto -<3E> -show -294 2259 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2303 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2347 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2391 moveto -<202020203C746F6F6C627574746F6E> -show -672 2391 moveto -<2D6974656D206974656D2D69643D933530333394206C6162656C2D69643D944D61782E20486578 -61686564726F6E206F72> -show -294 2435 moveto -<5465747261686564726F6E20566F6C756D65942069636F6E2D69643D946D6573685F6879706F5F -766F6C756D652E706E679420746F6F6C7469702D69643D94204D61782E> -show -294 2479 moveto -<48657861686564726F6E206F72205465747261686564726F6E20566F6C756D65204879706F7468 -657369739420616363656C2D69643D949420746F67676C652D69643D9494> -show -294 2523 moveto -<657865637574652D616374696F6E3D9494202F> -show -772 2523 moveto -<3E> -show -294 2567 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2611 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2655 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2699 moveto -<202020203C> -show -420 2699 moveto -<746F6F6C627574746F6E2D6974656D206974656D2D69643D93353032309420706F732D69643D94 -94206C6162656C2D69643D9448657861686564726F6E> -show -294 2743 moveto -<28692C6A2C6B29942069636F6E2D69643D946D6573685F616C676F5F686578612E706E67942074 -6F6F6C7469702D69643D942048657861686564726F6E2028692C6A2C6B29> -show -294 2787 moveto -<416C676F726974686D9420616363656C2D69643D949420746F67676C652D69643D949420657865 -637574652D616374696F6E3D9494202F> -show -1680 2787 moveto -<3E> -show -294 2831 moveto -<202020203C746F6F6C627574746F6E> -show -672 2831 moveto -<2D6974656D206974656D2D69643D93353032319420706F732D69643D9494206C6162656C2D6964 -3D945465747261686564726F6E> -show -294 2875 moveto -<284E657467656E29942069636F6E2D69643D946D6573685F616C676F5F686578612E706E679420 -746F6F6C7469702D69643D94205465747261686564726F6E20284E657467656E29> -show -294 2919 moveto -<416C676F726974686D9420616363656C2D69643D949420746F67676C652D69643D949420657865 -637574652D616374696F6E3D9494202F> -show -1680 2919 moveto -<3E> -show -220 3030 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<417474656E74696F6E> -show -220 3035 188 3 rectfill -408 3030 moveto -<3A20756E2066696368696572202E706E6720206D6573685F616C676F5F74657472612E706E6720 -646F697420EA7472652070726F6475697420706F75722064697374696E67756572206C276963F46E -65206475> -show -220 3086 moveto -<6D61696C6C657572206865786168E96472697175652064652063656C7569206475206D61696C6C -6575722074E974726168E9647269717565202861637475656C6C656D656E7420632765737420756E -652073696D706C65> -show -280 399 1 176 rectfill -2125 399 1 176 rectfill -280 399 1846 1 rectfill -280 574 1846 1 rectfill -280 809 1 176 rectfill -2125 809 1 176 rectfill -280 809 1846 1 rectfill -280 984 1846 1 rectfill -280 1276 1 132 rectfill -2125 1276 1 132 rectfill -280 1276 1846 1 rectfill -280 1407 1846 1 rectfill -280 1698 1 1231 rectfill -2125 1698 1 1231 rectfill -280 1698 1846 1 rectfill -280 2928 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 3 3 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -%%BeginResource: font NimbusMonL-ReguObli -%!PS-AdobeFont-1.0: NimbusMonL-ReguObli 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular Oblique) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle -12.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-ReguObli def -/PaintType 0 def -/WMode 0 def -/FontBBox {-61 -237 774 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020947 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A208511C6D0C255B9A5BB2FDEDB4D399C6CF1 -94FFAC236883767C0F68F4EF84EE696B677DE704EC3B097384F2E673A1F51692B7B260693738C211 -9F7D90FFDB21EB715FD5B8134FC87DBA320EE54C2CEC6A4D6BB350555EAFF2EC4F84365CCC0802DB -B3BD0E3F0D9F858647DD637725C2CAF9557FDF842A0DA6A0CA0F1B442EF8EE6CBF2B03858468A466 -AC5883CBBD3815B283343B39205803C02C917D06825C09E2BB14609FA32C28D720C0E14A4B12D4F1 -25FF6281FF324DA33A56FC49987AC7D3AA206540F8127273FFE9A3DACFFE2B1C269D3DB9A811578A -C7D532C2EFC18376F473FBB2B32EF642B19CDEC1D6DE83643723E3C6DFC87F97A7007B6081894BBC -45C955B7001EB36211B26AD7A3D07459CFB33F9C54A40A360CB802FD202C8E93D4DB888B325CE246 -D02D1220ABF55CE646DFB45F07CB848406E470362F80CE4C02D98DD845189877732744CC16C7F566 -9F77EF096EA55AFF98AA103EEAEFB971731EBF3782E6AB725D4E9E35B2968689E8007C038CF25B6A -E69451A4731E79AC22BD268F56942A233E52D71873E83E00A1874E04D3B22E72FB2D0671AF81C698 -53C389B51F4A257373AEBF4DE2DA1E4DA5E2CA88941F81EAE0E32D982064C8AFDD7A9A600D56D736 -05B9463C6240606B3361BAF22AF74EF89AC804A5793BD512DA2D13F4BB1B73EFCA1E621ED2A65D66 -5AAD0AD228B3B7E3D90DBDB6061E172B686E92355A7C7459D83199040A368B5697DDC3B81DDAD341 -6FF4405E1096B1240EDC18A0E9985CA55A0D697972BB11E9F1BC30765D6775BB68C69704BE200EEF -4E11B78ADDB6229D8FA49A6B1525ADADF17122C0FFF51A08AA7AED158724AC4352EBB91ED0C157E2 -4281BDC1FD610195F495E87062A8C38E0D046DA4067EE16E81BC5F87E583315B973184E474064482 -9B2A52E0D37E249BAB31988B906F891AC904D1BB8901F0673AECE60ACEDE97B8DB7935C6488ADE8D -FD898027424AA85A11A3DA494498B084133B857017A6D507D70A3421235486EB3CF7613C59139FD4 -DCB92EADC60BB6225D9CD0599779217BDAF4813A453989B2E56903F4DBB83D83DF4837C86BB4C3D3 -CCF98F07A23EBBF7AB5687C3E1E6792E40F92A7A466DE352294064537505EEF3F9C308C9EB94506D -B02CFAE289F10005A6E42D2DCE43731A7AE368564B2983038DAD6987F67062199018395BC0FCAF28 -7A2B040C71F7325FA1E9A9808979B2FEF19096B98B8A0A728EB98F2BA3D33B49E3C20BE992822C7A -1BCCA5B4E4D1099D456D8D7D83C57ECBA0FF21428024F7572A1470317CB8CBC8679A974E13D88C68 -1338C68C9AC9557F97784F4E1C8C2E61F26023ACF46232CBBDF3C0BCC5583B935FE9FA09A562129A -8927AE73988DB0F7E733C6561CA7C9716DCA9B88208A715166F2FAE6D5EFF289A9B2EDCE813403A4 -16F243F1B57EEDE7D81E10C2DA4065A3082BC92A38B2457368EEC9C3C17296CB09819E9E642D7365 -F9A6EF430FC7DD611EA5FDBDEDFA72634AB599EB666A5DC178B0A0BD1FAB042792115EF3B6222C12 -41DCE36CB38B738F68B1B3CB489FED9E53315553F3C5C3BBCE40451E47B7EA53FD3D3ABA6CE0AD22 -5DAEE734BDFA3BF1D81C1B42C6D856A05D0924E03F7627C5EB24D7FBEA3BD85716207F961B56803D -BE046E81ED5FDC378F9CA52C14FD8544CA7C539201BEE06487EBDC30FF3B28E8264EC7FD5DA7E080 -65B0A9147344CE28DA5182335875E9F8B2347A44E33DFAA167232A5C3E69E8C5B58B7C7216537827 -C936F5741B87FC68753EB0D4A466961D0050DB59DF3195BD3379F5647F8CFED35DA952D7CF2DED45 -EB442DBFE992711D22EB228BDDF36B8D7DBA27062D60D2271EA8E8412F4290B58F5BE26FF06F0559 -872F9DE4DEAABA015EAB4904BA1F509F6D517C6E897312DDD571D769BC474FD378AF4360E8B1F103 -AA75F48721B9E0BA589319E15D74AC0B03D730C3EF708C7C504787483F134EA6297097B46D2680FF -8AA50B7A255563C88D594B912F5574564A1371463674793E4834AF11D14C7991E7FDB3A6ABF8529E -1A4F10CAE79C60D37429579093DBD041ECAF03824DF9C007E96F45595A524B27EF8774A83AEEBD3A -7134AB4435C80944DEFF5C1CBA921B0A41B9651968581DA4834B3C0E6D4DE13C1E792FCEED26A72A -DC4D9E3903661D8803DDB58EB2B929CE31FC9F50A694116B00AC9F3EEF53FFDB1ACA3394BF111610 -38F39917B022394C75A0D467D64B89A44E5505DED7D9C6B8BA6BA098F140C9C00E09200EB4828356 -A2D6BE9EC1D5524B09C06D9C6FCB5E2808050A339B5E5FD4DD6C2035A48FE9674520901EDCAD107F -67AC8C8E508E6003011978D77ED225F361BC0F86A98B6120EEAFB73F7377DB1E7213E02D12C330F5 -492511B4DDE08558D75D5B8AA2D56A3111DCCD257EE96E3446EF1C76F000C8916C4CE261425ED9D1 -5B58CED128DAA6C1300466E7B152BCFB5E6FAAB2519B8A98F26B29F98133AF886A0AA7E586A090BD -A1DC6120DBB5640885C609A8BDADEEFE5DE0DA5B75A8A29E92515E86E7E66BB29581E5AFF8CB6551 -D8D1103DF60D558E7987E6F56126A13DB2C9A04886C655064E68A0A20D1B7DE24DAD22BBFEE1B7C3 -C208D4FD6A58DE78D6A0A6126EFDEE3B1A9713DEE94069A9F0A2B392A2F391C4C75327803B53F252 -CC9EF0323F84929BA4716C50385681FF5B4ED54929821594F9026B7C1297941B178C3F8A704CE097 -60533DBC6CF4B18AFBCBAD039ECB2EBDC7838A9410E7B227924BED7123944675A5DBCA388B710F8A -F6048B03DFB713F881EA0F3B191A5CD989EA150B979059C8AADE403855815D8F7980CE6288F47EAA -37C1097D33F13776F08779063C5217D7408D9835AACBE5C071EA40C9AE6DF685F4A9827B828815D8 -F3A672E73A418E5CB15684EB6C6FE0998A386E124D76620446907F993BE16FE5AFCEC681F585601E -18182EDCFD3024062A3082AF97E803C47D32229D0A24596CF7E03F18229FA631175699E2F0D60FC0 -9C4F1954C5D12D03BFB4395F0E5EB6C6877083807D91D93CA4177A6B5A8D2AA500131FCB670E7118 -73F8A3C77575EC93A3ACBA37EA117DB268CF10D04AD0F079484DB124F6DC14A50AD3B0294F7157D0 -837D8F9A6060FBCB385606066401708C041594E0396A0BE4B8B66FEA141CCE4BD29366A986ADB98D -9A6935C49C57F8CD415E93FF8AE0DF75E463E02AAC68DF064C1B789B685F84E15E512404E065A39E -9E8F5568A7D97671AE1602605FC7E4933975189837586FB1A55007FBB0E91382A629277C36A190BC -85AF49EF3F0F38D4ADD2B5DEE09916B79690EC83473C63E92CF617617A66DF472A49641DA10654E3 -AD3880D060B02A4A6C75B51E4E9917A2B6D8EFDA12D59DE5A8E222DC7E82F02F23A9D3DBF637154F -719B14114DBB102BE5EB76B441D7E9990EF6420C2E80942C8AED5A1D0B19BCE115B5929AB9E145F1 -496753DD6B1798324F5EC1D0C7F26FC3045D7BB46A14110C99BA07A45EC16002CB754C0BAE7A1A88 -EB387BB345FA70B0A38AB4D532C2DE49274D4F86F2582728A2CC54B4C09D26C0CDEB8FEE6A42885C -6207D74953CFCC583ED82DD7C0F29D35BDAE5BB251B8A2D4B1DC97E2264DCE035E359DFBADDE84F7 -37EA6A59C23D1A64D963E635769233624F7682EA34636B595CCD064AAFF3887D916867475731BFCB -F7F96D5E5E1FBE6AABF454C2F504EA4E8EB382911560195295C87793D5F7739AD7EC7176E126413C -D4D1058EBD7D6EBEE14BB94A1ECF28B686411D91E07373E891F78C4C0A05D2E8D90A8AE2614F7FC2 -63A762D0F43485473A54C31726F8547701D4A38D20565ED1707847AED9C805780F062B847E668E15 -565CBA07A72B0BA99F03FB57D26FA26FF579C30EED0AAB6FEC1B5DBEA81AA88F16F0C9BE869505BE -18C1CB79657D91D6706E2A3F0BE9920655B93EBBAE2B4D0B5DF6BE622C951F2CFA42AEDBF7AE649E -2150FE87CDBF5C2685EF36051080BF39D864573A45AE2648AD97662B1F69787031B9BC43511FB841 -55ECDC3D91E2475D072BDE6A5207ACEA1E0D2ECB1DA8A1BC4BEEC335A5C7102963E84B97BE741C44 -58ACC3D72A7E53B1F08C955F33EDC3A0DC3E7308270C0F7FF814B111459985733C62E8863625A551 -837952F3CBF32ADCFD9F345E14B585B23ECC440775310654DAF7F41E56FF45F89701292019A94BF3 -0EB2D65E14B1A1D6BF89D4CC43187ADADF3F6E03A90ED01E5D876BD3AA56E5EE84DBAA4DAD9824DE -9984BD45AF96FB8A56C010B3C3A3C6139D58E9D69D9109DB18561B55EAD6452497840B9AE90C749C -155B6329716F0152A7AD52DBD0B8A25B9995E1416681F38FDBDFA443879B5C4C25AA29E0DCC07DE8 -BB161C36D76EF286EC88D57C74BF44DBCB4FEFF771D3BD82C8F4E233357C48E516EFE3DB9E60EF16 -8E2C45B54651DF9A5ACB5F1790F7929BCB16CE5E9F6A43919AD287DBC8E12D9F9E97E5DBAA592879 -1A5A02D39D259F3CE273A870906A643CC18D86E23F115D2A35DE6926053D8C84B940B362E7DB183C -4905060316B269223DAD309EB5AC96DEBA757BEA45FA3100F77F4765334EDF3D659E09BD1A5552DA -492BE9174DD406F8353A059ECFEE3709422940A8C369919EE1F22F7C02412C995FE93DC4559D32A3 -155DD22D3526D89B16D9ADDC30CB7ADA6E52D62C5F2DFD142D4D7B6E066671EBAD08F54917E31704 -1F410CFD8A3243F8B39459C418B7B7C6494551C6F6753A94072D09E0D812351D62916383C6E061F3 -5ED864923002007E626089772D269B298DCA2CC1F25D9BE43FD8AD62D554C16AFEB7EF6E5DDA66D0 -5A810F003CDDCFD2C02FFF02BB61344968091F67D3862C1499409ECCA137B9A2A9BE314995B818AC -CDAE27ED4AD583BE29DDE4E8C2400C5F8152C85709AD2A4737BAC768FEB70CE81A92C9657DDDB2D0 -BCF9169D272A063C75C150ADDFCBC2F5F2503DE3D13231AA8CFB396DB38E80197A605F6BC20EFA1E -DE40CF424CF221218D51BEACE64A3DC88377E4F3EFE43DB4F4FC0803BF61764104CFF0B618C90311 -98B094E20B0FACFB94240B438B67BA298E31D3F4E31FD190E48BFCE27B1BE29D36E765E7D295E96E -DCE09094FAC43B87E294818FDE9363FC7DC5EA36A1497EE25762D02DFA00A9BE53F87ABE62E52ED6 -F59818FDFCA643042EC13D670DED1980413950EE43372D31AE2694B83DDA42E1FBB049F7E7B7E69C -93FFA3195A2462423DD2C022E5141783FFA07E192AEBC5070F08B23AEC9142EED56DA74F93BDB504 -78DA55DDD0A9987FEA131E4CCA0EFC51064E4B37632728261369C3FEDACA100F1AA78FB718ECE7A9 -F56296C5FB43781E63F36B0E1D34BB748EFF35E1953941F94D1A9B0FA474FD68B47183F2AC53A63F -9F1D30B9B89C5FE54C3765B43DB403D57994701C133E42B950D9BB1CA202F15B5E590EE75598FAE4 -3D5CF1546572770BBA9A6373F100CDC61DB4E5EBBE0A93E0E51C86005E333F69110B1C8E492F2BF2 -52CADD5B73E7D3EBB53E759353F1EF3C9B8B39C230D13AB7158A5D92EE4C452F81F6DFC18803280A -A023832FD0DCB482CE5AF615C952BC3F7E58F6417D69775FC7C0D5B405AAC632857736ACF32B2EE0 -F2A2C0F3B3CAD483C614505BE94706322F2A2830FC5AB592907D0291ED1873377E7A6158140C2CDB -1B0E27EEC9CA50176102200992308045CCB5A169B61EA0546778B8D280737319046716604945A21F -2A1CB9E15E3A5DB31E0FB5A3B0AFDFDF6F3424B7536D473F9756CA3694DEE4301FB1AB1AE47128F8 -D2B461C051C1B999DBB010E78DD13AFCBBA6F7D5226D540527F17881A18F551B3EEF76A7E28B4FDD -879381A2217EF2FF9F9982E9EA70AD2003B862D7C36D57C5FF9FBEAAB56040FEE973EFC3B34D8319 -1960010110BA10694C17B7635AE03CC1CD087C0B05522A7A791F0CA34022A3F5860B536D9551BDFD -BF560A07F63AA4E687407E5E48584E689591F1B52671213E430A708C06A34D2E1D51CFA6B328A122 -007C81B5EB263B967746961BCFC8772F8502DD95898724ABF369B0877F3313A167F3F714023C229C -5757D4D46FCD9B4AFECD093DCABE52B78132CE9AB6225C9A344C4BF8D96F2C50C4272CB9AA0D606F -013B2642F8C880E08EA2822C8CF5097D2CDB64932FE195ABD5FDF36D3BE123AEDD8BA2F82A8A628D -BE3ED6129DC0FDC4BE50D5574AE4FECC65062E70F4703BFECB35EADE196294FE173EA57938679DBA -6D15448FF44C0D1A903B202439DA93C0B0E612110068F8079219AA89F435E44D0464F54833BEB338 -670BD820D941DF4B31F51B895BEDF833F9C43CB7616DB80F988CE72FD3C12C7D49F740CF85B4766C -0ED398EB837695D102DEC16E24B7475A0F5DDE88FBF2D6B94F126417C811E8362B9CCC52D8891C13 -C10937AACC228D621D4712CB9DE0BAB60EDE2A97E9292BE04E42E6D3425594DF56931A61E1F96172 -6AF6E6891D63B240E6E79E5BF30C052091D681BA1102409874CFD8EDC3EE2BE331676E31AC00F807 -91D1019BB789CA4F5907F4823B002AF3581448C352BB67D80FDFFCD1C5BEEF60523330AA2C045600 -8F62DEB55E69AC2F86369FAB1ECC90D2487954E61117A90D9269A65DFBDF297EBD29C3DD1F62755F -8F289C42A534F59650685F8576EA2FC5D26B99B8E3DCD3F1FEEC73131000F99AA9868EA9BAC0B56D -AE2CF46DA6CC1D18C0AB8D77BECFF7B89992175CBA2E22779C13DB9DF53FF5B1C8FE95E164997D94 -202C37175E562C8622989B075CDCDE173452C064274354D5DB8F7D5A78D48AD4A103B9E47500D08E -DC7C51C1F3CFA7F43C3686A3C24A7EB5018B0F419961564F87E212CE0A0741AC68D6822C7AB9FD68 -85F5D0B2AC249CB7F50E2353CC4B0A6A24562F564FBBC7090C3FDF1284AB0EC615E0B3FBE132F315 -70C8A65C814F93910AA4BB80D516CB70D2E1D11969238E6F022D628FA2F33A0A15C4EF0CE7F753DF -80A8AD9494885A1B9ADAE6C38AC9DA6FB0A61696AD3A502630252AD7B574C841117D34BD20BD6581 -217D977B35F5D04E02B933E1E84F5C090F6615AF484D63265D28517BA74BEA8876FDA332A84AEA12 -E6CD82B94AE10A778CD3A216ABC08495EF319F06AD6FF8ADD237D911F846A514FDBFAA8A1EC8E0AA -9F80F11F1CE615519A4B044F3D1CF1A17D7F3D2174222A5FFA8B39F20197FF6CAF250B6ADBDBF519 -1C525070C8D38220FB501C223F493D80F498621A02EBCCD6EFE914F16B2A435D60C0A1A453E288A5 -3D818FE1EDCA7D55A26A017F2EE47A816E90D6C3FCDF0035EEA307DFB06D2BCCE43458A67354A4ED -B6E5C57233DE4FBE41ED07EE5EC77A5DFADC4032138DA9E1B74428CAD02A913E40152F8063A774D4 -FDD4070E4B8A6C089F199AF7C529C277E902195DB760D81EC655DFFD1BB283F3C5AA8BB58F2476BC -797B2892E94414ABBE96D4DB93E280CF7DE23EB852E7CA954D6682A6F1A4BE0507884C2A05AC863D -2BA73F3B54668397B6C54DC2F4183130AB414875F3C3D8792BF7E5FC4D228DF87748BF0B14178DB7 -E3FFB7891D700A1E9520D778B095DA80E4801F53442D5C073EDEB706A5DB8466FFE7E701ABA9C364 -A37169F585C883A83713A61C9C3BD9336A667EA4E3DB5F4DF6BC6A552BE8D3EF093639EC67E5FF71 -8959F9902477F5AA894ED2D1CD312ED82EE417D95C49C96671B23FB0E1738E892ADFFE62EC1C3D4C -BEB6CD089C98DE8D247DF7ED17DFA2959D3662F105E8386D75AD308480536959F8E6CF8F2C6937B0 -9F2E8137C811327D6B165ABE46C51834A955FE8306D10033F8C2A34667F13A8BA831CCF52C7A21C1 -3DB92F3E77B55CE291F6190BB1D194A33FD73151C3F61ABD2D8A0C9BDE90E796BD996D2D0094DB2B -E98657E751BDEEFE8A43EE4501B98F0CC6D80805189438872A60047A8CAA9039893530A3E5F6BD75 -BB466B25165737C939AFF3EA59BFF4A7DB09C2A5B36B8A1F0C6C5E5870C7C9412589877EF44F8428 -4B8A53B5B74315CE72D2EAFC631BC4CC2E5B71DC958B5A6350CB5F615C3A4502E973622E3E18193B -69572DEF1D02303A375ED60ABA1BC8A179FAA0F221A49078FE15AE13383585FB45FF4D5F3BB3D0F6 -D8BF62E9BD6BAB3C9A7D38C8A5AB0BE57ACDADCBD02B1DC7952D73AEF702D406F62719922BEA96B8 -FDC9B879708E794891C7A0A42F2CCD6812C3F4DB030B5178E3A627C3E77621D312CE4EBE815CD387 -7208FAD92761A5396B67E835222609F823728B1C987857CFEAAE21F2AD5EA9D841212993508091A4 -A2C268BF1D8DA1C650F6AB93995E7C13A3F84DB55748C626FD09C0DA1E3325CCB0BF091E996245BF -51EB486680162BAE63B6513C74CE83B92359938439921950D713C69324A87BCE67B45A030C9CF10A -DFA0A82781D49FF224AC57A23C6CB321F95915C5E14E41FA852F66E1E2044A9E7B1DC3BE9E818515 -D28B2C4D2F2210098C39557067062BA4239F2AAE28816D999955910298A450741947A9A1AABCBD8A -FF3530626089978C87DFC73618C044731B6DB8007739A9699ABC354A6F985E03C11D750B8B9E9AE0 -5436205FAAD1B895B159E2C90562B82A62EA1A7FFB501767DCE2B11C51D55A17529EF5ADF0A0EE9A -96D0E7E89F68E50EED813836531B4B46E9071E84AA413F4135CC882CE832BF78ECFA7CAB0C9F64EB -92C86DFCD1152BB7D4AB33831AA0C139B555967F6346068D5C3351A7A4368EEBD2933E6B9F789DAF -37EF536FCF965C397AF1B7F98AF864B301F3F440B7ACF704B59540453678FD6C1504519481893812 -3E2F47B265EC4F5CF2172D394543D84CD4281165CBEB11349B315A85DEB2D1699507B0C8C110C726 -62EA2959C4962FF093AA5EE6F21F89B3CCB0149CEFEF1855B9A48D28BB363416C015A1F4EA1975C3 -D8807F616C5817C8162536176F464A198EBEE6C97029F15F414275A39B8219128B8C8542E9483550 -7FC2D3908BB0EC375771280B9EBE87E827811418EF93E52EF70546891BFC0FB34969FD7DEA4CE752 -4D9EEFF2B46BED908C0FB2E02EFC1D1624642EAEA1CAC1EB4841E020532E88E59AC890E6C3F44734 -B99722E9816402D1D0FDF8045C5481EC055100836EBFB48E9FBC392143032C909853C9BA38A19363 -141BED09DAF02FDF4E7CC9808321CD0708A1B45270BFFCC3A0D7C27F7E781713D5DECE82C72ED303 -86B02D14575A1A6447547ECC7FAAC1BDFF332C92984758E242256C054656CDD2C45D46E67AEC6F83 -9F95D74E222A6EAE12EFAAB723A7C816D4E42D4ED2725A794743F67597F3DB8CCDDE45BAABC25726 -B851E02E56341EBE69E4D91F2A233583EC816F18A1DECBDA4AB69320F55E730617360FCFB8AC2D2B -737675B406297F7F8C4BC370CB084C22BFEC5FEF02E9AB290282F7B153F0A4B1AE569F1E52371A43 -46A748DDE09336CAD1F5337FC3D7CF0677091E59480AB15021E023E356B0E1BAC6C6471AD53625C7 -0206C338536F4D0D40733AB217E2297F86B593717C61458B6C93A16027CC886A8CFDC01EF19C34C9 -A608B95A84B6A2E31454BC03C10FA55CDCB7B1EB7DC16AC1E93981A46DECD7E7F00638DCAC568744 -69A2D9B45CBC81398727E4ED3DB5DB31965F358D8179CBF934EE2C4D652C9CC211807F070C80E3A8 -222B4C31FFEC8DFB9EE07A94C973462254BC1B1581903EE6F9AD91524A787129A63FCE048B45BBE6 -855826750C586B6B23B805FEC3E7AAAC079576949A06F422FC2C826BDB78AE96135E9E2C20C2B2EF -F6171D610B2EB8635ACAB7C5C5ED9C9FFC26CD54D2FD4CB9E4294E178CECA1E16CC8E3FC06518BD1 -6F4D63AE2B435753538834CDD9D8AE7DE624006CE688938031336351A6578C304C2E5480A3FCB43A -8BEE4953DABC30558B7790C6E7A6F0F9FFA557C50417407AC6A0DDA1E736F7070BC89455FC293453 -3DB004AA9070734C8C2608A07330E421A0220DAB99F8A77489132F6413ADB9EA637F3B75948050E6 -67276A55BEB09D4153DC126BBDBE0DB9298AC799A943D72AFB769BFA1488D311BEB86A907EC9385A -AE4F77835DFFE4389E3D9ADED1B08BBC2B1ED6084B3D1074A326CCBF38E06BD026919107BD03BD9C -30470DB779508DFE0DC82DFFD2DED749E872EB7EB9DDF509D5319865070DD76846C34E4E43691AF4 -29AA40DB4BF2CDD50B275589987D8081F7C5A0461AA5D1455A660178A94A0BA0DCB69C3CEBF5EE04 -26D6534F6F919D9795AD6A0E1A1F452AF3B4CB2EA54D6011FA809132421D111EFC51174E223AB6A1 -3596411A9723079231B050CEDAE7659CF168C39AEA9C6902C2CD37D25492CEE00096EDD63DC7643B -667FDFDE5B595DC54F0A72C2650E1E46990584C78A5CEF9BFC3C5F88CFB0C49CD6CADD9DBA675177 -D601927D75C6902B55AAED0E9E3CB52A577C887D581B3CE6201A1C77C9546CEE5A13B92963337F17 -070E2BF9F5C5E86B84225863874618AA50F4DE855DE567BF2AB7163944ED43DBD7F4BBC0E1623180 -7C43DCB47B2EB694E6FEDCFBE26194D2D9943A1BFE32AA1E5305F5E341EA021F91532162978DD1B8 -C5295A5E7551E2DEE46DC2347C6B32197AF430AF3BB676A53BCA9BD1EA88678377DC0A9A86E2AB6D -E29E3E261BFD5573C66FB5687BA9C0544D894A759866B066E1DB5C66E60AE071CC3A1C4AE40197CD -E4EC723F7B80137619DEDC99AF57A5497D6E03C1C9E672E74F48F6C213A3CFACF2699CAE72345A51 -C71C1D69348DE5BC5F443EC0EADE1E76A8A33066922CF3869E3C1D26A3B34E540DC08EA4DA2DDE3E -EB17C16790DA4EF1A3A76D71D34B788A87838BF2A5A3DB8176F9C097D2320050A79EA6C4A94926DA -11ABCDCD26DBA09FD33F30AEED977E8B5AD928F3967F607628859429DCB4ECEC7DA3411BE35A0385 -1017B535985632639D378CDCD13B00FE537A49FD9EB6DF1E3AAF5C41EBE35721FA6833C2FE08AA3C -FFC3477E7FCEBF9EF9F4DAE62FF78F319481C3F1E72999C8A493EC6EE295316B58A5CD62FFAB62C8 -96E521B678342F04BCE1613CF7F6778CBF5227BA20504500D743270771953ACBD5C6586432F3FA6C -0987BAD33B88BC6C15D29C4B3CC54A9DD72A2357AA5BAEB2CB057CDCE72DC80CC98C62B16AC50B4C -6A7641379B766CDDF990DBB2FC7F9CDBBA755B6E3DEA438FD6699C30A99A8B3178E6D613AA938120 -835E517431D28114BCA1AB745C11FE6E52ADB82B9D3D53A33BCC49740C93017D9531ECF43831359C -5C93CB0E926DB440B139E3125CC2E069B1CF6D96EF68407F32DB517242C3AE0BC6723E560B0F45FC -7F87A5E44E1751C8B7F9F669C24AD5CF16F84FB03BA121B86B0694234D8F2C9C947269AF96FCA08A -78F736E4E04ACEA44C5BAAFDE360FCD8BA6A59724CA86160A5527FD564468123D302DB45173C1B21 -6B01DC5B6D3415B13FBDBBD3121A5493374B3357EFB131CABFE5087AA1D2C7472B0377066B3632C8 -2073C6A846285CC953A8F28E131CF587B35217EE498D9A1DB57B063CE068DAF55D8CC1771C0C3099 -9CA4FDC5D67BE4E7E69418F6334BC6149000821B89A7437CCDF9A6A0ED702D5968F1E04F7E4FE9FE -C9D1E994885CB624035BBC5426CB8EDF0456828F8EEE75BE491B45FAC192A405EBA25CAA4F4C66C0 -DC234D7B417628DA5276C08260BE512B2432256C401A66E3B583E69D23E9FD278CD5F2178544D054 -16B9B4F61A88A4728AF2CEED07C08E207F31D644E8E3BA1E4E2F9D8E30936BCB9C6AEB54E37DB46B -D64F2ECC1021336D0564DF0F18E5A6B6BA470233D8D41FDD9D1079706EA685B6D8A740570BFB78E3 -984BB155C3155C69BCCCB41CB51975EEA1C1B4294CB546CFB03DC31BF86EC3BCB1977E8F94A771CA -B09DE12A82F1D6C791FA7800E5A21DF81C9C8FCDA78622ABE75B54AEEA747AA4F26D563200992E33 -7231A430137C720A17D44F3AD6CFFE63B2DE12D3184BD3E151F955786B8DDCCCB290C42718F3A219 -1759DF76371C2FC177544A6C425CAB14AAAB31628A9CF9D71B5257AFF0D59843989CF0D747375A26 -DC9ED29B66AC2147DA0168306C48C2484C70CA92F33C0C138F92F276F5EAF5EA3082A8A1CB12DB66 -1633C2F71E3B69918F509060AC949FCD52C36498A2ABB77D139DF1EB33E3B846A7C1BBDCEF5DEECA -4EF0AD250CEA9C2751E13EF7681E8FAE0491CFA6C144DBAC1FC39D39E76EB12D3EE9CA159AA77D27 -94F0C433345B135BA632F544082BBDC9471E9FA3AED3A7D465AB7158E8AC97F68B1FBC8D368E2350 -45C18EFCCADEE98778D894D96301F903283C5AE355A863BB0DC5809158F7E108662D04A5C1234915 -E7BD5B4C30F9EFA55E702E54F87FCA06FB321507BC57A1E55CC117E21AA4E3A4DFB77C1A949EFE36 -6D93F2BD827EF8CC16D387CA82AC039F77FE995BE6D9AEFC87F8D809E90C1017803BCFA1C737DAD5 -F1A631EBE6894AD20C70791665E7BC71F21C2C3F4462F60FDE75C8A377CF49BE99314663C6ECB538 -B1BF021B2F2174D2B22CF6FAD115EB0ECE8A2E64097A5FB0A2AF666E1EE13276AEC59FD0C9D4BFF2 -3F71E835984E5EEEE36490C54E077AD7355DBC98BDD37DF29B3DDF8C55480B7349C4D17322418705 -796A8C521FFF920DD11773FC44FC631C7D6E9B420D7965D7F62EC7385F2BE30A51E2D796483134F8 -40AEC71FA19ED1272C27F98F2CDC9C7E54DAB585AC1703ED08F5F9E825564902EFD08EDF99DFD494 -44C21FA6BE16CB8A1B6D0C8A5ABF80A50BB8D055483176FD0AA07EBAEAD88FD694F96FEBD60751E5 -C4D8F9BC747D4F4030BCDF9B0370B7A5E0A6923FF60DEA16EF47F886F10CCEE6956ECF41A21F7C59 -6F3BC78299A9657266807E01762B2B2878E551914CA312C2A68D34CD91E4F5115EA1FBE801346E14 -AE529049089B6B0273E258785773A9CE8E4B6C4211CB7C2767319576758F811CBAF3A3FFB41B3130 -6C49F3798B698A47BFA2E3CA0251C4D90C0B02ACA28C611744526906791D9E157E54CE4E1BCF5B68 -6990BA8AB7897D624EF00EAB92CBAC255AE9177DA9F0D86447D35B452CD2F337147B5D3EBBF2B952 -35778A72914EB3707EA78294B3A3BC4ACB19FE87C72AA1D982E4B822F07B115CADF4D3E7EE3D1BA7 -08653BEC6F0A352A0C33252ED0630E7274961896D461EE8BF523D5911BAC1C8AC763E5FB11FDD217 -4E1F129675969C195476C7A5E18A81BF9A11ED9F2336D5301E3BD32174ED5C933E8C85D6272EA218 -52A6F7E2AAB174E0965F73E0EF89E906BAFB181DBCF8B1F5AA0C12D12C6272753C016AFEC2EC9F95 -41B8757874D6F2E061ABBE8B29281677246305B3C41E90418426C575BAA216CEE3C5EC29B2FDEE1C -77C14FDF940792F48A56AE80AA33E370B037CB28A7373F882022AF378F26B6006A049FD3B35074A8 -65C97D153352ACC156992C00DE26AD21C982C71F0EDCFEB61593BB40FA5F2CEBF23C4FF34A4F4BDB -73CA273C269242D1C6117262B7C47771F2619FE5710855134A80FA8F92BB2425CF88940CA3450F81 -234ABF2B11775929B12CFF86442B2AA0F4243D324A5983E5D1829775B3C7A111D5622D1C4E2B2A2F -982FC8A95F789881416DCB34950A393F4F1720D2212F3D343A17683060182355DE9E4718506D76C9 -184F8DAC55788D7E603CFAF4907DDE965A49C323DFF425FE88C09AA4A4D16283F9B14AB9EF1BB885 -A954034710B4A9DA4C88A8A0932B18D139A687303EE562EC9F656F12F3E8F27DAA9C75DB0FA946FD -0E1A982BB58E040BFC0A49A4AD8CD668493FCB573C849EC5474049A693CBEBD4D79AC7515047CC34 -7A9A7570C90861F3ECFB57B9F53AB9C0D6B05C8C570A8F3C04D58555A45524C98FF091B8F8A422F2 -E0E9E5A7B7FF69F1CEFC13E42F1CA276BCD584516D266BA6838D5E9CA9E9854F50C7D92CAED61AAC -AF758A7C7BE59C3BAA82BF32B691ACA3E8EB171E08AD22C39FBE586A54E6E4DE2CD86B31138546BB -8DA5834B2C6E4838547A1B67E651964E43988C8036931088904BBB589CA901E7EBBC094C0DA81E09 -1915D9E46828AD8596FD0FCA39FF12A6C27A359337F973809E81B2E9E3D43B3146F2516667E607FF -EB9AC80FC95A7B7D4DED551FEE0F3561C70DB2D69ABA96673E39E3397F1C3F8FE5F48BAB8AD6E0ED -8901F90F6CFF24E80CB5DCAC498506C4D01033E497C1241E413B022227A3264DA68BC3F91B35781F -A2D018475C199F43CBA7D3A0D5697B45321BAD2C394B207136E1E16B41794975E8903EF2B2E1C33F -87CF72C325C11EC0B92FD3890ACDF60B521DA32596763BDFCDCA837ADC6F26F129B23CA32F9CD39B -33E64576970DF3C05B8DCA4BFE2F17E6C5678B84D69494F1DBA9FE0446AE6AFEAA1FF245C07916C7 -B7569E6267C42B459435A1D116CEC665B311E404171774C0ACC8DDE96B0D9167C8CC7D99C4240559 -2D745C4428755500EB4719340D2FC6BC215B67823F69FA949C08B5EC985D7AA87C9AC1F9BCC8994C -6CBCE6027B7D1E0C22A83A5DE61DBA05D4AF6884C95F46BA7F253E0B2337E312916E163CAF9DB2EC -56C5425990FE73EE53E42B3BCCA1CF642F02B0C5ABD529B568E9ADFF865B9DC190240AD78AD226ED -884BED3C285B4CB0E3929E805C67F1318D186504D92085764B70DE6AB5AB6990F181BDA50FC31262 -348D980EC76608CF08176C2502E065AC2D8EA5CF9E2D44E2B70A7DDC7B922047C471DF8A0B2087D1 -106B5BD8A830EC0E53223CE3C96EF56E5541191167860EEA58D696EC357EC55799438C90156BBF2B -13A0D5C9EE93227746654ED73EA5B9CAB61DAC5BC690F89C87FECAF9AD03BD39E438F43B81D39E07 -E0422F94E8B096AB38C88BC2E1A043811D8141C1A35DD3A6DBE41620E83C8ED3A379CD80D4F9BC30 -41BB44B933DACA7C5D4427AE94A176829F24B5968B713431CB8BD9F53080832C6B784CEA9B515687 -F121983EB9D9C9CE8BD4FA3BEC48AFE64E643B7BD86D8383D07521FE5D091392BE124CCC91113604 -3824B686988E7C83AEBF406D2DA88FD952D0FA9327F4AD04C55FEDBFBFA76ECAE8A176C516479AE1 -467125B7EB3C9E7C5B103BC0C470946346DF271F8EE19DF7E3FF7478C35EE059297F4BF21A5C7B95 -993BE6202E897776952A7ED0613A5CACAFA731FFC633CAB62963150E86EDAC796026CE02EB235B9F -7A54E0B0C5281567138A612BAFE409A818C216DA8EAC5EDF9D1E3A1E3514AE50735A111B4D2AA083 -4EC6C11E290D58FF340F82F0E079F1C7B3566F2336EAA45BF72BCF88569988DB5F65D4C1E59B50F3 -41E45A899656A0B522847ED567B49CD5284FE50E5F8652CDAC1C076804F2B2185F6A51ED19DD4941 -2E65A0D2DBC844B75E2DF71B009776D9F97A4C6F786EFFEB87A307FB6B912BB659DC2BCC6D509A9F -BDE87DE8D716040A8551B6CCFB7743978AD992D14D2B85CA052E87326138DB196C24593F8F7ECD6F -486F85D1666B9DE2ACA6C7900044EE369D223524664A2790B773F9EA26E0A4CDFD709942A44298B8 -249506EB9B77BC887DC0EF947DDDC7CB3CFC6B48F060DBF032A11884E6C226D9D447A5A458CBA325 -D57E144C6DC295262763E7BB8FF6A0CA473EB7661C12E0E8E23EA37E8AB3387B9E54686F3E57765D -4067E521BC1AFAE52394227793C737C19208803F2F2DA920B553E2AAF94EB992AB17E31B58C15CC4 -AA8A1B444DF5B3E7CD937CF03E1F7FAC63342731B4589F16939D16E8E497A74CDE5686F529E9495E -1603D74875288CF53271DB9313A4511B104F80B179FCF213558970A002E945281BF3AE51E668DD6D -13D9E85152747F562CA0B75DDEC8FE9FE31F8D05B0F59E802888A7A4F19B29954A31108D2F041367 -DEBD6AA1CAD856BDD1427E9EFE89956FE28D500CDC6A0CB80A76902A08D0BC6705583243F1DD8020 -749B257EDF4803BCAA653F7FD6D8B91690995BA5EA3EE92FCD367C11601C6B8ADCEDCE67B16C596C -5D200693AC5FA15D4CC6CE9DF7A71C8A925E99F5085313D60FAD25C1BBAAD28D4AC2B69062D68F39 -0530A976319A3904CEE44DC9451E441AAB4780425440F8C499B81460B5D3E268974145117ED843B1 -71BB14AA84C3A084A7D8E07B9979260675D5CE6534DC176DDB60DDE90F6A3674F67462EF78195F8D -FF74FB5882B079DEE31FE92816F16CE1A70D07752EA25FAF5000ADF79BBE7D17EB1BD2F9BF6CDBB6 -F078CAF97986442680A8FC4121866F9CE86C385DE34E30D8B9768A0136D9EEF79A4B38EE99CBB9A4 -D32316564C9D56996E2595753EA71BEF684834FD030D38BB100E2332B026B046316A53270A96DAB2 -182E994E91262FB03D1AFFBAD623F1689228409884F91DBA153030870A7BEB2C7EE2DEC51875B137 -33B7929041F8D23A94904BD54DD4BC9B432DD0C78DD81639F46D686FFAD39AAFBD1B6C1A37E248CE -48F23E12464D5379B4AED0D50B5A41577E6ECB75270E9AD3EA7D0FC09DAB271FB18B51DCFC0069F1 -5D72546E6C51049F3425AD005F88FD7F02042DABE9F097F9D6A076B30D8CD777B1EC12BD163FDABA -5972EAA61E3C87E9AC007A052B1A3FFE14D7D43C7A0ADC89B1DD4CB4F9C762A84A6C0701494B2D8C -4E4E1A9245738BE4111805C2F153A20ED9FECF2DCF4C8F7C3BAF84D60454A7403D4F5F81C6404173 -A7BA81BB0CEAECFD493D877465DC5735D43E3102CEC57B8A589182FC65A4704661A9E351FCCBC731 -5A87E62F65D24EEB9CEE979C6E10DBCF5C162ADB926EC8CC9BFFE381F6B8A3AC0A19D1631BEA2938 -731AFC99E8EAA39BC75DDB3A39D01AD8F0BC1838F4D674B9BEE9F6F7BE4D9C8BD97E8D171EFF330C -15B76614A1FFD25B3BE19E4A201BCC850F926ED51616318C965AD2F0E56F9433B1247C6D5B72EDF3 -D408A3E0674A509BF30BE813A5E669D72B978794683CA8B85E3469EACB167C30F7666DB5E081B81E -E99ECFBC1704B9646B1A29E4A4CE5654CA8409ADD60145DFC54225BDB8485E39CC98CBC3F38FD0A7 -97E5DFC2099452A2418C6636BD2D5F6B24345ACFA65F4E7DBD2D0AA0C1776A4920B4466C509BB5BC -7D6627946C4DCB38A27098B7B5BEEDC2B3BA18F927077F71E38644597719652037621BB350BB5369 -DCCC073954026E6438FD8393DDB3630C4473F06D9FB9E422E435566C396B12FDCD5605DFEA232171 -CD8EF298786806E9159B84599C26D4C7D8C3BB064665CDD072E2083190372AA808B2268B3FEC8878 -B6420CA829BCF995DC20E067EE6B8E44D2869D51BA3AEDD1763F7F8D2CFB8EC41E6E9E0129DE5343 -1457960CC51D546B10B8B6CE08A1C2B79FBA448DF9783D815608A16C55E589DCD8EF6B04C66232F4 -7A473973A35618000D79B8173258B7365C9691DDFE47B16EEB08B28F881828B946FB5D6FE10ECC6A -FC4EA1F762E90B3320403382E42AF4885B183AA48DB5E4DFC9A54E0B4FFBF7C26EB17A4F13B4BB93 -12234434FFF05549E7587BA0373ACB3E31418BFAF400D8938FC6466B94273D1735306AB912AAB13E -31DA3541C1733E2A7E4DA5B82767D37F3084AA7A7C488CDCA7ABEF77D19E42B4448ABBD346E9BC28 -8ABC4540C0A1CFD0BF46C5BC7454B25E27E9906A3E6CBF678BFECAD1B19B4E42398A210CD567EC35 -FB115D5C0DF0EEECE593982056B0E1D14C292F70B3E049984F8881C8B477956AD3140B4AA22256DA -AC0D11C4126808B5B9F922BCC5F24A77FF352E2C621A3941AC07A20E550A69C49B1B87D116EE6F2F -970918F0F1A501166AC4423FC212E4EC8039AC7F9C212D864F418CBB92948FBD588228108FAC1AD1 -837070512305C110F0FC3FAFE6E1529C2BD0DDE868A9EBE5137DFDFC5C12A3D08014BF0EE27B1080 -02AAD6B607F5C5C0F1B1EED3C552919C9A2E97204A8127F97B1066607ECFB47BA95EF2B51F007C29 -3B2F6A63041A9C1120D9CFCD5357222E5B02DFC73CF94CF9B5CB00EAF073E9BF253E30E09B50341E -57BF245A746EA31BFFD0B00201C34CF0881BBD1006BC9BA7D420A48E53686B598BEDB3449924EBA5 -8D5DB1B1B01AE2BA281D5758C99EFE38ADCE18F7B182FBD0D0622A6EA497A4E7C00C7D17299A2765 -EFD8DE376C214D01A21819451FC04A0277EC84A151FF93903D61C78AB7886911E36E12526ED855AB -43F6289C1890222602B8EFBF15782B374AC1E580B6E963403D6D15A051DB8558F2E61C0B9476C6DE -5D4861585CF515CE951732F20D32969F39192FBF1690D242AC04D47E0C53D467D0FE4656B9526C0F -7F852348B0437737CB0F29ECF9B54A5E17185236DD0C16349C3496F3ABA569EA20E343F6D771210C -39DC932DC65ECEF94575C6E76902CDF6C8C8361F9C757A2577DA535187FD526699917CFE0AD438C2 -A758727B306BC7979547E68B94E87ED820614BDBC649D469EF6B4E4E3DD2EAEB5F80B22FE576CED2 -56495467C76A75F589460061E03F3A1B065121A5ABE3E2C51148B3DDC9F624C97889AAF7FB84B158 -C015EDA5670746C6359D27B0C2BD65144F2B88A64331816DA904572BE398E015A9924218B3EEF951 -23AABFC3AC8217B7B4F691219A1C9DD0A3EDD5C04E63ACBDE71B423522532561F4B71B7028415C34 -37E346BE728A415596AB749015C1D59BD8328E39A850CB98085B34B57FB52DD1D154F98FEC49B3AE -BFCB1672762E4D2A1ECF02787F59DF1EBF2625C3631BED849B298C6D226BE4E6EA2AB66A287D2BA9 -2A6C9C612A5F849B3CB3C25F17164BE286F6E4F5E7E4C9EB17BC68AA5EF0190B64696A570442E1D9 -BDD1A30E7692524E30E4B4C3DF84481DCEC6E10E7308E65DE9D90099F3FABB3F4F766BB86CC98594 -6D2003E21287761A7386CD8461615B570BDA015F5EFA23D18E83C325EE444EC166A1A32D9818C2A6 -5A092D44156C06D3FD079B92450B8A491CBB3529DDAC7D95AFE8EAF33777FBB265FEB8A4B9AFF2CE -CEFFF49AFBDCF6C4197497D3B448866D70EF28D8E4B17E7CE95F43F64BB48C4A73EB84B26650F62D -3E5199D64DB0B5B87702650ED0B850FD5D16C848D096E4C7E61BC63B2A3ECFC099CD713E12C91A65 -77A88D6F55D348617C7A49890A86EA8FE2045704B5ED529DB128C9B19EE129E5FE6498CC97087F6B -DE96007C9D01CE9CAF75646E5A5B32BFEAD9362A52223D746943A2D09C536CFAF78E601BC2D2F0B7 -63AD722E3A7AE7069D65F9F2BDED7278511D0120F5EA071D41A69F8C2A2D720D3B24B4BE61C83FFB -EFFAE21B0560A6FD1A44E53E42E0D10E0E93F421A8A7E167BB65F0D7F1DDE2809FA3CDFD931CCC69 -B119C83238C1C00EC100D8E7AB1C7FB02EDE97073C8A5860371A8132BE391EB1C397B61F93876FEB -438C288EF2E38DDCD182A5CFBBA994A94A1BF818312CD8234215FCCD7C240A15AC01A885E1179E5D -7D6305DC2F534BAA141F25EA6A5F356486E5FA0AE3C6980A9F5E8E99E7AE5B95AC42977510970245 -4FC951E4319AE4B1DDC9B07D0998372C0A95ABA6985A4DBE6DC633154FAA30ACE689D36A7F17011B -F29CEDC58A6692A8B3B0A5742E6CEC2F69B255BCEDA762DEE72F125EBA98891CFF4D88AAC14188A1 -8D81424979C9079E44890D94EE094D4CADDC1C7AC5F6791FAB8849CC0240A579ABD800EFE3AA4EE2 -F78119A3C2806C05C2B1F17940BE73984982D1C0065433A9BD658EA31AC819DA9A11B87475BB565C -C294B6F302FE3F7752ED9B963C5279B5F1196762D0E12E6DA46FF9A0CADE3876D7DF695D8965CB4B -47B351FA3F759811269376B2C3134403633FDE27C9B024F6BA81F3E1699CF64A426618428BA6C326 -6BF016C5DAA5FA4CC82FB6DC23FF2D742160518CD3A65ADB38E53F1067076CA1625466E0C64670A1 -564A54CE14DC5C57D24A12283FBCBFFD0FD594AC2A56EE58B552F7586825E4FB1EC23F8221711692 -C8C56F42272B87EBFF3865191F1C11943BB76D8C0CFC53ED452AE49404D2C8193ECC2A7BB8CFBF24 -870ABA38D2CCF7869E9363DC0AD94FACAED5922B324DC3B6FE83E7B34FE29ABC1EAD62B49FFBCB81 -1ADBB5148D5AC2743E3A058386036FADAB6FF071BC1C3B8023F908B6FF48DB0AB1C9C67487C35211 -D40995E1892C8B66AD6C9C6203F6F8B513B11117B10DA8725AB45B4437B5A88A96AF3178D856D601 -196E8162868A83DA64E408FDDEBD14D6591881EA652032CF2F88B3FD6C0479C8F89AC68D14D01AF0 -CEAFD95AD146E68FAE01A07F39E7A0C5E4FFA6D6A91D710827CA5ACFE7D1F946A8D7B67621D60F53 -41F32C12A6EFB03AE5AC5373A382C044A276F6B41C173D0AAAAE0C1DE4C3CC71EC2637225CCBFBD4 -5EAB92BF39357C57195B410F74283585B12B926438AC72AFADAAD2D0FA2CCA728C8E86BD3FE75D47 -B8BEB96AB13B5480F7A3D5741EB51E3E40C21FF2ED7D9221D9877C7D1A8CECF394E4023FCF8C4EFD -B38B839499FF5CD96A46AB4FDB46F35D3B48B91757C0159328120E93CF1F2739E936E28908FB1947 -1D3AD7F6F1AD2BD1EC364986A411CC1B547D0CA104FBC10B1CA7B638A60E75485574034561DB345D -DA68415146AAC632DFA34769B6ED7D7D4694E92CBFF4EFB16B55495908102E85E827FC623CF1BBE6 -A13CBF64E878E1A2A159948B5529B75E071744A5F0E50DF18C110B0AF117CE7F33F8C959D4C98CED -5A9D492AE6F56DA57B0F17495DACB130660BCEFB064FD8309D965ABE8D2BE98F6898C1B7A39CBBE3 -E75DA0FFEF6CC3945CE76DA3BE915546FE8A5310130AE0ACAA9AB73C7E041C00533B4BC7724657AA -649B9388B791AAC5EABFCDDDEA2CC67A0FD0AE9BE37DF9AD40636538EE55A83F60E9E026C64FBD8B -220CEB46E67410144A520FCEACA252E8165448F84D8EA083C793AD09B90B3EE83B73FEFC3365C729 -E3C738894B8C01C2F8AEE0CC8B114E1175EFB44CC4C6CEF5C8754B1CC7CEC200AD8BF1189D741CB7 -5BCA4E88BE959E32216AD33F674F49AB20A354CF3969F1611A95D3934E148831AE7C81A7EBE3C524 -4F743E66A82E10D16CC09F8194EA7A596BC5981D833318AB4F7DBF2ABCE543E410B649D18D146F01 -486159683DF61A3F880F9B21EBFAB77E908C6CFC79F89BA5F51114F0BF7C3CCEC7BF0F3B057C3195 -CFBA6908E31E0DF10DF69163C9DA7BABC00E9A580FA7FAC202910615BD479BBF76FB8068630D1EC2 -1CD2926D351E869E16C2CF1E023CF04D4FC61607DAEFEEEDFF5593E6023492F00029E2AE4B4A2C14 -50954EFA2792F32B4934A768F892171245A1E2F034E2B9F39833F1B331A19A386BAACFEC8C929BA6 -B67CD8922BBC9DC005EC3976575D5B0508D0717C6BF11123EA36D8FD37FA77A6F1F5AA84D4AD8D25 -B2C11D1877A6E2F9B74F3B5829FAEFD4F7209CE9785AA6FDE68672554A6F29D8BF03FE108ED90A7F -58690FAC399A8AD3A26899072B832874DDB629581A51B3325CD9EDFD49E890EA8959DB937DAB83C7 -77F2A426B967AF5888C33A3635B78D647AD6BA441E222C958EA58D61945F781D7EF409771B89B202 -42AD7D07C2EF592CBF413C5FC89EC30FC9EBEE4BC63709AE33B65EE3091CECBE610B847E12C556A2 -79C8B114C3E460822D3330ADFD72BD69F54C08A81848C2002A08326CF3B09B1305490D35AEE59179 -08E1604ECE75BBE811A715AE8AF7EA9C371B322D0428EDF4C893FDEA607E70E1B6F6614947326101 -EAEF18E29BE0557D2A92CF1FC1505E8B434BC368CE07CCAABC0774F8A63E1073FBBCEB3F4052462A -A9008A1E53F188C9EAE339FABA74AFD6D60F47282CD9FF721F64BD51787F3C13B5A6C5A5F7861171 -0111F5E0471E206D72520F1DFA465F4A23C71DCF99A04CEEF11B0E3BDFC35B7461A60753D3AC26DC -50A5956C9195A4F5226388E0953DDD03AF128A98F03BDFA0602CBBAA20AB9ECCDF7255962A332E16 -D4380762E498FDA4885C64FF5F9B480DA487C58E78943DF62616E6E2C69EEC8836DFCFA9EBF58938 -A878F3E792E8BD8C5D6DF557A5D82018DBAE1CA9C64BA5AF8E21BE1B6680FC5DB22422220B776E9B -A0BF1ED2B7212F8BF111EC8C8C77B223C05EB5E5F1CFABD2D037F4BA0F9503E2CD83F4519D180476 -63F09E308883F5DA5228F83045FF41214D2273B2FE0A9017D5E0557BC2A198C35D1E7E81F7965444 -5760CBA1D3F05EA4B90658E53FDF0823BDB1501ED51DA75C47395073D8980D1E3504E3F67DB3259E -4EE73A87CFD96F84E221796573958D364A51E635FC55478C9CBF9AEA16B7D8C25F2115CFE4B7F598 -54E24968833BA0D64D1D332A666DFA2A3FD71B05A26BAB7DA382907B13DE0B80871DF184D3622B62 -3D7E09BC32A4F6EA2E6DA450A906EAD36D53FDEC7F83E101FEF32F4FAEC581B000686D86A0D3861C -1E67F18A4C4647F51F978484D9E3100B37BE9D20AE84C085461C1FBF929C669E936659050C2627AC -1B019837BAA75757F5B0A82E8AE9CF2111931A38BFC94744E2FDE3F8710342AC615286E4ACE7F269 -743AA05463AF537D9416230ECCA859D8C99B7C6E70BE7FE11DB698589BE9E11900C8E9582A4EF5EA -94B5F62820C90DBC022A620EC536E06CB8BE7526A789996D0E741AAD980880A33800A6FE92286CCD -02C9CB407EB31FB95D9C9F4AFF38B37087AC582C1F7B64A7C3D2202BDD62E9AEB31BCA85C4CF323F -03DA9D318B91F78FDC0D266630F7444ED068B55C05461C97552366A82C2E743CEC353D51028FDCF5 -403B3B74D379B82EB69C4380ED40239E15A86B2E5C860891E26781CC111FB5705E3B7C7AF1946006 -54B5FA1B5FC54FD0BA43666E7BABD2C91C859F393ED49F7123EDFB648A3D6152F2C17F7E438C0A63 -8968AC06B4FB3F77F64F358AE063820BD33F0213C85C40E4D97ED100EC2DA1C2E1EA258BF107AF67 -5A9D995F60BFA37222B9C2B325C0052BB8537D2B27DD43A129C7E8FF42757B3AC9B447703D382108 -DA520B8B3BB3E8C7295B776B44ED28F863B8E1F81B0BD1DAEE8A171525D09D2620C04DD3219D880C -2ECC79282DD7B1772A9CBBCA706909AE8BC7798E6EC7375189B6CFCE8A875849176E5913B85A18FB -197A33CA4B5B4058603CF1FA79A56856B43D538E9ECE117D99AFA73B57E307364F553644DE01EDB4 -6234EFAC13046B6E047ECC8F63942F20097AD7ACF0A45C0501A95263DE9439A880D6B5C5214D2918 -0A54D7FE9B2E627EF49E189B59FCC78745E878E45B46C0A648955D3EA8C935113D94F92EC963F66C -F3CF3A526BA71CDF3CD4CA69EFAB08B7389E3390716892A4872BD29DC1E0889A42D7FFB4190E9A8D -05D84EB9C5741BE6B02716BC75E0106F5F94BD3778BE985E03860D27E44088C3CB2A059DEBC420DC -E3A8F4087A9548485E616C409AC400DD1C411CE4B6A229D091B253EB68F06E43511EC5AA6ECA4D6E -4818D6AA2068DA1AEFCA377611BFA816B5215182432D5683294D67A7C1FD76C52233087CA44943EC -7280005E93145F5E7AE50100C18364E1B36741E9647C4DC1F68A58EC44095920FDCF05532F603717 -80F78420077EF5C24D63E26040CDDFF8DFD65D871DB943F50CDE84900C1372EF33FD8AB9889C82F9 -4F61A0E6842219A0F39EC7B232CBF802C4A744F33159432E827006C7CA77E480A48A9B0E6A876158 -8A3102E3F98A77BBD62A3A23150FD140D3941773BF7CBBA2338FF37B9EB640558A2313E8824E8E62 -0331568A9B76F4897198A709F9313F4AC40827D8C3A71F2ABFF02BFD57D30D0B14012FB5C39B85AF -540DDA0ADC27A85B31694E8D7B61F9D9B476571022D98F2D768246550A877293F3FF6ED918A498D6 -A600223E1A61890C49ACFB60265867CE9464F9C32C59E94F7641C3873FB4FA6EB237F8ED94579957 -270D6FD640BD9543E683F2372CCD7B60AAD269E03A72C5CDB732B128818D41A6DDD2BC139F7D3911 -F48E1B1D263DD4AE8E4CE1A686F3A00A2CBF48978631CD243566E22E68F8D7397134A3530EA3745E -4F1EACB4D6A5FD84C3011094F37573F7F9902305020C53926716D4780C6B0A257BF711AD94C83F1D -41A02C1C7DD203A3E6E4B14EDA2FDBB36B063A3E074495F626B0EEA146D22AC33457F44F41675967 -6D2A0566EC2B726D2F0540ABF225339F02F406D4E7A62E5233DDF20AE7C86CA0CDD561F33C422654 -BF2DC3685CA91BB9D4B09AC8B15A24A99FF56E2894F11F7BB4728FE8F0F5B799F74F475D2D01F61B -7E9E0E541F7FEB8A557486D7DF2CE50927515D833BCAA1CD9BF7A650BEE9E003A5951C98ED147C4C -52F64F692AB281984EE65A47E44A4A5FA93D6F18D276D3B01C5E5F6135AC6940524CD713DF4077FB -4943E8AC927A68489EA52ACF7A854393CD027EB52EA2DC6234EF034F3DC742D6DB5A67FC21D22B97 -146B9C268BA97C30161CE01EDC69A6A1F05EFB0E06F22644E1A368F0E2C0C6C1C832878E0614B74B -D645F5CB293CFDB7618B837FFF14A1210AA061C8C81867244305B80DAA73CB25A417228E9559E7BD -52C119B0CCDB7C4DCE7E1B9F7E8EBBCB575E5BD213BDD6DB88769DACB05E5870232F0EF82F448559 -187423409EEF756BA6247493BE24CB1879B5DD822E03D0ADEA1EDBDD83D3FC46759C679B921F0616 -F27212903F728AB44C1784E8A7DCED0DF5625A7D3F48A20FCA34008184CECD145CCD98E31B79E174 -CF107E8F35C40C19D86B40BAEE6164353408801EDF75A619FFC5B6FAF3F3A95F64795CC40C1F8963 -4FD8C13852D265FBCEF834C800AB46E3E8167476B23CDD8AFF6E2F997C99A86A9CB30EF8C853154D -0D89EEE9B9CDC1B4F27BDA32432A4173B55CA8D9FB50ACB2D886AD8E5862FFD5DFF224BA13C8B8A5 -4A7F1A9F987FBBDBC5A3C3D762A5BE309D5D926AE5093C40AA47B3B1BD828797CBB9BC9FEC9D19EE -A73D2A39764816113A8EDC6CFA6E605AD578FC8E30ABD600658A49ABCD5AC54655D29C50FDB72070 -169D1B389F114B7C71EF95A80D82AB537AC8C165D47371FC142A51625029A990A577EB1618480D72 -6DA93C98E5C5F24F622A850CDD94BADAEA91D4BC32CD50CE69E9F00E77DEA8EC1D37916398FB7092 -402605359DF08AFE7B99C76C2A7C70383F28A7C000C696F45291BB8F074791798197CAFF1544C76C -EEA8C9E6D76EDCBD92A86DF889481F3BBFF0865442264F0EA40D3CAA69AE467A08003F9C30FF7F2B -77E767580575398462D5B1171DD441D8986F33BC7BDA17D413EBB6B7A32642E33F20B284BF3EDED0 -02352FC66C6F7741A542155F4A159CD778BE56B9492CD95115C1A06189A216CFD2E6725965A13DE9 -73765A05114D9A5A4BE0615AF8BF6A5EAFF84468B849954D15BEAE1CDD57C435788B331905C01421 -B50F20B184506A0BEF746330BC98E9C89AAA8F9D102F158043BEB6A682059A1C8B8CF67B2F3D7AF4 -D8BBE086254CDE53765E3226BA2F95AE8063649F9F94BD9519411DAF8A0287307335668190638806 -E29484A4FFBC1E46B1800E03B162C23B1DC0B4C0DD3C7ABED2F00762972EF06EEB9BCDC7B3F39C70 -BE32789D366F073AC3280C273DFF2979507671B3E1E7685A9A4F0FD3867F96DD675BF05F25ED986A -79249B75F182FD73CDA2A6A66D693E4CC5AFE3402431B2C816DA1486C34BC9DCA4E2D51C868688A7 -787CD10ABB9ACA14B7181369DE89913CD8FAB58FC84519EA2AA14E54B7A8CE474F213E07CF2DE2E8 -88093DEEC937526816B71C96ED75FA9E2EDC0F9E6E84569C12BB8E39AAEDBF546630745553D6084F -F9524FEC6A7264F88CEB7EC3358E923B392474E3A48865564431662988FEA768CE555AB0DA48BD52 -6A84B0CB17B4584066C1640C1023D91F7869EF0C4D701BE121A6E3C832010427490758AED7A2B30D -6028F2215AA44E86D852FDC67DA5CCBA79EEA863BAC9EDC2535B66AB0E54EC4D4411390FDEB8D1FB -C1743F15C3B68DC92A8659E7A892D5E53872EA51EE8CA7EF51103E87C29A2714E907C79DB9CF3744 -1785D2F73A1EE58550111A4D9BCCBEBF2E39CD3B93DCA300FAC3ED1ADD8215301E5766C30C8CF296 -75746C5A77BF1FE3CD75D25CF193DE8D9AF02AF8F7A6E8F84B548058CDD3C6998ED13463FADE7391 -26D83D3CE2C7201F955382832E32C10DCBCCA35835985B9A93F8E3B0208BE6E92428787C47D3808A -0F77B8F1D76E6BF6A17FF81CDB065180E03809D03638307BD7BF5CEDBF64904E918FC805AC905379 -928B816480F6E3BDEE47042CBA98539DA0E113B1A5F23EAF1A3210BD18561985E6436EAB90395DA4 -77C7A6D7888D2377B3FC4169368357D880CE041E1F7C875E956600DB7D9B35D1EE66BE476E9DD806 -4CC02230276829C2C0A098F051502E828A0CC505AFD8C3DF293DA1508AC4D25866BEEE6BBD5A230E -9C2DCDD4F06883936381F476DDCD86CCFE15C2CE3C3243E148CBE603B8513A7CE7A6910A66A90B70 -89E5CCD4368BEFFF2BCF8E918BFE0A1B069AB2A914CA7BB91A0AC3B3C0B060FA1A0316F6135E890E -E549315897C8464496CC6DEA0F7E3AF43FFA4C3281156067582CA255B1D2E80F999A3AC0402BBD17 -01824C3BB524130F5B82A45275807BC2F3A0655EA208F968B297F98C369192C8ACA26BEBA7DC4506 -FBD1305E2EFA4DBE5375281A88EE2D6FC88FC0A755E72934B4B58F6DD3BDAF7171A4A3C776576735 -2492BFA9A7758504750AB7F38754683B70E9E293CB1CD7B23BA62BD7397ABB84D7EDB22EF6C3F58B -3EEAF656E361747ED04020163253D1CF3F905B5E85F83FFF30AB2778CAE43781667C0F65C8FD404D -6B9202A99EA76AF9AE1236631550B66B063847180B6DCA832EA8DC4A6EFDB674B5A26552A7C7D54C -2799C7D4E03C24F661A91103086DE3A90A774A6988347656344CFBA06065AB22476BB09FB68F9928 -C0045F2764AF643CFEF0516D87FDE6DBF93BAE2829B176CB507BB99835E01BAD5E55C2F8798C93FA -35EB3FEF02CFA31D3D21B030547F86D27B9448D68E2B155A65C742BD2999DAA0C3AED64447B9CC67 -F7AF33B63AFAF25F3CF7EF86657FE8F952288CA4B691D369E8F1935CDA44A180A6767560C2ED3F2F -CC38B6BD7991D4170C7C566D690A8A25BE03212A80871108D18CCEFF246623E653107631F29227D6 -4754B2208D19F84E547799E691CA473780DDD56AE620CD953D5133D135E3D51F237078FEEBB73714 -54EE633CFE238AEA63F9999E32850E6C197687A0EC4E5908D2A18C5349627E336AB5E3185B218228 -603A4B1852069F5EE849D571B8387DCE1F8F8E9FE94FADEF128BA83BDD245F8C1C27C11F2ED1A8AB -2D6D601726842CEE744EE7AAC6B6FA16CCAA39DBF5B3B1D47339F31DFA562671A9CF7DDE6915FEF9 -F19B3E068A464DD350A3AD146D1A241673B5112A4A8768F976723E6E184790C0604506C46591BEF2 -106C40789B733331A80740D59ABED39868F80BECC2AA21C400A0BD0CC326D186FFF9EB37680F1EDC -32AC78F9059280D07B5FF2E354FED545129FA5FA8F3D4317FF21E027602FDB2522F049BB545FF4DA -60248130F81F4E348373142F3148DED038AFBA818F26D5B49FC02DE9800D894E9239C88EE0EDE431 -F8083697CB0BE3B497473473E5714717C914A1A926730C249413FEA2615EF72BDB0906933387A892 -370F77EEBF62D26CD583EE643B02E323821379C0DC966407D36AE3CDF646B95DEDC7D7FD0F28E950 -78F12DFC0D6400B327B743C548A0A3517A175A7ED963ED756B1E107AE7087E2446BA702CD4E26E2D -CDC1A8B697108B5B5E81E9F03105F220C72D4AEBC57665887C8C7964089FBE9424120EFDB14D76EE -F8C6F7A30B13E1AE90CB9D93D2E14BDE47F4A1D05ED5B18D32AA39911B92D24C93976ACEB7EF597A -75161923A73B2CC761785493D0EEDC08B5AFE95F3C006B41438A0785C962B070DE2BD096CB63B847 -C87539880AA3D3FC5C345E0992D7BE77C6CFF4948617FDDA784CC55652192B0ED775129C4EA4245A -41BCF3875BE319DA0EE2DAFEFAE920CD2B6C6C2001762F88C0C5C05053025C0349DB17104360FCE1 -5D7F3A8E30ED13155A74FAF91DC77B8AABDD6FBD5A1EAF255DB209D7F2B90822296B5603FB5E2CC9 -5CBC5F7A6044058B8044ADCE73ACFD896177F1F70EAD2F6534DC3AD755AB2BA87126D63CA2E9C441 -DF0965BDDD6BE494E58D6B5057A561D1E31BD38E92CB73C1465AF6B9C001F7229059BCA4104847D1 -639E124E082F7364B56548BF8112D0EB461B316B2449049F6A476D36D6B7C0C1126C08F2E9A1246A -3B5B21E7C8FAC6E23B82E33A7783E4F31F0240E96E69C9444E7D7A928636CFD086475DF1E0A28464 -81387BB2010655B9F81A0744121699B4905AAEDCC84BC5D5AB3674601DBBB651EDE7B5DF05C8A463 -DAB41F79706D285C4F9063997F7AC8CEF35CAD51FBE5F5BB1B3FA6DA2C3ABF2B3E925581349728D6 -DA0D59C1EF6444539742EE9A23A5727F20CF9377F4F84DEA420607015A30FB14632D084A2DD181BB -02FC3A84FC499B318156B675B9CA3CCABD87FDB2497C6705FA70EBA43ADDB6CF961B30E8F6AB9F84 -E1DD8D6DB3314B34B7F7AA3BBE19D5BDC75ECADFD8EAE19E07B387A1FC586F0F30DB695926764B54 -0D89F1D854B0FF86528AD9523CAF56371E29498C11AFB2F4D5202670C834E930103F039D13348824 -16A49BF93B84FD3CF1209EEF7D4994C8302436C0794497461C11F5B8BA152BACBCC08AF8A15F4A4D -F3EFFB7227CA97FC21D2D0356C93390C749CBE9750B821F1A7BCFAE2C8BC6D9A27F844D8AD088320 -79ABF0EAD8ECD4EA72846DFEED021857F33C1ACE4C07BEC90398B629814C498D33BEB375B9A53DA0 -F926FE6E89E70322C72CB2DDBFB16B13EF7A4F50DF783316584C6AC2BD7D9029124933133B2229BF -74A228868AB30EA5C3E87C78C3F0962199480DBCADBEF53BDDE45849DA857A4FD85B96682F1EDEB8 -5384929DEE4AFAF84C51A09F5D572705673D885070303FDB47DC898F874E103A9E7C1E894115DFDD -AD81549C7375D4AEDCCE2E52C13E5130B47F206F7C5AFAF1F9EE83DA8188D70B473269CA280A6A02 -DE85300B93D8A4F6B402FB5DF58F1327470CE11CC63ECEF2EFAA396A6680A6746A20382D9529B58E -7CE684B39AC00F7086BCB47C2230DF0343BED9B9152A61C9826AEF9E00A1452D91305CF05490D4BC -0BADC9C6FCBFA93FAD52C3A80705A1956890497557C0873EBDCF61CCDD2219354A4F5621AB33B119 -32065C1D990A9B68858331EE7875CAC855F98563B14EF9E1060BEA90F195AFFF94728AE935453438 -DAB35123D0E2699475884DDAFC7307A5CC06920F35341728D85965F5BA86F261CFFCB1E29B429F97 -6970D42D10E6AF6C4B792B4384122AEF2448E22A58D3AA007743C71324EA08D06819FED14AC1F22A -4F0BE4787BC8738E1CEF240677571C65804ED3E748D72E89C94B6F310BE748FAEA31EE246859CAF7 -A1EA17CCB5B246C87EAB771E2AC5D378650191081514DDC2C66878E3766CB20DC49F630F2743A7FA -ECBE9DBE9E815A3CB57DADF2BFF5EF2FCE23A56298A30A2E052FEAEFBD698101F9DB992613706693 -CB0EFAF6F60C8BB5E7D0A50B3392B9831EF3A304A846CD4AF431E9F018FCD3A5B16387552D55DAEA -683D36257418AAA0E7BF8A03ED7BAB114D7C15119E6C71C1946BD7903C1C42E115E954619051B853 -BF05AE316E15E619A7DEE498F771E809D9435969C1056402725EF40C0200E083F3EC6E0EC27B8ED3 -8DFE32EA0E5E156AC36C4BB9AC5ED111A11678339703F1B9299345AEB1F251FCEFA11FB3101CC499 -907DC862B4463D5523B9B25C5B69F70AB6B29CFC1DF1ECAB8227EB3ED1F882E90B12080EE003714D -403EC43B7B54491446B6A3DD6EB641EFBFEF060C45E873E7398025B1CB7065441F1753028F6F8C49 -A96801C0D598E098EADC96A21117F817B6FD6E6947642F93E22425A00E8F6B592AD50B317B69C0F9 -4047386A45E5EBC9504FE55451A01EB29DDF9A41D4BAD85FC84CE280971E834F06CEF49C8C20ED2C -EAC889F158CB14A8C070900478804CFF1D1637CC880C81AA287D8382837FFA8F41FF3C9DF2F22CB2 -0044C171E4815D0D0F6C22D19A52114E780CECD71DAF63427782E85E463DCB333789F496340E8CFF -885A9D9A4250118B439C71C6BE51A9338BE29251AA794EDC67DEEC6337FA63CA9B03C1C9F75E733A -4A918646E7BC9792486CB5A4BCC5F84FBABDFE338C3792254A3EEA3D88903C2C47B91E076259DCCC -8BD3DCA90ECCC832C09C45141C6242026BFE309029A562C3EE0FCCDCD40E5CF265ED9C3DE582884E -0E14819DB98B3AF734B1B3276AC41D43384EBE73003D15CE39FFCC04109583390E470F431B4407F9 -8550E138F96C4564B494E5480F47C853BDD237E27301F55E42A3BED18FADA152572B7B465A581DBF -E7DB2619365CF16D71BF8F091862B9FCF04BF8D0859A76F46E7B5712F2757EDCE332D3213B8A30AC -2CE7D7797EEF6F30904906B0805DFA7CA36D32A20D989858497A66CE72491393DD79332003D55C09 -5A5AB5DF761C4BE5C041FA8407263D604E53091F7B6B15496245DBBEE96A63F10FC2978D99E65731 -28689366FE8B0BADA48B50185B861BAD03E3600F22BAD4274F2542B635F6C7944BEFC3BC741BDEF1 -1A8DD659038CB40FEF2E16AD1AE7EBEDB7D9BA15FDCF26355331505A386DD7399FB999535D6061EA -BC61DD76EF3EB457446F29D0BB6EC2FC0AABAC20B27A3C123C27BC27A76336D0A0A6D456DA070367 -4D959A4AFE428E2206A511BFC80039ECD56E75F69786DA0A8084D81A66644DD98B6018681F1D70AD -E09BD9BF3D16D68DD5D0A03AE26DCF1552549E459FE190B310A8776B2C8468C14CA8B1B9A7AF2956 -507A3B705AD75A17A0EEA7FE089273353CECD07BB8563465EC8DECA0EB42F43FE3664EB5F31E1D13 -24185539B28D508BCD065ED576D8814ED3FD637D576F027927162344AFB0255A91FFC616948E4E35 -8867E9FC76A9AFFACAEBFFE110808C1532A2BBB0DBEF3F010E45FFC73F228D28F12E98478B27397D -8F456781ED9E19711DF2E9EECBC3FE61F7493FDF1A59124668A91BE51F122F93DCA4BBD22DEEA339 -E6EDA3D6EBEE03DF958113E1CA49C8398D2C59DA6764882EE3663F62A55AE50A7E91B4FEAD1B11FE -0D50ACCC5D75F1A515F0C53616A500F1491381DFD0E2477E402AB0CF9F67D501A442629C8593ED5D -25A72EDB9746B02F2B0F0759CC9CDCB4C9D8B4519C8C617E569B432F0CF6890372AA879CA7DE46E1 -10D95E230A4F0E52CF65811C54365DF4A3E40D819E2FD379B47DA3233D0DEF0EFBCE04AD8BAA3888 -4F6A69FE5C373E38AE0FD0241480F2BE7CCD18AF85916D2703A049779FE7398FC47D348454CF03F2 -2EB3FECC064606957898B5643464845445C25C0C7D685C8DB042AF5D5882174374ACE90081C68678 -9BCA96AC602EB41D317BD652293EE628951875641661EC86A2C40A42E8F0813A861D41A0F5178E55 -43651CA0E99150462DB5EE0010F00DE6D55B0D7FD7EC5BAEA24ED3E90A7D6A0589761922B91A6A91 -3A7FEDDD3B68254D89ECF767CE8E27F966426A8B4FB1B4085384FD09D63E288405B78A646F44C87E -EE22C8596B13188085479F75F63D3D97A28F9C8306FD207DBFD38DEDF0FFEB7DD80B2A3292DFBF1E -D605ADF1B33E85B010309E3EC058FCD922B1325FEE71EFF2DBBC2E68DB52D513E024C01D47CF657B -B61C9734649A4AB63C0AF4720EC3EFCD82DD3CA6E80BB63BCF1B8DE810A0C6C517C63B76FE68C0B2 -86867BE102424FC31C4937048B6F323D039618586FC21731005D949E7D802A430DF8D2F0CE99F2A2 -376C2953EFC4184355E4D12F422C9E1E25C4DF38DEA334DBC89B540E14C61A7769D77115CE8968FB -76B27D0863CEA2496783114C24D4CC816DA884D953DA3F9B9D3AF8938BC607BF26A071956CA07E6A -5509EA2F5D80E5CBEB98041B197FAC760976EE75B470DC20AA023BA3F63C2876EB281FF5173BB490 -D6815604517AA1B1FA0631401B3C1A04CA103E2CA4ECCD83874D9CFC8ABC134CC0F9141D9AFA5684 -8BF222342016C556C14B3482482DCE5D0B6EF1AB522AA1812BDD8DD3397E05327EC12748FC480842 -9B97202E24E1DE0C7C0D272C046BA73B37D30930C5DE5A47D96955CB0F5DED8F3AD929A8B42D2839 -0458F5910A0F93610F79EDDB27078943DFE17C716D65F96589769349F3B66AB7B8C004CCC59EF688 -1F745EC7129865A76F9C2D029D4660CCFB4D5F9D412BA3372A27CB175E9D65F759575CF14A5899A8 -D31FF039AC02DBD8391C3397428AC0D5717C005200790785354813C8859BE90E0E17914F6CB9C674 -F1E9A9648657B54E5E1F52756C4F982DF74E73F6E4D40718C71D1D0E2420FB7462FEC9E457C0414A -96E475C6BE2C10437096FCA0C942E995A9ADA789AB637B648781D32DFB68E62E91C2CE7E13680F8D -31ECF8C824885FA7618981CD05FB335AA111B409C59EE337DF4E5F9DCC920A5FC0D620DC07F20DAD -63F4FF5E0EE5A2F390AF1C32122BA7780F210229E5A5E3ED97BC1C3CDDDD456E739CA782EDBF4B81 -0552368E9C734B0C78B0B8E3F8B2DD782862B74318871BB1EF087828CC173D7B049811FCF598B8EF -DE4D9BC5447F4848C98029C854F3AE461B9D46DDAD8CE67A521F3C811A81A396CB0F80F3C8D8EC88 -30532FB7F9624F7CAE0F8C6DF875073333DEB28AAA90AAF486AB8C932553CE697B885E71EC8E40C7 -835CD5D59A2C695DB9E51216FF9B77A15B0DA63717FF25B05B939E45CF7FBE490E51E9344213B32E -115C2DE14D76DFD5845088DE645B0E75042A61D82FB1753C445AD0A956A1263E5A096B681D3BC51A -9FF32EBAFFF7ECA8B59D40F0937EEFF38312AE57462C7BF3B1FE24D2BA8DFE84515270E09063CE3C -80DF4935E409F62EB4F54AF16A186D4329972B9BDF15FB08461B688ED49928429226CAD9F67C9D63 -6D1375CBB7B08A5631956B7FE29CC9EFA8D75C9E4919C8C2C54F401D2E0D7BFBA40C50CAE214D210 -C6F3EA5802339F63FC4C1C1995787617F3EC2C806CE44CF8E29F76606CD5836F6E5A2E423CD791BE -CD3F112F25657DFED9366FC4ADF90B685CCE4A5698E5FE16D7542B913FBC01B288DD13F43DB2B1ED -8CCB80159DBDC90A8132125DF8DF547C4851CA609D1F6F4D647741260E845B457937787827A89E37 -CDA06BB191669AC84B8608EAE132D10177F3FC384980F3A6E439B048A38D0D6B9CEF09F3F2D732AA -71BD058169D6D0F8C9D146D9DA046774027559A8B3843F6116B418427E78476AD8F0F81E8A6B1209 -8060FF7DD686503F972D6C42FD6CC29C083AC3D72E3751F21D2E44A572EEC80E81EE44C90FAA7AFA -BCD3ECEB98FD4068F6C3A4DED0E6CEC523C9A0054D1FC2A8D61A4A26F9BC250B8F302416924AB22E -722297888B85B9C12F8DFD2A744CBD143F9B2514C1CBE988D9CB4E77D90B2EFD5C2A528355A35F7C -4AF039C7D1D756305967B847D4ACBB81263D4992C001E2A262B9FEE2D1F5022BE5B15E1D8F1D67BC -52227344EE912C018CB73E5F47CED54FD202627777BB77AACF3EE6B22706FB2FA9062BEE87E22CD2 -802E7706322648DAA0C624EA885430175F746E1F536F9A8E1C610C4A761D07248426DB63C9319A88 -A3FA449C3FB8AC94C6003C745E6BAD717A3B2EA3862D1E08512A98E57772A62F85F1E2FFBA40E2EE -43AEC11203DA9CE5AFBF673436F2DB6AF85BBE89D802F7A9E5FA25A408DB69E51F0577DD26F94CF2 -BA2FC53EDDD6FBEB534AF15F74F66EF8D14E7FF77D8A5D284C8202DD5A6053CEAA606BF925992382 -5EF4EFFAA8D878652A4CAF2EE43ED26BF3590402686C876F86C1AE95046E527617CDD3C429BD4CC3 -F9654D2C76DD4102471FF746FA9FA379B16DF96BFE3836D43FCC0B8E95120C27370049ACA4AC313E -1D50D72D1814F2566B8B29FA9C9C20D0488743722A766436776783B939171FFFA00E04805A8B5821 -4D4F114F7B9C3C17CE7486AEA2BCC895ECDE809502BDE57981318A93F23016F056A421B733C4590E -34AB08BB348DA4A48F19B6BEFAA1DDD2A49A6C440443028333CDD48C85CD698ADAF3FD8676739E44 -400A98B575BE02350576F96CFA54D4184BA47555B8D12374B86D038D085F7FA51FF4BE2FF5981408 -999B48B2FAF305212ED54B2E371F5A0074CF68D1B0E5CD279BBC8BBAEF694A89A6C43F518D01BB4E -8402AADF34E96E9B3FCCAB4CBEA2741D3FD9ADF7AF32388F7771845AF99965A6078F4DA335EFA436 -BE36903E33A743C112C0267309F266DD44FA998C9A139704E400B89DAB952EECFE2AC09C82D9F497 -5371CCC27DA37890EC84123193314D8A7A707C217FFC951A547EE5B6D1B7C8ED85BEBD9D3F4B9B09 -6A78E5F7DF88C931E3F396973974454E59340CA51DBFEA1A00DE084B64630E26C6D6A3593B828814 -E27DB0186BF2A87EEF268AA1B135AC09B52CFE53051CBCC88CEC5657BD47F603C8E1A6249161684F -D9084AC279F57A4F9BBD0A546A87E147B62AC860911969A29B8AA20E3AAAD0079D64E6BF1B0F2CE8 -F0C54C9019207E1B403358253C2FA93A662F63B9380B65C5173C198D86A3D0DC1800D1F5378DA39C -E8523EB62C6AFAD8A0D7AD1629F2CECAD82B8FDE38975303768C7D3A08B91478EDB3C45A8C6B7725 -EA8596A8ED50B8355FB852FB8966479D12E1086223B1E6523A65FBA81DD106FE254F7309718768AB -009FF7714A8C363B09DDA73CD3F81BF9C0CD3B0C806CF3B7BBFAB73E46FACAD2480EEBA97AE68EC9 -4D3D79AA01ECC22067858EFFA9D7B7F997ABD2CE5AAA8781E5499E8580C405681CC63EEA53BB47E5 -5ECC5BA2A7A3C5472DF034B022F455C60FFF971B01583A29E211A87F7163187B190B0C1083D696B5 -86E9438FD8BAA45101A5EDCD1BE5AB9A585511089DDAC8DF1B1FDBE582ABD945E67F99ADC4452988 -A9859E39C90EF794C5C4E62997085B7A16A0D90107D08610BA175AD66377345662DA7DA4D8FEF847 -EE5D57E3AC54B928A0957CC1C944E7FF14658FE4A641CD26C61105C0F136A75950764B69CA17509E -3C19351D456B22C87C55E8DCC4ACD3E150D936333FF36499AD6B02B6403DE0F12901301ECB2EBA10 -324BA72B58206A13B8F37B0AEB12115D0C12879C8EA8A2EB70E85C95434564BA3DFF481C8972587E -FF74EEBBBAB14FB32B8A84B8FC42EBECA65D25E8C32C19CA5962832BF45DFDA4E871508AEC318495 -0D6DBE89019CEA29E40484C36E33D76B756255531ADD1DB24C03B2A64A47BD8FBA3FDCB1F5B96F8E -ECB60D5834AB001A70740498720AFB6EC03445CC35B51F7987109618C6C78CBE3041BEDC69B6FB12 -8142CEC5C8683B558AFE3024EFF7A12D04EF59A72E156DF11D33ABA08A8EEB16259DD9529CD003AD -4EF4137B6FF1654236473DFB93F597331A5E26C7796F528F65C94FE07B3B4F4DD49034FA0CC189DF -CDFF70C2F1C6D3DF30AE103E2AC5CFF20664AB934CE5C19693292071C93BD590383E0A1931E04D1D -DD18071DAFB628F5D7472E457BF81D6064EDFA8DEBFF91701C5038CB30865D6122076A336732DBCD -B0A625548773D0013648A76F07BBDC9C16284D158EC7A105AE37A62279419C3A2F360D0C7A74D6FD -D0E36DCA2A8BD59945A4196598F690878F84C894852C1811AFEA4BE3B9F6A5219E6628C66669DBD8 -FA9A0CFC2DDE7716A356FC4FB271D8A2CDDC8D4684DE447355BC7A287DC56852A638C5777826EB6E -B72FACCC86F80BEDDD0D649A883CFEEF4D74750172A90B5DD8252592FCFE19FFAAD868E99562DAEA -E70514F5DE296EF7B57E6F193737ABB6AA317956584423817E11664A67389197AD9F8F771EA59551 -98C9EE40A0761639E638CE9D890DF468642670235F1373D3AC6B1F43B5777FC0A91A96E095E89BB9 -FD62614DE456CE7AFD6B855112367573FD9FCBBD4A4F9C676E672D62DDD34A9BFE8311B6175A003C -D143C0DF15E4C0B48C735404086E48AEED6B6FA21FD9F40B84215DFF287F0677904E2DDFDA774A40 -19DF45CC877F553E95A1C65DF1D67BC0C60E0BBA4D205C0DA3DA80229FDD71859F65AD04506B308C -2B783839F31CFE4425263224F08C5C7E98A2C9D3DC8EA5AC1920F4E395413262E0836BC019A092A0 -DECA104EB2DF6B63392AE8E2136379140DE5FC98B0B69860FE8E31DAB5C5DF7807D19BEA34AC14E0 -ABC6F6519C51247B104DE7D912C5BF6EF11B48FC6DF84512E9F5FEBB48F72FF1B722BDC3BB2E835B -2E7CC6324BEE84893996B8DC2D4DC2793A4F69C18E63DAF04A7BB5C0A9076E2D5A343E134CC3C89C -4712900656FFC202E1988526D80C7FD9281FE47FBA8AB5D025E63A84051F6B13167BEC15B346212C -BD051AFE7A98BE3A2491F3C469718A58E783ED91F90E274FB4978F8719E92A99A1E8F142EA7E1F2C -46AFF0A2FB50F4D105130CE8EA309B0E480DC8F80D506172B609EA4BB4E2BBAE98D8882814FB273E -690DA990B60A9CDA20A2418246BD10AE67D846A0FA815AC25858145ADDA106A6778A11877FE59A2A -BE300D7DB9BBAB31CB5B960B7E4EF91D4600886D8795DC361CBDDDDE05EBD54B1941F426F7FA8399 -270D2F54C998BE92D146227270A8E89AF90C48BAFC4ECCCA01E6322AFC165743475E752F39BDAEC4 -9297290510FFA264342A0AFE2985F85DEEC66C36EB4A1D46683EE7C591A89B81569A8566AFBCA268 -10DDB0970577A76EC8A066622606B08315DB0F2E6C671F3259C73637D773D1A180AAD66ADADA2A65 -95B5F481E5F59E51CBA876FA06D21E1D674CFAB46A02D267E20234324D0891E7847C13C69BFCEEA3 -AC55F2EAF753726BCEB0DE1EECF42ADA964BF9E475953302C2FCA804B70B779482DC9319B40381E0 -9C0096460AE113C19A2DC9157FA138CF0E7758F71008E71D0F7599744D647B09B16E3C795C56EE5B -D14D8D63E7A512900D67487975EC9CEAEF69572FC3C2342AC5D365E8A4BCF462006B5268ECC15754 -94CAD9A9E7A9E8D9AFFE49AF647C017743EC7CFD5E66F4E4D845A6BBC836849274FBD270CBF263F1 -67DF7E26BA91F21C60F96257C07523AC37A2193010E976965CBD75751E312817C0564E1C5AE0CBA8 -BD12B01122D07020A0852120680985A8AC987BC33BE863EEC52AF13435B6E4048D951F5BCE36526E -07A8661CF2538F69D1F223BC53BF5896437D1BD46F57D9698F642F0E99C7392D8EE47134E34DCE94 -D392949B418D9821E12CAFA8337323E8469DAC24DADC6AAD4A0DADD7FF65694BA3A27964D28D8EB4 -1179458F91CD3F83B8F119BF5E76184DD29CC4C0718CF7945DCECC993A7A78739363136CEC7F2FB4 -95EEA8CEDB3EBF14373A058758C442939D36774435554851E9519B6F09C31EF26B6CD997DAFA11DA -91FA9759F17B7079164C5B47B99CCB7A876FBAB1D0D5D1E1A2683CD6914E6B3B755939CEF1C9168D -30B2738C4349650CF86C90D2542FC9B90F36A494C035A1C86DD716014AA16E6B9EC7AA03B16554BE -C436511DD3097FAB1FD0CD49EDAB96F74E8FD26400FC748CBD9EE1EEAEE24DA30DB6F8734B52818B -3A5E510AA5C14E42060898033E7E36CBA9A64042CF94A74E4B52E37AC027C0DC69BAC4944CCE12E7 -AD81AEDCE642EC34CA23E3FF07B8CD35DFF19F33C8D4DBB56A52534F8A827BE47AD4AEDCAD83B273 -38409FD1101C4DFF3F12D3DF79AD1FCE65B2F419451DD059C88BF066413E23DE27D3621DAC2DCC8F -9F3620DAD0F4B1A6E8C9E6E8ADB552E1EB2C4B2A3B73986AD53ED9ED8911F82F750DF05CD2EBA3E1 -B0DF208A87FB5ED44C3296B803881C1D9776D13350CD29C3F716F0B5A8B8557812024BA70069BE65 -89AA579EADB1F657712DF2570843D7C5FF7F4009D4D232D3547DC8B92ED5C4DB77B76255E661FF8B -163C6F3856DE5651B597EC7C78B84F0C6C1D6EA3A82286F1D3BB45F708D564E139E81F473C705AB2 -56346328DAA64D1EA8645DC10FD449092E0634D9D7344B2AEC3C75F6B6CD8B3F3867FF3CBB0F556B -186EE9A7C26BD2D17C8A773055D9D5013BD2F937D697A770C57BDB36D922CB911CD14E7FA14160BE -19C1A052E297B1A2D682D4BBC9F1D2493BCD7CAD2FA75D904C5F5479179DAF7DC6A4E0D269BACA2C -4F2430B4C8CF1572FBDC750A05DCD5B09FA3A9CD6F2F2A386E2B3D4D8E257BD43A783B38E63BCEE5 -03EA96FF2C373181744A607F0CB8D281D7DB1A6F4076AA3E2C61914BD796EF8A0873F79F964FDE28 -B792BA99A20C3F1F5ED1FD189FB1867C84DCD6AF43D49420C8B1F3DCE7DBAE71DEB17FE45644DB24 -4F44B1011C7C768EBB7254F4DACA64E9BA87AA7CD0F0C4B2228FFB9EBDCF3DDE4DCED39399FFEB34 -8811547D025320A88B480943A339E2CD2FA3605AAAE87939B1D7901465A1879BCB4C5BE1A179E7E3 -71F1BA2E0844F88AFBAE9B78DCCA47AE8AEDF5BD3D458C7D4A7A08ACCBF880D1F1DC69C636628DF1 -EBDC5C42FF88FF8B66351F3F72D703E52F3CE91E4E00759753A599FDD863788E99858498B66B93E5 -083BC3501C39A9BA928B0D763C28826FD237E949EF0BA85CCA9AA20C405DB6D5612DB718F7B4AD31 -D253AE306E4D7CB615C59AE668D347A4E60FFF7B103F8BD0E7CBDB142A763BE88AB40EEF6B8FC200 -458D728930AD0F94FE52ACBF0657C4907CC7942710AB1FD8BD149A9C9DEF6B8DCA7DB9062AA7B1B0 -11ABB5AAE8B77893A023F9EEEED4A20FBC30F922282A7AE2F1ACFF64151013D6B8AC2EAAE58171A1 -0F80BC18C3BBB5DE1E22EBE6033BF83040629023D74CCBAB3F1923CFA4A6735E1DFA8A1B261FBF1C -397E26F3BA9C2629CFDA84DFA3D1087EBB19DDA7E2D76E30DC2E15B8821D5291DA1DFD73940E5560 -A8A6DC91BE0075E3ED8D9E8CAC85AC20768D868CD2DC45DEADCC8B59AABE6EE5B2F891E0D7CBAE82 -0F83479332BF9707486698FE196C72EF72B52F54314329FC498171782BF160E1110A19B8208FC591 -EF0F0DA71AF657B43A7CC649A8488B759F7B69134B4F9DCF79DAEBC1CE52CC8015F324C9D46320F4 -4E1551EDA6D86139DFD1DB814CF38A22A89FABB4F75FB896B00E769820F763486E86668253CC466C -1529A5A924CC337C48448851A381DCEF63A0A302B65203D6571A1DD1FB9DC0C3BD6AEF4891497033 -109CEB5A481BFE442249940EC54096F1D0F2436D9E60495D0ACFF967A741B30467D24AC6B0032213 -18666B951EFD45324987B10BEF4AAA0FF1DF6887377A7F70F555DFB9FF1001C67438A167A00B05D2 -C37065655173A7ED9AE342DFA1497FB1F2FED6098901249A085D31B66DBB6AC25EF16C106B0A6FF3 -47CDF66434DC3F0012DAADE80B942D522CD59AF4C31C1C062157B3D000B9CB86E2AA7B4A5BF31605 -8A0D5A148EAA2C67977FAA0966E4C3454E08DF14C2498AD76E389AF65D2C139A6D8675298C46ACEB -7DBE6904C373C06E5F71399B2EDA0B40AB96E8BE991DDC39F92F1D24797F9EC9F2FAE25669B43754 -E2498E8EA5C44B176C3FB3E8F7A7A1481275A461F2549AFC4CC73E28417BD8C5212C13105EAB967D -AA679AE822B9B75B372A99C7E82D6BD83AA2BA00314DA4AC51B9CAA30D80507505BE24BAD0A87C5D -5D7336EDF60CCA4CEC8201D243C3932F74D171E2409D789AAD0D04A7BB22FB6DC3AB92AE33FFEA89 -7C484D741039F38C317EA396A0FBB9F15A27D87FCBE007558799BAB73212B6E5FAF2080BA074724E -AC87D88166DBC1464CF5D41B99428851FF1D99246944511CF42C3F9248513E9E51593F253D89C604 -388AD7132D6A169E9DD888E020AC1F8BA606F2E1EBB97977E505D8C40853653D8F398F71CC9F8F9C -540C22A1E6195BA578AE7262FC845FCCF77B33F33EEF266489AF8B81A615D6A13464BCA58BEC16C2 -3F31D678F14A938BEC31272DAC3CCB1B2DAE577A26BED852FC59843176A5FCFCFA0AB7FB00D2309D -E55C82CB9049F44FA61F1E313205A76317C4CF529A4456019D970624129681F46A9CD7950B8B5C40 -61853040113C8115319E68B37F88D864C6957DF813B305D09E6A1716B10F26F2EF5C727FC77AABBA -73E12B5AE6416AB19F6563CE14046B715BD4CB2B1E4D315F42D10F74CDEDE82BCDD524A1A5460921 -9084CF1CDABFE72CC8375478B41614BC18A914903596D6FC2F361EE519F875385F4ECB50F7053127 -4EBDEB14A5DBD906A60817246042E3799BB3AC647CDA7244B7998AE4F3BFBE5C767FD2142E48518A -4217599E0EC2CF5E86C8C270FF8B02F949EE001D6A439BCB4BC7D7F7C8167C3AE0A7E59687FB8BF6 -F37BEAA164541B8EAFD92E9D152E3FD0F413C99CCC34FCD8AA455A0B55DEC846A5874B94FC95CFF1 -BB386B2A1E22CD1C3914264B6D5BD1746972857C9235052D77A6C0DD3019F8A307FBEE63A3EF12B0 -39B224108276FFA84021F1AC5B745C54690B3FF587B4B1710AC3533A67BCEFC503ADF1F4B62B2910 -B31965E364EEC9CC437CC40181A7320CD52BE9C546B8F1DC824312216C2FD8232E2BB8D40EE2E314 -54C09772A387F9520E331456C269F51A078E6ABD9FB6A68BFD5F557215B0BBD2227B8959CBD1BD4A -EEAB094DD18E891C61FB00933C0A0D76174D169C0B6445D34C00DC9E06D85EB086C18F3BE27DF734 -EBB9CF078AFF6514438549CBE92A0C0D25EFE4A527D86F158B4E9D8870C7AC5D6C059643A3298079 -CC20398324CA87273B86ED801057D797D91BC3CF2F96C650EE1566CD3CF8656CC577D38B830201BE -718DC9A494268177A5019546EEEDBF101996BE593631654B638C75A6BAA648CD1E7AA9AC1EA60F4C -D604071C89DCCFF8B3E430A57ED6DE11C5837E78956ED991058F3646219BEAE94E4D9381A33D48CA -9B8FF12B54A73FF869D0EEED7E098D80152295E6016CDD809173C57D1F5FCE908A37010AD4C4471A -53451DE9B4363B63437C374C598F548F145D3D288F42531FCF36A9CDF72521F1C0868FCEEEB1857E -A983F6B75CE245D875BEAD1BCB8819E5464518E04717B78BD6E335F0AD77B832AF5682062A1E2AC7 -7CD5EDD5DC372EE456C96D38BF8BF348DAC2B4EBBB2440F2CE97B4B337F2E23247E3E8423BFA9237 -CA6CEB6FB93F960CAD894A96F0371168A33222052DE9B3BE04B022AB95C0C243486E35197721FC55 -311DC55F87BC72D09B6C940CA36E6640AEB66C394A5949A604E7F15DCE3A008BB41B0EEF2840A357 -F348443B4DCE064B4C15E5EC52E448C985FAA1C3D6526270B1CC691009959A7620C9A6202619A19B -E410FF7BD535A8B2640AAA459DFDCB8F2BB35112626497E8A397D4F9E04788322A738DC8907CB643 -15CF63C95809E90D06EF02F72AB04AA61FE02ECCF7E9049FF9F3EF2258A75656178AAAC9F3C2A26C -001341862D526CC14E92A81BD63502F959066E0BCD659CB9B5A45606153DD77039B8C5D5B13565F0 -0D95A41937CF97089F3938E39659A64DC3D6046D0E9EF66544CAF8A206635DF49926A3EEF3FDBC9D -CCEA2886EC855F1821C4B9CE1D02A19A11BBBEF43A7D4D536715548A62802F64AF30BBCBEA8C7E55 -AD56C801D8A569C8183615A78CD393CA42C103F155941E845712C335F4ACFC7807202B92A983111A -ED241BBB8501F15560E8F2157C29752BDCDB274008137277920053D6D7DCDC626A574A82A8A34F1E -77B2FC8CF7C1A7322F22DFCB450259EB450C52B70DF3584A7C54C813DB41E3DD81253A03B02BC252 -346AF0160716355797B6F8210C453DD7E1E756FF08C7E6A5F4F87605E1DFF35A130D79148A57B7AD -12D94A129FE3F055CF974EBA09A2B13DEECA2E02EA818A58B81E8743004646C7746110BC61B86ADF -2D5D8C45A6A5461EB34497FCCD09E711F47BFA742C73F87B257B53F30CB68D151424DC3C210D3E8A -C67C2495A8236EA2D7985A5E1DEAC699D7B700E6D38EEE2E93B191BAA5A8A2C916D206C63FE63427 -AAAFED2B5784276FC21EEFF2D70E47C8540DCCC3E00134642B703795CD3702631AE2A90E063A218B -61E5B89BBCFFF84F567E37A31A9B349717A8CDB9C9377215BA838FF7469BC486B64EF2B6D92519C0 -BF0826E3652903F40E400689F5749DF86FE3DE178E21E20EDF9053081F6510D8F19ACD021CBA481C -484D30EAD3B84ED0190087EE478A17154B243346C3938FDD5340CF6E47B185E64ABDF44F8CBCDB82 -94492B91929BFEB9DA2B033C3ACEE554F0F1A7F8A56DF7C06A3583C1E9C5CA458D40E550FDF3E2F2 -E7BE8312D5FEE98543388EDC8A04CA29F1B82B7AB4ADABBA3F2C331EFF3521B2B92F99C4377AB827 -A989B423750D36ADDD2E286E7F3B694E29B8BC403693C6F7CAB5FE34F1E48C8D41B47831E8C3F5BE -5ED5142E3C44ACF5180CD41FDA149B1F4AED36812E42BC184227F5034220F74F67830255E1CAEC12 -66DEFA358A87D2E3B4B4E7EF30181570D0B2B43072EE0311C2C157D32EE2BEA8EA4251B59F6B61D2 -B4FDEB654DEB67AA3DFF4AD65B727F0D6B7D61523E4B44D99BA5CD33540F340A35DDD466ABEA4E72 -E504FC9BAAE51D231C33A8CE7DC2970DE4C1FB5B096A3D9C641EF77DC9039886831DDD01C4F21E6E -168E38BBDDA5F4308C959C7BBF36A42D042DA6862937EB20D4FA2E5927741A58DA5CBFFD9553BEFF -BD92E6D64871D8B25D9049F4E71970A8FF5557D1DE83DD24286D6C3E4770EE00F9A1A0B0063C9999 -4AEC75E84D6F9C488434D1F3DCFD0A8BEE9ED8257CA97E75E8B1285747184D6D2228EF95D4A0B8DA -252318ABD35C8398FC6568B294D90AB308A7675F9F160140F0A08C88AD0CA1CA2CF85E4D031CFA3B -87635F1398EB7DBC666A259F02DB6741D13E11B230025DD6DD64C438409AF109090058151E4DFB8C -0E9CD65935C4CC063CC6100FDE70896E23E3661C7FC1B8228B26A55903E997F80207EDD8863FA074 -EE4FF23BE585BAF708040C9F8CFDEB42FB8EB71D4CB6D7757E973E4D8C9DDD082712C23F868E1135 -ECD91250BB4335958B07C12FDA75EEB56BE19D1644C1F76A8811C021122619F751CBBFEB1D3DC912 -999017FA163672A1EF754C5CB78962BAAB76EC48461B492FA88F9897170DE857CC8374C8BAE417D4 -C78A56047024731F4A45145F0393A27CAB614A7FF747BBC28E6880D4D01C0A6CF317A1DE5BB5ADFA -4B5FBFE0C57598C79F25AE57BB797A489D51F85A9B9CF8BEA64293F8FCC43B0D5484DF99DBE19152 -692CE756F6FBE8CE5831CF4B8A5AF47524E272C45C62ACBFBDFE7E60B05BB1A1A6AF0E9210012014 -69B3DBB49EC7B23A363FA68417B7118DCEA71D4ACA2E36F88C6DDEFB70205DF3AB7C74CF65CFD01F -F85FAF99F172689737331D4C6CFF7A29029772F487FBF625F17BDAD89B4AC076948277B4ED687840 -301016C2B7AD4C6D02F81E88C75B7A04D724E234E38A38269351582245E361A42C75B8256AFD5624 -B558ADA2190F960A896BBAE7A8C57E76DA10DC29E69BBF3AA86214C001A27B39C1D17C548DA5601E -86A5CF53E7B1896BF003AAE9387ABA9B102EB1E9002DD3754A378F3E49F2C6EECF47EB1BAC2CFCE1 -1AC0C5CB063672D32733563F3E1E891B6073739BC53AAA0043FC45E90E413DFBD4548DD320B681ED -70A7443A233D79E3F038D26975586E5CDD2115AA614727B1F6DD4024B85CCCFC79D10B7B6AFA789D -B37BD0E8C423C1A4A8681B5FF3A9FA1F61A46E46C4B1836D1AA41A89264A7F4B1C259E4B10ECDF37 -5BD26A1F412FE01FBDC03368FCAF48AA0EC28B1BD603A6A0D0DADE66D14C9B7285569230FAB76803 -35BE104305E4B748FA99FA31F23991608DFDD2097DA292551136F255051C9F7EEF3FB7C7FDB4E651 -C3D03A4CA357B587245236F4FF3252563F6BE08EF8A3EC09BE2BF27B9120F7D37801F6999EFB1C8A -D1A08698CC59CEAE2CFCDBF6BD8F94DEC94F7EBF33AF05F52C85760C63950B455510C6AB9398D09A -C288EFA09E8F631A59B03FBBC75BBDAFD675FFACCCF8ADF71E815A4A49F14BF70E42DB0B7347B528 -4E234C24010E2177DBBD57648E398FA6B54571A37BA8C989503594D03C6E60871A7F964599022154 -02BA168B8D1D2685F5CF8645D5E11A1769473027F42564C2966C10C0DEE1EE1B6975852A4870D492 -83A470E623337544A7CDA5C16FE2855BA2A548511FB4D4FF2E3E78D108E4C734F64EE2F12CC9562C -BDF363EFAF5201B673AD00583FF108AFF6B68055A5F299452D176EAAFB92C84F114C8C22A05EAD65 -64A3371420EA9E646308DE97D40705E1638DF08704FC90249CBC0D2D3E884A4562CC27370B1A9738 -9D8EFD237E644A7370B8B38ED1C377F522C75F981D878A5E87101E621DF9D85C7207BBE5A87CCB60 -7F93A2E52F660E05C83A7A6CE6D01AB4B62A1EF8DA47CF97D4BBA0FA8EFFA9C0F61A325A97ADA694 -45F23AB1FE27A66C271639F839203040D44B11ECC6E805FBE88843B34C4FD52D1D3C6C70FFED433F -C04501FC20536ABDFFA429B8DC8192B2D45DD9D646049CBF40719C3D674773F9676F9FCF32817DCB -55402A72C56D74AA4CE4035687C730B6B44A9CC614BCA5A3FD17C170ED949E588EE45E89E18B0766 -2A6327FB9E8475C43E5DA1B0AF07C23774B19C9EF59281F5D884990D6194170D8293A86DB52A0FE1 -7E88DA82209A00A16BD29B8B2F13FD60AA25FCFA9745F57C8216283C1D6EA1C119CB9B8D57C00419 -5210FFBD56395A3EC2D3098ED38F389EFC0324FD0E55EA339B3892568229D8D3E205A821E8219FCB -1A7713FCF3450F8BEF976CA0BECA47376A8CA73DF85B340C67EFE4534D459617996526B5E5D3D19E -17CC5449E5EF2B82B2C4C2131FF8A19FCFE6A186A9840D872D85C40665A7A04E67EE26B8BC9206C3 -5B44C8F8A1AFC3867D96DC6D48BD45063BE25B882E9BC0D0948C18DC870E6925818E1FE17D336217 -F174EB4481F5C0ED37A3BEAFAF4D46F857811B6728BEC461AE6468D87A736572F4FF95B58B04564A -9D3C22754587DF15495A319D822B838461764B73483C1F7CB930EECC6F7424841EE10E4087E95120 -2FE88A391375C96BEC4480328A54740213F741105B12A39F19808F3823507B88115D468C61B212A8 -ABAE7480E39BA52390A1892C7EC50271156B4E8076FC3ADA222695DF372385DA7B117A29E04CD2B8 -0A320F186D61C963FBDAFE9224E537057C49E82E405196AAB621B5FE4011E1782A747EF935ED8BB1 -1BDA39A141CC0BA42D04AE123383BC95A1D03A85A9440010C3B9613064FFECA76197E10919BA5006 -F35837ED9BCD7DE5E6D968AACB6FC91178091FA467EF6FDEB728E17293DC89DDE5A5261FAA95A2B0 -000FC750E7073900D4D88247DA464613ADC2B3903A6132D96AC0E1C564385FFBF6249DEA76BEA2A9 -9160632DD2FC2B99133E9F2F470F72B45D6F18B45020F604B06CD9174BA3805DB60EB9C5E6A9C789 -ACE76AE9C79C1BD34434E95E501BC968633AF93FF4883C6A596776254C0C74993710327086B2886B -02FD3E42A725A03459CB36EE34A094139AF5FCF487D3DFE63FAD20BF0DFB60DEEDA2ACCA3510E963 -189D1256EABD81253F7FF9D11263FDBC1DCFDA3D1EA2E52005CE3C605C993231258A717423F64BFE -EBC34684EFA676358B9B543C2042BEF954829FE3246A879845B30EBACB43D8DD7A20FCFEDF763AD2 -C5D20A798B69E08722DCE6A5762E249ACE3055B650D9E110599EA30DE5C4FE7200D5A8DA9E1FE268 -6350D0DF334877D0B9F6524C552D0B6DFFAE125EC4C18F7547BD51C14288E4ABB7F8A1A00458596C -390AEEE6FA308AC1F788FAE30D7F8928AFC91D4DE6352D20B19D8D8AB122B7378CB379C5BE7E3CE2 -922FE667EA057B5D7B3F0B51C7BF0C85F87AC2F360D82C38964F4DABCC9104B32F0FB8802235E8E8 -D9A5997D392259074C00AF2CE1D2BF7B8E90E2E2AC34185C68A03BAB8B267778292B227245D7FF86 -70786E3F746F86B9D4D17190DB859A0E144B2A61E6AC9254DE5DBAEF20E2E9DB0B2FF654B996E962 -F55E465DD238BD1643CE59DC2B5A58B1E6E4AE2DDC2D74D79AFF3C34E4E593E051FDA236B79CC0DB -268D2A89B1878051223BB8F33FF99BA87A4811C0B3BCC01171D0A731EB732ECD8749D27952C27886 -B252F9C3D190419FD2900987A0A255B9753FB7AA70C37462134C467A2C4B7920BED9F9E86F8F98B9 -6D00AF8B05A4BD5F14C2A0D914A9A84160D554FD0718F50ECB5DF5E76623065852DAA74C9AD6DA07 -A119DF12C3577FE276AE551D48B1C5CD8A50E84DEC9CB0840520D78FA7F9A7C2071E28CD20EC7649 -B991F3818CDE295CDB6085F24FCF93147E9F4DD084FBD32525326D2EA147ECD5B6C9D9F4A7166663 -AD18BF234E9CB92FF72138A8A49E73E527E9A6488A4CA808AECABC94D693CD2C0C357D285F65006F -A2F9197F61FBCA6EF07B013E2B558AB531D2FD270CEE7FA8E467FAB885E90C5884843AA08E2BBFEA -0AA575643727BA18ACC499FF34E3438645BE2AA71EA491E54687CD305E12BBC94FAEC848311AE816 -495B013BC5075A2D2AE54A7AD7C9105B64356CB51F18C2C28E3A83B9D81A4554DBEC9BEA9A660CF7 -E1BA89E6D4DFB3EEC6A3DE3FCDED9B2D614156EDAE8CFDAD5FF0EFEE31DA3E6A54D94CE9453A1CAA -D9756D91BE85315F6514BAFBC821EE810BB5D8E1B8F05F64F3F72C4B35D424F7E4DC3AB581B74ADE -B6D6297CDE7AA8278909F269FED79B7DFD39B1C0338E01D556C4DB9CA3A8578ACE3EC3D743ED4B9C -0145E4525E8C315F7A1B98584B975C70F0D415708C8CCC13F848B1D36AC8249B73638F95DE0CD27C -7EFB52BED4339EBDA4812564D7A77416DDF4CC88CFB52D07A252D89353C6826CA1832A153242979B -6CEE783ABDE65C8B40CF4EA7B42B8DBCC0E02423DD693108006F6A4AEBF053B666C3CB63D1861F86 -EAACD43BB9BB6F2C3A17293C189331D253B447757EE7CBF4518BABB73A1D44874D7F0625E6A013C6 -08E991B4AD17A9ADB36740D25E3E35B459B422F7370B134CDFFF3F3BCC4C32B4E9EBF6A2478013F6 -6933A1FA9403A2F1161EC632F1F04EDF95ED0F33DAD9665D54DD9DB2564E51DA7B65978CAB50D6DC -1568976E83B056EB0E3A6758518B6E17E9EBFE49B72EB148B472BA144BDC2AC95744C9BF1258F0A2 -E47470AB0EFF90E190A41108914AB8C1ED6B11E0681778521870E80C16AF2AFC723CAD8719ADB62D -3939D3BC8CC1D8A4E07E9D734F54ECA33D936D2C39D5C8055739C33E53359BD40E576C11E93B4B4C -122BDBC9B1BBF44243AF4F0BCDBDFADE68C526B5CD74E29CE3F70D62BA83C489034111FE8E4DAEA2 -F01F9D938ABB532DEEAC0E329F42453FF5C15DEC2AEA8C198323C9E8FEA55B3F5DC4751D2E2E16B6 -154E7F2ADD46860E9CA71DC114C99D80E7EA1DAB51E925DE161CEDD678EE6282AFF38E3CD0E65954 -9C970613209955A3F581E1ABE485E56402A3DB0D1E9B8A9DFD05C4B0B7F97FC6D0EED0B69AD6F182 -B1D028ADD2F24463834B13F5C1307F91D363891824E81108E57CFD5211F86400D3E96B107F3B1FE8 -9C4908649D04A46DC3CEE0DE66AF03A7FF9F4DAFECDD6DF4D93784CC899B527784DBE0718050FCE1 -85BDE3F39DEBCDD660B2488D23AB1CFF87B0546D02B48E7B7724C9E87B71BF34B5D6640E0F6ECE47 -B182D41C89461F712849C6CFDB7E3F5EBC1ACDD12D65A422BA362A8FD6CAAC5104CCC5AB5FC04A46 -E4309ACAC83D659DDDA256CCDDD1BFF9AB3622450C4FBC89C82214F00C42FB0311BCB1B722A691ED -839CAF9024FB1671F18E4639C96D84718C663A4341DEC037175C6BBD288BBF5A0478298CA726567A -9B74C32A527339C666A294A17F6821CBF243D13EA4B1603C292953308B566653423E7301A032E5D5 -E2B93F1C1434893633DD19501AD12728B5A1D9D36635B589FA2E151140B543D7C5E469AFAE8E80C4 -FC1D9CB6C3823CC1BB7EE40AECB58CBC1465792226B19E0FE79235115F6A3AFE19F98C5DB63D372D -D7C041CD940F4F79F2474D9CEEA0334FA04A97DC9773064895CF11CF73F11B4684F06E48F4469F6A -1AEB2CBBC52994DFAB3319DCE3A0C8C2EFA9627496F8CC84D3DF3BDC4FFCB61672780F294F453278 -AEB9262E66486856D37B7647141A82E049364ED3D03F925284A3F1FA3DDF4C0B48B3FE22E7DF9ABA -239D33CD300FFA8FD4B96192BD568FB18D325CAA8E1F1FD4B27527417B034841FD49E4A77F217062 -3CC8B22101166D80361EB15FA9020D24F61007B0A8274DF9DFCD8E97C85568E76D34AD5DB1779B02 -F034A69CCF9D4EBAA188EB3017EEF5B22A0A552696A574907F695098BD8A4849D5C8311F129447CD -7A3CF88B8191AEC0AFF30A38A9AB8135608A7829207A7D242F6E1FA7DDA19F5E4C28560D42DB4405 -77CC0C5F5803EEE897103ECA0BD944E320AC26553BEE7852EAA733BD13DF760056B2F5BD1243BEDA -BC3C1EA0531017D74B47E18F801A60074D6DF849FD0532234545E5B5E112D1E7385341D39A89551C -80DC2DEAED5D5DA2A4BE5015D297324E92BE64C68428132E6EC654DD4BDCC6640C68835FF8A05E09 -9604B8CD43D3AF2B2FE10C8AFEDEC5A70AF8509D12F662338CBF166D9452CD36331758AC4F4CBD7E -DD52139AD27DC52569877FE709F297444C4F31899D1945C81B14ABDECBF31DC463A4148F04EC4FB9 -703C158216C0FBE65CCD450043ABFD4E65BF8B28CC148252E9F3E797EA0B57B8721C94CBC2EA602D -F2C57E87938C887A382D2659226463BC7D6A1DA87F4A341A59BEA458177D3F18D1213539DC0E301F -6EFE0111FCF6921368BE17CCBB7428127E0C059C2C5ADB2A3F0197F0CEAB77FF7F3C027A8EC3EE76 -CF5C986EB47CB60561C773B3A2DA47B5A35394E29373DBD5C3FF4C9213A89AED77CC4F3FCFC49EF6 -EC7557C521979A546983C106B3627B5FD2D71CC5F08A32BF49332A89C5DA71AFBFB94C949A91220A -B1F885C981423AF93F73BC1CA4D92D9DBAE3EFE6A76E2DE3D0F74FD3255820636E3F1A6B7C185306 -23C12AF90CDCD2C0A728521E9B639EB6345D1DE8FFFC3B19C72E7A93823DFE3115E9E7BBBEB28CB7 -3DB121AED8920D47D8CC08EA2E472E39A4CAD5881B5C4204F2B732AF9D5189D25ABF413CC78714CB -01B1D8CA5565169A919DC481F6D2E67F1D490AEBC5CC62A8F62C1A323EBB55ED35AA5C8D6F8B970E -93205C2701CF4817BDA994FC16197B469ECC5F5E9DDF0FA05640C2E571849571CBD26402B1EB1E80 -3FCF423345007B9B52B13E3B034E8CB3984B925EBFFE719ED4F39F3D0E3343316A6FDC26BDBEA88C -4366D3B2F851D2B244CC4408251AE2C77348CCE9DD8BB9C89800B572D38C5D1CC34C740BEEBB5DDB -0A8BB251655FB989840D23205D16311A9FCCF7C85F6DFFEA9704492A4E7A8F6C0BDC29745AAC2ABF -AEBA02B0E7AEFEB92BA63AB0DF844EB09D505C3DFC1058CE42CDD8043B76398401E1DB862FF9F76C -05E8BC6260A4443CF494BC1755913D51745BF45ADF2F8C7A9546D7EF4FB11E9D94E4539632C2A396 -06D04480EE459408D7A2A869807A4C01881C1BB21C296A402B5E6E07093D833C3DFF075F4DD426EB -87B1B8DE16C146DE79F52F5943015331EEB852809CBB8E1D6460AC4D176FE96F8D19F6CCB22ABBBA -A27C4497D91312C3CFB5BB913B314E43D2EC6AB6897BA7C34CF2CAA6DB4BD69EB5DFCEE0AA917D69 -50E36A68A4C22A60DCC69379D47544A58D640EB10DFE120FCA843B588CA8B94F7869F97609A6FE03 -AC86EC1F7CEAD2EC8E81977D1B946E459DFCFEFE65A7BFF67E66F5F78A45D8DF65AF0146DF74E024 -FC042328886CC1DD7779F49CDBB750345CF83CD678A6A8897577299DEB38AD665DC4F21CE1892A18 -C256F318107DD3E9245C1AD3BC93CEF7B7BF057E33EC9A3F953251261AA3D1A8347261E70A46F777 -3A84F3D4D1A0DF6DD22A96429349DE0D180310E17955B10FBF53220EF6483D03C650A8D5C16D63DA -F65C21ADCD6C2D0B5D4ADEB2F5526AACF7CF42F9A8BF4832FB2D4F73F3D5FFD984B572232F87BD3E -59133ED3D2FA19F7856AD812515C74F7D851574019C532C25F8E163E595FC9C83E3E820C3CBF690D -A62578A980FC0803EB6DB9B1E90E3256BD4650816ABE5EA86CE65C2EB418D0ADDA5F3EA04E17AA8C -4536CC471AC20236E66ECA3619F161DFEFA485386C30EBB86A7AD930FD0AADF2DA69DCAF26C0F677 -206E2030E3B15B3662C0AD03DBC1636EBFAD1F2F2C37F5FA9856B0198C5B1D80B69C5EFFD94CE071 -5135C649C26B9BA1266B0A5B270CD08A706166C0B320915C87B27DE21DEB5D7E4806F6E700B7A06A -4E29B629CB40761983E9CA8E34E869ABD04DDA190BFE5A6EE8B22D7E511B84EA584A84211F27AF89 -18DC5AF8A1FF2D360B6BE3CA8E66BA4CD2CE6A25E7E89406684DA83FFBCCCCBFD0844FE3BECD7DE6 -7764C59C022DB1168D585FE25073FE00E30218D1DFE115CA1FC606AFCB04F2A082EF91788B6BD096 -84DEA31F20034A91AB9D971366F97B5009FEFBF1EF0AD941654081B1E8F0B2EA495069A1DDF11DC5 -6857D29533DC857958B49D1A0779732819FD22E437084BD9F3C4F2CDA4D12CA14431937AB63A03F9 -C040AF1D801F367ABDCA7302E18A9050D6026FBA5A5A7FAD44E31593173CDF277CD737D1CEF59FE9 -684252BC0DDD00A80E023B88222494C1C8C0884230AB11D1083225AFDCDBC1E24D4AD5FAB396D2E3 -70E44A7571B230660D510A5076D8E35F7DB72C0566DFC119EE1B8AC3C0406950A3C4A4DA36BDE297 -040A27F70753A87E6CD593DC6BE9962261A99AE5949340C5D45C94A9AA3DD636CE8B497BBB812345 -7C824F443A53B3EE595C38983FE3E07DBDC6ACD55CAE8BE1081AFD4857A5F52A3C925143507A3C37 -F1992CF72ED0D4C48D94AE6CADDC3BC87AC3A3EF035E02181F78449E4B063B0835E827644051551C -1603E2EAB5875F28FC77BEBA6923428D5521C698C6B7F133B0F689F105FDBAC30A8ED2F29F0255DD -F8A037B81F04EDF004CBE639C8DB0F94D0C5DB92D34D66C2FED66CF8B895AFC4E659D08388EA44EA -E83CE459E5BE306750A682B627802990037157339BF142BCB9C08FAFDC3C3FB16DC3544F62C6C7E3 -3E20CC4FC7CA21E2C3F6C546CD78DEE348F1A4C8CB548EF20C049678916771D83ACC9B7B22784AD8 -580134471A3C79BC86B5D6D0D305C32E6204274351C94F9DF45D9B2AD5B5087A89F90D6AA033E4B1 -D1BED022F36147C7ABD2B73134DFFD50907258E610C3B20949E141172B1C6A76DB238C375021CBA6 -645CDC26B717428B5A9B4D3F32A4B1E22FEFF3BB93FD889E1DEF8087718D5E3E650FE4A3330DA9C3 -7E9EB499DF5A342D8BA4C0A033C3347CB25A31BE143ECBF91384F2381E323E7FD3A82A3197C18905 -3200AE2C86B9D01AB0B289841EA7E9E9A26966E0DEF54DE0B85D8DF084B8C590081E444BAF1E1F60 -670FA12AB97159318624F2AF1B5EC7DD83C1073A99398D2143A52D10A13C201FB356BC9E90C63BB0 -BC2D4C42AF4A8B9C8C4D58A1B32E0597C63B3F8B3E893BD3BE8C60231838F1BC78E73A6C8CDD5E7F -2907F897FC8EE99BFFDA7338BCEFB5AEF950E5549ADFD207AEB15846B509FC57989883642498A381 -1B8E5CDE69C05924EFAEC232FA4CEF302EE3251366ECAEF57D25CFA3B4A9E6397D996F421C900BEB -CF73B038FE7B16FD0A1172AC2F40D19CE0B02FCEB8BC47DA5344CB933C7FEC950184F78ACB32D3E5 -E290E84BE753B9E7A7BFC4416CCF29D023760C06CDDEF2505806A65E1508990529245059AFD301DB -669D41BD72BF7A80A9DF66B876B3553FDF4DD38D15289AF7A1AFBC53FFFF135A6348DD784AB42A6C -0D6AA330B069607E2DF3CBEFCE79D6F63E274C9E73A33EB85246D5EBB986BFA923DF68B2B8CF82AF -6C33E785F35B25B1D1D614DE85A4F4510ADFE42D75B5FA5408A59ABE53859E28B3D000EB9C6A7D2F -67C91DD14C895BA87B9CB57B851E5193FCC2A443AF85FE28DF6F39537F23A058BCF81DD8C04CB2C2 -5040300F4C55975E856DCB4E21E2B5481BDCC05601942FB25BB8A6B6F93E2C2A33CD478B44655657 -C557EBB080179EE5D98C5CEBE0B25BFDD952FFEB258014D7A5BC4BCA4F1A23BBA73C454B12960451 -CE1752401B0151CB2E01D5C72595095EAE91D8D3BD55A54A2AEA89239FA176FA7CD6F16BB0733EF6 -CE6E77763A23AAC77DA88C8EFA7BBB2991E472FF2075FB25A75ACFA70A04C28764F4AE4C12051B25 -B120CAD2E3044DA35C1F94135DBD69B10DE147321CBBDC814CE99982AC1D76CE3D3330E41AB31F3C -76BF89B95EAB81AF3464C732D5B1411D97DB36C9063537F64756F205B16ED7058E2CB1D6946C00A1 -A0CDA9EBBE924BDA6C7D7B605C514A98133907B793C74CA858E82DA3519188CD974B34DAA74265DB -5BC8550D5F0B1173ACEB87458BCE2AB1F96996C811699A0FE4A9B849D39023725E2B1EE7E426D30A -6C5C75AE6BCEA6DB41E4EB2035F7F924E6B9F0DCD00EB2BB014222E55FE387FBF5B9B7C04F4688D5 -AE3529FDACB38B5EB0AF5C3A874C1AA6B17CDA8D1E22EEE05A3DA88449200D3D0D002DB86F6C51B3 -37C8E19F338E7BFA01E1202612D50E210140947D5F350E84F790286C3F679A5D7E43BCDC337265C2 -631527FD62D598B7CA1F5835C0441881B97F5197901ECDC4F195BC665A846823D2E41417373F8639 -567B228FE7B73D781F07A361AA49C3E9D80FE5B2A32C4C1E575D194E841967B08D10405FA44EEE28 -47DB9372C5CC931E50469532F1BAF577F680BAB4E30B7E1CFFA8574ABB679789F69A8A1BAC07B7C6 -4EF5CE5EB00E97B36FBEACA9BBA4A13B0293D34BDBC77AD1FF88E5744AF009823BC262511C4724DD -585E7E17D90F230F7A5861B0DFC42F0B4E49A04EE0EE4DADB908479DEF8372F334C53D2BA5D855CB -39DC7C9550F9D0F7F77E82D5A59FBBF34BFFE92DC9E6668B68FEEAA4F20053433D6749162BBAC5D0 -D428DCF2D58D49B127FA2E674EDC7D3613B1342F4D0ABD7F4C5B049FBF78E804D5F16505AE7EDCBF -4D6FA08D72890F5D55199034572AB4B0C9A7E7F6F5A403198864ADF113CAFF5BF9D4AB5B16F81D0F -C2188FC80875E10034D12E30C0364F8F72797F1AED525A2712A40D44210B813DF5A29C84E9F6D51B -1D60A5F6F938FAABF878D29E6AB252D95D05FC1ADF5D4CE1C9E585219112112BC6CD5C766411FBD2 -2731794B5DE0A27AC57D3C57926807469C360372BE529098C350EFE2154B87F1205A57A0B04C5206 -CC4FA66B8793BBBE492CC3271FB4F90A28D0066E0D7F63B8DD01549A05AFA5482C29560ABD628568 -75CAC16100087540162473498C14087B29B86B7BFAD693E81765CEC781F3FC80E9C7B410E9B55B88 -114191A1703C638DFBB469ED1DD8254B1407003A319CE74AD419B077F17047A01F0BC0AC8507191B -F72D77D9333C9DA8C9DA733EFB5305F49CB8C7BC451321ADD7D896395D269DCDFDD084EB3AA70338 -6C0697E962929651164135C094D9BB1C9B949D5EEBD3BB17F02C98C813CCBFB23C2C26218A2F4C63 -9A8B9DFF2C29406037F91938A5E1227310728428B56F48108CDEB33BD3191ECA89F947271983DB77 -6B2BC897A30EECF2601EE3B2A6F0E135397622AAC1F2DF523CE6E6BC720E13CB530CEF4AB9C8273B -D3D81563AC8A8E6C44A195112DAF824BC7A72FCDC4E129A480717BEB01085DEE65EE4344D0B41EC0 -BCDF842566B1D9F5353B1F6A063FFA6CDB06EF634C8BD5A7A63F991D178F56EACA653DD67685CE49 -E98C7554745A4AC533217662D23E1D6937135D13BC2208EB8D50560A2BAAC319DFAE478B6BA4CA5E -DA20222F0E9BDB0806320ED1665B54A347DE0C42E9F77842DE4D188E7E824EB2F0D7AD163F05480A -7FA99C5A603BBC5DBC843774CA66E889B945054C0ED0B1A4BB14324EF901B023C208CB95DFCE9284 -89789690CC45BAB97BE449F8E2F5AA9276C0571303E9788C46E7F789555BFCDC3FA9ED8DA8AD9BA4 -8B3AE09404664391E63A989EF1E24BB464043AA099E4F2D796E352EB277106D8D81BAF2F8562EF46 -BCFD1E0047E8018CBD973021DC1C1D821AF03F083F0B088A62EBCF2BF6C5B0FCFA441AAD1625FDB8 -34F943DD47A5A42EB3E9A5B49641F797C288B799A64897F1346070461B6D535E0C4ED099199C387A -3176AEDC7DA7E7D9E118E55565092A36F7C74ABF281720C0147F4E4F37D49436466C61FF12764E30 -43D8A6D027E70537164F0E7942F4ACA42BB2CB136177EF7197E76F49AB403F741C0EF902FEBC471A -D6C627424320A8C3A1F04C310C511B3F91C3937D9ACF459999C18A33F2C852EC38CA806599C728C5 -43714018C65E2C5F430F6270AF52AD71ED38813B60440779455F9529A4A1623CB9F5422B9216F9CD -BA913B9A1CD95DA225E254E8101216085020660509D03A034B5D7E32E3DB5E5962A9A27711D4C3E2 -9CD84057F7D0D7E8000947AFE896F8523253391D2E11FFFE523366B05C532D5629A90741EAB3D4A7 -31D3F6D4F03FF93233DDF88BB1913ABA22EB9AA6311E3144381DAE29BCC8639958EEE59ACCFA06F3 -5DCCC63E0609F542F3EE5DFB1CF718CA3F328455726F8F65E23ACD970E4049225998371B63E35AE9 -8DC54D8329B8DB0901FAA63129EDE21B158776981D4D094013C096E9CD020315D123C03DEBA21E97 -E4B584B4BC0AF25F5DCE53C2DC0F3E61F99BECAB40799478BE7F5AFD7F68E23EF50AD6645C967EE1 -1206B6E791769428ACDC370D64E4F2B3972E0E4F442297199350663D6E772FC6777A9B9DE215273D -082CCE4E8678FE9948DC8D5B0E459CD02F1645AC5620F3571A40B4D5A17DF5CFF48B6C843DDEAB5E -BF58FE13D7DA08E8AA7902119248B3B151DA583101CF80853B0150FE05BDEDBFB50A7FB0F65728C9 -3B9DF48CE8AF1DF1FAC25C1D58E1AD30274A00EB54CF2F16029E1AC0A0919C0655474B9A6936AEE0 -FB74BD185FE7D70BB84786997D34A40326A74356A4AFAEE67B6B26D1C1A7BCFF8697B55C816CCD77 -312C332A55315DC54F9BC0A0F12500E0A76B3936292A3DA2DDF5AA8CBB9B5DC32EDACC4827D684D2 -74E65B8B76FB2C2B19F7D5607523FA953E34BB39032C05B1C1244304606C55660D3CA8607E764EA5 -B03DB7FCAB5CF7788C6E60EC8C449BCAFD90BCABA4132B6CBCCFF16784FB59B36B77CF0A9EA572E4 -CA0A01C725A6CF2E4500CDDF5BACCB9094D48925434F044118CFDC2696AF5FC0CAB3884107ED17B9 -BDE0C0104B1292A1F8C99B06FC4A6360B24480BD59DF0488641899B0F42B1311B582717BA7ECFEE1 -4143654B5371C8B9B2D80685AD38D897AD1E64875C28C7020A84FBB3A3BBEE16617DCB9BC822B7C5 -9C5A18C0CF7E80163ADFB7AA03B7CDE8497C1697D90F2ED90F813095C5B91657FC294EF0E341DB33 -92ED860CB2E0AA09293D0F99AE9EB54C761CA2DB1E51E1CEAEAB276C7BD916C68510D72D9A67468B -09B3C39A7815628FB126CDFD5EFF59CC8184C0D35A5B5960F824BD175495DD3EB12A4E96008CB13B -8C5745303E66CF8608FF27C4709C1D854EB79608E52F068FEC0151A74C125EDEAEA555C198FC0802 -7BBBB802835E1D435077AE4B1CCDBF722354F6C572BEB1376D3E342195FA80AC9722EB2F46E44DE0 -5F5A227B731B8D4A4B6EDEF04AF2C5DEC2EEF8FF48C5B18710ADE3DBFA0C956505B6DA9CCB7CBB83 -4DB6CC754948855D833670FF0AC42A4773FEA8322BECEE04CA74AC2D66855132D11A51524488C547 -71B5B7A512796D7D7AE0F9C1FBC9CBDBA0831074F4D200349D0CA40537B92496692766F020AC43AC -01DB8B2AA2EFA9D21732BE3A315F6CAA402BB2E61D40DDEBDE11276D90C2C601A935C168BE600464 -76ADED15087D54A14C68EECBBBB590927C1E10D291C9285334CB0C80EDBD392BDE4D535EB61F8E76 -41F58AC1DF5B1C5A5D91E3E27E05CAF7EC97ECF0C85B6425197AA856521ED701E5AEB82A7F52A8BD -7DC97D5B3FB5C99A5DF84D1BAFF89072922509D76BC6EDB15CE5F9EB8F4154BEE1E82020240283BD -C83A8E49AA9A2649B7955D5C058F2818A63BD0BFE7EACED4A49063C489A626277AE1246F721C9926 -E2A2B6C31045FBCD235F3CC58BC4DD6C57FE998EBD1E9FA5154652BE3A1685BCD2EFAA079A3293F7 -8142A6473822FAB627927EACCD61B3E99C3077103D2D19382BC7EE15BAD0FDE489602D055A01DBBC -F91A566974559D1B477C209416887053169C3F8F59955BE4DE82B60558CC9AE15602A93F029F6B43 -29E0E62A03982DB32F5229714EFA1491A7B24AEFE18FEBC2C93DFE50B3F641B51BDD33DA38871BF5 -243C17502D00AEA2D9E9734E80A96788D4CF5BC12A42BC386162FC88A7435EE13200C1C2C6CCC5D2 -1A03941007B4C4291BDB711446CEAF27148104BB240357D5EDA0EA5A5CE27D4A83909D75BFC05D75 -F10AA74A6DE37D7DE15C1DDA3AC3045DA6CD48323D904E716B445E5E096FCB379353ED70CF4B6FAC -102C762711079EFAF13FB74C9B47AF75F3F6BDA2A4647D2AB47ECAB64DA6CC01479F618E8D2D0A36 -45445E8744683CBBC560D47C98078B84206E90EB839B02D37C852B8E284463D4E4D890203C3D5B20 -352110034EAD6BD7F41456B807E1DB1631A9D499E52E9D9853D86728B1A2E511F40F8CA1E4724A0D -17ECD640B52FF6C66E28693D89765FC391612E5889E77423EC85CBD0A038B6BA98B607701DC0C4B6 -6B3B28C7790A1F1EB8D051DC98276DD9CFEFAB3F65C1C928E48A060C992B392A43E56EAA6DED896D -EBCE71F8245BE4687F2F1B8FC0F43ECE8DB0BD0AB0811C5CE73CBE336023A0D66168B34A95B4B0A7 -50B3BF1D197E3C042C7914FA731D7831AF798E9429571CBB977E6258244E84701E5FF91D608F98FC -3D68A4EE5B81D5FF38B6C184F6118B875F022B4CE207DC7B37E1452DFDC591A3E506AE82C7E7BFF0 -011B0A3DBD616A993FBF878FB03B6C9F2055A2B095D29361F8253C2623653687FE0AB98078F6AEE5 -FC2C2BDE0405EABEDB3A33EB7F04CB6837176245F190C6BBBCD64522B12FE7F9CDCF201A1AA8A19A -7BBC4AC064B4958F44AA0F8DDA23835AD28A1FD0EA105DE2F395385DCCFBE2261DC5A89A23AF606A -3985E5038706B1FE0910400E16BF008F250F3BDE3AD806C735495D499F16F99275010478FD2127BF -7CEDD6B5BD505FBE9BD0065B4A7090C9D27CD5B36C3AD33E1B31EB6D44E375003B51B909DA50BD18 -218418B3CD22B43278B144BE78406EAF16C7DF6B6C1C6238004AAB73736B38E168441DC16F9A5CF6 -0793A18633BC43D78674D12D38CC979F7CAADA6EFE807CEA499CB9FE616496682A66E04BBDACE1DC -112B2156B9B0B20A58A8CB43FF0EEDB99805234B9A5789762AC7D65F5A319C33F4F7438CD15E06BB -80A7A97E976E8CEC23F4C646A5821880A82B2F1DC27767F090997E91488BFA15064B702F864FCE65 -05D6CEF87D2A0A12B55BA189AF269811E3B8B850C8401F3906C080D32618D9698A766732A40A9FC5 -A94E5BDDA3D028D823D6B603B6D17DD046DE181FD989EA0F80B4CA62F7973E4DF5E032A31FE6BC8F -5CDA678D4A72787EB8253EA5882C337CDF9AA3E1E7D9536DD09B047CD8962E773F72F6418A3AEF5A -289B3406C152A50CE7BD4B493FFFC27F6AA52F79EA67E362FD92559AA4F94A2F787F6C735DFADCF2 -F08AAF98B80C53CA5607A94F25F04AA65A70A75937840E73055B3D65FB054C63E2E48E68488C9315 -A13EE949E03E46723C11CC759D222CBFAD2E1A87CAD779B23D38F7E2F660DE1388EAF1CF4D18994D -75C6CC63F187FDB949940C18B537A0AFB12AC5F67B0283CA5EFE2E764C4369104B9D3B06490D1244 -C41D6085C85F1106082EC9DB84586230511C05C82412D2CDF3DAFBF4759A775628878F997415296B -C416AC8352A6C6988691FCB831CF95C10BAE691ADB3BA2918B35924BD5C3ACAD8B137397B10AF82B -479800FE16D472CD0CDBDAAB4F882A0649CF561004B8CB7CA32EC129D0A415BE6CB91DA2B65F44E8 -0D138808A127E851A7FCF927E99DAA0EA2D626B77A16C72E37F058A3B882FC4955DC8CB6312434BD -3BCED75780B13590BF4FE8D64ACF0371F9FB1D361B05025852AAB9EDA1A0C997CFA58052C454FD45 -1E6C1F194F4D363114E312F6DC35BBAF357A32CD200A3DD9654155134259887D677ACC44F89AA401 -CA27282DF7DC3F2F04A108CBEF2558DCCE28BAC2D87B8D5B7181EA927F61977764F882626D4AB338 -D95C9477C54E9C36012A3CFFBE199EC8120A99D2D70A21F9D9A0354E4EAC7947990E8A6E0601796A -AF6F14E758CABCABDFBD8204A8E748A3E5FEBA570D36E2BF474C0083229A63F96114182321B2EBE1 -BC76DD193724C4588C1D39D184C332FAEAF4C629F2B3B2F49996E46AA6C9F497428BEA52D58876B0 -DC07B460248BC85CC16773A5DAC36CDE8B152D96057F4EFAAF8B1DC10022038577368057699B3A37 -178A9F1F6C6CC60BAE820B7ADD0717911BD23A6DCDADAFA32473491AA80CFE90F2A77E24CE2826FF -77B18B869C33FA292FE01D6477765044C7D14A548B28B1360125C6933F05C58B0889390537CDD16F -8E967E0B38579449DFC1E07389B7069AA8594C5103465D5041CC929268DE863FADB6925B350AA94A -27D421FB7FCC81C6B35F906F12246B7A5140511A97211BA9BD6831A508E963FE8BE961332F557808 -488F06EAD75E86D60DE3FA2425AE8439ECB9112BC3E4D73747C1C8E87A649919827049832DB0BF6D -A8C85C9A2592AC002809070900ECAD52A56F1BFD456AFE066509694EAC075788456B0B0BDD7C192D -321E9FB6AADCAEF00F570F22CD4A5322FBCE8FA98FAEB681940895426270BB4319C11DA67D88552A -7373398AEC5DA7C9CAA9F3B34581C6E968DAAAB2751CC012199DD897B448986CFFBAE4D412BF9ECB -F46742715A9569932516259D3B3A5431CD7028E42FC751C434E2B714C718202BF02CAF9B8A2075DE -922322EA7CFA605C8376FA958B8FBE43031E1026FBE6126A3775F643EA67EBBD97F239FB3C435526 -75CD08B19CA5EBF53B40D728556B4481C7F73EC71CAB0F89E34D60C69B272FADC22E8E7BDC6210DB -09FDD913E209F49FD28E8712B8508904620250746CA3B21B026EDAE60A2822F59E912E626B93E0D2 -BFB3230DFD0E54E91A1DBA25A609B64D41ABD897A5D21764C351E85F9E87BEAB9E645149AD32AEEB -B3B1161032C701647115F98C1C2AAECE871862D91D321AB90F3E923B1FDEE00D927F897AA9812373 -6536E2E0700F10053D7E6C589BF66029D794883EAE4C8228941CE96565B50D48887B5314A2E55379 -59638222A6CA54C77CBABD460DAC11B063519AE4F50D93DE41763BA7CFBF4C7724360E750478EB62 -8921DAA065858341958E4F3EB5966C6DD77C05EEECDF4B5F6CF19AB507589B4219377959BD258EC9 -21C34FE1DB003F7D0FEA3E2FD6F5DDB0A2D62CA5A2CD3C7AB457DFF25094EFE04A9E1B9CE7AE3F30 -026B1CB039228D309A22899F6E9B9BFF922E117123347967D7C62C670E2C74579C35989925603022 -C17B1DCE378031ABC9B4B437C7B6E64620932E93189754C01D4B280B8B08699B2CA953AE4823BB9E -E34133C5C95B3290E1BF010705AD852C72BE87291E1034B09F44A95B6A2F83FEE8841DCF661770AF -44D0AC7F9CDB280939FC5D953D525E0B41B7BE188D5C794687330CD770D24D9CD53B895A253004E1 -8A31BE4E82B384 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/Times-Italic-iso1252 /Times-Italic ISO1252Encoding psp_definefont -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -/NimbusMonL-ReguObli-iso1252 /NimbusMonL-ReguObli ISO1252Encoding psp_definefont -220 265 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<636F706965292E> -show -295 383 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 383 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -671 383 moveto -<6475> -show -763 383 moveto -<66696368696572> -show -934 383 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F534D4553482F534D4553485F4879706F746865736973466163 -746F72792E637878> -show -370 443 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -479 443 moveto -<72616A6F75746572> -show -653 443 moveto -<6C6573> -show -726 443 moveto -<6E6F7576656C6C6573> -show -935 443 moveto -<6879706F7468E8736573> -show -1175 443 moveto -<636F6E6365726E616E74> -show -1411 443 moveto -<6C65> -show -1466 443 moveto -<63686F6978> -show -1595 443 moveto -<6475> -show -1662 443 moveto -<6D61696C6C657572> -show -1846 443 moveto -<74E974726168E9647269717565> -show -370 499 moveto -<6465204E657467656E206574206465206C61207461696C6C652064657320E96CE96D656E747320 -766F6C756D69717565732E> -show -294 601 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<23696E636C75646520> -show -520 601 moveto -<94534D4553485F4D6178456C656D656E74566F6C756D652E68787894> -show -294 645 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 689 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 733 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 776 moveto -<23696E636C7564652094534D4553485F4E455447454E5F332E68787894> -show -294 820 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 864 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 908 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 952 moveto -/NimbusMonL-ReguObli-iso1252 findfont 42 -42 matrix scale makefont setfont -<63726561746F724D61705B944D6178456C656D656E74566F6C756D65945D203D> -show -294 996 moveto -<20202020202020206E657720534D4553485F> -show -747 996 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<4879706F74686573697343726561746F72> -show -1175 996 moveto -<3C534D4553485F4D6178456C656D656E74566F6C756D653E3B> -show -294 1040 moveto -<2020202020202020202020202020202020> -show -722 1040 moveto -<2E20202020202020202020202020202020202020202020202020202020202020202020202E> -show -294 1084 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1128 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1172 moveto -/NimbusMonL-ReguObli-iso1252 findfont 42 -42 matrix scale makefont setfont -<63726561746F724D61705B944E455447454E5F3344945D203D206E657720534D4553485F> -show -1201 1172 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<4879706F74686573697343726561746F72> -show -1629 1172 moveto -<3C534D4553485F4E455447454E5F33443E3B> -show -295 1289 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1289 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E2064752066696368696572> -show -370 1349 moveto -<20> -show -382 1349 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F534D4553485F492F534D4553485F4879706F74686573697346 -6163746F72795F692E6378782C> -show -1651 1349 moveto -0 0 0 setrgbcolor -<20E971756976616C656E7420434F524241206465> -show -370 1405 moveto -<6C61206D6F64696669636174696F6E207072E963E964656E7465> -show -906 1405 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2E> -show -294 1507 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<23696E636C7564652094534D4553485F4D6178456C656D656E74566F6C756D655F692E68787894> -show -294 1551 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1595 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1639 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1683 moveto -<23696E636C7564652094534D4553485F4E455447454E5F335F692E68787894> -show -294 1726 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1770 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1814 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1858 moveto -/NimbusMonL-ReguObli-iso1252 findfont 42 -42 matrix scale makefont setfont -<63726561746F724D61705B944D6178456C656D656E74566F6C756D65945D203D> -show -294 1902 moveto -<20202020202020206E657720534D4553485F> -show -747 1902 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<4879706F74686573697343726561746F725F69> -show -1225 1902 moveto -<3C534D4553485F4D6178456C656D656E74566F6C756D655F693E3B> -show -294 1946 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1990 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2034 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2078 moveto -/NimbusMonL-ReguObli-iso1252 findfont 42 -42 matrix scale makefont setfont -<63726561746F724D61705B944E455447454E5F3344945D203D> -show -294 2122 moveto -<20202020202020206E657720534D4553485F> -show -747 2122 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<4879706F74686573697343726561746F725F69> -show -1225 2122 moveto -<3C534D4553485F4E455447454E5F33445F693E3B> -show -295 2239 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 2239 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -651 2239 moveto -<6475> -show -723 2239 moveto -<66696368696572> -show -874 2239 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F534D4553484755492F534D4553484755492E637878> -show -1843 2239 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1955 2239 moveto -<72616A6F75746572> -show -370 2299 moveto -<6C6573> -show -443 2299 moveto -<6E6F7576656C6C6573> -show -653 2299 moveto -<6879706F7468E8736573> -show -892 2299 moveto -<636F6E6365726E616E74> -show -1129 2299 moveto -<6C65> -show -1183 2299 moveto -<63686F6978> -show -1311 2299 moveto -<6475> -show -1380 2299 moveto -<6D61696C6C657572> -show -1564 2299 moveto -<74E974726168E9647269717565> -show -1845 2299 moveto -<6465> -show -1910 2299 moveto -<4E657467656E> -show -2073 2299 moveto -<6574> -show -370 2355 moveto -<6465206C61207461696C6C652064657320E96CE96D656E747320766F6C756D6971756573206461 -6E73206C612047554920646520534D4553482E> -show -294 2457 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<656C736520696620282054797065416C676F2E636F6D7061726528944E455447454E5F33449420 -3D3D203029> -show -294 2501 moveto -<2020487970203D206D79436F6D706F6E656E744D6573682D> -show -898 2501 moveto -<3E4372656174654879706F74686573697328> -show -1351 2501 moveto -<54797065416C676F2C206D795374756479496420293B> -show -294 2545 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2589 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2633 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2676 moveto -<20202020202020202020202020202020202020202020202020202020202074722894534D455348 -5F4D41585F454C454D454E545F564F4C554D455F4859504F54484553495394292C> -show -294 2720 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2764 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2808 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 2852 moveto -<2020627265616B3B> -show -294 2896 moveto -<20207D> -show -294 2940 moveto -<6361736520353032313A> -show -294 2984 moveto -<20207B> -show -294 3028 moveto -<2020736D6573684755492D> -show -571 3028 moveto -<3E437265617465416C676F726974686D28> -show -999 3028 moveto -<944E455447454E5F3344942C94546574726168656472616C20284E657467656E2994> -show -1855 3028 moveto -<293B> -show -280 567 1 616 rectfill -2125 567 1 616 rectfill -280 567 1846 1 rectfill -280 1182 1846 1 rectfill -280 1473 1 660 rectfill -2125 1473 1 660 rectfill -280 1473 1846 1 rectfill -280 2132 1846 1 rectfill -280 2423 1 616 rectfill -2125 2423 1 616 rectfill -280 2423 1846 1 rectfill -280 3038 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 4 4 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -%%BeginResource: font NimbusMonL-ReguObli -%!PS-AdobeFont-1.0: NimbusMonL-ReguObli 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular Oblique) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle -12.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-ReguObli def -/PaintType 0 def -/WMode 0 def -/FontBBox {-61 -237 774 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020947 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A208511C6D0C255B9A5BB2FDEDB4D399C6CF1 -94FFAC236883767C0F68F4EF84EE696B677DE704EC3B097384F2E673A1F51692B7B260693738C211 -9F7D90FFDB21EB715FD5B8134FC87DBA320EE54C2CEC6A4D6BB350555EAFF2EC4F84365CCC0802DB -B3BD0E3F0D9F858647DD637725C2CAF9557FDF842A0DA6A0CA0F1B442EF8EE6CBF2B03858468A466 -AC5883CBBD3815B283343B39205803C02C917D06825C09E2BB14609FA32C28D720C0E14A4B12D4F1 -25FF6281FF324DA33A56FC49987AC7D3AA206540F8127273FFE9A3DACFFE2B1C269D3DB9A811578A -C7D532C2EFC18376F473FBB2B32EF642B19CDEC1D6DE83643723E3C6DFC87F97A7007B6081894BBC -45C955B7001EB36211B26AD7A3D07459CFB33F9C54A40A360CB802FD202C8E93D4DB888B325CE246 -D02D1220ABF55CE646DFB45F07CB848406E470362F80CE4C02D98DD845189877732744CC16C7F566 -9F77EF096EA55AFF98AA103EEAEFB971731EBF3782E6AB725D4E9E35B2968689E8007C038CF25B6A -E69451A4731E79AC22BD268F56942A233E52D71873E83E00A1874E04D3B22E72FB2D0671AF81C698 -53C389B51F4A257373AEBF4DE2DA1E4DA5E2CA88941F81EAE0E32D982064C8AFDD7A9A600D56D736 -05B9463C6240606B3361BAF22AF74EF89AC804A5793BD512DA2D13F4BB1B73EFCA1E621ED2A65D66 -5AAD0AD228B3B7E3D90DBDB6061E172B686E92355A7C7459D83199040A368B5697DDC3B81DDAD341 -6FF4405E1096B1240EDC18A0E9985CA55A0D697972BB11E9F1BC30765D6775BB68C69704BE200EEF -4E11B78ADDB6229D8FA49A6B1525ADADF17122C0FFF51A08AA7AED158724AC4352EBB91ED0C157E2 -4281BDC1FD610195F495E87062A8C38E0D046DA4067EE16E81BC5F87E583315B973184E474064482 -9B2A52E0D37E249BAB31988B906F891AC904D1BB8901F0673AECE60ACEDE97B8DB7935C6488ADE8D -FD898027424AA85A11A3DA494498B084133B857017A6D507D70A3421235486EB3CF7613C59139FD4 -DCB92EADC60BB6225D9CD0599779217BDAF4813A453989B2E56903F4DBB83D83DF4837C86BB4C3D3 -CCF98F07A23EBBF7AB5687C3E1E6792E40F92A7A466DE352294064537505EEF3F9C308C9EB94506D -B02CFAE289F10005A6E42D2DCE43731A7AE368564B2983038DAD6987F67062199018395BC0FCAF28 -7A2B040C71F7325FA1E9A9808979B2FEF19096B98B8A0A728EB98F2BA3D33B49E3C20BE992822C7A -1BCCA5B4E4D1099D456D8D7D83C57ECBA0FF21428024F7572A1470317CB8CBC8679A974E13D88C68 -1338C68C9AC9557F97784F4E1C8C2E61F26023ACF46232CBBDF3C0BCC5583B935FE9FA09A562129A -8927AE73988DB0F7E733C6561CA7C9716DCA9B88208A715166F2FAE6D5EFF289A9B2EDCE813403A4 -16F243F1B57EEDE7D81E10C2DA4065A3082BC92A38B2457368EEC9C3C17296CB09819E9E642D7365 -F9A6EF430FC7DD611EA5FDBDEDFA72634AB599EB666A5DC178B0A0BD1FAB042792115EF3B6222C12 -41DCE36CB38B738F68B1B3CB489FED9E53315553F3C5C3BBCE40451E47B7EA53FD3D3ABA6CE0AD22 -5DAEE734BDFA3BF1D81C1B42C6D856A05D0924E03F7627C5EB24D7FBEA3BD85716207F961B56803D -BE046E81ED5FDC378F9CA52C14FD8544CA7C539201BEE06487EBDC30FF3B28E8264EC7FD5DA7E080 -65B0A9147344CE28DA5182335875E9F8B2347A44E33DFAA167232A5C3E69E8C5B58B7C7216537827 -C936F5741B87FC68753EB0D4A466961D0050DB59DF3195BD3379F5647F8CFED35DA952D7CF2DED45 -EB442DBFE992711D22EB228BDDF36B8D7DBA27062D60D2271EA8E8412F4290B58F5BE26FF06F0559 -872F9DE4DEAABA015EAB4904BA1F509F6D517C6E897312DDD571D769BC474FD378AF4360E8B1F103 -AA75F48721B9E0BA589319E15D74AC0B03D730C3EF708C7C504787483F134EA6297097B46D2680FF -8AA50B7A255563C88D594B912F5574564A1371463674793E4834AF11D14C7991E7FDB3A6ABF8529E -1A4F10CAE79C60D37429579093DBD041ECAF03824DF9C007E96F45595A524B27EF8774A83AEEBD3A -7134AB4435C80944DEFF5C1CBA921B0A41B9651968581DA4834B3C0E6D4DE13C1E792FCEED26A72A -DC4D9E3903661D8803DDB58EB2B929CE31FC9F50A694116B00AC9F3EEF53FFDB1ACA3394BF111610 -38F39917B022394C75A0D467D64B89A44E5505DED7D9C6B8BA6BA098F140C9C00E09200EB4828356 -A2D6BE9EC1D5524B09C06D9C6FCB5E2808050A339B5E5FD4DD6C2035A48FE9674520901EDCAD107F -67AC8C8E508E6003011978D77ED225F361BC0F86A98B6120EEAFB73F7377DB1E7213E02D12C330F5 -492511B4DDE08558D75D5B8AA2D56A3111DCCD257EE96E3446EF1C76F000C8916C4CE261425ED9D1 -5B58CED128DAA6C1300466E7B152BCFB5E6FAAB2519B8A98F26B29F98133AF886A0AA7E586A090BD -A1DC6120DBB5640885C609A8BDADEEFE5DE0DA5B75A8A29E92515E86E7E66BB29581E5AFF8CB6551 -D8D1103DF60D558E7987E6F56126A13DB2C9A04886C655064E68A0A20D1B7DE24DAD22BBFEE1B7C3 -C208D4FD6A58DE78D6A0A6126EFDEE3B1A9713DEE94069A9F0A2B392A2F391C4C75327803B53F252 -CC9EF0323F84929BA4716C50385681FF5B4ED54929821594F9026B7C1297941B178C3F8A704CE097 -60533DBC6CF4B18AFBCBAD039ECB2EBDC7838A9410E7B227924BED7123944675A5DBCA388B710F8A -F6048B03DFB713F881EA0F3B191A5CD989EA150B979059C8AADE403855815D8F7980CE6288F47EAA -37C1097D33F13776F08779063C5217D7408D9835AACBE5C071EA40C9AE6DF685F4A9827B828815D8 -F3A672E73A418E5CB15684EB6C6FE0998A386E124D76620446907F993BE16FE5AFCEC681F585601E -18182EDCFD3024062A3082AF97E803C47D32229D0A24596CF7E03F18229FA631175699E2F0D60FC0 -9C4F1954C5D12D03BFB4395F0E5EB6C6877083807D91D93CA4177A6B5A8D2AA500131FCB670E7118 -73F8A3C77575EC93A3ACBA37EA117DB268CF10D04AD0F079484DB124F6DC14A50AD3B0294F7157D0 -837D8F9A6060FBCB385606066401708C041594E0396A0BE4B8B66FEA141CCE4BD29366A986ADB98D -9A6935C49C57F8CD415E93FF8AE0DF75E463E02AAC68DF064C1B789B685F84E15E512404E065A39E -9E8F5568A7D97671AE1602605FC7E4933975189837586FB1A55007FBB0E91382A629277C36A190BC -85AF49EF3F0F38D4ADD2B5DEE09916B79690EC83473C63E92CF617617A66DF472A49641DA10654E3 -AD3880D060B02A4A6C75B51E4E9917A2B6D8EFDA12D59DE5A8E222DC7E82F02F23A9D3DBF637154F -719B14114DBB102BE5EB76B441D7E9990EF6420C2E80942C8AED5A1D0B19BCE115B5929AB9E145F1 -496753DD6B1798324F5EC1D0C7F26FC3045D7BB46A14110C99BA07A45EC16002CB754C0BAE7A1A88 -EB387BB345FA70B0A38AB4D532C2DE49274D4F86F2582728A2CC54B4C09D26C0CDEB8FEE6A42885C -6207D74953CFCC583ED82DD7C0F29D35BDAE5BB251B8A2D4B1DC97E2264DCE035E359DFBADDE84F7 -37EA6A59C23D1A64D963E635769233624F7682EA34636B595CCD064AAFF3887D916867475731BFCB -F7F96D5E5E1FBE6AABF454C2F504EA4E8EB382911560195295C87793D5F7739AD7EC7176E126413C -D4D1058EBD7D6EBEE14BB94A1ECF28B686411D91E07373E891F78C4C0A05D2E8D90A8AE2614F7FC2 -63A762D0F43485473A54C31726F8547701D4A38D20565ED1707847AED9C805780F062B847E668E15 -565CBA07A72B0BA99F03FB57D26FA26FF579C30EED0AAB6FEC1B5DBEA81AA88F16F0C9BE869505BE -18C1CB79657D91D6706E2A3F0BE9920655B93EBBAE2B4D0B5DF6BE622C951F2CFA42AEDBF7AE649E -2150FE87CDBF5C2685EF36051080BF39D864573A45AE2648AD97662B1F69787031B9BC43511FB841 -55ECDC3D91E2475D072BDE6A5207ACEA1E0D2ECB1DA8A1BC4BEEC335A5C7102963E84B97BE741C44 -58ACC3D72A7E53B1F08C955F33EDC3A0DC3E7308270C0F7FF814B111459985733C62E8863625A551 -837952F3CBF32ADCFD9F345E14B585B23ECC440775310654DAF7F41E56FF45F89701292019A94BF3 -0EB2D65E14B1A1D6BF89D4CC43187ADADF3F6E03A90ED01E5D876BD3AA56E5EE84DBAA4DAD9824DE -9984BD45AF96FB8A56C010B3C3A3C6139D58E9D69D9109DB18561B55EAD6452497840B9AE90C749C -155B6329716F0152A7AD52DBD0B8A25B9995E1416681F38FDBDFA443879B5C4C25AA29E0DCC07DE8 -BB161C36D76EF286EC88D57C74BF44DBCB4FEFF771D3BD82C8F4E233357C48E516EFE3DB9E60EF16 -8E2C45B54651DF9A5ACB5F1790F7929BCB16CE5E9F6A43919AD287DBC8E12D9F9E97E5DBAA592879 -1A5A02D39D259F3CE273A870906A643CC18D86E23F115D2A35DE6926053D8C84B940B362E7DB183C -4905060316B269223DAD309EB5AC96DEBA757BEA45FA3100F77F4765334EDF3D659E09BD1A5552DA -492BE9174DD406F8353A059ECFEE3709422940A8C369919EE1F22F7C02412C995FE93DC4559D32A3 -155DD22D3526D89B16D9ADDC30CB7ADA6E52D62C5F2DFD142D4D7B6E066671EBAD08F54917E31704 -1F410CFD8A3243F8B39459C418B7B7C6494551C6F6753A94072D09E0D812351D62916383C6E061F3 -5ED864923002007E626089772D269B298DCA2CC1F25D9BE43FD8AD62D554C16AFEB7EF6E5DDA66D0 -5A810F003CDDCFD2C02FFF02BB61344968091F67D3862C1499409ECCA137B9A2A9BE314995B818AC -CDAE27ED4AD583BE29DDE4E8C2400C5F8152C85709AD2A4737BAC768FEB70CE81A92C9657DDDB2D0 -BCF9169D272A063C75C150ADDFCBC2F5F2503DE3D13231AA8CFB396DB38E80197A605F6BC20EFA1E -DE40CF424CF221218D51BEACE64A3DC88377E4F3EFE43DB4F4FC0803BF61764104CFF0B618C90311 -98B094E20B0FACFB94240B438B67BA298E31D3F4E31FD190E48BFCE27B1BE29D36E765E7D295E96E -DCE09094FAC43B87E294818FDE9363FC7DC5EA36A1497EE25762D02DFA00A9BE53F87ABE62E52ED6 -F59818FDFCA643042EC13D670DED1980413950EE43372D31AE2694B83DDA42E1FBB049F7E7B7E69C -93FFA3195A2462423DD2C022E5141783FFA07E192AEBC5070F08B23AEC9142EED56DA74F93BDB504 -78DA55DDD0A9987FEA131E4CCA0EFC51064E4B37632728261369C3FEDACA100F1AA78FB718ECE7A9 -F56296C5FB43781E63F36B0E1D34BB748EFF35E1953941F94D1A9B0FA474FD68B47183F2AC53A63F -9F1D30B9B89C5FE54C3765B43DB403D57994701C133E42B950D9BB1CA202F15B5E590EE75598FAE4 -3D5CF1546572770BBA9A6373F100CDC61DB4E5EBBE0A93E0E51C86005E333F69110B1C8E492F2BF2 -52CADD5B73E7D3EBB53E759353F1EF3C9B8B39C230D13AB7158A5D92EE4C452F81F6DFC18803280A -A023832FD0DCB482CE5AF615C952BC3F7E58F6417D69775FC7C0D5B405AAC632857736ACF32B2EE0 -F2A2C0F3B3CAD483C614505BE94706322F2A2830FC5AB592907D0291ED1873377E7A6158140C2CDB -1B0E27EEC9CA50176102200992308045CCB5A169B61EA0546778B8D280737319046716604945A21F -2A1CB9E15E3A5DB31E0FB5A3B0AFDFDF6F3424B7536D473F9756CA3694DEE4301FB1AB1AE47128F8 -D2B461C051C1B999DBB010E78DD13AFCBBA6F7D5226D540527F17881A18F551B3EEF76A7E28B4FDD -879381A2217EF2FF9F9982E9EA70AD2003B862D7C36D57C5FF9FBEAAB56040FEE973EFC3B34D8319 -1960010110BA10694C17B7635AE03CC1CD087C0B05522A7A791F0CA34022A3F5860B536D9551BDFD -BF560A07F63AA4E687407E5E48584E689591F1B52671213E430A708C06A34D2E1D51CFA6B328A122 -007C81B5EB263B967746961BCFC8772F8502DD95898724ABF369B0877F3313A167F3F714023C229C -5757D4D46FCD9B4AFECD093DCABE52B78132CE9AB6225C9A344C4BF8D96F2C50C4272CB9AA0D606F -013B2642F8C880E08EA2822C8CF5097D2CDB64932FE195ABD5FDF36D3BE123AEDD8BA2F82A8A628D -BE3ED6129DC0FDC4BE50D5574AE4FECC65062E70F4703BFECB35EADE196294FE173EA57938679DBA -6D15448FF44C0D1A903B202439DA93C0B0E612110068F8079219AA89F435E44D0464F54833BEB338 -670BD820D941DF4B31F51B895BEDF833F9C43CB7616DB80F988CE72FD3C12C7D49F740CF85B4766C -0ED398EB837695D102DEC16E24B7475A0F5DDE88FBF2D6B94F126417C811E8362B9CCC52D8891C13 -C10937AACC228D621D4712CB9DE0BAB60EDE2A97E9292BE04E42E6D3425594DF56931A61E1F96172 -6AF6E6891D63B240E6E79E5BF30C052091D681BA1102409874CFD8EDC3EE2BE331676E31AC00F807 -91D1019BB789CA4F5907F4823B002AF3581448C352BB67D80FDFFCD1C5BEEF60523330AA2C045600 -8F62DEB55E69AC2F86369FAB1ECC90D2487954E61117A90D9269A65DFBDF297EBD29C3DD1F62755F -8F289C42A534F59650685F8576EA2FC5D26B99B8E3DCD3F1FEEC73131000F99AA9868EA9BAC0B56D -AE2CF46DA6CC1D18C0AB8D77BECFF7B89992175CBA2E22779C13DB9DF53FF5B1C8FE95E164997D94 -202C37175E562C8622989B075CDCDE173452C064274354D5DB8F7D5A78D48AD4A103B9E47500D08E -DC7C51C1F3CFA7F43C3686A3C24A7EB5018B0F419961564F87E212CE0A0741AC68D6822C7AB9FD68 -85F5D0B2AC249CB7F50E2353CC4B0A6A24562F564FBBC7090C3FDF1284AB0EC615E0B3FBE132F315 -70C8A65C814F93910AA4BB80D516CB70D2E1D11969238E6F022D628FA2F33A0A15C4EF0CE7F753DF -80A8AD9494885A1B9ADAE6C38AC9DA6FB0A61696AD3A502630252AD7B574C841117D34BD20BD6581 -217D977B35F5D04E02B933E1E84F5C090F6615AF484D63265D28517BA74BEA8876FDA332A84AEA12 -E6CD82B94AE10A778CD3A216ABC08495EF319F06AD6FF8ADD237D911F846A514FDBFAA8A1EC8E0AA -9F80F11F1CE615519A4B044F3D1CF1A17D7F3D2174222A5FFA8B39F20197FF6CAF250B6ADBDBF519 -1C525070C8D38220FB501C223F493D80F498621A02EBCCD6EFE914F16B2A435D60C0A1A453E288A5 -3D818FE1EDCA7D55A26A017F2EE47A816E90D6C3FCDF0035EEA307DFB06D2BCCE43458A67354A4ED -B6E5C57233DE4FBE41ED07EE5EC77A5DFADC4032138DA9E1B74428CAD02A913E40152F8063A774D4 -FDD4070E4B8A6C089F199AF7C529C277E902195DB760D81EC655DFFD1BB283F3C5AA8BB58F2476BC -797B2892E94414ABBE96D4DB93E280CF7DE23EB852E7CA954D6682A6F1A4BE0507884C2A05AC863D -2BA73F3B54668397B6C54DC2F4183130AB414875F3C3D8792BF7E5FC4D228DF87748BF0B14178DB7 -E3FFB7891D700A1E9520D778B095DA80E4801F53442D5C073EDEB706A5DB8466FFE7E701ABA9C364 -A37169F585C883A83713A61C9C3BD9336A667EA4E3DB5F4DF6BC6A552BE8D3EF093639EC67E5FF71 -8959F9902477F5AA894ED2D1CD312ED82EE417D95C49C96671B23FB0E1738E892ADFFE62EC1C3D4C -BEB6CD089C98DE8D247DF7ED17DFA2959D3662F105E8386D75AD308480536959F8E6CF8F2C6937B0 -9F2E8137C811327D6B165ABE46C51834A955FE8306D10033F8C2A34667F13A8BA831CCF52C7A21C1 -3DB92F3E77B55CE291F6190BB1D194A33FD73151C3F61ABD2D8A0C9BDE90E796BD996D2D0094DB2B -E98657E751BDEEFE8A43EE4501B98F0CC6D80805189438872A60047A8CAA9039893530A3E5F6BD75 -BB466B25165737C939AFF3EA59BFF4A7DB09C2A5B36B8A1F0C6C5E5870C7C9412589877EF44F8428 -4B8A53B5B74315CE72D2EAFC631BC4CC2E5B71DC958B5A6350CB5F615C3A4502E973622E3E18193B -69572DEF1D02303A375ED60ABA1BC8A179FAA0F221A49078FE15AE13383585FB45FF4D5F3BB3D0F6 -D8BF62E9BD6BAB3C9A7D38C8A5AB0BE57ACDADCBD02B1DC7952D73AEF702D406F62719922BEA96B8 -FDC9B879708E794891C7A0A42F2CCD6812C3F4DB030B5178E3A627C3E77621D312CE4EBE815CD387 -7208FAD92761A5396B67E835222609F823728B1C987857CFEAAE21F2AD5EA9D841212993508091A4 -A2C268BF1D8DA1C650F6AB93995E7C13A3F84DB55748C626FD09C0DA1E3325CCB0BF091E996245BF -51EB486680162BAE63B6513C74CE83B92359938439921950D713C69324A87BCE67B45A030C9CF10A -DFA0A82781D49FF224AC57A23C6CB321F95915C5E14E41FA852F66E1E2044A9E7B1DC3BE9E818515 -D28B2C4D2F2210098C39557067062BA4239F2AAE28816D999955910298A450741947A9A1AABCBD8A -FF3530626089978C87DFC73618C044731B6DB8007739A9699ABC354A6F985E03C11D750B8B9E9AE0 -5436205FAAD1B895B159E2C90562B82A62EA1A7FFB501767DCE2B11C51D55A17529EF5ADF0A0EE9A -96D0E7E89F68E50EED813836531B4B46E9071E84AA413F4135CC882CE832BF78ECFA7CAB0C9F64EB -92C86DFCD1152BB7D4AB33831AA0C139B555967F6346068D5C3351A7A4368EEBD2933E6B9F789DAF -37EF536FCF965C397AF1B7F98AF864B301F3F440B7ACF704B59540453678FD6C1504519481893812 -3E2F47B265EC4F5CF2172D394543D84CD4281165CBEB11349B315A85DEB2D1699507B0C8C110C726 -62EA2959C4962FF093AA5EE6F21F89B3CCB0149CEFEF1855B9A48D28BB363416C015A1F4EA1975C3 -D8807F616C5817C8162536176F464A198EBEE6C97029F15F414275A39B8219128B8C8542E9483550 -7FC2D3908BB0EC375771280B9EBE87E827811418EF93E52EF70546891BFC0FB34969FD7DEA4CE752 -4D9EEFF2B46BED908C0FB2E02EFC1D1624642EAEA1CAC1EB4841E020532E88E59AC890E6C3F44734 -B99722E9816402D1D0FDF8045C5481EC055100836EBFB48E9FBC392143032C909853C9BA38A19363 -141BED09DAF02FDF4E7CC9808321CD0708A1B45270BFFCC3A0D7C27F7E781713D5DECE82C72ED303 -86B02D14575A1A6447547ECC7FAAC1BDFF332C92984758E242256C054656CDD2C45D46E67AEC6F83 -9F95D74E222A6EAE12EFAAB723A7C816D4E42D4ED2725A794743F67597F3DB8CCDDE45BAABC25726 -B851E02E56341EBE69E4D91F2A233583EC816F18A1DECBDA4AB69320F55E730617360FCFB8AC2D2B -737675B406297F7F8C4BC370CB084C22BFEC5FEF02E9AB290282F7B153F0A4B1AE569F1E52371A43 -46A748DDE09336CAD1F5337FC3D7CF0677091E59480AB15021E023E356B0E1BAC6C6471AD53625C7 -0206C338536F4D0D40733AB217E2297F86B593717C61458B6C93A16027CC886A8CFDC01EF19C34C9 -A608B95A84B6A2E31454BC03C10FA55CDCB7B1EB7DC16AC1E93981A46DECD7E7F00638DCAC568744 -69A2D9B45CBC81398727E4ED3DB5DB31965F358D8179CBF934EE2C4D652C9CC211807F070C80E3A8 -222B4C31FFEC8DFB9EE07A94C973462254BC1B1581903EE6F9AD91524A787129A63FCE048B45BBE6 -855826750C586B6B23B805FEC3E7AAAC079576949A06F422FC2C826BDB78AE96135E9E2C20C2B2EF -F6171D610B2EB8635ACAB7C5C5ED9C9FFC26CD54D2FD4CB9E4294E178CECA1E16CC8E3FC06518BD1 -6F4D63AE2B435753538834CDD9D8AE7DE624006CE688938031336351A6578C304C2E5480A3FCB43A -8BEE4953DABC30558B7790C6E7A6F0F9FFA557C50417407AC6A0DDA1E736F7070BC89455FC293453 -3DB004AA9070734C8C2608A07330E421A0220DAB99F8A77489132F6413ADB9EA637F3B75948050E6 -67276A55BEB09D4153DC126BBDBE0DB9298AC799A943D72AFB769BFA1488D311BEB86A907EC9385A -AE4F77835DFFE4389E3D9ADED1B08BBC2B1ED6084B3D1074A326CCBF38E06BD026919107BD03BD9C -30470DB779508DFE0DC82DFFD2DED749E872EB7EB9DDF509D5319865070DD76846C34E4E43691AF4 -29AA40DB4BF2CDD50B275589987D8081F7C5A0461AA5D1455A660178A94A0BA0DCB69C3CEBF5EE04 -26D6534F6F919D9795AD6A0E1A1F452AF3B4CB2EA54D6011FA809132421D111EFC51174E223AB6A1 -3596411A9723079231B050CEDAE7659CF168C39AEA9C6902C2CD37D25492CEE00096EDD63DC7643B -667FDFDE5B595DC54F0A72C2650E1E46990584C78A5CEF9BFC3C5F88CFB0C49CD6CADD9DBA675177 -D601927D75C6902B55AAED0E9E3CB52A577C887D581B3CE6201A1C77C9546CEE5A13B92963337F17 -070E2BF9F5C5E86B84225863874618AA50F4DE855DE567BF2AB7163944ED43DBD7F4BBC0E1623180 -7C43DCB47B2EB694E6FEDCFBE26194D2D9943A1BFE32AA1E5305F5E341EA021F91532162978DD1B8 -C5295A5E7551E2DEE46DC2347C6B32197AF430AF3BB676A53BCA9BD1EA88678377DC0A9A86E2AB6D -E29E3E261BFD5573C66FB5687BA9C0544D894A759866B066E1DB5C66E60AE071CC3A1C4AE40197CD -E4EC723F7B80137619DEDC99AF57A5497D6E03C1C9E672E74F48F6C213A3CFACF2699CAE72345A51 -C71C1D69348DE5BC5F443EC0EADE1E76A8A33066922CF3869E3C1D26A3B34E540DC08EA4DA2DDE3E -EB17C16790DA4EF1A3A76D71D34B788A87838BF2A5A3DB8176F9C097D2320050A79EA6C4A94926DA -11ABCDCD26DBA09FD33F30AEED977E8B5AD928F3967F607628859429DCB4ECEC7DA3411BE35A0385 -1017B535985632639D378CDCD13B00FE537A49FD9EB6DF1E3AAF5C41EBE35721FA6833C2FE08AA3C -FFC3477E7FCEBF9EF9F4DAE62FF78F319481C3F1E72999C8A493EC6EE295316B58A5CD62FFAB62C8 -96E521B678342F04BCE1613CF7F6778CBF5227BA20504500D743270771953ACBD5C6586432F3FA6C -0987BAD33B88BC6C15D29C4B3CC54A9DD72A2357AA5BAEB2CB057CDCE72DC80CC98C62B16AC50B4C -6A7641379B766CDDF990DBB2FC7F9CDBBA755B6E3DEA438FD6699C30A99A8B3178E6D613AA938120 -835E517431D28114BCA1AB745C11FE6E52ADB82B9D3D53A33BCC49740C93017D9531ECF43831359C -5C93CB0E926DB440B139E3125CC2E069B1CF6D96EF68407F32DB517242C3AE0BC6723E560B0F45FC -7F87A5E44E1751C8B7F9F669C24AD5CF16F84FB03BA121B86B0694234D8F2C9C947269AF96FCA08A -78F736E4E04ACEA44C5BAAFDE360FCD8BA6A59724CA86160A5527FD564468123D302DB45173C1B21 -6B01DC5B6D3415B13FBDBBD3121A5493374B3357EFB131CABFE5087AA1D2C7472B0377066B3632C8 -2073C6A846285CC953A8F28E131CF587B35217EE498D9A1DB57B063CE068DAF55D8CC1771C0C3099 -9CA4FDC5D67BE4E7E69418F6334BC6149000821B89A7437CCDF9A6A0ED702D5968F1E04F7E4FE9FE -C9D1E994885CB624035BBC5426CB8EDF0456828F8EEE75BE491B45FAC192A405EBA25CAA4F4C66C0 -DC234D7B417628DA5276C08260BE512B2432256C401A66E3B583E69D23E9FD278CD5F2178544D054 -16B9B4F61A88A4728AF2CEED07C08E207F31D644E8E3BA1E4E2F9D8E30936BCB9C6AEB54E37DB46B -D64F2ECC1021336D0564DF0F18E5A6B6BA470233D8D41FDD9D1079706EA685B6D8A740570BFB78E3 -984BB155C3155C69BCCCB41CB51975EEA1C1B4294CB546CFB03DC31BF86EC3BCB1977E8F94A771CA -B09DE12A82F1D6C791FA7800E5A21DF81C9C8FCDA78622ABE75B54AEEA747AA4F26D563200992E33 -7231A430137C720A17D44F3AD6CFFE63B2DE12D3184BD3E151F955786B8DDCCCB290C42718F3A219 -1759DF76371C2FC177544A6C425CAB14AAAB31628A9CF9D71B5257AFF0D59843989CF0D747375A26 -DC9ED29B66AC2147DA0168306C48C2484C70CA92F33C0C138F92F276F5EAF5EA3082A8A1CB12DB66 -1633C2F71E3B69918F509060AC949FCD52C36498A2ABB77D139DF1EB33E3B846A7C1BBDCEF5DEECA -4EF0AD250CEA9C2751E13EF7681E8FAE0491CFA6C144DBAC1FC39D39E76EB12D3EE9CA159AA77D27 -94F0C433345B135BA632F544082BBDC9471E9FA3AED3A7D465AB7158E8AC97F68B1FBC8D368E2350 -45C18EFCCADEE98778D894D96301F903283C5AE355A863BB0DC5809158F7E108662D04A5C1234915 -E7BD5B4C30F9EFA55E702E54F87FCA06FB321507BC57A1E55CC117E21AA4E3A4DFB77C1A949EFE36 -6D93F2BD827EF8CC16D387CA82AC039F77FE995BE6D9AEFC87F8D809E90C1017803BCFA1C737DAD5 -F1A631EBE6894AD20C70791665E7BC71F21C2C3F4462F60FDE75C8A377CF49BE99314663C6ECB538 -B1BF021B2F2174D2B22CF6FAD115EB0ECE8A2E64097A5FB0A2AF666E1EE13276AEC59FD0C9D4BFF2 -3F71E835984E5EEEE36490C54E077AD7355DBC98BDD37DF29B3DDF8C55480B7349C4D17322418705 -796A8C521FFF920DD11773FC44FC631C7D6E9B420D7965D7F62EC7385F2BE30A51E2D796483134F8 -40AEC71FA19ED1272C27F98F2CDC9C7E54DAB585AC1703ED08F5F9E825564902EFD08EDF99DFD494 -44C21FA6BE16CB8A1B6D0C8A5ABF80A50BB8D055483176FD0AA07EBAEAD88FD694F96FEBD60751E5 -C4D8F9BC747D4F4030BCDF9B0370B7A5E0A6923FF60DEA16EF47F886F10CCEE6956ECF41A21F7C59 -6F3BC78299A9657266807E01762B2B2878E551914CA312C2A68D34CD91E4F5115EA1FBE801346E14 -AE529049089B6B0273E258785773A9CE8E4B6C4211CB7C2767319576758F811CBAF3A3FFB41B3130 -6C49F3798B698A47BFA2E3CA0251C4D90C0B02ACA28C611744526906791D9E157E54CE4E1BCF5B68 -6990BA8AB7897D624EF00EAB92CBAC255AE9177DA9F0D86447D35B452CD2F337147B5D3EBBF2B952 -35778A72914EB3707EA78294B3A3BC4ACB19FE87C72AA1D982E4B822F07B115CADF4D3E7EE3D1BA7 -08653BEC6F0A352A0C33252ED0630E7274961896D461EE8BF523D5911BAC1C8AC763E5FB11FDD217 -4E1F129675969C195476C7A5E18A81BF9A11ED9F2336D5301E3BD32174ED5C933E8C85D6272EA218 -52A6F7E2AAB174E0965F73E0EF89E906BAFB181DBCF8B1F5AA0C12D12C6272753C016AFEC2EC9F95 -41B8757874D6F2E061ABBE8B29281677246305B3C41E90418426C575BAA216CEE3C5EC29B2FDEE1C -77C14FDF940792F48A56AE80AA33E370B037CB28A7373F882022AF378F26B6006A049FD3B35074A8 -65C97D153352ACC156992C00DE26AD21C982C71F0EDCFEB61593BB40FA5F2CEBF23C4FF34A4F4BDB -73CA273C269242D1C6117262B7C47771F2619FE5710855134A80FA8F92BB2425CF88940CA3450F81 -234ABF2B11775929B12CFF86442B2AA0F4243D324A5983E5D1829775B3C7A111D5622D1C4E2B2A2F -982FC8A95F789881416DCB34950A393F4F1720D2212F3D343A17683060182355DE9E4718506D76C9 -184F8DAC55788D7E603CFAF4907DDE965A49C323DFF425FE88C09AA4A4D16283F9B14AB9EF1BB885 -A954034710B4A9DA4C88A8A0932B18D139A687303EE562EC9F656F12F3E8F27DAA9C75DB0FA946FD -0E1A982BB58E040BFC0A49A4AD8CD668493FCB573C849EC5474049A693CBEBD4D79AC7515047CC34 -7A9A7570C90861F3ECFB57B9F53AB9C0D6B05C8C570A8F3C04D58555A45524C98FF091B8F8A422F2 -E0E9E5A7B7FF69F1CEFC13E42F1CA276BCD584516D266BA6838D5E9CA9E9854F50C7D92CAED61AAC -AF758A7C7BE59C3BAA82BF32B691ACA3E8EB171E08AD22C39FBE586A54E6E4DE2CD86B31138546BB -8DA5834B2C6E4838547A1B67E651964E43988C8036931088904BBB589CA901E7EBBC094C0DA81E09 -1915D9E46828AD8596FD0FCA39FF12A6C27A359337F973809E81B2E9E3D43B3146F2516667E607FF -EB9AC80FC95A7B7D4DED551FEE0F3561C70DB2D69ABA96673E39E3397F1C3F8FE5F48BAB8AD6E0ED -8901F90F6CFF24E80CB5DCAC498506C4D01033E497C1241E413B022227A3264DA68BC3F91B35781F -A2D018475C199F43CBA7D3A0D5697B45321BAD2C394B207136E1E16B41794975E8903EF2B2E1C33F -87CF72C325C11EC0B92FD3890ACDF60B521DA32596763BDFCDCA837ADC6F26F129B23CA32F9CD39B -33E64576970DF3C05B8DCA4BFE2F17E6C5678B84D69494F1DBA9FE0446AE6AFEAA1FF245C07916C7 -B7569E6267C42B459435A1D116CEC665B311E404171774C0ACC8DDE96B0D9167C8CC7D99C4240559 -2D745C4428755500EB4719340D2FC6BC215B67823F69FA949C08B5EC985D7AA87C9AC1F9BCC8994C -6CBCE6027B7D1E0C22A83A5DE61DBA05D4AF6884C95F46BA7F253E0B2337E312916E163CAF9DB2EC -56C5425990FE73EE53E42B3BCCA1CF642F02B0C5ABD529B568E9ADFF865B9DC190240AD78AD226ED -884BED3C285B4CB0E3929E805C67F1318D186504D92085764B70DE6AB5AB6990F181BDA50FC31262 -348D980EC76608CF08176C2502E065AC2D8EA5CF9E2D44E2B70A7DDC7B922047C471DF8A0B2087D1 -106B5BD8A830EC0E53223CE3C96EF56E5541191167860EEA58D696EC357EC55799438C90156BBF2B -13A0D5C9EE93227746654ED73EA5B9CAB61DAC5BC690F89C87FECAF9AD03BD39E438F43B81D39E07 -E0422F94E8B096AB38C88BC2E1A043811D8141C1A35DD3A6DBE41620E83C8ED3A379CD80D4F9BC30 -41BB44B933DACA7C5D4427AE94A176829F24B5968B713431CB8BD9F53080832C6B784CEA9B515687 -F121983EB9D9C9CE8BD4FA3BEC48AFE64E643B7BD86D8383D07521FE5D091392BE124CCC91113604 -3824B686988E7C83AEBF406D2DA88FD952D0FA9327F4AD04C55FEDBFBFA76ECAE8A176C516479AE1 -467125B7EB3C9E7C5B103BC0C470946346DF271F8EE19DF7E3FF7478C35EE059297F4BF21A5C7B95 -993BE6202E897776952A7ED0613A5CACAFA731FFC633CAB62963150E86EDAC796026CE02EB235B9F -7A54E0B0C5281567138A612BAFE409A818C216DA8EAC5EDF9D1E3A1E3514AE50735A111B4D2AA083 -4EC6C11E290D58FF340F82F0E079F1C7B3566F2336EAA45BF72BCF88569988DB5F65D4C1E59B50F3 -41E45A899656A0B522847ED567B49CD5284FE50E5F8652CDAC1C076804F2B2185F6A51ED19DD4941 -2E65A0D2DBC844B75E2DF71B009776D9F97A4C6F786EFFEB87A307FB6B912BB659DC2BCC6D509A9F -BDE87DE8D716040A8551B6CCFB7743978AD992D14D2B85CA052E87326138DB196C24593F8F7ECD6F -486F85D1666B9DE2ACA6C7900044EE369D223524664A2790B773F9EA26E0A4CDFD709942A44298B8 -249506EB9B77BC887DC0EF947DDDC7CB3CFC6B48F060DBF032A11884E6C226D9D447A5A458CBA325 -D57E144C6DC295262763E7BB8FF6A0CA473EB7661C12E0E8E23EA37E8AB3387B9E54686F3E57765D -4067E521BC1AFAE52394227793C737C19208803F2F2DA920B553E2AAF94EB992AB17E31B58C15CC4 -AA8A1B444DF5B3E7CD937CF03E1F7FAC63342731B4589F16939D16E8E497A74CDE5686F529E9495E -1603D74875288CF53271DB9313A4511B104F80B179FCF213558970A002E945281BF3AE51E668DD6D -13D9E85152747F562CA0B75DDEC8FE9FE31F8D05B0F59E802888A7A4F19B29954A31108D2F041367 -DEBD6AA1CAD856BDD1427E9EFE89956FE28D500CDC6A0CB80A76902A08D0BC6705583243F1DD8020 -749B257EDF4803BCAA653F7FD6D8B91690995BA5EA3EE92FCD367C11601C6B8ADCEDCE67B16C596C -5D200693AC5FA15D4CC6CE9DF7A71C8A925E99F5085313D60FAD25C1BBAAD28D4AC2B69062D68F39 -0530A976319A3904CEE44DC9451E441AAB4780425440F8C499B81460B5D3E268974145117ED843B1 -71BB14AA84C3A084A7D8E07B9979260675D5CE6534DC176DDB60DDE90F6A3674F67462EF78195F8D -FF74FB5882B079DEE31FE92816F16CE1A70D07752EA25FAF5000ADF79BBE7D17EB1BD2F9BF6CDBB6 -F078CAF97986442680A8FC4121866F9CE86C385DE34E30D8B9768A0136D9EEF79A4B38EE99CBB9A4 -D32316564C9D56996E2595753EA71BEF684834FD030D38BB100E2332B026B046316A53270A96DAB2 -182E994E91262FB03D1AFFBAD623F1689228409884F91DBA153030870A7BEB2C7EE2DEC51875B137 -33B7929041F8D23A94904BD54DD4BC9B432DD0C78DD81639F46D686FFAD39AAFBD1B6C1A37E248CE -48F23E12464D5379B4AED0D50B5A41577E6ECB75270E9AD3EA7D0FC09DAB271FB18B51DCFC0069F1 -5D72546E6C51049F3425AD005F88FD7F02042DABE9F097F9D6A076B30D8CD777B1EC12BD163FDABA -5972EAA61E3C87E9AC007A052B1A3FFE14D7D43C7A0ADC89B1DD4CB4F9C762A84A6C0701494B2D8C -4E4E1A9245738BE4111805C2F153A20ED9FECF2DCF4C8F7C3BAF84D60454A7403D4F5F81C6404173 -A7BA81BB0CEAECFD493D877465DC5735D43E3102CEC57B8A589182FC65A4704661A9E351FCCBC731 -5A87E62F65D24EEB9CEE979C6E10DBCF5C162ADB926EC8CC9BFFE381F6B8A3AC0A19D1631BEA2938 -731AFC99E8EAA39BC75DDB3A39D01AD8F0BC1838F4D674B9BEE9F6F7BE4D9C8BD97E8D171EFF330C -15B76614A1FFD25B3BE19E4A201BCC850F926ED51616318C965AD2F0E56F9433B1247C6D5B72EDF3 -D408A3E0674A509BF30BE813A5E669D72B978794683CA8B85E3469EACB167C30F7666DB5E081B81E -E99ECFBC1704B9646B1A29E4A4CE5654CA8409ADD60145DFC54225BDB8485E39CC98CBC3F38FD0A7 -97E5DFC2099452A2418C6636BD2D5F6B24345ACFA65F4E7DBD2D0AA0C1776A4920B4466C509BB5BC -7D6627946C4DCB38A27098B7B5BEEDC2B3BA18F927077F71E38644597719652037621BB350BB5369 -DCCC073954026E6438FD8393DDB3630C4473F06D9FB9E422E435566C396B12FDCD5605DFEA232171 -CD8EF298786806E9159B84599C26D4C7D8C3BB064665CDD072E2083190372AA808B2268B3FEC8878 -B6420CA829BCF995DC20E067EE6B8E44D2869D51BA3AEDD1763F7F8D2CFB8EC41E6E9E0129DE5343 -1457960CC51D546B10B8B6CE08A1C2B79FBA448DF9783D815608A16C55E589DCD8EF6B04C66232F4 -7A473973A35618000D79B8173258B7365C9691DDFE47B16EEB08B28F881828B946FB5D6FE10ECC6A -FC4EA1F762E90B3320403382E42AF4885B183AA48DB5E4DFC9A54E0B4FFBF7C26EB17A4F13B4BB93 -12234434FFF05549E7587BA0373ACB3E31418BFAF400D8938FC6466B94273D1735306AB912AAB13E -31DA3541C1733E2A7E4DA5B82767D37F3084AA7A7C488CDCA7ABEF77D19E42B4448ABBD346E9BC28 -8ABC4540C0A1CFD0BF46C5BC7454B25E27E9906A3E6CBF678BFECAD1B19B4E42398A210CD567EC35 -FB115D5C0DF0EEECE593982056B0E1D14C292F70B3E049984F8881C8B477956AD3140B4AA22256DA -AC0D11C4126808B5B9F922BCC5F24A77FF352E2C621A3941AC07A20E550A69C49B1B87D116EE6F2F -970918F0F1A501166AC4423FC212E4EC8039AC7F9C212D864F418CBB92948FBD588228108FAC1AD1 -837070512305C110F0FC3FAFE6E1529C2BD0DDE868A9EBE5137DFDFC5C12A3D08014BF0EE27B1080 -02AAD6B607F5C5C0F1B1EED3C552919C9A2E97204A8127F97B1066607ECFB47BA95EF2B51F007C29 -3B2F6A63041A9C1120D9CFCD5357222E5B02DFC73CF94CF9B5CB00EAF073E9BF253E30E09B50341E -57BF245A746EA31BFFD0B00201C34CF0881BBD1006BC9BA7D420A48E53686B598BEDB3449924EBA5 -8D5DB1B1B01AE2BA281D5758C99EFE38ADCE18F7B182FBD0D0622A6EA497A4E7C00C7D17299A2765 -EFD8DE376C214D01A21819451FC04A0277EC84A151FF93903D61C78AB7886911E36E12526ED855AB -43F6289C1890222602B8EFBF15782B374AC1E580B6E963403D6D15A051DB8558F2E61C0B9476C6DE -5D4861585CF515CE951732F20D32969F39192FBF1690D242AC04D47E0C53D467D0FE4656B9526C0F -7F852348B0437737CB0F29ECF9B54A5E17185236DD0C16349C3496F3ABA569EA20E343F6D771210C -39DC932DC65ECEF94575C6E76902CDF6C8C8361F9C757A2577DA535187FD526699917CFE0AD438C2 -A758727B306BC7979547E68B94E87ED820614BDBC649D469EF6B4E4E3DD2EAEB5F80B22FE576CED2 -56495467C76A75F589460061E03F3A1B065121A5ABE3E2C51148B3DDC9F624C97889AAF7FB84B158 -C015EDA5670746C6359D27B0C2BD65144F2B88A64331816DA904572BE398E015A9924218B3EEF951 -23AABFC3AC8217B7B4F691219A1C9DD0A3EDD5C04E63ACBDE71B423522532561F4B71B7028415C34 -37E346BE728A415596AB749015C1D59BD8328E39A850CB98085B34B57FB52DD1D154F98FEC49B3AE -BFCB1672762E4D2A1ECF02787F59DF1EBF2625C3631BED849B298C6D226BE4E6EA2AB66A287D2BA9 -2A6C9C612A5F849B3CB3C25F17164BE286F6E4F5E7E4C9EB17BC68AA5EF0190B64696A570442E1D9 -BDD1A30E7692524E30E4B4C3DF84481DCEC6E10E7308E65DE9D90099F3FABB3F4F766BB86CC98594 -6D2003E21287761A7386CD8461615B570BDA015F5EFA23D18E83C325EE444EC166A1A32D9818C2A6 -5A092D44156C06D3FD079B92450B8A491CBB3529DDAC7D95AFE8EAF33777FBB265FEB8A4B9AFF2CE -CEFFF49AFBDCF6C4197497D3B448866D70EF28D8E4B17E7CE95F43F64BB48C4A73EB84B26650F62D -3E5199D64DB0B5B87702650ED0B850FD5D16C848D096E4C7E61BC63B2A3ECFC099CD713E12C91A65 -77A88D6F55D348617C7A49890A86EA8FE2045704B5ED529DB128C9B19EE129E5FE6498CC97087F6B -DE96007C9D01CE9CAF75646E5A5B32BFEAD9362A52223D746943A2D09C536CFAF78E601BC2D2F0B7 -63AD722E3A7AE7069D65F9F2BDED7278511D0120F5EA071D41A69F8C2A2D720D3B24B4BE61C83FFB -EFFAE21B0560A6FD1A44E53E42E0D10E0E93F421A8A7E167BB65F0D7F1DDE2809FA3CDFD931CCC69 -B119C83238C1C00EC100D8E7AB1C7FB02EDE97073C8A5860371A8132BE391EB1C397B61F93876FEB -438C288EF2E38DDCD182A5CFBBA994A94A1BF818312CD8234215FCCD7C240A15AC01A885E1179E5D -7D6305DC2F534BAA141F25EA6A5F356486E5FA0AE3C6980A9F5E8E99E7AE5B95AC42977510970245 -4FC951E4319AE4B1DDC9B07D0998372C0A95ABA6985A4DBE6DC633154FAA30ACE689D36A7F17011B -F29CEDC58A6692A8B3B0A5742E6CEC2F69B255BCEDA762DEE72F125EBA98891CFF4D88AAC14188A1 -8D81424979C9079E44890D94EE094D4CADDC1C7AC5F6791FAB8849CC0240A579ABD800EFE3AA4EE2 -F78119A3C2806C05C2B1F17940BE73984982D1C0065433A9BD658EA31AC819DA9A11B87475BB565C -C294B6F302FE3F7752ED9B963C5279B5F1196762D0E12E6DA46FF9A0CADE3876D7DF695D8965CB4B -47B351FA3F759811269376B2C3134403633FDE27C9B024F6BA81F3E1699CF64A426618428BA6C326 -6BF016C5DAA5FA4CC82FB6DC23FF2D742160518CD3A65ADB38E53F1067076CA1625466E0C64670A1 -564A54CE14DC5C57D24A12283FBCBFFD0FD594AC2A56EE58B552F7586825E4FB1EC23F8221711692 -C8C56F42272B87EBFF3865191F1C11943BB76D8C0CFC53ED452AE49404D2C8193ECC2A7BB8CFBF24 -870ABA38D2CCF7869E9363DC0AD94FACAED5922B324DC3B6FE83E7B34FE29ABC1EAD62B49FFBCB81 -1ADBB5148D5AC2743E3A058386036FADAB6FF071BC1C3B8023F908B6FF48DB0AB1C9C67487C35211 -D40995E1892C8B66AD6C9C6203F6F8B513B11117B10DA8725AB45B4437B5A88A96AF3178D856D601 -196E8162868A83DA64E408FDDEBD14D6591881EA652032CF2F88B3FD6C0479C8F89AC68D14D01AF0 -CEAFD95AD146E68FAE01A07F39E7A0C5E4FFA6D6A91D710827CA5ACFE7D1F946A8D7B67621D60F53 -41F32C12A6EFB03AE5AC5373A382C044A276F6B41C173D0AAAAE0C1DE4C3CC71EC2637225CCBFBD4 -5EAB92BF39357C57195B410F74283585B12B926438AC72AFADAAD2D0FA2CCA728C8E86BD3FE75D47 -B8BEB96AB13B5480F7A3D5741EB51E3E40C21FF2ED7D9221D9877C7D1A8CECF394E4023FCF8C4EFD -B38B839499FF5CD96A46AB4FDB46F35D3B48B91757C0159328120E93CF1F2739E936E28908FB1947 -1D3AD7F6F1AD2BD1EC364986A411CC1B547D0CA104FBC10B1CA7B638A60E75485574034561DB345D -DA68415146AAC632DFA34769B6ED7D7D4694E92CBFF4EFB16B55495908102E85E827FC623CF1BBE6 -A13CBF64E878E1A2A159948B5529B75E071744A5F0E50DF18C110B0AF117CE7F33F8C959D4C98CED -5A9D492AE6F56DA57B0F17495DACB130660BCEFB064FD8309D965ABE8D2BE98F6898C1B7A39CBBE3 -E75DA0FFEF6CC3945CE76DA3BE915546FE8A5310130AE0ACAA9AB73C7E041C00533B4BC7724657AA -649B9388B791AAC5EABFCDDDEA2CC67A0FD0AE9BE37DF9AD40636538EE55A83F60E9E026C64FBD8B -220CEB46E67410144A520FCEACA252E8165448F84D8EA083C793AD09B90B3EE83B73FEFC3365C729 -E3C738894B8C01C2F8AEE0CC8B114E1175EFB44CC4C6CEF5C8754B1CC7CEC200AD8BF1189D741CB7 -5BCA4E88BE959E32216AD33F674F49AB20A354CF3969F1611A95D3934E148831AE7C81A7EBE3C524 -4F743E66A82E10D16CC09F8194EA7A596BC5981D833318AB4F7DBF2ABCE543E410B649D18D146F01 -486159683DF61A3F880F9B21EBFAB77E908C6CFC79F89BA5F51114F0BF7C3CCEC7BF0F3B057C3195 -CFBA6908E31E0DF10DF69163C9DA7BABC00E9A580FA7FAC202910615BD479BBF76FB8068630D1EC2 -1CD2926D351E869E16C2CF1E023CF04D4FC61607DAEFEEEDFF5593E6023492F00029E2AE4B4A2C14 -50954EFA2792F32B4934A768F892171245A1E2F034E2B9F39833F1B331A19A386BAACFEC8C929BA6 -B67CD8922BBC9DC005EC3976575D5B0508D0717C6BF11123EA36D8FD37FA77A6F1F5AA84D4AD8D25 -B2C11D1877A6E2F9B74F3B5829FAEFD4F7209CE9785AA6FDE68672554A6F29D8BF03FE108ED90A7F -58690FAC399A8AD3A26899072B832874DDB629581A51B3325CD9EDFD49E890EA8959DB937DAB83C7 -77F2A426B967AF5888C33A3635B78D647AD6BA441E222C958EA58D61945F781D7EF409771B89B202 -42AD7D07C2EF592CBF413C5FC89EC30FC9EBEE4BC63709AE33B65EE3091CECBE610B847E12C556A2 -79C8B114C3E460822D3330ADFD72BD69F54C08A81848C2002A08326CF3B09B1305490D35AEE59179 -08E1604ECE75BBE811A715AE8AF7EA9C371B322D0428EDF4C893FDEA607E70E1B6F6614947326101 -EAEF18E29BE0557D2A92CF1FC1505E8B434BC368CE07CCAABC0774F8A63E1073FBBCEB3F4052462A -A9008A1E53F188C9EAE339FABA74AFD6D60F47282CD9FF721F64BD51787F3C13B5A6C5A5F7861171 -0111F5E0471E206D72520F1DFA465F4A23C71DCF99A04CEEF11B0E3BDFC35B7461A60753D3AC26DC -50A5956C9195A4F5226388E0953DDD03AF128A98F03BDFA0602CBBAA20AB9ECCDF7255962A332E16 -D4380762E498FDA4885C64FF5F9B480DA487C58E78943DF62616E6E2C69EEC8836DFCFA9EBF58938 -A878F3E792E8BD8C5D6DF557A5D82018DBAE1CA9C64BA5AF8E21BE1B6680FC5DB22422220B776E9B -A0BF1ED2B7212F8BF111EC8C8C77B223C05EB5E5F1CFABD2D037F4BA0F9503E2CD83F4519D180476 -63F09E308883F5DA5228F83045FF41214D2273B2FE0A9017D5E0557BC2A198C35D1E7E81F7965444 -5760CBA1D3F05EA4B90658E53FDF0823BDB1501ED51DA75C47395073D8980D1E3504E3F67DB3259E -4EE73A87CFD96F84E221796573958D364A51E635FC55478C9CBF9AEA16B7D8C25F2115CFE4B7F598 -54E24968833BA0D64D1D332A666DFA2A3FD71B05A26BAB7DA382907B13DE0B80871DF184D3622B62 -3D7E09BC32A4F6EA2E6DA450A906EAD36D53FDEC7F83E101FEF32F4FAEC581B000686D86A0D3861C -1E67F18A4C4647F51F978484D9E3100B37BE9D20AE84C085461C1FBF929C669E936659050C2627AC -1B019837BAA75757F5B0A82E8AE9CF2111931A38BFC94744E2FDE3F8710342AC615286E4ACE7F269 -743AA05463AF537D9416230ECCA859D8C99B7C6E70BE7FE11DB698589BE9E11900C8E9582A4EF5EA -94B5F62820C90DBC022A620EC536E06CB8BE7526A789996D0E741AAD980880A33800A6FE92286CCD -02C9CB407EB31FB95D9C9F4AFF38B37087AC582C1F7B64A7C3D2202BDD62E9AEB31BCA85C4CF323F -03DA9D318B91F78FDC0D266630F7444ED068B55C05461C97552366A82C2E743CEC353D51028FDCF5 -403B3B74D379B82EB69C4380ED40239E15A86B2E5C860891E26781CC111FB5705E3B7C7AF1946006 -54B5FA1B5FC54FD0BA43666E7BABD2C91C859F393ED49F7123EDFB648A3D6152F2C17F7E438C0A63 -8968AC06B4FB3F77F64F358AE063820BD33F0213C85C40E4D97ED100EC2DA1C2E1EA258BF107AF67 -5A9D995F60BFA37222B9C2B325C0052BB8537D2B27DD43A129C7E8FF42757B3AC9B447703D382108 -DA520B8B3BB3E8C7295B776B44ED28F863B8E1F81B0BD1DAEE8A171525D09D2620C04DD3219D880C -2ECC79282DD7B1772A9CBBCA706909AE8BC7798E6EC7375189B6CFCE8A875849176E5913B85A18FB -197A33CA4B5B4058603CF1FA79A56856B43D538E9ECE117D99AFA73B57E307364F553644DE01EDB4 -6234EFAC13046B6E047ECC8F63942F20097AD7ACF0A45C0501A95263DE9439A880D6B5C5214D2918 -0A54D7FE9B2E627EF49E189B59FCC78745E878E45B46C0A648955D3EA8C935113D94F92EC963F66C -F3CF3A526BA71CDF3CD4CA69EFAB08B7389E3390716892A4872BD29DC1E0889A42D7FFB4190E9A8D -05D84EB9C5741BE6B02716BC75E0106F5F94BD3778BE985E03860D27E44088C3CB2A059DEBC420DC -E3A8F4087A9548485E616C409AC400DD1C411CE4B6A229D091B253EB68F06E43511EC5AA6ECA4D6E -4818D6AA2068DA1AEFCA377611BFA816B5215182432D5683294D67A7C1FD76C52233087CA44943EC -7280005E93145F5E7AE50100C18364E1B36741E9647C4DC1F68A58EC44095920FDCF05532F603717 -80F78420077EF5C24D63E26040CDDFF8DFD65D871DB943F50CDE84900C1372EF33FD8AB9889C82F9 -4F61A0E6842219A0F39EC7B232CBF802C4A744F33159432E827006C7CA77E480A48A9B0E6A876158 -8A3102E3F98A77BBD62A3A23150FD140D3941773BF7CBBA2338FF37B9EB640558A2313E8824E8E62 -0331568A9B76F4897198A709F9313F4AC40827D8C3A71F2ABFF02BFD57D30D0B14012FB5C39B85AF -540DDA0ADC27A85B31694E8D7B61F9D9B476571022D98F2D768246550A877293F3FF6ED918A498D6 -A600223E1A61890C49ACFB60265867CE9464F9C32C59E94F7641C3873FB4FA6EB237F8ED94579957 -270D6FD640BD9543E683F2372CCD7B60AAD269E03A72C5CDB732B128818D41A6DDD2BC139F7D3911 -F48E1B1D263DD4AE8E4CE1A686F3A00A2CBF48978631CD243566E22E68F8D7397134A3530EA3745E -4F1EACB4D6A5FD84C3011094F37573F7F9902305020C53926716D4780C6B0A257BF711AD94C83F1D -41A02C1C7DD203A3E6E4B14EDA2FDBB36B063A3E074495F626B0EEA146D22AC33457F44F41675967 -6D2A0566EC2B726D2F0540ABF225339F02F406D4E7A62E5233DDF20AE7C86CA0CDD561F33C422654 -BF2DC3685CA91BB9D4B09AC8B15A24A99FF56E2894F11F7BB4728FE8F0F5B799F74F475D2D01F61B -7E9E0E541F7FEB8A557486D7DF2CE50927515D833BCAA1CD9BF7A650BEE9E003A5951C98ED147C4C -52F64F692AB281984EE65A47E44A4A5FA93D6F18D276D3B01C5E5F6135AC6940524CD713DF4077FB -4943E8AC927A68489EA52ACF7A854393CD027EB52EA2DC6234EF034F3DC742D6DB5A67FC21D22B97 -146B9C268BA97C30161CE01EDC69A6A1F05EFB0E06F22644E1A368F0E2C0C6C1C832878E0614B74B -D645F5CB293CFDB7618B837FFF14A1210AA061C8C81867244305B80DAA73CB25A417228E9559E7BD -52C119B0CCDB7C4DCE7E1B9F7E8EBBCB575E5BD213BDD6DB88769DACB05E5870232F0EF82F448559 -187423409EEF756BA6247493BE24CB1879B5DD822E03D0ADEA1EDBDD83D3FC46759C679B921F0616 -F27212903F728AB44C1784E8A7DCED0DF5625A7D3F48A20FCA34008184CECD145CCD98E31B79E174 -CF107E8F35C40C19D86B40BAEE6164353408801EDF75A619FFC5B6FAF3F3A95F64795CC40C1F8963 -4FD8C13852D265FBCEF834C800AB46E3E8167476B23CDD8AFF6E2F997C99A86A9CB30EF8C853154D -0D89EEE9B9CDC1B4F27BDA32432A4173B55CA8D9FB50ACB2D886AD8E5862FFD5DFF224BA13C8B8A5 -4A7F1A9F987FBBDBC5A3C3D762A5BE309D5D926AE5093C40AA47B3B1BD828797CBB9BC9FEC9D19EE -A73D2A39764816113A8EDC6CFA6E605AD578FC8E30ABD600658A49ABCD5AC54655D29C50FDB72070 -169D1B389F114B7C71EF95A80D82AB537AC8C165D47371FC142A51625029A990A577EB1618480D72 -6DA93C98E5C5F24F622A850CDD94BADAEA91D4BC32CD50CE69E9F00E77DEA8EC1D37916398FB7092 -402605359DF08AFE7B99C76C2A7C70383F28A7C000C696F45291BB8F074791798197CAFF1544C76C -EEA8C9E6D76EDCBD92A86DF889481F3BBFF0865442264F0EA40D3CAA69AE467A08003F9C30FF7F2B -77E767580575398462D5B1171DD441D8986F33BC7BDA17D413EBB6B7A32642E33F20B284BF3EDED0 -02352FC66C6F7741A542155F4A159CD778BE56B9492CD95115C1A06189A216CFD2E6725965A13DE9 -73765A05114D9A5A4BE0615AF8BF6A5EAFF84468B849954D15BEAE1CDD57C435788B331905C01421 -B50F20B184506A0BEF746330BC98E9C89AAA8F9D102F158043BEB6A682059A1C8B8CF67B2F3D7AF4 -D8BBE086254CDE53765E3226BA2F95AE8063649F9F94BD9519411DAF8A0287307335668190638806 -E29484A4FFBC1E46B1800E03B162C23B1DC0B4C0DD3C7ABED2F00762972EF06EEB9BCDC7B3F39C70 -BE32789D366F073AC3280C273DFF2979507671B3E1E7685A9A4F0FD3867F96DD675BF05F25ED986A -79249B75F182FD73CDA2A6A66D693E4CC5AFE3402431B2C816DA1486C34BC9DCA4E2D51C868688A7 -787CD10ABB9ACA14B7181369DE89913CD8FAB58FC84519EA2AA14E54B7A8CE474F213E07CF2DE2E8 -88093DEEC937526816B71C96ED75FA9E2EDC0F9E6E84569C12BB8E39AAEDBF546630745553D6084F -F9524FEC6A7264F88CEB7EC3358E923B392474E3A48865564431662988FEA768CE555AB0DA48BD52 -6A84B0CB17B4584066C1640C1023D91F7869EF0C4D701BE121A6E3C832010427490758AED7A2B30D -6028F2215AA44E86D852FDC67DA5CCBA79EEA863BAC9EDC2535B66AB0E54EC4D4411390FDEB8D1FB -C1743F15C3B68DC92A8659E7A892D5E53872EA51EE8CA7EF51103E87C29A2714E907C79DB9CF3744 -1785D2F73A1EE58550111A4D9BCCBEBF2E39CD3B93DCA300FAC3ED1ADD8215301E5766C30C8CF296 -75746C5A77BF1FE3CD75D25CF193DE8D9AF02AF8F7A6E8F84B548058CDD3C6998ED13463FADE7391 -26D83D3CE2C7201F955382832E32C10DCBCCA35835985B9A93F8E3B0208BE6E92428787C47D3808A -0F77B8F1D76E6BF6A17FF81CDB065180E03809D03638307BD7BF5CEDBF64904E918FC805AC905379 -928B816480F6E3BDEE47042CBA98539DA0E113B1A5F23EAF1A3210BD18561985E6436EAB90395DA4 -77C7A6D7888D2377B3FC4169368357D880CE041E1F7C875E956600DB7D9B35D1EE66BE476E9DD806 -4CC02230276829C2C0A098F051502E828A0CC505AFD8C3DF293DA1508AC4D25866BEEE6BBD5A230E -9C2DCDD4F06883936381F476DDCD86CCFE15C2CE3C3243E148CBE603B8513A7CE7A6910A66A90B70 -89E5CCD4368BEFFF2BCF8E918BFE0A1B069AB2A914CA7BB91A0AC3B3C0B060FA1A0316F6135E890E -E549315897C8464496CC6DEA0F7E3AF43FFA4C3281156067582CA255B1D2E80F999A3AC0402BBD17 -01824C3BB524130F5B82A45275807BC2F3A0655EA208F968B297F98C369192C8ACA26BEBA7DC4506 -FBD1305E2EFA4DBE5375281A88EE2D6FC88FC0A755E72934B4B58F6DD3BDAF7171A4A3C776576735 -2492BFA9A7758504750AB7F38754683B70E9E293CB1CD7B23BA62BD7397ABB84D7EDB22EF6C3F58B -3EEAF656E361747ED04020163253D1CF3F905B5E85F83FFF30AB2778CAE43781667C0F65C8FD404D -6B9202A99EA76AF9AE1236631550B66B063847180B6DCA832EA8DC4A6EFDB674B5A26552A7C7D54C -2799C7D4E03C24F661A91103086DE3A90A774A6988347656344CFBA06065AB22476BB09FB68F9928 -C0045F2764AF643CFEF0516D87FDE6DBF93BAE2829B176CB507BB99835E01BAD5E55C2F8798C93FA -35EB3FEF02CFA31D3D21B030547F86D27B9448D68E2B155A65C742BD2999DAA0C3AED64447B9CC67 -F7AF33B63AFAF25F3CF7EF86657FE8F952288CA4B691D369E8F1935CDA44A180A6767560C2ED3F2F -CC38B6BD7991D4170C7C566D690A8A25BE03212A80871108D18CCEFF246623E653107631F29227D6 -4754B2208D19F84E547799E691CA473780DDD56AE620CD953D5133D135E3D51F237078FEEBB73714 -54EE633CFE238AEA63F9999E32850E6C197687A0EC4E5908D2A18C5349627E336AB5E3185B218228 -603A4B1852069F5EE849D571B8387DCE1F8F8E9FE94FADEF128BA83BDD245F8C1C27C11F2ED1A8AB -2D6D601726842CEE744EE7AAC6B6FA16CCAA39DBF5B3B1D47339F31DFA562671A9CF7DDE6915FEF9 -F19B3E068A464DD350A3AD146D1A241673B5112A4A8768F976723E6E184790C0604506C46591BEF2 -106C40789B733331A80740D59ABED39868F80BECC2AA21C400A0BD0CC326D186FFF9EB37680F1EDC -32AC78F9059280D07B5FF2E354FED545129FA5FA8F3D4317FF21E027602FDB2522F049BB545FF4DA -60248130F81F4E348373142F3148DED038AFBA818F26D5B49FC02DE9800D894E9239C88EE0EDE431 -F8083697CB0BE3B497473473E5714717C914A1A926730C249413FEA2615EF72BDB0906933387A892 -370F77EEBF62D26CD583EE643B02E323821379C0DC966407D36AE3CDF646B95DEDC7D7FD0F28E950 -78F12DFC0D6400B327B743C548A0A3517A175A7ED963ED756B1E107AE7087E2446BA702CD4E26E2D -CDC1A8B697108B5B5E81E9F03105F220C72D4AEBC57665887C8C7964089FBE9424120EFDB14D76EE -F8C6F7A30B13E1AE90CB9D93D2E14BDE47F4A1D05ED5B18D32AA39911B92D24C93976ACEB7EF597A -75161923A73B2CC761785493D0EEDC08B5AFE95F3C006B41438A0785C962B070DE2BD096CB63B847 -C87539880AA3D3FC5C345E0992D7BE77C6CFF4948617FDDA784CC55652192B0ED775129C4EA4245A -41BCF3875BE319DA0EE2DAFEFAE920CD2B6C6C2001762F88C0C5C05053025C0349DB17104360FCE1 -5D7F3A8E30ED13155A74FAF91DC77B8AABDD6FBD5A1EAF255DB209D7F2B90822296B5603FB5E2CC9 -5CBC5F7A6044058B8044ADCE73ACFD896177F1F70EAD2F6534DC3AD755AB2BA87126D63CA2E9C441 -DF0965BDDD6BE494E58D6B5057A561D1E31BD38E92CB73C1465AF6B9C001F7229059BCA4104847D1 -639E124E082F7364B56548BF8112D0EB461B316B2449049F6A476D36D6B7C0C1126C08F2E9A1246A -3B5B21E7C8FAC6E23B82E33A7783E4F31F0240E96E69C9444E7D7A928636CFD086475DF1E0A28464 -81387BB2010655B9F81A0744121699B4905AAEDCC84BC5D5AB3674601DBBB651EDE7B5DF05C8A463 -DAB41F79706D285C4F9063997F7AC8CEF35CAD51FBE5F5BB1B3FA6DA2C3ABF2B3E925581349728D6 -DA0D59C1EF6444539742EE9A23A5727F20CF9377F4F84DEA420607015A30FB14632D084A2DD181BB -02FC3A84FC499B318156B675B9CA3CCABD87FDB2497C6705FA70EBA43ADDB6CF961B30E8F6AB9F84 -E1DD8D6DB3314B34B7F7AA3BBE19D5BDC75ECADFD8EAE19E07B387A1FC586F0F30DB695926764B54 -0D89F1D854B0FF86528AD9523CAF56371E29498C11AFB2F4D5202670C834E930103F039D13348824 -16A49BF93B84FD3CF1209EEF7D4994C8302436C0794497461C11F5B8BA152BACBCC08AF8A15F4A4D -F3EFFB7227CA97FC21D2D0356C93390C749CBE9750B821F1A7BCFAE2C8BC6D9A27F844D8AD088320 -79ABF0EAD8ECD4EA72846DFEED021857F33C1ACE4C07BEC90398B629814C498D33BEB375B9A53DA0 -F926FE6E89E70322C72CB2DDBFB16B13EF7A4F50DF783316584C6AC2BD7D9029124933133B2229BF -74A228868AB30EA5C3E87C78C3F0962199480DBCADBEF53BDDE45849DA857A4FD85B96682F1EDEB8 -5384929DEE4AFAF84C51A09F5D572705673D885070303FDB47DC898F874E103A9E7C1E894115DFDD -AD81549C7375D4AEDCCE2E52C13E5130B47F206F7C5AFAF1F9EE83DA8188D70B473269CA280A6A02 -DE85300B93D8A4F6B402FB5DF58F1327470CE11CC63ECEF2EFAA396A6680A6746A20382D9529B58E -7CE684B39AC00F7086BCB47C2230DF0343BED9B9152A61C9826AEF9E00A1452D91305CF05490D4BC -0BADC9C6FCBFA93FAD52C3A80705A1956890497557C0873EBDCF61CCDD2219354A4F5621AB33B119 -32065C1D990A9B68858331EE7875CAC855F98563B14EF9E1060BEA90F195AFFF94728AE935453438 -DAB35123D0E2699475884DDAFC7307A5CC06920F35341728D85965F5BA86F261CFFCB1E29B429F97 -6970D42D10E6AF6C4B792B4384122AEF2448E22A58D3AA007743C71324EA08D06819FED14AC1F22A -4F0BE4787BC8738E1CEF240677571C65804ED3E748D72E89C94B6F310BE748FAEA31EE246859CAF7 -A1EA17CCB5B246C87EAB771E2AC5D378650191081514DDC2C66878E3766CB20DC49F630F2743A7FA -ECBE9DBE9E815A3CB57DADF2BFF5EF2FCE23A56298A30A2E052FEAEFBD698101F9DB992613706693 -CB0EFAF6F60C8BB5E7D0A50B3392B9831EF3A304A846CD4AF431E9F018FCD3A5B16387552D55DAEA -683D36257418AAA0E7BF8A03ED7BAB114D7C15119E6C71C1946BD7903C1C42E115E954619051B853 -BF05AE316E15E619A7DEE498F771E809D9435969C1056402725EF40C0200E083F3EC6E0EC27B8ED3 -8DFE32EA0E5E156AC36C4BB9AC5ED111A11678339703F1B9299345AEB1F251FCEFA11FB3101CC499 -907DC862B4463D5523B9B25C5B69F70AB6B29CFC1DF1ECAB8227EB3ED1F882E90B12080EE003714D -403EC43B7B54491446B6A3DD6EB641EFBFEF060C45E873E7398025B1CB7065441F1753028F6F8C49 -A96801C0D598E098EADC96A21117F817B6FD6E6947642F93E22425A00E8F6B592AD50B317B69C0F9 -4047386A45E5EBC9504FE55451A01EB29DDF9A41D4BAD85FC84CE280971E834F06CEF49C8C20ED2C -EAC889F158CB14A8C070900478804CFF1D1637CC880C81AA287D8382837FFA8F41FF3C9DF2F22CB2 -0044C171E4815D0D0F6C22D19A52114E780CECD71DAF63427782E85E463DCB333789F496340E8CFF -885A9D9A4250118B439C71C6BE51A9338BE29251AA794EDC67DEEC6337FA63CA9B03C1C9F75E733A -4A918646E7BC9792486CB5A4BCC5F84FBABDFE338C3792254A3EEA3D88903C2C47B91E076259DCCC -8BD3DCA90ECCC832C09C45141C6242026BFE309029A562C3EE0FCCDCD40E5CF265ED9C3DE582884E -0E14819DB98B3AF734B1B3276AC41D43384EBE73003D15CE39FFCC04109583390E470F431B4407F9 -8550E138F96C4564B494E5480F47C853BDD237E27301F55E42A3BED18FADA152572B7B465A581DBF -E7DB2619365CF16D71BF8F091862B9FCF04BF8D0859A76F46E7B5712F2757EDCE332D3213B8A30AC -2CE7D7797EEF6F30904906B0805DFA7CA36D32A20D989858497A66CE72491393DD79332003D55C09 -5A5AB5DF761C4BE5C041FA8407263D604E53091F7B6B15496245DBBEE96A63F10FC2978D99E65731 -28689366FE8B0BADA48B50185B861BAD03E3600F22BAD4274F2542B635F6C7944BEFC3BC741BDEF1 -1A8DD659038CB40FEF2E16AD1AE7EBEDB7D9BA15FDCF26355331505A386DD7399FB999535D6061EA -BC61DD76EF3EB457446F29D0BB6EC2FC0AABAC20B27A3C123C27BC27A76336D0A0A6D456DA070367 -4D959A4AFE428E2206A511BFC80039ECD56E75F69786DA0A8084D81A66644DD98B6018681F1D70AD -E09BD9BF3D16D68DD5D0A03AE26DCF1552549E459FE190B310A8776B2C8468C14CA8B1B9A7AF2956 -507A3B705AD75A17A0EEA7FE089273353CECD07BB8563465EC8DECA0EB42F43FE3664EB5F31E1D13 -24185539B28D508BCD065ED576D8814ED3FD637D576F027927162344AFB0255A91FFC616948E4E35 -8867E9FC76A9AFFACAEBFFE110808C1532A2BBB0DBEF3F010E45FFC73F228D28F12E98478B27397D -8F456781ED9E19711DF2E9EECBC3FE61F7493FDF1A59124668A91BE51F122F93DCA4BBD22DEEA339 -E6EDA3D6EBEE03DF958113E1CA49C8398D2C59DA6764882EE3663F62A55AE50A7E91B4FEAD1B11FE -0D50ACCC5D75F1A515F0C53616A500F1491381DFD0E2477E402AB0CF9F67D501A442629C8593ED5D -25A72EDB9746B02F2B0F0759CC9CDCB4C9D8B4519C8C617E569B432F0CF6890372AA879CA7DE46E1 -10D95E230A4F0E52CF65811C54365DF4A3E40D819E2FD379B47DA3233D0DEF0EFBCE04AD8BAA3888 -4F6A69FE5C373E38AE0FD0241480F2BE7CCD18AF85916D2703A049779FE7398FC47D348454CF03F2 -2EB3FECC064606957898B5643464845445C25C0C7D685C8DB042AF5D5882174374ACE90081C68678 -9BCA96AC602EB41D317BD652293EE628951875641661EC86A2C40A42E8F0813A861D41A0F5178E55 -43651CA0E99150462DB5EE0010F00DE6D55B0D7FD7EC5BAEA24ED3E90A7D6A0589761922B91A6A91 -3A7FEDDD3B68254D89ECF767CE8E27F966426A8B4FB1B4085384FD09D63E288405B78A646F44C87E -EE22C8596B13188085479F75F63D3D97A28F9C8306FD207DBFD38DEDF0FFEB7DD80B2A3292DFBF1E -D605ADF1B33E85B010309E3EC058FCD922B1325FEE71EFF2DBBC2E68DB52D513E024C01D47CF657B -B61C9734649A4AB63C0AF4720EC3EFCD82DD3CA6E80BB63BCF1B8DE810A0C6C517C63B76FE68C0B2 -86867BE102424FC31C4937048B6F323D039618586FC21731005D949E7D802A430DF8D2F0CE99F2A2 -376C2953EFC4184355E4D12F422C9E1E25C4DF38DEA334DBC89B540E14C61A7769D77115CE8968FB -76B27D0863CEA2496783114C24D4CC816DA884D953DA3F9B9D3AF8938BC607BF26A071956CA07E6A -5509EA2F5D80E5CBEB98041B197FAC760976EE75B470DC20AA023BA3F63C2876EB281FF5173BB490 -D6815604517AA1B1FA0631401B3C1A04CA103E2CA4ECCD83874D9CFC8ABC134CC0F9141D9AFA5684 -8BF222342016C556C14B3482482DCE5D0B6EF1AB522AA1812BDD8DD3397E05327EC12748FC480842 -9B97202E24E1DE0C7C0D272C046BA73B37D30930C5DE5A47D96955CB0F5DED8F3AD929A8B42D2839 -0458F5910A0F93610F79EDDB27078943DFE17C716D65F96589769349F3B66AB7B8C004CCC59EF688 -1F745EC7129865A76F9C2D029D4660CCFB4D5F9D412BA3372A27CB175E9D65F759575CF14A5899A8 -D31FF039AC02DBD8391C3397428AC0D5717C005200790785354813C8859BE90E0E17914F6CB9C674 -F1E9A9648657B54E5E1F52756C4F982DF74E73F6E4D40718C71D1D0E2420FB7462FEC9E457C0414A -96E475C6BE2C10437096FCA0C942E995A9ADA789AB637B648781D32DFB68E62E91C2CE7E13680F8D -31ECF8C824885FA7618981CD05FB335AA111B409C59EE337DF4E5F9DCC920A5FC0D620DC07F20DAD -63F4FF5E0EE5A2F390AF1C32122BA7780F210229E5A5E3ED97BC1C3CDDDD456E739CA782EDBF4B81 -0552368E9C734B0C78B0B8E3F8B2DD782862B74318871BB1EF087828CC173D7B049811FCF598B8EF -DE4D9BC5447F4848C98029C854F3AE461B9D46DDAD8CE67A521F3C811A81A396CB0F80F3C8D8EC88 -30532FB7F9624F7CAE0F8C6DF875073333DEB28AAA90AAF486AB8C932553CE697B885E71EC8E40C7 -835CD5D59A2C695DB9E51216FF9B77A15B0DA63717FF25B05B939E45CF7FBE490E51E9344213B32E -115C2DE14D76DFD5845088DE645B0E75042A61D82FB1753C445AD0A956A1263E5A096B681D3BC51A -9FF32EBAFFF7ECA8B59D40F0937EEFF38312AE57462C7BF3B1FE24D2BA8DFE84515270E09063CE3C -80DF4935E409F62EB4F54AF16A186D4329972B9BDF15FB08461B688ED49928429226CAD9F67C9D63 -6D1375CBB7B08A5631956B7FE29CC9EFA8D75C9E4919C8C2C54F401D2E0D7BFBA40C50CAE214D210 -C6F3EA5802339F63FC4C1C1995787617F3EC2C806CE44CF8E29F76606CD5836F6E5A2E423CD791BE -CD3F112F25657DFED9366FC4ADF90B685CCE4A5698E5FE16D7542B913FBC01B288DD13F43DB2B1ED -8CCB80159DBDC90A8132125DF8DF547C4851CA609D1F6F4D647741260E845B457937787827A89E37 -CDA06BB191669AC84B8608EAE132D10177F3FC384980F3A6E439B048A38D0D6B9CEF09F3F2D732AA -71BD058169D6D0F8C9D146D9DA046774027559A8B3843F6116B418427E78476AD8F0F81E8A6B1209 -8060FF7DD686503F972D6C42FD6CC29C083AC3D72E3751F21D2E44A572EEC80E81EE44C90FAA7AFA -BCD3ECEB98FD4068F6C3A4DED0E6CEC523C9A0054D1FC2A8D61A4A26F9BC250B8F302416924AB22E -722297888B85B9C12F8DFD2A744CBD143F9B2514C1CBE988D9CB4E77D90B2EFD5C2A528355A35F7C -4AF039C7D1D756305967B847D4ACBB81263D4992C001E2A262B9FEE2D1F5022BE5B15E1D8F1D67BC -52227344EE912C018CB73E5F47CED54FD202627777BB77AACF3EE6B22706FB2FA9062BEE87E22CD2 -802E7706322648DAA0C624EA885430175F746E1F536F9A8E1C610C4A761D07248426DB63C9319A88 -A3FA449C3FB8AC94C6003C745E6BAD717A3B2EA3862D1E08512A98E57772A62F85F1E2FFBA40E2EE -43AEC11203DA9CE5AFBF673436F2DB6AF85BBE89D802F7A9E5FA25A408DB69E51F0577DD26F94CF2 -BA2FC53EDDD6FBEB534AF15F74F66EF8D14E7FF77D8A5D284C8202DD5A6053CEAA606BF925992382 -5EF4EFFAA8D878652A4CAF2EE43ED26BF3590402686C876F86C1AE95046E527617CDD3C429BD4CC3 -F9654D2C76DD4102471FF746FA9FA379B16DF96BFE3836D43FCC0B8E95120C27370049ACA4AC313E -1D50D72D1814F2566B8B29FA9C9C20D0488743722A766436776783B939171FFFA00E04805A8B5821 -4D4F114F7B9C3C17CE7486AEA2BCC895ECDE809502BDE57981318A93F23016F056A421B733C4590E -34AB08BB348DA4A48F19B6BEFAA1DDD2A49A6C440443028333CDD48C85CD698ADAF3FD8676739E44 -400A98B575BE02350576F96CFA54D4184BA47555B8D12374B86D038D085F7FA51FF4BE2FF5981408 -999B48B2FAF305212ED54B2E371F5A0074CF68D1B0E5CD279BBC8BBAEF694A89A6C43F518D01BB4E -8402AADF34E96E9B3FCCAB4CBEA2741D3FD9ADF7AF32388F7771845AF99965A6078F4DA335EFA436 -BE36903E33A743C112C0267309F266DD44FA998C9A139704E400B89DAB952EECFE2AC09C82D9F497 -5371CCC27DA37890EC84123193314D8A7A707C217FFC951A547EE5B6D1B7C8ED85BEBD9D3F4B9B09 -6A78E5F7DF88C931E3F396973974454E59340CA51DBFEA1A00DE084B64630E26C6D6A3593B828814 -E27DB0186BF2A87EEF268AA1B135AC09B52CFE53051CBCC88CEC5657BD47F603C8E1A6249161684F -D9084AC279F57A4F9BBD0A546A87E147B62AC860911969A29B8AA20E3AAAD0079D64E6BF1B0F2CE8 -F0C54C9019207E1B403358253C2FA93A662F63B9380B65C5173C198D86A3D0DC1800D1F5378DA39C -E8523EB62C6AFAD8A0D7AD1629F2CECAD82B8FDE38975303768C7D3A08B91478EDB3C45A8C6B7725 -EA8596A8ED50B8355FB852FB8966479D12E1086223B1E6523A65FBA81DD106FE254F7309718768AB -009FF7714A8C363B09DDA73CD3F81BF9C0CD3B0C806CF3B7BBFAB73E46FACAD2480EEBA97AE68EC9 -4D3D79AA01ECC22067858EFFA9D7B7F997ABD2CE5AAA8781E5499E8580C405681CC63EEA53BB47E5 -5ECC5BA2A7A3C5472DF034B022F455C60FFF971B01583A29E211A87F7163187B190B0C1083D696B5 -86E9438FD8BAA45101A5EDCD1BE5AB9A585511089DDAC8DF1B1FDBE582ABD945E67F99ADC4452988 -A9859E39C90EF794C5C4E62997085B7A16A0D90107D08610BA175AD66377345662DA7DA4D8FEF847 -EE5D57E3AC54B928A0957CC1C944E7FF14658FE4A641CD26C61105C0F136A75950764B69CA17509E -3C19351D456B22C87C55E8DCC4ACD3E150D936333FF36499AD6B02B6403DE0F12901301ECB2EBA10 -324BA72B58206A13B8F37B0AEB12115D0C12879C8EA8A2EB70E85C95434564BA3DFF481C8972587E -FF74EEBBBAB14FB32B8A84B8FC42EBECA65D25E8C32C19CA5962832BF45DFDA4E871508AEC318495 -0D6DBE89019CEA29E40484C36E33D76B756255531ADD1DB24C03B2A64A47BD8FBA3FDCB1F5B96F8E -ECB60D5834AB001A70740498720AFB6EC03445CC35B51F7987109618C6C78CBE3041BEDC69B6FB12 -8142CEC5C8683B558AFE3024EFF7A12D04EF59A72E156DF11D33ABA08A8EEB16259DD9529CD003AD -4EF4137B6FF1654236473DFB93F597331A5E26C7796F528F65C94FE07B3B4F4DD49034FA0CC189DF -CDFF70C2F1C6D3DF30AE103E2AC5CFF20664AB934CE5C19693292071C93BD590383E0A1931E04D1D -DD18071DAFB628F5D7472E457BF81D6064EDFA8DEBFF91701C5038CB30865D6122076A336732DBCD -B0A625548773D0013648A76F07BBDC9C16284D158EC7A105AE37A62279419C3A2F360D0C7A74D6FD -D0E36DCA2A8BD59945A4196598F690878F84C894852C1811AFEA4BE3B9F6A5219E6628C66669DBD8 -FA9A0CFC2DDE7716A356FC4FB271D8A2CDDC8D4684DE447355BC7A287DC56852A638C5777826EB6E -B72FACCC86F80BEDDD0D649A883CFEEF4D74750172A90B5DD8252592FCFE19FFAAD868E99562DAEA -E70514F5DE296EF7B57E6F193737ABB6AA317956584423817E11664A67389197AD9F8F771EA59551 -98C9EE40A0761639E638CE9D890DF468642670235F1373D3AC6B1F43B5777FC0A91A96E095E89BB9 -FD62614DE456CE7AFD6B855112367573FD9FCBBD4A4F9C676E672D62DDD34A9BFE8311B6175A003C -D143C0DF15E4C0B48C735404086E48AEED6B6FA21FD9F40B84215DFF287F0677904E2DDFDA774A40 -19DF45CC877F553E95A1C65DF1D67BC0C60E0BBA4D205C0DA3DA80229FDD71859F65AD04506B308C -2B783839F31CFE4425263224F08C5C7E98A2C9D3DC8EA5AC1920F4E395413262E0836BC019A092A0 -DECA104EB2DF6B63392AE8E2136379140DE5FC98B0B69860FE8E31DAB5C5DF7807D19BEA34AC14E0 -ABC6F6519C51247B104DE7D912C5BF6EF11B48FC6DF84512E9F5FEBB48F72FF1B722BDC3BB2E835B -2E7CC6324BEE84893996B8DC2D4DC2793A4F69C18E63DAF04A7BB5C0A9076E2D5A343E134CC3C89C -4712900656FFC202E1988526D80C7FD9281FE47FBA8AB5D025E63A84051F6B13167BEC15B346212C -BD051AFE7A98BE3A2491F3C469718A58E783ED91F90E274FB4978F8719E92A99A1E8F142EA7E1F2C -46AFF0A2FB50F4D105130CE8EA309B0E480DC8F80D506172B609EA4BB4E2BBAE98D8882814FB273E -690DA990B60A9CDA20A2418246BD10AE67D846A0FA815AC25858145ADDA106A6778A11877FE59A2A -BE300D7DB9BBAB31CB5B960B7E4EF91D4600886D8795DC361CBDDDDE05EBD54B1941F426F7FA8399 -270D2F54C998BE92D146227270A8E89AF90C48BAFC4ECCCA01E6322AFC165743475E752F39BDAEC4 -9297290510FFA264342A0AFE2985F85DEEC66C36EB4A1D46683EE7C591A89B81569A8566AFBCA268 -10DDB0970577A76EC8A066622606B08315DB0F2E6C671F3259C73637D773D1A180AAD66ADADA2A65 -95B5F481E5F59E51CBA876FA06D21E1D674CFAB46A02D267E20234324D0891E7847C13C69BFCEEA3 -AC55F2EAF753726BCEB0DE1EECF42ADA964BF9E475953302C2FCA804B70B779482DC9319B40381E0 -9C0096460AE113C19A2DC9157FA138CF0E7758F71008E71D0F7599744D647B09B16E3C795C56EE5B -D14D8D63E7A512900D67487975EC9CEAEF69572FC3C2342AC5D365E8A4BCF462006B5268ECC15754 -94CAD9A9E7A9E8D9AFFE49AF647C017743EC7CFD5E66F4E4D845A6BBC836849274FBD270CBF263F1 -67DF7E26BA91F21C60F96257C07523AC37A2193010E976965CBD75751E312817C0564E1C5AE0CBA8 -BD12B01122D07020A0852120680985A8AC987BC33BE863EEC52AF13435B6E4048D951F5BCE36526E -07A8661CF2538F69D1F223BC53BF5896437D1BD46F57D9698F642F0E99C7392D8EE47134E34DCE94 -D392949B418D9821E12CAFA8337323E8469DAC24DADC6AAD4A0DADD7FF65694BA3A27964D28D8EB4 -1179458F91CD3F83B8F119BF5E76184DD29CC4C0718CF7945DCECC993A7A78739363136CEC7F2FB4 -95EEA8CEDB3EBF14373A058758C442939D36774435554851E9519B6F09C31EF26B6CD997DAFA11DA -91FA9759F17B7079164C5B47B99CCB7A876FBAB1D0D5D1E1A2683CD6914E6B3B755939CEF1C9168D -30B2738C4349650CF86C90D2542FC9B90F36A494C035A1C86DD716014AA16E6B9EC7AA03B16554BE -C436511DD3097FAB1FD0CD49EDAB96F74E8FD26400FC748CBD9EE1EEAEE24DA30DB6F8734B52818B -3A5E510AA5C14E42060898033E7E36CBA9A64042CF94A74E4B52E37AC027C0DC69BAC4944CCE12E7 -AD81AEDCE642EC34CA23E3FF07B8CD35DFF19F33C8D4DBB56A52534F8A827BE47AD4AEDCAD83B273 -38409FD1101C4DFF3F12D3DF79AD1FCE65B2F419451DD059C88BF066413E23DE27D3621DAC2DCC8F -9F3620DAD0F4B1A6E8C9E6E8ADB552E1EB2C4B2A3B73986AD53ED9ED8911F82F750DF05CD2EBA3E1 -B0DF208A87FB5ED44C3296B803881C1D9776D13350CD29C3F716F0B5A8B8557812024BA70069BE65 -89AA579EADB1F657712DF2570843D7C5FF7F4009D4D232D3547DC8B92ED5C4DB77B76255E661FF8B -163C6F3856DE5651B597EC7C78B84F0C6C1D6EA3A82286F1D3BB45F708D564E139E81F473C705AB2 -56346328DAA64D1EA8645DC10FD449092E0634D9D7344B2AEC3C75F6B6CD8B3F3867FF3CBB0F556B -186EE9A7C26BD2D17C8A773055D9D5013BD2F937D697A770C57BDB36D922CB911CD14E7FA14160BE -19C1A052E297B1A2D682D4BBC9F1D2493BCD7CAD2FA75D904C5F5479179DAF7DC6A4E0D269BACA2C -4F2430B4C8CF1572FBDC750A05DCD5B09FA3A9CD6F2F2A386E2B3D4D8E257BD43A783B38E63BCEE5 -03EA96FF2C373181744A607F0CB8D281D7DB1A6F4076AA3E2C61914BD796EF8A0873F79F964FDE28 -B792BA99A20C3F1F5ED1FD189FB1867C84DCD6AF43D49420C8B1F3DCE7DBAE71DEB17FE45644DB24 -4F44B1011C7C768EBB7254F4DACA64E9BA87AA7CD0F0C4B2228FFB9EBDCF3DDE4DCED39399FFEB34 -8811547D025320A88B480943A339E2CD2FA3605AAAE87939B1D7901465A1879BCB4C5BE1A179E7E3 -71F1BA2E0844F88AFBAE9B78DCCA47AE8AEDF5BD3D458C7D4A7A08ACCBF880D1F1DC69C636628DF1 -EBDC5C42FF88FF8B66351F3F72D703E52F3CE91E4E00759753A599FDD863788E99858498B66B93E5 -083BC3501C39A9BA928B0D763C28826FD237E949EF0BA85CCA9AA20C405DB6D5612DB718F7B4AD31 -D253AE306E4D7CB615C59AE668D347A4E60FFF7B103F8BD0E7CBDB142A763BE88AB40EEF6B8FC200 -458D728930AD0F94FE52ACBF0657C4907CC7942710AB1FD8BD149A9C9DEF6B8DCA7DB9062AA7B1B0 -11ABB5AAE8B77893A023F9EEEED4A20FBC30F922282A7AE2F1ACFF64151013D6B8AC2EAAE58171A1 -0F80BC18C3BBB5DE1E22EBE6033BF83040629023D74CCBAB3F1923CFA4A6735E1DFA8A1B261FBF1C -397E26F3BA9C2629CFDA84DFA3D1087EBB19DDA7E2D76E30DC2E15B8821D5291DA1DFD73940E5560 -A8A6DC91BE0075E3ED8D9E8CAC85AC20768D868CD2DC45DEADCC8B59AABE6EE5B2F891E0D7CBAE82 -0F83479332BF9707486698FE196C72EF72B52F54314329FC498171782BF160E1110A19B8208FC591 -EF0F0DA71AF657B43A7CC649A8488B759F7B69134B4F9DCF79DAEBC1CE52CC8015F324C9D46320F4 -4E1551EDA6D86139DFD1DB814CF38A22A89FABB4F75FB896B00E769820F763486E86668253CC466C -1529A5A924CC337C48448851A381DCEF63A0A302B65203D6571A1DD1FB9DC0C3BD6AEF4891497033 -109CEB5A481BFE442249940EC54096F1D0F2436D9E60495D0ACFF967A741B30467D24AC6B0032213 -18666B951EFD45324987B10BEF4AAA0FF1DF6887377A7F70F555DFB9FF1001C67438A167A00B05D2 -C37065655173A7ED9AE342DFA1497FB1F2FED6098901249A085D31B66DBB6AC25EF16C106B0A6FF3 -47CDF66434DC3F0012DAADE80B942D522CD59AF4C31C1C062157B3D000B9CB86E2AA7B4A5BF31605 -8A0D5A148EAA2C67977FAA0966E4C3454E08DF14C2498AD76E389AF65D2C139A6D8675298C46ACEB -7DBE6904C373C06E5F71399B2EDA0B40AB96E8BE991DDC39F92F1D24797F9EC9F2FAE25669B43754 -E2498E8EA5C44B176C3FB3E8F7A7A1481275A461F2549AFC4CC73E28417BD8C5212C13105EAB967D -AA679AE822B9B75B372A99C7E82D6BD83AA2BA00314DA4AC51B9CAA30D80507505BE24BAD0A87C5D -5D7336EDF60CCA4CEC8201D243C3932F74D171E2409D789AAD0D04A7BB22FB6DC3AB92AE33FFEA89 -7C484D741039F38C317EA396A0FBB9F15A27D87FCBE007558799BAB73212B6E5FAF2080BA074724E -AC87D88166DBC1464CF5D41B99428851FF1D99246944511CF42C3F9248513E9E51593F253D89C604 -388AD7132D6A169E9DD888E020AC1F8BA606F2E1EBB97977E505D8C40853653D8F398F71CC9F8F9C -540C22A1E6195BA578AE7262FC845FCCF77B33F33EEF266489AF8B81A615D6A13464BCA58BEC16C2 -3F31D678F14A938BEC31272DAC3CCB1B2DAE577A26BED852FC59843176A5FCFCFA0AB7FB00D2309D -E55C82CB9049F44FA61F1E313205A76317C4CF529A4456019D970624129681F46A9CD7950B8B5C40 -61853040113C8115319E68B37F88D864C6957DF813B305D09E6A1716B10F26F2EF5C727FC77AABBA -73E12B5AE6416AB19F6563CE14046B715BD4CB2B1E4D315F42D10F74CDEDE82BCDD524A1A5460921 -9084CF1CDABFE72CC8375478B41614BC18A914903596D6FC2F361EE519F875385F4ECB50F7053127 -4EBDEB14A5DBD906A60817246042E3799BB3AC647CDA7244B7998AE4F3BFBE5C767FD2142E48518A -4217599E0EC2CF5E86C8C270FF8B02F949EE001D6A439BCB4BC7D7F7C8167C3AE0A7E59687FB8BF6 -F37BEAA164541B8EAFD92E9D152E3FD0F413C99CCC34FCD8AA455A0B55DEC846A5874B94FC95CFF1 -BB386B2A1E22CD1C3914264B6D5BD1746972857C9235052D77A6C0DD3019F8A307FBEE63A3EF12B0 -39B224108276FFA84021F1AC5B745C54690B3FF587B4B1710AC3533A67BCEFC503ADF1F4B62B2910 -B31965E364EEC9CC437CC40181A7320CD52BE9C546B8F1DC824312216C2FD8232E2BB8D40EE2E314 -54C09772A387F9520E331456C269F51A078E6ABD9FB6A68BFD5F557215B0BBD2227B8959CBD1BD4A -EEAB094DD18E891C61FB00933C0A0D76174D169C0B6445D34C00DC9E06D85EB086C18F3BE27DF734 -EBB9CF078AFF6514438549CBE92A0C0D25EFE4A527D86F158B4E9D8870C7AC5D6C059643A3298079 -CC20398324CA87273B86ED801057D797D91BC3CF2F96C650EE1566CD3CF8656CC577D38B830201BE -718DC9A494268177A5019546EEEDBF101996BE593631654B638C75A6BAA648CD1E7AA9AC1EA60F4C -D604071C89DCCFF8B3E430A57ED6DE11C5837E78956ED991058F3646219BEAE94E4D9381A33D48CA -9B8FF12B54A73FF869D0EEED7E098D80152295E6016CDD809173C57D1F5FCE908A37010AD4C4471A -53451DE9B4363B63437C374C598F548F145D3D288F42531FCF36A9CDF72521F1C0868FCEEEB1857E -A983F6B75CE245D875BEAD1BCB8819E5464518E04717B78BD6E335F0AD77B832AF5682062A1E2AC7 -7CD5EDD5DC372EE456C96D38BF8BF348DAC2B4EBBB2440F2CE97B4B337F2E23247E3E8423BFA9237 -CA6CEB6FB93F960CAD894A96F0371168A33222052DE9B3BE04B022AB95C0C243486E35197721FC55 -311DC55F87BC72D09B6C940CA36E6640AEB66C394A5949A604E7F15DCE3A008BB41B0EEF2840A357 -F348443B4DCE064B4C15E5EC52E448C985FAA1C3D6526270B1CC691009959A7620C9A6202619A19B -E410FF7BD535A8B2640AAA459DFDCB8F2BB35112626497E8A397D4F9E04788322A738DC8907CB643 -15CF63C95809E90D06EF02F72AB04AA61FE02ECCF7E9049FF9F3EF2258A75656178AAAC9F3C2A26C -001341862D526CC14E92A81BD63502F959066E0BCD659CB9B5A45606153DD77039B8C5D5B13565F0 -0D95A41937CF97089F3938E39659A64DC3D6046D0E9EF66544CAF8A206635DF49926A3EEF3FDBC9D -CCEA2886EC855F1821C4B9CE1D02A19A11BBBEF43A7D4D536715548A62802F64AF30BBCBEA8C7E55 -AD56C801D8A569C8183615A78CD393CA42C103F155941E845712C335F4ACFC7807202B92A983111A -ED241BBB8501F15560E8F2157C29752BDCDB274008137277920053D6D7DCDC626A574A82A8A34F1E -77B2FC8CF7C1A7322F22DFCB450259EB450C52B70DF3584A7C54C813DB41E3DD81253A03B02BC252 -346AF0160716355797B6F8210C453DD7E1E756FF08C7E6A5F4F87605E1DFF35A130D79148A57B7AD -12D94A129FE3F055CF974EBA09A2B13DEECA2E02EA818A58B81E8743004646C7746110BC61B86ADF -2D5D8C45A6A5461EB34497FCCD09E711F47BFA742C73F87B257B53F30CB68D151424DC3C210D3E8A -C67C2495A8236EA2D7985A5E1DEAC699D7B700E6D38EEE2E93B191BAA5A8A2C916D206C63FE63427 -AAAFED2B5784276FC21EEFF2D70E47C8540DCCC3E00134642B703795CD3702631AE2A90E063A218B -61E5B89BBCFFF84F567E37A31A9B349717A8CDB9C9377215BA838FF7469BC486B64EF2B6D92519C0 -BF0826E3652903F40E400689F5749DF86FE3DE178E21E20EDF9053081F6510D8F19ACD021CBA481C -484D30EAD3B84ED0190087EE478A17154B243346C3938FDD5340CF6E47B185E64ABDF44F8CBCDB82 -94492B91929BFEB9DA2B033C3ACEE554F0F1A7F8A56DF7C06A3583C1E9C5CA458D40E550FDF3E2F2 -E7BE8312D5FEE98543388EDC8A04CA29F1B82B7AB4ADABBA3F2C331EFF3521B2B92F99C4377AB827 -A989B423750D36ADDD2E286E7F3B694E29B8BC403693C6F7CAB5FE34F1E48C8D41B47831E8C3F5BE -5ED5142E3C44ACF5180CD41FDA149B1F4AED36812E42BC184227F5034220F74F67830255E1CAEC12 -66DEFA358A87D2E3B4B4E7EF30181570D0B2B43072EE0311C2C157D32EE2BEA8EA4251B59F6B61D2 -B4FDEB654DEB67AA3DFF4AD65B727F0D6B7D61523E4B44D99BA5CD33540F340A35DDD466ABEA4E72 -E504FC9BAAE51D231C33A8CE7DC2970DE4C1FB5B096A3D9C641EF77DC9039886831DDD01C4F21E6E -168E38BBDDA5F4308C959C7BBF36A42D042DA6862937EB20D4FA2E5927741A58DA5CBFFD9553BEFF -BD92E6D64871D8B25D9049F4E71970A8FF5557D1DE83DD24286D6C3E4770EE00F9A1A0B0063C9999 -4AEC75E84D6F9C488434D1F3DCFD0A8BEE9ED8257CA97E75E8B1285747184D6D2228EF95D4A0B8DA -252318ABD35C8398FC6568B294D90AB308A7675F9F160140F0A08C88AD0CA1CA2CF85E4D031CFA3B -87635F1398EB7DBC666A259F02DB6741D13E11B230025DD6DD64C438409AF109090058151E4DFB8C -0E9CD65935C4CC063CC6100FDE70896E23E3661C7FC1B8228B26A55903E997F80207EDD8863FA074 -EE4FF23BE585BAF708040C9F8CFDEB42FB8EB71D4CB6D7757E973E4D8C9DDD082712C23F868E1135 -ECD91250BB4335958B07C12FDA75EEB56BE19D1644C1F76A8811C021122619F751CBBFEB1D3DC912 -999017FA163672A1EF754C5CB78962BAAB76EC48461B492FA88F9897170DE857CC8374C8BAE417D4 -C78A56047024731F4A45145F0393A27CAB614A7FF747BBC28E6880D4D01C0A6CF317A1DE5BB5ADFA -4B5FBFE0C57598C79F25AE57BB797A489D51F85A9B9CF8BEA64293F8FCC43B0D5484DF99DBE19152 -692CE756F6FBE8CE5831CF4B8A5AF47524E272C45C62ACBFBDFE7E60B05BB1A1A6AF0E9210012014 -69B3DBB49EC7B23A363FA68417B7118DCEA71D4ACA2E36F88C6DDEFB70205DF3AB7C74CF65CFD01F -F85FAF99F172689737331D4C6CFF7A29029772F487FBF625F17BDAD89B4AC076948277B4ED687840 -301016C2B7AD4C6D02F81E88C75B7A04D724E234E38A38269351582245E361A42C75B8256AFD5624 -B558ADA2190F960A896BBAE7A8C57E76DA10DC29E69BBF3AA86214C001A27B39C1D17C548DA5601E -86A5CF53E7B1896BF003AAE9387ABA9B102EB1E9002DD3754A378F3E49F2C6EECF47EB1BAC2CFCE1 -1AC0C5CB063672D32733563F3E1E891B6073739BC53AAA0043FC45E90E413DFBD4548DD320B681ED -70A7443A233D79E3F038D26975586E5CDD2115AA614727B1F6DD4024B85CCCFC79D10B7B6AFA789D -B37BD0E8C423C1A4A8681B5FF3A9FA1F61A46E46C4B1836D1AA41A89264A7F4B1C259E4B10ECDF37 -5BD26A1F412FE01FBDC03368FCAF48AA0EC28B1BD603A6A0D0DADE66D14C9B7285569230FAB76803 -35BE104305E4B748FA99FA31F23991608DFDD2097DA292551136F255051C9F7EEF3FB7C7FDB4E651 -C3D03A4CA357B587245236F4FF3252563F6BE08EF8A3EC09BE2BF27B9120F7D37801F6999EFB1C8A -D1A08698CC59CEAE2CFCDBF6BD8F94DEC94F7EBF33AF05F52C85760C63950B455510C6AB9398D09A -C288EFA09E8F631A59B03FBBC75BBDAFD675FFACCCF8ADF71E815A4A49F14BF70E42DB0B7347B528 -4E234C24010E2177DBBD57648E398FA6B54571A37BA8C989503594D03C6E60871A7F964599022154 -02BA168B8D1D2685F5CF8645D5E11A1769473027F42564C2966C10C0DEE1EE1B6975852A4870D492 -83A470E623337544A7CDA5C16FE2855BA2A548511FB4D4FF2E3E78D108E4C734F64EE2F12CC9562C -BDF363EFAF5201B673AD00583FF108AFF6B68055A5F299452D176EAAFB92C84F114C8C22A05EAD65 -64A3371420EA9E646308DE97D40705E1638DF08704FC90249CBC0D2D3E884A4562CC27370B1A9738 -9D8EFD237E644A7370B8B38ED1C377F522C75F981D878A5E87101E621DF9D85C7207BBE5A87CCB60 -7F93A2E52F660E05C83A7A6CE6D01AB4B62A1EF8DA47CF97D4BBA0FA8EFFA9C0F61A325A97ADA694 -45F23AB1FE27A66C271639F839203040D44B11ECC6E805FBE88843B34C4FD52D1D3C6C70FFED433F -C04501FC20536ABDFFA429B8DC8192B2D45DD9D646049CBF40719C3D674773F9676F9FCF32817DCB -55402A72C56D74AA4CE4035687C730B6B44A9CC614BCA5A3FD17C170ED949E588EE45E89E18B0766 -2A6327FB9E8475C43E5DA1B0AF07C23774B19C9EF59281F5D884990D6194170D8293A86DB52A0FE1 -7E88DA82209A00A16BD29B8B2F13FD60AA25FCFA9745F57C8216283C1D6EA1C119CB9B8D57C00419 -5210FFBD56395A3EC2D3098ED38F389EFC0324FD0E55EA339B3892568229D8D3E205A821E8219FCB -1A7713FCF3450F8BEF976CA0BECA47376A8CA73DF85B340C67EFE4534D459617996526B5E5D3D19E -17CC5449E5EF2B82B2C4C2131FF8A19FCFE6A186A9840D872D85C40665A7A04E67EE26B8BC9206C3 -5B44C8F8A1AFC3867D96DC6D48BD45063BE25B882E9BC0D0948C18DC870E6925818E1FE17D336217 -F174EB4481F5C0ED37A3BEAFAF4D46F857811B6728BEC461AE6468D87A736572F4FF95B58B04564A -9D3C22754587DF15495A319D822B838461764B73483C1F7CB930EECC6F7424841EE10E4087E95120 -2FE88A391375C96BEC4480328A54740213F741105B12A39F19808F3823507B88115D468C61B212A8 -ABAE7480E39BA52390A1892C7EC50271156B4E8076FC3ADA222695DF372385DA7B117A29E04CD2B8 -0A320F186D61C963FBDAFE9224E537057C49E82E405196AAB621B5FE4011E1782A747EF935ED8BB1 -1BDA39A141CC0BA42D04AE123383BC95A1D03A85A9440010C3B9613064FFECA76197E10919BA5006 -F35837ED9BCD7DE5E6D968AACB6FC91178091FA467EF6FDEB728E17293DC89DDE5A5261FAA95A2B0 -000FC750E7073900D4D88247DA464613ADC2B3903A6132D96AC0E1C564385FFBF6249DEA76BEA2A9 -9160632DD2FC2B99133E9F2F470F72B45D6F18B45020F604B06CD9174BA3805DB60EB9C5E6A9C789 -ACE76AE9C79C1BD34434E95E501BC968633AF93FF4883C6A596776254C0C74993710327086B2886B -02FD3E42A725A03459CB36EE34A094139AF5FCF487D3DFE63FAD20BF0DFB60DEEDA2ACCA3510E963 -189D1256EABD81253F7FF9D11263FDBC1DCFDA3D1EA2E52005CE3C605C993231258A717423F64BFE -EBC34684EFA676358B9B543C2042BEF954829FE3246A879845B30EBACB43D8DD7A20FCFEDF763AD2 -C5D20A798B69E08722DCE6A5762E249ACE3055B650D9E110599EA30DE5C4FE7200D5A8DA9E1FE268 -6350D0DF334877D0B9F6524C552D0B6DFFAE125EC4C18F7547BD51C14288E4ABB7F8A1A00458596C -390AEEE6FA308AC1F788FAE30D7F8928AFC91D4DE6352D20B19D8D8AB122B7378CB379C5BE7E3CE2 -922FE667EA057B5D7B3F0B51C7BF0C85F87AC2F360D82C38964F4DABCC9104B32F0FB8802235E8E8 -D9A5997D392259074C00AF2CE1D2BF7B8E90E2E2AC34185C68A03BAB8B267778292B227245D7FF86 -70786E3F746F86B9D4D17190DB859A0E144B2A61E6AC9254DE5DBAEF20E2E9DB0B2FF654B996E962 -F55E465DD238BD1643CE59DC2B5A58B1E6E4AE2DDC2D74D79AFF3C34E4E593E051FDA236B79CC0DB -268D2A89B1878051223BB8F33FF99BA87A4811C0B3BCC01171D0A731EB732ECD8749D27952C27886 -B252F9C3D190419FD2900987A0A255B9753FB7AA70C37462134C467A2C4B7920BED9F9E86F8F98B9 -6D00AF8B05A4BD5F14C2A0D914A9A84160D554FD0718F50ECB5DF5E76623065852DAA74C9AD6DA07 -A119DF12C3577FE276AE551D48B1C5CD8A50E84DEC9CB0840520D78FA7F9A7C2071E28CD20EC7649 -B991F3818CDE295CDB6085F24FCF93147E9F4DD084FBD32525326D2EA147ECD5B6C9D9F4A7166663 -AD18BF234E9CB92FF72138A8A49E73E527E9A6488A4CA808AECABC94D693CD2C0C357D285F65006F -A2F9197F61FBCA6EF07B013E2B558AB531D2FD270CEE7FA8E467FAB885E90C5884843AA08E2BBFEA -0AA575643727BA18ACC499FF34E3438645BE2AA71EA491E54687CD305E12BBC94FAEC848311AE816 -495B013BC5075A2D2AE54A7AD7C9105B64356CB51F18C2C28E3A83B9D81A4554DBEC9BEA9A660CF7 -E1BA89E6D4DFB3EEC6A3DE3FCDED9B2D614156EDAE8CFDAD5FF0EFEE31DA3E6A54D94CE9453A1CAA -D9756D91BE85315F6514BAFBC821EE810BB5D8E1B8F05F64F3F72C4B35D424F7E4DC3AB581B74ADE -B6D6297CDE7AA8278909F269FED79B7DFD39B1C0338E01D556C4DB9CA3A8578ACE3EC3D743ED4B9C -0145E4525E8C315F7A1B98584B975C70F0D415708C8CCC13F848B1D36AC8249B73638F95DE0CD27C -7EFB52BED4339EBDA4812564D7A77416DDF4CC88CFB52D07A252D89353C6826CA1832A153242979B -6CEE783ABDE65C8B40CF4EA7B42B8DBCC0E02423DD693108006F6A4AEBF053B666C3CB63D1861F86 -EAACD43BB9BB6F2C3A17293C189331D253B447757EE7CBF4518BABB73A1D44874D7F0625E6A013C6 -08E991B4AD17A9ADB36740D25E3E35B459B422F7370B134CDFFF3F3BCC4C32B4E9EBF6A2478013F6 -6933A1FA9403A2F1161EC632F1F04EDF95ED0F33DAD9665D54DD9DB2564E51DA7B65978CAB50D6DC -1568976E83B056EB0E3A6758518B6E17E9EBFE49B72EB148B472BA144BDC2AC95744C9BF1258F0A2 -E47470AB0EFF90E190A41108914AB8C1ED6B11E0681778521870E80C16AF2AFC723CAD8719ADB62D -3939D3BC8CC1D8A4E07E9D734F54ECA33D936D2C39D5C8055739C33E53359BD40E576C11E93B4B4C -122BDBC9B1BBF44243AF4F0BCDBDFADE68C526B5CD74E29CE3F70D62BA83C489034111FE8E4DAEA2 -F01F9D938ABB532DEEAC0E329F42453FF5C15DEC2AEA8C198323C9E8FEA55B3F5DC4751D2E2E16B6 -154E7F2ADD46860E9CA71DC114C99D80E7EA1DAB51E925DE161CEDD678EE6282AFF38E3CD0E65954 -9C970613209955A3F581E1ABE485E56402A3DB0D1E9B8A9DFD05C4B0B7F97FC6D0EED0B69AD6F182 -B1D028ADD2F24463834B13F5C1307F91D363891824E81108E57CFD5211F86400D3E96B107F3B1FE8 -9C4908649D04A46DC3CEE0DE66AF03A7FF9F4DAFECDD6DF4D93784CC899B527784DBE0718050FCE1 -85BDE3F39DEBCDD660B2488D23AB1CFF87B0546D02B48E7B7724C9E87B71BF34B5D6640E0F6ECE47 -B182D41C89461F712849C6CFDB7E3F5EBC1ACDD12D65A422BA362A8FD6CAAC5104CCC5AB5FC04A46 -E4309ACAC83D659DDDA256CCDDD1BFF9AB3622450C4FBC89C82214F00C42FB0311BCB1B722A691ED -839CAF9024FB1671F18E4639C96D84718C663A4341DEC037175C6BBD288BBF5A0478298CA726567A -9B74C32A527339C666A294A17F6821CBF243D13EA4B1603C292953308B566653423E7301A032E5D5 -E2B93F1C1434893633DD19501AD12728B5A1D9D36635B589FA2E151140B543D7C5E469AFAE8E80C4 -FC1D9CB6C3823CC1BB7EE40AECB58CBC1465792226B19E0FE79235115F6A3AFE19F98C5DB63D372D -D7C041CD940F4F79F2474D9CEEA0334FA04A97DC9773064895CF11CF73F11B4684F06E48F4469F6A -1AEB2CBBC52994DFAB3319DCE3A0C8C2EFA9627496F8CC84D3DF3BDC4FFCB61672780F294F453278 -AEB9262E66486856D37B7647141A82E049364ED3D03F925284A3F1FA3DDF4C0B48B3FE22E7DF9ABA -239D33CD300FFA8FD4B96192BD568FB18D325CAA8E1F1FD4B27527417B034841FD49E4A77F217062 -3CC8B22101166D80361EB15FA9020D24F61007B0A8274DF9DFCD8E97C85568E76D34AD5DB1779B02 -F034A69CCF9D4EBAA188EB3017EEF5B22A0A552696A574907F695098BD8A4849D5C8311F129447CD -7A3CF88B8191AEC0AFF30A38A9AB8135608A7829207A7D242F6E1FA7DDA19F5E4C28560D42DB4405 -77CC0C5F5803EEE897103ECA0BD944E320AC26553BEE7852EAA733BD13DF760056B2F5BD1243BEDA -BC3C1EA0531017D74B47E18F801A60074D6DF849FD0532234545E5B5E112D1E7385341D39A89551C -80DC2DEAED5D5DA2A4BE5015D297324E92BE64C68428132E6EC654DD4BDCC6640C68835FF8A05E09 -9604B8CD43D3AF2B2FE10C8AFEDEC5A70AF8509D12F662338CBF166D9452CD36331758AC4F4CBD7E -DD52139AD27DC52569877FE709F297444C4F31899D1945C81B14ABDECBF31DC463A4148F04EC4FB9 -703C158216C0FBE65CCD450043ABFD4E65BF8B28CC148252E9F3E797EA0B57B8721C94CBC2EA602D -F2C57E87938C887A382D2659226463BC7D6A1DA87F4A341A59BEA458177D3F18D1213539DC0E301F -6EFE0111FCF6921368BE17CCBB7428127E0C059C2C5ADB2A3F0197F0CEAB77FF7F3C027A8EC3EE76 -CF5C986EB47CB60561C773B3A2DA47B5A35394E29373DBD5C3FF4C9213A89AED77CC4F3FCFC49EF6 -EC7557C521979A546983C106B3627B5FD2D71CC5F08A32BF49332A89C5DA71AFBFB94C949A91220A -B1F885C981423AF93F73BC1CA4D92D9DBAE3EFE6A76E2DE3D0F74FD3255820636E3F1A6B7C185306 -23C12AF90CDCD2C0A728521E9B639EB6345D1DE8FFFC3B19C72E7A93823DFE3115E9E7BBBEB28CB7 -3DB121AED8920D47D8CC08EA2E472E39A4CAD5881B5C4204F2B732AF9D5189D25ABF413CC78714CB -01B1D8CA5565169A919DC481F6D2E67F1D490AEBC5CC62A8F62C1A323EBB55ED35AA5C8D6F8B970E -93205C2701CF4817BDA994FC16197B469ECC5F5E9DDF0FA05640C2E571849571CBD26402B1EB1E80 -3FCF423345007B9B52B13E3B034E8CB3984B925EBFFE719ED4F39F3D0E3343316A6FDC26BDBEA88C -4366D3B2F851D2B244CC4408251AE2C77348CCE9DD8BB9C89800B572D38C5D1CC34C740BEEBB5DDB -0A8BB251655FB989840D23205D16311A9FCCF7C85F6DFFEA9704492A4E7A8F6C0BDC29745AAC2ABF -AEBA02B0E7AEFEB92BA63AB0DF844EB09D505C3DFC1058CE42CDD8043B76398401E1DB862FF9F76C -05E8BC6260A4443CF494BC1755913D51745BF45ADF2F8C7A9546D7EF4FB11E9D94E4539632C2A396 -06D04480EE459408D7A2A869807A4C01881C1BB21C296A402B5E6E07093D833C3DFF075F4DD426EB -87B1B8DE16C146DE79F52F5943015331EEB852809CBB8E1D6460AC4D176FE96F8D19F6CCB22ABBBA -A27C4497D91312C3CFB5BB913B314E43D2EC6AB6897BA7C34CF2CAA6DB4BD69EB5DFCEE0AA917D69 -50E36A68A4C22A60DCC69379D47544A58D640EB10DFE120FCA843B588CA8B94F7869F97609A6FE03 -AC86EC1F7CEAD2EC8E81977D1B946E459DFCFEFE65A7BFF67E66F5F78A45D8DF65AF0146DF74E024 -FC042328886CC1DD7779F49CDBB750345CF83CD678A6A8897577299DEB38AD665DC4F21CE1892A18 -C256F318107DD3E9245C1AD3BC93CEF7B7BF057E33EC9A3F953251261AA3D1A8347261E70A46F777 -3A84F3D4D1A0DF6DD22A96429349DE0D180310E17955B10FBF53220EF6483D03C650A8D5C16D63DA -F65C21ADCD6C2D0B5D4ADEB2F5526AACF7CF42F9A8BF4832FB2D4F73F3D5FFD984B572232F87BD3E -59133ED3D2FA19F7856AD812515C74F7D851574019C532C25F8E163E595FC9C83E3E820C3CBF690D -A62578A980FC0803EB6DB9B1E90E3256BD4650816ABE5EA86CE65C2EB418D0ADDA5F3EA04E17AA8C -4536CC471AC20236E66ECA3619F161DFEFA485386C30EBB86A7AD930FD0AADF2DA69DCAF26C0F677 -206E2030E3B15B3662C0AD03DBC1636EBFAD1F2F2C37F5FA9856B0198C5B1D80B69C5EFFD94CE071 -5135C649C26B9BA1266B0A5B270CD08A706166C0B320915C87B27DE21DEB5D7E4806F6E700B7A06A -4E29B629CB40761983E9CA8E34E869ABD04DDA190BFE5A6EE8B22D7E511B84EA584A84211F27AF89 -18DC5AF8A1FF2D360B6BE3CA8E66BA4CD2CE6A25E7E89406684DA83FFBCCCCBFD0844FE3BECD7DE6 -7764C59C022DB1168D585FE25073FE00E30218D1DFE115CA1FC606AFCB04F2A082EF91788B6BD096 -84DEA31F20034A91AB9D971366F97B5009FEFBF1EF0AD941654081B1E8F0B2EA495069A1DDF11DC5 -6857D29533DC857958B49D1A0779732819FD22E437084BD9F3C4F2CDA4D12CA14431937AB63A03F9 -C040AF1D801F367ABDCA7302E18A9050D6026FBA5A5A7FAD44E31593173CDF277CD737D1CEF59FE9 -684252BC0DDD00A80E023B88222494C1C8C0884230AB11D1083225AFDCDBC1E24D4AD5FAB396D2E3 -70E44A7571B230660D510A5076D8E35F7DB72C0566DFC119EE1B8AC3C0406950A3C4A4DA36BDE297 -040A27F70753A87E6CD593DC6BE9962261A99AE5949340C5D45C94A9AA3DD636CE8B497BBB812345 -7C824F443A53B3EE595C38983FE3E07DBDC6ACD55CAE8BE1081AFD4857A5F52A3C925143507A3C37 -F1992CF72ED0D4C48D94AE6CADDC3BC87AC3A3EF035E02181F78449E4B063B0835E827644051551C -1603E2EAB5875F28FC77BEBA6923428D5521C698C6B7F133B0F689F105FDBAC30A8ED2F29F0255DD -F8A037B81F04EDF004CBE639C8DB0F94D0C5DB92D34D66C2FED66CF8B895AFC4E659D08388EA44EA -E83CE459E5BE306750A682B627802990037157339BF142BCB9C08FAFDC3C3FB16DC3544F62C6C7E3 -3E20CC4FC7CA21E2C3F6C546CD78DEE348F1A4C8CB548EF20C049678916771D83ACC9B7B22784AD8 -580134471A3C79BC86B5D6D0D305C32E6204274351C94F9DF45D9B2AD5B5087A89F90D6AA033E4B1 -D1BED022F36147C7ABD2B73134DFFD50907258E610C3B20949E141172B1C6A76DB238C375021CBA6 -645CDC26B717428B5A9B4D3F32A4B1E22FEFF3BB93FD889E1DEF8087718D5E3E650FE4A3330DA9C3 -7E9EB499DF5A342D8BA4C0A033C3347CB25A31BE143ECBF91384F2381E323E7FD3A82A3197C18905 -3200AE2C86B9D01AB0B289841EA7E9E9A26966E0DEF54DE0B85D8DF084B8C590081E444BAF1E1F60 -670FA12AB97159318624F2AF1B5EC7DD83C1073A99398D2143A52D10A13C201FB356BC9E90C63BB0 -BC2D4C42AF4A8B9C8C4D58A1B32E0597C63B3F8B3E893BD3BE8C60231838F1BC78E73A6C8CDD5E7F -2907F897FC8EE99BFFDA7338BCEFB5AEF950E5549ADFD207AEB15846B509FC57989883642498A381 -1B8E5CDE69C05924EFAEC232FA4CEF302EE3251366ECAEF57D25CFA3B4A9E6397D996F421C900BEB -CF73B038FE7B16FD0A1172AC2F40D19CE0B02FCEB8BC47DA5344CB933C7FEC950184F78ACB32D3E5 -E290E84BE753B9E7A7BFC4416CCF29D023760C06CDDEF2505806A65E1508990529245059AFD301DB -669D41BD72BF7A80A9DF66B876B3553FDF4DD38D15289AF7A1AFBC53FFFF135A6348DD784AB42A6C -0D6AA330B069607E2DF3CBEFCE79D6F63E274C9E73A33EB85246D5EBB986BFA923DF68B2B8CF82AF -6C33E785F35B25B1D1D614DE85A4F4510ADFE42D75B5FA5408A59ABE53859E28B3D000EB9C6A7D2F -67C91DD14C895BA87B9CB57B851E5193FCC2A443AF85FE28DF6F39537F23A058BCF81DD8C04CB2C2 -5040300F4C55975E856DCB4E21E2B5481BDCC05601942FB25BB8A6B6F93E2C2A33CD478B44655657 -C557EBB080179EE5D98C5CEBE0B25BFDD952FFEB258014D7A5BC4BCA4F1A23BBA73C454B12960451 -CE1752401B0151CB2E01D5C72595095EAE91D8D3BD55A54A2AEA89239FA176FA7CD6F16BB0733EF6 -CE6E77763A23AAC77DA88C8EFA7BBB2991E472FF2075FB25A75ACFA70A04C28764F4AE4C12051B25 -B120CAD2E3044DA35C1F94135DBD69B10DE147321CBBDC814CE99982AC1D76CE3D3330E41AB31F3C -76BF89B95EAB81AF3464C732D5B1411D97DB36C9063537F64756F205B16ED7058E2CB1D6946C00A1 -A0CDA9EBBE924BDA6C7D7B605C514A98133907B793C74CA858E82DA3519188CD974B34DAA74265DB -5BC8550D5F0B1173ACEB87458BCE2AB1F96996C811699A0FE4A9B849D39023725E2B1EE7E426D30A -6C5C75AE6BCEA6DB41E4EB2035F7F924E6B9F0DCD00EB2BB014222E55FE387FBF5B9B7C04F4688D5 -AE3529FDACB38B5EB0AF5C3A874C1AA6B17CDA8D1E22EEE05A3DA88449200D3D0D002DB86F6C51B3 -37C8E19F338E7BFA01E1202612D50E210140947D5F350E84F790286C3F679A5D7E43BCDC337265C2 -631527FD62D598B7CA1F5835C0441881B97F5197901ECDC4F195BC665A846823D2E41417373F8639 -567B228FE7B73D781F07A361AA49C3E9D80FE5B2A32C4C1E575D194E841967B08D10405FA44EEE28 -47DB9372C5CC931E50469532F1BAF577F680BAB4E30B7E1CFFA8574ABB679789F69A8A1BAC07B7C6 -4EF5CE5EB00E97B36FBEACA9BBA4A13B0293D34BDBC77AD1FF88E5744AF009823BC262511C4724DD -585E7E17D90F230F7A5861B0DFC42F0B4E49A04EE0EE4DADB908479DEF8372F334C53D2BA5D855CB -39DC7C9550F9D0F7F77E82D5A59FBBF34BFFE92DC9E6668B68FEEAA4F20053433D6749162BBAC5D0 -D428DCF2D58D49B127FA2E674EDC7D3613B1342F4D0ABD7F4C5B049FBF78E804D5F16505AE7EDCBF -4D6FA08D72890F5D55199034572AB4B0C9A7E7F6F5A403198864ADF113CAFF5BF9D4AB5B16F81D0F -C2188FC80875E10034D12E30C0364F8F72797F1AED525A2712A40D44210B813DF5A29C84E9F6D51B -1D60A5F6F938FAABF878D29E6AB252D95D05FC1ADF5D4CE1C9E585219112112BC6CD5C766411FBD2 -2731794B5DE0A27AC57D3C57926807469C360372BE529098C350EFE2154B87F1205A57A0B04C5206 -CC4FA66B8793BBBE492CC3271FB4F90A28D0066E0D7F63B8DD01549A05AFA5482C29560ABD628568 -75CAC16100087540162473498C14087B29B86B7BFAD693E81765CEC781F3FC80E9C7B410E9B55B88 -114191A1703C638DFBB469ED1DD8254B1407003A319CE74AD419B077F17047A01F0BC0AC8507191B -F72D77D9333C9DA8C9DA733EFB5305F49CB8C7BC451321ADD7D896395D269DCDFDD084EB3AA70338 -6C0697E962929651164135C094D9BB1C9B949D5EEBD3BB17F02C98C813CCBFB23C2C26218A2F4C63 -9A8B9DFF2C29406037F91938A5E1227310728428B56F48108CDEB33BD3191ECA89F947271983DB77 -6B2BC897A30EECF2601EE3B2A6F0E135397622AAC1F2DF523CE6E6BC720E13CB530CEF4AB9C8273B -D3D81563AC8A8E6C44A195112DAF824BC7A72FCDC4E129A480717BEB01085DEE65EE4344D0B41EC0 -BCDF842566B1D9F5353B1F6A063FFA6CDB06EF634C8BD5A7A63F991D178F56EACA653DD67685CE49 -E98C7554745A4AC533217662D23E1D6937135D13BC2208EB8D50560A2BAAC319DFAE478B6BA4CA5E -DA20222F0E9BDB0806320ED1665B54A347DE0C42E9F77842DE4D188E7E824EB2F0D7AD163F05480A -7FA99C5A603BBC5DBC843774CA66E889B945054C0ED0B1A4BB14324EF901B023C208CB95DFCE9284 -89789690CC45BAB97BE449F8E2F5AA9276C0571303E9788C46E7F789555BFCDC3FA9ED8DA8AD9BA4 -8B3AE09404664391E63A989EF1E24BB464043AA099E4F2D796E352EB277106D8D81BAF2F8562EF46 -BCFD1E0047E8018CBD973021DC1C1D821AF03F083F0B088A62EBCF2BF6C5B0FCFA441AAD1625FDB8 -34F943DD47A5A42EB3E9A5B49641F797C288B799A64897F1346070461B6D535E0C4ED099199C387A -3176AEDC7DA7E7D9E118E55565092A36F7C74ABF281720C0147F4E4F37D49436466C61FF12764E30 -43D8A6D027E70537164F0E7942F4ACA42BB2CB136177EF7197E76F49AB403F741C0EF902FEBC471A -D6C627424320A8C3A1F04C310C511B3F91C3937D9ACF459999C18A33F2C852EC38CA806599C728C5 -43714018C65E2C5F430F6270AF52AD71ED38813B60440779455F9529A4A1623CB9F5422B9216F9CD -BA913B9A1CD95DA225E254E8101216085020660509D03A034B5D7E32E3DB5E5962A9A27711D4C3E2 -9CD84057F7D0D7E8000947AFE896F8523253391D2E11FFFE523366B05C532D5629A90741EAB3D4A7 -31D3F6D4F03FF93233DDF88BB1913ABA22EB9AA6311E3144381DAE29BCC8639958EEE59ACCFA06F3 -5DCCC63E0609F542F3EE5DFB1CF718CA3F328455726F8F65E23ACD970E4049225998371B63E35AE9 -8DC54D8329B8DB0901FAA63129EDE21B158776981D4D094013C096E9CD020315D123C03DEBA21E97 -E4B584B4BC0AF25F5DCE53C2DC0F3E61F99BECAB40799478BE7F5AFD7F68E23EF50AD6645C967EE1 -1206B6E791769428ACDC370D64E4F2B3972E0E4F442297199350663D6E772FC6777A9B9DE215273D -082CCE4E8678FE9948DC8D5B0E459CD02F1645AC5620F3571A40B4D5A17DF5CFF48B6C843DDEAB5E -BF58FE13D7DA08E8AA7902119248B3B151DA583101CF80853B0150FE05BDEDBFB50A7FB0F65728C9 -3B9DF48CE8AF1DF1FAC25C1D58E1AD30274A00EB54CF2F16029E1AC0A0919C0655474B9A6936AEE0 -FB74BD185FE7D70BB84786997D34A40326A74356A4AFAEE67B6B26D1C1A7BCFF8697B55C816CCD77 -312C332A55315DC54F9BC0A0F12500E0A76B3936292A3DA2DDF5AA8CBB9B5DC32EDACC4827D684D2 -74E65B8B76FB2C2B19F7D5607523FA953E34BB39032C05B1C1244304606C55660D3CA8607E764EA5 -B03DB7FCAB5CF7788C6E60EC8C449BCAFD90BCABA4132B6CBCCFF16784FB59B36B77CF0A9EA572E4 -CA0A01C725A6CF2E4500CDDF5BACCB9094D48925434F044118CFDC2696AF5FC0CAB3884107ED17B9 -BDE0C0104B1292A1F8C99B06FC4A6360B24480BD59DF0488641899B0F42B1311B582717BA7ECFEE1 -4143654B5371C8B9B2D80685AD38D897AD1E64875C28C7020A84FBB3A3BBEE16617DCB9BC822B7C5 -9C5A18C0CF7E80163ADFB7AA03B7CDE8497C1697D90F2ED90F813095C5B91657FC294EF0E341DB33 -92ED860CB2E0AA09293D0F99AE9EB54C761CA2DB1E51E1CEAEAB276C7BD916C68510D72D9A67468B -09B3C39A7815628FB126CDFD5EFF59CC8184C0D35A5B5960F824BD175495DD3EB12A4E96008CB13B -8C5745303E66CF8608FF27C4709C1D854EB79608E52F068FEC0151A74C125EDEAEA555C198FC0802 -7BBBB802835E1D435077AE4B1CCDBF722354F6C572BEB1376D3E342195FA80AC9722EB2F46E44DE0 -5F5A227B731B8D4A4B6EDEF04AF2C5DEC2EEF8FF48C5B18710ADE3DBFA0C956505B6DA9CCB7CBB83 -4DB6CC754948855D833670FF0AC42A4773FEA8322BECEE04CA74AC2D66855132D11A51524488C547 -71B5B7A512796D7D7AE0F9C1FBC9CBDBA0831074F4D200349D0CA40537B92496692766F020AC43AC -01DB8B2AA2EFA9D21732BE3A315F6CAA402BB2E61D40DDEBDE11276D90C2C601A935C168BE600464 -76ADED15087D54A14C68EECBBBB590927C1E10D291C9285334CB0C80EDBD392BDE4D535EB61F8E76 -41F58AC1DF5B1C5A5D91E3E27E05CAF7EC97ECF0C85B6425197AA856521ED701E5AEB82A7F52A8BD -7DC97D5B3FB5C99A5DF84D1BAFF89072922509D76BC6EDB15CE5F9EB8F4154BEE1E82020240283BD -C83A8E49AA9A2649B7955D5C058F2818A63BD0BFE7EACED4A49063C489A626277AE1246F721C9926 -E2A2B6C31045FBCD235F3CC58BC4DD6C57FE998EBD1E9FA5154652BE3A1685BCD2EFAA079A3293F7 -8142A6473822FAB627927EACCD61B3E99C3077103D2D19382BC7EE15BAD0FDE489602D055A01DBBC -F91A566974559D1B477C209416887053169C3F8F59955BE4DE82B60558CC9AE15602A93F029F6B43 -29E0E62A03982DB32F5229714EFA1491A7B24AEFE18FEBC2C93DFE50B3F641B51BDD33DA38871BF5 -243C17502D00AEA2D9E9734E80A96788D4CF5BC12A42BC386162FC88A7435EE13200C1C2C6CCC5D2 -1A03941007B4C4291BDB711446CEAF27148104BB240357D5EDA0EA5A5CE27D4A83909D75BFC05D75 -F10AA74A6DE37D7DE15C1DDA3AC3045DA6CD48323D904E716B445E5E096FCB379353ED70CF4B6FAC -102C762711079EFAF13FB74C9B47AF75F3F6BDA2A4647D2AB47ECAB64DA6CC01479F618E8D2D0A36 -45445E8744683CBBC560D47C98078B84206E90EB839B02D37C852B8E284463D4E4D890203C3D5B20 -352110034EAD6BD7F41456B807E1DB1631A9D499E52E9D9853D86728B1A2E511F40F8CA1E4724A0D -17ECD640B52FF6C66E28693D89765FC391612E5889E77423EC85CBD0A038B6BA98B607701DC0C4B6 -6B3B28C7790A1F1EB8D051DC98276DD9CFEFAB3F65C1C928E48A060C992B392A43E56EAA6DED896D -EBCE71F8245BE4687F2F1B8FC0F43ECE8DB0BD0AB0811C5CE73CBE336023A0D66168B34A95B4B0A7 -50B3BF1D197E3C042C7914FA731D7831AF798E9429571CBB977E6258244E84701E5FF91D608F98FC -3D68A4EE5B81D5FF38B6C184F6118B875F022B4CE207DC7B37E1452DFDC591A3E506AE82C7E7BFF0 -011B0A3DBD616A993FBF878FB03B6C9F2055A2B095D29361F8253C2623653687FE0AB98078F6AEE5 -FC2C2BDE0405EABEDB3A33EB7F04CB6837176245F190C6BBBCD64522B12FE7F9CDCF201A1AA8A19A -7BBC4AC064B4958F44AA0F8DDA23835AD28A1FD0EA105DE2F395385DCCFBE2261DC5A89A23AF606A -3985E5038706B1FE0910400E16BF008F250F3BDE3AD806C735495D499F16F99275010478FD2127BF -7CEDD6B5BD505FBE9BD0065B4A7090C9D27CD5B36C3AD33E1B31EB6D44E375003B51B909DA50BD18 -218418B3CD22B43278B144BE78406EAF16C7DF6B6C1C6238004AAB73736B38E168441DC16F9A5CF6 -0793A18633BC43D78674D12D38CC979F7CAADA6EFE807CEA499CB9FE616496682A66E04BBDACE1DC -112B2156B9B0B20A58A8CB43FF0EEDB99805234B9A5789762AC7D65F5A319C33F4F7438CD15E06BB -80A7A97E976E8CEC23F4C646A5821880A82B2F1DC27767F090997E91488BFA15064B702F864FCE65 -05D6CEF87D2A0A12B55BA189AF269811E3B8B850C8401F3906C080D32618D9698A766732A40A9FC5 -A94E5BDDA3D028D823D6B603B6D17DD046DE181FD989EA0F80B4CA62F7973E4DF5E032A31FE6BC8F -5CDA678D4A72787EB8253EA5882C337CDF9AA3E1E7D9536DD09B047CD8962E773F72F6418A3AEF5A -289B3406C152A50CE7BD4B493FFFC27F6AA52F79EA67E362FD92559AA4F94A2F787F6C735DFADCF2 -F08AAF98B80C53CA5607A94F25F04AA65A70A75937840E73055B3D65FB054C63E2E48E68488C9315 -A13EE949E03E46723C11CC759D222CBFAD2E1A87CAD779B23D38F7E2F660DE1388EAF1CF4D18994D -75C6CC63F187FDB949940C18B537A0AFB12AC5F67B0283CA5EFE2E764C4369104B9D3B06490D1244 -C41D6085C85F1106082EC9DB84586230511C05C82412D2CDF3DAFBF4759A775628878F997415296B -C416AC8352A6C6988691FCB831CF95C10BAE691ADB3BA2918B35924BD5C3ACAD8B137397B10AF82B -479800FE16D472CD0CDBDAAB4F882A0649CF561004B8CB7CA32EC129D0A415BE6CB91DA2B65F44E8 -0D138808A127E851A7FCF927E99DAA0EA2D626B77A16C72E37F058A3B882FC4955DC8CB6312434BD -3BCED75780B13590BF4FE8D64ACF0371F9FB1D361B05025852AAB9EDA1A0C997CFA58052C454FD45 -1E6C1F194F4D363114E312F6DC35BBAF357A32CD200A3DD9654155134259887D677ACC44F89AA401 -CA27282DF7DC3F2F04A108CBEF2558DCCE28BAC2D87B8D5B7181EA927F61977764F882626D4AB338 -D95C9477C54E9C36012A3CFFBE199EC8120A99D2D70A21F9D9A0354E4EAC7947990E8A6E0601796A -AF6F14E758CABCABDFBD8204A8E748A3E5FEBA570D36E2BF474C0083229A63F96114182321B2EBE1 -BC76DD193724C4588C1D39D184C332FAEAF4C629F2B3B2F49996E46AA6C9F497428BEA52D58876B0 -DC07B460248BC85CC16773A5DAC36CDE8B152D96057F4EFAAF8B1DC10022038577368057699B3A37 -178A9F1F6C6CC60BAE820B7ADD0717911BD23A6DCDADAFA32473491AA80CFE90F2A77E24CE2826FF -77B18B869C33FA292FE01D6477765044C7D14A548B28B1360125C6933F05C58B0889390537CDD16F -8E967E0B38579449DFC1E07389B7069AA8594C5103465D5041CC929268DE863FADB6925B350AA94A -27D421FB7FCC81C6B35F906F12246B7A5140511A97211BA9BD6831A508E963FE8BE961332F557808 -488F06EAD75E86D60DE3FA2425AE8439ECB9112BC3E4D73747C1C8E87A649919827049832DB0BF6D -A8C85C9A2592AC002809070900ECAD52A56F1BFD456AFE066509694EAC075788456B0B0BDD7C192D -321E9FB6AADCAEF00F570F22CD4A5322FBCE8FA98FAEB681940895426270BB4319C11DA67D88552A -7373398AEC5DA7C9CAA9F3B34581C6E968DAAAB2751CC012199DD897B448986CFFBAE4D412BF9ECB -F46742715A9569932516259D3B3A5431CD7028E42FC751C434E2B714C718202BF02CAF9B8A2075DE -922322EA7CFA605C8376FA958B8FBE43031E1026FBE6126A3775F643EA67EBBD97F239FB3C435526 -75CD08B19CA5EBF53B40D728556B4481C7F73EC71CAB0F89E34D60C69B272FADC22E8E7BDC6210DB -09FDD913E209F49FD28E8712B8508904620250746CA3B21B026EDAE60A2822F59E912E626B93E0D2 -BFB3230DFD0E54E91A1DBA25A609B64D41ABD897A5D21764C351E85F9E87BEAB9E645149AD32AEEB -B3B1161032C701647115F98C1C2AAECE871862D91D321AB90F3E923B1FDEE00D927F897AA9812373 -6536E2E0700F10053D7E6C589BF66029D794883EAE4C8228941CE96565B50D48887B5314A2E55379 -59638222A6CA54C77CBABD460DAC11B063519AE4F50D93DE41763BA7CFBF4C7724360E750478EB62 -8921DAA065858341958E4F3EB5966C6DD77C05EEECDF4B5F6CF19AB507589B4219377959BD258EC9 -21C34FE1DB003F7D0FEA3E2FD6F5DDB0A2D62CA5A2CD3C7AB457DFF25094EFE04A9E1B9CE7AE3F30 -026B1CB039228D309A22899F6E9B9BFF922E117123347967D7C62C670E2C74579C35989925603022 -C17B1DCE378031ABC9B4B437C7B6E64620932E93189754C01D4B280B8B08699B2CA953AE4823BB9E -E34133C5C95B3290E1BF010705AD852C72BE87291E1034B09F44A95B6A2F83FEE8841DCF661770AF -44D0AC7F9CDB280939FC5D953D525E0B41B7BE188D5C794687330CD770D24D9CD53B895A253004E1 -8A31BE4E82B384 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/Times-Italic-iso1252 /Times-Italic ISO1252Encoding psp_definefont -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -/NimbusMonL-ReguObli-iso1252 /NimbusMonL-ReguObli ISO1252Encoding psp_definefont -295 271 moveto -0 0 0 setrgbcolor -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 271 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -659 271 moveto -<6475> -show -738 271 moveto -<66696368696572> -show -897 271 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F534D4553485F492F534D4553485F47656E5F692E6378782C> -show -1898 271 moveto -0 0 0 setrgbcolor - -show -370 331 moveto -<434F52424120> -show -543 331 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572206C652067E96EE97261746575722E> -show -294 433 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<23696E636C7564652094534D4553485F4D6178456C656D656E74566F6C756D655F692E68787894> -show -294 476 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 520 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 564 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 608 moveto -<202020202020646F75626C65206C656E6774682C6D6178456C656D656E7473417265612C6D6178 -456C656D656E7473566F6C756D653B> -show -294 652 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 696 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 740 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 784 moveto -<2020202020202020656C73652069662028737472636D70286D794879702D> -show -1050 784 moveto -<3E4765744E616D652C> -show -1276 784 moveto -<944D6178456C656D656E74566F6C756D659429203D3D203029207B> -show -294 828 moveto -<20202020202020202020534D4553483A3A534D4553485F4D6178456C656D656E74566F6C756D65 -5F766172204D4556203D> -show -294 872 moveto -<202020202020202020202020202020202020534D4553483A3A534D4553485F4D6178456C656D65 -6E74566F6C756D653A3A5F6E6172726F7728206D7948797020293B> -show -294 916 moveto -<202020202020202020206D6178456C656D656E74566F6C756D65203D204D45562D> -show -1125 916 moveto -<3E4765744D6178456C656D656E74566F6C756D6528293B> -show -294 960 moveto -<20202020202020202020667072696E7466286465737446696C652C> -show -974 960 moveto -<9425665C6E942C6D6178456C656D656E74566F6C756D65> -show -1553 960 moveto -<293B> -show -294 1004 moveto -<20202020202020207D> -show -294 1048 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1092 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1136 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1180 moveto -<202020202020646F75626C65206C656E6774682C6D6178456C656D656E7473417265612C6D6178 -456C656D656E7473566F6C756D653B> -show -294 1224 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1268 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1312 moveto -<20202020202020202020202020202020202E202020202020202020202020202020202020202020 -20202020202020202020202020202E> -show -294 1356 moveto -<2020202020202020202020656C73652069662028737472636D7028614C696E65> -show -1100 1356 moveto -<2C> -show -1125 1356 moveto -<944D6178456C656D656E74566F6C756D659429203D3D203029207B> -show -294 1400 moveto -<2020202020202020534D4553483A3A534D4553485F4879706F7468657369735F766172206D7948 -7970203D> -show -294 1444 moveto -<2020202020202020202020202020202020746869732D> -show -848 1444 moveto -<3E4372656174654879706F74686573697328614C696E652C73747564794964293B> -show -294 1488 moveto -<2020202020202020534D4553483A3A534D4553485F4D6178456C656D656E74566F6C756D655F76 -6172204D4556203D> -show -294 1531 moveto -<202020202020202020202020202020202020534D4553483A3A534D4553485F4D6178456C656D65 -6E74566F6C756D653A3A5F6E6172726F7728206D7948797020293B> -show -294 1575 moveto -<2020202020202020667363616E66286C6F616446696C652C942573942C614C696E65293B> -show -294 1619 moveto -<20202020202020206D6178456C656D656E74566F6C756D65203D2061746F6628614C696E65293B> -show -294 1663 moveto -<20202020202020204D45562D> -show -596 1663 moveto -<3E5365744D6178456C656D656E74566F6C756D6528> -show -1125 1663 moveto -<6D6178456C656D656E74566F6C756D65> -show -1528 1663 moveto -<293B> -show -294 1707 moveto -<2020202020202020737472696E6720696F72537472696E67203D20> -show -974 1707 moveto -/NimbusMonL-ReguObli-iso1252 findfont 42 -42 matrix scale makefont setfont -<6F7262> -show -1049 1707 moveto -<2D> -show -1074 1707 moveto -<3E6F626A656374> -show -1250 1707 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<746F5F737472696E67284D4556293B> -show -294 1751 moveto -<2020202020202020737072696E7466286F626A65637449642C> -show -924 1751 moveto -<942564942C4D45562D> -show -1150 1751 moveto -<3E47657449642829293B> -show -294 1795 moveto -<2020202020202020534D455348436F7262614F626A5B737472696E6728> -show -1024 1795 moveto -<944879706F5F94> -show -1200 1795 moveto -<292B737472696E67286F626A6563744964295D203D20696F72537472696E673B> -show -294 1839 moveto -<2020202020202020> -show -495 1839 moveto -<7D> -show -295 1956 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 1956 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E20646573206669636869657273> -show -370 2015 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F4472697665724D45442F4472697665724D45445F575F534D44 -535F4D6573682E63787820> -show -1672 2015 moveto -0 0 0 setrgbcolor -<6574> -show -370 2070 moveto -1 0 0 setrgbcolor -<534D4553485F5352432F7372632F4472697665724D45442F4472697665724D45445F575F534D45 -534844535F4D6573682E63787820> -show -1764 2070 moveto -0 0 0 setrgbcolor -<706F7572206C27E96372697475726520E0> -show -370 2125 moveto -<74726176657273> -show -551 2125 moveto -<6C65> -show -627 2125 moveto -<647269766572> -show -789 2125 moveto -<4D4544> -show -937 2125 moveto -<64616E73> -show -1072 2125 moveto -<6C61> -show -1150 2125 moveto -<737472756374757265> -show -1370 2125 moveto -<6465> -show -1458 2125 moveto -<646F6E6EE965> -show -1642 2125 moveto -<534D4453> -show -1809 2125 moveto -<6574> -show -1885 2125 moveto -<534D4553484453> -show -370 2180 moveto -<726573706563746976656D656E742028636573207374727563747572657320736F6E7420696E74 -65726E6573206175206D6F64756C6520534D455348292E> -show -294 2282 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<202020202020636173652034> -show -596 2282 moveto -<20> -show -621 2282 moveto -<3A> -show -294 2326 moveto -<2020202020202020656C656D5F49645B375D2E707573685F6261636B28656C656D2D> -show -1150 2326 moveto -<3E47657449442829293B> -show -294 2370 moveto -<20202020202020206E6D61696C6C65735B375D2B2B3B> -show -294 2414 moveto -<2020202020202020627265616B3B> -show -294 2458 moveto -<2020202020207D> -show -295 2575 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 2575 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E2064752066696368696572> -show -370 2634 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F4472697665724D45442F4472697665724D45445F525F534D44 -535F4D6573682E637878> -show -1712 2634 moveto -0 0 0 setrgbcolor -<706F7572> -show -1838 2634 moveto -<6C61> -show -1909 2634 moveto -<6C656374757265> -show -2080 2634 moveto - -show -370 2689 moveto -<74726176657273206C6520647269766572204D454420737572206C612073747275637475726520 -646520646F6E6EE965206475206D61696C6C61676520534D44532E> -show -280 399 1 1451 rectfill -2125 399 1 1451 rectfill -280 399 1846 1 rectfill -280 1849 1846 1 rectfill -280 2248 1 220 rectfill -2125 2248 1 220 rectfill -280 2248 1846 1 rectfill -280 2467 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 5 5 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -/Times-Italic-iso1252 /Times-Italic ISO1252Encoding psp_definefont -294 254 moveto -0 0 0 setrgbcolor -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<202020202020> -show -445 254 moveto -<63617365204D45445F544554524134> -show -823 254 moveto -<20> -show -848 254 moveto -<3A207B> -show -294 298 moveto -<202020202020202069662028696E75656C6529207B> -show -294 342 moveto -<20202020202020202020666F7220286A3D303B6A> -show -798 342 moveto -<3C6E6D61696C6C65735B695D3B6A2B2B29207B> -show -294 386 moveto -<202020202020202020202020656C656D5F69643D2A286E756D656C652B6A293B> -show -294 430 moveto -<2020202020202020202020206F6B203D206D794D657368> -show -873 430 moveto -<2D> -show -898 430 moveto -<3E416464566F6C756D65576974684944282A28636F6E6E65637469766974652B6A2A287461696C -6C6529292C> -show -294 474 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -20202A28636F6E6E65637469766974652B6A2A287461696C6C65292B31292C> -show -294 518 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -20202A28636F6E6E65637469766974652B6A2A287461696C6C65292B32292C> -show -294 562 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -20202A28636F6E6E65637469766974652B6A2A287461696C6C65292B33292C> -show -294 606 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -2020656C656D5F6964293B> -show -294 650 moveto -<202020202020202020207D> -show -294 694 moveto -<2020202020202020> -show -495 694 moveto -<7D> -show -294 738 moveto -<2020202020202020656C7365207B> -show -294 782 moveto -<20202020202020202020666F7220286A3D303B6A> -show -798 782 moveto -<3C6E6D61696C6C65735B695D3B6A2B2B29207B> -show -294 826 moveto -<202020202020202020202020> -show -596 826 moveto -<636D70743B> -show -294 870 moveto -<202020202020202020202020> -show -596 870 moveto -<6F6B203D206D794D657368> -show -874 870 moveto -<2D> -show -899 870 moveto -<3E416464566F6C756D65576974684944282A28636F6E6E65637469766974652B6A2A287461696C -6C6529292C> -show -294 914 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -2020> -show -1327 914 moveto -<2A28636F6E6E65637469766974652B6A2A287461696C6C65292B31292C> -show -294 958 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -20202A28636F6E6E65637469766974652B6A2A287461696C6C65292B32292C> -show -294 1002 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -20202A28636F6E6E65637469766974652B6A2A287461696C6C65292B33292C> -show -294 1046 moveto -<202020202020202020202020202020202020202020202020202020202020202020202020202020 -2020636D7074293B> -show -294 1090 moveto -<20202020202020207D> -show -294 1134 moveto -<2020202020202020627265616B3B> -show -294 1178 moveto -<2020202020207D> -show -220 1289 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -507 1289 moveto -<6475> -show -583 1289 moveto -<66696368696572> -show -740 1289 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F5352432F7372632F4472697665724D45442F4472697665724D45445F525F534D45 -534844535F4D6573682E637878> -show -220 1344 moveto -0 0 0 setrgbcolor -<706F7572206C61206C65637475726520E02074726176657273206C6520647269766572204D4544 -20737572206C612073747275637475726520646520646F6E6EE965206475206D61696C6C61676520 -534D45534844532E> -show -294 1445 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<202020202020> -show -445 1445 moveto -<63617365204D45445F544554524134> -show -823 1445 moveto -<20> -show -848 1445 moveto -<3A207B> -show -294 1489 moveto -<202020202020202069662028696E75656C6529207B> -show -294 1533 moveto -<20202020202020202020666F7220286A3D303B6A> -show -798 1533 moveto -<3C6E6D61696C6C65735B695D3B6A2B2B29207B> -show -294 1577 moveto -<202020202020202020202020> -show -596 1577 moveto -<656C656D5F69643D2A286E756D656C652B6A293B> -show -294 1621 moveto -<2020202020202020202020206F6B203D206D79534D45534844534D657368> -show -1050 1621 moveto -<2D> -show -1075 1621 moveto -<3E416464566F6C756D6557697468494428> -show -294 1665 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C652D6E73757029292C> -show -294 1709 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C652D6E737570292B31292C> -show -294 1753 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C652D6E737570292B32292C> -show -294 1797 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C652D6E737570292B33292C> -show -294 1841 moveto -<2020202020202020202020202020202020202020202020202020202020202020> -show -1100 1841 moveto -<656C656D5F6964293B> -show -294 1885 moveto -<202020202020202020207D> -show -294 1929 moveto -<20202020202020207D> -show -294 1973 moveto -<2020202020202020656C7365207B> -show -294 2016 moveto -<20202020202020202020666F7220286A3D303B6A> -show -798 2016 moveto -<3C6E6D61696C6C65735B695D3B6A2B2B29207B> -show -294 2060 moveto -<202020202020202020202020636D70743B> -show -294 2104 moveto -<2020202020202020202020206F6B203D206D79534D45534844534D657368> -show -1050 2104 moveto -<2D> -show -1075 2104 moveto -<3E416464566F6C756D6557697468494428> -show -294 2148 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C6529292C> -show -294 2192 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C65292B31292C> -show -294 2236 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C65292B32292C> -show -294 2280 moveto -<20202020202020202020202020202020202020202020202020202020202020202A28636F6E6E65 -637469766974652B6A2A287461696C6C65292B33292C> -show -294 2324 moveto -<2020202020202020202020202020202020202020202020202020202020202020636D7074293B> -show -294 2368 moveto -<20202020202020207D> -show -294 2412 moveto -<2020202020202020627265616B3B> -show -294 2456 moveto -<2020202020207D> -show -295 2573 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 2573 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D6F64696669636174696F6E> -show -656 2573 moveto -<6475> -show -733 2573 moveto -<66696368696572> -show -889 2573 moveto -1 0 0 setrgbcolor -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F535243> -show -1161 2573 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1174 2573 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<737263> -show -1235 2573 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1248 2573 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<534D4553485F53574947> -show -1550 2573 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<2F> -show -1563 2573 moveto -/Times-Italic-iso1252 findfont 50 -50 matrix scale makefont setfont -<4D616B6566696C652E696E2C> -show -1826 2573 moveto -0 0 0 setrgbcolor -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<706F7572> -show -1943 2573 moveto -<6578706F72746572> -show -370 2633 moveto -<6C6573> -show -465 2633 moveto -<73637269707473> -show -635 2633 moveto -<707974686F6E> -show -813 2633 moveto -<6465> -show -900 2633 moveto -<7465737465> -show -1031 2633 moveto -<6475> -show -1119 2633 moveto -<6D61696C6C657572> -show -1326 2633 moveto -<74E974726168E9647269717565> -show -1589 2633 moveto -<20> -show -1601 2633 moveto -<3A> -show -1654 2633 moveto -<534D4553485F626F785F74657472612E70792C> -show -370 2689 moveto -<534D4553485F626F78325F74657472612E70792C20534D4553485F626F78335F74657472612E70 -792C20534D4553485F6D656368616E69635F74657472612E70792C> -show -370 2745 moveto -<534D4553485F6669786174696F6E5F74657472612E7079> -show -912 2745 moveto -<6574> -show -966 2745 moveto -<534D4553485F506172746974696F6E315F74657472612E70792E> -show -1581 2745 moveto -<534D4553485F6669786174696F6E5F686578612E7079> -show -370 2801 moveto -<657374> -show -468 2801 moveto -<756E> -show -560 2801 moveto -<6175747265> -show -703 2801 moveto -<736372697074> -show -856 2801 moveto -<707974686F6E> -show -1038 2801 moveto -<6465> -show -1127 2801 moveto -<7465737465> -show -1261 2801 moveto -<6475> -show -1354 2801 moveto -<6D61696C6C657572> -show -1563 2801 moveto -<6865786168E9647269717565> -show -1875 2801 moveto -<74616E646973> -show -2037 2801 moveto -<717565> -show -370 2858 moveto -<534D4553485F666C696768745F736B696E2E7079> -show -859 2858 moveto -<657374> -show -933 2858 moveto -<756E> -show -1001 2858 moveto -<6175747265> -show -1120 2858 moveto -<736372697074> -show -1249 2858 moveto -<707974686F6E> -show -1406 2858 moveto -<6465> -show -1472 2858 moveto -<7465737465> -show -1582 2858 moveto -<6475> -show -1650 2858 moveto -<6D61696C6C657572> -show -1835 2858 moveto -<73757266616369717565> -show -2061 2858 moveto -<656E> -show -370 2914 moveto -<747269616E676C65> -show -621 2914 moveto -<7574696C6973616E74> -show -880 2914 moveto -<4D45464953544F5F3244> -show -1279 2914 moveto -<61766563> -show -1469 2914 moveto -<6C276879706F7468E87365> -show -1792 2914 moveto -<6465> -show -1938 2914 moveto -<6D61696C6C616765> -show -370 2970 moveto -<534D4553485F4C656E67746846726F6D45646765732E> -show -1028 2970 moveto -<4427617574726573> -show -1275 2970 moveto -<6669636869657273> -show -1508 2970 moveto -<6465> -show -1637 2970 moveto -<7465737465> -show -1811 2970 moveto -<736F6E74> -show -1977 2970 moveto -<7072E9767573> -show -370 3026 moveto -<534D4553485F506172746974696F6E5B322C332C342C355D2E7079> -show -1000 3026 moveto -<6D616973> -show -1110 3026 moveto -<636575782D6369> -show -1273 3026 moveto -<6EE9636573736974656E74> -show -1509 3026 moveto -<756E> -show -1576 3026 moveto -<616A757374656D656E74> -show -1809 3026 moveto -<646573> -show -1891 3026 moveto -<706172616DE874726573> -show -370 3082 moveto -<6465206D61696C6C6167652E> -show -280 221 1 967 rectfill -2125 221 1 967 rectfill -280 221 1846 1 rectfill -280 1187 1846 1 rectfill -280 1411 1 1056 rectfill -2125 1411 1 1056 rectfill -280 1411 1846 1 rectfill -280 2466 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Page: 6 6 -%%PageBoundingBox: 18 18 577 824 -%%BeginSetup -% -%%EndSetup -%%BeginPageSetup -% -gsave -[0.24 0 0 -0.24 18 824] concat -gsave -%%EndPageSetup -%%BeginResource: font NimbusMonL-Regu -%!PS-AdobeFont-1.0: NimbusMonL-Regu 1.05 -%%CreationDate: Wed Dec 22 1999 -% Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development -% (URW)++,Copyright 1999 by (URW)++ Design & Development -% See the file PUBLIC (Aladdin Free Public License) for license conditions. -% As a special exception, permission is granted to include this font -% program in a Postscript or PDF file that consists of a document that -% contains text to be displayed or printed using this font, regardless -% of the conditions or license applying to the document itself. -12 dict begin -/FontInfo 10 dict dup begin -/version (1.05) readonly def -/Notice ((URW)++,Copyright 1999 by (URW)++ Design & Development. See the file PUBLIC (Aladdin Free Public License) for license conditions. As a special exception, permission is granted to include this font program in a Postscript or PDF file that consists of a document that contains text to be displayed or printed using this font, regardless of the conditions or license applying to the document itself.) readonly def -/Copyright (Copyright (URW)++,Copyright 1999 by (URW)++ Design & Development) readonly def -/FullName (Nimbus Mono L Regular) readonly def -/FamilyName (Nimbus Mono L) readonly def -/Weight (Regular) readonly def -/ItalicAngle 0.0 def -/isFixedPitch false def -/UnderlinePosition -100 def -/UnderlineThickness 50 def -end readonly def -/FontName /NimbusMonL-Regu def -/PaintType 0 def -/WMode 0 def -/FontBBox {-12 -237 650 811} readonly def -/FontType 1 def -/FontMatrix [0.001 0.0 0.0 0.001 0.0 0.0] readonly def -/Encoding StandardEncoding def -/UniqueID 5020945 def -currentdict end -currentfile eexec -E98D09D760A3C22CF119F9DC699A22C35B5B35ED6AA23593C76D54CABB5E942BF7D6DD84F1664B89 -699C74B472DE9F8E6DF925F6C4F204E9F1C639B4DBA988ED2AC419FF2B2BDE605B8EE3264EDD6641 -2D4F21C64AC522BDFC7C5502F9C3F3E5592B3B2093D33C9BFAEDD2D49E89AABAA832E23F062E91A2 -5032519D1868816E44B4E0747795003D7930299D6E1E2A5BFE0D595DC97E140989CE81D8D7F852FF -9CDC7A1B1B598C69131DEE005B415805A16D8A123E6A2261C63C769D2F4B60FA2C438AD7D199D8E4 -5F7E7C9A605C8CA14E21FCD81C9A515FB8DB6F99604534D06EA9D87FE0FAA852899C9D0595C7A97E -6C55F79FAC45CD38E87B10D210CE7501E88C8FCD3444354365FB893A12F596AE2C1E70D5819EE0D0 -87D10BF8DA96F3DABD5405D28C4228C6C31BA4052464859640933FEEFD8071C0C84CDD829A9B1D0B -A01F25A4D50EE2EA2B45160CA6333B2D2800306ED2BEFDFE155E9D9F9342EB8D5B0ADBF2460CCC98 -643FB1287CCD28ABA7B5CAB92EC39EE2E918990372B16F8487EBA30EAE88708B6CF33B6C015D8096 -C7CFE2F139F52052E3925C0D50FD64CE68236D59CB83EF56BFC584150EC38065059F3308AD6F9A99 -F83EF4E6CB13855C8175E31417D190D036B387D3952344A950F4D8C7781B307A094DF1ECAEE4D2C2 -FD747BC6F7F9C6BD0E90C19294F96C8C5CFE88FB34C477574A1B1630B8CC591529E59B20794DA32E -61DECDA8ABBD1AE956CF74012AA01D42EE01E861B0AA6897C864788AE59DEF43C493246FDB1ACA55 -4C12594BC7B33657A9ECC9E3D1472EF826073F632BE540C35FF6FB40566773F3BB2204D3A579A08C -CBC844C14B18C350F003B9DA23A570C362D6003893CA32F86F59B829C78EE3188B6E3F7FA81D7F62 -2825C639638DFB78B7AF1F500F5B450FA54DBFA5CBA277C794ECE93275A3DE0B452FDC8DDC2993BA -A42F28A636008CDCB03EBF71BDCAF35019778993443F88412AD2AD0D7155A3944606463266322DBC -0244B07DA1E9C27A27B59664E8566D7A54CC03E995AAD008B0A17E2C3EF61F720CE7F7788599C4E4 -4C709CD5C31B11107F16AD70B17B9AFE2E8CD922A7428DAC171427FFAF51067307FAB0ADB530E701 -FD22DA22C4CD3064067BD4F6089C4B2C87937DD426E4E9D2F60E608288BAC9056554D04947E69200 -61E379CF5E81BFD32FD37EFAC1F61CEBEE551B0851516471A7472C60DF89DAA9EB1DC5A67E479745 -3E69B9E22BAF4E3CCA4192D603295B018C4AB69D18DE52DFDF15E96B557F290A4B8C5B1E7A6CACA8 -1F2351B97ADFC36995ABA43803A6E5AC04A3C93495F6D38106B8B144449C07D1358210F9176E1565 -72363CFBDE576BFDF99FA329DD1346E83F79E06CF68250CA57A68931BC7F342AD295D0CBA17AA95B -B8EEB53EA6E8E660B814E9F857CECB14F44A43288B69A9E7908D55BF19E844359879D28CAEF1C38A -36420185D20DFB32C2E002202800E8EF3D67C5D50E919657CA958B538D537D503444865331D79BFC -40312068D72364503BD0CC84B5F30A74D8B5B6A26AF2DB764564FB65A6BA8F9051AE2B4EA458D46A -4569F30C6E77DC097356770362E6CF3F1661074778EBB44FF7D1E3B64FF75E77E11FE525BB121C65 -46CFD13300CA1F02D571B82A5825E6226D14FDCF27F06D87452A8B6C5DCA658535CEE2A795E58137 -D48E566B69D53A0C3B766E84C51EAA221C46999CC8065ADB2F129D5B630FAB1814C0C33B5AEA0EFB -B6E994D80941B53079AF96D90A0B924F9B0E319BED9836B8F9053F868363D3CA554CBB181863301F -8CB940872ED5FA7BD18CE39218B5AD8AC57D0F752D941076B1C64D99BE0DB86D7A6D96510D772EB2 -4C587F11779BD21CFE5BDE1F29C1EF9022B2B8BCD7F91153C845906722477829C40111D810480F3C -F62DE8DBA7FD86CD236E656618CAF6FC46827FBC4898EA7672F8C9971AFE43E0E01EC8B77D4AF48C -BF1210E98C1DB15C16D149BFF58AB0270CF015B107A3A50F5DC8F37FFB92EEC8CB6778DDB7CE4AAB -C464C4AFF654223006A550EB52485A23D2B4AA7198D3CD54418102F1E9A4FBDE37B841E56F5C2C53 -966DB9B66B000E4588282E3FB80C2C519339F0002D2F83C979EDC5827A3B3C8EF8810A0F9DACB6B9 -998E9AF6551F56313DC4011904CB979AA2D32B11A811BC248141E4B9734D9FB7982A5671002D8279 -CAB93ABE057474628DEFC95D43890DB1ED34CFA8A20BDC3D874E7679A396158E522ED0AB969A4E3E -C7E4474E192590504D54DEB7B260B7935C4E56548A7D121AC1F741F8CDF259EA1B5813175A77A1D2 -D30BA26F65EB765A04C09ED51F69F41551ADF399E6AA2FC09788137BEA4913F17B8EB838C38FB272 -1FDCB55FD65697FF0B850E7D3D1CE266BF90F7EC06A9A0876BDFE767D3A918B092FC78C775F945CF -1F96E859C03DBF630D9A940939654C3549D8F7921CB94EE23D5A0535DE9DF31EA0F937F860B4F220 -A99ADDFC343D7CF7BFA0B803C12C26403F0DCFFC8EA786D0D8A8D9C367419CA8AE41190CE93A8086 -583A1E6C9D70B612C84D87D2EEAA71EC2DC12F4CDE6A821303D5F6A9BBDB7EEDCD289E80FA3B75F4 -7F481B50719DCF4A142069393593B9AF9CCEEAEC56A35B8787193D7C88113E9E1E221D151E093B01 -9EF89F6118BEC4735103CC8003CC5AD1B6727B3226CD44C497DA7052DD681695DBEC3397F9598C91 -77701C73BF0594CE93F23D50EC5BEE2FB9DA1FC966DF148B27B28EE3C89526DD6625E2887F9FA076 -7C127C609EE315626BC14D274FBEA56528DC06A27B2D476D46E9E7916590B156A5DF04A6CB15E362 -45D77021767B6E5BDFCC679670263FD891446C3371B11BB6E1DF60F960AAB4149D7753E6A5C33810 -C42C8BFF4E935003388506F8278BD7CB672F132E065AE684DCA0B9064D01DD620E7FFDFE04F14277 -EFE8E60159BA0FCA3FE2F28B902D4AC275D19F0AC6971EBE827C4A232D87650D2688345BCA78F879 -077114F0463C5F058107B669566F8171E4E284D278405580F04BFFC9902784216E0C9A17AA9B2935 -E66E18A783F723BE044389B7E9D62AA36818FF2EA406C3C1A9D2F3436F3EE7DB8BE86AFA8DAA6A4B -1B84611350D8D27605509612B515E16AA843164D5D0805E36A2B9EF74C5F6A0B9D59A04B55697123 -27F4B1B30E9587CD103337639967CBDC655AA46E80D2CFD24BEB50815B5338E522B3A7AFE8362AB4 -F05D8BC52BBA9C5089ADA8C89529B0275AF422EB540D31A938B8740860756325B966B36817115213 -FAAF92DE63F6BAE1E0064BFBC5588098B61EB83C71F1C2082436D37DAF1ACBE186FEDC4BE7C1233B -6F18BEC5F99002D21CB7864E4811F7AB3C03003E1E4490AD1AC793BD28FCD5EF0E6CC30EF39A08C5 -2F71939B0CEF620DC69E31E39D6DB969049031B0C92EF2DB653D97F370141456A52985076B268652 -FA2648C792780BAD637C4D7581FB2D62011D57E293719487CF2D1F013CFAA532E1C2D39178D51272 -A6AF041440BCA174B5CC902BD7390C7D3695056CB4BD7791F9FB6D88E7A70DEF2C97869F5DBC5BD8 -23C517C7B7C39D624DF627DC9653EA5347BFDA80B723F05F6DBB4C9EA501D862ACE05B9DBDF21B70 -56FBCD8C6D4B85873DCEE6166C8B5ADC0316CA12D9639F361B15A42F00E1D62EDBCA1111972FA0F4 -5758BECB31DB38316F3CDFE1B41748C93ED58B67E9B57ABBED5924A6D53E99FBC9A994A6489A8BDF -13EB685548B4DC6D62DA7426C22227D4D43B6FFC7B5EA91C896730253E8941AFEE588359C2BECF6F -FC415B9EB6D31CCB0F6C7F85853E6449FA6D627A97A3CE8303F148393ADCCCDFA2FE085C6908BE5C -3C05AF00A6F02840206C3253A559AC5C049BDDFD11AD9B118403B84DA10AE3C470CB9A9A2D1D7B73 -2F59F5FE146DEDA60AE750F551AAC934621B4470E1BC324C436303E25F81D0DC3188BE0D6FEC5414 -C20E4CB18952E12CB6423DF7124627ACDE145500D77A97A8BFD9CB50D1FAA008E2CE2B2505A4749F -1EBBB092C347023714055A9B63353AF9E7FEE05BB54C9843698101F79888A91531773830C2C967B5 -88D3ACD2192883D5CE3962D51084FC653EAE2C5FB2DA41DACEFB5C76812D2EDB5B109677289CD199 -8D457FB1023A19AC67295BBC1A9A20A426B06A368DF3C5DD083CB1180D287F5500F2C635EDE157EE -FCEEC5503447382D15C748C1E35F68753992E5C90F900DE54D18F8E1B355D1076ADFB1F3590135FA -D1A36F028E44F48ABB149B80CA9A54614D467F8D71CB310BBC7AC7100261092DB8C5BFD39E0AC6BC -2C9D6CBC3A8C05FF8A74CB21608EC4A4CFE4CBAA2D056DBA14206106044DECF59F957EF8A9CADE4C -9B19D8D30DD4FDE6A9548E50DB51ACA73330142153FC36B69C1C8D5B26D0C689B7040E81AC2C864F -D7C097C99BE5953843E172C97AB5684F35FB03A725A89DBF371F08DDF40A1531FC1B676DB0E1543A -EC6E97D3D2E4AA3D5831D8B3C952ABBFA112352814FB6FAB61A0D680E6640F6AEC8426200CF61286 -F7422CB2F78C61EBAA36D47EC16D7FAF8B4AF31D090CDFA255D9D7C61D46CFB22A7D6E1758E71ED5 -67E00CBD8E8F468DDFB477F091A2F915627F22FF47B876544BC1F03B6BBB98385F009C20BB1AA2A7 -A78674692B8EAC2E3C8069B79E679338DA57F72976810F845BEB6B9ADD32B95D78E5E60F16DD1668 -9C05FD82D36A3115BE8ED494A74DD211D58A2CDF983FCB9CDC29BF7F0E29988FA23560EDF514BC1D -183F3B2A22C09FB179B47E05ADEF48DF02F31C29875D1915037B19407764A4292FE44E741651A8E3 -BEB5F0D972B6327090F664417C84F84FFBF0AFFF8B1D85C822D90730AB4140C42A51AA8B1DBE4398 -4EA8566040EB8B341CCE23FD3F69DD235A080BA5C69AECB9BC732BC2D7D40617DDA6B79FB6EE40C3 -556C7DF9B23DAD89E94054B1345DB8402AE679FC4655A4A776C0150463F8DB2BFC0608EA1F124E22 -1DDAE6026B5E5D007A7E4A0D6B3B0CF3A2669E67C5E4F01551966A7BC48F2F4B6A87E740D8095E63 -F77C7A027F26B52F2299DE5B8A2F6209BCF3D31CB0235F998F781E5CC81E31DC424E008D46EC0920 -2951E5684804A0592EA47D6C788A20487BEA2EC8F2E6C1D7F378B62DB43CA43C4B366F8B4319631C -FE9854F0E10321CFA3B01C873584863BBEFC23C72C05E695B56E8A52E89AA2DAB543834D34DCAC5F -ED08DC51825C5257AE59850D101D84F4CAA1D29FC932F9E0EFFBF7A9A7F3685F61F0490CD3CC8988 -2DB52A757A6AF4C4E67B407BD2316B1C0FFE7DC54E43C87B874F57E4903334E2140B011484863CDC -ACA331175F2CF3D72E0042855983AAF8853D3015E870FF0807014C31D55060DF3FE1FCE157324481 -2744AB51322444632F9AFDA6706E320FFE82B8CBE242A19DF00CE73EE48E25FF49D5871BD3E60652 -298FE3E8D400609E232E0DDC794C0579ACEF89E841B2EDCA50D51151F65E8C1CC3B01EF1870558F0 -BF5743718C3E068617E81BFE120C6CA16E0924BFC2541177D53671CAA3AB641C41557DCDAE1A3461 -47B5E999C4541B08B4AFCBC187AFD653D5B5F8386DF6AD8FE69E21BD0567DF494F736C6A184FA4DE -48DC9F347787CA96E2E00A296C2DA05C2AD9BC423E9CA428D7F1FA12DC9353A302FB8C529AF8688C -BB543B45B2717EBF8F6C497935F4F3BFFD285E0402AB7544B3CA4643AE5A8B5250ED987A95FC1F27 -5B9707ACD0641BD0EE2AE9758494F8D8A51DCE408A38AC20EAF0852D72D84D0C6BE973326793AEB9 -55EAC6FE0A2813A355DCD22F6F2CE56588D1C055CDDFA98878BCEB6A018DB22922D2B600A20F8184 -2E665DF41013CA0947C4237C2BD60A75E2FD1A3FB8C8FA19485730B87461AD466ACB02DF8CA24091 -4FB090B3D2B41EB6B8FF05E1A59D9FD668AF70BA5BB72778953BA55FC5F9F626043450E1D09BC83D -8605098ABEF884639A37809A32565CBEFB3FF39EE53D6C18C58C272BB928E4410E361E59A50F242D -69747A032617C52DEBBF62364AB5A96EFAF642D9D82BA679B1D70FAC10A4EB62FA5CFC308E86368A -AAD7E75948F43598CD1C544A0D4091374D7E88D4522CBE902391641327E888E7748FA889DCE67ADE -61699E7D77763681CAEE9B1CA8837B2F7EF9C18CBCC538C465C8E2DD34616953CCB6030A222C728B -834911C1A179E2C770289407AB28B303E724D97F747D6134B425216A64C6E0B60F633E2B85300047 -E4C90339CE030A0FAE31E830C8ABA5AB3386A3B69267351A7BFDD66356AE5E57FB2994452993E90D -E7C4E260ABAB93C37831856A650D56E44172FECA01D6C7C380F250B82473960D2A2A5FB6B4DA668F -46E624ACF7FA0FD4490F485D640A3ADFC9F8652E7A38CE5799F770C3606DB4B8B947F93967F779E3 -A3C0572F13A5A187D31D7BD12A5C7BE23CB6ED6192086241B76C5BA6983DB9C93E4B208D707D3760 -F03CD6272EF3A4CE89B8E52E6AC5871A3D03EB975759AB4BE239E5EC7842CBB333E692CC607C722E -185D3C39164DD320C6945629C70FF66A5237C0A9520A1FAD6EB9816069351AB0F135D90CC0982B14 -7D2294AE4A38A527EE40BE9CDE2512AAEBB590E134388BB171D0956A7C4566D65A9A041BE6C4F883 -6B3EC3D2ED1B48B566A783292B15B6127920D247D494F070BB20BEFF60640B11B276DDEEE49706E8 -B2B21BB40B7F00AAFC594C492C25DCA774E0B80D82E927448DE2E74A9D0DC7AC9260096EAF187B6C -D6AEAA6D1DC4205B4411122751A5B22688404EA7C5861730371FFAC10F5AFD4727A0E402AB5EA757 -606B75EB86A05E8F774D6E430A1A3FE2A37EBB06700474239FB1CFA05EE44B91B82244C575B52E7F -AF934B04EEB0D933FEB57EBE326D75821C8B23EAA85B583AED4320B7F04B9F2DC591091216FDE52E -064BAAA9C2C9D9714B95A4558C21F3CEBE624B5403B31508F178581AF6863083ED762F1E2E34A45C -FDD71660D626FF8648F5D6C5E580D4765A67FB6159EC8077A9F0A88038C8D3D7C77FF0926E2123BE -874F7BCAF129D55A5B5960F824BD1728ABCFCC51D23936DE9A25C408D786E44C3A2BAFA4423177AD -060D21D38E15E23EB6FFC0B4120E814695D423EEFC2744A1FC81B4DF89D76F0A6803D8B14E75538C -AAD03A72517B86514F6952F6FD619D9E910D980F00964DB325318C045BDF79647F453D4A5CF4E61D -D5359782827229310405FBCF6107C3AD9DDEF9A9A339D5D5A6EB2E7838A0A43221BD62CBDF732DB0 -A638A52016FB35BA7761AEC846A023D3BF2D1BB183543E81EB7CAC1E5970CDC6F068C5EA118C7AAE -528D1396E6DC939112DA4460C890EAD5C01BDC438F5BB734218BA6270ADD0DC1778FD8AB16831D6A -302B814A1A44B07EDC65956C9E6CF4875DF521F3CE5B422F71081B6D69BD270F739095C9E81C0377 -934A8BC6390C420C4E4CDD9CF7E32544C68D884E15ACA3BCC07FC8C132D8FB9D752C15D75C52C288 -57E2EA461A6FCAD90C56843513F74461F18D7164BC597A28AE4BA7C86EE1703535A9B9ED50122627 -71FC12F102E800E0E1AF7BB46681BD2B14B614CEA91B7B2AAA35235DE76C0E113C92688F8EC81277 -D58C3406778E1EC1CC15F1CD9A137C8FFDAAB99ACE3BFC782916F1A877170589A92DC921E6740A22 -B84DC6BACDABCC76E64C79E3A588D80F8F4D376E1B426F15751CF7391102102F0AFAFD8B22DFDEB5 -48AEB5F30B1673023D22054A13391A0EC08DE6E7B685A0D031AABF20B7C62187C0284892D5EAADF1 -21BA28263EB863D5E36EA9C06A77CCFC0E17F593961591F84D82AF823EFE41044C8D606FEF83CCC7 -B0E961E7994DF8A3CC36B209D953E250ADAB8D22D7F2B4E2C9CA39EFA2D93E56195C1560E30A5190 -CC5B17FAEFCF250DF79F6B624A4B917E11C332222FCCFEC4F6A47BD9E75DA9854FC3F7AE554E91ED -DE144D7AEF38A0E3EDB5E5A5626374DB94F022C8CF549093041DE00D7269B7CE544E748439BA2870 -718C08E58FB4A77D93EBC04B7957D272AE1601D41BF85A2BADAA0DF73B0D3841D4839C85677FB2E1 -5F1D6CE592669FF4BBC9C69DBA334DC37706F2F6BE83D5863E8CD6A30C08640AAC4C233684E66B4F -E6B62D4A8BE9D531E47BEF5640D9B5C27D990092BE1597F6995C8A77BE9C18AAE6C1CF130775DDAC -41D34438FC7AD8E042CB56CBF2944932EBA7D053E9376FF398367450E35A1945FE23E05C921096A1 -5454721FFD0F429A3E06DC3ED36F1C170BE79C66996EF8337AFF85B90C5D3A4A94455AE9FA32E211 -7A63E59001F052D5F6223125BFAFA40901E98960ADF7BB886729DCA82FC3B8CC52B37FF2517299E1 -D769057F8154FB95582F02CB0BECC873A9C71796ADBD3E91324FAA94F2C41CF57C30B5897D031C02 -D256C909E080E70BFD1F32E69EF67031138C2DDCD1A8E4B65E485C23C3E450ABDD9815512D6F34A8 -4B9DB715DB2C7A93BFB424316E1AA44397749CB01088428F149A3B4324737ED9957FD388248462AC -1B2610D72BF5C073ECA567E7385CC959E37CAC7E05470160FFA5A9F63B8E9B082937E911586EA165 -374938F492EDF28CE6020953A5B5CCEC7737F9D9CC8538C4339567AAED3794ABA3B9F4EAE65466E8 -E326F6C399B36355935FBDCB9972F10B13494DC25097FCEC5A6398F275C8C151558E74C5175F7BAF -4155E36B733F75CF9D5C5979B0764F14D8306E06BA24BF791141E404C69F3F8FCCD91B9C58C2C671 -AAE7D4F9E5D6414E46ED633A5F78AA5BF04E652246A066EAD9E582B181CC196EA2D3CFAA383B5D0E -4CAC9336E119C08CC6AC55CBFBAE147C623B400453BBF447E96DE036FC025624384359EED7C7D5F7 -858DC0521377CF647A157FC3F188DE5EEF094DBA125510FDE34C570D7BE76AB5DF0A28BF45DDAADB -EA7EEEDB936332DFE93081E0AFD3FDD46BED08D6914B2EFCFDC41662A33B90B03D76D34F48D30FC6 -BBBB600E90E6AC7243FDF026762A44B4D6E4ECBEF48C9D7B696AF29EEE063E557D8FCF0F09E0136F -45D17E608DA36E59F2AECF8493F8D62536119B5F7E1554DFE3F6E8D7C9A2C6F557D18B4AF92C9F6E -050975C3B5C54F9B5F4E39D600B6FA2CD6DE203A174028CBB2A201AF126D1013C229BB82CFD013ED -199D01E51EE2780FE896E01C63C655087A3E61A7F1029FA5E97EA1872F1B45F22282DDC317E17926 -7368CB52DA9444F6055A3C653659CAD2A1D8712BC2B1B32C1DC6906D957FB88524EE066156ED6BDE -B8D832F9338F9912E29A250A8C4674E667C1C278B677AEC9972BE83CBA3FB779893FCB8F81A323AC -91474BA2A2334A07BB5628E905C518E634F6761A3289056F83D5DD7B3890987EEE1C18FB2D379CC1 -905F1AEB3B3D2AD578F0D6C845D2D40C4BCEE3F71C90E68E5417BB8CDDD878D83BA80AD8485F4067 -E5C3CABF28AB56CBB219C0AAB8FFC6C7E192BEC8CBCA1459AE4450AFCC81B9548F40CE2622E5A7C2 -81F74DCC02DAD57EFD92D072318DDF05BF42F1EA8163071E23949B0179CF7DE64677CA99B23CB926 -B3E294194EC13397EA1DC9A5E1CDCD828156CD71F81B64167D4FB01E6002713BD8AC6F82B20CD369 -9C6CA4704DC5C65A2D66EB155B7AF1C9BB46469416FB49C1C7E17A30A5F045271D7DF3FFF2F42C6B -470701C381E3456A500C6BB3D0E47B4D91C5F34B49BB6272F1F8698B307D89EDA3A1565DAD1C0864 -627560CF922DCF5B34C67860352390B282F95394AA2CDE0E97CE3ED39546A6AF1C52BFCF81A29BE8 -2C47C99E8050E4889E4575B75F39E662F2DB7420673797E2ED3D67CDA7AE2C15D0A0A794D57D168E -BE13214E89E0209AB2C0EB7784E9491AEFA3C02D0DF3AE5365A0FC4AE023CAB528162C7A1B173664 -9DFADDACA8DA5FA18B7D6489E4229E9E24D38A620464A744A5C60F6F9D334B908706B738AED18669 -8A8B278341FA4D65A0A88680BA484694921512F7DE93337FC1C02BBE6E64AF2DAD07603279D87329 -1D1F4D39C1DD6D89C90F65240F4808F6F1115CA55B88E242565E59F3BBF1F10EC7B88872E9AE61D4 -4CAE185463EDFAF7DF63DE4D2207D307AFB61501892965170D2945846FCF5973A1D458607F50C15E -06E5BEC715E0C156259AAA6C735593E5564F65F443B78CC7512EC35A56F126DF9D30974A40872E42 -65E1AE5FD483CFCBBBA26DEE426CDC4721F19C3FDA86ED7AD4FA1120F63669BEFE7002B128CEAFD8 -C63E8AC09943B6CBDFB3D2476A026C00A8FF81B1F651B97F310C82ABA5F388CC1DB5AFCFF5996D52 -52A6A42FA4D972E41EE56088F78CB966F9051171C472C774879AECFFF08BFD9CEA40D7C298922ACE -64F28C14E0B81F4DCADE81D71DE3983D87D905192EF13CEE71B2D3FF1A88AEC671EC318917DF98A3 -C9054E372D22A3CEC82FCC217F47319A40900312F6E32B536B9E7A7FA0837EC65CCDB5FB0D414371 -17596CB39D9382262DE6E65379D3A9709B2CFBABF5FC5D5B352425F06F88CD31012A2A4147B112F0 -C1C0ACCC808CD625E0228EEF66661F70AF96D3DCFECD402700E4F6522AC9A856DA466D55C84F65BE -2810A1565163872D62EB81333A698ED7B68352CACCA2D7AD38AB55C19E4F5582F75818302F5FDADF -1DCED09D94872F2D48FB636C8E38C7563C72C771A08C6B1F041F3532BDB39006C89A33C09BE1E3E6 -03622D891F98010BF1DE5355F557A1E09448D486ADEF565705277B31B8BF2B86761E32631E3435B6 -88B79D566F1747BA456DDB43CD239FB47FF7B425EAA4C657C8EEC26EE01AED07CF916E77D53634C1 -37AEEA009C6B515B6342C54BE2C7B95955B1A9DA277A0ABCDA2346E88018C726F481F71D6011AA42 -F8852F2E5749518FE3B3AB668213FE1A05C10A1C53953D75312631D6BBBA01D418199DFEFF8CF548 -6109B099FE8E2F606165FE30F532C03567785D5362AA873C9D3EECEB20F1945D55F49B0CCAC84967 -59FCC7292E46938943C262D78F3212D3F9D0F7B103157F423D71B1ED54B2A603F4C269029918F238 -EC6828FFCEC66009DB9C9E59534EABB183F31D7AD4C57B1BDF0BD2CE5A421882BC10CC1BCE6A970E -2B586BB221567CCA483989DD0B8DEC424C1D1FF042DCB7834423CF244EDA28D2D969B17440CAEAF0 -24A6119DB010CE366821AFA424D1B8299609C04148275AE6E5257A7ACB3C766C747CE99CBA2D703C -F19B7CF301B634D8B613DDC4AFE4633A4D77BFF8E00CFB5E289EBBCAC90A24307E7941EC1685CBAE -400CADD876FCEF7F6557EEE167D2035A05120293527700DC510B038A496BE1D5CBAEF24ED39F7421 -1A93AADF22214ED606A80582485AFE358E3A46D0671148998A3B3BE209467009B43400870359D418 -9A8CEB4D5866AB52D16D9CEB1EAB71C07E6CAA34B70E3096BF7604C22C40D5FBFEEA616DA3BABD59 -DCDB97D883FC8742B8267A16A99B7953225F7144568D566E64542C92E538AC140C851E5D295528EB -7CBB49909B1CAF6409C9BCCEB325468FA0B5F7CB2987382616B477CCFE4F4AC79E4A6F7165363543 -F04DE5B6F6E1C2E910CDC3CDD6C4C92737198F892337DCB6647BD226C820AC99C65D8E7772BBB74F -E65DCAA8A22C33BC168BF48E40A82700A3A7668C5A9A71E397ACDFEE7D556C5C19467B7AA69C260B -727407AC837BDB7D67DEC055C1F45D8BAC61048C45BC9FB3CEFE7549EAA2992D2EDC126FF7A05EAE -58613332A2BC1465B2BC0429162B907D65F793D236EDDD8D35405866D71B25F62DC4A7E06D4DEE82 -840ACCAABC0774F8A63E9C0F7FC980B3583E7A8B01C46590E3BC04EBA565C2EA94F057D964A78A90 -EA9F52ABFD70F84E44E434BD10A42E98C794065724341F907E35D3CB257161E01C7084E3A0166D15 -CED65DA7BA87DBB2EA33D39BD99AFB93D3548358D08330E807F8552CECF63C84F805205491BA3A1A -622E70C232FADF3BF2DCFD6F0539158D3306506F150B0518371912A25EB96163D73E9EEED42EDC84 -D688BC7F7708D9DCA348FAB4DF62E5809BD094842D0A31DBB7C4B41F94D946810C5EC10B69AABC2C -91A59500B2E5D37F4755DDFB7AE4ABF757F4C5BCF77C7F95E6A616646456FE8F18407080BCABBFA5 -7704287AD26222DF91AB2613951E2D679472F8ADF06EA2A20205EC19972299A78BAC52114334470C -5F5890C2F846B4C6042D73945127F2E3910ECA1C4CD7A16EFE4B4BE38A15AAA710682C3836A8CA83 -FD384970139D8B46FB0AEBB002DD224199672FFA02250FBCFA4E649E335428FC71F50F45E498419E -DB0E970F46894A48F65580881C9C4250FCEF65C9B28699408E18B26FE6DB7F1CBDB767564E73CB59 -54C6D639CE33220C894F36E70F71C9F9AA3FE2AE0AA0E3F2E304EC5ABC661675CDE2E70519E4220A -E26FBACBD01D5169EB844750753E6CED53E3678FDCD08AB93E10067E9C64F38B40B76D99B6CD92BD -F4155A1EA5CC824998B59AAD06E09E5F15EBB2288D66EA71B296616734FEF2796F07FF0D8B047074 -A1111D68B99C2B70FC56E74A51B062F4998ACC85B1943C9477E436E5CD7AB18DBC898D21BB93475A -623BDDA71D7B895BA2D4C10F4B90BF335126F4FD57D73AFA50170F6B3C364922E551D40E35DA75FA -891762FA23401D39260F2E92C7807C746F13BB35CEF9DBF2E76E66A72FEFF095DA482A4DE8A42091 -7065736CF4DE904FB52E649A32255E2030A7B31B686353492F31C064A3C4B0448C4BFD44B8E15384 -FD809B8761EE26A7DFA1758D57CE4F0BC376EB2B3833534B15A83436BA553955ACB5A7A66796AC5B -92DB5388BC53EFA27508B08E82821E5CF669BCE52BB860780F749B4F38ACDF5FF12726BF3EC2743F -01014CDE96FE6B4C40A034E9EAFCA2A35CCC776C2669E6AD138070A40F48ED79136D7FF57E993E09 -B81C543FBADD350FF5B5F7A46F060F88E30FE2D8233832D18B6C323EE017EBC1DF5C838321CDC8A8 -4CABCAB20B60A1A3AA028F36EA6E87C850AF8AF7CD50AA6359038BFA8818821D02CEE8F51DAB8C05 -F7AE9797814D97F3DB8CCDDE45B21DBB15CEE292FAA534A5F317B357F4091F3DA357325B8B9F5EDB -45865415973C143E5E5BAA483FBF2D06CDD4246675EC58B84C6AE65CA743117FF00F229243772561 -31A7F2BA26A9115AFD96C18216CFDF41B7220ED0CB3FCC26C36380007B382A02AEAE428887DC8BE5 -FDD630AC57EE3DC156C7B8B29E687F24442E35CE10BA4087295A641F7139C831F7CCDA6CCEB5DAFE -537CC1A97C5A337D3C48A6AE947F58A30DC08CC7B58DBBB4737AD52783C573FC1E9408F55495A80E -7FDA61F0B9C4F090158F1A416249EBBA936C27BEFDEF19D1BFB839EB70576A010706D8B95657B218 -9C2AE04C11EF9E57FE09880273761FB4302C388BD608FA0C7F00F033C9C00F4E3D5CE2D903E0DA52 -E69C7745EE9FA75E2AD93DC6CB5CCFCD3782A699B807AFC36AD1F62B05856D5DFD6F88831B90EB3D -CD523582A49732E3FD7253126D39E8AFB8458B5F7AD7F94A8DAC13365F433C857AF4A42C0A08C4DB -9887C4957259ED22D13CFDF5995DA957EA5A0F620B0214FBFE08AB6D552DBF048D62CEF6EFF12F15 -3511ECA7833E0E3E95F85E6AC0F95438AC4C126E1F1ECF336ED31CCA7EB216D279877123FD9FCD8F -B5E52B587CFFC4428456DDCA816819A8A4A211D8F1629E5D42BA4C5C356E580C8A22C61D987552FA -A97893816DA73D423686E4EBD44375C257F031318865A20F22115E72BF1EB9F93AAA169C140A33A0 -6C35BD4526A38BE79CF40AD1EFA10411E8F3300A8A8B97AB140EE6734E1BEE6C8EE443D698D34159 -97649C6F10F20ACD80236422E215E146D744A262DA3FC88DC0D86FF66512F49D3F957D3C5CFFEB42 -4823509F33F155057A4C6F37B52F4667767BA94F6B8B62856B553F307E5D230C44CBFDC9A97A45B1 -39FFB2F2565EB0E22026972FAD0FB7B9576FB6F368B61979943A398773600E7EE1DFEFBF26D45D40 -BDA66EBB96A56EE9CAE0B2420C5DD83E24DBA9FF885BB844BF3D2BF93B07325DFF60C0CB5FDCCA0A -C8FB5A2E119D5AF26E53AB8E3B428481C2871DDA26EF0B621CD8572B3C664BC7AAC01A1D05B98F79 -1A7080D294BE81099BDA7982432F3DFF4775C44D23F4F1B2E0162B61A8B2CB5EE8564BF98E2ED403 -2219085FE6194C19DAC98A421826CAED7F1AB1477AB327506010217283894235D7DBFC1153D5ECC4 -8AA7293F19592B4D7E95FE55151889BCD1D7FA7DC2370D2DFE11D7E4EA34B5C7A8E73BD3A348FD38 -9EF45B6167FB90BA44C23E912F9A4F2FC0427ED070592F7110183BFDB2C400393BA7569058227926 -351F07FED4F33633BA03A72AA2DC6B598E49B96021DD868DAD0F352E5722FB714F667C15C68D49C0 -3D822D82677EDFE86FE9668E537DA284068C9B0AED83074C92A5B939296D505B837E6A9DDAB1AEAB -7455A08A114C2222B339284674B74BF4CA9EE0C020BF2A148B439C71C6BE51A94CB64FBE4A7EB295 -5A455047CF5CB348B062ED4F6471CBC3E9ADD9BE9B96879AC7BC71BCE02FD02F17C6063985A5E898 -3D205AA1489DA13C408990ABA1C54F2F501AA172F530480D789C848118C0A74EF98D5F607A067BAF -F6030D887AC6A6497F9A0B38F9705F328AAD4BFBB634F739386177B07F22D5771282444E5EE17335 -B4D0EC86117C697E79A5F4F65FDC08E4904DAEDAB20067EAE2448FD4301849E456D085F392DD1316 -7ADF75CCFDB723E2904A9C0C976D6B84DDEF9D92B0E15FB246C3ECC2D0BF314CFB957757B3A3E8E5 -801F520644E4601D291DA0F7507C06F3B9BB36FC1C70EAA444E14E56C0CFF06C7F853DF36DA9D8B6 -AF2544B853DFFF535A7E5C6FC145250CDDA229956019659D0D253A19A7B51A4E538BDC01F74D7704 -9949C2C97C7EC6392C2E61CCC0992B66DAF1AB08551063E53180D2A67DE496716CCBAA45462D9F91 -B66A22545962DDAB120511FF08627131B95E5DEEB8B4DD9643E7B2AF65C0FDCE11F5F1E8DD468DA1 -8D41C8C4F00EA73836F4F70EC50FC3EC6D358C0658A4261C6D15A582A2C7C994E7882E661855B352 -014576858A265FFBC425160669CE159D07EDAC04D060B44E5800A7AAE8E339C29B929AA81D2F515C -46229D2080D5917AB20AB6B34FDCA8E4AF64ED660A3173786FB1A1D005D575C2A5187D3F7CFDC94C -CC44A38C5CD523E9DA726D8EFA6DA7B6131DFF3435FEE838B2C7D6B97934295F06202D307FF78D90 -6699CB9C5BBB10D1D4DEA5FDA5BFB094E704607083B646D37F5DA1FC7AD21B813F44D8C1AFEAB666 -55AAA19703BEA2E77DF3BF350E17C74B3447A452235919452B5175570A006C7680AC05E8950A62E1 -1D7E3ACA35A397D1E19630D094A86807593C97F4C484E4E06BCFF708B6DCA972E3A0009E1CAC0EA4 -141530F5C1B8AEF5E1B933F37FDDBC4BE22B74FE346D1A3F5FEC0818F8E61765568A2AC04713E828 -F98C449D9A1CCE52D10D61DD8BFD084C8D099A75D89DEA64D5A7CC68BD5B0593D97953DADA976383 -F5015915618AEC56D71D1DCD55B89736395C609B315A3F1E1255432FDBD37F38CC43C354FB4B7C44 -F1A7318B0B7E99C3C08C33B953727B6A6328051783A0A33E3CD9E498346A3CA6A77B517096EDD52A -E443B87643A646C3A7BB97F742888D33F9B3127E61942F4103C1DBDCD8EAC8F9E259773066736CA6 -53CE57E8822651261D847C131321BB9D6626A1AC50D047C0BA47B411DF2A995545BD68EC0287CC9B -31D5DDCA8755EBEB10ACCB3903AB0FD5788E984220443B8459E7C078DA4289F1350905881AD6DFDE -C47302B0ACB0D4AF8CAED02B4B70DF3CF8FEC118F0FC2D3DDE3E494CD160E676E300BC464BD4400D -B50EE43B314E0517037BF971ACD7CD327CB2134893B8A0410E68DDC518F5DEC966C7884CF5FDFE74 -723177F20DEDC039D879056CAAB4BF045062D3904F615C5CFE109AC7A35599C94024B41019B9AFD4 -04A80ACAA4837929F5C9317680A13D157A03B59A5588DF79D2E113F5F51021D6F6F90E8BBBA2C252 -FD10651BE80BAFD59C53A3367BA3C28DB6EB9DABF1EA99F47B503F627E15DCF3FD645FC52C5D5D0F -2F07DB4C25C0D1E1C00146E1C4D973E613CCDBD3F9450CC0F5343D79F05E9492E86A1BB889ADF405 -03BD7F3E7543436859184A5B20BD8A172F350D846B7570803990ADAA48D4B9155A2B4C4BFBEF1E1A -065C08E03928559735BDD442FF1E83E1FA20A5DA57D8BDB2FF5427C034CF0128AF111E6E73099E04 -6E0C240E80A73D7BE72B87834E45898D475521CA3306707631F5C6136199F354632D1A085F12A1C7 -C473868B62E534D15F5484323E63D0574196A19EF175214EB35A90873EFCFB92D6CF68761D45E37E -AA61E1A1979A82009507CA193E44B36A806486665CEDBCF387053ACEAB979BD35D30978FC7659ABB -E844F4ECAB3303318ECE80777A5FA5A9DD91B3D06804C4B4E9B4EFCF07EB89866D0DD8CA390CFD15 -98651417114D78776B1A1D36B4BA17746D6BE7FC123D473EF1EFED1C3BC1D555F914536869FD5B0C -35F9C83F65B0E6BF7A627B9202D787D72C600DDB6BCCE613D88492E13CA0AAAB196E8A49928C62CE -A4FFE2D0208EDA334ACF47F20BD793124D2C5546C03F4A364369A76A0425262F9D9118AF54E37D32 -E33AB25DD533A49DF5FBF1BAF4CEAC2D9D378CDCD13B00FDA432D9042F623DA41AFB80699B5538A2 -5403B0B3EABEC9E8EFCF42FEF3EA9F91766902CD206B0787C187D5370B60AD6DCD002DE2DE8DCDC0 -B4719A797C5E26BAA67665016DA0D967FA1346F9588AEDA174CA001B31213617FE19EA218EC23597 -79D979E2663166489C06993230B0D07973A117C4E3F4A4C93CF8428248DD5389414D679C69644142 -67C7FEA17E35B0CEE456667A9B1875C81B2302BDDEA2818D6019FC1622A82051F60584ABC904CD91 -8676305DC03FFBCC64FDDAC8D8AA9CE2EA00D6C97BC63C8A617DEDFC0E40775649438E9F61AFD179 -5E3B20560B01BE5E0983F136CF48AB206954E41DEE0D9DDD953DFD01CAEB569151D6BC0DFEF29D70 -FAE3E198E7EDD8922C0E0BCB8BCCF1C016142C1A8B337AFA7A05A9D7534B184BF3BF827F371E9BD1 -9A71244ECA1BA73D484CD2FAD54DB2F0EEFBD54B536EBCB5094E6BC2F5B2AAE41F05B4B311115876 -ED42C34F8E643B53372E3F6350DB8A38445822EA9A33E27FB0CC42CEDCD1FE2FDF723FC47C996EE3 -56C402112F24D0AF899B2D00BEA1CFD427998BD22B2A09046D6737814448ACFB10D387547D7009FB -384AF0562C85694C071584236D0F1F3D3FCD0CFB38B77C81889061E668BA7AB37AA60F58A3967DE2 -6F939B79CBF10A9DCC42852561D8D6754F1B660D216AAB1E133FBAA321C56E2584BE5C9BAE20CCF0 -0E8DBE6D9C2FCEBEBAD945C3C04101D2387351F132628786F6D9D4CAB83419288D31F9BC600D9664 -12E6AA457CE6CAD26A4C0671097B98C2384C81DD8B9A3222D4F4BBDA7017895C3EDC26662779AEE7 -40D9D7E24185FB821970B0A3A94041A69E4805EC88EE1EE521981536F2844FB8F5EF645F67D42CE5 -148E2DDE43AD5AEF200EDB3A2C7866C98458A92666E5F9E070178BCC39F65A893102A10564AF4E8C -AAA5075D2F8CD7FAB0401C03AF299EA3515CC93066744EB5AF7CF0ED06675BF049A6E3C211A89E16 -DE5BF0445A7CCA6EE8EB0347454950485D884606651E5887FE8B24323E2AA16DE22FC1FC8C4F06A8 -2A1FDE5758976024068197E1F4506E4D3D8A16D40461A4586338B374A592DC60334402F76388AD6A -457DC3F54E6169CF7AE3959676E966A45609621055EC3AF80E182633300A4418E34A66DDFA6B569E -5A13C9115B5FD3EC1CEBE50FBA247F60803AA83976F00117536342DC3D9890C49B2AC701D370E43A -955118967827760F7091469C5406F08F18D7E3548148CF0E312B1DC71DF67A5E7A1656CF2F47F3AF -F3DD50FFC2FCDAB7177285B29C17CA43019F62AC6FBA52D1493ED7C427526470ACC8389BAE827759 -4958908F517B2863B83292EB5AB3F57FFFB08393CA610FB1FE905D88A0A16AC395E2A2A6DD033D6A -0D68992F830B2E1B95FE357BF672716E88FFB92FFC3D62945D1EAD22BC68C51EE0E10A43011DB94C -44685A5C4576F6EF44CBFB45F2A4BF110A01657DB51FD499767E78058199B31DFD60813F1A344F86 -289F9378231D5B151C92385E3650B4FEB1DC91018EAB8474CBF69FDC1496A4D078D2C351C8196451 -247A9DCF8117E5B637371D8E22E248C64D999015C3FD2311E9950B8EE0922FBDD3D7BFF766BFE9E7 -CE0BE12F318FF2A7B5A9C6D00A54401609304ED2C55F5C1EAC3D4B38355BBD85D66D61636FA6E30C -2E82829376BEC979A6FEEE040E452359768ECF90CC539A546F17AE906C76F14F86FF697797322B05 -1EB311A759FE260C1EEE5DACF383816AAF1294CFFA7BF87A4D9BC595EE8F2C2F86FEEE11AD959D86 -F22FDAF4CEC098942A57E57813A0FA99239E994FFF353C1E781D666B8928CFC648FCF0869FC68468 -BDBDA7D280DFAB8B0B3A4CA35B074B686DE8D372C61FB32305169A1A9912F6541DA16CD6316A6EA4 -51524757BE5CF6E820011BE3859FB8B8578C100FF029680E05F0E0BF11D33FE19460C85EA5E4C0EF -28E29407C8AE6BE01CFA0D5022BF9FB01416FFF722A784DFC8FCE330EC95737A854471D334FDC58F -AB42867A7B62836A8B56466E9A6C1247D46EBAFFB905CD4321970F59FB8D6FF65FDDD34BF913AD32 -2E68455C5FF2D23C1A5EAE687F259BC982B6A384D35440F7C693CF50B9ECAC0B5578CAEE87588B56 -2EB6B7F42034C9F2E545EC866316552354EB3728C7D26527ED75174EAF635E048B08DC5D23E88981 -070AD5641A652F2344956E9CF4C16E652A99F4A644D1787D6D36537489DA4D74E61B2FC4DFDF1D1D -9D58F9C26C5EB63200526AFD168AC57D5611ADE4D4A382FC28BB60F9E7D626A6C67AFBCCD1183C5E -3CF2EF210D0BF5CFA7BB10FA3887BDD4CD96EEEAA8F9219AA2F10ABC0A960C3B57C0EC0313AE10CC -FF1F522124CFC8D2D49BFBB0C193EAFFC5B48FB3FF30B21CB76F0A4C0F1377C9223145BB0468A5D7 -1B9BC25873EA12E1C60334571C67385C00D0B570D3FFC6C7FF0DE62C183C76AEEB12DFFEE1459E0F -C818C621B8D12FA1357E2B55D48935D70BF140B4CFFE8813DEFD479350B20DC2EB1D3CBB1A2D3DC6 -EE975D58C89D61FC50E6A0197DA9A586B72255023DE47DABEFB11E8AA02414C2FF6258A281219B9D -DDFE41BA7D7977D0D6F18224FE22F7D4E9355FDB35BF7ED3418F4F68D093AC48F7D8FE4194FEB6C8 -0B9DC1F74E023C604DEA27089F98C3973FF9F4AD7BF7BAE601DB89B08D5D8139B95EDCF6C885FFA8 -B3E4B0477E7040225733826BACFD1EC4A0DD72DC41734856AB9FB700DF83CA2CE812913BD142D84C -5C83C0B2583768198AF9E885F2BA74877A414233207234AA5F18840557CA11682AABDE8993533887 -7C6D404BDE4153C9827EB16D66C1D73A8143C8A2D3604FF72CE579FAA3C5224BAC48EA83BA848429 -9472007DE96466B5B29ACC7C03B05DCAA38A48BFF9F214DE43146AE4E04FA705421917F99BC54533 -F0EBC01849E396216B9F0794E6F6C6B61B52EF1B1950C0FB609895C3C55FF574163FC8B6B09E66AB -AED1810E698FF37CC1F926B2CDA3B48C7D77790EBD2D514B6F385D397F713EC3AD3954EA9C846158 -6031D369E8B99E53408A79D64C34EB5A56DE8A67DE91837960E98A66FC04DFA0EBDE21DB003234BB -78665B039D0A469A0221BD541AF7149A2A659C300132C14581EF766FFFBECBA8B58A5EB3F95446DE -F49AF863A8113D17B2E7E6ECDEAFC3834D4DF900E3475596E86FBB4E2974C090DB4AD61A737D611D -92B4535AC291C56AD8B1C031D2F9B505BB77517B737D70AB3723DB52AE2ACCD5DD2F617423ED3CC3 -9CA882EF41757BF7151806A9B8B0F312808863E3673FB54DE939B35CDECA7FBC4DC3BDF5A5F47D35 -E345916C39366C8B4F439CE1C6F1835C320BD1E67375B03B5DE18C93256F251761A4C8CEC01019C0 -68E34447BCC503B9571FE8000627A6B3DAD5854CBC0A2D69E5A8F46BC78F6A7B1422334EC7A98ABE -FE9B83E01DCF3C6C9273B346F3240EA225AE4A4083CC7B0EA141A0773FDE940768358EB4B13D82AA -304A1386D450C1C0C6A7D5A8FD2BD313F78F85248B5196241E31E5595F3BC01F37700A2DD3D4A0EE -2DD01A36569CD507130E8F5B1E96CB560BB7DA15560CCADF3B2C9804A11D9E8055C9EC70E48C1D21 -3EB756A1376F2EDCB7189D78CD3D6CA5865537EEC31C17D801605EFD860B0B629472690588D02575 -02C6F7A75B9A1C1B397781329832CF3EC43C09F1559CD562C48FA9500295CD3B0A790DD3FCD4684A -7C7AC49AC9BFFF36B39A9FB148BC28D37907433943CBBF0CBDAB46D3EA86DC8F81C859C52D15302B -94A9B51C199B7104DEEC9D769C2634CECF8B700CE9C04152CC59C9326BDACBEC4312DEED92DD087A -1C4840868D9F97CAC046581F762F75E8D24D6445370A3F1E0AE74A6478D9DAC37E7FA5BEBEC0A1E0 -81AF89C1BBF7F51E3E2E22C8C405E8671BA85F1BF0DF79A465DAC7EC07F731E00632E017D190A99D -83E27E5C2E63D7DABBA23B2E88334C63721AC5A4CBC5D45F4C177259F34C2EADE01FA008AF65EBC6 -01D8DD16436D86AA94C99F3CC0A2F87134E73BF22F108B825A8963B49C6C685474AFE4A542C8641D -C0375D7EFE9AC1168D9700459BE52D0DA399023E141969F25C0DAC4668534B6647EC85454BE945E8 -26B26DE6E3C4584B97A38E2B40A0D23481BCA78084FE80E00A71A790BF31DF468A435ECC88E60A57 -860BBCA3D65930186E9917CBD209C230E8F8255A7ABC7D3F043AE4D7AD63D9980BEDF062B7D5C298 -C40225B6D03F29A0339E0FCA02138E526F06B9EF47F5E7A8068A846CFDE2BFDEBD24F5A73A66C079 -18662AEC80B43246284FA4E2EE0D9AAB172B1E59A6CC46B801149D8C0DF6DEC9A55D8E1B0EFD9D30 -2FF618075944CCCB6831D336B11617107B0530D09885E5CA11A5F1FCC8D69D603DA16BEA51116D42 -CAB1AA1E4D7B9B4D79993F2BFE53EAC904FEB70B2D330A89780EAC10D12CC0C35B8399F218AC2976 -E57A26BAD20CE2FA2AE2363D3FD2A8A971747556F2959DA74A8963C20B504711AE1CB0D0C02457FF -2E9BF696B159AF031DD5155F21C0F5549B0471A3C5DC8918B675CEBCB23E29322B959ABC05283A70 -2E878DE8EF25EA760F3C5C7B7B49D398283DE2ED837FD59F7C22D62C58FE4448B1049FDEBFC8787E -67D7DAFE9774979BB3802254DA59BCC0219F98C219F84D995CA768B8B5D9D4A32525DFECE003675E -E4BD5D8DFFC11025AF2B468F9207B5B2B42349B98232BAC0759758C1F4A283405815BD7145C93FA0 -8F3ED2826655053A3C2559073D8ACD199DEA2C5BA5F616A2E48548B4370EC73493BA07E197165DCA -774438B0766867819C1154D1959FE6E01E6312E0AB91FC2E2BD240FC8652A2D456A1DE7F34EF372A -53794D4C4E050BF3CA5B7BD2F1B8DE93B4C8002485CB219AD2D029739FD3C81CC6E78EDF38723576 -1A57143EEDE5CC887F282FECD261F6A25D0A7E154ECDF5DC38E426811BE86AAA458577E5E0C5F0F7 -5AAFA9C41E5D1DC9D91ECD79B514F8CDF7A5F1A189470D35FDF4F9B8788879CCBD91B427822ED658 -389E981E0EE5F7FB87692A3E3E931DF8A1D1573E3B0166204240B7080089A09EF7487C9AEE2D665F -5A82F94C877FB5B0DC531CEBF1E71C6592CEA2401E4B5122E5091DF03D203DF979B9A6EFBA12E2F6 -B422FDF15D49AC0914D372D21E871DE65CBECD105FD4A3E4714B9CCA5C6803FA39DBB015EA8A88BE -7913502E562E5B170B87BFC8572DC9DF49AD63694311EF1334444BDF0B4CA3245271C1F7A4D7FAF1 -703E3AA0E1EA8D5C6E821B28707EE0C9B4F22F23796FE87356C58AE2CADC191F4C58E1FB58DA03B4 -5A25AC95DBAE13A293474217BDB214742B9D9D6AF35F70FED2891942EACE3E625E55FFB820543FBB -250A062D3D395BC0F219ECFE0D76686AC148BC41476A887BC494DDBD396BE200FD3E03CFA12EC9AF -6B934A283C42AA05589AA6B4A8D16946BB51F50419CABECEAEC5AEF9085C9989289E9B46BAFB6FB2 -782D84DE2B068F91A9744AAB237CEB1BA513E57E4C307108E993C972A3E0A898D5A8D27833155031 -FDB98863C3BE7FEF3004CBAA5CB60A1F2E3EB4D7290FF5FAFA088B1CECCB6CF51A58DAAD998F0839 -6CDFD68F5ABC9C1CCB8F6514107773C69C26873E889D1F79D10E866910E4684186FCD71C965ADF62 -39BA3418B313A27AD632300969B6F284519366ED85E7CD968D64823F8C59B5911A72D0A20EB72B60 -3A61E36F52F256FFCDF706B4560B4DFA5D918FBC530D83A4B3C01BDD3CB4572E24242D141BF9E775 -36693A0407D002E09CDA5B195BF1CCF430AE9824C07928A050D0B460F2704BE8F9E647A4884C4567 -0A81EACF7CC038643EB0FF18A376FF6F32B6FE4F197273327FBBDEC6443A299CAD4B26F7778A99F6 -5A11BDE047153E764039EDB251936AA43DEE50DDFDF8856519056AAFC4C5AE6F2051AF0579A9ACD4 -1D00775D7DBE70022CC263DCA5E0A25B9C7C4F5C418587666B2FE24816B1E0EC92F9074F1403BB83 -AFC3F1D52CA79C387BDEF864366E34C90BE52F7AA09935373A07E4E026224E76F9EC3CB9E7EDE50D -EFDA48248D61F3CEC880A3B8843306375D9711E58645F3625BDB8E87052DA67F9794EF4AF8DB0BCF -E00677C3A26907DC651BC838C40EC39E2B5A5DC0DBD345944A6C32226089D63C52490FA10B215AE7 -03CFB663EB8A47793B84CE7364DA1C4E7FCE32DFEF09490121222774915BA59C78C2275F829D15CF -4D8686B095C38C731B83D48738C25F40B8ADD487C350A2EBE846C3916AE384CB1050F9F5DFE09FCB -D9129C6270FD86D55A459618FDFA4F907E6B4746196BB717865AB378414029017551161A52E9D24B -E4F7EED553A927933D4ABC8F25DF607779A717909CB4D810DE8F5762581900E224E4B91598149BA4 -71CF8068ABE8744356B261600BFCC57FB8BE45036CF6571D9B2A95304933BD4F17215F8EF53F8E08 -1AF61FA7F9583C34EB5655CB0ECB82246959F09091F36989EBDD646BEDCA614B9A61AB7696B3FF18 -1058A150FA6EC1BE2EBC7F64357A3FF2A2B0491D2F4E0B970DE5B7788B467CA678039B5EF55C88A3 -84578D427FD2CB16C87B0BF0A3D37CE8ED43E0F049AF2436344D5F47C948C632C94A287509282561 -6C64C5D262FE5B24916FFEE982A69A6CCF888BD01D62EA591EEC51F4B7DDFAFFBEEA93FE08D736C2 -0129E345D06B10246A5F57151C198D407730713F32299638EFBDC01367E23EB59AAD42A83AB41B43 -2DB462652E29813740F4680A5D4BD47B18328FAE6BDF4200CFA4CE3773809B45E8887C9B2E423698 -9F6C48D64F5986F563D9A7538A8716082F81936AEBD0461E6F4BD470436D8B7656F0FDF89108E6DD -02ABDEF907731D458D690BC608EA9CED09EB1E6E64C0790C7A2378201CE997FFE0317679EE1D4EE9 -F91157449323E53B4ADA8096CD628B5861BF794543A98F2FA2AB54FF0F25A13DAD43DAF9394329B9 -5AA53CA32749FECB0B2BC035DD1EBD53FF9FB5AD8BCE06CD89E5568091C1CC314CFB1D9821D7F9AC -7C55F55E0A16E39A87D43148201B928F3C42B110FC056189DEF183745F3B637441DE8BD4C3C7EF12 -F4258E306B2877ADAEC63441010750DB4E6269A4C78A0AC01BB3603C386651FE814031CB5D8C1F14 -9EEAFF652A53E57BBD4C8C0CE36A84A319A53BC1E5FD3F1ED1EE72F4C1A9BF264B594062FCAFB22C -C1FDE3F2E3D3C17DD3F7FE0E15EBD812D550227C06D01127385374A11438ABD50048E17255FCD2BB -85122A6FB9B7DA9D5E9DE8A747FAE0DA45A1FCEFE92B9E70A5B2CAC668D4D07527A5C1403267D823 -048BE671F725CFC7474B44FC5AAA348420B2D7C23C6CA066666FD6F2208E329878D90CEF1C2E77ED -22D3BEBB9D547810B189F08920A27E7107F208591A84D463CE2576C70C3DFE6643E4EA93F4E1DAEB -41D46F0E2F56FC10C69AD5034FC9859D31CF27A3A1EE256C93111F81C11ACF1FC0CE20B90BAC9AA3 -27A5C85A7985B951519FD4B03C40BE637162AF41B2FDA68F0D1E9B7602FE2659D3D75955C579AC51 -DF6A552EB9581AC3F712F083F19B52A6C4F560F36C59CEEB0C996AAF1728A2AA45DCAD79BD7B23AB -388D5B0B64A2B95154B6259B730B0F4A72C8C7F7CC93C7D64D9D8810D1F63FF8ABD4DB89824E2D26 -4FDEE916C41E299211DB1A53256E1DB5CDD04862F034D9404B73183A99D3D13D642A663F129B6D16 -7095BEB4EAEFD03DF2FF2F0B6B594C1EE90FDB203DA89FACEE23F1BA3901FECC75FE1811BD701259 -343011262B6A0A9707AAA6316BC3C17F787BB80AC8DA5AAC942D90F80C5A3BB59E47EC767244AA95 -C63E50BF809998957936D3BF6ABC24B0A397258F9EB4DC8F65692CB023D9091FB180C69498CD0C08 -BBEBADC84A7E0016E8F8BEA325D924EB0DF82E75D2CC2CCBF039B11934363D4332C5FBC5EC556BE8 -5EE4E707CC2753CCC43D2ED50558E51A104221C9323CDCB0199B7B83454DE3FDC810D0F362C0299F -5DD981B31D8E3DDA284FEF9DC8F9C8DE138D3065437A7FE8C30572AD06D62E8527AD37AE39AAB0B2 -25F76A25F6C6505241ED73BA494CF923E919F688DDEBF193E188F8C4C154F21631080763B4D091E8 -AD1D2FD6649E0CD9360E8D1A67A5B5FAFC67547CA31C95A5EA8D4EB5D68B9F6D6532DB9B54584735 -9558542A2AE58C09F3BD2918EFBE1699E9C8F2C2A11EA4D224C726D2ACD4A8D8ABAEDC6588CF2AE5 -66528B94F55B823A2A1F7BE19000F3E7579D094E047075DF18C8C868760295533B26EB3ED90635B1 -29C17ACA679C3E88B06998CE5A7A2544B700229F5A6A504BD3E45B276471959C8A3F81917A534287 -39B5EF9E3D463B3BA7318448E2A3E79520D2D245A2A72F31FF7070B6E4624E3A5E216BD103640C8D -F387E49D732529C611F8B971073F17EBD2F6EB18F9B74A67E1997926DF178D4C9EDED435B9682F1A -279C81BB9F60DAFE125845A2FF3B02979E5481C78A45C479BEFB9FEF3CE2BA9BC46C77B50B03E48D -A6D17B76F06F3AD118371ADC69E178C52B5FB4B261C9311874ED07DD6D5B3226A005FDD7A6D53848 -09E7063F036CDEA41619122635E835D2D74CBB6AA9B38CAA4D819C26E95115FE0DBAB4198FC5838F -2C91B7A87B07D734C6D4F4F83444C1E90AA9BFC908A2BAC4B3DEF9157AFCA5248F2DA31CA87BD363 -AC25E9E77F741D4B2C6E02F04987A6F49D30E9038CEFC41BA172DD675AED8B392164411144E5B738 -F3210B0E66B17A13CB9631C33D44484E792A7C082DD0A5382F34C5637653261B1EB6D2035B08B4D9 -1FA9AB770CAF40A103629511F7B43F2743D7E583433DECFB19C21FD4FD0AFCC22A4119E77C87BFE6 -FE50068B22479015BE5A9F06BEAB4D37412E062A45E0CBCD7BB39FEE747E96306F79FC4F2E8942DF -5D9DA0E55AACCDA547DA19D30B8404FA121298B44C9CCE198C708C69A8D6BF17591C5C50D3FC5BE6 -961F7ABA8F366DAE957A1C3730DA4A5B4F035A9274675EE3BBF0CA8CE9D8349F50CABB1C3EA4948A -BE6F9F143592F1EA95404E6909A909168E3279A957AE1924245C356331A75E7008BEE92BEAA304BA -40B7C3F48F74D9018B3247DF50EBD7CE541DA48ECCB1B0BE51A455C3C13C279D4D8676078C3EBE43 -08748D52C9B041D3E7244C745B1F2F742D010A9E60695F3EC4FDC1050AC082B905D6A57E8F407A3B -472F731011A5798965B7B1A307E252FE02C8F79CEEDDEB6E165F1A94D7FFF18DDBDF79477F14E9E9 -3981ABD200FE7771B29D1D2D120EE79D28B9543818527039AC74085EAFF241B56D08220C958B5D9C -87C0C04A14D52AFD475B542D391BC54FF33DEF8D9484AFF6873BEED32DDA4B371112B523B6CE22B4 -0D1B416B64C9370F1CDF2C548F4CCBE9E12E21C36CC3EA52DA232DCFB65F66B22B5E2EC04852510D -5E264EE939BB67AEC4764B87062AEB7F680B40BCEE04AD45C7519EB3B6199C9E0E332661463647F2 -FB7EDF303EFEF84891CEBCF0FAC5F723A9D0476C3F8C092604C87FC69C7A90F4D64AE45A478EE8BA -2DF50FB93F55A3546123F0B0E2C1C40C98EAAE9F0F26B8F80FFE6E6B94B7E27D2884D58B8A119662 -2DF6BE608C5569D7864BB756DF2EDD184B90812B44ED4A32D001C31383A40AEEE9743651F7950846 -15C48E402DBC01C818D477EAC0347795CB2792E9C11E8FD4A02E194EED1C919D4598FEC003B6D9A8 -A0BC7D456047A1C0579453FC1D7CB2D158D466939A23D7A7B8ABED7E2777EC7487973E73F2266D9C -250CE30729E3C5223AD93B9AE8443B35711E446A3DC660123ED45CE1942A1A2AD0610467E081CE2C -8B92A6C82F0B17B5D2429E99F1A6268072C6B5AAAA6EB6283A872C54D3694CD825EB2926E57DBBC7 -C1663075E687A144E4D61C225781D80FCAE1497B442342B4A3F1CD6BDB50E31791CC3928C30835FE -F845B6BE5E2D7E3F2F5F085AA3FAEB45CAD0D76BCBB1ED859A9CEBB9F7457036F0BC3F195CB1A98C -9C8648F6583CDBB23894BC719D68C2DBD8003B10D08C8CAA40BCE784D7BFB4EEC9EA5359AC056E57 -B8B0F2EBCB1F4CE40C87FC7861180133E0CB6CE2FC4FE690756D327A2B5AE063E3021C0C0BD420D0 -56F0B941E6B36088A55BA11D0C35FD0132D5F48E5D9673572347171B4328D4807B972831C0D74CFF -A5638C145B89C989E6EC942148207D6DB82257585958034D9F9D4221C7C9F7013790DBD130F277E0 -BC88BB179DD09E27062379ED06F25EEA8B7FB33C35861A0034776E3813D2E9E5C10E227CC569AB36 -CB2D9DF2E7B7B44758F9DC4FFAD7A24AC7E9F47AA850C221048C3CB35A37CE8EA75632AE65FE3212 -175146FECD6334AE3D3C5F492F067F795E1E8FF386BA198CB74F0BB4DC0000DA383BC4CC3F070DE1 -7721431988D69C8B1A5AFDCCC83C22E16A87E01C6D3E79DC7AFA3DB0371B0866EFB8B6F88900472A -FEF1C4A878243C52D4E02E82658979731C841C489A6B97E271C4C93800EC7D91F93EB9B9C659A554 -E1FCE42A5EC65AC39190EF4B66DEAF6FC0569A000A9E1495F42F706FBEA4D32EB7EF11A648910259 -6A65CF899C2F322F5679C6D123469192A9BF1A7F1F2C81C554ADB97BD19ADB746A4F81A4D5559E60 -AB94C483DBABF6CE2F28CDB412D50FF3FCFA3B3DAAACC6A83CFED910CCB3B8D2C19590AFF4D75303 -4A6CE7F4156896A13808E0DFEAC547E69D3C886691728E4A35ACD575B40D721E8FCC5385A2EB28D7 -08101DC50811529528F5CB0C009BA7E3C88468E37768FB0D83895AB54DB2DD5426562AF9D8AF304B -F6EDA54E9C92643DF926F5C3578269750120302A37CB140A18BA56BA01108D4ACACE8FEAE640A6C6 -958EF156B588ADB0EA5F3B0F37BBA12B7BCB221C811415387B024B7076FA4403A3AD6EBB5D9C26EF -EBDB7ADE7C60B444AB9F90EA493B658B7767AE2BE649BDBB3FE85F460F1ED137C61BD95F7CD3D8B0 -15CE45138538930AB62AA0E54B4CE1A5EC5FEC0A2B28B345B67089A4E4AE14D2E1F5A9C8848DA688 -CA298F93860649EC3AAFEF3E820D86988C8E3E5A4D4BB937791827994AA3E81D0BB3EE115EC36D5F -B9A392D09E79AF514D11C7B3A03C9F9C13355CE79E119A19177FFDCA34704D38118A8976D1EE5AA0 -2D14FEB1414419F5E85244ADC5C0A765A522EEF36170064BB19FEE3B5F7B441E4DB967DAE0BAC2C4 -8FC6A836E0EF5A69F073BEE1699F55E9C757EBD6FD8B514E2B49D6333815B7DBD1E0694695FCA3D2 -1320A0C4B852D9706DADD8369A95FDD917328BE93DD33818954DBD2C212D2CA81560ED5BC284EB04 -7A5F389E24E43F4FA8C97FECF46589FA7341076555CF55B1C21B28E0C1CBB00AB8B6F67472F27BC0 -D11148F407824B0159B5188D4BB7386FBDBF1C0FAF34721B7BCB5C0FCB7C4010DCB6A1284E9D7883 -9E3C2111A05D29AB7997073B590A81C6168020F1D48951BC7D8476D5BA593F4F23CAC1F9BB0E091E -84B4E99E5C584D1370DD12DEE8DF16AF8BC6B7B23E2FEABDB7F32779AF8E2B5094A6E9B7A7225F24 -C43A8E5D2B977E1E19E633C26771E23017ED233DBB02C64F8CF03992C6484528D0C8464B46F24F9E -8380F385D5D01B8893C67FC103498983CF939432AA380CA576D09030CD52FD99BDC3BE16C7204CDC -3365BF76294A83A1FC14A236F5FE5321904E779B13232A76F8FE521F425562678436359C2461BEA5 -AB27209541F557AE2AA60009C9CA0A9FC7898C14306CE35A50017BADEFDECBBF94EE2905220706DC -806409EF87DB1D73EAB0698AD2DB72CDCDB293E7FB13C94D9FC87E74502E6927A212F0D7D2F2D194 -64F7A66AC07872E18CB1DDE8F11835DCBC5C4EF039333FFFC0FC1456DAADE7DAE3EC2EE0D3415B0C -ABB69FC5006F4D14A4EE1A5CA99AD4D5E629C0DD1E0F097B5B93DE2DD001A8C418234C9C45E8C13D -1AE04E9466DAB8CF1ECB88A4E059C111A6468D2DABB90DA79C7C79E94DB28F6968B1A632F8C57D9E -565FF91C6916026FFAC0661856B9FB8DE9C81661816221B1FC159CFEF1751E7E403F5F2CE32529DD -540792FC17A12A3DCD7C50D38EEAADBD10ADBF5D8A82442AA900CE6150EB7A4639DD9FB6E385B2FD -093493DCCD9014B23EB172E21AA89643A6CAD1093343D85D81261972DE0ACB16A4C6B5F0BE4C978B -FA12D3CAF0134F9EA49F6E9687C8F99A456745EA252F0BA9968C7F9586E3DD841AA92DC7705BDD68 -2DAE41518A09DF0E209F321D7FA3417202F4BA76A984DA3ADDBC58136885362F02F0A24EBC439B3D -BBDACFFD8498EBD29F88F016B1FEABC10785438EAC860B554525F3266097A675299AA0967BD3B7A0 -EEEE3FC578D1BE99D3533BD91571AED904BFC9DA1A1451FDC5406E1CD614E0C7FBC733563CD6CE6C -C31E9237CA153F1F0411114361D731636BCF98555ABF12848AD109371A42B63675A4130B81E97C2A -2EE2BB5D8FAE2640156001AF0F55D9D5DF8FF23C8AEFE14F120000F14149A36E5C94CD9081DEC277 -C2C34870D05011F99D48B0875A5FF542F067F7E9880109F586BCF2B50522A1F23ECE44349E539E70 -F84E207DC9BEC7CDF856A046F1A03226AA41F541719AD1AF88FF211E57DD0C1275DD0B7B47440DA0 -89B98C6EE92A7D94700B83CEBE19EAEDD8A615F6587587BA8BBA3CE3AA5E8EAFB1FB0F486BE3609B -169EFB178A4292F4C0378AFE5D24EED1CAAB514DDC66C696D8E37F294A6579131DDF5488E9436609 -ACD750C3DB0A940C84FE022B22ADC2676F62E91E8F891225F891FBA537679B24547BBBF35F04915D -20B11739F620D18B5B216921D222F15044368569AA302980B9225BB839F494588481B94B0C724352 -B2DF600A22B062561D86CB8F81514FBDAA4F8A043A0265F992FAB71FC9124A45B8475E1EF3DF6B6D -E35CF329777D45F08325E8505EC0D979F542807AE77E57E453525F23BC59A50740371EFA98678AEE -6C425374AEB745B99DDB5D8D908FDB551FBC0DB15832107BBECC4E11A1A8DEC69358A574A2ED46CC -31D564549EFF23102D92BFDCBB2BB985F78F36033E34F59C0EBAFA3BDD71338736464CEFDBA91398 -33995EDA4207BFD4A9867D32E867FBEB7DE60D132803EF9347CB17BD91315484EF6570892297DD8B -7D966103339535E28A00CB1EECA4A9775F60A9F5FC9BD8B06D78FE8E6318C31DA2E847E3F9CA587C -B01AE2BA0A2EBDE308314413F4F230A758184ED60D4F71F6CEC22A93A01B6C54E0449A3860FCA895 -4A347B7588329A80974ECBECDA1070FBC055666375229F13DD995E99265DF870BC8B8CC6347FADBC -1A6AF64599271A475B9123493D46BEC41289BEEB67EB97A8DED7A9C9730D37C65164CFBDC22E5CA5 -89D2E7954C7136EF4E084C43A6C7F361A3E96989239BDDB9A593CC2A80BA16DE9EE90E95CD39393C -212AB22EECB677FD36D34DEB46C4AD0D21BF7E6D7CBD0C8083842FCD87B18FEA7CECF939987E99BA -34C214E44DD84C176C9CC5A4CEA76D380CB316BB4EF9DE73D73B4AFD4ADB54451591DEF86621D138 -D5A0A29441502BF6C2ADE671CEC3CB5CAB903A657EB2D70C943F976C110E46C5D9D29BC00A875F27 -38E5D22496A43E096E009C5D3CB724B4CABB32838DBE527F83B18CB457E57B092C302EE557FD4F00 -DB9C56E66C9FDF4EC9FFAAB85F60D02BA79694FABA476A199A0331C30A78A92E10417BA236E23364 -8174C826331DC1BAB87C5F95027846130C6A2B4027930EBF9A97BA1B039D386FC51C302648E25980 -212F6A582CDE2778C677A01FBFB3C5D1B8A374ADAF6ADBF7DC94075F25ED66D440B3922C5F255FB2 -3FD8F6E21EA65B1D93BB225684B50F11310E242B087575973345B229BA62C1E2C35BDAEC04D10148 -F5B2F3BCF7399BDFDF1F3F79119714AEA697245BC647316EA157484ECB951BE367234FD02E8B1F09 -1AAC3D29BF282DFF4011BC0CBA8E55234D943DB3017CC7A766720BBC29B2D097A956C0F1067177F0 -12D42ADCB473CDE8D1BA35B4030757FA1D8211989DF3BD22CE5D501C21EF8708FB3449DF47D88650 -9FF7B59B76C0DBAE443F336FEE2D615D7EED1C284F14335BC8A26BF4621E10DE9611FB2F1DBD52E4 -B7565D8C65B54EA36D508BCF0C578A49A2665227CDE1F9768EFE847F9D94F1BBB7DB83701C232198 -5C7283D47B2E40B27A268428AAEFE75F6B2F8764A8494E5827573758CB9CA46FA93208836BCCC8B5 -564A69F5AD882052AF1C1417C3FA7F580569528682C77080F3688B65E7FC24D2A3AEB61574B4A321 -5927281544DDD7A6EE0A3E9388F8F631CE7251724DF70726E5912DDCCC8C652DD6C9608F8462303D -867F589DE0F2F71711B35142EE6EF93B64D6326C4DD7DC83278E057100EE772082E6BA368ED91A55 -53ECFE2293A481E42F83BC8F9148C70EACE91F7B7D9CB8A72415BDB3AF66F68EA733A17ABE9DB005 -3BF148629132969589F38D30EABFA96A01FAC72650B5A6FF3935670198A1EA33810A9B11E330EB8B -451F24F93544263436F669AB5A90A53B16CCEEAC36B1445574EFA7E802DE73522BE725E68704822C -B7D3912717333367895BBFBE06966A5CC653AAB5E9B3596702086BF0010085B900711932A95ACF15 -CA4DC45A754EA334E9EB84D6FC8E3FC4F897456BED64BB93B593549FF0D5352275D8E417172A6664 -C5E0ECED1019494A7ED49AB0B965BEC1A82E5873766BB38D7D856049CCE2FCA65AAF61E961B60634 -E2A69EF059754C9D8163D87F928C222772D070D83FEC6FA5AC734AF65E40BFDE521F7D9CB1650FDF -64754BFF21EA3FF0AF7611A93D525EC9B28C51AFECB04E7FC8323DD6C9B0D8539A34FC3CD8CEB795 -8E8EBBFED4313C77ED469C199552A9FF70BA5423B03B6148D4EAAE17B71C5B39DC436AC53D6BA8A7 -AD81AA8B02335A8B2B11E9F4FA913159A725B8AB60F52F1A2EA50EAF4D56656E615BF382CC68A690 -BF83DFF24FE986570ADC0290ED1A37C1C2AD469CE789E0EA0BB5CE01020100E729721AF3B5BADD33 -A2DAA6C33EB8F9064F5292F715F820B4BBFDD56F76D42E7A1A068C1CBDCE4640082F6E7D582D1939 -990CE6EE8D270015A2C461798B37DCB5798EE9F7512168B76D26C28BE4A49A1BF96C89D235F21A1D -B6A96E5DA474D0B19B808D13D7A11BF39EA8647499C410ED9894A1ADF33D41B6FC2E614D8087F4C8 -4E437B136F3CB32DB8393C49177A0675A0C9E7EECEF448A97AFDBE840FA01FC7E5F2E8FECEDC1884 -84C312E8635CD79195475DDBFDD4D38D5A0246DE2C7F21608F8D2C0DA1371D302E941572E5792A3C -F4E51A33228B93A814D03FD4FC223C314CF3714BB3A34BD4F7ED6348577FEED9DEB082C4049E57B5 -D3CDB7F26629E9F3BA36893E09E3C7463D02A22D7056BE76B87763260E46E48BB832B7EE13F8DC05 -37EC8E81E9BDFEAD8C27EBDF1AD706933EFD11131E12814F236EBB01BE85B7F1B2D627413B324918 -D247604F56EC128909873FEC3857028BEF76A3494364C2A7002D104D486236C30B48E2B75D851C34 -EA50BA7FFEB4E19190898AE21768C157C0CAC628A2181A32796FBC1A7271D2473CD88E5395DDBDB1 -FC3AA8DF0F3D588637F19A8B833AFDEB5F655A8838EECD684E2315B72C75CEEFBCEF94344ACE8D6A -DBE355008EC72FE7CEEAB01363A895F4E73F867639BE0A0BE67333848816B05B419221BE8F9066C3 -62C23FE85B7F392930BFE4C12B9526FF2FDEC38F23A159ED61A0718E7115C24597D849FA76369153 -54A40C965D4D72EC94DA61A03766AB39AAB684E134FD1407A5B1B19BFEBA52AA0DA5D99CBE5C82DB -AA663711E6DEBA180E1D4A39C320516A4350D296BC19BF1BE054859A0889C7E9727A021F3176FE62 -0FB0C837E4141FECE531A950C03D319E3255703220B7185BD20FE5DBA673F8129AB211EFCF36EE39 -4C7E00EB0876624BC840FA86E58B2F584754CB6BFDFD76810E300741EBE4544E5AC17413ADEB21C6 -2F66CA4F075C32381796BA709782DE34A675B717A2C7F6D88104CB924FDE5DF775B4F0B68E0E2E5C -2F788BBDEAF06D8E1FC2105CCBBD5827C0B03FD6CD64F0D073F3192D5F94839644E5EC6C5185BADC -F04112A65F49A8C83174A9AE958E76A2F5AF469E8B76C833782C5FFB8BD7B1BBBB3EA0CB7C9786C3 -BE2ADE5E7AFA8C8F20892659A59BC421E28845A108E34EE17864042EF587A6D67DECDFB3F510EB40 -D2229585347A0035670FCC76C2837A4E4D68304FE113C539B35C1F0234B5079B8E32934546982978 -C5E4DF955A454EA263C3CA5D7101F31A318D82A3F9FCB5A8AFD7A65209663B0FC9DA400B26F285EF -46D0E1EAF8ACB1F1CB805E3986D04BC585073FC64895E4DAE1CCB749BB439CB32EA91176D5C39C36 -50D10AFB9C9884D5FB90183424CEE67EF2175D01D2478D67511EC9F54F88763C152697B06D948BED -49240096EEE3D06AB4575E8E8B2CB8263B5BCF4FA1608720F52B675309833071879DF52C3EC2871D -20F398B5CAC8F8A4D41D0F1D47584DD90DCDAEA4A1CF160C4B3BF1AAB890B5CEB6CB3488672AA68F -BD938281DBC1D8BCFE92FBF514DA5358443CB6E0147254E91B38CE6787B2BB0DEDD2D38F5938737A -977B5EA42892520C58F8FBB53C994B57382379E9490F0D6970B980E1BDF8CF9F4C3C5E0A18F66E86 -EE93FFE7FE546DE50F41364BCB3721B637072571FA1779F1D672FAD260C16D7F13CBDF3E4376E7FF -56D2A710AC5AC35FCBDBCE2C9C17E523BBE6218617B13C1FA6679B308979AE7C61DA6E68369324C6 -CBC7DDEC364E5A86707266C0B459EE7B2C03FE584E529BFFDCE98C90A2F3D9305AA74D3ED8430DBF -3A49FE2ECFD9C4BC9FEFD22618FE9C8A973AD072AB6F713E4DF02DCDA7AC5359B2D652013E131B76 -B3ED6C75FD53BA58D862846264627F6B9E70D8800F6D9B32242B747A67BB2B45675840D34F852AA8 -062FA6B01E31ED24DAE02F6CF788A17F7B9368175195DB0072259CCE0FFB2C1035C1D26E1777CCA3 -D56A827C3242069E76D6DD69B653768614B9ACFF16567FEA61508D51454BC02F6C60F755AEF6AFAE -3536BBFA1823F8E1A53C41124DE983E51CEC92AEF4F99785D554488A51C20885346D1F761DA79017 -940A0C557D93F1DB6B3D00FFD61D08E96FF3AFCE5FEDF545CC9F47A2B1BB26713431D6D1E47FD6BD -6E3C668B0368241F0EBB5FA9C991DF79890E52E83A3675EE699B61BAF869DE91F67278F510061C6F -E41DE2D883F48CD0E068E2A652B244128D82E5CD52F35F210DDAE3054691ED55A7D99088AAE8FB04 -F525C2084AC09F5EDF80A4EFAFE981F74C0DE9D194320709B3464F3FF2C0F6AAEA6D973D9C323F53 -DE3D741F698FBF01036716BBD62957CB32CD81D3A2674560FFBC5BDC5C6E4F547E589AD0B1CFE14F -5E17FED1C4A8ABE4E67CCF8A49F32C4C6044F1431E1CC382E7758722A6D0DF9ED23E51F8AD14D11D -7B6428E27443715EBA4E9C05D6F238378F9498AEF0E7EE4FE6856622CC8E6ED141EE5F109E343CB6 -695C4BE1E0F66601C27975983BF557C04ACFC19227A1AD7E6C44C00529FC7EDD7F886D24B7E029B9 -C395260088BBFB96972199A7B32796D27257DE83A7402291C14FECDF7998C5C96B1EDADE0280F856 -8A8F5007852EED303969180B3329917973C2D32C080C9765B6BAB0673BC7ECFDBBFBEA980C263843 -39B7F1052591D91667D4FEE413AFC23DE2D4B9DA742F4269C6C939F5FC32A38040730A018155AD73 -3F231E4D5B9D01C03A58EAE7B5F590CCFAF25EDC8552CFC8D95C60EBAE1837D7A97CA137E9D4A4BD -2CD34AEFD68D64B3F4F62326AC429921D7FB3C235184FE0899690A0B775F1A566EC29D5830D32372 -6526F7E7F5AFDD71B77E07613DDC4FC63EDF49051AEB59E6337AC0A4B6DD872E776C9CD0CCB86130 -5322D816732124F5978A86C186BF0A0F88E733CE38E4D7C1BA5378C5629B1EFC97806059990ED42C -5CD183BAD7E94070E4058569DA2E51831FFE0D080301AEAB4350BA290318AEC582C78D05DD92E5AF -B4424EA808629BC972E68F4FF2489C245593F07555CA6A2B25964794CF31CBD3AE5C229AB9B8C298 -06C01D116EBD0FF0F159ED2D3D7DFC73EAB4910BFF5B0B0B587CD9EA6E6FC45D63C09766224D8346 -1F0588140B258B1729F70BAE7962189B1554483392988CF230AF4077193E53330519394DD99BA135 -6D4730AB221DC6A66019BFAE564893DDAD7B177DADD16ADD21D396CFA6C3DC818052E2F71149FD59 -4A16DE0C2FFDD366C99B486C55A6E991E4D22CCB15843F0C3363676AF2F5B2D1B7EF66CCF2F12DC5 -0D63776BFFB058D70A9C76DCE96C754872D72C82A0C33F90D49C935402CDD26B6D743B1F43BED5D8 -B539424849C1495DAE73044E885A7D0F307F1816DF6244A6F2D97BFD4E200E93F69B08AF39EA21E6 -E347A47CEEBF803F73B978ADBFCF056789BB8E6E2563DE87DD9A8C877157B934102DCEDAC54D487A -1BB2694F0034093C48F10A17D32E2BDD0C723CAF59ADDD1BE373AF8C9BEB4415AA5AF36310C31F24 -354A53C0B962573148BEF91D994FE3F3D8450DD4D686725799F53C373A0A3E3C060C2E1A3E800504 -9F26D716E1F381B9F83125E4683264A07E2D8938F605978E2513DD2050B3D8A1012797CBA8961632 -BED260916338A812AE751C7B657E086A0C7DDCD3BFDDFF3E48B84751925736D1310C4910FC114387 -F3ED7FE163F91895EBF55FCB425CEF5729D99BD8F2C072E36C310523E75CD8E5DE49C031C4263410 -9D56E91A46C8C8E89FD92012A00C33D0DEC52597B5C6933291A7BDC5CEDA95DCDA5600F9AE1C8250 -54E7EE1067458CCB66610704C58E4A4FC0CB5FC933D0322A716B2CD430A3AD48DAB3D4CBE9D23F2D -092368CFC4E1F5495C133A92942EC62118D45C17723646E69407B4A89DCDFD2AB3FFC099A21D9D29 -741D68270629AA3A414FE58658DC9170C247B6E23F35C4BC5FF83009F462F2EEF4DBAC5FD158A658 -57F9B6DC1F5192DFB169DCB65621CAB2F1B07BD22F4155A8E9E2B6388D430FDE5EC1C834D22EA035 -C52E1E34482EADC36B4CAE902AAE89A7284E62B3C84B608D6BD05F75BC31310B2DD3B2C08A00E073 -7F104F03A41989D5F6B9A2C38B22F1D1803EE5D7A4D8DE44E4ABD496A1DE0C0E12C4BC96D0122846 -3F0EA9CE9509FEE987139F3DD3F9D0DF4313F555BE85433718F6D05F197C41A9D9C7A8B0D2740196 -82D49F58DD5F66B12A6520D9F226D1DF1F1B65CDFA261F980CA25A92645B86B64606293F8BFDE364 -C47D2AF2C709BBE77A70A5712F2CC26F3D66F5BE2C307A48E6F887F681D30121E32BBD87271B5DC4 -615D28C309F15AD263FB37424E56DDA6E17B998B45BE6C7FC6C28E3394A8764C9EB2DF5C06626593 -B5C665D550D4600172791CD208AE9F37BC082B0B242B0A504B751B18F4D7495172B697EE217834A8 -A4FB7CC16D6F9E8BB400BE8AEB0850960283DCE725249FCC4DE97D9886745AB6066C3E2F64DD8AB7 -9AA11667F11188D7965DC11EB760B772E282DBF13249F31986AC6898FEBFE23E3E8B8D2C33E00EA6 -FC493850ECB2E6D831D1EFCA3C2EC8EE2E394599091ED58BEDE97D7A43B6F739EB0F845EAC1DF6B1 -EBFE876009CC5D804B15ED4B56761B3CE1AF59C07B49DC798A44532297AD73D5101ED47F36A3678F -818297CC27F6AAA2AACCC9AA9B6F5459911D8C56CF499E390AE607F3790450B2B9C9BE0F006EDA0C -715B5CA0481734CFB0597478E7602B0D2C1E4F78F03C68C17C70E4B42D7D2D3C95CF40F73488B371 -8E2CB05A549944D86944D78724E266C3319AF89AE430E777E95F0D792B1C654306E421F3D63A26B2 -1E74B6E8B21B2E2B9DC596D013CDA16D08E65E8F24A84B12B2BADC653E6E1110DE2E709C1C1BED13 -707B70A421B384F20CA7A9A9D20324DD383F28B2D3C7A9C53F5D4C6B7C378D26DF11CF55238BE1B2 -4FA70DCC178DAD3D35670FE4919085EB1CD905971D76A368FDFCF9D2F0A23739851A3A6D2E02D65D -54DEE69ED5D81315D3EA5E356F94EF256DD267FD1E1A9EDC9CD63E743F299BCC4A4506233B8DD765 -2CA067F741603F93250C087D368F9E9CC4CC1A6DED567487C05BAA992B0056A77F630A72008E3946 -15A9DB24FE56A956650EC9DE90A6C2259189440247970541CA198748928215C0E132A81AA13208D8 -63C1FE817F70CA573B54577D10B73100AF8EA088208A44FB92ACA314AE5879706180788C17BB1D0B -81B6B95A1C4E0F9EA66F9B39BFE12444A6446691A7BDB03E0F03D9F07A10A7598F2166F108529F34 -CD90E601FFED3479ABCFCBDE8F051C348E48C61D95B00C59EA1287423F05666C3D36288844067E83 -E14F6B5210842C742B89F13ACD126B9FC50ABE2CA7D7ED513D43B6AC7F41EEDA416BFFFCC5C844AB -2D23D4DC09B2D510504CE98D02E72020D9E669DDAA344C63A1B75632F912A1C0DA3885DA4AF7E243 -E4A4C6493D6595BB6D56B0359106957259E59E336BAAF35BD1CEC5CDE735272EBCCAE8D4904AEBD2 -B32610C6FEA2B69941D6542ECB44D71092A3CF067708A3D087AE99FF29671AB7DD8758759B971A08 -AE1BAD78270D2FBEE37AA2DCB119D72F6C7B0C8509018A70D0B0BE2C6830EF8E0B24B1CE1141EF87 -3A4D7DCC501F808BFD94E4DC0F2915AA023076BCC8006490A43685EA25AAFC187302EBDE7FE1965A -04A5A398985D29F08E085127B56B057334D88EB638A4DDE64AFD204974C3939536B1B66A54B4DB81 -151853915718F70813F096CC1B0EA25E363B49264C2AD17158A4489F91453FBEDBDE15D7B74D7F98 -E81DF23251785D58295BA297F295AA6248A912CDD4F1111E6B628EECBB5139709E76EA4AB743CEC8 -26621D08E6BC64691CC90B3C3C1778931A28D3D5B1E20E96C643316613FC487C9B604C43463FA453 -3BCA1236286E6F5A6EEB2F1D9C34BDDE4595495A365F88055D9268541CF1654ACF478D384A5496A8 -772EA1402751A093582A6625A0A44816B5FDBE166835D598644296249B92CC90AA3FD6445C9A19BF -27F59CB0616C7306070F33C7DF4E1DE64AC8C5BB2FFAC1EF2B1B30E5A0275E6004CF64BBE2C6710E -DCFC3AA4ADD60106334708862FFA6652825BC84842736E47AE6917180365C75B27505EED3C6108E9 -898A780E20C3F606A860229AC46D0471ACA0187D6D539A1B8820F620F72B41AD1D3BF3834BF48CA2 -AFEA8BF535AF74C4562DEADCB63D2F5C7585722B77C989342D190FF926C8A5263B4F25286F99CF6F -C62EE6E2AD61C82B29D82468AC10FD27764278E5558CE8B41BA111CB2F040914451A480C93084237 -CAC8F66BB7C6689F340B8ABF0150E06D5B1177278A4C08742FE22F42C28680F190900344ADFA486D -59718C25D37275BCE4DF981AAC35D2C7E85C72A0188B8953CFA516FD545AEE0BF4B8BA301CFDE214 -4241FBDF3D204E3D2823301572E23F204C97305A82401660E12926EE7BA6EA1A81FF5C007933AFC7 -3266FAC4C134ED818A48E7DA01C71A46335C845F9DA5E960B25339D551582B375814148D94CFB781 -FC56093827B78578A73D4FF67B6B87F40CFA5E3F4325D9108CDB64BD06427B88C84105187316FA29 -90B4E3E8EDB6C78ABF164F4A9717D523794B2FE772A04DABBE688CCA977090979B5F47CEB90A1DBC -167D305EAB231C9F4260C4AD10889CB785169902FC0BED78DA15B8417453BB65856EA0BEA5245BA0 -573F623D215F6C0CF801851C305B355D26B52B0B343645FE25C78A3526841EDA480919A1BBE5F56F -C10ABEAA3E1FCCA7C43EE560F067F1AA2AFD642F769D1ACE8E2AAAF38850F0D757CD808C921D716E -96FBC07DA7860DFA70CEAE2888C0ED3CBF9586443532B68DAED9A926655C157A416C383A53D8F283 -2A4E67468112A09ADC837ED8EC95F70852921F50D4417239FC42EE3624CA97F682745CC5E76CC7C6 -7BD99F2180F8C0B7FB49539C8CC474C25C0DDE491671FF329E51BCFA779346D4686835A3AD6633FC -B5E0F67E0CA9CED8F215BEF4D240453EB2EDD6ADB22278AA5B985FA140C9834D38753DF2014F8C0E -E6DAD19E8FC54C03C1F6CB0F858986691D99592562CAD95FA0A5B2ABE4A8B54B457D42E8C33A2D19 -51C0419A72FB94FDA78ECD92BD2A1416459E9DECA9469F35E4C47DB531726DEE8F203D7042EDB32F -025DF3D582547BB1D45F7A5B70D317DF4EBB16E36B0D798E0932FD2A85B04FD67143E4B287A50416 -2C1F5A037CCD780088C5476385AF8168E12D97D44B0630621759173C8F1E3006B5B1C6D7138B7EEF -C3CC5F54E24B2C3CA7B41AACFD25E554880AAF406EA4C3C6E21D3B550B040FB1952598A7E8E6488F -E38288B2AEB6C4718338598A2BFE4D2B9D14C65732DA304C16FF3E1F8F03046EF095B65FD609DA87 -EC24A69278BFE65C905CD0329F6A486B8525B7EEA4F7AE56C2633CD83543269E8ACD6D71F500D82F -DFBDE7F7F7B1AEE67328549232E26CA55085B6E84D9E2E7F74068F93A90C4654F2F396E57C5F76F7 -E61CBBE523DBFBA6E76638BBA3064DA025A79E3A294FE7F1CC28A3B4C57DD6FDC48E541A85534B25 -E1BC11B4F78019457239EAEFD4BE9007D205F1D985F389DB22400B279C10948551A6B4A17FBDA0FF -C9428B18B43DC76EFB15FC2182216F1B60B4E344A03AD6C00F141EF99F89F24C819C3E32877A927D -84C2D006940F39CA8B71E5951673EA9BFD1749923219DE38929ECAA9CE43B06CFA7DA1BBEDFDA56C -61FF6C24F40E59B13870D5FDEB82D981154FAE5D6D5152DE69339359461A41A9713B6BBE47E868C9 -33CD74C75DB71D13BAE4DEC85E02FAA14EAD6C0A253B16C79514657B15E68CCFF9EE6AA385CFF9E2 -C53D9AE40F85C793E4E8FF50B2B7420F4FE69807BC5F37C3E300E6B3C3549D1D3246A2E70F091054 -1135BDF805E0A698E236B6496702D061241687B7B8D1A0E517DF0476DA09D89667A7AB375FD2672D -CBAB8124E511502DDBD08BA04D941DF1CEBDCCF7ED48405CBCC33774A68C5212FC6F132641FF413C -984F8B43BDFD7B1A2A3435F15AF07EF4970D3E4A0BB947C181E9CA27CC14A35BD1BD096875B45873 -8CA244F88C28728B74E25CB8C4FC1095A56CA75E4569AD3082EF194ADD11350DB3B74B96761D4538 -596FF7243B1E1B724716A144106E080D42036444FD472998460CE9ABBD05B42AF9389AC452BDBBA3 -A13A96890025789F16B9D92251FD3B3BEB2C61EDDB370A20456E3BFE5F4039E2557C451C524F8087 -015BAF3FF05F51869FB97512968BDB2B49589C1C7AF1E085250A47657465F480B7023E24C76731AC -0EAB6704123D77977D3A2C4C56B691346EBE589C619C04515D34F81FC6A17527D5D8319013C5D4FF -27CC3925E24C99231AC7FB9EAF0BBA482D3B75807AC85D03CD09DE5D9AE0B07B7A813F0449786500 -0AE8A7E00080300F0AB8C399057EDDBA273DD2E1B2A0DCEFAD3B332E6D4AC1FFAD846167DFD70E03 -46DAF84AF292D4F424256ED5AC4E104F80697050D50844A708EAC9E7F7784FD01646F3BD0C595CA5 -1EE6BD607D254E78ADDC5E15C3B6AC4940EC865A5C23105B6BE09EA09F2C05D6D76960A843B81EE4 -33977FAAC3CBDA85CDD2F4DB7C28293A77825635992AF8F3B38B4480D9A139B1662345A8ABE1634A -77496C3F57597D2985E9E54717AB2E99CA35789441BCDDEDE9A9E2106B401D9684ADBEFE40D607F0 -75C179E9CC03E59E65430DB70B441D43DF03F2AA6FF06F224B6E455B01C64FB89EEC9103E48453A9 -749B4D602808C7E408A8903091D85E06AAF635D0D529C3CDD1B8479AC0F4208C284BB678A547F2BD -77BB17C86D4560434F7AD1937760A6AA55B614CFA9FF8C9C96561AE6C8F2121C4E20237428BC51DF -2099B6C49E3EFA18E6D439E6E6981E746EBB1DC461259D8EA0F8099C47CCA27B2D982B72C9A07CF2 -1B3C05D6E26E6E286E348B8944078E24809F9C5F3D014B4CBA02533F5621BFBA1F0EDB776C634746 -703C9F73BA89B1960A496420C68F54E5B901A6D733D7ACC79F275FFFB253F389AA480084468BB34D -A1E797E43B7F6E8CAF5E8C93069A3A2730E57EC39B677BB73E3F07C2055599F7062E53B37A5F0099 -907D2ED87FF7A82C95FBAEB888033BDFD67BA3A6031A4CDC56CB1E4CF5B06B46E16D988BECCEFACB -9E1C037023D7BF5CCF5D65AA66A17AB361BE7981F132A578F3ABFB97960A6034F052D9D5AFDC0679 -782EC90F240F943A5F9A3D969ED7399254FF67D89DF668F7C56FCEA1FFDCF20481474AC8495D3AF4 -B6D7EE093E369C057F0B70858220693B398ACF8E8143558132E4391405E30A73937C53402E459F4A -A3539CF7A99A3F51C0307D045DF8B77757E92EA2F51BF0BB4F77D3904DD355665870C2B59F1ED7F8 -4FC71FDD7F0B6C5D3182DB77827CA6A2060D2B8C83C4EA4A432EF43A4D0A952CC6CBBE52A9F0CD66 -1A538973DE41FFE9C5CF55F2506B9EFEE51FBAE5E63BDCF5528499A47C031163C88D3022606784DE -2F46A9C9235AEE3D4F71D4959B0CFDC5B7E78C8C0A8F9DC99440C2263DBACB343C5C648577F5610B -50EAB1CF7FD02419EF3941C7CA0B0E64EBAD4B2CB05A0793DBC38F1946D44767BD287F5E9779C611 -CA0DAAA1E7393DBE0683C8D3455CDFEBC0E64B54B737E298DDA605227C0C4BBA87AA3EC7FA6EBAEC -39E6EF2537D5974391D31739D9FC42983D81AEE44711C823F35F8E2321AC74943871739D2DBE9748 -FE68592263E7713F27E0D49B9B5CB7A4E55DE54E6B800D15856450FFD3AE5F287B12AE4F438B20AE -9E27E6CAA00F3EAEADBE08432684FDF9931E925544A680182602A3C1997DE5D0630BD5A010535E66 -E1C123013D23966B3545C7431C39B97295BFA4099D14461004C42C85095EEACB9B47C593BC6DB863 -533A8619BAE09095DE8ECA432D4DDD49AA600D277E75DC3F5C6631E2A05382CB007825FADB77438D -CFA78E252D79B6A196D5164C2FEB85D75ECA25FF80B1D97FE10E87960CA0FC47C41D3A213BF141B4 -8BC3AAA93FA86245064668394665BFD52D12C3BE4CE39EFD8111754398A944C3FD1AFA98EC337BAA -AF899D35E804CF416AD7FE45FFF13FC6354007501043F98FE8428DE8013901BA6A28711A2CA85A27 -0BB135B72F1D5026E8217581860729E94F2F1878A0E96C59E9F62714FB5F8F25003DFC7347E99007 -8A9A331CB3A6A535BC61866F02513DEB982C4A13ADBFBAC3FF70A7335F40D5489E48E5EDEDEF1619 -1973D932479C62183B0E25EE8C4F76D4F1AE45DAEA4A12AEDD9EF81D248E8D19F8C8A5BECDD1EA1E -98783EB7A38149170851B1942C96C53DE06DEF80913BFC04E539EC67C110498D15B78268853E5C72 -F485F8A27B768569E54241F6115875E2973292CF48FF91D45EBED627AE9F0766D22201B20AFDD40E -5B17CF337F2999E0BD15B86E46EB3C18FC12B7DCADCF9DD50C6C7E3F37E615A892DB3F57E250A072 -A49F7277DD6A2C8042698233D35A699B17ECA5DBDA6D250ED4A16FCC893BF0DC2E33FB1EBD7DEDEA -3C1C39603C8B7E1A5A833A8FCDD5570BD088749BB232615366687962C7E56ED089CD7B092505CAFA -5A80F503C4CF337F07ADF0D106937E25670839D491F7BFF7A523DB609D126328C16113ECBCBF9C40 -04904427A108618AE5D4ED809F8CCAF72251104C94EC5BEE21F91B179D31DBA79CEEE5EC7FF698EB -84AB1D2D1A624F58B3622A78844CE51498B2CEF38EAFE259D22C7BA61104651A862008BC1DDDA58C -C45F663EB26428DAA85E7785363A69D2790996EF5D9621D53042F42F794962FEA46E46F37B8AD1FB -76FC8D5CF2146843F8CC625139C75FB42DDA71A752BAC48F294E4C0C8289FC46DA5EFD9C91BDA6D0 -27518B7E81E8B21F755A9615627D5812ACA674D1527A1185EED4E3C628196E7D0759B1CAE6B9B7E9 -01E9599A65230F1EE469CD33B9BD9C104C44E3C1AB966C9678BD0AD78111A4E0F2D07A01A038CEDE -7036D0534D684A1562A17AD64A00F279200C0371B1CBA61747671D2A21D3F9646CA290F6B82418A9 -6FA177C6278277504B7FBA936325F5FA124AB018A15DC18D2C5E8F93CDEEA52BEEDB78A57828D81A -3E6C38B9FAF3DC4EB7273ECE3EA4482A1C6242A335862C2C3717F9C9ED95F77B140C4E1569B2192F -C7DCF702D0BC9A50428EC406F8BD0CAF886B4D979320D3E429816D88F7C7146D960AC12E70F2CB7A -9F4E3E366665AB3F1B4B6440F55EEA26DC9EE0096BB7763731740A537766490C8C174723BF0EB40C -53701AAD12B21D436ADCE22203C1053A9DC4E9F17AE617888C4B4E6F3A720E4E6366BA628221A387 -D8AB15E04AD69387C310D3528BD2FAA5B22BFF3FA494F5FBFAC4F771C9C7402B95580C5AC4BB3AF6 -92A70CB2C851FA5CF1173EEC3EC29B5A05A0B728BBBB51D3B7AD8B0AF17A1563E82FAFD93F8B7118 -1FB7AFE352874F4EC6D334AB6747519AB8E847B7BCED33EB5458A828E074E74BA621BDCD03FEA604 -7F7B6ABDA01FC7514BA1AFF0D4D0C0CB8F4E42D5A87E395D9ACDD02CCC220C157153422018725846 -009A3ACD8C8CDDB66BC6836B4026FD9F526AA275D06C813179E5924F26A25094E7BDA8BD26AFC4CE -B41D8964D4FC4AF1DFB0595BC5D6714C32F15DC7194E9A3A73013C45D8FA55CC0550A12D9AAE8E9F -F199FA28EFC2426D8D1DEFB93A65717AF3EA8E2D5B4AA8EF0EF38E9600F7D4E7D9F1D67A2E63ECE4 -789FA74B159BFE2F91C19B0378BA52E93DF12830D99553B6618645E26126842AB70262D96E35E5E7 -50ECA0CE3458B3E51BEE2F21191136DFDBCA39BDC07939E521E4F492F392DEBD029C1EA237BD89AF -76BC89F618D530160AB16269FA6B693CF14BDC4EC7C630025703C5337F61458FA09104EB15C7CB20 -AA4C9BDB7CEF3A09F25BC7F3149951A7CD75372993B80CD2112F7674CEFD6AFA764AA3486730D2C1 -897A264D82A91709FEC4A21E30D812F558451804EE6F3DEE2C4C437846BCBDA07C5B6CBA1D94AF02 -9163B7383CAC6E088AB1DC14ED3743EE77E26EA7AD3119A76C0B5F925C4DE305CD7BB3A09A453947 -5B9BD79BE28FC462D8718CE05F9D94CAF3387BA55E6E447BF81A9EDDD3A34E17BE66BC52B0C0BB6F -86F6F008829173816D205182ED2ECED319864A796AB65D4E3950288BADA94FA32B6F453AFDFC6C39 -A4FCFE60353A64627E2057D4B379D3240012B3BB0ED0C7876CB83C1BA5EFB6E2A03F340C2B576731 -F848F762A7E1CCAF267EE06D621BC33FC245D0E1547ADC12CC0EB58B26BABDB8EAE9CBFBAB93836F -FF22BDA1831DD01B7346AD377AA298D84628BF1C07433284B0A90FC89F5AEB2651BA2CEA405D4F52 -DDC0E74B871D43F71EB4ACE0D2B401F9348EAC3A2EF0AD295036BF6CF6F870D58E00B619D50EA7DD -77BC28DEF91D805CD527DCBCFDC16C042BF9B874E3B1567EBA4C1E70744B9E7E5BD1FDA6A5FF6E10 -1613FBE58DC46CFAC1A65ADAF65E49757E9304E2AC9A91E0588600C709A61D4231730073A36D473F -518A145E141D0A5A494441B9EA99AC23F60F54F8127B477E1CE698BB4129B4B1DFEEDF10D9E665C2 -47A62F112F5CA30B0AE5DBF3E495FF06EB28EB438CE8AAAD84D5F50FB56A3AF002C23BCF66ABC270 -7AC233FC0F2723DB99D2CFE7D3B3667732A531F5DC315CE74EDB9050BF75D29E6430F57CB6778B2A -CBD57DFCEF896E6766C8FC5C9F9FBD701CD62CACF33EE0FC95E78DADD205B5F42CC63024624BAA0A -B4DD447832B4E1DBA77BDFADD223989F8E958C8D759AAA37930664C6EFEC708116248A2A7AF3D656 -DDEAFD009B7F5333854608E67E5E588A857167ADF9225CF6C641F5E19C3E08678A281199EDDAC831 -B57223B1BEEADFDCBC8F6F25D32FCA2336C808162E8F381656E847FB6CB13969572425AA05AC830C -33DE6E030F86A3A85D2A66A77F103C7042C97205526DC882EA9A00EB8BD5519847EB424C15F808A9 -1652A6CC89B66A5731126DEBADE123C63D88A2E550FACDEB3886FF98646000C64B3A91078012CA30 -904B71737CEF6BECABD43DD702880538F5A70085E6CC6015D2163681067C3D513A8C66032C34A0FE -17A58AD4BC97CA69BF41F11D5E910FDFE9729652D3EA21F8DD8CC19160A8FC77573B1E9CEF4E790A -79D8AD6723B6804E9616466C935303E063DEE29CAA6C3BAEBF278B818C2EC2F13ED645AB452397BF -00DB8B26E115026E256746CD0C78A959364FDE6DEDDCD0F441A61A1EBA32C7BC172BB09512148D1E -BAC9E791B7D51B71CAD2DC9B83B2F99B3726607D9CBE58B499A13753CE87FCDCE21C0AD0528ED0EF -B9B2C927F57C78C626248AA2B835A0791244C5896686A66173EC9F802C4C633A42B086334D2A4878 -0E53D00809247BE64E529F96AD2F8B3922A6097D414DDE1EC76F9552F9B8D58B8E34F359AD792B2B -E50C26DB05035E7497162E7C49C38D3CD9B98D620AA67492BE5AFCA3A81A7080185C7F0B5105223F -1FA77805502A2E8C5FEEA27699858D84A95842C5F2FB68686D59FE24091FCDDE139B6463BC6C7B1E -0E90D20A83651AF00C85797BB9F53ECEC1675C7EE636D0D9E77DBD8F89670F855EE4D4800FF3F695 -0EFF09BBF8A0DAF6B8242840CFA5BA73BEB95115F4A78BCC02D85ECCE0C0F2EF6F328AD1DD6CC049 -5A3315B414A4D61DA50DA46D7ACCEFF6EE56451805D26B0359AF193531F95F6589CEAD6FA041AF15 -3067F88A0A2FECD135C56682DB2B45A71D1FA737C064EE9A4F404BB72A70B3AF0330359393247EC7 -81512482579865240A23CD8479F21C2C44A119EBC4E81B308DD8AA86E60C3DD8ADA50E0DFE8308EB -1A7F201EDE8DCFDA405AEFB47E0E6CA7DDB376DCB21D37F7ACC4D3E9F26B03A8DE0E8940CA3A9E75 -963A389DF8038D2C486072F61C0CEAF500753C7A6352B1CD0338D9212B42A4D3DA23D5BDF44C27C9 -4B88A415A3242FFE2E1B332477A21D2B9CE075EE479C6E657A4D8874A8C53964229310E01ED4F3C6 -86FEF5258EDF3B464DD6FFD7F1CAF473BBE722D60FB14AB4918E93878A8AE4773930B8CEE110F476 -7F42A52D9304C55BE12846C911A10AB9B2E036BF9DFD597F5348D42233315FA80D0F563C388BC253 -2103F05E90DBF1923F229F980A2F4585C7A373511372D07DCBACA583099EA972C03E5AA67E663882 -6DB134564DB993CEEB6E7A6659C7C5C05C310267D5F8A24EEC2D5CC3E3F3C808E6D6068D1A57646B -37FABD98ECB7BAF99E7D9AC4414A491A73CA34C52F394352F6B5A15F0FC4D88622DAC694699C2464 -84ADAC3B1D366AFEDE2A2CD2042C90516A666A19A91C80248B11224BEDDF1A320E230739E755D098 -B6A67315535F4C187CFA67ED817A035056353FC859BF286317996FFFB478A2248B908FF12ABDE705 -402224A3EE5F463DD3D243875C84E02DB968ECA1CC52C75171EA50D6A88CA91327A7AA5795019F36 -C0A19C093A1C9D3723C7568F9D41F2E4FFB712FD47F897703D7A620B586B81936C84AAED61D84332 -B3BEBC4F95B796B93EF7A1F565C494F8A65EDB21E2EE18DC025522EF8E599887CA2836069CDDD889 -88E5862977B7472584303198CCE97EF9F9E1446D1F1F5ED1CFC666A8A0C3A03E1792EFB60A9B4065 -49E0DEDF6ACCDBD98742568B4735A747D8E5DE21E630125AE0C691D054E42199C15B1F80CAFA6E7B -B2005F374A9A5F9900ABB7409CCD50C3AFCCAB1214E6A856F7C7EBA89BC3291801E1343DA9DAD2C6 -ED075C8ECA1423B43E587AEC67E6145272814B3F191B3C285639F9E2D6E148A02DC2CBC0E054D629 -5CD05DBAC1950400A9189316F0265B86A732D302C5BEE8ED233768F237C62600CBAAFF3A110D5EFB -6CC7CA3B92D965CA7C5E8D3E64ECF239FE2507FC797FDBE54C1112B28D4DA44C60AB09D994C5BA78 -D663A2591934CC052BC70CD1DCA3325C66C9CB982E2039F5DB70C848D3DCEF655B1C2CD0CEC8865F -E8E1C0A267BE4F707ECE6F5A3DFCA3CC1EDF92C760439F51AA69A4C1801E96CA4D6EA4AD980258F3 -D15C893913ABCE09101984C61B91D603053E49A97CB82FBA707DAE8AF1D579FD69C8481CB7B712CB -CDDB4D287BE995E32C02B399602A08B9DD849039B5673F1930BEC7BF366EB082D2CA5DB2385C8CC4 -5BE3FC0E31820191A814EBA7C4F23B1938E6C4D800732787CD2CB97F762DFC85D4B798809B5F2254 -D826CA42B32695428D120298B44CF38494E56240B75DF1E41E46E53C44DC505452256DFEC819408D -605FF14D6C1F3F152F2FEA96EA0AB3B472D8704E06BE9F8C3E8395CAADD06D6DA033E81ADE5DC3B8 -3DAFF743C6E9E48716003D358DF63CD7FD3E2F727D1F2D0C29962F76D5C95ED44B6F08D052025A66 -5785F264A3D5F5593677B630E628B5EA81FB37CFFD7A30B7FAD226B6FDC82B0878AF4C0EC4F4243A -807B9839EA62BCBDF7C2E9B30A623876E632E084EBF4A21EDA04FC88A1C07021D0C72EC3E969D449 -FEB08E5826EC20E55B21EA71EA59F6E3B0710B0DDAB3261B4A2029ECAB68C19ADD5174E55D5E984A -4E5F38F592A302FEE6ECE732DDE841A28672C620CC5D687455A5C06FA9FE688394A04F96312ED025 -B7AA6FBCE2925F3AE559CC1886BEECDB70822E2E5CA3F732A87404B1536AAC469989E9610CFA440A -CE43875A70CA51F36CB6F629D9424C1E35A88F92D5DA3CD8CBAE6E8425A36968E21F4F30349749E0 -205BFF8D552837D6FC39532525370BBAC833F75F1854C93FC533A4AA53ADF7008173A70D94A4EBF5 -38EA9E62BCDA7C20E0A073BEE2EFAC34D2EF1D03BABD5147659E50B557045B2EB89DB303749B04D3 -F54B43FED612FCC68206E001A7AFE90230D9C12F74A32C7EDB5D0241DC3A5D51481FD7C8FAE08FEE -263FBCED7C7D911B3A303C835AF5FADFD218F61A9D6DE80485ABCA88200047B094441F7767B97A24 -E8C612590FA2407BAB1E8B56C71914EEF2355DD97CFAFCC192BC06FCE063D3D9D1A629AADC75E3BF -207234C208E7E30663EDD691043065C9CBC473D97C6D4DD3DFF59D6A9ABCDD4412C3128F603160AA -D8F81C6E7A4DCAF35F3A99B4EA10A34375B477C2BF846521A7EABD4D28078E9340452A198F3F5ACC -3DB7E3908939FF6E3709C6A3FD9889439A4AE3E10B618CC92E14B68429A3AD2C80940A1079452EC2 -66F254657BE7D79A2A24084AF73F6DF71FBCD32BF6913A3FAB25F977787F7BB0C3A3E8BAB38D7A2D -B0B4826950643DD1E03BD7DD1FB149A33862A89226B7CB454DAF613128C2075470E42E70A9444A8E -6ECA526345AB48E6F5160BA23B5BDDFDA6049EC44ED1461C7E0DD514B16E2FB285F72039DE3C7982 -EFD40D7F6C8E8F4CF35AC71B467BFC578002E8D2239A2FD2C4BCCDD8AF3D7DB1F4AE7F2D2E0811DF -9D0155BA6EDE50B5F052F14F6AB884FFF244D8806C07EBCB49ED22D85DF696995991A954AA97A1EC -D86ACD76E061B7541E87997FEF0657A826BD88EF3A4A5920462C6595E7A156F453291CA044CED810 -860C3B0149BCE73BECA713040664AD0591304106129600AF71317B0D2907839CEAC99515D357E980 -B1937B6E1200AACADA205421001F1B2F91753E80D2263C56AA164A74701A8D5FD28E46480B0DD963 -A683A1F355D7FB4463C7347C94EA5E2CA40B60B56297CB22D972C5BB10E56715A955605256C1541D -9F3BC5768A6F355CD3B863F0FA1A781EDB49368F51B29481CBB41D4AEB07AF9DBE8F52C5D0FF75F7 -FB6431D37D6AED84D78C778871CB0F715B4F07580F23B586C969C81B471FF6A6C7276F7E141E02A8 -584D4B9AB00E7BD643D2C3FAAA299B1F1E25048461952EA42D4882768A70DE46B213A287F8D31AC4 -6D5436F22A796C05D1FE50A9BC2A928066627A0D87DD57A3AD91DB446404B41557D1457873482005 -EA20916BBE46C613F456C849D46BA79D20627B446B2F49E3FA309AE14F8C420CFD94922CBC0FB9D3 -5A0F7DBEF577F1849A1A80E0011DA8AC082A8C6F61658E65AD177ABDF23EE17C8CF0D26B9FA3A6E9 -4837EB9E930336889767A8D7EA3CE980A8EA95528B004957BE6067CD9BD8E02A0F23CC1762CCA656 -D33412FF45E917FD4A03EB6E8C1F43FDB0A8965A33B4FD26BC24A20B304CA817E88495BA9B361A3E -933717FFB0271F7F70C5D3CBA1E86D0F51BF3ABA194DAF32C35C796627D00C7B2271ACE2463E37E9 -7B3C826CF3DB60028F240F9452CBE08F7EBCC5FDB1BCBB3C327A9F450B9E5671916101D6E3E5E458 -CA31F04D12F592F83BADA2C3683D3886AA3B403963AB5DBE220FEC00037A745839F67A3635DFD3BF -F08F367482962DED88ECF6322852D643A54D5D303EB04BFDDEE9BBA1EBCCBA7C653B3A613A8E719A -DEBE3CE1BD7E754E5F4977E863E3C2D388A65227B451D4F3A4F94E06513CBA4AC1F2F511613FF035 -611684CCC461599000E546E4D972CA6960E095A526E4735A23421A4C9B597ECE08AFA2753592BD16 -DED93255A1E33DEECE3C5EB77B94670E8137F2A4A4B98AC193258E7DEA5DB8408A806188F2D1DDC4 -40CCF0E9A6E2F0C78FDBD7B68DD4939D2458C1965BF8BED4564B32462FFF3EC892C03B11D3EA813F -AB4CFBE8D3016329C5B7E3DFED0F08284D44AA0B7A2F6BC96EA4503E8EF52A64C22BED6B452581AE -8FF8917D53976471941A9116A2D878FB2541B561767ABD4E31CCD8A590CA03494C62AFFD64EA0A1B -C779173DAD84999C7A8D844EB1259DE7BB5B25CD023537A474A524EBE4660B22568949E624D8FEA0 -AD37F4CE1EC75955EEFA49C6BF1803BE87E9C9865FF3F6B8525B8C15FE8835CA153D27E6C0FF0CA5 -1029A7A9185D25F0F14D86FC797DCC1F99EE97E2054B9C2A2E06FDBEB8DEF6CDD368BF23A858D9F8 -C1DEFDCEAF1B4A8DE5EAFC604CECCF0D285BE00AA912EAB66EFF4D37AD2EFE34853BBFD87CE09B18 -749B489943EECAE7887B006FB827D10191DAD18466CD1F86505879310A8B171F902EA0C26A388E13 -B53C700272CEE2BFB47ACB58247C13449C6BB9D01232C32517358F1A3DE064D43C18F8827D53789C -CF3CE2EBE78949A6ABFA1A6B8414CE360A5E22AFB7D1DCE6F5A06182C3B984B4F9BB1A905A9D5A14 -83750A1DE0A857CD5C06945EB7D4A2A6BF1237F32A154FDC06D51A703D44FE052FD3C53E9E8F417B -35D1C851F9203A8997521529F21AD8498F96930AA77EBAF82EE02A57BC77C792D9F220294B45F48E -A8FD94E01CD25645D36D168923562F3FDC93CB79DD4760DA0C103C2675722D7A1B79FCB4245ED12F -A0DB52492C9CCE58B333CFEE822812F7DCA68E802C451B5CFAEBAC608B950386B6C58239D1C62D62 -4DD5D15782FC552222CCA06DDF387B373E32C3C2864C63C768350C37283760F3515A5B0AFD66C48A -B522EB3E808C061F5CD6BD96CD18C9839D30508E7D4EDB88E8F11E31E10919B16B7971F06D7877A0 -58D8A4944C84FC6CAEDF3341B48B6E0D3C7B85D710E0C35F5B5053CF4B4798B3778CC28B2DC7AE0D -F3A49F9F3BCD8E95D746C35C3F47D68B8AA35D97AA08E711B5FBE70D1A623C82541EBDC51A827D0A -69E6C049087AD26F256EB7577F58CCFFBCCBA5A95D093DC29464C9A38DE95BC6B1853963B2DEB0B5 -7AD1248D6F1625E115EEB9510B5772AAE4E3C866657DB0B3BF0E0AC345E116F8D4976B770876FFE3 -748C36165522991F46A36F193DD1A1C94713673C7E4C81582391B636C72DE94CE6254374F99B623E -5686C13D8A8322E83E11BB0B0A896C6A8C2C4F756C5385CD7017F26D23F7C3EE97372C868C8C9155 -81723BB6B76B4C3CE8998E4FA6CA40B633DFDAA59BA902A4952DA90EC4FC3CF0F2676ACFA7F76F78 -236FA2DE10FD3545357215246BB7E527F277C28B353CC6D79DCEF21BCC8F77603CDD58A2CCDDBE3A -9802F941CED8E035313875319548C41992A2BE939A17CC109426E33825AE59BCD17CB19F50D972FF -CBE7D9B4B0BB095303D9DC9D406696C2508D6CE99E11CF00F6461147E97449ED5F486D480A86D3A7 -ACECB7E9A945984724EFC21C5079B1FD03ED803C2DEAFCE3327D2D7827715FD65D9506216C88B0FA -26935E95C64114A51919D419038B1A7E9C1E829FBFB53275093752DF19891A97F3CBF7719C1FD6CB -17019A6D2D25360ECA804C4B35172662CC4769D2B785C6C87E5A4ECCE31704E59F71263B7C3CAEC8 -ACB4C7426EC25F11A0042323EE6C3EEB04284DBAE2C770BC419DCE79BD4560AEA41571C3B595F525 -60191DC7A8FBF63D413A77A0905E517441B16C2B501EA2F9E99CC38D052679F288FDF1894542E3A6 -6989A0090185EB2E75134BFA3D9147C3DB8A621D9D35E37786853779E157B47F71626D6B3E633005 -9159C17596C1B87FE2B4FF47ED9D78FA4C2160077276C8B58CEF5DC030B4A5D83CF257096C047FE6 -4DE307C598B815058E72D5F57DF5C369E664E137DE29349E2F9DCD8C9F4EBA6E765B6327D7A20DFC -B20711273FD8091CBA605C4C494248076F7E03DF65A6A50164980BBBB708741E5BF6056E6F996DC0 -7FFF408C5B8EAB8DCEC315E92873228C805D4440A6470E3EE3983758DD211C9CECDBFAA4C9300CBA -00608A4B2404A3C7AF017A3B7E67F39F0B51ACF950D3E75CC7BC2B8D3480202FA958E8EE0B240501 -5232EE0D264C7CA02C18CA45CB3C2DE322D3EB7F00F9455DB6C5B1F4E59C3E95520EC36D7D903CBB -625D70B54BF6F8255E412604BBB29FEE026CC660577F91DB1DB4A613EEEFB20CF7AE3CD89D565AC8 -38416B01B5DE4FFA5550D17FB51FBBEBE21CF1D56038863EE931B90DEC2E211ED42BA92EC244D4CE -2C4EC5CA87A026992772DC2AF754FC982B94F36EA7B7BF75E0ECE90CBB2A6AA1A012E8898BD679C2 -3CB3827C35D5D02F0569C7AA82615D4AA67518ECF668D3B57D6EF1A8013424AC2268BA0D9A74D588 -79EDCF6382A89D397864940303EAEC45A38304BA8B1CB198967AE23EB81054BE74B16909A405E8A7 -799CEE3C270FE2A6DC50BD7370B6B2C8FDB9A87D88D5D40348D3984E39C693B6F4486D994778607A -80A3122872DD65E40492107C71C3CF708A9717E9EEFAFBDDC239C53AA9645B711038E59C8B861B37 -411AB2039BEDF9CFD00F08D9C5D76154427FF5DD39878CECC5D7BFB3F1F035087185C0981F3C2139 -BE84872FFAD3408531C4EA9387B89F5E3EC779E8850D50992DFDCF9132BC551E985943B07618AC10 -D1150451F0844C0DC41D6E17EB508DC8689EC726400D5A7F6FEB3CC7BCE05F09228B7CB2C5393664 -D8DD9A4B96B1020EF25D70AA2D91CAE93AFB5F2BF0AA18CA5C599FA1A708EF35BF8F7FFEC9AFC1F2 -42870D028B2B1459063B493943EF1283829783E1010242E5CF4DA39D93D506F3892936E7D6CF1124 -70A521D397438733D053944CFF12D6FFAE8246F20618684F263715AA98E15D72A526383E05C23214 -B78338E5B476F0981D90056E6E5D0DB66B1DF2298E597B2ABE1D817E18BEB056E65EDB4234342D96 -00470B1420C9210419D834E431B82F58608C87AC361A02D0F1FE4B470A3D71E0D21BB87E1023D428 -E23D596CB9E1A2184403A16E36E644BCCF9BBDE27290485057E62827283E7380AF786BF395B3961B -A5EA469C315763FA59E0F176EF81985F38B882DE56A74D128E256D1B89939728E55A92ABA21A6B78 -44FAC1BA7BBDD8B34A18194A2984B000380FE9F672E83EFDBF276FE797A325815B0F25CC95C97A9D -ACF56D583486305D7C9E51A7E337D14E3B900333EB38FD93A99587DA2341B10C059C71CE080FE753 -3C0F059FA40E560AF9C4A41A4BE6FB45846FF8F78165E10B4AD40F264BCF5596A1E8EF8CB6EA4B1A -3A5C69059AB1563843679ECB2511A90E8898F54295649CB73D277760D8D04ABACC7BCC6E777A0530 -E2067CCBC08673F9C8C178F9D672AC8A15E5367F0C5651B53E75E0CFA57C931746AE1A679C246D7C -9417F1CD89DDDBD1173C2F880B7B3847CBCCEBF99F7122E832D7C9BAFE2B54CBAA1ED48158DE3F36 -238B76B0E67644A28AEA996DDC006F6AC0242E4B667639E7523CBC90A0561193C1AF34481C2EF402 -EE43A82E1EBF4E3D601BB36B2D95CD93550D61CEE7A94E72F6D30C32C8F91A61E964B1F66ACFC398 -7F95D4028F116E9A9A8474AA29C1C1A984BE0E393BDC41DCEF6A6F1018DB60D52024899D8EB5D55D -324D73F39BFA47377B9E15B3B06A7585589FCF52A54684173E5183367E7B0952DC4BC2767C4C6247 -B1D6103E52BC7B7EA6298F454C5D97AC575F19C10ACDFF4E10C7D3755CFAB4200CAC545269FF1D8D -B0D607C7AD47F40DDF257AB4E7D0750577003C13E4941960C3DD7B0774DDAC18E8ABAF8F53E03CBE -F6D57B44F24CF821014C064278FD51B3427593D17694B4ABCE81F49CBB984C5878CDF0C38D1ED7FD -99B0B9A3BD8D8FF6219588B3B8FA59D0CDD1D9B2F65122AB45E48F1757467B9204926140E3C350C5 -A927A2E700173053EC35D3F1DA2D7258714C97FAA857F0898917BD94625C6D1E2D77138EFCAAAF51 -7B17FE187A2212C24A881A2C6A647DEF6376ED80AE4175C5EE80921F001995B44E49F0D33DD9075A -CF33BB03671C0BCC34AD5784AD1CDFED3A6D9BA103B3DDC1CC2DE74DBB576A0277715275218CD19C -A8899209125266D8BF1286F881DCC2C383749D1E768D670F4099F7DE959EDFE852583183C9111601 -2881A56A24AAF020EA45CD5F39660DEBCE30AC1C7B8CFC60387B1B0C3E361BE612FDFA9F01B7E4B4 -A18839A2C7E0E393EBC5AD9A8A4EBC316A740C1C295D9EF5F4DFFA0667F9582C0BB837B142C4CFC6 -B1798E9476D0631111033B8BA75A10FDC800E2AB1E0E829632F869CFE4737BE9E2800759EE0831DC -7D1195EAF80555771981DD6DC6606812D92CB8EF86447F5F6C6F626D0E265C67E52A6319189EE349 -D48E49DFE6A9E98F76C414A1E3217AE0A215A17E54AA498F4ECDC50242ACC7E2322F63BB2FF2189D -057E7354E32A3ED1803116176B9B9D0129930F919E2FEC280B2C8924E49E7BB75768A2EE1DA8ADBE -D4E3589906DF1B923AEF84C1BD327438B731012E69BB0D43A1842CB88BB54EA4516477F704CFEB28 -6E3EA483445AD4D74586FCF32E96D366901084365F693A53C5FB532FBFE7BC0CADC404C4985042D6 -8DBB90A6DCDA3531EE324D558A214F935CD9FCC9A0CEBE9B5FB0323F4B3820529599EF48EE068B5A -CE85004FEA2984F0A86F5AC9D56163BBFE1142B774148F1EB0A4DC89C3349052533A7DE66729DB24 -41B82F8F7360111DACF69293C9B281A0534F3E9E9224A75C49A832F28B2E497262475507B6DDFA9F -01CA0A6696E3F5AC7EA68595EBA0C2EB8A47813FF936D84AC1B23ECA7AA2862B793CCBB0DF9FDD49 -31BEF354CEC12FBF478559FEC29F81ADF4452E83963E56541D31F3691C93A50F0BBA5E9552C4F2A2 -3A6E53060729854A3DD71CC4308B91957DB19E66AAA18FA67055A950F1C2CFF78A03BC1A588CF624 -696068068719AFB1001C4581EE072113882D9052B21E355D401ED8CD24D067B99E616BDA5A0A5A93 -36FC499632B79FF2FD0DEFB096EF46B75E2D4E0F48DAEA239719FEC4D9A29818F5875FC5041A9EDB -D26CAF0ACE14CC80BA49BBA59E918EB3D8F1E541AA16026585A2F72DF7D83541816DE46981FB3EFD -0C30E458CFAD04C79421AB7C4925E23AEA07F9F018431C790002596D26BD9663B51B699DF53E4882 -CBC34EDE88EB55045B889B6062E35FD1E018BCE785157B85EC3B9CA6C85D4B16238275385B8285DB -012D8FB7C9F5B946A41D7A0FB878FF72C39683144D8A007CFF631B43748F2D5FC690300F9BC0C837 -006B92ECEBE0605E8C3A4A400E18AE8997D1B45FEE10068E247C647CF82C6DFBE5E881D511FFA687 -B7AEB78546BFD07D5F7EC242DCEF4930D8AAAD8C6152B6642AAC325963FD147F236BB850A9966573 -9D06CDBD7CA79A527DCF461E33F22BC9C5DB00DA2BD3DDDD8C99D99793BC98282AA8872FF96C3942 -85D82D9419EB78B6AE37A5F519397700F75D624A09BD255B576E955A323E784E8FC31131F003B0E3 -024A4F58FEF2A6C043796201FC425482E1155E229D1B2D43EF7B0D22322B22EF5C9A1BE026A1C3D3 -75EDAFF99597E1E5477952A4E8D2ACF5D014BC00DC2A272FA62B6983E27D228881E2EF2B8B95A681 -CBE90C5FDE16331C85222FE2A16F0A3C3000A63E2E21666C0C119F8AF89A543D37977069A5ACF155 -6324F05204CE8CAD50FF4FB630D9CBBFC324DEDA584AA56A99D3A76FF55BDC2C2EA3A021361CCD4A -83C7A5E2768D210FA6DE889FD48A39D679C94EC3C99A8D33FF11377DA7F6F1B71A2A05B302ECDE95 -4F26773F39AC881542F0D0969C3995C3519A8EF70B4220D86BF01BEECC6462855E7B686E1AFF1CA9 -1FB8FD8B4A69E10EE0C2AD94ADD44449506F9B6EF43641F2026EFF6E605C670560C2B74706FB949A -A7E8CC6A2D0D6207E457E7FD87EC1B9092DC68B9143947CC8ED14AFDDCBF8FDDA228A76847F96802 -E561F67CEEFDE45AE587673983FC04C96744DBAA83F2DC838D633943C75DCB9E6410474EB27B348F -26E505F0AB90878940E846C5E9F3C5FE8C3558C3236B1B88C405716949B8506841CABE1717474BB7 -C30DB91CDEE33B0F844811762FAEC535BDCF84C1C747CEF9B1FA61D2AFB5A81335BC42C06A94D7D5 -9B7EDE55BCF6F9867AEE107555CDD084B7684C2C87087475A39A9DA6347BE281CE5635A4D07865BA -98CE26C1465B1AB0343F49FF37B4D0CA9F3BB693D78DC3B21925CB996A038DCC172527FE57C07460 -EF39C07D4396E7FA970D9F22ABD21A9C794B64AD96762C7428F59A8757C36D6C4FFB23216195A04C -2A2C2E7B10EF7193931544D782FEE4B91E01119C5553BBC6252270A8D8C56DD62D448F5AD8DC69CC -B45E1F17F0AA1E445129DD00F000005B23D38DE93A3BE55A4C041947F36B4E4536E307D0180553F9 -2E46B743881CB5D5386C48C7D5F84C2BCD06B9C501F78C7EE61FA23516791FCF4DB278AF688A2E60 -10A56692AD92008497487EDFE4BD5FA083FA544138B20D6940020887E35D46E093B71F7A04A67460 -DC8116B4D4839625D7CA6959D6831CD93F81AC4EA2709036DD738364FDE71113BF22EBF13DFE1642 -E564701E6F0FFE7511EDF03FE448C2B28C64FB7D54B94CA576E481FA56B2B18AF10C71F699B6BFD4 -7459CDE1869D0FD306BF489A6F42E5B2F05CCF55BB6B9526973D19CB134CA7F13F1DB3716F8CC217 -73A832568C16250B5CDB16DF24BF81D49F5B37018BD310262EA7078107868AB0216CEC83CEFCAB1E -9F2C665A31585CA04DC01879CAA79AAA5AB201B516F7052B01B16BEE5606098393B0E5D9F9E5E3F4 -EB20F63C958E796DF41CF28839F5C62A0431648745D7837B519F3AA36BC6C08EF040CCF53D9B6D8C -0C7D1A84D707EC57A3C6AC9A62AB37251A01A5ED40FDEC6F5BE6E34C6A91D058319439778A2EE5D0 -363E2E1F33463C33327D05FFC0CBF08D5BC457C7230448972FB9B4D0D782BA7DBF10D3FFEF8BF523 -6EC16D4DD6D0D870D9D5EB5C64C9A46A4F583D4F831FEE74B0E5B33A09ABFD4444929BD8F638CD72 -EAB99CF2E9551DF427683964A592E49D186F285258C5D5F62196A98532421D73E3495F82695FEEC6 -E1952C562D546B28618FFAEEBEFF03A57F4D855021F85B0C7BC37FCC6DA9AECA099B646B99D41896 -09D3FF2D56422F8C37E97640293EC7C90E3380887836F4938FBF495CAC14FBA5648D89282D8D49D9 -1AF73ED36581139D8BD42551E263E830EA3C6EB381D85C42D74C50DB0CCAEC03F535ADE92128A016 -0E811C34748309AF7604919B66CD43EB5CA975302DCB6076FEB6BDD6FF55976FE990FB0CE9ABB11B -195403FB26E3D6C6A0DE1C5BE79E171A61E21F79EE8DBE7A832519813EF6B33EA098C2C32ADEA219 -AB2AAC8B093F40000995539D1276D5F2EF84CCD099B71FE4269BDBDB6A8D59C86F7D2E3FBCCF8773 -D0FAE97640BC1AD43CB4B992BFADFB09DBD0CAAEB8CD9DA264187C4F97300E9A6C9DEED5525479E6 -05C65AE336CBBDF4E5D7F79AD098F977285E065579B748FEAA97F2A753E1F962FCAB68D72BAA8EE4 -FF6691C23E31BC0F3E981A96FB440404856AE1AB32A7205B17D411D8F21C8C93B704D07EC594422A -BC368CDA2B1610CE6A973F4474E12B78B532666797F5755D269772C9F5400B3BFC6C58395D38527E -2CCCF29B56123F7DCEF3BDE5DC1DFC5B0293BB125085B1D2D929BC3EE84F4FAD571A4991C3DEE03F -2DB3A3097E52B1A7D5C73CCB6148EAC62E8E36DE9A71C57638C6E4D5D9DED18174E8C390E50B4A5B -913C074EEAEBE390B214B3A68F02862B9A296DB4B409769649E51D738CBBDFB7702E15C73C2AFC6B -C37CE15171F4E822CF20EFE55D9F061AA43E648989628FF79E65932390CBB15D8E621333B18B11C3 -BDF96F841D7434E01AD501FEA964A75B248A35CD9DF9A37E48A1E5A09C624B93CE44F0042FA00D7F -9EE89B9F7AB785E9C718CF6E7228F743271C2C9BBA17E5208B920E44E765D99D86650EB454B0FAAA -112753AA1BD3A24239E9C5FC47EEB1547AC9D23731B8DC48B9707830DAEC60C8D3790BBA1120F776 -4EFAC542CFFBCD5C05F9510B27B2534B704ECD36C8B041FD49A96881302FFF5B0163A2DD09C751D6 -D6AFEA9170A4F4C4AB8D46E62F763FE1BDA51DD1CE4A27E772F3A2869155F762FF26B7AA6FCFA4F1 -292E56F03AAB6322BF867E7710C34D43B5D85B45AA68014AD7879EED051B1933E491496E3E26D9AA -8B80A07BF2B94F1077E84A9726F08199887D66DE7A307BF33C30DD9CF3DA188088C03B2BAD09A217 -6B110DB2C868B53DA9A66C85737BA66C93C58A259860E294AD0191E3A72C73F40B0BD98699AA08DA -F03587B78F391F3A4313C58D9F29B53C70785637BD0C58310109C54091AB0A34CBB0C478613A7AC0 -FB8F0A8B4645AC966395D8BA775262CD291136AFFDDF01C1D83DD4EB3B59CCAD18057FE7D92A8CD4 -A58F22508D9FD7CF356571F701BBB23E749BDDCBF8A317FDA0AEFD952BB18545610FFAD3AC143D35 -1B8DB3F66293375E0E50235F0D0466932181D377EDD32A5F0FFA4E22B5A0CB4F343D9A7E4A342E9D -09DFF6C697630CD3971802C277A5590B8CA94BDE6B38446C794D072BBCCB724D5BC208EEF1B018D7 -39373BB910D668882CAA779C2D686081DE6A2606417B54D7C20E0E7F722648D893E4EDBAE8F00D6A -6DA3712F91AE860C756D1127D133AB828E9D80023B50B162C5A1C5CDF70CCB3FDD7EA060ED20838B -E1E50C4094C9E79E1A0187CDF780CAF45A725964F004253E034C5BE46BBF89D94631F1A33BAA35B8 -4FA2A9D08481C6674126CD96ED05DCE48BDA069D902D6836D5DFBA701DC0F98A863E64F0E312145D -8DC0B77F25B43AEC729A1243B45B08CA228DD6101CAA2AC5ADCC8EFF84A4CA3F254176C2CC711EE6 -C273835D0FD3528ECA2A976B88E51FE347FDB60F32370B66D338931D6581630ED586F349C638960C -31AE4204E89521A96E1219E696B913DEB2AAB7A3B022D06F34FDFCB810A04E60A4FEBE284C2F063E -0AE9EDF87704921CCFA193BDC912B747E13570066223A49F1F6E2AF0D4D65DA04CA876FF7A462FFC -9C0BA2CC545C3BD36DBE762F32B2D6BE5867C59F479195C92440DC165098B74EA5C3AD93CDF2D410 -B04C16BC7801E7956F4E5107450787AA592493171C3628E6B8F49D4F8429EB98DC52EF025F001387 -BC1A7093F7A99F10B5D2D7DD8BBB393BF6E56F08F4F7FA1A343F220D5A1EAE7168C74D41BE1DC1A8 -3BD65B72B982F4F7B34F24F97F9EC9A91011064031FACFF2A14921A32024385F4E061CD07D152E74 -1BF97156D951A342488FA7F5EF934CCAD13E2753A0AB7A1F565C2F7F6B349DF03BBC25BBD972A9AD -F809BB5C5048A8CCEF9297B2ED3324D18867F293CC66E88B3A39D107B610DFE79A3B4E83A96D3D52 -A17FE8A62C9FDD271130148366942C9CE57558D023DA5F7501319EBFA33DE9E6D1E76D7C20DB8A09 -B657839DA99F3D8143F1EE6253A3295C9651FA4366547893C2DC7ABCBF4BB7609DE5D001E0A36D9F -FBE01F7D0903B3208AE8547E2E5F14EC1AF4C2535CA8F4EA37E3F3CE172C7A1E8308995B1CC23E6E -81190246BCAB6E755BF868D449BB02A2AA87C44C9CC0F571ADC72547CEECBE104BB274B8AC16DCB7 -5D5F458D356466B921ACDEEAE384E2EB1DF6EF393B41B9747F0A4FAEB4AF1928D9AD6FB7E06FDC62 -1E4C6FC98CFB43F88584BD55D9B97CC9549093EDE586912161931162B1B1D52D0443260DABA02AF2 -B4432100D5506546013DA703573FA8013685CC798CE501960093DED713FFCCF89CA2B9106390198C -29A00864108CDCC1984A8BAB53919028C01B26ECC7925E38CBE6CCA8978EE21C2B06E7B3E48FBA97 -8E2A7D186E563C088F84AA23178B60E4729EE87D67B1091F3B6973676C1CBFE6530EB773C62E2C24 -97014AB0E8B71A1F4E86A378AA26591511BEE3CF3D64C94848582E1354E1605B6457823F2C5E640A -D3802946BB2E7E8E594E8C04B430C2385DD40746CE8534F50842E74D7115F3DB0C72D1C9C607C657 -3B094AEB73B7A79876CFFC3E2F8C9FEAAA07D3BFCE05B61F7749A8793BE90CCCECA2D7077F25E899 -D3331FE161A7E86C842495D584C6E4A0880B2951D8A13B88C4672080A0B1BE36BF47C3ACE7288CFE -41A8C1BAA6F0814A947FBD6B3AA72B6C73A8C578CA51CCC96F2352316C467BB960E981F2B6485BFB -44B577E71EFDA16E7405954BC7C9F0759F5A9F1EBCD2FA9CC9648D5831A68887F41B15081A204C24 -B4B992A231DEF9E698D4C3A25B6F5474F5BE6A601F2D337A58A0D21FF37FD91EB86D1D738893A03A -69F0CD743F611CDFFE69DB2C6ED0E4611D56F803BB0DC06E7FE85A303839612707647B1BE9FAF8D6 -84122CA9E5CB8BDE2936D3F4FF254D31529D7538BBD4D35539489F9E7316F24214B996BCDCF1818E -749A71CF0E8845AA1E2A58AA62A48E02BA4564625D20AA220EE719608521D7D7A7FCA0BD8904A401 -9819D371F3F59D46C1354E5FC1A6E5F79B20CF4ACA2BF0F2DE73DA193A6F9ACBFE0B4731C4BCEBE6 -D96FE822965DE965232282A3A130361F188B3AABDA95A8A2790D9240BE008B6A6DE4BBFCADA05B67 -86B9BB8E0DFA0C30043A3B07ED46277E07B9808422C8ED16758B9C396F4EA929D769785B2C9568E5 -70A83B989B25CE200F1727D41E2B702E7F88F1784F4C83FA60A74EB26B2DA95126E508ED519A61CC -151DB6804F61826C5F86D8FA89D06E526FED97A0DB88EDB432FF32C1ACC9B622EEDF601081AF7B96 -3C9CFC1D13E4A9C74FEA0A1C8E3D8653CD92A944D4CA6B0D306619AFD503506D77732D6514F604BE -4610C2560931BDE0B40939BC1D126B0E97F72AE1B4A9252123B54F7A27E0CFA4425B4546526FD741 -CA77952B10D13E0AC2E32006A903808FF0CD013F936238C74CC75FD915244C56A8412F37F0134840 -347699508D6F3D7F3203A25B7C70100719582CD588590EE34B3AB13E255B613A6D00386A0104CC5E -D2C646F09A88888D3751651D5646C5227A3C80E8DA1B0A331121DD2429F1F4775D30564DFF47D01B -BE2C6C72CE4D1FD9A2077C04D2B0274B8916F6A9D1A4A6964A534F47CF241D5A8E34B23F85BE9ACF -FC2FEA961F277539F215F8728D6788F67BEAF45502839BCF23D8763C3949352F00C579A9A4FC408E -C625E310DAE61512DFE6844E82D36A2F81709E1F05B38AE9C222ED62C961EE63593CED7AAF73CE2E -D3667740C77B309B93EEFE1B4BA65D48575A66BE86743DC9E5D3C2FF418D11F7F211B86E827EE1DF -C3613E7498030F07050524536D1F8A94DDB6698BE7B963C55CB3F74B676CD815A7B3DF4B1A0EA2BE -1B0B9A11FFBFD5B1FA49668AEE14629316AF436A0821C20BEEF7B3480847934A99F6D85B68F4DDF8 -859A754E009428AF89A90D1852C220A607FF0806E8080726EDC94D691D214B4521C147C4273AEBDD -BB4A697EF16448CD9B2FC95293305858DECFD406B89B9F3FDAE2AC579E80CF321EBAE5701FB2F7CA -D8ED04B4A63115886D45D6120F69AEF1A21D80AD3C2D35D2899F1902242B96CD349E0AAADA40F7A1 -1282B6B52BDD97708E58DC5E2D22D1153E5FA3F3B300BCDFAF98DEC2F4E3C82A1C85F985735F3987 -4F557579F422664E07CBE19DA680EFB0FC82C323EC5C4644C51709AC8D674608A8043C91E6C7988D -430F10BA6CE1FC7FC0604FCD8F723895250AEC36CC35B3FA14FE2A0D24095DCC30B2093F2298F5F0 -A97676A0BE66C3DC9ADACFE2FC0F721A20E945AFC1096A619075D5E9A264C796EC6C90EF1AEEA8DC -089B44FFC13D27CB2370070A52D4416C53F364393E46EDD7EDE00799960CE6E0D57E4909E88ADD64 -BDD2B0EBE2D73FA6ACF8B40280DAA0637E705C65AABD523B8815F22F23E9FF81E7829C7E4BC980C9 -143AEBE1A04DC0D253396BBB7268BD5AEEA356B610D5DCEE03135E00AE34388251F31714A1C40E18 -2652C48CDA2211A22CB6F02490E69A44CECB169754C53B16028D352E0119F5D5FAE0BD7EA1CDA647 -12A6147374B64244E21E9EC9F0D1381AD22D5B6212B26C3F9AA5F6045F25DD9F5EB4489EA39B1945 -331AC70510C5752557DE21D0A6CFC1EB10A98FA867B76DA6E4249469F591FD154D39E89364A43DB0 -07AA0D7A911CFAE6CE2B557997FBC44F55A27F622BD7B8B10EC9F5D10F2649A646FD964AE1B111B3 -5B46A252C4DEE44E7426EB5739F24E8A390694597DB3A1FE7800C97E59558322F0E49A0CCE2AD94B -1E2D1026AFA771723E3F523916F55ED866C9FB4A2F759651C613A2CFF362028CDF9D38F05D4C7C60 -24C533E930B64B099FB1AF04B01F5FB9CA6867E6EFF55A772C5391831059987E10CBF987E3F378E0 -1329F73D54DC0484177D3C3C06F67397955FF1CA4EF8AD1606B70455255D631A7D6EB92BFDBA14A0 -FF28B2ACE7E81AD666EA9B3A0F5A6BA3B5DFE35044FA4B3D8ED956009C60E98CC132F2E84967F4A9 -8A67B336D5EE7CAF7DD1F74D1FA08619941361FA7312CF225D89CEF97E864C8369EAFAB94D97F056 -5505D825972B754F6729596EEA91210B75DD8F645382ACE36DE60819A02B3B48DD00F5485F9264F9 -FA926D732E2C267B0BE8CA98526F124F97EFDB86132C5EF16B103908172FC51F286FFE45FF253512 -E0033F037FF182BA536A9EB2DF2D1DB257D9C86C46E1B002FB32AC70CA9462E6EB48994752CEBCE3 -9F08ABD4F4B0889283E55500702185A841E328 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000000000000000000 -cleartomark -%%EndResource -/NimbusMonL-Regu-iso1252 /NimbusMonL-Regu ISO1252Encoding psp_definefont -/Times-Roman-iso1252 /Times-Roman ISO1252Encoding psp_definefont -294 310 moveto -0 0 0 setrgbcolor -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<4558504F52545F505953435249505453202B3D20534D4553485F6669786174696F6E5F68657861 -2E70792C20534D4553485F666C696768745F736B696E2E7079> -show -294 398 moveto -<69666571202840574954484E455447454E402C79657329> -show -294 442 moveto -<20204558504F52545F505953435249505453202B3D20534D4553485F626F785F74657472612E70 -792C20534D4553485F626F78325F74657472612E70792C205C202020> -show -294 486 moveto -<20202020202020202020202020202020202020202020534D4553485F626F78335F74657472612E -70792C20534D4553485F6669786174696F6E5F74657472612E70792C205C> -show -294 530 moveto -<20202020202020202020202020202020202020202020534D4553485F506172746974696F6E315F -74657472612E7079> -show -294 574 moveto -<656E646966> -show -295 691 moveto -/Symbol findfont 50 -50 matrix scale makefont setfont - -show -370 691 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<46696E616C656D656E74> -show -595 691 moveto -<20> -show -607 691 moveto -<3A> -show -294 797 moveto -/NimbusMonL-Regu-iso1252 findfont 42 -42 matrix scale makefont setfont -<2E2F6275696C645F636F6E666967757265> -show -294 841 moveto -<636420534D4553485F4255494C44> -show -294 885 moveto -<726D20636F6E6669672E2A> -show -294 929 moveto -<53414C4F4D45325F524F4F542F534D4553485F5352432F636F6E6669677572652096776974682D -6E657467656E3D6E657467656E5F696E7374616C6C6174696F6E5F70617468205C> -show -294 973 moveto -<967072656669783D736D6573685F696E7374616C6C5F70617468> -show -294 1016 moveto -<6D616B65> -show -220 1128 moveto -/Times-Roman-iso1252 findfont 50 -50 matrix scale makefont setfont -<6FF920736D6573685F696E7374616C6C5F7061746820657374206C61206469726563746F727920 -6427696E7374616C6C6174696F6E206475206D6F64756C6520534D4553482E> -show -280 277 1 308 rectfill -2125 277 1 308 rectfill -280 277 1846 1 rectfill -280 584 1846 1 rectfill -280 763 1 264 rectfill -2125 763 1 264 rectfill -280 763 1846 1 rectfill -280 1026 1846 1 rectfill -showpage -grestore grestore -%%PageTrailer - -%%Trailer -%%Pages: 7 -%%EOF diff --git a/doc/salome/tui/extra/AddNetgenInSalome2.sxw b/doc/salome/tui/extra/AddNetgenInSalome2.sxw deleted file mode 100644 index 15381905dbdc2d5bd91fb9f039013a733fea0b1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13363 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 diff --git a/doc/salome/tui/extra/PluginMeshers.html b/doc/salome/tui/extra/PluginMeshers.html deleted file mode 100755 index 64c368819..000000000 --- a/doc/salome/tui/extra/PluginMeshers.html +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - - - Main Page - - - - -   -
                    - - - - - - - - -
                    -

                    -

                    -
                    - -
                    -
                    - -
                    -

                    How to add your own mesher

                    -
                    - -
                    -

                    (as a set of hypotheses and algorithms)

                    -
                    - -
                    -

                    to the application.

                    -
                    - -

                    Table of contents

                    - - - -

                    1. Introduction

                    - All hypotheses and algorithms are available in SMESH module via plugin mechanism. -Such approach allows easily to introduce new hypotheses and algorithms types -to the application. Also, it makes possible the customization of available -hypotheses and algorithms list for different users without recompilation -of sources.
                    -The goal of this document is to describe the process of creation external -mesher plugins. -

                    Back to the contents

                    -

                    2. Implementation steps

                    - -

                    -

                    2.1. Mesher plugin package

                    - -Create your mesher plugin package which will contain the sources files, e.g. - MyMesherPlugin.
                    -
                    -Back -to the contents
                    -

                    2.2. List of available hypotheses and algorithms

                    -Create XML file to describe all algorithms and hypotheses, provided by your -plugin package (see SMESH_SRC/resources/SMESH_Meshers.xml for example).
                    -
                    <meshers-group name="MyName"
                    -               resources="MyResourceKey"
                    -               -server-lib="libMyServerLib.so"
                    -               gui-lib="libMyClientLib.so">
                    -     <hypotheses>
                    -          <hypothesis type="MyHypType1"
                    -                  -    label-id="My beautiful hypothesis name"
                    -                   -   icon-id="my_hypo_1_icon.png"/>
                    -          </hypotheses>
                    -     <algorithms>
                    -          <algorithm type="MyAlgType1"
                    -                -     label-id="My beautiful algorithm name"
                    -                 -    icon-id="my_algo_1_icon.png"/>
                    -          </algorithms>
                    -</meshers-group>
                    -

                    - -
                      -
                    • Attributes of <meshers-group> tag:
                    • -
                    -
                    -
                      -
                    • value of <name> attribute is used to collect hypotheses/algoritms -in groups; you can also use this attribute for short description of your -mesher plugin.
                    • -
                    • value of <resources> attribute (MyResourceKey) is used to -access resources (messages and icons) from GUI (see paragraphs 2.4.2 and 2.5); in the current implementation -it should coincide with the name of plugin package; this limitation will -be eliminated in the further development.
                    • -
                    • value of <server-lib> attribute describes the name of your -mesher's server plugin library (See paragraph 2.3)
                    • -
                    • value of <gui-lib> attribute describes the name of your mesher's -client plugin library (See paragraph 2.4)
                    • -
                    -
                    -
                      -
                    • Attributes of <hypothesis/algorithm> tag:
                    • -
                    -
                    -
                      -
                    • value of <type> attribute is an unique name of the hypothesis/algorithm
                    • -
                    -
                      -
                        -
                      • It is a value of _name field of your hypothesis class (see paragraph - 2.3, implementation of constructor of StdMeshers_LocalLength -class: _name = "LocalLength")
                      • -
                      • It is a key to each certain hypothesis class (see paragraph 2.3, implementation of "GetHypothesisCreator()" method in -StdMeshers_i.cxx)
                      • -
                      • It is a key to each certain hypothesis GUI (see paragraph 2.4, implementation of "StdMeshersGUI_HypothesisCreator::CreateHypothesis()" -and "StdMeshersGUI_HypothesisCreator::EditHypothesis()" methods in StdMeshersGUI.cxx)
                      • -
                      • It is a key to each certain hypothesis icon in Object Browser -(see paragraph 2.4.2.1)
                      • -
                      -
                    -
                    -
                    -
                      -
                    • value of <label-id> attribute is displayed in the GUI in -the list of available hypotheses/algorithms ("Create Hypothesis/Algorithm" -dialog)
                    • -
                    • value of <icon-id> attribute is a name of icon file, which -is displayed in GUI in the list of available hypotheses/algorithms ("Create -Hypothesis/Algorithm" dialog)
                    • -
                    -
                    -Note: All attributes values are accessible in your GUI via HypothesisData -class (see paragraph 2.4.1)
                    -
                    -Note: The environment variable SMESH_MeshersList contains the list of plugins -names, separated by colon (":") symbol, e.g.:
                    -
                    -     setenv SMESH_MeshersList StdMeshers:NETGENPlugin
                    -
                    -Please, pay attention that StdMeshers should also be included into this environment -variable, if you want to use standard hypotheses/algorithms, provided with -SMESH module.
                    -
                    -The SALOME automatically locates XML files, searching them in the following -directories:
                    -
                    -    ${<PLUGINNAME>_ROOT_DIR}/share/salome/resources/<pluginname>
                    -    ${SALOME_<PluginName>Resources}
                    -    ${HOME}/.salome/resources
                    -    ${KERNEL_ROOT_DIR}/share/salome/resources/kernel

                    -
                    -where <PluginName> is a name of each mesher plugin package
                    -
                    -Back to the contents -

                    2.3. Build server plugin library <libMyServerLib.so>.

                    - -

                    2.3.1. Define interface to your hypotheses and algorithms.

                    - -
                    Example: SMESH_SRC/idl/SMESH_BasicHypothesis.idl
                    -         NETGENPLUGIN_SRC/src/NETGENPlugin_Algorithm.idl
                    -
                    - -

                    2.3.2. Implement functionality of your hypotheses -and algorithms.

                    - -
                    Inherit corresponding classes from SMESH. -

                    Example: SMESH_SRC/src/StdMeshers/StdMeshers_*
                    -         NETGENPLUGIN_SRC/src/NETGENPlugin_NETGEN_3D
                    -

                    -
                    - -

                    2.3.3.Implement interface to your hypotheses and -algorithms.

                    - -
                    Inherit corresponding classes from SMESH_I. -

                    Example: SMESH_SRC/src/StdMeshers_I/SMESH_*_i
                    -         NETGENPLUGIN_SRC/src/NETGENPlugin_NETGEN_3D_i
                    -

                    -
                    - -

                    2.3.4. Implement being exported method.

                    - -
                    GenericHypothesisCreator_i* GetHypothesisCreator (const char* -aHypType) -

                    <aHypType> is a value of <type> attribute in the XML-description -file

                    -
                    - -
                    Example: SMESH_SRC/src/StdMeshers_I/StdMeshers_i.cxx
                    -         NETGENPLUGIN_SRC/src/NETGENPlugin_i.cxx
                    -
                    - Back to the contents -

                    2.4. Build client (GUI) plugin library <libMyClientLib.so>.

                    - -
                    This step is required only if your hypotheses/algorithms need -specific GUI for their construction.
                    - -

                    2.4.1. Implement the required GUI (e.g. construction -dialog boxes).

                    - -
                    Example: SMESH_SRC/src/StdMeshersGUI/StdMeshersGUI_*Dlg -

                    Note: all data from XML-description files is accessible in your GUI -via HypothesisData class  (mySMESHGUI->GetHypothesisData (aHypType), -see SMESHGUI_Hypotheses.h for HypothesisData definition)

                    -
                    - -

                    2.4.2. Provide icons and messages for your GUI.

                    - -
                    2.4.2.1. Implement resource files
                    - -
                    MyResourceKey_icons.po and MyResourceKey_msg_en.po -

                    Example: SMESH_SRC/src/StdMeshersGUI/StdMeshers_*.po
                    -         NETGENPLUGIN_SRC/src/NETGENPlugin_icons.po
                    -

                    -Note: ICON_SMESH_TREE_HYPO_MyHypType1 is ID of icon for Object Browser for -hypothesis with type="MyHypType1"; ICON_SMESH_TREE_ALGO_MyAlgType1 is ID -of icon for Object Browser for algorithm with type="MyAlgType1".
                    -See paragraph 2 for definition of MyResourceKey, MyHypType1, MyAlgType1.
                    -

                    -
                    - -
                    2.4.2.2. Define environment variable SALOME_<MyResourceKey>Resources
                    -
                    It should point to the directory where resources are situated.
                    - -
                    Example: setenv SALOME_StdMeshersResources ${SMESH_ROOT_DIR}/share/salome/resources/smesh
                    - -

                    2.4.3. Implement your Hypothesis Creator and being -exported method

                    - -
                    SMESHGUI_GenericHypothesisCreator* GetHypothesisCreator -
                    -   (QString aHypType, QString aServerLibName, SMESHGUI* aSMESHGUI) -

                    <aHypType> is to pass a value of <type> attribute in XML-description -file;
                    -<aServerLibName> is to pass a value of <server-lib> attribute -in XML-description file.

                    -

                    Example: SMESH_SRC/src/StdMeshersGUI/StdMeshersGUI.cxx

                    -
                    - Back to the contents -

                    2.5. Provide icons for object browser.

                    - -
                    If your hypotheses/algorithms do not need specific GUI, but you -want to provide icons for object browser, see 2.4.2 paragrath.
                    -
                    - - -

                    2.6. Setup your SALOME environment.

                    - -

                    2.6.1.  Add your plugin to the LD_LIBRARY_PATH, -PYTHONPATH (and maybe PATH) environment variables.
                    -

                    -
                    setenv PATH <path-to-my-plugin>/bin/salome:${PATH}
                    -setenv LD_LIBRARY_PATH <path-to-my-plugin>/lib/salome:${LD_LIBRARY_PATH}
                    -Setenv PYTHONPATH <path-to-my-plugin>/lib/python2.2/site-packages/salome:${PYTHONPATH}
                    -
                      
                    -
                    -

                    2.6.2.  Set mesher plugin resources environment -variable

                    -
                     This enviroment variable is used to set meshers plugins -which should be loaded by SMESH module (see 2.4.2.2 -paragraph). Add your plugin to this variable. All plugins are separated by -colon (":") symbol.
                    -
                    -Note: If you use runSalome.py script from KERNEL package to launch SALOME, -you may not to set environment variables, because this script sets them itself. -All what you should do is to add <plugin> section to your ${HOME}/.salome/salome.launch -file for SMESH module section:
                    -
                    -...
                    -<modules-list>
                    -    ...
                    -    <module name="SMESH">
                    -        <plugin name="MyMesher"/>
                    -    </module>
                    -    ...
                    -</modules-list>
                    -...

                    -
                    -Back to the contents

                    - - diff --git a/doc/salome/tui/extra/PluginMeshers.txt b/doc/salome/tui/extra/PluginMeshers.txt deleted file mode 100644 index 71e58b2e7..000000000 --- a/doc/salome/tui/extra/PluginMeshers.txt +++ /dev/null @@ -1,188 +0,0 @@ -####################################################################################### -# File : PluginMeshers.txt -# Author : Julia DOROVSKIKH -# Module : SMESH -# $Header$ -####################################################################################### -# -# How to add your own mesher (as a set of hypotheses and algorithms) to the application -# -####################################################################################### - -1. Create mesher plugin package, e.g. MyMesher. - -2. Create XML file to describe all algorithms and hypotheses, provided by plugin package - (see SMESH_SRC/resources/SMESH_Meshers.xml for example). - - - - - - - - - - - Attributes of tag: - - - value of attribute is used to collect hypotheses/algoritms in groups; - you can also use this attribute for short description of your mesher plugin - - - value of attribute (MyResourceKey) is used to access resources - (messages and icons) from GUI (see paragraphs 4.2 and 5); - currently it should coincide with the name of plugin package; this limitation - will be eliminated in the further development. - - - value of attribute describes the name of your mesher's - server plugin library (See paragraph 3) - - - value of attribute describes the name of your mesher's - client plugin library (See paragraph 4) - - Attributes of tag: - - - value of attribute is an unique name of the hypothesis/algorithm - - * It is a value of _name field of your hypothesis class - (see paragraph 3, implementation of constructor of - StdMeshers_LocalLength class: _name = "LocalLength") - - * It is a key to each certain hypothesis class - (see paragraph 3, implementation of "GetHypothesisCreator()" method in StdMeshers_i.cxx) - - * It is a key to each certain hypothesis GUI - (see paragraph 4, implementation of "StdMeshersGUI_HypothesisCreator::CreateHypothesis()" - and "StdMeshersGUI_HypothesisCreator::EditHypothesis()" methods in StdMeshersGUI.cxx) - - * It is a key to each certain hypothesis icon in Object Browser - (see paragraph 4.2.1) - - - value of attribute is displayed in the GUI in the list - of available hypotheses/algorithms ("Create Hypothesis/Algorithm" dialog) - - - value of attribute is a name of icon file, which is displayed in GUI - in the list of available hypotheses/algorithms ("Create Hypothesis/Algorithm" dialog) - - Note: All attributes values are accessible in your GUI via HypothesisData class - (see paragraph 4.1) - - Note: The environment variable SMESH_MeshersList contains the list of plugins names, - separated by colon (":") symbol, e.g.: - - setenv SMESH_MeshersList StdMeshers:NETGENPlugin - - Please, pay attention that StdMeshers should also be included into this environment variable, - if you want to use standard hypotheses/algorithms, provided with SMESH module. - - The SALOME automatically locates XML files, searching them in the following directories: - - ${_ROOT_DIR}/share/salome/resources/ - ${SALOME_Resources} - ${HOME}/.salome/resources - ${KERNEL_ROOT_DIR}/share/salome/resources/kernel - - where is a name of each mesher plugin package - -3. Build server plugin library . - - 3.1. Define interface to your hypotheses and algorithms. - - Example: SMESH_SRC/idl/SMESH_BasicHypothesis.idl - NETGENPLUGIN_SRC/src/NETGENPlugin_Algorithm.idl - - 3.2. Implement functionality of your hypotheses and algorithms. - Inherit corresponding classes from SMESH. - - Example: SMESH_SRC/src/StdMeshers/StdMeshers_* - NETGENPLUGIN_SRC/src/NETGENPlugin_NETGEN_3D - - 3.3. Implement interface to your hypotheses and algorithms. - Inherit corresponding classes from SMESH_I. - - Example: SMESH_SRC/src/StdMeshers_I/SMESH_*_i - NETGENPLUGIN_SRC/src/NETGENPlugin_NETGEN_3D_i - - 3.4. Implement being exported method. - - GenericHypothesisCreator_i* GetHypothesisCreator (const char* aHypType) - - is a value of attribute in the XML-description file - - Example: SMESH_SRC/src/StdMeshers_I/StdMeshers_i.cxx - NETGENPLUGIN_SRC/src/NETGENPlugin_i.cxx - -4. Build client (GUI) plugin library . - This step is required only if your hypotheses/algorithms need specific GUI for their construction. - - 4.1. Implement the required GUI (e.g. construction dialog boxes). - - Example: SMESH_SRC/src/StdMeshersGUI/StdMeshersGUI_*Dlg - - Note: all data from XML-description files is accessible in your GUI via HypothesisData class - (mySMESHGUI->GetHypothesisData (aHypType), - see SMESHGUI_Hypotheses.h for HypothesisData definition) - - 4.2. Provide icons and messages for your GUI. - - 4.2.1. Implement resource files - MyResourceKey_icons.po and MyResourceKey_msg_en.po - - Example: SMESH_SRC/src/StdMeshersGUI/StdMeshers_*.po - NETGENPLUGIN_SRC/src/NETGENPlugin_icons.po - - Note: ICON_SMESH_TREE_HYPO_MyHypType1 is ID of icon for Object Browser - for hypothesis with type="MyHypType1". - - ICON_SMESH_TREE_ALGO_MyAlgType1 is ID of icon for Object Browser - for algorithm with type="MyAlgType1". - - See paragraph 2 for definition of MyResourceKey, MyHypType1, MyAlgType1. - - 4.2.2. Define environment variable SALOME_Resources to point to the - directory where resources are situated. - - Example: setenv SALOME_StdMeshersResources ${SMESH_ROOT_DIR}/share/salome/resources/smesh - - 4.3. Implement your Hypothesis Creator and being exported method - - SMESHGUI_GenericHypothesisCreator* GetHypothesisCreator - (QString aHypType, QString aServerLibName, SMESHGUI* aSMESHGUI) - - is to pass a value of attribute in XML-description file; - is to pass a value of attribute in XML-description file. - - Example: SMESH_SRC/src/StdMeshersGUI/StdMeshersGUI.cxx - -5. If your hypotheses/algorithms do not need specific GUI, - but you want to provide icons for object browser, see 4.2 paragrath - -6. Setup your SALOME environment. - - 6.1. Add your plugin to the LD_LIBRARY_PATH, PYTHONPATH (and maybe PATH) environment variables, e.g.: - - setenv PATH /bin/salome:${PATH} - setenv LD_LIBRARY_PATH /lib/salome:${LD_LIBRARY_PATH} - setenv PYTHONPATH /lib/python2.2/site-packages/salome:${PYTHONPATH} - - 6.2. Set mesher plugin resources environment variable (see 4.2.2 paragraph) - -Note: If you use runSalome.py script from KERNEL package to launch SALOME, you may not to set - environment variables, because this script sets them itself. All what you should do is - to add section to your ${HOME}/.salome/salome.launch file for SMESH module section: - - ... - - ... - - - - ... - - ... diff --git a/doc/salome/tui/images/head.png b/doc/salome/tui/images/head.png new file mode 100755 index 0000000000000000000000000000000000000000..307d9ef9a4c07f8fba2c8721309a978433bcf63c GIT binary patch literal 78545 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 D4zrO! literal 0 HcmV?d00001 diff --git a/doc/salome/tui/images/smeshscreen.png b/doc/salome/tui/images/smeshscreen.png new file mode 100755 index 0000000000000000000000000000000000000000..638870e5fb2c49febd11e67fb7781615980cd9fd GIT binary patch literal 61648 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 literal 0 HcmV?d00001 diff --git a/doc/salome/tui/input/index.doc b/doc/salome/tui/input/index.doc new file mode 100644 index 000000000..d0ef8933b --- /dev/null +++ b/doc/salome/tui/input/index.doc @@ -0,0 +1,13 @@ +/*! \mainpage + +\image html smeshscreen.png + +To browse the \b SALOME Mesh module Developer Documentation, follow the links below or use navigation menu at the top of the page: +
                      +
                    • Modules - documentation of TUI functionality.
                    • +
                    • Name Spaces - list of TUI packages and scripts.
                    • +
                    • Data Structures - list of all data structures and classes with brief descriptions.
                    • +
                    • Files - list of all files with brief descriptions.
                    • +
                    + +*/ \ No newline at end of file diff --git a/doc/salome/tui/static/doxygen.css b/doc/salome/tui/static/doxygen.css index 389449d87..7a2dcbde8 100755 --- a/doc/salome/tui/static/doxygen.css +++ b/doc/salome/tui/static/doxygen.css @@ -1,160 +1,836 @@ -H1 { - text-align: center; +/* The standard CSS for doxygen */ + +body, table, div, p, dl { + font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif; + font-size: 12px; +} + +/* @group Heading Levels */ + +h1 { + font-size: 150%; +} + +h2 { + font-size: 120%; +} + +h3 { + font-size: 100%; } -CAPTION { - font-weight: bold +dt { + font-weight: bold; } -/* Link in the top navbar */ -A.qindex {} +div.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; +} + +p.startli, p.startdd, p.starttd { + margin-top: 2px; +} + +p.endli { + margin-bottom: 0px; +} -A.qindexRef {} +p.enddd { + margin-bottom: 4px; +} -/* Link to any cross-referenced Doxygen element */ -A.el { - text-decoration: none; - font-weight: bold +p.endtd { + margin-bottom: 2px; } -A.elRef { - font-weight: bold +/* @end */ + +caption { + font-weight: bold; } -/* Link to any cross-referenced Doxygen element inside a code section - (ex: header) +span.legend { + font-size: 70%; + text-align: center; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, div.navtab{ + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + padding: 2px; +} + +div.qindex, div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + +/* @group Link Styling */ + +a { + color: #3D578C; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: #4665A2; +} + +a:hover { + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #ffffff; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #ffffff; +} + +a.el { + font-weight: bold; +} + +a.elRef { +} + +a.code { + color: #4665A2; +} + +a.codeRef { + color: #4665A2; +} + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +.fragment { + font-family: monospace, fixed; + font-size: 105%; +} + +pre.fragment { + border: 1px solid #C4CFE5; + background-color: #FBFCFD; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + font-size: 9pt; + line-height: 125%; +} + +div.ah { + background-color: black; + font-weight: bold; + color: #ffffff; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border: solid thin #333; + border-radius: 0.5em; + -webkit-border-radius: .5em; + -moz-border-radius: .5em; + box-shadow: 2px 2px 3px #999; + -webkit-box-shadow: 2px 2px 3px #999; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); + background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000); +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.version { + border:1px solid #0000FF; + color: #CCCCCC; + font-family: Arial, Helvetica, sans-serif; + font-size: 9pt; + text-align: center; + width:100px; + -moz-border-radius: 8px; + margin: 5px; +} + +div.footer1 { + background-color: #DFE5F1; + border: 1px solid #AAAAAA; + font-family: Arial, Helvetica, sans-serif; + font-size: 11px; + padding: 10px; + margin-top: 15px; +} + + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background: white; + color: black; + margin: 0; +} + +div.contents { + margin-top: 10px; + margin-left: 10px; + margin-right: 10px; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp { + +} + +img.formulaInl { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + text-align: right; + padding-right: 12px; +} + +img.footer { + border: 0px; + vertical-align: middle; +} + +/* @group Code Colorization */ + +span.keyword { + color: #008000 +} + +span.keywordtype { + color: #604020 +} + +span.keywordflow { + color: #e08000 +} + +span.comment { + color: #800000 +} + +span.preprocessor { + color: #806020 +} + +span.stringliteral { + color: #002080 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +/* @end */ + +/* +.search { + color: #003399; + font-weight: bold; +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; +} + +input.search { + font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} */ -A.code { - text-decoration: none; - font-weight: normal; - color: #4444ee + +td.tiny { + font-size: 75%; } -A.codeRef { - font-weight: normal; - color: #4444ee +.dirtab { + padding: 4px; + border-collapse: collapse; + border: 1px solid #A3B4D7; } -A:hover { - text-decoration: none; - background-color: lightblue +th.dirtab { + background: #EBEFF6; + font-weight: bold; } -DL.el { - margin-left: -1cm +hr { + height: 0px; + border: none; + border-top: 1px solid #4A6AAA; } -/* A code fragment (ex: header) */ -DIV.fragment { - width: 100%; - border: none; - background-color: #CCCCCC +hr.footer { + height: 1px; } -/* In the alpha list (coumpound index), style of an alphabetical index letter */ -DIV.ah { - background-color: #CCCCCC; - font-weight: bold; - color: #ffffff; - margin-bottom: 3px; - margin-top: 3px +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; } -/* Method name (+ type) */ -TD.md { - background-color: lightblue; - font-weight: bold; +.mdescLeft, .mdescRight, +.memItemLeft, .memItemRight, +.memTemplItemLeft, .memTemplItemRight, .memTemplParams { + background-color: #F9FAFC; + border: none; + margin: 4px; + padding: 1px 0 0 8px; } -/* Method parameter (some of them) */ -TD.mdname1 { - background-color: lightblue; - font-weight: bold; color: #602020; +.mdescLeft, .mdescRight { + padding: 0px 8px 4px 8px; + color: #555; } -/* Method parameter (some of them) */ -TD.mdname { - background-color: lightblue; - font-weight: bold; - color: #602020; - width: 600px; +.memItemLeft, .memItemRight, .memTemplParams { + border-top: 1px solid #C4CFE5; } -/* Separator between methods group (usually empty, seems not supported by IE) */ -DIV.groupHeader { - margin-left: 16px; - margin-top: 12px; - margin-bottom: 6px; - font-weight: bold +.memItemLeft, .memTemplItemLeft { + white-space: nowrap; } -DIV.groupText { - margin-left: 16px; - font-style: italic; - font-size: smaller +.memTemplParams { + color: #4665A2; + white-space: nowrap; } -BODY { - background: #FFFFFF; +/* @end */ + +/* @group Member Details */ + +/* Styles for detailed member documentation */ + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; } -/*div.div-page { - background-color: #FFFFFF; - margin-left: 1em; - margin-right: 1em; - margin-top: 1em; - margin-bottom: 0.1em; +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} - padding-left: 1em; - padding-right: 1em; - padding-top: 0.5em; - padding-bottom: 0.5em; +.memitem { + padding: 0; + margin-bottom: 10px; +} + +.memname { + white-space: nowrap; + font-weight: bold; + margin-left: 6px; +} + +.memproto { + border-top: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 6px 0px 6px 0px; + color: #253555; + font-weight: bold; + text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + /* opera specific markup */ + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + border-top-right-radius: 8px; + border-top-left-radius: 8px; + /* firefox specific markup */ + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + -moz-border-radius-topright: 8px; + -moz-border-radius-topleft: 8px; + /* webkit specific markup */ + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + -webkit-border-top-right-radius: 8px; + -webkit-border-top-left-radius: 8px; + background-image:url('nav_f.png'); + background-repeat:repeat-x; + background-color: #E2E8F2; + +} + +.memdoc { + border-bottom: 1px solid #A8B8D9; + border-left: 1px solid #A8B8D9; + border-right: 1px solid #A8B8D9; + padding: 2px 5px; + background-color: #FBFCFD; + border-top-width: 0; + /* opera specific markup */ + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + /* firefox specific markup */ + -moz-border-radius-bottomleft: 8px; + -moz-border-radius-bottomright: 8px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7); + /* webkit specific markup */ + -webkit-border-bottom-left-radius: 8px; + -webkit-border-bottom-right-radius: 8px; + -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + background-image: -webkit-gradient(linear,center top,center bottom,from(#FFFFFF), color-stop(0.6,#FFFFFF), color-stop(0.60,#FFFFFF), color-stop(0.95,#F7F8FB), to(#EEF1F7)); +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: #602020; + white-space: nowrap; +} +.paramname em { + font-style: normal; +} + +.params, .retval, .exception, .tparams { + border-spacing: 6px 2px; +} + +.params .paramname, .retval .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir { + font-family: "courier new",courier,monospace; + vertical-align: top; +} + + + + +/* @end */ + +/* @group Directory (tree) */ + +/* for the tree view */ + +.ftvtree { + font-family: sans-serif; + margin: 0px; +} + +/* these are for tree view when used as main index */ + +.directory { + font-size: 9pt; + font-weight: bold; + margin: 5px; +} + +.directory h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +/* +The following two styles can be used to replace the root node title +with an image of your choice. Simply uncomment the next two styles, +specify the name of your image and be sure to set 'height' to the +proper pixel height of your image. +*/ + +/* +.directory h3.swap { + height: 61px; + background-repeat: no-repeat; + background-image: url("yourimage.gif"); +} +.directory h3.swap span { + display: none; +} +*/ + +.directory > h3 { + margin-top: 0; +} + +.directory p { + margin: 0px; + white-space: nowrap; +} + +.directory div { + display: none; + margin: 0px; +} + +.directory img { + vertical-align: -30%; +} + +/* these are for tree view when not used as main index */ + +.directory-alt { + font-size: 100%; + font-weight: bold; +} + +.directory-alt h3 { + margin: 0px; + margin-top: 1em; + font-size: 11pt; +} + +.directory-alt > h3 { + margin-top: 0; +} + +.directory-alt p { + margin: 0px; + white-space: nowrap; +} + +.directory-alt div { + display: none; + margin: 0px; +} + +.directory-alt img { + vertical-align: -30%; +} + +/* @end */ - border: 2px solid #0D299A; - border-width: 2px; - border-color: #0D299A; -}*/ +div.dynheader { + margin-top: 8px; +} + +address { + font-style: normal; + color: #2A3D61; +} + +table.doxtable { + border-collapse:collapse; +} + +table.doxtable td, table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + text-align:left; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul +{ + font-size: 11px; + background-image:url('tab_b.png'); + background-repeat:repeat-x; + height:30px; + line-height:30px; + color:#8AA0CC; + border:solid 1px #C2CDE4; + overflow:hidden; + margin:0px; + padding:0px; +} + +.navpath li +{ + list-style-type:none; + float:left; + padding-left:10px; + padding-right:15px; + background-image:url('bc_s.png'); + background-repeat:no-repeat; + background-position:right; + color:#364D7C; +} + +.navpath li.navelem a +{ + height:32px; + display:block; + text-decoration: none; + outline: none; +} + +.navpath li.navelem a:hover +{ + color:#6884BD; +} + +.navpath li.footer +{ + list-style-type:none; + float:right; + padding-left:10px; + padding-right:15px; + background-image:none; + background-repeat:no-repeat; + background-position:right; + color:#364D7C; + font-size: 8pt; +} + + +div.summary +{ + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a +{ + white-space: nowrap; +} + +div.ingroups +{ + font-size: 8pt; + padding-left: 5px; + width: 50%; + text-align: left; +} + +div.ingroups a +{ + white-space: nowrap; +} + +div.header +{ + background-image:url('nav_h.png'); + background-repeat:repeat-x; + background-color: #F9FAFC; + margin: 0px; + border-bottom: 1px solid #C4CFE5; +} + +div.headertitle +{ + padding: 5px 5px 5px 10px; +} + +.title { + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +dl +{ + padding: 0 0 0 10px; +} + +dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug +{ + border-left:4px solid; + padding: 0 0 0 6px; +} + +dl.note +{ + border-color: #D0D000; +} + +dl.warning, dl.attention +{ + border-color: #FF0000; +} + +dl.pre, dl.post, dl.invariant +{ + border-color: #00D000; +} + +dl.deprecated +{ + border-color: #505050; +} + +dl.todo +{ + border-color: #00C0E0; +} + +dl.test +{ + border-color: #3030E0; +} + +dl.bug +{ + border-color: #C08050; +} + +#projectlogo +{ + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img +{ + border: 0px none; +} + +#projectname +{ + background-color: #175783; + border: 1px solid; + height: 80px; + background-repeat: no-repeat; +/* font: 300% arial,sans-serif;*/ + margin: 0px; + padding: 0px; +} + +#projectbrief +{ + font: 120% arial,sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber +{ + font: 50% arial,sans-serif; + margin: 0px; + padding: 0px; +} -DIV.div-footer { - margin-left: 1em; - margin-right: 1em; - margin-bottom: 0.2em; - text-align: right; - font-size: 9pt; +#titlearea +{ + background: url("head.png"); + background-color: #175783; + border: 1px solid; + height: 80px; + background-repeat: no-repeat; + padding: 0px; + margin: 0px; + width: 100%; + border-bottom: 1px solid #5373B4; } -/* In File List, Coumpound List, etc, 1st column of the index */ -TD.indexkey { - background-color: #CCCCCC; - font-weight: bold; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px -} - -/* In File List, Coumpound List, etc, 2nd column of the index */ -TD.indexvalue { - background-color: #CCCCCC; - font-style: italic; - padding-right : 10px; - padding-top : 2px; - padding-left : 10px; - padding-bottom : 2px; - margin-left : 0px; - margin-right : 0px; - margin-top : 2px; - margin-bottom : 2px -} - -span.keyword { color: #008000 } -span.keywordtype { color: #604020 } -span.keywordflow { color: #e08000 } -span.comment { color: #800000 } -span.preprocessor { color: #806020 } -span.stringliteral { color: #002080 } -span.charliteral { color: #008080 } diff --git a/doc/salome/tui/static/footer.html b/doc/salome/tui/static/footer.html index cb55f397a..5b030c021 100755 --- a/doc/salome/tui/static/footer.html +++ b/doc/salome/tui/static/footer.html @@ -1,5 +1,14 @@ - - - - + + + + + +
                    + +
                    +Copyright © 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE
                    +Copyright © 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
                    +
                    +
                    + + diff --git a/doc/salome/tui/static/header.html.in b/doc/salome/tui/static/header.html.in new file mode 100755 index 000000000..4571b4363 --- /dev/null +++ b/doc/salome/tui/static/header.html.in @@ -0,0 +1,20 @@ + + + + + +$title + +$treeview +$search +$mathjax + + + + +
                    +
                    Version: @VERSION@
                    + +
                    diff --git a/doc/salome/tui/static/myheader.html b/doc/salome/tui/static/myheader.html deleted file mode 100755 index d2efb75fa..000000000 --- a/doc/salome/tui/static/myheader.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Main Page - - - - -  - - diff --git a/idl/Makefile.am b/idl/Makefile.am index a1d468cb5..c43e554d3 100644 --- a/idl/Makefile.am +++ b/idl/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # This Makefile is responsible of generating the client and server # implementation of IDL interfaces for both C++ and python usage. # The building process of the C++ files is in charge of each source @@ -34,7 +32,10 @@ BASEIDL_FILES = \ SMESH_Filter.idl \ SMESH_Group.idl \ SMESH_Pattern.idl \ - SMESH_MeshEditor.idl + SMESH_MeshEditor.idl \ + SMESH_Measurements.idl + +BASEIDL_FILES_PY=$(BASEIDL_FILES:%.idl=%_idl.py) # This variable defines the files to be installed dist_salomeidl_DATA = $(BASEIDL_FILES) @@ -51,13 +52,16 @@ nodist_libSalomeIDLSMESH_la_SOURCES = \ SMESH_FilterSK.cc \ SMESH_GroupSK.cc \ SMESH_PatternSK.cc \ - SMESH_MeshEditorSK.cc + SMESH_MeshEditorSK.cc \ + SMESH_MeasurementsSK.cc + +# to be done at first, to avoid parallel compilation problems +BUILT_SOURCES = $(nodist_libSalomeIDLSMESH_la_SOURCES) # header files must be exported: other modules have to use this library nodist_salomeinclude_HEADERS = $(BASEIDL_FILES:%.idl=%.hh) libSalomeIDLSMESH_la_CPPFLAGS = \ - -I$(top_builddir)/salome_adm/unix \ -I$(top_builddir)/idl \ $(CORBA_CXXFLAGS) $(CORBA_INCLUDES) \ $(KERNEL_CXXFLAGS) $(MED_CXXFLAGS) \ @@ -85,8 +89,7 @@ IDLCXXFLAGS = \ -I$(top_builddir)/idl/salome \ -I$(KERNEL_ROOT_DIR)/idl/salome \ -I$(MED_ROOT_DIR)/idl/salome \ - -I$(GEOM_ROOT_DIR)/idl/salome \ - -I$(top_builddir)/salome_adm/unix + -I$(GEOM_ROOT_DIR)/idl/salome IDLPYFLAGS = \ @IDLPYFLAGS@ \ @@ -107,9 +110,15 @@ install-exec-local: $(BASEIDL_FILES:%=$(top_srcdir)/idl/%) $(OMNIORB_IDL) $(IDLPYFLAGS) -C$(DESTDIR)$(salomepythondir) $$file ; \ done -# uninstall-local removes too much, but it works in distcheck +# we want to remove only staff generated for IDL files and nothing more uninstall-local: - rm -rf $(DESTDIR)$(salomepythondir)/* + @for modulen in SMESH StdMeshers ; do \ + test -d $(DESTDIR)$(salomepythondir)/$${modulen} && echo "Removing $(DESTDIR)$(salomepythondir)/$${modulen}" && rm -rf $(DESTDIR)$(salomepythondir)/$${modulen} ; \ + test -d $(DESTDIR)$(salomepythondir)/$${modulen}__POA && echo "Removing $(DESTDIR)$(salomepythondir)/$${modulen}__POA" && rm -rf $(DESTDIR)$(salomepythondir)/$${modulen}__POA ; \ + done ; \ + for filen in $(BASEIDL_FILES_PY) ; do \ + echo "Removing $(DESTDIR)$(salomepythondir)/$${filen}" && rm -f $(DESTDIR)$(salomepythondir)/$${filen}* ; \ + done mostlyclean-local: -rm -f *.hh *.cc .depidl diff --git a/idl/SMESH_BasicHypothesis.idl b/idl/SMESH_BasicHypothesis.idl index 9c428ba14..5ec9ebbf0 100644 --- a/idl/SMESH_BasicHypothesis.idl +++ b/idl/SMESH_BasicHypothesis.idl @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_BasicHypothesis.idl // Author : Paul RASCLE, EDF // @@ -29,7 +30,6 @@ #include "SMESH_Hypothesis.idl" #include "SMESH_Mesh.idl" - /*! * StdMeshers: interfaces to standard hypotheses and algorithms */ @@ -209,6 +209,26 @@ module StdMeshers */ long ConversionMode() raises (SALOME::SALOME_Exception); + + /*! + * Set list of edges to reverse + */ + void SetReversedEdges( in SMESH::long_array list ); + + /*! + * Returns list of edges to reverse + */ + SMESH::long_array GetReversedEdges(); + + /*! + * Set entry of the main object + */ + void SetObjectEntry( in string entry ); + + /*! + * Get the entry of the main object + */ + string GetObjectEntry(); }; /*! @@ -218,14 +238,48 @@ module StdMeshers { /*! * Sets or parameter value + * * OBSOLETE *. Avoid such a way of interface design + * * It is recommended to dedicate a method to each parameter. */ void SetLength(in double length, in boolean isStartLength) raises (SALOME::SALOME_Exception); + /*! + * Sets parameter value + */ + void SetStartLength(in double length) + raises (SALOME::SALOME_Exception); + + /*! + * Sets parameter value + */ + void SetEndLength(in double length) + raises (SALOME::SALOME_Exception); + /*! * Returns or parameter value */ double GetLength(in boolean isStartLength); + + /*! + * Set list of edges to reverse + */ + void SetReversedEdges( in SMESH::long_array list ); + + /*! + * Returns list of edges to reverse + */ + SMESH::long_array GetReversedEdges(); + + /*! + * Set entry of the main object + */ + void SetObjectEntry( in string entry ); + + /*! + * Get the entry of the main object + */ + string GetObjectEntry(); }; /*! @@ -269,14 +323,48 @@ module StdMeshers { /*! * Sets or parameter value + * * OBSOLETE *. Avoid such a way of interface design. + * * It is recommended to dedicate a method to each parameter. */ void SetLength(in double length, in boolean isStartLength) raises (SALOME::SALOME_Exception); + /*! + * Sets parameter value + */ + void SetStartLength(in double length) + raises (SALOME::SALOME_Exception); + + /*! + * Sets parameter value + */ + void SetEndLength(in double length) + raises (SALOME::SALOME_Exception); + /*! * Returns or parameter value */ double GetLength(in boolean isStartLength); + + /*! + * Set list of edges to reverse + */ + void SetReversedEdges( in SMESH::long_array list ); + + /*! + * Returns list of edges to reverse + */ + SMESH::long_array GetReversedEdges(); + + /*! + * Set entry of the main object + */ + void SetObjectEntry( in string entry ); + + /*! + * Get the entry of the main object + */ + string GetObjectEntry(); }; @@ -298,6 +386,54 @@ module StdMeshers }; + /*! + * StdMeshers_FixedPoints1D: interface of "Fixed points 1D" hypothesis + */ + interface StdMeshers_FixedPoints1D : SMESH::SMESH_Hypothesis + { + /*! + * Sets some points on edge using parameter on curve from 0 to 1 + * (additionally it is neecessary to check orientation of edges and + * create list of reversed edges if it is needed) and sets numbers + * of segments between given points (default values are equals 1) + */ + void SetPoints(in SMESH::double_array listParams) + raises (SALOME::SALOME_Exception); + void SetNbSegments(in SMESH::long_array listNbSeg) + raises (SALOME::SALOME_Exception); + + /*! + * Returns list of point's parameters + */ + SMESH::double_array GetPoints(); + + /*! + * Returns list of numbers of segments + */ + SMESH::long_array GetNbSegments(); + + /*! + * Set list of edges to reverse + */ + void SetReversedEdges( in SMESH::long_array list ); + + /*! + * Returns list of edges to reverse + */ + SMESH::long_array GetReversedEdges(); + + /*! + * Set entry of the main object + */ + void SetObjectEntry( in string entry ); + + /*! + * Get the entry of the main object + */ + string GetObjectEntry(); + }; + + /*! * StdMeshers_MaxElementVolume: interface of "Max. Hexahedron or Tetrahedron Volume" hypothesis */ @@ -344,16 +480,6 @@ module StdMeshers { }; - /*! - * StdMeshers_TrianglePreference: interface of "TrianglePreference" hypothesis. - * This hypothesis is used by StdMeshers_Quadrangle_2D algorithm. - * Presence of this hypothesis forces construction of triangles in the refinement - * area if the number of nodes on opposite edges is not the same. - */ - interface StdMeshers_TrianglePreference : SMESH::SMESH_Hypothesis - { - }; - /*! * StdMeshers_QuadraticMesh: interface of "QuadraticMesh" hypothesis. * This is an auxiliary 1D hypothesis whose presence forces construction @@ -411,6 +537,26 @@ module StdMeshers }; + /*! + * StdMeshers_NumberOfLayers2D: interface of "Nb. Layers" hypothesis. + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies number of segments between the internal + * and the external surfaces. + */ + interface StdMeshers_NumberOfLayers2D : StdMeshers_NumberOfLayers + { + }; + + /*! + * StdMeshers_LayerDistribution2D: interface of "Distribution of Layers" hypothesis. + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies 1D hypothesis defining distribution of segments between the internal + * and the external surfaces. + */ + interface StdMeshers_LayerDistribution2D : StdMeshers_LayerDistribution + { + }; + /*! * interface of "ProjectionSource1D" hypothesis. * This hypothesis specifies a meshed edge to take a mesh pattern from @@ -594,6 +740,189 @@ module StdMeshers double GetLength(); }; + /*! + * StdMeshers_QuadrangleParams: interface of "Quadrangle Params" hypothesis + */ + enum QuadType + { + QUAD_STANDARD, + QUAD_TRIANGLE_PREF, + QUAD_QUADRANGLE_PREF, + QUAD_QUADRANGLE_PREF_REVERSED, + QUAD_REDUCED, + QUAD_NB_TYPES /* this is not a type of quadrangulation */ + }; + + interface StdMeshers_QuadrangleParams : SMESH::SMESH_Hypothesis + { + /*! + * Set base vertex for triangles + */ + void SetTriaVertex( in long vertID ); + + /*! + * Returns base vertex for triangles + */ + long GetTriaVertex(); + + /*! + * Set entry of the main object + */ + void SetObjectEntry( in string entry ); + + /*! + * Get the entry of the main object + */ + string GetObjectEntry(); + + /*! + * Set the type of quadrangulation + */ + void SetQuadType( in QuadType type ); + + /*! + * Get the type of quadrangulation + */ + QuadType GetQuadType(); + }; + + /*! + * interface of "Source edges" hypothesis. + * This hypothesis specifies groups of edges of other mesh to be imported + * in this mesh + */ + interface StdMeshers_ImportSource1D : SMESH::SMESH_Hypothesis + { + /*! + * Set edges to import from other mesh + */ + void SetSourceEdges(in SMESH::ListOfGroups groups); + SMESH::string_array GetSourceEdges(); + + /*! + * Set to import the whole other mesh or not, and if yes, to + * copy groups of not. By default the mesh is not copied. + */ + void SetCopySourceMesh(in boolean toCopyMesh, in boolean toCopyGroups); + void GetCopySourceMesh(out boolean toCopyMesh,out boolean toCopyGroups); + }; + + /*! + * interface of "Source faces" hypothesis. + * This hypothesis specifies groups of faces of other mesh to be imported + * in this mesh + */ + interface StdMeshers_ImportSource2D : SMESH::SMESH_Hypothesis + { + /*! + * Set faces to import from other mesh + */ + void SetSourceFaces(in SMESH::ListOfGroups groups); + SMESH::string_array GetSourceFaces(); + + /*! + * Set to import the whole other mesh or not, and if yes, to + * copy groups of not. By default the mesh is not copied. + */ + void SetCopySourceMesh(in boolean toCopyMesh,in boolean toCopyGroups); + void GetCopySourceMesh(out boolean toCopyMesh,out boolean toCopyGroups); + }; + + /*! + * interface of "Viscous Layers" hypothesis. + * This hypothesis specifies parameters of layers of prisms to build + * near mesh boundary. This hypothesis can be used by several 3D algorithms: + * NETGEN 3D, GHS3D, Hexahedron(i,j,k) + */ + interface StdMeshers_ViscousLayers : SMESH::SMESH_Hypothesis + { + /*! + * Set faces to exclude from treatment + */ + void SetIgnoreFaces(in SMESH::long_array faceIDs) raises (SALOME::SALOME_Exception); + SMESH::long_array GetIgnoreFaces(); + + /*! + * Set total thickness of layers of prisms + */ + void SetTotalThickness(in double thickness) raises (SALOME::SALOME_Exception); + double GetTotalThickness(); + + /*! + * Set number of layers of prisms + */ + void SetNumberLayers(in short nb) raises (SALOME::SALOME_Exception); + short GetNumberLayers(); + + /*! + * Set factor (>1.0) of growth of layer thickness towards inside of mesh + */ + void SetStretchFactor(in double factor) raises (SALOME::SALOME_Exception); + double GetStretchFactor(); + }; + + /*! + * interface of "Body fitting Parameters" hypothesis. + * This hypothesis specifies + * - Definition of the Cartesian grid + * - Size threshold + */ + interface StdMeshers_CartesianParameters3D : SMESH::SMESH_Hypothesis + { + /*! + * Set size threshold. A polyhedral cell got by cutting an initial + * hexahedron by geometry boundary is considered small and is removed if + * it's size is \athreshold times less than the size of the initial hexahedron. + * threshold must be > 1.0 + */ + void SetSizeThreshold(in double threshold) raises (SALOME::SALOME_Exception); + double GetSizeThreshold(); + + /*! + * \brief Return true if the grid is defined by spacing functions and + * not by node coordinates in given direction (X==0,...) + */ + boolean IsGridBySpacing(in short axis); + + /*! + * Set coordinates of nodes along an axis (counterd from zero) + */ + void SetGrid(in SMESH::double_array coords, + in short axis) raises (SALOME::SALOME_Exception); + SMESH::double_array GetGrid(in short axis) raises (SALOME::SALOME_Exception); + + /*! + * \brief Set grid spacing along an axis + * \param spaceFunctions - functions defining spacing values at given point on axis + * \param internalPoints - points dividing a grid into parts along an axis + * \param axis - index of an axis counterd from zero, i.e. 0==X, 1==Y, 2==Z + * + * Parameter t of spaceFunction f(t) is a position [0,1] withing bounding box of + * the shape to mesh or withing an interval defined by internal points + */ + void SetGridSpacing(in SMESH::string_array spaceFunctions, + in SMESH::double_array internalPoints, + in short axis) raises (SALOME::SALOME_Exception); + + void GetGridSpacing(out SMESH::string_array spaceFunctions, + out SMESH::double_array internalPoints, + in short axis) raises (SALOME::SALOME_Exception); + + /*! + * \brief Computes node coordinates by spacing functions + * \param x0 - lower coordinate + * \param x1 - upper coordinate + * \param spaceFuns - space functions + * \param points - internal points + * \param coords - the computed coordinates + */ + SMESH::double_array ComputeCoordinates(in double x0, + in double x1, + in SMESH::string_array spaceFuns, + in SMESH::double_array points, + in string axisName ) raises (SALOME::SALOME_Exception); + }; + /*! * StdMeshers_SegmentAroundVertex_0D: interface of "SegmentAroundVertex" algorithm */ @@ -664,6 +993,13 @@ module StdMeshers { }; + /*! + * StdMeshers_Projection_1D2D: interface of "Projection 1D-2D" algorithm + */ + interface StdMeshers_Projection_1D2D : SMESH::SMESH_2D_Algo + { + }; + /*! * StdMeshers_Projection_1D: interface of "Projection 1D" algorithm */ @@ -687,6 +1023,32 @@ module StdMeshers { }; + /*! + * StdMeshers_RadialQuadrangle_1D2D: interface of "Radial quadrangle" algorithm + */ + interface StdMeshers_RadialQuadrangle_1D2D : SMESH::SMESH_2D_Algo + { + }; + + /*! + * StdMeshers_Import_1D2D: interface of "Use existing 2D elements" algorithm + */ + interface StdMeshers_Import_1D2D : SMESH::SMESH_2D_Algo + { + }; + /*! + * StdMeshers_Import_1D: interface of "Use existing 1D elements" algorithm + */ + interface StdMeshers_Import_1D : SMESH::SMESH_1D_Algo + { + }; + /*! + * StdMeshers_Cartesian_3D: interface of "Body fitting" algorithm + */ + interface StdMeshers_Cartesian_3D : SMESH::SMESH_3D_Algo + { + }; + }; #endif diff --git a/idl/SMESH_Filter.idl b/idl/SMESH_Filter.idl index 83c1d43d2..8cc7bc864 100644 --- a/idl/SMESH_Filter.idl +++ b/idl/SMESH_Filter.idl @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Filter.idl // Author : Alexey Petrov, OCC // @@ -46,10 +47,16 @@ module SMESH FT_Skew, FT_Area, FT_Volume3D, + FT_MaxElementLength2D, + FT_MaxElementLength3D, FT_FreeBorders, FT_FreeEdges, FT_FreeNodes, FT_FreeFaces, + FT_EqualNodes, // IMPORTANT: when a new item is added, please + FT_EqualEdges, // 1) update getFunctNames() in SMESH_Filter_i.cxx: line 3910 + FT_EqualFaces, // 2) update fixFunctorType() in SMESH_2smeshpy.cxx: line 234 + FT_EqualVolumes, FT_MultiConnection, FT_MultiConnection2D, FT_Length, @@ -61,9 +68,15 @@ module SMESH FT_LyingOnGeom, FT_RangeOfIds, FT_BadOrientedVolume, + FT_BareBorderVolume, + FT_BareBorderFace, + FT_OverConstrainedVolume, + FT_OverConstrainedFace, FT_LinearOrQuadratic, FT_GroupColor, FT_ElemGeomType, + FT_CoplanarFaces, + FT_BallDiameter, FT_LessThan, FT_MoreThan, FT_EqualTo, @@ -73,6 +86,18 @@ module SMESH FT_Undefined }; + /*! + * Parameters of a reclangle of histogram + */ + struct HistogramRectangle + { + long nbEvents; + double min; + double max; + }; + typedef sequence Histogram; + + /*! * Base interface for all functors ( i.e. numerical functors and predicates ) */ @@ -83,8 +108,6 @@ module SMESH ElementType GetElementType(); }; - - /*! * Numerical functors are intended for calculating value by Id of mesh entity */ @@ -92,6 +115,8 @@ module SMESH { double GetValue( in long theElementId ); + Histogram GetHistogram( in short nbIntervals ); + /*! * Set precision for calculation. It is a position after point which is * used to functor value after calculation. @@ -107,6 +132,8 @@ module SMESH interface Skew : NumericalFunctor{}; interface Area : NumericalFunctor{}; interface Volume3D : NumericalFunctor{}; + interface MaxElementLength2D : NumericalFunctor{}; + interface MaxElementLength3D : NumericalFunctor{}; interface Length : NumericalFunctor{}; interface Length2D : NumericalFunctor { @@ -130,7 +157,9 @@ module SMESH typedef sequence Values; Values GetValues(); }; + interface BallDiameter : NumericalFunctor{}; + /*! * Predicates are intended for verification of criteria, * must return bool value by mesh id @@ -147,6 +176,54 @@ module SMESH */ interface BadOrientedVolume: Predicate {}; + /*! + * Logical functor (predicate) "Volumes with bare border". + * Verify whether a mesh volume has a free facet without a mesh face on it + */ + interface BareBorderVolume: Predicate {}; + /*! + * Logical functor (predicate) "Faces with bare border". + * Verify whether a mesh face has a side not shared with another face + * and without a mesh edge on it + */ + interface BareBorderFace: Predicate {}; + + /*! + * Logical functor (predicate) "Over-constrained Volume" + * Verify whether a mesh volume has only one facet shared with other volumes + */ + interface OverConstrainedVolume: Predicate {}; + /*! + * Logical functor (predicate) "Over-constrained Face". + * Verify whether a mesh face has only one border shared with other faces + */ + interface OverConstrainedFace: Predicate {}; + + /*! + * Logical functor (predicate) "Equal Nodes". + * Verify whether there is another mesh node with same coordinates + */ + interface EqualNodes: Predicate + { + void SetTolerance( in double theToler ); + double GetTolerance(); + }; + /*! + * Logical functor (predicate) "Equal Edges". + * Verify whether there is another mesh edge basing on the same nodes + */ + interface EqualEdges: Predicate {}; + /*! + * Logical functor (predicate) "Equal Faces". + * Verify whether there is another mesh face basing on the same nodes + */ + interface EqualFaces: Predicate {}; + /*! + * Logical functor (predicate) "Equal Volumes". + * Verify whether there is another mesh volumes basing on the same nodes + */ + interface EqualVolumes: Predicate {}; + /*! * Logical functor (predicate) "Belong To Geometry". * Verify whether mesh element or node belong to pointed Geom Object @@ -345,6 +422,16 @@ module SMESH void SetGeometryType( in GeometryType theType ); }; + /*! + * Functor "Coplanar faces" + * Returns true if a mesh face is a coplanar neighbour to a given one. It checks + * if normal of a face has angle with the threshold face less than a tolerance. + */ + interface CoplanarFaces : Predicate{ + void SetFace ( in long theFaceID ); + void SetTolerance( in double theToler ); + }; + /*! * Filter */ @@ -359,13 +446,13 @@ module SMESH * BinaryOp - binary logical operation FT_LogicalAND, FT_LogicalOR or * (FT_Undefined must be for the last criterion) * ThresholdStr - Threshold value defined as string. Used for: - * 1. Diaposon of identifiers. Example: "1,2,3,5-10,12,27-29" + * 1. Diapason of identifiers. Example: "1,2,3,5-10,12,27-29" * 2. BelongToGeom predicate for storing name of shape * 3. GroupColor predicate for storing group color "0.2;0;0.5" * ThresholdID - One more threshold value defined as string. Used for: * 1. BelongToGeom predicate for storing id of shape * Tolerance - Tolerance is used for comparators (EqualTo comparision) and for - * "Belong to plane" and "Belong to cylinder" predicates + * "Belong to plane", "Belong to cylinder" etc predicates * TypeOfElement - type of element SMESH::NODE, SMESH::FACE (used by BelongToGeom predicate only) * Precision - Precision of numerical functors */ @@ -449,10 +536,13 @@ module SMESH Skew CreateSkew(); Area CreateArea(); Volume3D CreateVolume3D(); + MaxElementLength2D CreateMaxElementLength2D(); + MaxElementLength3D CreateMaxElementLength3D(); Length CreateLength(); Length2D CreateLength2D(); MultiConnection CreateMultiConnection(); MultiConnection2D CreateMultiConnection2D(); + BallDiameter CreateBallDiameter(); /*! * Create logical functors ( predicates ) @@ -469,13 +559,23 @@ module SMESH FreeNodes CreateFreeNodes(); FreeFaces CreateFreeFaces(); + EqualNodes CreateEqualNodes(); + EqualEdges CreateEqualEdges(); + EqualFaces CreateEqualFaces(); + EqualVolumes CreateEqualVolumes(); + RangeOfIds CreateRangeOfIds(); BadOrientedVolume CreateBadOrientedVolume(); + BareBorderVolume CreateBareBorderVolume(); + BareBorderFace CreateBareBorderFace(); + OverConstrainedVolume CreateOverConstrainedVolume(); + OverConstrainedFace CreateOverConstrainedFace(); LinearOrQuadratic CreateLinearOrQuadratic(); GroupColor CreateGroupColor(); ElemGeomType CreateElemGeomType(); + CoplanarFaces CreateCoplanarFaces(); /*! * Create comparators ( predicates ) diff --git a/idl/SMESH_Gen.idl b/idl/SMESH_Gen.idl index 7cc93177b..f6e9f89ad 100644 --- a/idl/SMESH_Gen.idl +++ b/idl/SMESH_Gen.idl @@ -1,23 +1,23 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // // File : SMESH_Gen.idl // Author : Paul RASCLE, EDF @@ -41,6 +41,7 @@ module SMESH interface FilterManager; interface SMESH_Pattern; + interface Measurements; /*! * Tags definition @@ -69,12 +70,14 @@ module SMESH const long Tag_EdgeGroups = 12; const long Tag_FaceGroups = 13; const long Tag_VolumeGroups = 14; - const long Tag_LastGroup = 14; + const long Tag_0DElementsGroups = 15; + const long Tag_BallElementsGroups = 16; + const long Tag_LastGroup = 16; /*! * Hypothesis definintion error */ - struct AlgoStateError + struct AlgoStateError { Hypothesis_Status state; string algoName; @@ -96,20 +99,22 @@ module SMESH COMPERR_EXCEPTION , // other exception raised COMPERR_MEMORY_PB , // memory allocation problem COMPERR_ALGO_FAILED , // computation failed - COMPERR_BAD_SHAPE // bad geometry + COMPERR_BAD_SHAPE , // bad geometry + COMPERR_WARNING , // algo reports error but sub-mesh is computed anyway + COMPERR_CANCELED // compute canceled }; struct ComputeError { short code; // ComputeErrorName or, if negative, algo specific code string comment; // textual problem description string algoName; - short subShapeID; // id of subshape of a shape to mesh + short subShapeID; // id of sub-shape of a shape to mesh boolean hasBadMesh; // there are elements preventing computation available for visualization }; typedef sequence compute_error_array; - interface SMESH_Gen : Engines::Component, SALOMEDS::Driver + interface SMESH_Gen : Engines::EngineComponent, SALOMEDS::Driver { //GEOM::GEOM_Gen SetGeomEngine( in string containerLoc ); void SetGeomEngine( in GEOM::GEOM_Gen geomcompo ); @@ -118,6 +123,8 @@ module SMESH SMESH_Pattern GetPattern(); + Measurements CreateMeasurements(); + /*! Set the current mode */ @@ -146,7 +153,7 @@ module SMESH * Algorithms are 1D, 2D or 3D. */ SMESH_Hypothesis CreateHypothesis( in string theHypName, - in string theLibName ) + in string theLibName ) raises ( SALOME::SALOME_Exception ); /*! @@ -177,7 +184,7 @@ module SMESH * Set the object name */ void SetName( in string theObjectIOR, - in string theObjectName ) + in string theObjectName ) raises ( SALOME::SALOME_Exception ); /*! @@ -189,13 +196,13 @@ module SMESH */ SMESH_Mesh CreateMesh( in GEOM::GEOM_Object theObject ) raises ( SALOME::SALOME_Exception ); - + /*! * Create an empty mesh object */ SMESH_Mesh CreateEmptyMesh() raises ( SALOME::SALOME_Exception ); - + /*! * Create Mesh object importing data from given UNV file * (UNV supported version is I-DEAS 10) @@ -207,7 +214,14 @@ module SMESH * Create Mesh object(s) importing data from given MED file */ mesh_array CreateMeshesFromMED( in string theFileName, - out SMESH::DriverMED_ReadStatus theStatus ) + out SMESH::DriverMED_ReadStatus theStatus ) + raises ( SALOME::SALOME_Exception ); + + /*! + * Create Mesh object(s) importing data from given MED file + */ + mesh_array CreateMeshesFromSAUV( in string theFileName, + out SMESH::DriverMED_ReadStatus theStatus ) raises ( SALOME::SALOME_Exception ); /*! @@ -217,43 +231,98 @@ module SMESH raises ( SALOME::SALOME_Exception ); /*! - * Create a Mesh object, without a geometry shape reference + * Create Mesh object(s) importing data from given CGNS file + */ + mesh_array CreateMeshesFromCGNS( in string theFileName, + out SMESH::DriverMED_ReadStatus theStatus ) + raises ( SALOME::SALOME_Exception ); + + /*! + * Create a mesh by copying a part of another mesh + * \param meshPart - a part of mesh to copy + * \param meshName - a name of the new mesh + * \param toCopyGroups - to create in the new mesh groups + * the copied elements belongs to + * \param toKeepIDs - to preserve IDs of the copied elements or not + */ + SMESH_Mesh CopyMesh(in SMESH_IDSource meshPart, + in string meshName, + in boolean toCopyGroups, + in boolean toKeepIDs) + raises ( SALOME::SALOME_Exception ); + + /*! + * Concatenate the given meshes into one mesh. + * Union groups with the same name and type if + * theUniteIdenticalGroups flag is true. + * Merge coincident nodes and elements if + * theMergeNodesAndElements flag is true. + */ + SMESH_Mesh Concatenate(in mesh_array theMeshesArray, + in boolean theUniteIdenticalGroups, + in boolean theMergeNodesAndElements, + in double theMergeTolerance) + raises ( SALOME::SALOME_Exception ); + + /*! + * Concatenate the given meshes into one mesh. + * Union groups with the same name and type if + * theUniteIdenticalGroups flag is true. + * Merge coincident nodes and elements if + * theMergeNodesAndElements flag is true. + * Create the groups of all elements from initial meshes. */ -// SMESH_Mesh NewEmpty() -// raises ( SALOME::SALOME_Exception ); + SMESH_Mesh ConcatenateWithGroups(in mesh_array theMeshesArray, + in boolean theUniteIdenticalGroups, + in boolean theMergeNodesAndElements, + in double theMergeTolerance) + raises ( SALOME::SALOME_Exception ); /*! - * Mesh a subShape. - * First, verify list of hypothesis associated with the subShape, + * Mesh a subShape. + * First, verify list of hypothesis associated with the Sub-shape, * return NOK if hypothesis are not sufficient */ - boolean Compute( in SMESH_Mesh theMesh, - in GEOM::GEOM_Object theSubObject ) + boolean Compute( in SMESH_Mesh theMesh, + in GEOM::GEOM_Object theSubObject ) raises ( SALOME::SALOME_Exception ); + /*! + * Cancel a computation. + */ + void CancelCompute( in SMESH_Mesh theMesh, + in GEOM::GEOM_Object theSubObject ); + /*! * Return true if hypotheses are defined well */ - boolean IsReadyToCompute( in SMESH_Mesh theMesh, - in GEOM::GEOM_Object theSubObject ) + boolean IsReadyToCompute( in SMESH_Mesh theMesh, + in GEOM::GEOM_Object theSubObject ) + raises ( SALOME::SALOME_Exception ); + + /*! + * Evaluates size of prospective mesh on a shape + */ + long_array Evaluate(in SMESH_Mesh theMesh, + in GEOM::GEOM_Object theSubObject) raises ( SALOME::SALOME_Exception ); /*! * Calculate Mesh as preview till indicated dimension - * First, verify list of hypothesis associated with the subShape. + * First, verify list of hypothesis associated with the Sub-shape. * Return mesh preview structure */ - MeshPreviewStruct Precompute( in SMESH_Mesh theMesh, - in GEOM::GEOM_Object theSubObject, - in Dimension theDimension, - inout long_array theShapesId ) + MeshPreviewStruct Precompute( in SMESH_Mesh theMesh, + in GEOM::GEOM_Object theSubObject, + in Dimension theDimension, + inout long_array theShapesId ) raises ( SALOME::SALOME_Exception ); /*! * Return errors of hypotheses definintion * algo_error_array is empty if everything is OK */ - algo_error_array GetAlgoState( in SMESH_Mesh theMesh, + algo_error_array GetAlgoState( in SMESH_Mesh theMesh, in GEOM::GEOM_Object theSubObject ) raises ( SALOME::SALOME_Exception ); @@ -261,19 +330,19 @@ module SMESH * Return errors of mesh computation * compute_error_array is empty if everything is OK */ - compute_error_array GetComputeErrors( in SMESH_Mesh theMesh, + compute_error_array GetComputeErrors( in SMESH_Mesh theMesh, in GEOM::GEOM_Object theSubObject ) raises ( SALOME::SALOME_Exception ); /*! - * Return mesh elements preventing computation of a subshape + * Return mesh elements preventing computation of a sub-shape */ MeshPreviewStruct GetBadInputElements( in SMESH_Mesh theMesh, in short theSubShapeID ) raises ( SALOME::SALOME_Exception ); /*! - * Return indeces of faces, edges and vertices of given subshapes + * Return indeces of faces, edges and vertices of given sub-shapes * within theMainObject */ long_array GetSubShapesId( in GEOM::GEOM_Object theMainObject, @@ -282,7 +351,7 @@ module SMESH /*! * Return geometrical object the given element is built on. - * The returned geometrical object, if not nil, is either found in the + * The returned geometrical object, if not nil, is either found in the * study or is published by this method with the given name */ GEOM::GEOM_Object GetGeometryByMeshElement( in SMESH_Mesh theMesh, @@ -295,42 +364,25 @@ module SMESH * The returned geometrical object not published in study by this method. */ GEOM::GEOM_Object FindGeometryByMeshElement( in SMESH_Mesh theMesh, - in long theElementID) + in long theElementID) raises ( SALOME::SALOME_Exception ); /*! - * Concatenate the given meshes into one mesh. - * Union groups with the same name and type if - * theUniteIdenticalGroups flag is true. - * Merge coincident nodes and elements if - * theMergeNodesAndElements flag is true. + * \brief Return id of object, registered in current study context + * + * Can be used to check if the object was created in the same container, as this engine. */ - SMESH_Mesh Concatenate(in mesh_array theMeshesArray, - in boolean theUniteIdenticalGroups, - in boolean theMergeNodesAndElements, - in double theMergeTolerance) - raises ( SALOME::SALOME_Exception ); + long GetObjectId(in Object theObject); /*! - * Concatenate the given meshes into one mesh. - * Union groups with the same name and type if - * theUniteIdenticalGroups flag is true. - * Merge coincident nodes and elements if - * theMergeNodesAndElements flag is true. - * Create the groups of all elements from initial meshes. + * \brief Get MED version of the file by its name. */ - SMESH_Mesh ConcatenateWithGroups(in mesh_array theMeshesArray, - in boolean theUniteIdenticalGroups, - in boolean theMergeNodesAndElements, - in double theMergeTolerance) - raises ( SALOME::SALOME_Exception ); + boolean GetMEDVersion(in string theFileName, out MED_VERSION theVersion); /*! - * \brief Return id of object, registered in current study context - * - * Can be used to check if the object was created in the same container, as this engine. + * \brief Get names of meshes defined in file with the specified name. */ - long GetObjectId(in Object theObject); + string_array GetMeshNames(in string theFileName); }; }; diff --git a/idl/SMESH_Group.idl b/idl/SMESH_Group.idl index aa9cee125..d00fe0e9d 100644 --- a/idl/SMESH_Group.idl +++ b/idl/SMESH_Group.idl @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Group.idl // Author : Sergey ANIKIN, OCC // $Header$ @@ -35,6 +36,7 @@ module SMESH { interface Predicate; + interface Filter; /*! * SMESH_Group: base interface of group object @@ -82,9 +84,21 @@ module SMESH long_array GetListOfID(); /*! - * Returns the mesh object this group belongs to + * Get the number of nodes of cells included to the group + * For a nodal group returns the same value as Size() function + */ + long GetNumberOfNodes(); + + /*! + * Get IDs of nodes of cells included to the group + * For a nodal group returns result of GetListOfID() function + */ + long_array GetNodeIDs(); + + /*! + * Return true if GetNumberOfNodes() won't take a long time for computation */ - SMESH_Mesh GetMesh(); + boolean IsNodeInfoAvailable(); /*! * Sets group color @@ -110,7 +124,7 @@ module SMESH }; /*! - * SMESH_Group: interface of group object + * SMESH_Group: interface of a standalone group object */ interface SMESH_Group : SMESH_GroupBase { @@ -120,26 +134,46 @@ module SMESH void Clear(); /*! - * Adds elements to the group + * Adds elements or nodes with specified identifiers to the group */ long Add( in long_array elem_ids ); + /*! + * Adds elements or nodes that match specified predicate to the group + */ long AddByPredicate( in Predicate thePredicate ); + /*! + * Add all elements or nodes from the specified source to the group + */ + long AddFrom( in SMESH_IDSource theSource ); /*! - * Removes elements from the group + * Removes elements or nodes with specified identifiers from the group */ long Remove( in long_array elem_ids ); + /*! + * Removes elements or nodes that match specified predicate from the group + */ long RemoveByPredicate( in Predicate thePredicate ); }; + /*! - * SMESH_Group: interface of group object linked to geometry + * SMESH_GroupOnGeom: interface of a group object linked to geometry */ interface SMESH_GroupOnGeom : SMESH_GroupBase { GEOM::GEOM_Object GetShape(); }; + /*! + * SMESH_GroupOnFilter: interface of a group object defined by filter + */ + interface SMESH_GroupOnFilter : SMESH_GroupBase + { + void SetFilter( in Filter theFilter); + Filter GetFilter(); + }; + }; diff --git a/idl/SMESH_Hypothesis.idl b/idl/SMESH_Hypothesis.idl index 56ae48b0c..8d18789ba 100644 --- a/idl/SMESH_Hypothesis.idl +++ b/idl/SMESH_Hypothesis.idl @@ -1,27 +1,27 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Hypothesis.idl // Author : Paul RASCLE, EDF -// $Header$ // #ifndef _SMESH_HYPOTHESIS_IDL_ #define _SMESH_HYPOTHESIS_IDL_ @@ -59,6 +59,22 @@ module SMESH */ long GetId(); + /*! + * Set the variable parameter + * \param parameter is a string containing the notebook variable + * used for Hypothesis creation + * \param method is a name of Hypothesis method setting this parameter + * + * This method must be called before calling method() + */ + void SetVarParameter (in string parameter, in string method); + + /*! + * Return the variable parameter used for Hypothesis creation by name of method + * setting this parameter + */ + string GetVarParameter (in string methodName); + /*! * Set list of parameters * \param theParameters is a string containing the notebook variables separated by ":" symbol, diff --git a/idl/SMESH_Measurements.idl b/idl/SMESH_Measurements.idl new file mode 100644 index 000000000..6d8d7f545 --- /dev/null +++ b/idl/SMESH_Measurements.idl @@ -0,0 +1,61 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESH_Measurements.idl +// Author : Pavel Telkov, OCC +// +#ifndef _SMESH_MEASUREMENTS_IDL_ +#define _SMESH_MEASUREMENTS_IDL_ + +#include "SALOME_GenericObj.idl" +#include "SMESH_Mesh.idl" + +module SMESH +{ + + /* + * Measure component + */ + struct Measure { + double minX, minY, minZ; + double maxX, maxY, maxZ; + long node1, node2; + long elem1, elem2; + double value; + }; + + interface Measurements: SALOME::GenericObj + { + /*! + * minimal distance between two entities + */ + Measure MinDistance(in SMESH_IDSource source1, + in SMESH_IDSource source2); + + /*! + * common bounding box of entities + */ + Measure BoundingBox(in ListOfIDSources sources); + }; +}; + +#endif diff --git a/idl/SMESH_Mesh.idl b/idl/SMESH_Mesh.idl index fa5180a24..409bb4e6e 100644 --- a/idl/SMESH_Mesh.idl +++ b/idl/SMESH_Mesh.idl @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Mesh.idl // Author : Paul RASCLE, EDF // - #ifndef _SMESH_MESH_IDL_ #define _SMESH_MESH_IDL_ @@ -37,6 +37,8 @@ module SMESH typedef sequence ListOfHypothesis; interface SMESH_GroupBase; typedef sequence ListOfGroups; + interface SMESH_IDSource; + typedef sequence ListOfIDSources; typedef sequence double_array ; typedef sequence long_array ; @@ -68,7 +70,12 @@ module SMESH ADD_QUADTETRAHEDRON, ADD_QUADPYRAMID, ADD_QUADPENTAHEDRON, - ADD_QUADHEXAHEDRON + ADD_QUADHEXAHEDRON, + ADD_ELEM0D, + ADD_BIQUAD_QUADRANGLE, + ADD_TRIQUAD_HEXA, + ADD_HEXAGONAL_PRISM, + ADD_BALL }; struct log_block @@ -112,8 +119,11 @@ module SMESH NODE, EDGE, FACE, - VOLUME + VOLUME, + ELEM0D, + BALL }; + typedef sequence array_of_ElementType ; /*! * Enumeration for element geometry type, like in SMDS @@ -123,13 +133,15 @@ module SMESH Geom_POINT, Geom_EDGE, Geom_TRIANGLE, - Geom_QUADRANGLE, - Geom_POLYGON, + Geom_QUADRANGLE, // when a new GeometryType is added, please + Geom_POLYGON, // update a corresponding list in SMESH_2smeshpy.cxx, ln 665 Geom_TETRA, Geom_PYRAMID, Geom_HEXA, Geom_PENTA, - Geom_POLYHEDRA + Geom_HEXAGONAL_PRISM, + Geom_POLYHEDRA, + Geom_BALL }; /*! @@ -141,6 +153,41 @@ module SMESH ORDER_QUADRATIC /*! entities of 2nd order */ }; + + /*! + * Enumeration of entity type used in mesh info array, + * it should be synchronised with enum SMDSAbs_EntityType + */ + enum EntityType + { + Entity_Node, + Entity_0D, + Entity_Edge, + Entity_Quad_Edge, + Entity_Triangle, + Entity_Quad_Triangle, + Entity_Quadrangle, + Entity_Quad_Quadrangle, + Entity_BiQuad_Quadrangle, + Entity_Polygon, + Entity_Quad_Polygon, + Entity_Tetra, + Entity_Quad_Tetra, + Entity_Pyramid, + Entity_Quad_Pyramid, + Entity_Hexa, + Entity_Quad_Hexa, + Entity_TriQuad_Hexa, + Entity_Penta, + Entity_Quad_Penta, + Entity_Hexagonal_Prism, + Entity_Polyhedra, + Entity_Quad_Polyhedra, + Entity_Ball, + Entity_Last + }; + + /*! * Enumeration for hypothesis status (used by AddHypothesis() and RemoveHypothesis() methods) */ @@ -158,7 +205,7 @@ module SMESH HYP_NOTCONFORM, // not conform mesh is produced appling a hypothesis HYP_ALREADY_EXIST,// such hypothesis already exist HYP_BAD_DIM, // bad dimension - HYP_BAD_SUBSHAPE, // shape is neither the main one, nor its subshape, nor a group + HYP_BAD_SUBSHAPE, // shape is neither the main one, nor its sub-shape, nor a group HYP_BAD_GEOMETRY, // geometry mismatches algorithm's expectation HYP_NEED_SHAPE // algorithm can work on shape only }; @@ -212,18 +259,49 @@ module SMESH long_array elementConnectivities; types_array elementTypes; }; + interface SMESH_Mesh; + interface SMESH_IDSource { /*! * Returns a sequence of all element IDs */ long_array GetIDs(); + + /*! + * Returns statistic of mesh elements + * @return array of number enityties by index of EntityType + */ + long_array GetMeshInfo(); + + /*! + * Returns types of elements it contains. + * It's empty if the SMESH_IDSource contains no IDs + */ + array_of_ElementType GetTypes(); + + /*! + * Returns the mesh + */ + SMESH_Mesh GetMesh(); + + /*! + * Returns false if GetMeshInfo() returns incorrect information that may + * happen if mesh data is not yet fully loaded from the file of study. + */ + boolean IsMeshInfoCorrect(); }; interface SMESH_Group; interface SMESH_GroupOnGeom; + interface Filter; + interface SMESH_GroupOnFilter; interface SMESH_subMesh; interface SMESH_MeshEditor; + + typedef sequence submesh_array; + typedef sequence submesh_array_array; + interface SMESH_Mesh : SALOME::GenericObj, SMESH_IDSource { /*! @@ -239,12 +317,30 @@ module SMESH GEOM::GEOM_Object GetShapeToMesh() raises (SALOME::SALOME_Exception); + /*! + * Return false if the mesh is not yet fully loaded from the study file + */ + boolean IsLoaded() + raises (SALOME::SALOME_Exception); + + /*! + * Load full mesh data from the study file + */ + void Load() + raises (SALOME::SALOME_Exception); + /*! * Remove all nodes and elements */ void Clear() raises (SALOME::SALOME_Exception); + /*! + * Get the list of sub-meshes existing in the mesh + */ + submesh_array GetSubMeshes() + raises (SALOME::SALOME_Exception); + /*! * Remove all nodes and elements of submesh */ @@ -252,9 +348,9 @@ module SMESH raises (SALOME::SALOME_Exception); /*! - * Get the subMesh object associated to a subShape. The subMesh object + * Get the subMesh object associated to a Sub-shape. The subMesh object * gives access to nodes and elements IDs. - * SubMesh will be used instead of SubShape in a next idl version to + * SubMesh will be used instead of Sub-shape in a next idl version to * adress a specific subMesh... */ SMESH_subMesh GetSubMesh(in GEOM::GEOM_Object aSubObject, in string name) @@ -271,15 +367,23 @@ module SMESH * Create a group */ SMESH_Group CreateGroup( in ElementType elem_type, - in string name ) + in string name ) + raises (SALOME::SALOME_Exception); + + /*! + * Create a group from geometry + */ + SMESH_GroupOnGeom CreateGroupFromGEOM( in ElementType elemType, + in string name, + in GEOM::GEOM_Object geomObject ) raises (SALOME::SALOME_Exception); /*! - * Create a group from geometry group + * Create a group from filter */ - SMESH_GroupOnGeom CreateGroupFromGEOM( in ElementType elem_type, - in string name, - in GEOM::GEOM_Object theGeomObject ) + SMESH_GroupOnFilter CreateGroupFromFilter( in ElementType elemType, + in string name, + in SMESH::Filter filter ) raises (SALOME::SALOME_Exception); /*! @@ -376,26 +480,26 @@ module SMESH raises (SALOME::SALOME_Exception); /*! - * Convert group on geometry into standalone group + * Convert group on geometry or on filter into standalone group */ - SMESH_Group ConvertToStandalone( in SMESH_GroupOnGeom theGeomGroup ) + SMESH_Group ConvertToStandalone( in SMESH_GroupBase theGroupOn ) raises (SALOME::SALOME_Exception); /*! - * Add hypothesis to the mesh, under a particular subShape + * Add hypothesis to the mesh, under a particular Sub-shape * (or the main shape itself) * The Add method is only used to prepare the build of the mesh and store * the algorithms and associated parameters. * Actual job of mesh the shape is done by MESH_Gen. * @params - * - aSubShape : subShape obtained by a shape explode in GEOM + * - aSubShape : sub-shape obtained by a shape explode in GEOM * (or main shape) * - anHyp : hypothesis object * @return - * - OK if the hypothesis is compatible with the subShape - * (and all previous hypothesis on the subShape) - * - NOK if the hypothesis is not compatible with the subShape - * (or one previous hypothesis on the subShape) + * - OK if the hypothesis is compatible with the sub-shape + * (and all previous hypothesis on the sub-shape) + * - NOK if the hypothesis is not compatible with the sub-shape + * (or one previous hypothesis on the sub-shape) * raises exception if hypothesis has not been created */ Hypothesis_Status AddHypothesis(in GEOM::GEOM_Object aSubObject, @@ -415,7 +519,7 @@ module SMESH // raises (SALOME::SALOME_Exception); /*! - * Get the list of hypothesis added on a subShape + * Get the list of hypothesis added on a sub-shape */ ListOfHypothesis GetHypothesisList(in GEOM::GEOM_Object aSubObject) raises (SALOME::SALOME_Exception); @@ -477,6 +581,13 @@ module SMESH SMESH_MeshEditor GetMeshEditPreviewer() raises (SALOME::SALOME_Exception); + /*! + * Return true if the mesh has been edited since a total re-compute + * and those modifications may prevent successful partial re-compute + */ + boolean HasModificationsToDiscard() + raises (SALOME::SALOME_Exception); + /*! Check group names for duplications. * Consider maximum group name length stored in MED file. */ @@ -485,22 +596,56 @@ module SMESH /*! * Export Mesh to different MED Formats * @params + * - file : name of the MED file * - auto_groups : boolean parameter for creating/not creating * the groups Group_On_All_Nodes, Group_On_All_Faces, ... ; * the typical use is auto_groups=false. - * - theVersion : define the version of format of MED file, that will be created + * - version : define the version of format of MED file, that will be created + * - overwrite : boolean parameter for overwriting/not overwriting the file, if it exists + */ + void ExportToMEDX( in string file, + in boolean auto_groups, + in MED_VERSION version, + in boolean overwrite ) raises (SALOME::SALOME_Exception); + + /*! + * Export a part of Mesh into a MED file + * @params + * - meshPart : a part of mesh to store + * - file : name of the MED file + * - version : define the version of format of MED file, that will be created + * - overwrite : boolean parameter for overwriting/not overwriting the file, if it exists + */ + void ExportPartToMED( in SMESH_IDSource meshPart, + in string file, + in boolean auto_groups, + in MED_VERSION version, + in boolean overwrite ) raises (SALOME::SALOME_Exception); + + /*! + * Export Mesh to different MED Formats + * Works, just the same as ExportToMEDX, with overwrite parameter equal to true. + * The method is kept in order to support old functionality */ void ExportToMED( in string file, in boolean auto_groups, in MED_VERSION theVersion ) raises (SALOME::SALOME_Exception); /*! * Export Mesh to MED_V2_1 MED format - * Works, just the same as ExportToMED, with MED_VERSION parameter equal to MED_V2_1. + * Works, just the same as ExportToMEDX with MED_VERSION parameter equal to MED_V2_1 + * and overwrite parameter equal to true * The method is kept in order to support old functionality */ void ExportMED( in string file, in boolean auto_groups ) raises (SALOME::SALOME_Exception); + /*! + * Export Mesh to SAUV formatted file + * Write a temporary med file and use med2sauv + */ + void ExportSAUV( in string file, in boolean auto_groups ) + raises (SALOME::SALOME_Exception); + /*! * Return string representation of a MED file version comprising nbDigits */ @@ -510,12 +655,20 @@ module SMESH * Export Mesh to DAT, UNV and STL Formats * (UNV supported version is I-DEAS 10) */ - void ExportDAT( in string file ) - raises (SALOME::SALOME_Exception); - void ExportUNV( in string file ) - raises (SALOME::SALOME_Exception); - void ExportSTL( in string file, in boolean isascii ) - raises (SALOME::SALOME_Exception); + void ExportDAT( in string file ) raises (SALOME::SALOME_Exception); + void ExportUNV( in string file ) raises (SALOME::SALOME_Exception); + void ExportSTL( in string file, + in boolean isascii ) raises (SALOME::SALOME_Exception); + void ExportCGNS( in SMESH_IDSource meshPart, + in string file, + in boolean overwrite ) raises (SALOME::SALOME_Exception); + void ExportPartToDAT( in SMESH_IDSource meshPart, + in string file ) raises (SALOME::SALOME_Exception); + void ExportPartToUNV( in SMESH_IDSource meshPart, + in string file ) raises (SALOME::SALOME_Exception); + void ExportPartToSTL( in SMESH_IDSource meshPart, + in string file, + in boolean isascii ) raises (SALOME::SALOME_Exception); /*! * Get MED Mesh @@ -532,6 +685,12 @@ module SMESH long NbElements() raises (SALOME::SALOME_Exception); + long Nb0DElements() + raises (SALOME::SALOME_Exception); + + long NbBalls() + raises (SALOME::SALOME_Exception); + long NbEdges() raises (SALOME::SALOME_Exception); @@ -556,6 +715,9 @@ module SMESH long NbQuadranglesOfOrder(in ElementOrder order) raises (SALOME::SALOME_Exception); + long NbBiQuadQuadrangles() + raises (SALOME::SALOME_Exception); + long NbPolygons() raises (SALOME::SALOME_Exception); @@ -577,6 +739,9 @@ module SMESH long NbHexasOfOrder(in ElementOrder order) raises (SALOME::SALOME_Exception); + long NbTriQuadraticHexas() + raises (SALOME::SALOME_Exception); + long NbPyramids() raises (SALOME::SALOME_Exception); @@ -589,6 +754,9 @@ module SMESH long NbPrismsOfOrder(in ElementOrder order) raises (SALOME::SALOME_Exception); + long NbHexagonalPrisms() + raises (SALOME::SALOME_Exception); + long NbPolyhedrons() raises (SALOME::SALOME_Exception); @@ -610,6 +778,9 @@ module SMESH ElementType GetElementType( in long id, in boolean iselem ) raises (SALOME::SALOME_Exception); + EntityType GetElementGeomType( in long id ) + raises (SALOME::SALOME_Exception); + long_array GetSubMeshElementsId(in long ShapeID) raises (SALOME::SALOME_Exception); @@ -619,6 +790,22 @@ module SMESH ElementType GetSubMeshElementType(in long ShapeID) raises (SALOME::SALOME_Exception); + + /*! + * Methods to set meshing order of submeshes + */ + + /*! + * \brief Return submesh objects list in meshing order + */ + submesh_array_array GetMeshOrder(); + + /*! + * \brief Set submesh object order + */ + boolean SetMeshOrder(in submesh_array_array theSubMeshArray); + + /*! * Get mesh description */ @@ -699,6 +886,16 @@ module SMESH */ long ElemNbFaces(in long id); + /*! + * Returns nodes of given face (counted from zero) for given volumic element. + */ + long_array GetElemFaceNodes(in long elemId, in short faceIndex); + + /*! + * Returns an element based on all given nodes. + */ + long FindElementByNodes(in long_array nodes); + /*! * Returns true if given element is polygon */ @@ -709,6 +906,11 @@ module SMESH */ boolean IsQuadratic(in long id); + /*! + * Returns diameter of a ball discrete element or zero in case of an invalid \a id + */ + double GetBallDiameter(in long id); + /*! * Returns XYZ coordinates of bary center for given element * as list of double diff --git a/idl/SMESH_MeshEditor.idl b/idl/SMESH_MeshEditor.idl index 0a4b5b7cd..561c5b884 100644 --- a/idl/SMESH_MeshEditor.idl +++ b/idl/SMESH_MeshEditor.idl @@ -1,26 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // // File : SMESH_MeshEditor.idl -// #ifndef _SMESH_MESHEDITOR_IDL_ #define _SMESH_MESHEDITOR_IDL_ @@ -29,21 +28,65 @@ module SMESH { + enum Bnd_Dimension { BND_2DFROM3D, BND_1DFROM3D, BND_1DFROM2D }; + /*! * This interface makes modifications on the Mesh - removing elements and nodes etc. */ interface NumericalFunctor; interface SMESH_MeshEditor { + /*! + * \brief Wrap a sequence of ids in a SMESH_IDSource + * \param IDsOfElements list of mesh elements identifiers + * \return new ID source object + */ + SMESH_IDSource MakeIDSource(in long_array IDsOfElements, in ElementType type); + + /*! + * \brief Remove mesh elements specified by their identifiers. + * \param IDsOfElements list of mesh elements identifiers + * \return \c true if elements are correctly removed or \c false otherwise + */ boolean RemoveElements(in long_array IDsOfElements); + /*! + * \brief Remove mesh nodes specified by their identifiers. + * \param IDsOfNodes list of mesh nodes identifiers + * \return \c true if nodes are correctly removed or \c false otherwise + */ boolean RemoveNodes(in long_array IDsOfNodes); + /*! + * \brief Remove all orphan nodes. + * \return number of removed nodes + */ + long RemoveOrphanNodes(); + + /*! + * \brief Add a new node. + * \param x X coordinate of new node + * \param y Y coordinate of new node + * \param z Z coordinate of new node + * \return integer identifier of new node + */ long AddNode(in double x, in double y, in double z); /*! - * Create edge, either linear and quadratic (this is determed - * by number of given nodes). + * Create a 0D element on the given node. + * \param IdOfNode Node IDs for creation of element. + */ + long Add0DElement(in long IDOfNode); + + /*! + * Create a ball element on the given node. + * \param IdOfNode Node IDs for creation of element. + */ + long AddBall(in long IDOfNode, in double diameter); + + /*! + * Create an edge, either linear and quadratic (this is determed + * by number of given nodes, two or three). * \param IdsOfNodes List of node IDs for creation of element. * Needed order of nodes in this list corresponds to description * of MED. This description is located by the following link: @@ -139,6 +182,19 @@ module SMESH boolean Reorient(in long_array IDsOfElements); boolean ReorientObject(in SMESH_IDSource theObject); + /*! + * \brief Reorient faces contained in \a the2Dgroup. + * \param the2Dgroup - the mesh or its part to reorient + * \param theDirection - desired direction of normal of \a theFace + * \param theFace - ID of face whose orientation is checked. + * It can be < 1 then \a thePoint is used to find a face. + * \param thePoint - is used to find a face if \a theFace < 1. + * \return number of reoriented elements. + */ + long Reorient2D(in SMESH_IDSource the2Dgroup, + in DirStruct theDirection, + in long theFace, + in PointStruct thePoint) raises (SALOME::SALOME_Exception); /*! * \brief Fuse neighbour triangles into quadrangles. @@ -149,8 +205,8 @@ module SMESH * \return TRUE in case of success, FALSE otherwise. */ boolean TriToQuad (in long_array IDsOfElements, - in NumericalFunctor Criterion, - in double MaxAngle); + in NumericalFunctor Criterion, + in double MaxAngle); /*! * \brief Fuse neighbour triangles into quadrangles. @@ -158,8 +214,8 @@ module SMESH * Behaves like the above method, taking list of elements from \a theObject */ boolean TriToQuadObject (in SMESH_IDSource theObject, - in NumericalFunctor Criterion, - in double MaxAngle); + in NumericalFunctor Criterion, + in double MaxAngle); /*! * \brief Split quadrangles into triangles. @@ -168,7 +224,7 @@ module SMESH * \return TRUE in case of success, FALSE otherwise. */ boolean QuadToTri (in long_array IDsOfElements, - in NumericalFunctor Criterion); + in NumericalFunctor Criterion); /*! * \brief Split quadrangles into triangles. @@ -176,7 +232,7 @@ module SMESH * Behaves like the above method, taking list of elements from \a theObject */ boolean QuadToTriObject (in SMESH_IDSource theObject, - in NumericalFunctor Criterion); + in NumericalFunctor Criterion); /*! * \brief Split quadrangles into triangles. @@ -185,7 +241,7 @@ module SMESH * \return TRUE in case of success, FALSE otherwise. */ boolean SplitQuad (in long_array IDsOfElements, - in boolean Diag13); + in boolean Diag13); /*! * \brief Split quadrangles into triangles. @@ -193,7 +249,7 @@ module SMESH * Behaves like the above method, taking list of elements from \a theObject */ boolean SplitQuadObject (in SMESH_IDSource theObject, - in boolean Diag13); + in boolean Diag13); /*! * Find better splitting of the given quadrangle. @@ -203,7 +259,19 @@ module SMESH * diagonal is better, 0 if error occurs. */ long BestSplit (in long IDOfQuad, - in NumericalFunctor Criterion); + in NumericalFunctor Criterion); + + /*! + * \brief Split volumic elements into tetrahedrons + * \param elems - elements to split + * \param methodFlags - flags passing splitting method: + * 1 - split the hexahedron into 5 tetrahedrons + * 2 - split the hexahedron into 6 tetrahedrons + * 3 - split the hexahedron into 24 tetrahedrons + */ + void SplitVolumesIntoTetra(in SMESH_IDSource elems, in short methodFlags) + raises (SALOME::SALOME_Exception); + enum Smooth_Method { LAPLACIAN_SMOOTH, CENTROIDAL_SMOOTH }; @@ -214,10 +282,10 @@ module SMESH in Smooth_Method Method); boolean SmoothObject(in SMESH_IDSource theObject, - in long_array IDsOfFixedNodes, - in long MaxNbOfIterations, - in double MaxAspectRatio, - in Smooth_Method Method); + in long_array IDsOfFixedNodes, + in long MaxNbOfIterations, + in double MaxAspectRatio, + in Smooth_Method Method); boolean SmoothParametric(in long_array IDsOfElements, in long_array IDsOfFixedNodes, @@ -235,6 +303,13 @@ module SMESH boolean ConvertFromQuadratic(); + void ConvertToQuadraticObject(in boolean theForce3d, + in SMESH_IDSource theObject) + raises (SALOME::SALOME_Exception); + + void ConvertFromQuadraticObject(in SMESH_IDSource theObject) + raises (SALOME::SALOME_Exception); + void RenumberNodes(); void RenumberElements(); @@ -268,10 +343,10 @@ module SMESH * \param NbOfSteps - number of elements to generate from one element */ void RotationSweepObject(in SMESH_IDSource theObject, - in AxisStruct Axix, - in double AngleInRadians, - in long NbOfSteps, - in double Tolerance); + in AxisStruct Axix, + in double AngleInRadians, + in long NbOfSteps, + in double Tolerance); /*! * \brief Same as previous but additionally create groups of elements * generated from elements belonging to preexisting groups @@ -289,19 +364,19 @@ module SMESH * \param NbOfSteps - number of elements to generate from one element */ void RotationSweepObject1D(in SMESH_IDSource theObject, - in AxisStruct Axix, - in double AngleInRadians, - in long NbOfSteps, - in double Tolerance); + in AxisStruct Axix, + in double AngleInRadians, + in long NbOfSteps, + in double Tolerance); /*! * \brief Same as previous but additionally create groups of elements * generated from elements belonging to preexisting groups */ ListOfGroups RotationSweepObject1DMakeGroups(in SMESH_IDSource theObject, - in AxisStruct Axix, - in double AngleInRadians, - in long NbOfSteps, - in double Tolerance); + in AxisStruct Axix, + in double AngleInRadians, + in long NbOfSteps, + in double Tolerance); /*! * \brief Genarate dim+1 elements by rotation of the object around axis * \param theObject - object containing elements to ratate @@ -310,19 +385,19 @@ module SMESH * \param NbOfSteps - number of elements to generate from one element */ void RotationSweepObject2D(in SMESH_IDSource theObject, - in AxisStruct Axix, - in double AngleInRadians, - in long NbOfSteps, - in double Tolerance); + in AxisStruct Axix, + in double AngleInRadians, + in long NbOfSteps, + in double Tolerance); /*! * \brief Same as previous but additionally create groups of elements * generated from elements belonging to preexisting groups */ ListOfGroups RotationSweepObject2DMakeGroups(in SMESH_IDSource theObject, - in AxisStruct Axix, - in double AngleInRadians, - in long NbOfSteps, - in double Tolerance); + in AxisStruct Axix, + in double AngleInRadians, + in long NbOfSteps, + in double Tolerance); /*! * \brief Genarate dim+1 elements by extrusion of elements along vector * \param IDsOfElements - elements to sweep @@ -332,6 +407,15 @@ module SMESH void ExtrusionSweep(in long_array IDsOfElements, in DirStruct StepVector, in long NbOfSteps); + /*! + * \brief Genarate dim+1 elements by extrusion of elements along vector + * \param IDsOfElements - elements to sweep + * \param StepVector - vector giving direction and distance of an extrusion step + * \param NbOfSteps - number of elements to generate from one element + */ + void ExtrusionSweep0D(in long_array IDsOfElements, + in DirStruct StepVector, + in long NbOfSteps); /*! * \brief Same as previous but additionally create groups of elements * generated from elements belonging to preexisting groups @@ -340,7 +424,13 @@ module SMESH in DirStruct StepVector, in long NbOfSteps); /*! - * Generate new elements by extrusion of theElements + * \brief Same as previous but elements are nodes + */ + ListOfGroups ExtrusionSweepMakeGroups0D(in long_array IDsOfElements, + in DirStruct StepVector, + in long NbOfSteps); + /*! + * Generate new elements by extrusion of theElements * by StepVector by NbOfSteps * param ExtrFlags set flags for performing extrusion * param SewTolerance - uses for comparing locations of nodes if flag @@ -349,8 +439,8 @@ module SMESH void AdvancedExtrusion(in long_array IDsOfElements, in DirStruct StepVector, in long NbOfSteps, - in long ExtrFlags, - in double SewTolerance); + in long ExtrFlags, + in double SewTolerance); /*! * \brief Same as previous but additionally create groups of elements * generated from elements belonging to preexisting groups @@ -362,22 +452,29 @@ module SMESH in double SewTolerance); void ExtrusionSweepObject(in SMESH_IDSource theObject, - in DirStruct StepVector, - in long NbOfSteps); + in DirStruct StepVector, + in long NbOfSteps); ListOfGroups ExtrusionSweepObjectMakeGroups(in SMESH_IDSource theObject, in DirStruct StepVector, in long NbOfSteps); + void ExtrusionSweepObject0D(in SMESH_IDSource theObject, + in DirStruct StepVector, + in long NbOfSteps); + ListOfGroups ExtrusionSweepObject0DMakeGroups(in SMESH_IDSource theObject, + in DirStruct StepVector, + in long NbOfSteps); + void ExtrusionSweepObject1D(in SMESH_IDSource theObject, - in DirStruct StepVector, - in long NbOfSteps); + in DirStruct StepVector, + in long NbOfSteps); ListOfGroups ExtrusionSweepObject1DMakeGroups(in SMESH_IDSource theObject, in DirStruct StepVector, in long NbOfSteps); void ExtrusionSweepObject2D(in SMESH_IDSource theObject, - in DirStruct StepVector, - in long NbOfSteps); + in DirStruct StepVector, + in long NbOfSteps); ListOfGroups ExtrusionSweepObject2DMakeGroups(in SMESH_IDSource theObject, in DirStruct StepVector, in long NbOfSteps); @@ -392,14 +489,38 @@ module SMESH EXTR_CANT_GET_TANGENT }; + ListOfGroups ExtrusionAlongPathX(in long_array IDsOfElements, + in SMESH_IDSource Path, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean LinearVariation, + in boolean HasRefPoint, + in PointStruct RefPoint, + in boolean MakeGroups, + in ElementType ElemType, + out Extrusion_Error Error); + + ListOfGroups ExtrusionAlongPathObjX(in SMESH_IDSource theObject, + in SMESH_IDSource Path, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean LinearVariation, + in boolean HasRefPoint, + in PointStruct RefPoint, + in boolean MakeGroups, + in ElementType ElemType, + out Extrusion_Error Error); + Extrusion_Error ExtrusionAlongPath(in long_array IDsOfElements, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint); ListOfGroups ExtrusionAlongPathMakeGroups(in long_array IDsOfElements, in SMESH_Mesh PathMesh, in GEOM::GEOM_Object PathShape, @@ -411,13 +532,13 @@ module SMESH out Extrusion_Error Error); Extrusion_Error ExtrusionAlongPathObject(in SMESH_IDSource theObject, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint); ListOfGroups ExtrusionAlongPathObjectMakeGroups(in SMESH_IDSource theObject, in SMESH_Mesh PathMesh, in GEOM::GEOM_Object PathShape, @@ -429,47 +550,47 @@ module SMESH out Extrusion_Error Error); Extrusion_Error ExtrusionAlongPathObject1D(in SMESH_IDSource theObject, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint); ListOfGroups ExtrusionAlongPathObject1DMakeGroups(in SMESH_IDSource theObject, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint, - out Extrusion_Error Error); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint, + out Extrusion_Error Error); Extrusion_Error ExtrusionAlongPathObject2D(in SMESH_IDSource theObject, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint); ListOfGroups ExtrusionAlongPathObject2DMakeGroups(in SMESH_IDSource theObject, - in SMESH_Mesh PathMesh, - in GEOM::GEOM_Object PathShape, - in long NodeStart, - in boolean HasAngles, - in double_array Angles, - in boolean HasRefPoint, - in PointStruct RefPoint, - out Extrusion_Error Error); + in SMESH_Mesh PathMesh, + in GEOM::GEOM_Object PathShape, + in long NodeStart, + in boolean HasAngles, + in double_array Angles, + in boolean HasRefPoint, + in PointStruct RefPoint, + out Extrusion_Error Error); /*! * Compute rotation angles for ExtrusionAlongPath as linear variation * of given angles along path steps - * param PathMesh mesh containing a 1D sub-mesh on the edge, along + * param PathMesh mesh containing a 1D sub-mesh on the edge, along * which proceeds the extrusion - * param PathShape is shape(edge); as the mesh can be complex, the edge + * param PathShape is shape(edge); as the mesh can be complex, the edge * is used to define the sub-mesh for the path */ double_array LinearAnglesVariation(in SMESH_Mesh PathMesh, @@ -492,9 +613,9 @@ module SMESH in string MeshName); void MirrorObject (in SMESH_IDSource theObject, - in AxisStruct Mirror, - in MirrorType Type, - in boolean Copy); + in AxisStruct Mirror, + in MirrorType Type, + in boolean Copy); ListOfGroups MirrorObjectMakeGroups (in SMESH_IDSource theObject, in AxisStruct Mirror, in MirrorType Type); @@ -515,8 +636,8 @@ module SMESH in string MeshName); void TranslateObject (in SMESH_IDSource theObject, - in DirStruct Vector, - in boolean Copy); + in DirStruct Vector, + in boolean Copy); ListOfGroups TranslateObjectMakeGroups (in SMESH_IDSource theObject, in DirStruct Vector); SMESH_Mesh TranslateObjectMakeMesh (in SMESH_IDSource theObject, @@ -524,6 +645,19 @@ module SMESH in boolean CopyGroups, in string MeshName); + void Scale (in SMESH_IDSource theObject, + in PointStruct thePoint, + in double_array theScaleFact, + in boolean Copy); + ListOfGroups ScaleMakeGroups (in SMESH_IDSource theObject, + in PointStruct thePoint, + in double_array theScaleFact); + SMESH_Mesh ScaleMakeMesh (in SMESH_IDSource theObject, + in PointStruct thePoint, + in double_array theScaleFact, + in boolean CopyGroups, + in string MeshName); + void Rotate (in long_array IDsOfElements, in AxisStruct Axis, in double AngleInRadians, @@ -538,9 +672,9 @@ module SMESH in string MeshName); void RotateObject (in SMESH_IDSource theObject, - in AxisStruct Axis, - in double AngleInRadians, - in boolean Copy); + in AxisStruct Axis, + in double AngleInRadians, + in boolean Copy); ListOfGroups RotateObjectMakeGroups (in SMESH_IDSource theObject, in AxisStruct Axis, in double AngleInRadians); @@ -554,8 +688,13 @@ module SMESH out array_of_long_array GroupsOfNodes); void FindCoincidentNodesOnPart (in SMESH_IDSource SubMeshOrGroup, - in double Tolerance, - out array_of_long_array GroupsOfNodes); + in double Tolerance, + out array_of_long_array GroupsOfNodes); + + void FindCoincidentNodesOnPartBut (in SMESH_IDSource SubMeshOrGroup, + in double Tolerance, + out array_of_long_array GroupsOfNodes, + in ListOfIDSources ExceptSubMeshOrGroups); void MergeNodes (in array_of_long_array GroupsOfNodes); @@ -565,7 +704,7 @@ module SMESH * \return List of groups of equal elements. */ void FindEqualElements (in SMESH_IDSource MeshOrSubMeshOrGroup, - out array_of_long_array GroupsOfElementsID); + out array_of_long_array GroupsOfElementsID); /*! * \brief Merge elements in each given group. @@ -577,13 +716,41 @@ module SMESH * \brief Merge equal elements in the whole mesh. */ void MergeEqualElements(); - + /*! * If the given ID is a valid node ID (nodeID > 0), just move this node, else * move the node closest to the point to point's location and return ID of the node */ long MoveClosestNodeToPoint(in double x, in double y, in double z, in long nodeID); + /*! + * Return ID of node closest to a given point + */ + long FindNodeClosestTo(in double x, in double y, in double z); + + /*! + * Return elements of given type where the given point is IN or ON. + * + * 'ALL' type means elements of any type excluding nodes and 0D elements + */ + long_array FindElementsByPoint(in double x, in double y, in double z, in ElementType type); + + /*! + * Searching among the given elements, return elements of given type + * where the given point is IN or ON. + * + * 'ALL' type means elements of any type excluding nodes and 0D elements + */ + long_array FindAmongElementsByPoint(in SMESH_IDSource elements, + in double x, in double y, in double z, + in ElementType type); + + /*! + * Return point state in a closed 2D mesh in terms of TopAbs_State enumeration. + * TopAbs_UNKNOWN state means that either mesh is wrong or the analysis fails. + */ + short GetPointState(in double x, in double y, in double z); + enum Sew_Error { SEW_OK, SEW_BORDER1_NOT_FOUND, @@ -604,8 +771,8 @@ module SMESH in long FirstNodeID2, in long SecondNodeID2, in long LastNodeID2, - in boolean CreatePolygons, - in boolean CreatePolyedrs); + in boolean CreatePolygons, + in boolean CreatePolyedrs); Sew_Error SewConformFreeBorders (in long FirstNodeID1, in long SecondNodeID1, @@ -618,8 +785,8 @@ module SMESH in long LastNodeIDOnFreeBorder, in long FirstNodeIDOnSide, in long LastNodeIDOnSide, - in boolean CreatePolygons, - in boolean CreatePolyedrs); + in boolean CreatePolygons, + in boolean CreatePolyedrs); Sew_Error SewSideElements (in long_array IDsOfSide1Elements, in long_array IDsOfSide2Elements, @@ -636,7 +803,7 @@ module SMESH boolean ChangeElemNodes(in long ide, in long_array newIDs); /*! - * Return data of mesh edition preview which is computed provided + * Return data of mesh edition preview which is computed provided * that the editor was obtained trough SMESH_Mesh::GetMeshEditPreviewer() */ MeshPreviewStruct GetPreviewData(); @@ -654,50 +821,302 @@ module SMESH * not creared - returns empty list */ long_array GetLastCreatedElems(); - + /*! * \brief Creates a hole in a mesh by doubling the nodes of some particular elements * \param theNodes - identifiers of nodes to be doubled - * \param theModifiedElems - identifiers of elements to be updated by the new (doubled) - * nodes. If list of element identifiers is empty then nodes are doubled but + * \param theModifiedElems - identifiers of elements to be updated by the new (doubled) + * nodes. If list of element identifiers is empty then nodes are doubled but * they not assigned to elements * \return TRUE if operation has been completed successfully, FALSE otherwise * \sa DoubleNode(), DoubleNodeGroup(), DoubleNodeGroups() - */ - boolean DoubleNodes( in long_array theNodes, in long_array theModifiedElems ); + */ + boolean DoubleNodes( in long_array theNodes, in long_array theModifiedElems ); /*! - * \brief Creates a hole in a mesh by doubling the nodes of some particular elements - * This method provided for convenience works as DoubleNodes() described above. - * \param theNodeId - identifier of node to be doubled. - * \param theModifiedElems - identifiers of elements to be updated. - * \return TRUE if operation has been completed successfully, FALSE otherwise - * \sa DoubleNodes(), DoubleNodeGroup(), DoubleNodeGroups() - */ - boolean DoubleNode( in long theNodeId, in long_array theModifiedElems ); + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theNodeId - identifier of node to be doubled. + * \param theModifiedElems - identifiers of elements to be updated. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodes(), DoubleNodeGroup(), DoubleNodeGroups() + */ + boolean DoubleNode( in long theNodeId, in long_array theModifiedElems ); /*! - * \brief Creates a hole in a mesh by doubling the nodes of some particular elements - * This method provided for convenience works as DoubleNodes() described above. - * \param theNodes - group of nodes to be doubled. - * \param theModifiedElems - group of elements to be updated. - * \return TRUE if operation has been completed successfully, FALSE otherwise - * \sa DoubleNode(), DoubleNodes(), DoubleNodeGroups() - */ - boolean DoubleNodeGroup( in SMESH_GroupBase theNodes, + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theNodes - group of nodes to be doubled. + * \param theModifiedElems - group of elements to be updated. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNode(), DoubleNodes(), DoubleNodeGroups(), DoubleNodeGroupNew() + */ + boolean DoubleNodeGroup( in SMESH_GroupBase theNodes, in SMESH_GroupBase theModifiedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeGroup() described above, but returns a new group with + * newly created nodes. + * \param theNodes - group of nodes to be doubled. + * \param theModifiedElems - group of elements to be updated. + * \return a new group with newly created nodes + * \sa DoubleNodeGroup() + */ + SMESH_Group DoubleNodeGroupNew( in SMESH_GroupBase theNodes, + in SMESH_GroupBase theModifiedElems ); /*! - \brief Creates a hole in a mesh by doubling the nodes of some particular elements - This method provided for convenience works as DoubleNodes() described above. - \param theNodes - list of groups of nodes to be doubled - \param theModifiedElems - list of groups of elements to be updated. - \return TRUE if operation has been completed successfully, FALSE otherwise - \sa DoubleNode(), DoubleNodeGroup(), DoubleNodes() - */ - boolean DoubleNodeGroups( in ListOfGroups theNodes, + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theNodes - list of groups of nodes to be doubled + * \param theModifiedElems - list of groups of elements to be updated. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNode(), DoubleNodeGroup(), DoubleNodes() + */ + boolean DoubleNodeGroups( in ListOfGroups theNodes, in ListOfGroups theModifiedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * Works as DoubleNodeGroups() described above, but returns a new group with + * newly created nodes. + * \param theNodes - list of groups of nodes to be doubled + * \param theModifiedElems - list of groups of elements to be updated. + * \return a new group with newly created nodes + * \sa DoubleNodeGroups() + */ + SMESH_Group DoubleNodeGroupsNew( in ListOfGroups theNodes, + in ListOfGroups theModifiedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - the list of elements (edges or faces) to be replicated + * The nodes for duplication could be found from these elements + * \param theNodesNot - list of nodes to NOT replicate + * \param theAffectedElems - the list of elements (cells and edges) to which the + * replicated nodes should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroup(), DoubleNodeGroups() + */ + boolean DoubleNodeElem( in long_array theElems, + in long_array theNodesNot, + in long_array theAffectedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - the list of elements (edges or faces) to be replicated + * The nodes for duplication could be found from these elements + * \param theNodesNot - list of nodes to NOT replicate + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroupInRegion(), DoubleNodeGroupsInRegion() + */ + boolean DoubleNodeElemInRegion( in long_array theElems, + in long_array theNodesNot, + in GEOM::GEOM_Object theShape ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodes(), DoubleNodeGroups(), DoubleNodeElemGroupNew() + */ + boolean DoubleNodeElemGroup( in SMESH_GroupBase theElems, + in SMESH_GroupBase theNodesNot, + in SMESH_GroupBase theAffectedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeElemGroup() described above, but returns a new group with + * newly created elements. + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroup() + */ + SMESH_Group DoubleNodeElemGroupNew( in SMESH_GroupBase theElems, + in SMESH_GroupBase theNodesNot, + in SMESH_GroupBase theAffectedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeElemGroup() described above, but returns two new groups: + * a group of newly created elements and a group of newly created nodes + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \param theElemGroupNeeded - to create group of new elements or not + * \param theNodeGroupNeeded - to create group of new nodes or not + * \return two new groups of newly created elements (1st) and nodes (2nd) + * \sa DoubleNodeElemGroup() + */ + ListOfGroups DoubleNodeElemGroup2New( in SMESH_GroupBase theElems, + in SMESH_GroupBase theNodesNot, + in SMESH_GroupBase theAffectedElems, + in boolean theElemGroupNeeded, + in boolean theNodeGroupNeeded); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - group of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodesInRegion(), DoubleNodeGroupsInRegion() + */ + boolean DoubleNodeElemGroupInRegion( in SMESH_GroupBase theElems, + in SMESH_GroupBase theNodesNot, + in GEOM::GEOM_Object theShape ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroup(), DoubleNodes(), DoubleNodeElemGroupsNew() + */ + boolean DoubleNodeElemGroups( in ListOfGroups theElems, + in ListOfGroups theNodesNot, + in ListOfGroups theAffectedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeElemGroups() described above, but returns a new group with + * newly created elements. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroups() + */ + SMESH_Group DoubleNodeElemGroupsNew( in ListOfGroups theElems, + in ListOfGroups theNodesNot, + in ListOfGroups theAffectedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeElemGroups() described above, but returns two new groups: + * a group of newly created elements and a group of newly created nodes. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \param theElemGroupNeeded - to create group of new elements or not + * \param theNodeGroupNeeded - to create group of new nodes or not + * \return two new groups of newly created elements (1st) and nodes (2nd) + * \sa DoubleNodeElemGroups() + */ + ListOfGroups DoubleNodeElemGroups2New( in ListOfGroups theElems, + in ListOfGroups theNodesNot, + in ListOfGroups theAffectedElems, + in boolean theElemGroupNeeded, + in boolean theNodeGroupNeeded ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroupInRegion(), DoubleNodesInRegion() + */ + boolean DoubleNodeElemGroupsInRegion( in ListOfGroups theElems, + in ListOfGroups theNodesNot, + in GEOM::GEOM_Object theShape ); + + /*! + * \brief Generates skin mesh (containing 2D cells) from 3D mesh + * The created 2D mesh elements based on nodes of free faces of boundary volumes + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ + boolean Make2DMeshFrom3D(); + + /*! + * \brief Creates missing boundary elements + * \param elements - elements whose boundary is to be checked + * \param dimension - defines type of boundary elements to create + * BND_1DFROM3D creates mesh edges on all borders of free facets of 3D elements. + * \param groupName - a name of group to store created boundary elements in, + * "" means not to create the group + * \param meshName - a name of new mesh to store created boundary elements in, + * "" means not to create the new mesh + * \param toCopyElements - if true, the checked elements will be copied into the new mesh + * else only boundary elements will be copied into the new mesh + * \param toCopyExistingBondary - if true, not only new but also pre-existing + * boundary elements will be copied into the new mesh + * \param group - returns the create group, if any + * \retval SMESH::SMESH_Mesh - the mesh where elements were added to + */ + SMESH_Mesh MakeBoundaryMesh(in SMESH_IDSource elements, + in Bnd_Dimension dimension, + in string groupName, + in string meshName, + in boolean toCopyElements, + in boolean toCopyExistingBondary, + out SMESH_Group group); + /*! + * \brief Creates missing boundary elements around either the whole mesh or + * groups of 2D elements + * \param dimension - defines type of boundary elements to create + * \param groupName - a name of group to store all boundary elements in, + * "" means not to create the group + * \param meshName - a name of a new mesh, which is a copy of the initial + * mesh + created boundary elements; "" means not to create the new mesh + * \param toCopyAll - if true, the whole initial mesh will be copied into + * the new mesh else only boundary elements will be copied into the new mesh + * \param groups - optional groups of 2D elements to make boundary around + * \param mesh - returns the mesh where elements were added to + * \param group - returns the created group, if any + * \retval long - number of added boundary elements + */ + long MakeBoundaryElements(in Bnd_Dimension dimension, + in string groupName, + in string meshName, + in boolean toCopyAll, + in ListOfIDSources groups, + out SMESH_Mesh mesh, + out SMESH_Group group) raises (SALOME::SALOME_Exception); + + /*! + * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand. + * Flat elements are mainly used by some types of mechanic calculations. + * + * The list of groups must describe a partition of the mesh volumes. + * The nodes of the internal faces at the boundaries of the groups are doubled. + * In option, the internal faces are replaced by flat elements. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * \param theDomains - list of groups of volumes + * \param createJointElems - if TRUE, create the elements + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ + boolean DoubleNodesOnGroupBoundaries( in ListOfGroups theDomains, + in boolean createJointElems ) + raises (SALOME::SALOME_Exception); + + /*! + * \brief Double nodes on some external faces and create flat elements. + * Flat elements are mainly used by some types of mechanic calculations. + * + * Each group of the list must be constituted of faces. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * \param theGroupsOfFaces - list of groups of faces + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ + boolean CreateFlatElementsOnFacesGroups( in ListOfGroups theGroupsOfFaces ); }; }; diff --git a/idl/SMESH_Pattern.idl b/idl/SMESH_Pattern.idl index 4f0bcfaa6..85b842f83 100644 --- a/idl/SMESH_Pattern.idl +++ b/idl/SMESH_Pattern.idl @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Pattern.idl // Created : Mon Aug 2 10:48:36 2004 // Author : Edward AGAPOV (eap) diff --git a/resources/Makefile.am b/resources/Makefile.am index 2ebd83edf..dcbb70ebc 100644 --- a/resources/Makefile.am +++ b/resources/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # -* Makefile *- # Author : Patrick GOLDBRONN (CEA) # Date : 28/06/2001 @@ -38,6 +36,7 @@ dist_salomeres_DATA = \ mesh_area.png \ mesh_aspect.png \ mesh_aspect_3d.png \ + mesh_biquad_quadrangle.png \ mesh_clear.png \ mesh_compute.png \ mesh_diagonal.png \ @@ -51,18 +50,27 @@ dist_salomeres_DATA = \ mesh_info.png \ advanced_mesh_info.png \ standard_mesh_info.png \ + mesh_elem_info.png \ + mesh_equal_edge.png \ + mesh_equal_face.png \ + mesh_equal_node.png \ + mesh_equal_volume.png \ mesh_whatis.png \ mesh_init.png \ mesh_length.png \ mesh_length_2d.png \ + mesh_find_elem_by_point.png \ mesh_free_edges.png \ mesh_free_edges_2d.png \ mesh_free_nodes.png \ + mesh_max_element_length_2d.png \ + mesh_max_element_length_3d.png \ mesh_multi_edges.png \ mesh_multi_edges_2d.png \ mesh_line_n.png \ mesh_line.png \ mesh_move_node.png \ + mesh_octahedron.png \ mesh_orientation.png \ mesh.png \ mesh_polygon.png \ @@ -71,8 +79,14 @@ dist_salomeres_DATA = \ mesh_pyramid.png \ mesh_quad_n.png \ mesh_quad.png \ + mesh_quadrangle_quadpref.png \ + mesh_quadrangle_quadpref_reversed.png \ + mesh_quadrangle_reduced.png \ + mesh_quadrangle_standard.png \ + mesh_quadrangle_triapref.png \ mesh_rem_element.png \ mesh_rem_node.png \ + mesh_rem_orphan_nodes.png \ mesh_shading.png \ mesh_shrink.png \ mesh_skew.png \ @@ -81,6 +95,7 @@ dist_salomeres_DATA = \ mesh_tree_algo_hexa.png \ mesh_tree_algo_mefisto.png \ mesh_tree_algo.png \ + mesh_tree_algo_0D.png \ mesh_tree_algo_quad.png \ mesh_tree_algo_regular.png \ mesh_tree_algo_tetra.png \ @@ -93,16 +108,22 @@ dist_salomeres_DATA = \ mesh_tree_importedmesh.png \ mesh_tree_mesh_warn.png \ mesh_triangle_n.png \ + mesh_triquad_hexahedron.png \ mesh_triangle.png \ mesh_update.png \ mesh_vertex_n.png \ mesh_vertex.png \ mesh_volume_3d.png \ + bare_border_volume.png \ + bare_border_face.png \ + over_constrained_volume.png \ + over_constrained_face.png \ mesh_wireframe.png \ mesh_points.png \ mesh_wrap.png \ mesh_group.png \ mesh_tree_group.png \ + mesh_tree_group_on_filter.png \ mesh_edit_group.png \ mesh_make_group.png \ mesh_groups_from_gemetry.png \ @@ -134,8 +155,8 @@ dist_salomeres_DATA = \ mesh_merge_elements.png \ select1.png \ StdMeshers.xml \ - SalomeApp.xml \ mesh_pattern.png \ + mesh_pentahedron.png \ pattern_sample_2d.png \ pattern_sample_3D.png \ mesh_add.png \ @@ -150,6 +171,9 @@ dist_salomeres_DATA = \ mesh_conv_to_quad.png \ mesh_tree_hypo_layers_distribution.png \ mesh_tree_algo_radial_prism.png \ + mesh_tree_algo_radial_quadrangle_1D2D.png \ + mesh_tree_algo_existing_2D.png \ + mesh_tree_algo_prism.png \ mesh_tree_algo_projection_2d.png \ mesh_hypo_source_edge.png \ mesh_hypo_source_3d.png \ @@ -161,11 +185,25 @@ dist_salomeres_DATA = \ mesh_tree_hypo_projection_3d.png \ mesh_tree_hypo_projection_2d.png \ mesh_build_compound.png \ + copy_mesh.png \ mesh_node_to_point.png \ mesh_tree_mesh_partial.png \ mesh_extractGroup.png \ mesh_precompute.png \ - mesh_free_faces.png + mesh_2d_from_3d.png \ + mesh_free_faces.png \ + scale.png \ + scale_along_axes.png \ + split_into_tetra.png \ + mesh_duplicate_nodes.png \ + mesh_duplicate_nodes_with_elem.png \ + mesh_bounding_box.png \ + mesh_hypo_viscous_layers.png \ + mesh_tree_hypo_viscous_layers.png \ + mesh_min_dist.png \ + reorient_faces_point.png \ + reorient_faces_face.png \ + mesh_ball.png # VSR: little trick to avoid putting if SMESHCatalog.xml to the distribution archive -nodist_salomeres_SCRIPTS = SMESHCatalog.xml +nodist_salomeres_SCRIPTS = SMESHCatalog.xml SalomeApp.xml diff --git a/resources/SMESHCatalog.xml.in b/resources/SMESHCatalog.xml.in index 200d23297..f65821917 100644 --- a/resources/SMESHCatalog.xml.in +++ b/resources/SMESHCatalog.xml.in @@ -1,6 +1,6 @@ + - - - + + + + + - + + + + + @@ -43,9 +50,8 @@ - - + @@ -54,33 +60,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + +
                    + + +
                    - - - - - - - - - - - + +
                    diff --git a/resources/StdMeshers.xml b/resources/StdMeshers.xml index 4adf93e83..0d519fc57 100644 --- a/resources/StdMeshers.xml +++ b/resources/StdMeshers.xml @@ -1,7 +1,7 @@ Branche V6_main + +Problemes en cours +================== +- a faire ++ en cours, OK mais perfectible +* OK + ++ visualisation de groupe (type d'element): on voit tout le maillage, mais le groupe est OK + creation d'une structure vtkUnstructuredGrid locale : iteration un peu lourde, et pas de partage avec la structure du maillage (pas evident) +- inversion d'un volume (tetra): exception +- script de creation de noeuds et d'elements: OK, mais pas compatible avec version precedente (numerotation noeuds differente) ++ affichage numeros noeuds: numeros en trop sur (O,0,0) pas systematique, trouver la condition (enlever dans vtkUnstructuredGrid ?) + ==> purge systematique noeuds et cellules en trop dans compactage grid. ++ gestion du mode embedded mal faite lors d'un script python : journal commandes intempestif +- affichage des noeuds apres changement lineaire <--> quadratique à l'IHM : pas pris en compte, alors que maillage OK, + mais script OK + ==> cassé apres mode embedded ou elimination noeuds en trop ? +- extrusion elements 2D along a path : affichage apres calcul pas toujours OK (filaire) +- branche git a ouvrir pour merge avec V5_1_4_BR tag V5_1_4rc1 + +A tester, non pris en compte +============================ +- engine standalone +- polyedres (attendre vtk) + + +=============================== Hypothese de refonte de l'API de SMDS + +n'utiliser que vtkUnstructuredGrid, ne pas avor d'objets SMDS_MeshElement mais seulement des index de vtkUnstructuredGrid. +2987 usages de SMDS_MeshNodes +810 SMDS_MeshElement +... +==> en dernier ressort, lourd +================================================================================ + +Essai a API SMDS a peu pres constante +===================================== + +SMDS_Mesh + static vector _meshList; --> retrouver un SMDS_Mesh + vtkUnstructuredGrid* myGrid; + + vector myNodes; --> meme index que dans le pointSet de myGrid + vector myCells; --> index = ID client, pas le meme index que dans le cellTypes de myGrid (ID vtk) + + + +SMDS_MeshElement + int myID; --> index dans la structure geree par SMDS_Mesh + int myMeshId; --> pour retrouver SMDS_Mesh* dans _meshList + int myShapeId; --> pour retrouver la subShape + + +SMDS_MeshNode: SMDS_MeshElement + SMDS_PositionPtr myPosition; --> A REVOIR : objet position dans la shape geom + ##vector myInverseElements; --> SUPPRIME : pour retrouver les elements, vtkCellLinks + + +SMDS_MeshCell: SMDS_MeshElement --> generique pour tous les elements (cells) + int myVtkID --> A SUPPRIMER + +SMDS_MeshVolume: SMDS_MeshCell + +SMDS_VolumeOfNodes: SMDS_MeshVolume --> Garder temporairement, utilisation dans StdMesher et SMDS_VolumeTool + const SMDS_MeshNode **myNodes; --> Couteux + int myNbNodes; --> "" + +SMDS_VolumeVtkNodes: SMDS_MeshVolume --> Utiliser systematiquement dans SMDS, + --> IMPLEMENTER. + + +SMDS_MeshElementIDFactory: SMDS_MeshNodeIDFactory + vector myIDElements; // index = ID client, value = ID vtk --> A SUPPRIMER, ne sert que dans SMDS_MeshElementIDFactory + vector myVtkIndex; // index = ID vtk, value = ID client --> A REPORTER dans SMDS_Mesh + + + + +========= TODO ============ + +enlever vtkId de SMDS_MeshCell, utiliser SMDS_MeshElementIDFactory. + +ajouter ID dans SMDS_Mesh::createTriangle +verifier ID dans SMDS_Mesh::Find*OrCreate + +=================================================== +occupation memoire cube 100*100*100 sans affichage +NOTES: +- sur Debian Sarge 64 bits, les mesures malloc_stat() semblent coherentes + avec une mesure externe globale(recherche du passage en swap du process). +- sur Ubuntu 9.10 64 bits, les mesures malloc_stat() donnent des resultats bizarres (surestimation ?), + mais la mesure avec l'outil KDE de surveillance systeme est OK avec la recherche du swap. + + +Reference : V513 Debian Sarge 64 bits: --> 463 - 33 = 430 Mo +------------------------------------- +Total (incl. mmap): +system bytes = 43757568 +in use bytes = 32909584 = 33M +max mmap regions = 41 +max mmap bytes = 16371712 +---- +Total (incl. mmap): +system bytes = 464670720 +in use bytes = 463105120 = 463M +max mmap regions = 47 +max mmap bytes = 28188672 + +Debian Sarge 64 bits, vtkUnstructuredGrid nodes et hexa, 4 janvier 2010 --> 512 - 41 = 471M +----------------------------------- + +Total (incl. mmap): +system bytes = 52133888 +in use bytes = 41340320 : 41M +max mmap regions = 72 +max mmap bytes = 24625152 +---- +Total (incl. mmap): +system bytes = 520560640 +in use bytes = 518735584 : 512M +max mmap regions = 88 +max mmap bytes = 198385664 + +idem avec pool SMDS_MeshNodes --> 483 -33 = 450M +----------------------------- +Total (incl. mmap): +system bytes = 43696128 +in use bytes = 32915184 : 33M +max mmap regions = 41 +max mmap bytes = 16371712 +---- +Total (incl. mmap): +system bytes = 484806656 +in use bytes = 482980992 : 483M +max mmap regions = 58 +max mmap bytes = 184557568 + +idem ci-dessus + pool SMDS_VolumeVtkNodes --> 475 -33 = 442M (git: add ObjectPool.hxx) +----------------------------------------- + +Total (incl. mmap): +system bytes = 43200512 +in use bytes = 32908576 : 33M +max mmap regions = 41 +max mmap bytes = 16371712 +---- +Total (incl. mmap): +system bytes = 478068736 +in use bytes = 475144400 : 475M +max mmap regions = 59 +max mmap bytes = 184692736 + +remplacement SMDS_PositionPtr: (boost::shared_ptr --> SMDS_Position*) --> 436 - 35 = 401M (git SMDS_Position) +------------------------------------------------------------------------------------ +Total (incl. mmap): +system bytes = 45408256 +in use bytes = 35097680 : 35M +max mmap regions = 47 +max mmap bytes = 18116608 +---- +Total (incl. mmap): +system bytes = 438935552 +in use bytes = 436116560 : 436M +max mmap regions = 65 +max mmap bytes = 186437632 + +simplification SMDS_SpacePosition (pas de double[3]) --> 418 -33 = 385M (git SMDS_SpacePosition) +---------------------------------------------------- +Total (incl. mmap): +system bytes = 42582016 +in use bytes = 32883552 : 33M +max mmap regions = 41 +max mmap bytes = 16371712 +---- +Total (incl. mmap): +system bytes = 421728256 +in use bytes = 418378000 : 418M +max mmap regions = 58 +max mmap bytes = 183640064 + +sizeof(SMDS_MeshElement) 16 +sizeof(SMDS_MeshNode) 24 +sizeof(SMDS_MeshCell) 24 +sizeof(SMDS_VolumeVtkNodes) 24 +sizeof(SMDS_Position) 16 +sizeof(SMDS_SpacePosition) 16 + +impact d'un int en plus dans SMDS_MeshElement --> 426 - 33 = 393M +--------------------------------------------- + +sizeof(SMDS_MeshElement) 24 +sizeof(SMDS_MeshNode) 32 --> on retrouve bien les 8M +sizeof(SMDS_MeshCell) 24 +sizeof(SMDS_VolumeVtkNodes) 24 +sizeof(SMDS_Position) 16 +sizeof(SMDS_SpacePosition) 16 + +Total (incl. mmap): +system bytes = 43192320 +in use bytes = 32681088 : 33M +max mmap regions = 41 +max mmap bytes = 16371712 +---- +Total (incl. mmap): +system bytes = 429334528 +in use bytes = 426424576 : 426M +max mmap regions = 59 +max mmap bytes = 184692736 + +remplacement std::set par std::vector dans SMESHDS_SubMesh --> 347 - 35 = 312M +---------------------------------------------------------- +sizeof(SMDS_MeshElement) 24 +sizeof(SMDS_MeshNode) 32 +sizeof(SMDS_MeshCell) 24 +sizeof(SMDS_VolumeVtkNodes) 24 +sizeof(SMDS_Position) 16 +sizeof(SMDS_SpacePosition) 16 + +Total (incl. mmap): +system bytes = 45404160 +in use bytes = 35132160 --> 35M +max mmap regions = 49 +max mmap bytes = 17723392 +---- +Total (incl. mmap): +system bytes = 349831168 +in use bytes = 346885424 --> 347M +max mmap regions = 73 +max mmap bytes = 204148736 + +Ce resultat est coherent avec une recherche de swap sur une machine a 8Go de memoire: +Cube a 270**3 mailles (~20M mailles) --> 6.2 Go (idem Debian Sarge et Ubuntu 9.10, 64 bits) +Le meme avec V5.1.3 --> 14 Go (swap) + diff --git a/src/SMDS/ObjectPool.hxx b/src/SMDS/ObjectPool.hxx new file mode 100644 index 000000000..1ee371055 --- /dev/null +++ b/src/SMDS/ObjectPool.hxx @@ -0,0 +1,134 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _OBJECTPOOL_HXX_ +#define _OBJECTPOOL_HXX_ + +#include +#include +#include + +template class ObjectPool +{ + +private: + std::vector _chunkList; + std::vector _freeList; + int _nextFree; + int _maxAvail; + int _chunkSize; + + int getNextFree() + { + for (int i = _nextFree; i < _maxAvail; i++) + if (_freeList[i] == true) + { + return i; + break; + } + return _maxAvail; + } + + void checkDelete(int chunkId) + { + int i0 = _chunkSize * chunkId; + int i1 = _chunkSize * (chunkId + 1); + for (int i = i0; i < i1; i++) + if (_freeList[i] == false) + return; + std::cerr << "a chunk to delete" << std::endl; + // compactage des vecteurs un peu lourd, pas necessaire + //X* chunk = _chunkList[chunkId]; + //delete [] chunk; + } + +public: + ObjectPool(int nblk) + { + _chunkSize = nblk; + _nextFree = 0; + _maxAvail = 0; + _chunkList.clear(); + _freeList.clear(); + } + + virtual ~ObjectPool() + { + for (int i = 0; i < _chunkList.size(); i++) + delete[] _chunkList[i]; + } + + X* getNew() + { + X *obj = 0; + _nextFree = getNextFree(); + if (_nextFree == _maxAvail) + { + X* newChunk = new X[_chunkSize]; + _chunkList.push_back(newChunk); + _freeList.insert(_freeList.end(), _chunkSize, true); + _maxAvail += _chunkSize; + _freeList[_nextFree] = false; + obj = newChunk; // &newChunk[0]; + } + else + { + int chunkId = _nextFree / _chunkSize; + int rank = _nextFree - chunkId * _chunkSize; + _freeList[_nextFree] = false; + obj = _chunkList[chunkId] + rank; // &_chunkList[chunkId][rank]; + } + //obj->init(); + return obj; + } + + void destroy(X* obj) + { + long adrobj = (long) (obj); + for (int i = 0; i < _chunkList.size(); i++) + { + X* chunk = _chunkList[i]; + long adrmin = (long) (chunk); + if (adrobj < adrmin) + continue; + long adrmax = (long) (chunk + _chunkSize); + if (adrobj >= adrmax) + continue; + int rank = (adrobj - adrmin) / sizeof(X); + int toFree = i * _chunkSize + rank; + _freeList[toFree] = true; + if (toFree < _nextFree) + _nextFree = toFree; + //obj->clean(); + //checkDelete(i); compactage non fait + break; + } + } + + // void destroy(int toFree) + // { + // // no control 0<= toFree < _freeList.size() + // _freeList[toFree] = true; + // if (toFree < _nextFree) + // _nextFree = toFree; + // } + +}; + +#endif diff --git a/src/SMDS/SMDSAbs_ElementType.hxx b/src/SMDS/SMDSAbs_ElementType.hxx index a76772e3f..e66bbff09 100644 --- a/src/SMDS/SMDSAbs_ElementType.hxx +++ b/src/SMDS/SMDSAbs_ElementType.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDSAbs_ElementType.hxx // Module : SMESH @@ -30,33 +31,40 @@ /// Type (node, edge, face or volume) of elements /////////////////////////////////////////////////////////////////////////////// enum SMDSAbs_ElementType -{ - SMDSAbs_All, - SMDSAbs_Node, - SMDSAbs_Edge, - SMDSAbs_Face, - SMDSAbs_Volume, - SMDSAbs_NbElementTypes -}; + { + SMDSAbs_All, + SMDSAbs_Node, + SMDSAbs_Edge, + SMDSAbs_Face, + SMDSAbs_Volume, + SMDSAbs_0DElement, + SMDSAbs_Ball, + SMDSAbs_NbElementTypes + }; /*! enumeration for element geometry type */ enum SMDSAbs_GeometryType -{ - // 0D element - SMDSGeom_POINT, - // 1D element - SMDSGeom_EDGE, - // 2D element - SMDSGeom_TRIANGLE, - SMDSGeom_QUADRANGLE, - SMDSGeom_POLYGON, - // 3D element - SMDSGeom_TETRA, - SMDSGeom_PYRAMID, - SMDSGeom_PENTA, - SMDSGeom_HEXA, - SMDSGeom_POLYHEDRA, -}; + { + // 0D element + SMDSGeom_POINT, + // 1D element + SMDSGeom_EDGE, + // 2D element + SMDSGeom_TRIANGLE, + SMDSGeom_QUADRANGLE, + SMDSGeom_POLYGON, + // 3D element + SMDSGeom_TETRA, + SMDSGeom_PYRAMID, + SMDSGeom_HEXA, + SMDSGeom_PENTA, + SMDSGeom_HEXAGONAL_PRISM, + SMDSGeom_POLYHEDRA, + // Discrete elements + SMDSGeom_BALL, + // + SMDSGeom_NONE + }; enum SMDSAbs_ElementOrder { @@ -65,4 +73,36 @@ enum SMDSAbs_ElementOrder { ORDER_QUADRATIC /*! entities of 2nd order */ }; +/*! + * Enumeration of entity type uses in mesh info array, + * and should be synchronised with enum in SMDS + */ +enum SMDSAbs_EntityType { + SMDSEntity_Node, + SMDSEntity_0D, + SMDSEntity_Edge, + SMDSEntity_Quad_Edge, + SMDSEntity_Triangle, + SMDSEntity_Quad_Triangle, + SMDSEntity_Quadrangle, + SMDSEntity_Quad_Quadrangle, + SMDSEntity_BiQuad_Quadrangle, + SMDSEntity_Polygon, + SMDSEntity_Quad_Polygon, + SMDSEntity_Tetra, + SMDSEntity_Quad_Tetra, + SMDSEntity_Pyramid, + SMDSEntity_Quad_Pyramid, + SMDSEntity_Hexa, + SMDSEntity_Quad_Hexa, + SMDSEntity_TriQuad_Hexa, + SMDSEntity_Penta, + SMDSEntity_Quad_Penta, + SMDSEntity_Hexagonal_Prism, + SMDSEntity_Polyhedra, + SMDSEntity_Quad_Polyhedra, + SMDSEntity_Ball, + SMDSEntity_Last +}; + #endif diff --git a/src/SMDS/SMDS_BallElement.cxx b/src/SMDS/SMDS_BallElement.cxx new file mode 100644 index 000000000..9595e9889 --- /dev/null +++ b/src/SMDS/SMDS_BallElement.cxx @@ -0,0 +1,102 @@ +// Copyright (C) 2010-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// Module : SMESH +// File : SMDS_BallElement.cxx +// Author : Edward AGAPOV (eap) + +#include "SMDS_BallElement.hxx" + +#include "SMDS_ElemIterator.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_VtkCellIterator.hxx" + +SMDS_BallElement::SMDS_BallElement() +{ + SMDS_MeshCell::init(); +} + +SMDS_BallElement::SMDS_BallElement (const SMDS_MeshNode * node, double diameter) +{ + init( node->getVtkId(), diameter, SMDS_Mesh::_meshList[ node->getMeshId() ] ); +} + +SMDS_BallElement::SMDS_BallElement(vtkIdType nodeId, double diameter, SMDS_Mesh* mesh) +{ + init( nodeId, diameter, mesh ); +} + +void SMDS_BallElement::init(vtkIdType nodeId, double diameter, SMDS_Mesh* mesh) +{ + SMDS_MeshCell::init(); + SMDS_UnstructuredGrid* grid = mesh->getGrid(); + myVtkID = grid->InsertNextLinkedCell( GetVtkType(), 1, &nodeId ); + myMeshId = mesh->getMeshId(); + grid->SetBallDiameter( myVtkID, diameter ); + mesh->setMyModified(); +} + +double SMDS_BallElement::GetDiameter() const +{ + return SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetBallDiameter( myVtkID ); +} + +void SMDS_BallElement::SetDiameter(double diameter) +{ + SMDS_Mesh::_meshList[myMeshId]->getGrid()->SetBallDiameter( myVtkID, diameter ); +} + +bool SMDS_BallElement::ChangeNode (const SMDS_MeshNode * node) +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + pts[0] = node->getVtkId(); + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); + return true; +} + +void SMDS_BallElement::Print (std::ostream & OS) const +{ + OS << "ball<" << GetID() << "> : "; +} + +const SMDS_MeshNode* SMDS_BallElement::GetNode (const int ind) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts, *pts; + grid->GetCellPoints( myVtkID, npts, pts ); + return SMDS_Mesh::_meshList[myMeshId]->FindNodeVtk( pts[ 0 ]); +} + +SMDS_ElemIteratorPtr SMDS_BallElement::elementsIterator (SMDSAbs_ElementType type) const +{ + switch (type) + { + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIterator(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); + default: + ; + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); + } +} + diff --git a/src/SMDS/SMDS_BallElement.hxx b/src/SMDS/SMDS_BallElement.hxx new file mode 100644 index 000000000..c8ead2a7a --- /dev/null +++ b/src/SMDS/SMDS_BallElement.hxx @@ -0,0 +1,60 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_BallElement.hxx +// Module : SMESH +// +#ifndef _SMDS_BallElement_HeaderFile +#define _SMDS_BallElement_HeaderFile + +#include "SMESH_SMDS.hxx" +#include "SMDS_MeshCell.hxx" + +#include + +class SMDS_EXPORT SMDS_BallElement: public SMDS_MeshCell +{ + public: + SMDS_BallElement(); + SMDS_BallElement (const SMDS_MeshNode * node, double diameter); + SMDS_BallElement(vtkIdType nodeId, double diameter, SMDS_Mesh* mesh); + void init(vtkIdType nodeId, double diameter, SMDS_Mesh* mesh); + double GetDiameter() const; + void SetDiameter(double diameter); + bool ChangeNode (const SMDS_MeshNode * node); + + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], + const int nbNodes) { return ChangeNode( nodes[0] ); } + virtual void Print (std::ostream & OS) const; + + virtual SMDSAbs_ElementType GetType() const { return SMDSAbs_Ball; } + virtual vtkIdType GetVtkType() const { return VTK_POLY_VERTEX; } + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Ball; } + virtual SMDSAbs_GeometryType GetGeomType() const { return SMDSGeom_BALL; } + virtual int NbNodes() const { return 1; } + virtual int NbEdges() const { return 0; } + virtual int NbFaces() const { return 0; } + virtual const SMDS_MeshNode* GetNode (const int ind) const; + + protected: + SMDS_ElemIteratorPtr elementsIterator (SMDSAbs_ElementType type) const; +}; + +#endif diff --git a/src/SMDS/SMDS_Downward.cxx b/src/SMDS/SMDS_Downward.cxx new file mode 100644 index 000000000..67ff05ffc --- /dev/null +++ b/src/SMDS/SMDS_Downward.cxx @@ -0,0 +1,2235 @@ +// Copyright (C) 2010-2012 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 +// + +// File: SMDS_Downward.cxx +// Created: Jun 3, 2010 +// Author: prascle + +#include "SMDS_Downward.hxx" +#include "SMDS_Mesh.hxx" +#include "utilities.h" + +#include +#include + +#include + +using namespace std; + +// --------------------------------------------------------------------------- + +vector SMDS_Downward::_cellDimension; + +/*! get the dimension of a cell (1,2,3 for 1D, 2D 3D) given the vtk cell type + * + * @param cellType vtk cell type @see vtkCellType.h + * @return 1,2 or 3 + */ +int SMDS_Downward::getCellDimension(unsigned char cellType) +{ + if (_cellDimension.empty()) + { + _cellDimension.resize(VTK_MAXTYPE + 1, 0); + _cellDimension[VTK_LINE] = 1; + _cellDimension[VTK_QUADRATIC_EDGE] = 1; + _cellDimension[VTK_TRIANGLE] = 2; + _cellDimension[VTK_QUADRATIC_TRIANGLE] = 2; + _cellDimension[VTK_QUAD] = 2; + _cellDimension[VTK_QUADRATIC_QUAD] = 2; + _cellDimension[VTK_BIQUADRATIC_QUAD] = 2; + _cellDimension[VTK_TETRA] = 3; + _cellDimension[VTK_QUADRATIC_TETRA] = 3; + _cellDimension[VTK_HEXAHEDRON] = 3; + _cellDimension[VTK_QUADRATIC_HEXAHEDRON] = 3; + _cellDimension[VTK_TRIQUADRATIC_HEXAHEDRON] = 3; + _cellDimension[VTK_WEDGE] = 3; + _cellDimension[VTK_QUADRATIC_WEDGE] = 3; + _cellDimension[VTK_PYRAMID] = 3; + _cellDimension[VTK_QUADRATIC_PYRAMID] = 3; + _cellDimension[VTK_HEXAGONAL_PRISM] = 3; + } + return _cellDimension[cellType]; +} + +// --------------------------------------------------------------------------- + +/*! Generic constructor for all the downward connectivity structures (one per vtk cell type). + * The static structure for cell dimension is set only once. + * @param grid unstructured grid associated to the mesh. + * @param nbDownCells number of downward entities associated to this vtk type of cell. + * @return + */ +SMDS_Downward::SMDS_Downward(SMDS_UnstructuredGrid *grid, int nbDownCells) : + _grid(grid), _nbDownCells(nbDownCells) +{ + this->_maxId = 0; + this->_cellIds.clear(); + this->_cellTypes.clear(); + if (_cellDimension.empty()) + getCellDimension( VTK_LINE ); +} + +SMDS_Downward::~SMDS_Downward() +{ +} + +/*! Give or create an entry for downward connectivity structure relative to a cell. + * If the entry already exists, just return its id, otherwise, create it. + * The internal storage memory is allocated if needed. + * The SMDS_UnstructuredGrid::_cellIdToDownId vector is completed for vtkUnstructuredGrid cells. + * @param vtkId for a vtkUnstructuredGrid cell or -1 (default) for a created downward cell. + * @return the rank in downward[vtkType] structure. + */ +int SMDS_Downward::addCell(int vtkId) +{ + int localId = -1; + if (vtkId >= 0) + localId = _grid->CellIdToDownId(vtkId); + if (localId >= 0) + return localId; + + localId = this->_maxId; + this->_maxId++; + this->allocate(_maxId); + if (vtkId >= 0) + { + this->_vtkCellIds[localId] = vtkId; + _grid->setCellIdToDownId(vtkId, localId); + } + this->initCell(localId); + return localId; +} + +/*! generic method do nothing. see derived methods + * + * @param cellId + */ +void SMDS_Downward::initCell(int cellId) +{ +} + +/*! Get the number of downward entities associated to a cell (always the same for a given vtk type of cell) + * + * @param cellId not used here. + * @return + */ +int SMDS_Downward::getNumberOfDownCells(int cellId) +{ + return _nbDownCells; +} + +/*! get a pointer on the downward entities id's associated to a cell. + * @see SMDS_Downward::getNumberOfDownCells for the number of downward entities. + * @see SMDS_Downward::getDownTypes for the vtk cell types associated to the downward entities. + * @param cellId index of the cell in the downward structure relative to a given vtk cell type. + * @return table of downward entities id's. + */ +const int* SMDS_Downward::getDownCells(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return &_cellIds[_nbDownCells * cellId]; +} + +/*! get a list of vtk cell types associated to downward entities of a given cell, in the same order + * than the downward entities id's list (@see SMDS_Downward::getDownCells). + * + * @param cellId index of the cell in the downward structure relative to a vtk cell type. + * @return table of downward entities types. + */ +const unsigned char* SMDS_Downward::getDownTypes(int cellId) +{ + return &_cellTypes[0]; +} + +/*! add a downward entity of dimension n-1 (cell or node) to a given cell. + * Actual implementation is done in derived methods. + * @param cellId index of the parent cell (dimension n) in the downward structure relative to a vtk cell type. + * @param lowCellId index of the children cell to add (dimension n-1) + * @param aType vtk cell type of the cell to add (needed to find the SMDS_Downward structure containing the cell to add). + */ +void SMDS_Downward::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + ASSERT(0); // must be re-implemented in derived class +} + +/*! add a downward entity of dimension n+1 to a given cell. + * Actual implementation is done in derived methods. + * @param cellId index of the children cell (dimension n) in the downward structure relative to a vtk cell type. + * @param upCellId index of the parent cell to add (dimension n+1) + * @param aType vtk cell type of the cell to add (needed to find the SMDS_Downward structure containing the cell to add). + */ +void SMDS_Downward::addUpCell(int cellId, int upCellId, unsigned char aType) +{ + ASSERT(0); // must be re-implemented in derived class +} + +int SMDS_Downward::getNodeSet(int cellId, int* nodeSet) +{ + return 0; +} + +// --------------------------------------------------------------------------- + +SMDS_Down1D::SMDS_Down1D(SMDS_UnstructuredGrid *grid, int nbDownCells) : + SMDS_Downward(grid, nbDownCells) +{ + _upCellIdsVector.clear(); + _upCellTypesVector.clear(); + _upCellIds.clear(); + _upCellTypes.clear(); + _upCellIndex.clear(); +} + +SMDS_Down1D::~SMDS_Down1D() +{ +} + +/*! clear vectors used to reference 2D cells containing the edge + * + * @param cellId + */ +void SMDS_Down1D::initCell(int cellId) +{ + _upCellIdsVector[cellId].clear(); + _upCellTypesVector[cellId].clear(); +} + +/*! Resize the downward connectivity storage vector if needed. + * + * @param nbElems total number of elements of the same type required + */ +void SMDS_Down1D::allocate(int nbElems) +{ + if (nbElems >= _vtkCellIds.size()) + { + _vtkCellIds.resize(nbElems + SMDS_Mesh::chunkSize, -1); + _cellIds.resize(_nbDownCells * (nbElems + SMDS_Mesh::chunkSize), -1); + _upCellIdsVector.resize(nbElems + SMDS_Mesh::chunkSize); + _upCellTypesVector.resize(nbElems + SMDS_Mesh::chunkSize); + } +} + +void SMDS_Down1D::compactStorage() +{ + _cellIds.resize(_nbDownCells * _maxId); + _vtkCellIds.resize(_maxId); + + int sizeUpCells = 0; + for (int i = 0; i < _maxId; i++) + sizeUpCells += _upCellIdsVector[i].size(); + _upCellIds.resize(sizeUpCells, -1); + _upCellTypes.resize(sizeUpCells); + _upCellIndex.resize(_maxId + 1, -1); // id and types of rank i correspond to [ _upCellIndex[i], _upCellIndex[i+1] [ + int current = 0; + for (int i = 0; i < _maxId; i++) + { + _upCellIndex[i] = current; + for (int j = 0; j < _upCellIdsVector[i].size(); j++) + { + _upCellIds[current] = _upCellIdsVector[i][j]; + _upCellTypes[current] = _upCellTypesVector[i][j]; + current++; + } + } + _upCellIndex[_maxId] = current; + + _upCellIdsVector.clear(); + _upCellTypesVector.clear(); +} + +void SMDS_Down1D::addUpCell(int cellId, int upCellId, unsigned char aType) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + int nbFaces = _upCellIdsVector[cellId].size(); + for (int i = 0; i < nbFaces; i++) + { + if ((_upCellIdsVector[cellId][i] == upCellId) && (_upCellTypesVector[cellId][i] == aType)) + { + return; // already done + } + } + _upCellIdsVector[cellId].push_back(upCellId); + _upCellTypesVector[cellId].push_back(aType); +} + +int SMDS_Down1D::getNumberOfUpCells(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return _upCellIndex[cellId + 1] - _upCellIndex[cellId]; +} + +const int* SMDS_Down1D::getUpCells(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return &_upCellIds[_upCellIndex[cellId]]; +} + +const unsigned char* SMDS_Down1D::getUpTypes(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return &_upCellTypes[_upCellIndex[cellId]]; +} + +void SMDS_Down1D::getNodeIds(int cellId, std::set& nodeSet) +{ + for (int i = 0; i < _nbDownCells; i++) + nodeSet.insert(_cellIds[_nbDownCells * cellId + i]); +} + +int SMDS_Down1D::getNodeSet(int cellId, int* nodeSet) +{ + for (int i = 0; i < _nbDownCells; i++) + nodeSet[i] = _cellIds[_nbDownCells * cellId + i]; + return _nbDownCells; +} + +void SMDS_Down1D::setNodes(int cellId, int vtkId) +{ + vtkIdType npts = 0; + vtkIdType *pts; // will refer to the point id's of the face + _grid->GetCellPoints(vtkId, npts, pts); + // MESSAGE(vtkId << " " << npts << " " << _nbDownCells); + //ASSERT(npts == _nbDownCells); + for (int i = 0; i < npts; i++) + { + _cellIds[_nbDownCells * cellId + i] = pts[i]; + } +} + +void SMDS_Down1D::setNodes(int cellId, const int* nodeIds) +{ + //ASSERT(nodeIds.size() == _nbDownCells); + for (int i = 0; i < _nbDownCells; i++) + { + _cellIds[_nbDownCells * cellId + i] = nodeIds[i]; + } +} + +/*! Build the list of vtkUnstructuredGrid cells containing the edge. + * We keep in the list the cells that contains all the nodes, we keep only volumes and faces. + * @param cellId id of the edge in the downward structure + * @param vtkIds vector of vtk id's + * @return number of vtk cells (size of vector) + */ +int SMDS_Down1D::computeVtkCells(int cellId, std::vector& vtkIds) +{ + vtkIds.clear(); + + // --- find all the cells the points belong to, and how many of the points belong to a given cell + + int *pts = &_cellIds[_nbDownCells * cellId]; + int ncells = this->computeVtkCells(pts, vtkIds); + return ncells; +} + +/*! Build the list of vtkUnstructuredGrid cells containing the edge. + * + * @param pts list of points id's defining an edge + * @param vtkIds vector of vtk id's + * @return number of vtk cells (size of vector) + */ +int SMDS_Down1D::computeVtkCells(int *pts, std::vector& vtkIds) +{ + + // --- find all the cells the points belong to, and how many of the points belong to a given cell + + int cellIds[1000]; + int cellCnt[1000]; + int cnt = 0; + for (int i = 0; i < _nbDownCells; i++) + { + vtkIdType point = pts[i]; + int numCells = _grid->GetLinks()->GetNcells(point); + vtkIdType *cells = _grid->GetLinks()->GetCells(point); + for (int j = 0; j < numCells; j++) + { + int vtkCellId = cells[j]; + bool found = false; + for (int k = 0; k < cnt; k++) + { + if (cellIds[k] == vtkCellId) + { + cellCnt[k] += 1; + found = true; + break; + } + } + if (!found) + { + cellIds[cnt] = vtkCellId; + cellCnt[cnt] = 1; + // TODO ASSERT(cnt<1000); + cnt++; + } + } + } + + // --- find the face and volume cells: they contains all the points and are of type volume or face + + int ncells = 0; + for (int i = 0; i < cnt; i++) + { + if (cellCnt[i] == _nbDownCells) + { + int vtkElemId = cellIds[i]; + int vtkType = _grid->GetCellType(vtkElemId); + if (SMDS_Downward::getCellDimension(vtkType) > 1) + { + vtkIds.push_back(vtkElemId); + ncells++; + } + } + } + + return ncells; +} + +/*! Build the list of downward faces from a list of vtk cells. + * + * @param cellId id of the edge in the downward structure + * @param vtkIds vector of vtk id's + * @param downFaces vector of face id's in downward structures + * @param downTypes vector of face types + * @return number of downward faces + */ +int SMDS_Down1D::computeFaces(int cellId, int* vtkIds, int nbcells, int* downFaces, unsigned char* downTypes) +{ + int *pts = &_cellIds[_nbDownCells * cellId]; + int nbFaces = this->computeFaces(pts, vtkIds, nbcells, downFaces, downTypes); + return nbFaces; +} + +/*! Build the list of downward faces from a list of vtk cells. + * + * @param pts list of points id's defining an edge + * @param vtkIds vector of vtk id's + * @param downFaces vector of face id's in downward structures + * @param downTypes vector of face types + * @return number of downward faces + */ +int SMDS_Down1D::computeFaces(int* pts, int* vtkIds, int nbcells, int* downFaces, unsigned char* downTypes) +{ + int cnt = 0; + for (int i = 0; i < nbcells; i++) + { + int vtkId = vtkIds[i]; + int vtkType = _grid->GetCellType(vtkId); + if (SMDS_Downward::getCellDimension(vtkType) == 2) + { + int faceId = _grid->CellIdToDownId(vtkId); + downFaces[cnt] = faceId; + downTypes[cnt] = vtkType; + cnt++; + } + else if (SMDS_Downward::getCellDimension(vtkType) == 3) + { + int volId = _grid->CellIdToDownId(vtkId); + SMDS_Downward * downvol = _grid->getDownArray(vtkType); + //const int *downIds = downvol->getDownCells(volId); + const unsigned char* downTypesVol = downvol->getDownTypes(volId); + int nbFaces = downvol->getNumberOfDownCells(volId); + const int* faceIds = downvol->getDownCells(volId); + for (int n = 0; n < nbFaces; n++) + { + SMDS_Down2D *downFace = static_cast (_grid->getDownArray(downTypesVol[n])); + bool isInFace = downFace->isInFace(faceIds[n], pts, _nbDownCells); + if (isInFace) + { + bool alreadySet = false; + for (int k = 0; k < cnt; k++) + if (faceIds[n] == downFaces[k]) + { + alreadySet = true; + break; + } + if (!alreadySet) + { + downFaces[cnt] = faceIds[n]; + downTypes[cnt] = downTypesVol[n]; + cnt++; + } + } + } + } + } + return cnt; +} + +// --------------------------------------------------------------------------- + +SMDS_Down2D::SMDS_Down2D(SMDS_UnstructuredGrid *grid, int nbDownCells) : + SMDS_Downward(grid, nbDownCells) +{ + _upCellIds.clear(); + _upCellTypes.clear(); + _tempNodes.clear(); + _nbNodes = 0; +} + +SMDS_Down2D::~SMDS_Down2D() +{ +} + +int SMDS_Down2D::getNumberOfUpCells(int cellId) +{ + int nbup = 0; + if (_upCellIds[2 * cellId] >= 0) + nbup++; + if (_upCellIds[2 * cellId + 1] >= 0) + nbup++; + return nbup; +} + +const int* SMDS_Down2D::getUpCells(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return &_upCellIds[2 * cellId]; +} + +const unsigned char* SMDS_Down2D::getUpTypes(int cellId) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + return &_upCellTypes[2 * cellId]; +} + +void SMDS_Down2D::getNodeIds(int cellId, std::set& nodeSet) +{ + for (int i = 0; i < _nbDownCells; i++) + { + int downCellId = _cellIds[_nbDownCells * cellId + i]; + unsigned char cellType = _cellTypes[i]; + this->_grid->getDownArray(cellType)->getNodeIds(downCellId, nodeSet); + } +} + +/*! Find in vtkUnstructuredGrid the volumes containing a face already stored in vtkUnstructuredGrid. + * Search the volumes containing a face, to store the info in SMDS_Down2D for later uses + * with SMDS_Down2D::getUpCells and SMDS_Down2D::getUpTypes. + * A face belongs to 0, 1 or 2 volumes, identified by their id in vtkUnstructuredGrid. + * @param cellId the face cell id in vkUnstructuredGrid + * @param ids a couple of vtkId, initialized at -1 (no parent volume) + * @return number of volumes (0, 1 or 2) + */ +int SMDS_Down2D::computeVolumeIds(int cellId, int* ids) +{ + // --- find point id's of the face + + vtkIdType npts = 0; + vtkIdType *pts; // will refer to the point id's of the face + _grid->GetCellPoints(cellId, npts, pts); + vector nodes; + for (int i = 0; i < npts; i++) + nodes.push_back(pts[i]); + int nvol = this->computeVolumeIdsFromNodesFace(&nodes[0], npts, ids); + return nvol; +} + +/*! Find in vtkUnstructuredGrid the volumes containing a face described by it's nodes + * Search the volumes containing a face, to store the info in SMDS_Down2D for later uses + * with SMDS_Down2D::getUpCells and SMDS_Down2D::getUpTypes. + * A face belongs to 0, 1 or 2 volumes, identified by their id in vtkUnstructuredGrid. + * @param faceByNodes + * @param ids a couple of vtkId, initialized at -1 (no parent volume) + * @return number of volumes (0, 1 or 2) + */ +int SMDS_Down2D::computeVolumeIds(ElemByNodesType& faceByNodes, int* ids) +{ + int nvol = this->computeVolumeIdsFromNodesFace(&faceByNodes.nodeIds[0], faceByNodes.nbNodes, ids); + return nvol; +} + +/*! Find in vtkUnstructuredGrid the volumes containing a face described by it's nodes + * Search the volumes containing a face, to store the info in SMDS_Down2D for later uses + * with SMDS_Down2D::getUpCells and SMDS_Down2D::getUpTypes. + * A face belongs to 0, 1 or 2 volumes, identified by their id in vtkUnstructuredGrid. + * @param pts array of vtk node id's + * @param npts number of nodes + * @param ids + * @return number of volumes (0, 1 or 2) + */ +int SMDS_Down2D::computeVolumeIdsFromNodesFace(int* pts, int npts, int* ids) +{ + + // --- find all the cells the points belong to, and how many of the points belong to a given cell + + int cellIds[1000]; + int cellCnt[1000]; + int cnt = 0; + for (int i = 0; i < npts; i++) + { + vtkIdType point = pts[i]; + int numCells = _grid->GetLinks()->GetNcells(point); + //MESSAGE("cells pour " << i << " " << numCells); + vtkIdType *cells = _grid->GetLinks()->GetCells(point); + for (int j = 0; j < numCells; j++) + { + int vtkCellId = cells[j]; + bool found = false; + for (int k = 0; k < cnt; k++) + { + if (cellIds[k] == vtkCellId) + { + cellCnt[k] += 1; + found = true; + break; + } + } + if (!found) + { + cellIds[cnt] = vtkCellId; + cellCnt[cnt] = 1; + // TODO ASSERT(cnt<1000); + cnt++; + } + } + } + + // --- find the volume cells: they contains all the points and are of type volume + + int nvol = 0; + for (int i = 0; i < cnt; i++) + { + //MESSAGE("cell " << cellIds[i] << " points " << cellCnt[i]); + if (cellCnt[i] == npts) + { + int vtkElemId = cellIds[i]; + int vtkType = _grid->GetCellType(vtkElemId); + if (SMDS_Downward::getCellDimension(vtkType) == 3) + { + ids[nvol] = vtkElemId; // store the volume id in given vector + nvol++; + } + } + if (nvol == 2) + break; + } + + return nvol; +} + +void SMDS_Down2D::setTempNodes(int cellId, int vtkId) +{ + vtkIdType npts = 0; + vtkIdType *pts; // will refer to the point id's of the face + _grid->GetCellPoints(vtkId, npts, pts); + // MESSAGE(vtkId << " " << npts << " " << _nbNodes); + //ASSERT(npts == _nbNodes); + for (int i = 0; i < npts; i++) + { + _tempNodes[_nbNodes * cellId + i] = pts[i]; + } +} + +void SMDS_Down2D::setTempNodes(int cellId, ElemByNodesType& faceByNodes) +{ + for (int i = 0; i < faceByNodes.nbNodes; i++) + _tempNodes[_nbNodes * cellId + i] = faceByNodes.nodeIds[i]; +} + +/*! Find if all the nodes belongs to the face. + * + * @param cellId the face cell Id + * @param nodeSet set of node id's to be found in the face list of nodes + * @return + */ +bool SMDS_Down2D::isInFace(int cellId, int *pts, int npts) +{ + int nbFound = 0; + int *nodes = &_tempNodes[_nbNodes * cellId]; + for (int j = 0; j < npts; j++) + { + int point = pts[j]; + for (int i = 0; i < _nbNodes; i++) + { + if (nodes[i] == point) + { + nbFound++; + break; + } + } + } + return (nbFound == npts); +} + +/*! Resize the downward connectivity storage vector if needed. + * + * @param nbElems total number of elements of the same type required + */ +void SMDS_Down2D::allocate(int nbElems) +{ + if (nbElems >= _vtkCellIds.size()) + { + _cellIds.resize(_nbDownCells * (nbElems + SMDS_Mesh::chunkSize), -1); + _vtkCellIds.resize(nbElems + SMDS_Mesh::chunkSize, -1); + _upCellIds.resize(2 * (nbElems + SMDS_Mesh::chunkSize), -1); + _upCellTypes.resize(2 * (nbElems + SMDS_Mesh::chunkSize), -1); + _tempNodes.resize(_nbNodes * (nbElems + SMDS_Mesh::chunkSize), -1); + } +} + +void SMDS_Down2D::compactStorage() +{ + _cellIds.resize(_nbDownCells * _maxId); + _upCellIds.resize(2 * _maxId); + _upCellTypes.resize(2 * _maxId); + _vtkCellIds.resize(_maxId); + _tempNodes.clear(); +} + +void SMDS_Down2D::addUpCell(int cellId, int upCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + int *vols = &_upCellIds[2 * cellId]; + unsigned char *types = &_upCellTypes[2 * cellId]; + for (int i = 0; i < 2; i++) + { + if (vols[i] < 0) + { + vols[i] = upCellId; // use non affected volume + types[i] = aType; + return; + } + if ((vols[i] == upCellId) && (types[i] == aType)) // already done + return; + } + ASSERT(0); +} + +int SMDS_Down2D::getNodeSet(int cellId, int* nodeSet) +{ + for (int i = 0; i < _nbNodes; i++) + nodeSet[i] = _tempNodes[_nbNodes * cellId + i]; + return _nbNodes; +} + +int SMDS_Down2D::FindEdgeByNodes(int cellId, ElemByNodesType& edgeByNodes) +{ + int *edges = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if ((edges[i] >= 0) && (edgeByNodes.vtkType == _cellTypes[i])) + { + int nodeSet[3]; + int npts = this->_grid->getDownArray(edgeByNodes.vtkType)->getNodeSet(edges[i], nodeSet); + bool found = false; + for (int j = 0; j < npts; j++) + { + int point = edgeByNodes.nodeIds[j]; + found = false; + for (int k = 0; k < npts; k++) + { + if (nodeSet[k] == point) + { + found = true; + break; + } + } + if (!found) + break; + } + if (found) + return edges[i]; + } + } + return -1; +} + +// --------------------------------------------------------------------------- + +SMDS_Down3D::SMDS_Down3D(SMDS_UnstructuredGrid *grid, int nbDownCells) : + SMDS_Downward(grid, nbDownCells) +{ +} + +SMDS_Down3D::~SMDS_Down3D() +{ +} + +void SMDS_Down3D::allocate(int nbElems) +{ + if (nbElems >= _vtkCellIds.size()) + { + _cellIds.resize(_nbDownCells * (nbElems + SMDS_Mesh::chunkSize), -1); + _vtkCellIds.resize(nbElems + SMDS_Mesh::chunkSize, -1); + } +} + +void SMDS_Down3D::compactStorage() +{ + // nothing to do, size was known before +} + +int SMDS_Down3D::getNumberOfUpCells(int cellId) +{ + return 0; +} + +const int* SMDS_Down3D::getUpCells(int cellId) +{ + return 0; +} + +const unsigned char* SMDS_Down3D::getUpTypes(int cellId) +{ + return 0; +} + +void SMDS_Down3D::getNodeIds(int cellId, std::set& nodeSet) +{ + int vtkId = this->_vtkCellIds[cellId]; + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(vtkId, npts, nodes); + for (int i = 0; i < npts; i++) + nodeSet.insert(nodes[i]); +} + +int SMDS_Down3D::FindFaceByNodes(int cellId, ElemByNodesType& faceByNodes) +{ + int *faces = &_cellIds[_nbDownCells * cellId]; + int npoints = 0; + + for (int i = 0; i < _nbDownCells; i++) + { + if ((faces[i] >= 0) && (faceByNodes.vtkType == _cellTypes[i])) + { + if (npoints == 0) + npoints = faceByNodes.nbNodes; + + int nodeSet[10]; + int npts = this->_grid->getDownArray(faceByNodes.vtkType)->getNodeSet(faces[i], nodeSet); + if (npts != npoints) + continue; // skip this face + bool found = false; + for (int j = 0; j < npts; j++) + { + int point = faceByNodes.nodeIds[j]; + found = false; + for (int k = 0; k < npts; k++) + { + if (nodeSet[k] == point) + { + found = true; + break; // point j is in the 2 faces, skip remaining k values + } + } + if (!found) + break; // point j is not in the 2 faces, skip the remaining tests + } + if (found) + return faces[i]; + } + } + return -1; +} + +// --------------------------------------------------------------------------- + +SMDS_DownEdge::SMDS_DownEdge(SMDS_UnstructuredGrid *grid) : + SMDS_Down1D(grid, 2) +{ + _cellTypes.push_back(VTK_VERTEX); + _cellTypes.push_back(VTK_VERTEX); +} + +SMDS_DownEdge::~SMDS_DownEdge() +{ +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadEdge::SMDS_DownQuadEdge(SMDS_UnstructuredGrid *grid) : + SMDS_Down1D(grid, 3) +{ + _cellTypes.push_back(VTK_VERTEX); + _cellTypes.push_back(VTK_VERTEX); + _cellTypes.push_back(VTK_VERTEX); +} + +SMDS_DownQuadEdge::~SMDS_DownQuadEdge() +{ +} + +// --------------------------------------------------------------------------- + +SMDS_DownTriangle::SMDS_DownTriangle(SMDS_UnstructuredGrid *grid) : + SMDS_Down2D(grid, 3) +{ + _cellTypes.push_back(VTK_LINE); + _cellTypes.push_back(VTK_LINE); + _cellTypes.push_back(VTK_LINE); + _nbNodes = 3; +} + +SMDS_DownTriangle::~SMDS_DownTriangle() +{ +} + +void SMDS_DownTriangle::computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes) +{ + int *nodes = &_tempNodes[_nbNodes * cellId]; + edgesWithNodes.nbElems = 3; + + edgesWithNodes.elems[0].nodeIds[0] = nodes[0]; + edgesWithNodes.elems[0].nodeIds[1] = nodes[1]; + edgesWithNodes.elems[0].nbNodes = 2; + edgesWithNodes.elems[0].vtkType = VTK_LINE; + + edgesWithNodes.elems[1].nodeIds[0] = nodes[1]; + edgesWithNodes.elems[1].nodeIds[1] = nodes[2]; + edgesWithNodes.elems[1].nbNodes = 2; + edgesWithNodes.elems[1].vtkType = VTK_LINE; + + edgesWithNodes.elems[2].nodeIds[0] = nodes[2]; + edgesWithNodes.elems[2].nodeIds[1] = nodes[0]; + edgesWithNodes.elems[2].nbNodes = 2; + edgesWithNodes.elems[2].vtkType = VTK_LINE; +} + +void SMDS_DownTriangle::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_LINE); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadTriangle::SMDS_DownQuadTriangle(SMDS_UnstructuredGrid *grid) : + SMDS_Down2D(grid, 3) +{ + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _nbNodes = 6; +} + +SMDS_DownQuadTriangle::~SMDS_DownQuadTriangle() +{ +} + +void SMDS_DownQuadTriangle::computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes) +{ + int *nodes = &_tempNodes[_nbNodes * cellId]; + edgesWithNodes.nbElems = 3; + + edgesWithNodes.elems[0].nodeIds[0] = nodes[0]; + edgesWithNodes.elems[0].nodeIds[1] = nodes[1]; + edgesWithNodes.elems[0].nodeIds[2] = nodes[3]; + edgesWithNodes.elems[0].nbNodes = 3; + edgesWithNodes.elems[0].vtkType = VTK_QUADRATIC_EDGE; + + edgesWithNodes.elems[1].nodeIds[0] = nodes[1]; + edgesWithNodes.elems[1].nodeIds[1] = nodes[2]; + edgesWithNodes.elems[1].nodeIds[2] = nodes[4]; + edgesWithNodes.elems[1].nbNodes = 3; + edgesWithNodes.elems[1].vtkType = VTK_QUADRATIC_EDGE; + + edgesWithNodes.elems[2].nodeIds[0] = nodes[2]; + edgesWithNodes.elems[2].nodeIds[1] = nodes[0]; + edgesWithNodes.elems[2].nodeIds[2] = nodes[5]; + edgesWithNodes.elems[2].nbNodes = 3; + edgesWithNodes.elems[2].vtkType = VTK_QUADRATIC_EDGE; +} + +void SMDS_DownQuadTriangle::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_QUADRATIC_EDGE); + int *edges = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (edges[i] < 0) + { + edges[i] = lowCellId; + return; + } + if (edges[i] == lowCellId) + return; + } + ASSERT(0); +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadrangle::SMDS_DownQuadrangle(SMDS_UnstructuredGrid *grid) : + SMDS_Down2D(grid, 4) +{ + _cellTypes.push_back(VTK_LINE); + _cellTypes.push_back(VTK_LINE); + _cellTypes.push_back(VTK_LINE); + _cellTypes.push_back(VTK_LINE); + _nbNodes = 4; +} + +SMDS_DownQuadrangle::~SMDS_DownQuadrangle() +{ +} + +void SMDS_DownQuadrangle::computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes) +{ + int *nodes = &_tempNodes[_nbNodes * cellId]; + edgesWithNodes.nbElems = 4; + + edgesWithNodes.elems[0].nodeIds[0] = nodes[0]; + edgesWithNodes.elems[0].nodeIds[1] = nodes[1]; + edgesWithNodes.elems[0].nbNodes = 2; + edgesWithNodes.elems[0].vtkType = VTK_LINE; + + edgesWithNodes.elems[1].nodeIds[0] = nodes[1]; + edgesWithNodes.elems[1].nodeIds[1] = nodes[2]; + edgesWithNodes.elems[1].nbNodes = 2; + edgesWithNodes.elems[1].vtkType = VTK_LINE; + + edgesWithNodes.elems[2].nodeIds[0] = nodes[2]; + edgesWithNodes.elems[2].nodeIds[1] = nodes[3]; + edgesWithNodes.elems[2].nbNodes = 2; + edgesWithNodes.elems[2].vtkType = VTK_LINE; + + edgesWithNodes.elems[3].nodeIds[0] = nodes[3]; + edgesWithNodes.elems[3].nodeIds[1] = nodes[0]; + edgesWithNodes.elems[3].nbNodes = 2; + edgesWithNodes.elems[3].vtkType = VTK_LINE; +} + +void SMDS_DownQuadrangle::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_LINE); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadQuadrangle::SMDS_DownQuadQuadrangle(SMDS_UnstructuredGrid *grid) : + SMDS_Down2D(grid, 4) +{ + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _cellTypes.push_back(VTK_QUADRATIC_EDGE); + _nbNodes = 8; +} + +SMDS_DownQuadQuadrangle::~SMDS_DownQuadQuadrangle() +{ +} + +void SMDS_DownQuadQuadrangle::computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes) +{ + int *nodes = &_tempNodes[_nbNodes * cellId]; + edgesWithNodes.nbElems = 4; + + edgesWithNodes.elems[0].nodeIds[0] = nodes[0]; + edgesWithNodes.elems[0].nodeIds[1] = nodes[1]; + edgesWithNodes.elems[0].nodeIds[2] = nodes[4]; + edgesWithNodes.elems[0].nbNodes = 3; + edgesWithNodes.elems[0].vtkType = VTK_QUADRATIC_EDGE; + + edgesWithNodes.elems[1].nodeIds[0] = nodes[1]; + edgesWithNodes.elems[1].nodeIds[1] = nodes[2]; + edgesWithNodes.elems[1].nodeIds[2] = nodes[5]; + edgesWithNodes.elems[1].nbNodes = 3; + edgesWithNodes.elems[1].vtkType = VTK_QUADRATIC_EDGE; + + edgesWithNodes.elems[2].nodeIds[0] = nodes[2]; + edgesWithNodes.elems[2].nodeIds[1] = nodes[3]; + edgesWithNodes.elems[2].nodeIds[2] = nodes[6]; + edgesWithNodes.elems[2].nbNodes = 3; + edgesWithNodes.elems[2].vtkType = VTK_QUADRATIC_EDGE; + + edgesWithNodes.elems[3].nodeIds[0] = nodes[3]; + edgesWithNodes.elems[3].nodeIds[1] = nodes[0]; + edgesWithNodes.elems[3].nodeIds[2] = nodes[7]; + edgesWithNodes.elems[3].nbNodes = 3; + edgesWithNodes.elems[3].vtkType = VTK_QUADRATIC_EDGE; +} + +void SMDS_DownQuadQuadrangle::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_QUADRATIC_EDGE); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +// --------------------------------------------------------------------------- + +SMDS_DownTetra::SMDS_DownTetra(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 4) +{ + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); +} + +SMDS_DownTetra::~SMDS_DownTetra() +{ +} + +void SMDS_DownTetra::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; + int ids[12] = { 0, 1, 2, 0, 3, 1, 2, 3, 0, 1, 3, 2 }; +//int ids[12] = { 2, 1, 0, 1, 3, 0, 0, 3, 2, 2, 3, 1 }; + for (int k = 0; k < 4; k++) + { + tofind.clear(); + for (int i = 0; i < 3; i++) + tofind.insert(nodes[ids[3 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 3; i++) + orderedNodes[i] = nodes[ids[3 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownTetra::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_TRIANGLE); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The linear tetrahedron is defined by four points. + * @see vtkTetra.h in Filtering. + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownTetra::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 4; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nbNodes = 3; + facesWithNodes.elems[0].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[1].nodeIds[0] = nodes[0]; + facesWithNodes.elems[1].nodeIds[1] = nodes[1]; + facesWithNodes.elems[1].nodeIds[2] = nodes[3]; + facesWithNodes.elems[1].nbNodes = 3; + facesWithNodes.elems[1].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[2]; + facesWithNodes.elems[2].nodeIds[2] = nodes[3]; + facesWithNodes.elems[2].nbNodes = 3; + facesWithNodes.elems[2].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[3].nodeIds[0] = nodes[1]; + facesWithNodes.elems[3].nodeIds[1] = nodes[2]; + facesWithNodes.elems[3].nodeIds[2] = nodes[3]; + facesWithNodes.elems[3].nbNodes = 3; + facesWithNodes.elems[3].vtkType = VTK_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadTetra::SMDS_DownQuadTetra(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 4) +{ + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); +} + +SMDS_DownQuadTetra::~SMDS_DownQuadTetra() +{ +} + +void SMDS_DownQuadTetra::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; + int ids[24] = { 0, 1, 2, 4, 5, 6, 0, 3, 1, 7, 8, 4, 2, 3, 0, 9, 7, 6, 1, 3, 2, 8, 9, 5 }; +//int ids[24] = { 2, 1, 0, 5, 4, 6, 1, 3, 0, 8, 7, 4, 0, 3, 2, 7, 9, 6, 2, 3, 1, 9, 8, 5 }; + for (int k = 0; k < 4; k++) + { + tofind.clear(); + for (int i = 0; i < 6; i++) + tofind.insert(nodes[ids[6 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 6; i++) + orderedNodes[i] = nodes[ids[6 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownQuadTetra::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + //ASSERT(aType == VTK_QUADRATIC_TRIANGLE); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The ordering of the ten points defining the quadratic tetrahedron cell is point id's (0-3,4-9) + * where id's 0-3 are the four tetrahedron vertices; + * and point id's 4-9 are the mid-edge nodes between (0,1), (1,2), (2,0), (0,3), (1,3), and (2,3). + * @see vtkQuadraticTetra.h in Filtering. + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownQuadTetra::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 4; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nodeIds[3] = nodes[4]; + facesWithNodes.elems[0].nodeIds[4] = nodes[5]; + facesWithNodes.elems[0].nodeIds[5] = nodes[6]; + facesWithNodes.elems[0].nbNodes = 6; + facesWithNodes.elems[0].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[1].nodeIds[0] = nodes[0]; + facesWithNodes.elems[1].nodeIds[1] = nodes[1]; + facesWithNodes.elems[1].nodeIds[2] = nodes[3]; + facesWithNodes.elems[1].nodeIds[3] = nodes[4]; + facesWithNodes.elems[1].nodeIds[4] = nodes[8]; + facesWithNodes.elems[1].nodeIds[5] = nodes[7]; + facesWithNodes.elems[1].nbNodes = 6; + facesWithNodes.elems[1].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[2]; + facesWithNodes.elems[2].nodeIds[2] = nodes[3]; + facesWithNodes.elems[2].nodeIds[3] = nodes[6]; + facesWithNodes.elems[2].nodeIds[4] = nodes[9]; + facesWithNodes.elems[2].nodeIds[5] = nodes[7]; + facesWithNodes.elems[2].nbNodes = 6; + facesWithNodes.elems[2].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[3].nodeIds[0] = nodes[1]; + facesWithNodes.elems[3].nodeIds[1] = nodes[2]; + facesWithNodes.elems[3].nodeIds[2] = nodes[3]; + facesWithNodes.elems[3].nodeIds[3] = nodes[5]; + facesWithNodes.elems[3].nodeIds[4] = nodes[9]; + facesWithNodes.elems[3].nodeIds[5] = nodes[8]; + facesWithNodes.elems[3].nbNodes = 6; + facesWithNodes.elems[3].vtkType = VTK_QUADRATIC_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownPyramid::SMDS_DownPyramid(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 5) +{ + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); +} + +SMDS_DownPyramid::~SMDS_DownPyramid() +{ +} + +void SMDS_DownPyramid::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; + int ids[16] = { 0, 1, 2, 3, 0, 3, 4, 3, 2, 4, 2, 1, 4, 1, 0, 4 }; + + tofind.clear(); + for (int i = 0; i < 4; i++) + tofind.insert(nodes[ids[i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 4; i++) + orderedNodes[i] = nodes[ids[i]]; + return; + } + for (int k = 0; k < 4; k++) + { + tofind.clear(); + for (int i = 0; i < 3; i++) + tofind.insert(nodes[ids[4 + 3 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 3; i++) + orderedNodes[i] = nodes[ids[4 + 3 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownPyramid::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + if (aType == VTK_QUAD) + { + if (faces[0] < 0) + { + faces[0] = lowCellId; + return; + } + if (faces[0] == lowCellId) + return; + } + else + { + //ASSERT(aType == VTK_TRIANGLE); + for (int i = 1; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The pyramid is defined by the five points (0-4) where (0,1,2,3) is the base of the pyramid which, + * using the right hand rule, forms a quadrilateral whose normal points in the direction of the + * pyramid apex at vertex #4. + * @see vtkPyramid.h in Filtering. + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownPyramid::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 5; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nbNodes = 4; + facesWithNodes.elems[0].vtkType = VTK_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[0]; + facesWithNodes.elems[1].nodeIds[1] = nodes[1]; + facesWithNodes.elems[1].nodeIds[2] = nodes[4]; + facesWithNodes.elems[1].nbNodes = 3; + facesWithNodes.elems[1].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[2].nodeIds[0] = nodes[1]; + facesWithNodes.elems[2].nodeIds[1] = nodes[2]; + facesWithNodes.elems[2].nodeIds[2] = nodes[4]; + facesWithNodes.elems[2].nbNodes = 3; + facesWithNodes.elems[2].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[3].nodeIds[0] = nodes[2]; + facesWithNodes.elems[3].nodeIds[1] = nodes[3]; + facesWithNodes.elems[3].nodeIds[2] = nodes[4]; + facesWithNodes.elems[3].nbNodes = 3; + facesWithNodes.elems[3].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[4].nodeIds[0] = nodes[3]; + facesWithNodes.elems[4].nodeIds[1] = nodes[0]; + facesWithNodes.elems[4].nodeIds[2] = nodes[4]; + facesWithNodes.elems[4].nbNodes = 3; + facesWithNodes.elems[4].vtkType = VTK_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadPyramid::SMDS_DownQuadPyramid(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 5) +{ + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); +} + +SMDS_DownQuadPyramid::~SMDS_DownQuadPyramid() +{ +} + +void SMDS_DownQuadPyramid::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; + int ids[32] = { 0, 1, 2, 3, 5, 6, 7, 8, + 0, 3, 4, 8, 12, 9, 3, 2, 4, 7 , 11, 12, 2, 1, 4, 6, 10, 11, 1, 0, 4, 5, 9, 10 }; + + tofind.clear(); + for (int i = 0; i < 4; i++) + tofind.insert(nodes[ids[i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 8; i++) + orderedNodes[i] = nodes[ids[i]]; + return; + } + for (int k = 0; k < 4; k++) + { + tofind.clear(); + for (int i = 0; i < 6; i++) + tofind.insert(nodes[ids[8 + 6 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 6; i++) + orderedNodes[i] = nodes[ids[8 + 6 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownQuadPyramid::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + if (aType == VTK_QUADRATIC_QUAD) + { + if (faces[0] < 0) + { + faces[0] = lowCellId; + return; + } + if (faces[0] == lowCellId) + return; + } + else + { + //ASSERT(aType == VTK_QUADRATIC_TRIANGLE); + for (int i = 1; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The ordering of the thirteen points defining the quadratic pyramid cell is point id's (0-4,5-12) + * where point id's 0-4 are the five corner vertices of the pyramid; followed + * by eight mid-edge nodes (5-12). Note that these mid-edge nodes lie on the edges defined by + * 5(0,1), 6(1,2), 7(2,3), 8(3,0), 9(0,4), 10(1,4), 11(2,4), 12(3,4). + * @see vtkQuadraticPyramid.h in Filtering. + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownQuadPyramid::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 5; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nodeIds[4] = nodes[5]; + facesWithNodes.elems[0].nodeIds[5] = nodes[6]; + facesWithNodes.elems[0].nodeIds[6] = nodes[7]; + facesWithNodes.elems[0].nodeIds[7] = nodes[8]; + facesWithNodes.elems[0].nbNodes = 8; + facesWithNodes.elems[0].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[0]; + facesWithNodes.elems[1].nodeIds[1] = nodes[1]; + facesWithNodes.elems[1].nodeIds[2] = nodes[4]; + facesWithNodes.elems[1].nodeIds[3] = nodes[5]; + facesWithNodes.elems[1].nodeIds[4] = nodes[10]; + facesWithNodes.elems[1].nodeIds[5] = nodes[9]; + facesWithNodes.elems[1].nbNodes = 6; + facesWithNodes.elems[1].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[2].nodeIds[0] = nodes[1]; + facesWithNodes.elems[2].nodeIds[1] = nodes[2]; + facesWithNodes.elems[2].nodeIds[2] = nodes[4]; + facesWithNodes.elems[2].nodeIds[3] = nodes[6]; + facesWithNodes.elems[2].nodeIds[4] = nodes[11]; + facesWithNodes.elems[2].nodeIds[5] = nodes[10]; + facesWithNodes.elems[2].nbNodes = 6; + facesWithNodes.elems[2].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[3].nodeIds[0] = nodes[2]; + facesWithNodes.elems[3].nodeIds[1] = nodes[3]; + facesWithNodes.elems[3].nodeIds[2] = nodes[4]; + facesWithNodes.elems[3].nodeIds[3] = nodes[7]; + facesWithNodes.elems[3].nodeIds[4] = nodes[12]; + facesWithNodes.elems[3].nodeIds[5] = nodes[11]; + facesWithNodes.elems[3].nbNodes = 6; + facesWithNodes.elems[3].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[4].nodeIds[0] = nodes[3]; + facesWithNodes.elems[4].nodeIds[1] = nodes[0]; + facesWithNodes.elems[4].nodeIds[2] = nodes[4]; + facesWithNodes.elems[4].nodeIds[3] = nodes[8]; + facesWithNodes.elems[4].nodeIds[4] = nodes[9]; + facesWithNodes.elems[4].nodeIds[5] = nodes[12]; + facesWithNodes.elems[4].nbNodes = 6; + facesWithNodes.elems[4].vtkType = VTK_QUADRATIC_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownPenta::SMDS_DownPenta(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 5) +{ + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_TRIANGLE); + _cellTypes.push_back(VTK_TRIANGLE); +} + +SMDS_DownPenta::~SMDS_DownPenta() +{ +} + +void SMDS_DownPenta::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; +//int ids[18] = { 0, 2, 1, 3, 4, 5, 0, 1, 4, 3, 1, 2, 5, 4, 2, 0, 3, 5 }; + int ids[18] = { 0, 1, 2, 3, 5, 4, 0, 3, 4, 1, 1, 4, 5, 2, 2, 5, 3, 0 }; + + for (int k = 0; k < 2; k++) + { + tofind.clear(); + for (int i = 0; i < 3; i++) + tofind.insert(nodes[ids[3 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 3; i++) + orderedNodes[i] = nodes[ids[3 * k + i]]; + return; + } + } + for (int k = 0; k < 3; k++) + { + tofind.clear(); + for (int i = 0; i < 4; i++) + tofind.insert(nodes[ids[6 + 4 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 4; i++) + orderedNodes[i] = nodes[ids[6 + 4 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownPenta::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + if (aType == VTK_QUAD) + for (int i = 0; i < 3; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + else + { + //ASSERT(aType == VTK_TRIANGLE); + for (int i = 3; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's. + * A wedge or pentahedron consists of two triangular and three quadrilateral faces + * and is defined by the six points (0-5) where (0,1,2) is the base of the wedge which, + * using the right hand rule, forms a triangle whose normal points outward + * (away from the triangular face (3,4,5)). + * @see vtkWedge.h in Filtering + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownPenta::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 5; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[2]; + facesWithNodes.elems[0].nodeIds[2] = nodes[5]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nbNodes = 4; + facesWithNodes.elems[0].vtkType = VTK_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[1]; + facesWithNodes.elems[1].nodeIds[1] = nodes[2]; + facesWithNodes.elems[1].nodeIds[2] = nodes[5]; + facesWithNodes.elems[1].nodeIds[3] = nodes[4]; + facesWithNodes.elems[1].nbNodes = 4; + facesWithNodes.elems[1].vtkType = VTK_QUAD; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[1]; + facesWithNodes.elems[2].nodeIds[2] = nodes[4]; + facesWithNodes.elems[2].nodeIds[3] = nodes[3]; + facesWithNodes.elems[2].nbNodes = 4; + facesWithNodes.elems[2].vtkType = VTK_QUAD; + + facesWithNodes.elems[3].nodeIds[0] = nodes[0]; + facesWithNodes.elems[3].nodeIds[1] = nodes[1]; + facesWithNodes.elems[3].nodeIds[2] = nodes[2]; + facesWithNodes.elems[3].nbNodes = 3; + facesWithNodes.elems[3].vtkType = VTK_TRIANGLE; + + facesWithNodes.elems[4].nodeIds[0] = nodes[3]; + facesWithNodes.elems[4].nodeIds[1] = nodes[4]; + facesWithNodes.elems[4].nodeIds[2] = nodes[5]; + facesWithNodes.elems[4].nbNodes = 3; + facesWithNodes.elems[4].vtkType = VTK_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadPenta::SMDS_DownQuadPenta(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 5) +{ + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); + _cellTypes.push_back(VTK_QUADRATIC_TRIANGLE); +} + +SMDS_DownQuadPenta::~SMDS_DownQuadPenta() +{ +} + +void SMDS_DownQuadPenta::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; +//int ids[18] = { 0, 2, 1, 3, 4, 5, 0, 1, 4, 3, 1, 2, 5, 4, 2, 0, 3, 5 }; + int ids[36] = { 0, 1, 2, 6, 7, 8, 3, 5, 4, 11, 10, 9, + 0, 3, 4, 1, 12, 9, 13, 6, 1, 4, 5, 2, 13, 10, 14, 7, 2, 5, 3, 0, 14, 11, 12, 8 }; + + for (int k = 0; k < 2; k++) + { + tofind.clear(); + for (int i = 0; i < 6; i++) + tofind.insert(nodes[ids[6 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 6; i++) + orderedNodes[i] = nodes[ids[6 * k + i]]; + return; + } + } + for (int k = 0; k < 3; k++) + { + tofind.clear(); + for (int i = 0; i < 8; i++) + tofind.insert(nodes[ids[12 + 8 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 8; i++) + orderedNodes[i] = nodes[ids[12 + 8 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownQuadPenta::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0) && (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + if (aType == VTK_QUADRATIC_QUAD) + for (int i = 0; i < 3; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + else + { + //ASSERT(aType == VTK_QUADRATIC_TRIANGLE); + for (int i = 3; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The quadratic wedge (or pentahedron) is defined by fifteen points. + * The ordering of the fifteen points defining the cell is point id's (0-5,6-14) + * where point id's 0-5 are the six corner vertices of the wedge, followed by + * nine mid-edge nodes (6-14). Note that these mid-edge nodes lie on the edges defined by + * (0,1), (1,2), (2,0), (3,4), (4,5), (5,3), (0,3), (1,4), (2,5). + * @see vtkQuadraticWedge.h in Filtering + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownQuadPenta::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 5; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[2]; + facesWithNodes.elems[0].nodeIds[2] = nodes[5]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nodeIds[4] = nodes[8]; + facesWithNodes.elems[0].nodeIds[5] = nodes[14]; + facesWithNodes.elems[0].nodeIds[6] = nodes[11]; + facesWithNodes.elems[0].nodeIds[7] = nodes[12]; + facesWithNodes.elems[0].nbNodes = 8; + facesWithNodes.elems[0].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[1]; + facesWithNodes.elems[1].nodeIds[1] = nodes[2]; + facesWithNodes.elems[1].nodeIds[2] = nodes[5]; + facesWithNodes.elems[1].nodeIds[3] = nodes[4]; + facesWithNodes.elems[1].nodeIds[4] = nodes[7]; + facesWithNodes.elems[1].nodeIds[5] = nodes[14]; + facesWithNodes.elems[1].nodeIds[6] = nodes[10]; + facesWithNodes.elems[1].nodeIds[7] = nodes[13]; + facesWithNodes.elems[1].nbNodes = 8; + facesWithNodes.elems[1].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[1]; + facesWithNodes.elems[2].nodeIds[2] = nodes[4]; + facesWithNodes.elems[2].nodeIds[3] = nodes[3]; + facesWithNodes.elems[2].nodeIds[4] = nodes[6]; + facesWithNodes.elems[2].nodeIds[5] = nodes[13]; + facesWithNodes.elems[2].nodeIds[6] = nodes[9]; + facesWithNodes.elems[2].nodeIds[7] = nodes[12]; + facesWithNodes.elems[2].nbNodes = 8; + facesWithNodes.elems[2].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[3].nodeIds[0] = nodes[0]; + facesWithNodes.elems[3].nodeIds[1] = nodes[1]; + facesWithNodes.elems[3].nodeIds[2] = nodes[2]; + facesWithNodes.elems[3].nodeIds[3] = nodes[6]; + facesWithNodes.elems[3].nodeIds[4] = nodes[7]; + facesWithNodes.elems[3].nodeIds[5] = nodes[8]; + facesWithNodes.elems[3].nbNodes = 6; + facesWithNodes.elems[3].vtkType = VTK_QUADRATIC_TRIANGLE; + + facesWithNodes.elems[4].nodeIds[0] = nodes[3]; + facesWithNodes.elems[4].nodeIds[1] = nodes[4]; + facesWithNodes.elems[4].nodeIds[2] = nodes[5]; + facesWithNodes.elems[4].nodeIds[3] = nodes[9]; + facesWithNodes.elems[4].nodeIds[4] = nodes[10]; + facesWithNodes.elems[4].nodeIds[5] = nodes[11]; + facesWithNodes.elems[4].nbNodes = 6; + facesWithNodes.elems[4].vtkType = VTK_QUADRATIC_TRIANGLE; +} + +// --------------------------------------------------------------------------- + +SMDS_DownHexa::SMDS_DownHexa(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 6) +{ + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); + _cellTypes.push_back(VTK_QUAD); +} + +SMDS_DownHexa::~SMDS_DownHexa() +{ +} + +void SMDS_DownHexa::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; +//int ids[24] = { 0, 1, 2, 3, 7, 6, 5, 4, 4, 0, 3, 7, 5, 1, 0, 4, 6, 2, 1, 5, 7, 3, 2, 6}; + int ids[24] = { 3, 2, 1, 0, 4, 5, 6, 7, 7, 3, 0, 4, 4, 0, 1, 5, 5, 1, 2, 6, 6, 2, 3, 7}; + for (int k = 0; k < 6; k++) // loop on the 6 faces + { + tofind.clear(); + for (int i = 0; i < 4; i++) + tofind.insert(nodes[ids[4 * k + i]]); // node ids of the face i + if (setNodes == tofind) + { + for (int i = 0; i < 4; i++) + orderedNodes[i] = nodes[ids[4 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2] << " " << orderedNodes[3]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); + MESSAGE(nodes[4] << " " << nodes[5] << " " << nodes[6] << " " << nodes[7]); +} + +void SMDS_DownHexa::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); + // MESSAGE("-------------------------------------> trop de faces ! " << cellId << " " << lowCellId); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The hexahedron is defined by the eight points (0-7), where (0,1,2,3) is the base + * of the hexahedron which, using the right hand rule, forms a quadrilateral whose normal + * points in the direction of the opposite face (4,5,6,7). + * @see vtkHexahedron.h in Filtering + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownHexa::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 6; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nbNodes = 4; + facesWithNodes.elems[0].vtkType = VTK_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[4]; + facesWithNodes.elems[1].nodeIds[1] = nodes[5]; + facesWithNodes.elems[1].nodeIds[2] = nodes[6]; + facesWithNodes.elems[1].nodeIds[3] = nodes[7]; + facesWithNodes.elems[1].nbNodes = 4; + facesWithNodes.elems[1].vtkType = VTK_QUAD; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[1]; + facesWithNodes.elems[2].nodeIds[2] = nodes[5]; + facesWithNodes.elems[2].nodeIds[3] = nodes[4]; + facesWithNodes.elems[2].nbNodes = 4; + facesWithNodes.elems[2].vtkType = VTK_QUAD; + + facesWithNodes.elems[3].nodeIds[0] = nodes[1]; + facesWithNodes.elems[3].nodeIds[1] = nodes[2]; + facesWithNodes.elems[3].nodeIds[2] = nodes[6]; + facesWithNodes.elems[3].nodeIds[3] = nodes[5]; + facesWithNodes.elems[3].nbNodes = 4; + facesWithNodes.elems[3].vtkType = VTK_QUAD; + + facesWithNodes.elems[4].nodeIds[0] = nodes[2]; + facesWithNodes.elems[4].nodeIds[1] = nodes[6]; + facesWithNodes.elems[4].nodeIds[2] = nodes[7]; + facesWithNodes.elems[4].nodeIds[3] = nodes[3]; + facesWithNodes.elems[4].nbNodes = 4; + facesWithNodes.elems[4].vtkType = VTK_QUAD; + + facesWithNodes.elems[5].nodeIds[0] = nodes[3]; + facesWithNodes.elems[5].nodeIds[1] = nodes[7]; + facesWithNodes.elems[5].nodeIds[2] = nodes[4]; + facesWithNodes.elems[5].nodeIds[3] = nodes[0]; + facesWithNodes.elems[5].nbNodes = 4; + facesWithNodes.elems[5].vtkType = VTK_QUAD; +} + +// --------------------------------------------------------------------------- + +SMDS_DownQuadHexa::SMDS_DownQuadHexa(SMDS_UnstructuredGrid *grid) : + SMDS_Down3D(grid, 6) +{ + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); + _cellTypes.push_back(VTK_QUADRATIC_QUAD); +} + +SMDS_DownQuadHexa::~SMDS_DownQuadHexa() +{ +} + +void SMDS_DownQuadHexa::getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) +{ + set setNodes; + setNodes.clear(); + for (int i = 0; i < orderedNodes.size(); i++) + setNodes.insert(orderedNodes[i]); + //MESSAGE("cellId = " << cellId); + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(this->_vtkCellIds[cellId], npts, nodes); + + set tofind; + //int ids[24] = { 3, 2, 1, 0, 4, 5, 6, 7, 7, 3, 0, 4, 4, 0, 1, 5, 5, 1, 2, 6, 6, 2, 3, 7}; + int ids[48] = { 3, 2, 1, 0,10, 9, 8,11, 4, 5, 6, 7,12,13,14,15, 7, 3, 0, 4,19,11,16,15, + 4, 0, 1, 5,16, 8,17,12, 5, 1, 2, 6,17, 9,18,13, 6, 2, 3, 7,18,10,19,14}; + for (int k = 0; k < 6; k++) + { + tofind.clear(); + for (int i = 0; i < 8; i++) + tofind.insert(nodes[ids[8 * k + i]]); + if (setNodes == tofind) + { + for (int i = 0; i < 8; i++) + orderedNodes[i] = nodes[ids[8 * k + i]]; + return; + } + } + MESSAGE("=== Problem volume " << _vtkCellIds[cellId] << " " << _grid->_mesh->fromVtkToSmds(_vtkCellIds[cellId])); + MESSAGE(orderedNodes[0] << " " << orderedNodes[1] << " " << orderedNodes[2] << " " << orderedNodes[3]); + MESSAGE(nodes[0] << " " << nodes[1] << " " << nodes[2] << " " << nodes[3]); +} + +void SMDS_DownQuadHexa::addDownCell(int cellId, int lowCellId, unsigned char aType) +{ + //ASSERT((cellId >=0)&& (cellId < _maxId)); + int *faces = &_cellIds[_nbDownCells * cellId]; + for (int i = 0; i < _nbDownCells; i++) + { + if (faces[i] < 0) + { + faces[i] = lowCellId; + return; + } + if (faces[i] == lowCellId) + return; + } + ASSERT(0); +} + +/*! Create a list of faces described by a vtk Type and an ordered set of Node Id's + * The ordering of the twenty points defining the quadratic hexahedron cell is point id's (0-7,8-19) + * where point id's 0-7 are the eight corner vertices of the cube, followed by twelve mid-edge nodes (8-19). + * Note that these mid-edge nodes lie on the edges defined by + * (0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7). + * @see vtkQuadraticHexahedron.h in Filtering + * @param cellId volumeId in vtkUnstructuredGrid + * @param facesWithNodes vector of face descriptors to be filled + */ +void SMDS_DownQuadHexa::computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) +{ + // --- find point id's of the volume + + vtkIdType npts = 0; + vtkIdType *nodes; // will refer to the point id's of the volume + _grid->GetCellPoints(cellId, npts, nodes); + + // --- create all the ordered list of node id's for each face + + facesWithNodes.nbElems = 6; + + facesWithNodes.elems[0].nodeIds[0] = nodes[0]; + facesWithNodes.elems[0].nodeIds[1] = nodes[1]; + facesWithNodes.elems[0].nodeIds[2] = nodes[2]; + facesWithNodes.elems[0].nodeIds[3] = nodes[3]; + facesWithNodes.elems[0].nodeIds[4] = nodes[8]; + facesWithNodes.elems[0].nodeIds[5] = nodes[9]; + facesWithNodes.elems[0].nodeIds[6] = nodes[10]; + facesWithNodes.elems[0].nodeIds[7] = nodes[11]; + facesWithNodes.elems[0].nbNodes = 8; + facesWithNodes.elems[0].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[1].nodeIds[0] = nodes[4]; + facesWithNodes.elems[1].nodeIds[1] = nodes[5]; + facesWithNodes.elems[1].nodeIds[2] = nodes[6]; + facesWithNodes.elems[1].nodeIds[3] = nodes[7]; + facesWithNodes.elems[1].nodeIds[4] = nodes[12]; + facesWithNodes.elems[1].nodeIds[5] = nodes[13]; + facesWithNodes.elems[1].nodeIds[6] = nodes[14]; + facesWithNodes.elems[1].nodeIds[7] = nodes[15]; + facesWithNodes.elems[1].nbNodes = 8; + facesWithNodes.elems[1].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[2].nodeIds[0] = nodes[0]; + facesWithNodes.elems[2].nodeIds[1] = nodes[1]; + facesWithNodes.elems[2].nodeIds[2] = nodes[5]; + facesWithNodes.elems[2].nodeIds[3] = nodes[4]; + facesWithNodes.elems[2].nodeIds[4] = nodes[8]; + facesWithNodes.elems[2].nodeIds[5] = nodes[17]; + facesWithNodes.elems[2].nodeIds[6] = nodes[12]; + facesWithNodes.elems[2].nodeIds[7] = nodes[16]; + facesWithNodes.elems[2].nbNodes = 8; + facesWithNodes.elems[2].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[3].nodeIds[0] = nodes[1]; + facesWithNodes.elems[3].nodeIds[1] = nodes[2]; + facesWithNodes.elems[3].nodeIds[2] = nodes[6]; + facesWithNodes.elems[3].nodeIds[3] = nodes[5]; + facesWithNodes.elems[3].nodeIds[4] = nodes[9]; + facesWithNodes.elems[3].nodeIds[5] = nodes[18]; + facesWithNodes.elems[3].nodeIds[6] = nodes[13]; + facesWithNodes.elems[3].nodeIds[7] = nodes[17]; + facesWithNodes.elems[3].nbNodes = 8; + facesWithNodes.elems[3].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[4].nodeIds[0] = nodes[2]; + facesWithNodes.elems[4].nodeIds[1] = nodes[6]; + facesWithNodes.elems[4].nodeIds[2] = nodes[7]; + facesWithNodes.elems[4].nodeIds[3] = nodes[3]; + facesWithNodes.elems[4].nodeIds[4] = nodes[18]; + facesWithNodes.elems[4].nodeIds[5] = nodes[14]; + facesWithNodes.elems[4].nodeIds[6] = nodes[19]; + facesWithNodes.elems[4].nodeIds[7] = nodes[10]; + facesWithNodes.elems[4].nbNodes = 8; + facesWithNodes.elems[4].vtkType = VTK_QUADRATIC_QUAD; + + facesWithNodes.elems[5].nodeIds[0] = nodes[3]; + facesWithNodes.elems[5].nodeIds[1] = nodes[7]; + facesWithNodes.elems[5].nodeIds[2] = nodes[4]; + facesWithNodes.elems[5].nodeIds[3] = nodes[0]; + facesWithNodes.elems[5].nodeIds[4] = nodes[19]; + facesWithNodes.elems[5].nodeIds[5] = nodes[15]; + facesWithNodes.elems[5].nodeIds[6] = nodes[16]; + facesWithNodes.elems[5].nodeIds[7] = nodes[11]; + facesWithNodes.elems[5].nbNodes = 8; + facesWithNodes.elems[5].vtkType = VTK_QUADRATIC_QUAD; +} + +// --------------------------------------------------------------------------- + diff --git a/src/SMDS/SMDS_Downward.hxx b/src/SMDS/SMDS_Downward.hxx new file mode 100644 index 000000000..56854fd1d --- /dev/null +++ b/src/SMDS/SMDS_Downward.hxx @@ -0,0 +1,381 @@ +// Copyright (C) 2010-2012 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 +// + +// File: SMDS_Downward.hxx +// Created: Jun 3, 2010 +// Author: prascle + +#ifndef SMDS_DOWNWARD_HXX_ +#define SMDS_DOWNWARD_HXX_ + +#include "SMDS_UnstructuredGrid.hxx" + +#include +#include + +typedef struct +{ + int nodeIds[8]; //!< max number of nodes in a face or edge: quad quad = 8 + int nbNodes; + unsigned char vtkType; +} ElemByNodesType; // TODO resize for polyhedrons + +typedef struct +{ + ElemByNodesType elems[6]; //!< max number of faces in a volume or edges in a face : hexahedron = 6 + int nbElems; +} ListElemByNodesType; // TODO resize for polyhedrons + +class DownIdType +{ +public: + DownIdType(int a, unsigned char b) : + cellId(a), cellType(b) + { + } + int cellId; + unsigned char cellType; +}; + +struct DownIdCompare +{ + bool operator ()(const DownIdType e1, const DownIdType e2) const + { + if (e1.cellId == e2.cellId) + return (e1.cellType < e2.cellType); + else + return (e1.cellId < e2.cellId); + } +}; + +class SMDS_Downward +{ + friend class SMDS_UnstructuredGrid; + friend class SMDS_Down2D; + friend class SMDS_Down3D; +public: + virtual int getNumberOfDownCells(int cellId); + virtual const int* getDownCells(int cellId); + virtual const unsigned char* getDownTypes(int cellId); + virtual int getNumberOfUpCells(int cellId) = 0; + virtual const int* getUpCells(int cellId) = 0; + virtual const unsigned char* getUpTypes(int cellId) = 0; + virtual void getNodeIds(int cellId, std::set& nodeSet) = 0; + virtual int getNodes(int cellId, int* nodevec) {return 0; } + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes) {}; + int getVtkCellId(int cellId) + { + return _vtkCellIds[cellId]; + } + int getMaxId() + { + return _maxId; + } + static int getCellDimension(unsigned char cellType); +protected: + SMDS_Downward(SMDS_UnstructuredGrid *grid, int nbDownCells); + ~SMDS_Downward(); + int addCell(int vtkId = -1); + virtual void initCell(int cellId); + virtual void allocate(int nbElems) = 0; + virtual void compactStorage() = 0; + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); //!< Id's are downward connectivity id's + virtual void addUpCell(int cellId, int upCellId, unsigned char aType); //!< Id's are downward connectivity id's + virtual int getNodeSet(int cellId, int* nodeSet); + + SMDS_UnstructuredGrid* _grid; + int _maxId; + int _nbDownCells; //!< the same number for all cells of a derived class + std::vector _cellIds; //!< growing size: all the down cell id's, size = _maxId * _nbDownCells + std::vector _vtkCellIds; //!< growing size: size = _maxId, either vtkId or -1 + std::vector _cellTypes; //!< fixed size: the same vector for all cells of a derived class + + static std::vector _cellDimension; //!< conversion table: type --> dimension +}; + +class SMDS_Down1D: public SMDS_Downward +{ + friend class SMDS_UnstructuredGrid; +public: + virtual int getNumberOfUpCells(int cellId); + virtual const int* getUpCells(int cellId); + virtual const unsigned char* getUpTypes(int cellId); + virtual void getNodeIds(int cellId, std::set& nodeSet); + virtual int getNodes(int cellId, int* nodevec) { return getNodeSet(cellId, nodevec); } +protected: + SMDS_Down1D(SMDS_UnstructuredGrid *grid, int nbDownCells); + ~SMDS_Down1D(); + virtual void initCell(int cellId); + virtual void allocate(int nbElems); + virtual void compactStorage(); + virtual void addUpCell(int cellId, int upCellId, unsigned char aType); //!< Id's are downward connectivity id's + virtual int getNodeSet(int cellId, int* nodeSet); + void setNodes(int cellId, int vtkId); + void setNodes(int cellId, const int* nodeIds); + int computeVtkCells(int cellId, std::vector& vtkIds); + int computeVtkCells(int* pts, std::vector& vtkIds); + int computeFaces(int cellId, int* vtkIds, int nbcells, int* downFaces, unsigned char* downTypes); + int computeFaces(int* pts, int* vtkIds, int nbcells, int* downFaces, unsigned char* downTypes); + + std::vector > _upCellIdsVector; //!< the number of faces sharing an edge is not known + std::vector > _upCellTypesVector; //!< the number of faces sharing an edge is not known + std::vector _upCellIds; //!< compacted storage after connectivity calculation + std::vector _upCellTypes; //!< compacted storage after connectivity calculation + std::vector _upCellIndex; //!< compacted storage after connectivity calculation +}; + +class SMDS_Down2D: public SMDS_Downward +{ + friend class SMDS_UnstructuredGrid; + friend class SMDS_Down1D; +public: + virtual int getNumberOfUpCells(int cellId); + virtual const int* getUpCells(int cellId); + virtual const unsigned char* getUpTypes(int cellId); + virtual void getNodeIds(int cellId, std::set& nodeSet); +protected: + SMDS_Down2D(SMDS_UnstructuredGrid *grid, int nbDownCells); + ~SMDS_Down2D(); + virtual void allocate(int nbElems); + virtual void compactStorage(); + virtual void addUpCell(int cellId, int upCellId, unsigned char aType); + virtual void computeEdgesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) = 0; + virtual int getNodeSet(int cellId, int* nodeSet); + int computeVolumeIds(int cellId, int* ids); + int computeVolumeIds(ElemByNodesType& faceByNodes, int* ids); + int computeVolumeIdsFromNodesFace(int* nodes, int nbNodes, int* ids); + void setTempNodes(int cellId, int vtkId); + void setTempNodes(int cellId, ElemByNodesType& faceByNodes); + bool isInFace(int cellId, int *pts, int npts); + int FindEdgeByNodes(int cellId, ElemByNodesType& edgeByNodes); + + std::vector _upCellIds; //!< 2 volumes max. per face + std::vector _upCellTypes; //!< 2 volume types per face + std::vector _tempNodes; //!< temporary storage of nodes, until downward connectivity completion + int _nbNodes; //!< number of nodes in a face +}; + +class SMDS_Down3D: public SMDS_Downward +{ + friend class SMDS_UnstructuredGrid; +public: + virtual int getNumberOfUpCells(int cellId); + virtual const int* getUpCells(int cellId); + virtual const unsigned char* getUpTypes(int cellId); + virtual void getNodeIds(int cellId, std::set& nodeSet); +protected: + SMDS_Down3D(SMDS_UnstructuredGrid *grid, int nbDownCells); + ~SMDS_Down3D(); + virtual void allocate(int nbElems); + virtual void compactStorage(); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes) = 0; + int FindFaceByNodes(int cellId, ElemByNodesType& faceByNodes); +}; + +class SMDS_DownEdge: public SMDS_Down1D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownEdge(SMDS_UnstructuredGrid *grid); + ~SMDS_DownEdge(); +}; + +class SMDS_DownQuadEdge: public SMDS_Down1D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownQuadEdge(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadEdge(); +}; + +class SMDS_DownTriangle: public SMDS_Down2D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownTriangle(SMDS_UnstructuredGrid *grid); + ~SMDS_DownTriangle(); + virtual void computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); //!< Id's are downward connectivity id's +}; + +class SMDS_DownQuadTriangle: public SMDS_Down2D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownQuadTriangle(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadTriangle(); + virtual void computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); //!< Id's are downward connectivity id's +}; + +class SMDS_DownQuadrangle: public SMDS_Down2D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownQuadrangle(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadrangle(); + virtual void computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); //!< Id's are downward connectivity id's +}; + +class SMDS_DownQuadQuadrangle: public SMDS_Down2D +{ + friend class SMDS_UnstructuredGrid; +public: +protected: + SMDS_DownQuadQuadrangle(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadQuadrangle(); + virtual void computeEdgesWithNodes(int cellId, ListElemByNodesType& edgesWithNodes); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); //!< Id's are downward connectivity id's +}; + +//class SMDS_DownPolygon: public SMDS_Down2D +//{ +//public: +// SMDS_DownPolygon(SMDS_UnstructuredGrid *grid); +// ~SMDS_DownPolygon(); +//protected: +//}; + +//class SMDS_DownQuadPolygon: public SMDS_Down2D +//{ +//public: +// SMDS_DownQuadPolygon(SMDS_UnstructuredGrid *grid); +// ~SMDS_DownQuadPolygon(); +//protected: +//}; + +class SMDS_DownTetra: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownTetra(SMDS_UnstructuredGrid *grid); + ~SMDS_DownTetra(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownQuadTetra: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownQuadTetra(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadTetra(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownPyramid: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownPyramid(SMDS_UnstructuredGrid *grid); + ~SMDS_DownPyramid(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownQuadPyramid: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownQuadPyramid(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadPyramid(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownPenta: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownPenta(SMDS_UnstructuredGrid *grid); + ~SMDS_DownPenta(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownQuadPenta: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownQuadPenta(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadPenta(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownHexa: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownHexa(SMDS_UnstructuredGrid *grid); + ~SMDS_DownHexa(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +class SMDS_DownQuadHexa: public SMDS_Down3D +{ + friend class SMDS_UnstructuredGrid; +public: + virtual void getOrderedNodesOfFace(int cellId, std::vector& orderedNodes); +protected: + SMDS_DownQuadHexa(SMDS_UnstructuredGrid *grid); + ~SMDS_DownQuadHexa(); + virtual void addDownCell(int cellId, int lowCellId, unsigned char aType); + virtual void computeFacesWithNodes(int cellId, ListElemByNodesType& facesWithNodes); +}; + +//class SMDS_DownPolyhedra: public SMDS_Down3D +//{ +//public: +// SMDS_DownPolyhedra(SMDS_UnstructuredGrid *grid); +// ~SMDS_DownPolyhedra(); +//protected: +//}; + +//class SMDS_DownQuadPolyhedra: public SMDS_Down3D +//{ +//public: +// SMDS_DownQuadPolyhedra(SMDS_UnstructuredGrid *grid); +// ~SMDS_DownQuadPolyhedra(); +//protected: +//}; + +#endif /* SMDS_DOWNWARD_HXX_ */ diff --git a/src/SMDS/SMDS_EdgePosition.cxx b/src/SMDS/SMDS_EdgePosition.cxx index 1317d2b75..dcc47f72a 100644 --- a/src/SMDS/SMDS_EdgePosition.cxx +++ b/src/SMDS/SMDS_EdgePosition.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_EdgePosition.cxx // Author : Jean-Michel BOULCOURT @@ -35,33 +36,23 @@ using namespace std; //purpose : //======================================================================= -SMDS_EdgePosition::SMDS_EdgePosition(const int aEdgeId, - const double aUParam):SMDS_Position(aEdgeId), myUParameter(aUParam) -{ -} - -//======================================================================= -//function : Coords -//purpose : -//======================================================================= - -const double *SMDS_EdgePosition::Coords() const +SMDS_EdgePosition::SMDS_EdgePosition(const double aUParam): myUParameter(aUParam) { - static double origin[]={0,0,0}; - MESSAGE("SMDS_EdgePosition::Coords not implemented"); - return origin; + //MESSAGE("********************************* SMDS_EdgePosition " << myUParameter); } /** */ SMDS_TypeOfPosition SMDS_EdgePosition::GetTypeOfPosition() const { - return SMDS_TOP_EDGE; + //MESSAGE("###################################### SMDS_EdgePosition::GetTypeOfPosition"); + return SMDS_TOP_EDGE; } void SMDS_EdgePosition::SetUParameter(double aUparam) { - myUParameter = aUparam; + //MESSAGE("############################### SMDS_EdgePosition::SetUParameter " << aUparam); + myUParameter = aUparam; } //======================================================================= @@ -71,5 +62,6 @@ void SMDS_EdgePosition::SetUParameter(double aUparam) double SMDS_EdgePosition::GetUParameter() const { - return myUParameter; + //MESSAGE("########################## SMDS_EdgePosition::GetUParameter " << myUParameter); + return myUParameter; } diff --git a/src/SMDS/SMDS_EdgePosition.hxx b/src/SMDS/SMDS_EdgePosition.hxx index f6005e947..ced2e9403 100644 --- a/src/SMDS/SMDS_EdgePosition.hxx +++ b/src/SMDS/SMDS_EdgePosition.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_EdgePosition.hxx // Module : SMESH @@ -34,15 +35,14 @@ class SMDS_EXPORT SMDS_EdgePosition:public SMDS_Position { public: - SMDS_EdgePosition(const int aEdgeId=0, const double aUParam=0); - const virtual double * Coords() const; - SMDS_TypeOfPosition GetTypeOfPosition() const; - void SetUParameter(double aUparam); - double GetUParameter() const; + SMDS_EdgePosition(const double aUParam=0); + SMDS_TypeOfPosition GetTypeOfPosition() const; + void SetUParameter(double aUparam); + double GetUParameter() const; private: - double myUParameter; + double myUParameter; }; diff --git a/src/SMDS/SMDS_ElemIterator.hxx b/src/SMDS/SMDS_ElemIterator.hxx index 511426e5d..129d8f6c4 100755 --- a/src/SMDS/SMDS_ElemIterator.hxx +++ b/src/SMDS/SMDS_ElemIterator.hxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshElement.hxx // Module : SMESH -// Created: 12.01.05 18:02:52 -// Author: Michael Sazonov +// Created: 12.01.05 18:02:52 +// Author: Michael Sazonov // #ifndef SMDS_ElemIterator_HeaderFile #define SMDS_ElemIterator_HeaderFile @@ -33,6 +34,7 @@ class SMDS_MeshElement; class SMDS_MeshNode; +class SMDS_Mesh0DElement; class SMDS_MeshEdge; class SMDS_MeshFace; class SMDS_MeshVolume; @@ -43,6 +45,9 @@ typedef boost::shared_ptr > SMDS_ElemIte typedef SMDS_Iterator SMDS_NodeIterator; typedef boost::shared_ptr > SMDS_NodeIteratorPtr; +typedef SMDS_Iterator SMDS_0DElementIterator; +typedef boost::shared_ptr > SMDS_0DElementIteratorPtr; + typedef SMDS_Iterator SMDS_EdgeIterator; typedef boost::shared_ptr > SMDS_EdgeIteratorPtr; diff --git a/src/SMDS/SMDS_FaceOfEdges.cxx b/src/SMDS/SMDS_FaceOfEdges.cxx index 5b5483022..a03bc0bf7 100644 --- a/src/SMDS/SMDS_FaceOfEdges.cxx +++ b/src/SMDS/SMDS_FaceOfEdges.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -28,6 +29,7 @@ #include "SMDS_FaceOfEdges.hxx" #include "SMDS_IteratorOfElements.hxx" #include "SMDS_MeshNode.hxx" +#include "utilities.h" using namespace std; @@ -38,12 +40,12 @@ using namespace std; int SMDS_FaceOfEdges::NbEdges() const { - return myNbEdges; + return myNbEdges; } int SMDS_FaceOfEdges::NbFaces() const { - return 1; + return 1; } //======================================================================= //function : Print @@ -52,15 +54,15 @@ int SMDS_FaceOfEdges::NbFaces() const void SMDS_FaceOfEdges::Print(ostream & OS) const { - OS << "face <" << GetID() << " > : "; - int i; - for (i = 0; i < NbEdges() - 1; i++) OS << myEdges[i] << ","; - OS << myEdges[i] << ") " << endl; + OS << "face <" << GetID() << " > : "; + int i; + for (i = 0; i < NbEdges() - 1; i++) OS << myEdges[i] << ","; + OS << myEdges[i] << ") " << endl; } SMDSAbs_ElementType SMDS_FaceOfEdges::GetType() const { - return SMDSAbs_Face; + return SMDSAbs_Face; } //======================================================================= @@ -86,7 +88,7 @@ class SMDS_FaceOfEdges_MyIterator:public SMDS_ElemIterator { index++; return mySet[index-1]; - } + } }; SMDS_ElemIteratorPtr SMDS_FaceOfEdges::elementsIterator @@ -110,11 +112,12 @@ SMDS_FaceOfEdges::SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, const SMDS_MeshEdge* edge2, const SMDS_MeshEdge* edge3) { - myNbEdges = 3; - myEdges[0]=edge1; - myEdges[1]=edge2; - myEdges[2]=edge3; - myEdges[3]=0; + //MESSAGE("****************************************************** SMDS_FaceOfEdges"); + myNbEdges = 3; + myEdges[0]=edge1; + myEdges[1]=edge2; + myEdges[2]=edge3; + myEdges[3]=0; } SMDS_FaceOfEdges::SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, @@ -122,38 +125,39 @@ SMDS_FaceOfEdges::SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, const SMDS_MeshEdge* edge3, const SMDS_MeshEdge* edge4) { - myNbEdges = 4; - myEdges[0]=edge1; - myEdges[1]=edge2; - myEdges[2]=edge3; - myEdges[3]=edge4; + //MESSAGE("****************************************************** SMDS_FaceOfEdges"); + myNbEdges = 4; + myEdges[0]=edge1; + myEdges[1]=edge2; + myEdges[2]=edge3; + myEdges[3]=edge4; } /*bool operator<(const SMDS_FaceOfEdges& f1, const SMDS_FaceOfEdges& f2) { - set set1,set2; - SMDS_ElemIteratorPtr it; - const SMDS_MeshNode * n; - - it=f1.nodesIterator(); - - while(it->more()) - { - n=static_cast(it->next()); - set1.insert(*n); - } - - delete it; - it=f2.nodesIterator(); - - while(it->more()) - { - n=static_cast(it->next()); - set2.insert(*n); - } - - delete it; - return set1 set1,set2; + SMDS_ElemIteratorPtr it; + const SMDS_MeshNode * n; + + it=f1.nodesIterator(); + + while(it->more()) + { + n=static_cast(it->next()); + set1.insert(*n); + } + + delete it; + it=f2.nodesIterator(); + + while(it->more()) + { + n=static_cast(it->next()); + set2.insert(*n); + } + + delete it; + return set1= myEdges[ i ]->NbNodes() ) index -= myEdges[ i ]->NbNodes(); @@ -183,3 +185,12 @@ const SMDS_MeshNode* SMDS_FaceOfEdges::GetNode(const int ind) const return 0; } +SMDSAbs_EntityType SMDS_FaceOfEdges::GetEntityType() const +{ + return myNbEdges == 3 ? SMDSEntity_Triangle : SMDSEntity_Quadrangle; +} + +SMDSAbs_GeometryType SMDS_FaceOfEdges::GetGeomType() const +{ + return myNbEdges == 3 ? SMDSGeom_TRIANGLE : SMDSGeom_QUADRANGLE; +} diff --git a/src/SMDS/SMDS_FaceOfEdges.hxx b/src/SMDS/SMDS_FaceOfEdges.hxx index 23659b85c..9eb81b1f0 100644 --- a/src/SMDS/SMDS_FaceOfEdges.hxx +++ b/src/SMDS/SMDS_FaceOfEdges.hxx @@ -1,25 +1,26 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMDS : implementaion of Salome mesh data structure + +// SMESH SMDS : implementation of Salome mesh data structure // #ifndef _SMDS_FaceOfEdges_HeaderFile #define _SMDS_FaceOfEdges_HeaderFile @@ -36,37 +37,30 @@ class SMDS_EXPORT SMDS_FaceOfEdges:public SMDS_MeshFace { public: - void Print(std::ostream & OS) const; - SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, + void Print(std::ostream & OS) const; + SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, const SMDS_MeshEdge* edge2, const SMDS_MeshEdge* edge3); - SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, + SMDS_FaceOfEdges(const SMDS_MeshEdge* edge1, const SMDS_MeshEdge* edge2, const SMDS_MeshEdge* edge3, const SMDS_MeshEdge* edge4); - - SMDSAbs_ElementType GetType() const; - int NbNodes() const; - int NbEdges() const; - int NbFaces() const; -// friend bool operator<(const SMDS_FaceOfEdges& e1, const SMDS_FaceOfEdges& e2); - - - /*! - * \brief Return node by its index - * \param ind - node index - * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range - */ - virtual const SMDS_MeshNode* GetNode(const int ind) const; + + virtual SMDSAbs_ElementType GetType() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual SMDSAbs_GeometryType GetGeomType() const; + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], + const int nbNodes) {return false;} + virtual int NbNodes() const; + virtual int NbEdges() const; + virtual int NbFaces() const; + virtual const SMDS_MeshNode* GetNode(const int ind) const; protected: - SMDS_ElemIteratorPtr - elementsIterator(SMDSAbs_ElementType type) const; + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; private: - const SMDS_MeshEdge* myEdges[4]; + const SMDS_MeshEdge* myEdges[4]; int myNbEdges; }; diff --git a/src/SMDS/SMDS_FaceOfNodes.cxx b/src/SMDS/SMDS_FaceOfNodes.cxx index 645e934dd..9d05d4505 100644 --- a/src/SMDS/SMDS_FaceOfNodes.cxx +++ b/src/SMDS/SMDS_FaceOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -42,17 +43,17 @@ using namespace std; int SMDS_FaceOfNodes::NbEdges() const { - return NbNodes(); + return NbNodes(); } int SMDS_FaceOfNodes::NbFaces() const { - return 1; + return 1; } int SMDS_FaceOfNodes::NbNodes() const { - return myNbNodes; + return myNbNodes; } //======================================================================= @@ -62,10 +63,10 @@ int SMDS_FaceOfNodes::NbNodes() const void SMDS_FaceOfNodes::Print(ostream & OS) const { - OS << "face <" << GetID() << " > : "; - int i; - for (i = 0; i < NbNodes() - 1; i++) OS << myNodes[i] << ","; - OS << myNodes[i] << ") " << endl; + OS << "face <" << GetID() << " > : "; + int i; + for (i = 0; i < NbNodes() - 1; i++) OS << myNodes[i] << ","; + OS << myNodes[i] << ") " << endl; } //======================================================================= @@ -95,7 +96,7 @@ public: myElems.reserve( face->NbNodes() ); for ( int i = 0; i < face->NbNodes(); ++i ) { const SMDS_MeshElement* edge = - SMDS_Mesh::FindEdge( face->GetNode( i ), face->GetNode( i + 1 )); + SMDS_Mesh::FindEdge( face->GetNode( i ), face->GetNodeWrap( i + 1 )); if ( edge ) myElems.push_back( edge ); } @@ -132,11 +133,12 @@ SMDS_FaceOfNodes::SMDS_FaceOfNodes(const SMDS_MeshNode* node1, const SMDS_MeshNode* node2, const SMDS_MeshNode* node3) { - myNbNodes = 3; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=0; + //MESSAGE("******************************************************* SMDS_FaceOfNodes"); + myNbNodes = 3; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=0; } SMDS_FaceOfNodes::SMDS_FaceOfNodes(const SMDS_MeshNode* node1, @@ -144,11 +146,12 @@ SMDS_FaceOfNodes::SMDS_FaceOfNodes(const SMDS_MeshNode* node1, const SMDS_MeshNode* node3, const SMDS_MeshNode* node4) { - myNbNodes = 4; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=node4; + //MESSAGE("******************************************************* SMDS_FaceOfNodes"); + myNbNodes = 4; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=node4; } bool SMDS_FaceOfNodes::ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) @@ -169,39 +172,17 @@ bool SMDS_FaceOfNodes::ChangeNodes(const SMDS_MeshNode* nodes[], * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ const SMDS_MeshNode* SMDS_FaceOfNodes::GetNode(const int ind) const { - return myNodes[ WrappedIndex( ind )]; + return myNodes[ ind ]; } -/*bool operator<(const SMDS_FaceOfNodes& f1, const SMDS_FaceOfNodes& f2) +SMDSAbs_EntityType SMDS_FaceOfNodes::GetEntityType() const { - set set1,set2; - SMDS_ElemIteratorPtr it; - const SMDS_MeshNode * n; - - it=f1.nodesIterator(); - - while(it->more()) - { - n=static_cast(it->next()); - set1.insert(*n); - } - - delete it; - it=f2.nodesIterator(); - - while(it->more()) - { - n=static_cast(it->next()); - set2.insert(*n); - } - - delete it; - return set1 class SMDS_Iterator { public: - /// Return true if and only if there are other object in this iterator - virtual bool more()=0; - - /// Return the current object and step to the next one - virtual VALUE next()=0; - - /// Delete the current element and step to the next one - virtual void remove(){} - - /// Provide virtual destructor just for case if some derived iterator + /// Return true if and only if there are other object in this iterator + virtual bool more()=0; + + /// Return the current object and step to the next one + virtual VALUE next()=0; + + /// Delete the current element and step to the next one + virtual void remove(){} + + /// Provide virtual destructor just for case if some derived iterator /// must have a destructor - virtual ~SMDS_Iterator(){} + virtual ~SMDS_Iterator(){} }; #endif diff --git a/src/SMDS/SMDS_IteratorOfElements.cxx b/src/SMDS/SMDS_IteratorOfElements.cxx index 220e67ac9..761fa55e9 100644 --- a/src/SMDS/SMDS_IteratorOfElements.cxx +++ b/src/SMDS/SMDS_IteratorOfElements.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -29,24 +30,24 @@ bool SMDS_IteratorOfElements::subMore() { - if((t2Iterator.get()==NULL)||(!t2Iterator->more())) - { - if(t1Iterator->more()) - { - t2Iterator=t1Iterator->next()->elementsIterator(myType); - return subMore(); - } - else return false; - } - else return true; + if((t2Iterator.get()==NULL)||(!t2Iterator->more())) + { + if(t1Iterator->more()) + { + t2Iterator=t1Iterator->next()->elementsIterator(myType); + return subMore(); + } + else return false; + } + else return true; } const SMDS_MeshElement * SMDS_IteratorOfElements::subNext() { - if((t2Iterator.get()==NULL)||(!t2Iterator->more())) - if(t1Iterator->more()) - t2Iterator=t1Iterator->next()->elementsIterator(myType); - return t2Iterator->next(); + if((t2Iterator.get()==NULL)||(!t2Iterator->more())) + if(t1Iterator->more()) + t2Iterator=t1Iterator->next()->elementsIterator(myType); + return t2Iterator->next(); } ///////////////////////////////////////////////////////////////////////////// @@ -61,48 +62,48 @@ SMDS_IteratorOfElements::SMDS_IteratorOfElements(const SMDS_MeshElement * elemen myType(type), myElement(element), myProxyElement(NULL) { - while(subMore()) - alreadyReturnedElements.insert(subNext()); - itAlreadyReturned= alreadyReturnedElements.begin(); - switch(myElement->GetType()) - { - case SMDSAbs_Node: - case SMDSAbs_Edge: myReverseIteration=true; break; - case SMDSAbs_Face: myReverseIteration=(type==SMDSAbs_Volume); break; - default: myReverseIteration=false; - } + while(subMore()) + alreadyReturnedElements.insert(subNext()); + itAlreadyReturned= alreadyReturnedElements.begin(); + switch(myElement->GetType()) + { + case SMDSAbs_Node: + case SMDSAbs_Edge: myReverseIteration=true; break; + case SMDSAbs_Face: myReverseIteration=(type==SMDSAbs_Volume); break; + default: myReverseIteration=false; + } } bool SMDS_IteratorOfElements::more() { - if(myProxyElement==NULL) - { - while(itAlreadyReturned!=alreadyReturnedElements.end()) - { - myProxyElement=*itAlreadyReturned; - itAlreadyReturned++; + if(myProxyElement==NULL) + { + while(itAlreadyReturned!=alreadyReturnedElements.end()) + { + myProxyElement=*itAlreadyReturned; + itAlreadyReturned++; - if(myReverseIteration) - { - SMDS_ElemIteratorPtr it= - myProxyElement->elementsIterator(myElement->GetType()); - while(it->more()) - { - if(it->next()==myElement) return true; - } - } - else return true; - } - myProxyElement=NULL; - return false; - } - else return true; + if(myReverseIteration) + { + SMDS_ElemIteratorPtr it= + myProxyElement->elementsIterator(myElement->GetType()); + while(it->more()) + { + if(it->next()==myElement) return true; + } + } + else return true; + } + myProxyElement=NULL; + return false; + } + else return true; } const SMDS_MeshElement * SMDS_IteratorOfElements::next() { - more(); - const SMDS_MeshElement *e=myProxyElement; - myProxyElement=NULL; - return e; + more(); + const SMDS_MeshElement *e=myProxyElement; + myProxyElement=NULL; + return e; } diff --git a/src/SMDS/SMDS_IteratorOfElements.hxx b/src/SMDS/SMDS_IteratorOfElements.hxx index 381ff647a..239cb9fb0 100644 --- a/src/SMDS/SMDS_IteratorOfElements.hxx +++ b/src/SMDS/SMDS_IteratorOfElements.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #include "SMESH_SMDS.hxx" @@ -35,22 +36,22 @@ class SMDS_EXPORT SMDS_IteratorOfElements:public SMDS_ElemIterator /// Create an iterator which look for elements of type type which are linked /// to the element element. it is the iterator to get connectivity of element ////////////////////////////////////////////////////////////////////////////// - SMDS_IteratorOfElements(const SMDS_MeshElement * element, + SMDS_IteratorOfElements(const SMDS_MeshElement * element, SMDSAbs_ElementType type, const SMDS_ElemIteratorPtr& it); - bool more(); - const SMDS_MeshElement * next(); + bool more(); + const SMDS_MeshElement * next(); private: - SMDS_ElemIteratorPtr t2Iterator; - SMDS_ElemIteratorPtr t1Iterator; - SMDSAbs_ElementType myType; - const SMDS_MeshElement * myProxyElement; - const SMDS_MeshElement * myElement; - bool myReverseIteration; + SMDS_ElemIteratorPtr t2Iterator; + SMDS_ElemIteratorPtr t1Iterator; + SMDSAbs_ElementType myType; + const SMDS_MeshElement * myProxyElement; + const SMDS_MeshElement * myElement; + bool myReverseIteration; - std::set alreadyReturnedElements; - std::set::iterator itAlreadyReturned; - bool subMore(); - const SMDS_MeshElement * subNext(); + std::set alreadyReturnedElements; + std::set::iterator itAlreadyReturned; + bool subMore(); + const SMDS_MeshElement * subNext(); }; diff --git a/src/SMDS/SMDS_IteratorOnIterators.hxx b/src/SMDS/SMDS_IteratorOnIterators.hxx new file mode 100644 index 000000000..105edfdc1 --- /dev/null +++ b/src/SMDS/SMDS_IteratorOnIterators.hxx @@ -0,0 +1,67 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_IteratorOnIterators.hxx +// Author : Edward AGAPOV (eap) +// +#ifndef SMDS_IteratorOnIterators_HeaderFile +#define SMDS_IteratorOnIterators_HeaderFile + +#include "SMDS_Iterator.hxx" + +/////////////////////////////////////////////////////////////////////////////// +/// SMDS_Iterator iterating over all elements provided by other iterators +/// +/// Other iterators must implement SMDS_Iterator iterface and +/// must be provided within a stl-like container +/// BE CAREFUL: iterator pointed value is static_cast'ed to VALUE +/////////////////////////////////////////////////////////////////////////////// + +template +class SMDS_IteratorOnIterators : public SMDS_Iterator +{ +protected: + CONTAINER_OF_ITERATORS _iterators; + typename CONTAINER_OF_ITERATORS::iterator _beg, _end; +public: + SMDS_IteratorOnIterators(const CONTAINER_OF_ITERATORS& iterators): + _iterators( iterators ), _beg( _iterators.begin()), _end(_iterators.end() ) + { + while ( _beg != _end && !(*_beg)->more()) ++_beg; + } + + /// Return true iff there are other object in this iterator + virtual bool more() { return _beg != _end && (*_beg)->more(); } + + /// Return the current object and step to the next one + virtual VALUE next() + { + VALUE __v = (VALUE)(*_beg)->next(); + while ( _beg != _end && !(*_beg)->more()) + ++_beg; + return __v; + } +}; + +#endif diff --git a/src/SMDS/SMDS_LinearEdge.cxx b/src/SMDS/SMDS_LinearEdge.cxx new file mode 100644 index 000000000..fd2ba30d6 --- /dev/null +++ b/src/SMDS/SMDS_LinearEdge.cxx @@ -0,0 +1,162 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMDS_LinearEdge.cxx +// Author : Jean-Michel BOULCOURT +// Module : SMESH +// +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#include "SMDS_LinearEdge.hxx" +#include "SMDS_IteratorOfElements.hxx" +#include "SMDS_MeshNode.hxx" +#include "utilities.h" + +using namespace std; + +//======================================================================= +//function : SMDS_LinearEdge +//purpose : +//======================================================================= + +SMDS_LinearEdge::SMDS_LinearEdge(const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2) +{ + //MESSAGE("SMDS_LinearEdge " << GetID()); + myNodes[0] = node1; + myNodes[1] = node2; +} + +//======================================================================= +//function : Print +//purpose : +//======================================================================= + +void SMDS_LinearEdge::Print(ostream & OS) const +{ + OS << "edge <" << GetID() << "> : (" << myNodes[0] << " , " << myNodes[1] + << ") " << endl; +} + +int SMDS_LinearEdge::NbNodes() const +{ + return 2; +} + +int SMDS_LinearEdge::NbEdges() const +{ + return 1; +} + +class SMDS_LinearEdge_MyNodeIterator: public SMDS_ElemIterator +{ + const SMDS_MeshNode * const * myNodes; + int myIndex; +public: + SMDS_LinearEdge_MyNodeIterator(const SMDS_MeshNode * const * nodes) : + myNodes(nodes), myIndex(0) + { + } + + bool more() + { + return myIndex < 2; + } + + const SMDS_MeshElement* next() + { + myIndex++; + return myNodes[myIndex - 1]; + } +}; + +SMDS_ElemIteratorPtr SMDS_LinearEdge::elementsIterator(SMDSAbs_ElementType type) const +{ + switch (type) + { + case SMDSAbs_Edge: + return SMDS_MeshElement::elementsIterator(SMDSAbs_Edge); + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr(new SMDS_LinearEdge_MyNodeIterator(myNodes)); + default: + return SMDS_ElemIteratorPtr( + new SMDS_IteratorOfElements( + this, + type, + SMDS_ElemIteratorPtr( + new SMDS_LinearEdge_MyNodeIterator( + myNodes)))); + } +} + +bool operator<(const SMDS_LinearEdge & e1, const SMDS_LinearEdge & e2) +{ + int id11 = e1.myNodes[0]->getVtkId(); + int id21 = e2.myNodes[0]->getVtkId(); + int id12 = e1.myNodes[1]->getVtkId(); + int id22 = e2.myNodes[1]->getVtkId(); + int tmp; + + if (id11 >= id12) + { + tmp = id11; + id11 = id12; + id12 = tmp; + } + if (id21 >= id22) + { + tmp = id21; + id21 = id22; + id22 = tmp; + } + + if (id11 < id21) + return true; + else if (id11 == id21) + return (id21 < id22); + else + return false; +} + +/*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ +const SMDS_MeshNode* SMDS_LinearEdge::GetNode(const int ind) const +{ + return myNodes[ind]; +} + +//======================================================================= +//function : ChangeNodes +//purpose : +//======================================================================= + +bool SMDS_LinearEdge::ChangeNodes(const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2) +{ + myNodes[0] = node1; + myNodes[1] = node2; + return true; +} diff --git a/src/SMDS/SMDS_LinearEdge.hxx b/src/SMDS/SMDS_LinearEdge.hxx new file mode 100644 index 000000000..34ca147ee --- /dev/null +++ b/src/SMDS/SMDS_LinearEdge.hxx @@ -0,0 +1,68 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMDS_LinearEdge.hxx +// Module : SMESH +// +#ifndef _SMDS_LinearEdge_HeaderFile +#define _SMDS_LinearEdge_HeaderFile + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshEdge.hxx" +#include + +class SMDS_EXPORT SMDS_LinearEdge: public SMDS_MeshEdge +{ + +public: + SMDS_LinearEdge(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2); + bool ChangeNodes(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2); + void Print(std::ostream & OS) const; + + virtual SMDSAbs_EntityType GetEntityType() const + { + return SMDSEntity_Edge; + } + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) + { + return false; + } + int NbNodes() const; + int NbEdges() const; + friend bool operator<(const SMDS_LinearEdge& e1, const SMDS_LinearEdge& e2); + + /*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ + virtual const SMDS_MeshNode* GetNode(const int ind) const; + +protected: + SMDS_ElemIteratorPtr + elementsIterator(SMDSAbs_ElementType type) const; + +protected: + const SMDS_MeshNode* myNodes[3]; + +}; +#endif diff --git a/src/SMDS/SMDS_MemoryLimit.cxx b/src/SMDS/SMDS_MemoryLimit.cxx index 4de22248e..8df52064a 100644 --- a/src/SMDS/SMDS_MemoryLimit.cxx +++ b/src/SMDS/SMDS_MemoryLimit.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMDS_MemoryLimit.cxx // Created : Fri Sep 21 17:16:42 2007 // Author : Edward AGAPOV (eap) diff --git a/src/SMDS/SMDS_Mesh.cxx b/src/SMDS/SMDS_Mesh.cxx index e8d8af697..fe6f186e4 100644 --- a/src/SMDS/SMDS_Mesh.cxx +++ b/src/SMDS/SMDS_Mesh.cxx @@ -1,52 +1,70 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMDS : implementaion of Salome mesh data structure + +// SMESH SMDS : implementation of Salome mesh data structure // #ifdef _MSC_VER #pragma warning(disable:4786) #endif -#include "utilities.h" -#include "SMDS_Mesh.hxx" -#include "SMDS_VolumeOfNodes.hxx" -#include "SMDS_VolumeOfFaces.hxx" -#include "SMDS_FaceOfNodes.hxx" #include "SMDS_FaceOfEdges.hxx" -#include "SMDS_PolyhedralVolumeOfNodes.hxx" +#include "SMDS_FaceOfNodes.hxx" +#include "SMDS_Mesh.hxx" #include "SMDS_PolygonalFaceOfNodes.hxx" +#include "SMDS_PolyhedralVolumeOfNodes.hxx" #include "SMDS_QuadraticEdge.hxx" #include "SMDS_QuadraticFaceOfNodes.hxx" #include "SMDS_QuadraticVolumeOfNodes.hxx" +#include "SMDS_SetIterator.hxx" +#include "SMDS_SpacePosition.hxx" +#include "SMDS_UnstructuredGrid.hxx" +#include "SMDS_VolumeOfFaces.hxx" +#include "SMDS_VolumeOfNodes.hxx" + +#include "utilities.h" + +#include +#include +#include +#include +#include +#include #include #include +#include +#include using namespace std; #ifndef WIN32 #include #endif -// number of added entitis to check memory after -#define CHECKMEMORY_INTERVAL 1000 +// number of added entities to check memory after +#define CHECKMEMORY_INTERVAL 100000 + +vector SMDS_Mesh::_meshList = vector(); +int SMDS_Mesh::chunkSize = 1024; + //================================================================================ /*! @@ -64,35 +82,38 @@ int SMDS_Mesh::CheckMemory(const bool doNotRaise) throw (std::bad_alloc) if ( err ) return -1; + const unsigned long Mbyte = 1024 * 1024; + static int limit = -1; if ( limit < 0 ) { int status = system("SMDS_MemoryLimit"); // it returns lower limit of free RAM if (status >= 0 ) { limit = WEXITSTATUS(status); } + else { + double factor = ( si.totalswap == 0 ) ? 0.1 : 0.2; + limit = int(( factor * si.totalram * si.mem_unit ) / Mbyte ); + } if ( limit < 20 ) limit = 20; else - limit = int( limit * 1.5 ); -#ifdef _DEBUG_ + limit = int ( limit * 1.5 ); MESSAGE ( "SMDS_Mesh::CheckMemory() memory limit = " << limit << " MB" ); -#endif } - const unsigned long Mbyte = 1024 * 1024; // compute separately to avoid overflow int freeMb = ( si.freeram * si.mem_unit ) / Mbyte + ( si.freeswap * si.mem_unit ) / Mbyte; + //cout << "freeMb = " << freeMb << " limit = " << limit << endl; if ( freeMb > limit ) return freeMb - limit; if ( doNotRaise ) return 0; -#ifdef _DEBUG_ + MESSAGE ("SMDS_Mesh::CheckMemory() throws as free memory too low: " << freeMb <<" MB" ); -#endif throw std::bad_alloc(); #else return -1; @@ -103,12 +124,50 @@ int SMDS_Mesh::CheckMemory(const bool doNotRaise) throw (std::bad_alloc) /// Create a new mesh object /////////////////////////////////////////////////////////////////////////////// SMDS_Mesh::SMDS_Mesh() - :myParent(NULL), - myNodeIDFactory(new SMDS_MeshElementIDFactory()), - myElementIDFactory(new SMDS_MeshElementIDFactory()), - myHasConstructionEdges(false), myHasConstructionFaces(false), - myHasInverseElements(true) -{ + :myParent(NULL), + myNodeIDFactory(new SMDS_MeshNodeIDFactory()), + myElementIDFactory(new SMDS_MeshElementIDFactory()), + myHasConstructionEdges(false), myHasConstructionFaces(false), + myHasInverseElements(true), + myNodeMin(0), myNodeMax(0), + myNodePool(0), myEdgePool(0), myFacePool(0), myVolumePool(0),myBallPool(0), + myModified(false), myModifTime(0), myCompactTime(0), + xmin(0), xmax(0), ymin(0), ymax(0), zmin(0), zmax(0) +{ + myMeshId = _meshList.size(); // --- index of the mesh to push back in the vector + MESSAGE("myMeshId=" << myMeshId); + MESSAGE("sizeof(SMDS_MeshElement) " << sizeof(SMDS_MeshElement) ); + MESSAGE("sizeof(SMDS_MeshNode) " << sizeof(SMDS_MeshNode) ); + MESSAGE("sizeof(SMDS_MeshCell) " << sizeof(SMDS_MeshCell) ); + MESSAGE("sizeof(SMDS_VtkVolume) " << sizeof(SMDS_VtkVolume) ); + MESSAGE("sizeof(SMDS_Position) " << sizeof(SMDS_Position) ); + MESSAGE("sizeof(SMDS_SpacePosition) " << sizeof(SMDS_SpacePosition) ); + myNodeIDFactory->SetMesh(this); + myElementIDFactory->SetMesh(this); + _meshList.push_back(this); + myNodePool = new ObjectPool(SMDS_Mesh::chunkSize); + myEdgePool = new ObjectPool(SMDS_Mesh::chunkSize); + myFacePool = new ObjectPool(SMDS_Mesh::chunkSize); + myVolumePool = new ObjectPool(SMDS_Mesh::chunkSize); + myBallPool = new ObjectPool(SMDS_Mesh::chunkSize); + + myNodes.clear(); + myCells.clear(); + //myCellIdSmdsToVtk.clear(); + myCellIdVtkToSmds.clear(); + myGrid = SMDS_UnstructuredGrid::New(); + myGrid->setSMDS_mesh(this); + myGrid->Initialize(); + myGrid->Allocate(); + vtkPoints* points = vtkPoints::New(); + // rnv: to fix bug "21125: EDF 1233 SMESH: Degrardation of precision in a test case for quadratic conversion" + // using double type for storing coordinates of nodes instead float. + points->SetDataType(VTK_DOUBLE); + points->SetNumberOfPoints(SMDS_Mesh::chunkSize); + myGrid->SetPoints( points ); + points->Delete(); + myGrid->BuildLinks(); + this->Modified(); } /////////////////////////////////////////////////////////////////////////////// @@ -117,10 +176,15 @@ SMDS_Mesh::SMDS_Mesh() /// (2003-09-08) of SMESH /////////////////////////////////////////////////////////////////////////////// SMDS_Mesh::SMDS_Mesh(SMDS_Mesh * parent) - :myParent(parent), myNodeIDFactory(parent->myNodeIDFactory), - myElementIDFactory(parent->myElementIDFactory), - myHasConstructionEdges(false), myHasConstructionFaces(false), - myHasInverseElements(true) + :myParent(parent), myNodeIDFactory(parent->myNodeIDFactory), + myElementIDFactory(parent->myElementIDFactory), + myHasConstructionEdges(false), myHasConstructionFaces(false), + myHasInverseElements(true), + myNodePool(parent->myNodePool), + myEdgePool(parent->myEdgePool), + myFacePool(parent->myFacePool), + myVolumePool(parent->myVolumePool), + myBallPool(parent->myBallPool) { } @@ -130,9 +194,9 @@ SMDS_Mesh::SMDS_Mesh(SMDS_Mesh * parent) SMDS_Mesh *SMDS_Mesh::AddSubMesh() { - SMDS_Mesh *submesh = new SMDS_Mesh(this); - myChildren.insert(myChildren.end(), submesh); - return submesh; + SMDS_Mesh *submesh = new SMDS_Mesh(this); + myChildren.insert(myChildren.end(), submesh); + return submesh; } /////////////////////////////////////////////////////////////////////////////// @@ -156,22 +220,131 @@ SMDS_MeshNode * SMDS_Mesh::AddNodeWithID(double x, double y, double z, int ID) // find the MeshNode corresponding to ID const SMDS_MeshElement *node = myNodeIDFactory->MeshElement(ID); if(!node){ - if ( myNodes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); - SMDS_MeshNode * node=new SMDS_MeshNode(x, y, z); - myNodes.Add(node); + if (ID < 1) + { + MESSAGE("=============> Bad Node Id: " << ID); + ID = myNodeIDFactory->GetFreeID(); + } + myNodeIDFactory->adjustMaxId(ID); + SMDS_MeshNode * node = myNodePool->getNew(); + node->init(ID, myMeshId, 0, x, y, z); + + if (ID >= myNodes.size()) + { + myNodes.resize(ID+SMDS_Mesh::chunkSize, 0); +// MESSAGE(" ------------------ myNodes resize " << ID << " --> " << ID+SMDS_Mesh::chunkSize); + } + myNodes[ID] = node; myNodeIDFactory->BindID(ID,node); myInfo.myNbNodes++; + myModified = true; + this->adjustBoundingBox(x, y, z); return node; }else return NULL; } +/////////////////////////////////////////////////////////////////////////////// +/// create a Mesh0DElement and add it to the current Mesh +/// @return : The created Mesh0DElement +/////////////////////////////////////////////////////////////////////////////// +SMDS_Mesh0DElement* SMDS_Mesh::Add0DElementWithID(int idnode, int ID) +{ + SMDS_MeshNode * node = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode); + if (!node) return NULL; + return SMDS_Mesh::Add0DElementWithID(node, ID); +} + +/////////////////////////////////////////////////////////////////////////////// +/// create a Mesh0DElement and add it to the current Mesh +/// @return : The created Mesh0DElement +/////////////////////////////////////////////////////////////////////////////// +SMDS_Mesh0DElement* SMDS_Mesh::Add0DElement(const SMDS_MeshNode * node) +{ + return SMDS_Mesh::Add0DElementWithID(node, myElementIDFactory->GetFreeID()); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Create a new Mesh0DElement and at it to the mesh +/// @param idnode ID of the node +/// @param ID ID of the 0D element to create +/// @return The created 0D element or NULL if an element with this +/// ID already exists or if input node is not found. +/////////////////////////////////////////////////////////////////////////////// +SMDS_Mesh0DElement* SMDS_Mesh::Add0DElementWithID(const SMDS_MeshNode * n, int ID) +{ + if (!n) return 0; + + if (Nb0DElements() % CHECKMEMORY_INTERVAL == 0) CheckMemory(); + //MESSAGE("Add0DElementWithID" << ID) + SMDS_Mesh0DElement * el0d = new SMDS_Mesh0DElement(n); + if (myElementIDFactory->BindID(ID, el0d)) { + //SMDS_MeshNode *node = const_cast(n); + //node->AddInverseElement(el0d);// --- fait avec BindID + adjustmyCellsCapacity(ID); + myCells[ID] = el0d; + myInfo.myNb0DElements++; + return el0d; + } + + delete el0d; + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +/// create a Ball and add it to the current Mesh +/// @return : The created Ball +/////////////////////////////////////////////////////////////////////////////// +SMDS_BallElement* SMDS_Mesh::AddBallWithID(int idnode, double diameter, int ID) +{ + SMDS_MeshNode * node = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode); + if (!node) return NULL; + return SMDS_Mesh::AddBallWithID(node, diameter, ID); +} + +/////////////////////////////////////////////////////////////////////////////// +/// create a Ball and add it to the current Mesh +/// @return : The created Ball +/////////////////////////////////////////////////////////////////////////////// +SMDS_BallElement* SMDS_Mesh::AddBall(const SMDS_MeshNode * node, double diameter) +{ + return SMDS_Mesh::AddBallWithID(node, diameter, myElementIDFactory->GetFreeID()); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Create a new Ball and at it to the mesh +/// @param idnode ID of the node +// @param diameter ball diameter +/// @param ID ID of the 0D element to create +/// @return The created 0D element or NULL if an element with this +/// ID already exists or if input node is not found. +/////////////////////////////////////////////////////////////////////////////// +SMDS_BallElement* SMDS_Mesh::AddBallWithID(const SMDS_MeshNode * n, double diameter, int ID) +{ + if (!n) return 0; + + if (NbBalls() % CHECKMEMORY_INTERVAL == 0) CheckMemory(); + + SMDS_BallElement *ball = myBallPool->getNew(); + ball->init(n->getVtkId(), diameter, this); + if (!this->registerElement(ID,ball)) + { + this->myGrid->GetCellTypesArray()->SetValue(ball->getVtkId(), VTK_EMPTY_CELL); + myBallPool->destroy(ball); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = ball; + myInfo.myNbBalls++; + return ball; +} + /////////////////////////////////////////////////////////////////////////////// /// create a MeshEdge and add it to the current Mesh /// @return : The created MeshEdge /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int idnode1, int idnode2, int ID) +SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int idnode1, int idnode2, int ID) { SMDS_MeshNode * node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); SMDS_MeshNode * node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); @@ -185,7 +358,7 @@ SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int idnode1, int idnode2, int ID) /////////////////////////////////////////////////////////////////////////////// SMDS_MeshEdge* SMDS_Mesh::AddEdge(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2) + const SMDS_MeshNode * node2) { return SMDS_Mesh::AddEdgeWithID(node1, node2, myElementIDFactory->GetFreeID()); } @@ -200,28 +373,37 @@ SMDS_MeshEdge* SMDS_Mesh::AddEdge(const SMDS_MeshNode * node1, /////////////////////////////////////////////////////////////////////////////// SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - int ID) + const SMDS_MeshNode * n2, + int ID) { if ( !n1 || !n2 ) return 0; + SMDS_MeshEdge * edge = 0; - if ( myEdges.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + // --- retreive nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); - SMDS_MeshEdge * edge=new SMDS_MeshEdge(n1,n2); - if(myElementIDFactory->BindID(ID, edge)) { - SMDS_MeshNode *node1,*node2; - node1=const_cast(n1); - node2=const_cast(n2); - node1->AddInverseElement(edge); - node2->AddInverseElement(edge); - myEdges.Add(edge); - myInfo.myNbEdges++; - return edge; - } - else { - delete edge; - return NULL; - } + SMDS_VtkEdge *edgevtk = myEdgePool->getNew(); + edgevtk->init(nodeIds, this); + if (!this->registerElement(ID,edgevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(edgevtk->getVtkId(), VTK_EMPTY_CELL); + myEdgePool->destroy(edgevtk); + return 0; + } + edge = edgevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = edge; + myInfo.myNbEdges++; + +// if (edge && !registerElement(ID, edge)) +// { +// RemoveElement(edge, false); +// edge = NULL; +// } + return edge; } /////////////////////////////////////////////////////////////////////////////// @@ -230,8 +412,8 @@ SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(const SMDS_MeshNode * n1, /////////////////////////////////////////////////////////////////////////////// SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3) { return SMDS_Mesh::AddFaceWithID(n1,n2,n3, myElementIDFactory->GetFreeID()); } @@ -246,7 +428,7 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int idnode1, int idnode2, int idnode3, i SMDS_MeshNode * node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); SMDS_MeshNode * node3 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode3); if(!node1 || !node2 || !node3) return NULL; - return SMDS_Mesh::AddFaceWithID(node1, node2, node3, ID); + return SMDS_Mesh::AddFaceWithID(node1, node2, node3, ID); } /////////////////////////////////////////////////////////////////////////////// @@ -258,12 +440,13 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n3, int ID) { - SMDS_MeshFace * face=createTriangle(n1, n2, n3); + //MESSAGE("AddFaceWithID " << ID) + SMDS_MeshFace * face=createTriangle(n1, n2, n3, ID); - if (face && !registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; - } +// if (face && !registerElement(ID, face)) { +// RemoveElement(face, false); +// face = NULL; +// } return face; } @@ -273,9 +456,9 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, /////////////////////////////////////////////////////////////////////////////// SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4) { return SMDS_Mesh::AddFaceWithID(n1,n2,n3, n4, myElementIDFactory->GetFreeID()); } @@ -284,11 +467,11 @@ SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, /// Add a quadrangle defined by its nodes IDs /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int idnode1, - int idnode2, - int idnode3, - int idnode4, - int ID) +SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int ID) { SMDS_MeshNode *node1, *node2, *node3, *node4; node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); @@ -296,7 +479,7 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int idnode1, node3 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode3); node4 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode4); if(!node1 || !node2 || !node3 || !node4) return NULL; - return SMDS_Mesh::AddFaceWithID(node1, node2, node3, node4, ID); + return SMDS_Mesh::AddFaceWithID(node1, node2, node3, node4, ID); } /////////////////////////////////////////////////////////////////////////////// @@ -309,12 +492,13 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n4, int ID) { - SMDS_MeshFace * face=createQuadrangle(n1, n2, n3, n4); + //MESSAGE("AddFaceWithID " << ID); + SMDS_MeshFace * face=createQuadrangle(n1, n2, n3, n4, ID); - if (face && !registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; - } +// if (face && !registerElement(ID, face)) { +// RemoveElement(face, false); +// face = NULL; +// } return face; } @@ -329,7 +513,8 @@ SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshEdge * e1, { if (!hasConstructionEdges()) return NULL; - return AddFaceWithID(e1,e2,e3, myElementIDFactory->GetFreeID()); + //MESSAGE("AddFaceWithID"); + return AddFaceWithID(e1,e2,e3, myElementIDFactory->GetFreeID()); } /////////////////////////////////////////////////////////////////////////////// @@ -345,15 +530,18 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshEdge * e1, return NULL; if ( !e1 || !e2 || !e3 ) return 0; - if ( myFaces.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbFaces() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + MESSAGE("AddFaceWithID" << ID); SMDS_MeshFace * face = new SMDS_FaceOfEdges(e1,e2,e3); - myFaces.Add(face); + adjustmyCellsCapacity(ID); + myCells[ID] = face; myInfo.myNbTriangles++; if (!registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; + registerElement(myElementIDFactory->GetFreeID(), face); + //RemoveElement(face, false); + //face = NULL; } return face; } @@ -370,7 +558,8 @@ SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshEdge * e1, { if (!hasConstructionEdges()) return NULL; - return AddFaceWithID(e1,e2,e3,e4, myElementIDFactory->GetFreeID()); + //MESSAGE("AddFaceWithID" ); + return AddFaceWithID(e1,e2,e3,e4, myElementIDFactory->GetFreeID()); } /////////////////////////////////////////////////////////////////////////////// @@ -385,49 +574,54 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshEdge * e1, { if (!hasConstructionEdges()) return NULL; + MESSAGE("AddFaceWithID" << ID); if ( !e1 || !e2 || !e3 || !e4 ) return 0; - if ( myFaces.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbFaces() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); SMDS_MeshFace * face = new SMDS_FaceOfEdges(e1,e2,e3,e4); - myFaces.Add(face); + adjustmyCellsCapacity(ID); + myCells[ID] = face; myInfo.myNbQuadrangles++; if (!registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; + registerElement(myElementIDFactory->GetFreeID(), face); + //RemoveElement(face, false); + //face = NULL; } return face; } /////////////////////////////////////////////////////////////////////////////// -///Create a new tetrahedron and add it to the mesh. -///@return The created tetrahedron +///Create a new tetrahedron and add it to the mesh. +///@return The created tetrahedron /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4) { int ID = myElementIDFactory->GetFreeID(); + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, ID); if(v==NULL) myElementIDFactory->ReleaseID(ID); return v; } /////////////////////////////////////////////////////////////////////////////// -///Create a new tetrahedron and add it to the mesh. +///Create a new tetrahedron and add it to the mesh. ///@param ID The ID of the new volume ///@return The created tetrahedron or NULL if an element with this ID already exists ///or if input nodes are not found. /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, - int idnode2, - int idnode3, - int idnode4, - int ID) +SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int ID) { + //MESSAGE("AddVolumeWithID" << ID); SMDS_MeshNode *node1, *node2, *node3, *node4; node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); @@ -436,11 +630,11 @@ SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, if(!node1 || !node2 || !node3 || !node4) return NULL; return SMDS_Mesh::AddVolumeWithID(node1, node2, node3, node4, ID); } - + /////////////////////////////////////////////////////////////////////////////// -///Create a new tetrahedron and add it to the mesh. +///Create a new tetrahedron and add it to the mesh. ///@param ID The ID of the new volume -///@return The created tetrahedron +///@return The created tetrahedron /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, @@ -449,16 +643,18 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n4, int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume* volume = 0; if ( !n1 || !n2 || !n3 || !n4) return volume; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionFaces()) { SMDS_MeshFace * f1=FindFaceOrCreate(n1,n2,n3); SMDS_MeshFace * f2=FindFaceOrCreate(n1,n2,n4); SMDS_MeshFace * f3=FindFaceOrCreate(n1,n3,n4); SMDS_MeshFace * f4=FindFaceOrCreate(n2,n3,n4); volume=new SMDS_VolumeOfFaces(f1,f2,f3,f4); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbTetras++; } else if(hasConstructionEdges()) { @@ -466,51 +662,70 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, return NULL; } else { - volume=new SMDS_VolumeOfNodes(n1,n2,n3,n4); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n3->getVtkId()); // order SMDS-->VTK + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbTetras++; } - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } +// if (!registerElement(ID, volume)) { +// RemoveElement(volume, false); +// volume = NULL; +// } return volume; } /////////////////////////////////////////////////////////////////////////////// -///Create a new pyramid and add it to the mesh. +///Create a new pyramid and add it to the mesh. ///Nodes 1,2,3 and 4 define the base of the pyramid -///@return The created pyramid +///@return The created pyramid /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5) { int ID = myElementIDFactory->GetFreeID(); + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, ID); if(v==NULL) myElementIDFactory->ReleaseID(ID); return v; } /////////////////////////////////////////////////////////////////////////////// -///Create a new pyramid and add it to the mesh. +///Create a new pyramid and add it to the mesh. ///Nodes 1,2,3 and 4 define the base of the pyramid ///@param ID The ID of the new volume ///@return The created pyramid or NULL if an element with this ID already exists ///or if input nodes are not found. /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, - int idnode2, - int idnode3, - int idnode4, - int idnode5, - int ID) +SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int idnode5, + int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshNode *node1, *node2, *node3, *node4, *node5; node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); @@ -520,7 +735,7 @@ SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, if(!node1 || !node2 || !node3 || !node4 || !node5) return NULL; return SMDS_Mesh::AddVolumeWithID(node1, node2, node3, node4, node5, ID); } - + /////////////////////////////////////////////////////////////////////////////// ///Create a new pyramid and add it to the mesh. ///Nodes 1,2,3 and 4 define the base of the pyramid @@ -535,16 +750,18 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n5, int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume* volume = 0; if ( !n1 || !n2 || !n3 || !n4 || !n5) return volume; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionFaces()) { SMDS_MeshFace * f1=FindFaceOrCreate(n1,n2,n3,n4); SMDS_MeshFace * f2=FindFaceOrCreate(n1,n2,n5); SMDS_MeshFace * f3=FindFaceOrCreate(n2,n3,n5); SMDS_MeshFace * f4=FindFaceOrCreate(n3,n4,n5); volume=new SMDS_VolumeOfFaces(f1,f2,f3,f4); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPyramids++; } else if(hasConstructionEdges()) { @@ -552,53 +769,73 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, return NULL; } else { - volume=new SMDS_VolumeOfNodes(n1,n2,n3,n4,n5); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPyramids++; } - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } +// if (!registerElement(ID, volume)) { +// RemoveElement(volume, false); +// volume = NULL; +// } return volume; } /////////////////////////////////////////////////////////////////////////////// -///Create a new prism and add it to the mesh. +///Create a new prism and add it to the mesh. ///Nodes 1,2,3 is a triangle and 1,2,5,4 a quadrangle. -///@return The created prism +///@return The created prism /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6) { int ID = myElementIDFactory->GetFreeID(); + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, ID); if(v==NULL) myElementIDFactory->ReleaseID(ID); return v; } /////////////////////////////////////////////////////////////////////////////// -///Create a new prism and add it to the mesh. +///Create a new prism and add it to the mesh. ///Nodes 1,2,3 is a triangle and 1,2,5,4 a quadrangle. ///@param ID The ID of the new volume ///@return The created prism or NULL if an element with this ID already exists ///or if input nodes are not found. /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, - int idnode2, - int idnode3, - int idnode4, - int idnode5, - int idnode6, - int ID) +SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int idnode5, + int idnode6, + int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshNode *node1, *node2, *node3, *node4, *node5, *node6; node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); @@ -609,7 +846,7 @@ SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, if(!node1 || !node2 || !node3 || !node4 || !node5 || !node6) return NULL; return SMDS_Mesh::AddVolumeWithID(node1, node2, node3, node4, node5, node6, ID); } - + /////////////////////////////////////////////////////////////////////////////// ///Create a new prism and add it to the mesh. ///Nodes 1,2,3 is a triangle and 1,2,5,4 a quadrangle. @@ -625,9 +862,10 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n6, int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume* volume = 0; if ( !n1 || !n2 || !n3 || !n4 || !n5 || !n6) return volume; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionFaces()) { SMDS_MeshFace * f1=FindFaceOrCreate(n1,n2,n3); SMDS_MeshFace * f2=FindFaceOrCreate(n4,n5,n6); @@ -635,7 +873,8 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, SMDS_MeshFace * f4=FindFaceOrCreate(n2,n5,n6,n3); SMDS_MeshFace * f5=FindFaceOrCreate(n3,n6,n4,n1); volume=new SMDS_VolumeOfFaces(f1,f2,f3,f4,f5); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPrisms++; } else if(hasConstructionEdges()) { @@ -643,57 +882,208 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, return NULL; } else { - volume=new SMDS_VolumeOfNodes(n1,n2,n3,n4,n5,n6); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPrisms++; } - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; +// if (!registerElement(ID, volume)) { +// RemoveElement(volume, false); +// volume = NULL; +// } + return volume; +} + +/////////////////////////////////////////////////////////////////////////////// +///Create a new hexagonal prism and add it to the mesh. +///@return The created prism +/////////////////////////////////////////////////////////////////////////////// + +SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12) +{ + int ID = myElementIDFactory->GetFreeID(); + SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, + n7, n8, n9, n10, n11, n12, + ID); + if(v==NULL) myElementIDFactory->ReleaseID(ID); + return v; +} + +/////////////////////////////////////////////////////////////////////////////// +///Create a new hexagonal prism and add it to the mesh. +///@param ID The ID of the new volume +///@return The created prism or NULL if an element with this ID already exists +///or if input nodes are not found. +/////////////////////////////////////////////////////////////////////////////// + +SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int idnode5, + int idnode6, + int idnode7, + int idnode8, + int idnode9, + int idnode10, + int idnode11, + int idnode12, + int ID) +{ + SMDS_MeshNode *node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); + SMDS_MeshNode *node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); + SMDS_MeshNode *node3 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode3); + SMDS_MeshNode *node4 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode4); + SMDS_MeshNode *node5 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode5); + SMDS_MeshNode *node6 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode6); + SMDS_MeshNode *node7 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode7); + SMDS_MeshNode *node8 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode8); + SMDS_MeshNode *node9 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode9); + SMDS_MeshNode *node10 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode10); + SMDS_MeshNode *node11 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode11); + SMDS_MeshNode *node12 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode12); + return SMDS_Mesh::AddVolumeWithID(node1, node2, node3, node4, node5, node6, + node7, node8, node9, node10, node11, node12, + ID); +} + +/////////////////////////////////////////////////////////////////////////////// +///Create a new hexagonal prism and add it to the mesh. +///@param ID The ID of the new volume +///@return The created prism +/////////////////////////////////////////////////////////////////////////////// + +SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12, + int ID) +{ + SMDS_MeshVolume* volume = 0; + if(!n1 || !n2 || !n3 || !n4 || !n5 || !n6 || + !n7 || !n8 || !n9 || !n10 || !n11 || !n12 ) + return volume; + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if(hasConstructionFaces()) { + MESSAGE("Error : Not implemented"); + return NULL; + } + else if(hasConstructionEdges()) { + MESSAGE("Error : Not implemented"); + return NULL; + } + else { + // --- retrieve nodes ID + vector nodeIds; + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + + nodeIds.push_back(n7->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + nodeIds.push_back(n11->getVtkId()); + nodeIds.push_back(n10->getVtkId()); + nodeIds.push_back(n9->getVtkId()); + nodeIds.push_back(n8->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; + adjustmyCellsCapacity(ID); + myCells[ID] = volume; + myInfo.myNbHexPrism++; } + return volume; } /////////////////////////////////////////////////////////////////////////////// -///Create a new hexahedron and add it to the mesh. +///Create a new hexahedron and add it to the mesh. ///Nodes 1,2,3,4 and 5,6,7,8 are quadrangle and 5,1 and 7,3 are an edges. -///@return The created hexahedron +///@return The created hexahedron /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8) { int ID = myElementIDFactory->GetFreeID(); - SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8, ID); + SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8, ID); if(v==NULL) myElementIDFactory->ReleaseID(ID); return v; } /////////////////////////////////////////////////////////////////////////////// -///Create a new hexahedron and add it to the mesh. +///Create a new hexahedron and add it to the mesh. ///Nodes 1,2,3,4 and 5,6,7,8 are quadrangle and 5,1 and 7,3 are an edges. ///@param ID The ID of the new volume ///@return The created hexahedron or NULL if an element with this ID already ///exists or if input nodes are not found. /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, - int idnode2, - int idnode3, - int idnode4, - int idnode5, - int idnode6, - int idnode7, - int idnode8, - int ID) +SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, + int idnode2, + int idnode3, + int idnode4, + int idnode5, + int idnode6, + int idnode7, + int idnode8, + int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshNode *node1, *node2, *node3, *node4, *node5, *node6, *node7, *node8; node1 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode1); node2 = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(idnode2); @@ -708,7 +1098,7 @@ SMDS_MeshVolume * SMDS_Mesh::AddVolumeWithID(int idnode1, return SMDS_Mesh::AddVolumeWithID(node1, node2, node3, node4, node5, node6, node7, node8, ID); } - + /////////////////////////////////////////////////////////////////////////////// ///Create a new hexahedron and add it to the mesh. ///Nodes 1,2,3,4 and 5,6,7,8 are quadrangle and 5,1 and 7,3 are an edges. @@ -727,9 +1117,10 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n8, int ID) { + //MESSAGE("AddVolumeWithID " << ID); SMDS_MeshVolume* volume = 0; if ( !n1 || !n2 || !n3 || !n4 || !n5 || !n6 || !n7 || !n8) return volume; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionFaces()) { SMDS_MeshFace * f1=FindFaceOrCreate(n1,n2,n3,n4); SMDS_MeshFace * f2=FindFaceOrCreate(n5,n6,n7,n8); @@ -738,7 +1129,8 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, SMDS_MeshFace * f5=FindFaceOrCreate(n2,n3,n7,n6); SMDS_MeshFace * f6=FindFaceOrCreate(n3,n4,n8,n7); volume=new SMDS_VolumeOfFaces(f1,f2,f3,f4,f5,f6); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbHexas++; } else if(hasConstructionEdges()) { @@ -746,16 +1138,36 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, return NULL; } else { -// volume=new SMDS_HexahedronOfNodes(n1,n2,n3,n4,n5,n6,n7,n8); - volume=new SMDS_VolumeOfNodes(n1,n2,n3,n4,n5,n6,n7,n8); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n8->getVtkId()); + nodeIds.push_back(n7->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbHexas++; } - - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } + +// if (!registerElement(ID, volume)) { +// RemoveElement(volume, false); +// volume = NULL; +// } return volume; } @@ -769,6 +1181,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, const SMDS_MeshFace * f3, const SMDS_MeshFace * f4) { + //MESSAGE("AddVolumeWithID"); if (!hasConstructionFaces()) return NULL; return AddVolumeWithID(f1,f2,f3,f4, myElementIDFactory->GetFreeID()); @@ -777,7 +1190,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, /////////////////////////////////////////////////////////////////////////////// ///Create a new tetrahedron defined by its faces and add it to the mesh. ///@param ID The ID of the new volume -///@return The created tetrahedron +///@return The created tetrahedron /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, @@ -786,17 +1199,20 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, const SMDS_MeshFace * f4, int ID) { + MESSAGE("AddVolumeWithID" << ID); if (!hasConstructionFaces()) return NULL; if ( !f1 || !f2 || !f3 || !f4) return 0; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); SMDS_MeshVolume * volume = new SMDS_VolumeOfFaces(f1,f2,f3,f4); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbTetras++; if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; + registerElement(myElementIDFactory->GetFreeID(), volume); + //RemoveElement(volume, false); + //volume = NULL; } return volume; } @@ -812,7 +1228,8 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, const SMDS_MeshFace * f4, const SMDS_MeshFace * f5) { - if (!hasConstructionFaces()) + //MESSAGE("AddVolumeWithID"); + if (!hasConstructionFaces()) return NULL; return AddVolumeWithID(f1,f2,f3,f4,f5, myElementIDFactory->GetFreeID()); } @@ -820,7 +1237,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, /////////////////////////////////////////////////////////////////////////////// ///Create a new pyramid defined by its faces and add it to the mesh. ///@param ID The ID of the new volume -///@return The created pyramid +///@return The created pyramid /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, @@ -830,17 +1247,20 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, const SMDS_MeshFace * f5, int ID) { + MESSAGE("AddVolumeWithID" << ID); if (!hasConstructionFaces()) return NULL; if ( !f1 || !f2 || !f3 || !f4 || !f5) return 0; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); SMDS_MeshVolume * volume = new SMDS_VolumeOfFaces(f1,f2,f3,f4,f5); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPyramids++; if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; + registerElement(myElementIDFactory->GetFreeID(), volume); + //RemoveElement(volume, false); + //volume = NULL; } return volume; } @@ -857,7 +1277,8 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, const SMDS_MeshFace * f5, const SMDS_MeshFace * f6) { - if (!hasConstructionFaces()) + //MESSAGE("AddVolumeWithID" ); + if (!hasConstructionFaces()) return NULL; return AddVolumeWithID(f1,f2,f3,f4,f5,f6, myElementIDFactory->GetFreeID()); } @@ -865,7 +1286,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshFace * f1, /////////////////////////////////////////////////////////////////////////////// ///Create a new prism defined by its faces and add it to the mesh. ///@param ID The ID of the new volume -///@return The created prism +///@return The created prism /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, @@ -876,17 +1297,20 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, const SMDS_MeshFace * f6, int ID) { + MESSAGE("AddVolumeWithID" << ID); if (!hasConstructionFaces()) return NULL; if ( !f1 || !f2 || !f3 || !f4 || !f5 || !f6) return 0; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); SMDS_MeshVolume * volume = new SMDS_VolumeOfFaces(f1,f2,f3,f4,f5,f6); - myVolumes.Add(volume); + adjustmyCellsCapacity(ID); + myCells[ID] = volume; myInfo.myNbPrisms++; if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; + registerElement(myElementIDFactory->GetFreeID(), volume); + //RemoveElement(volume, false); + //volume = NULL; } return volume; } @@ -895,16 +1319,16 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshFace * f1, /// Add a polygon defined by its nodes IDs /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshFace* SMDS_Mesh::AddPolygonalFaceWithID (std::vector nodes_ids, - const int ID) +SMDS_MeshFace* SMDS_Mesh::AddPolygonalFaceWithID (const vector & nodes_ids, + const int ID) { int nbNodes = nodes_ids.size(); - std::vector nodes (nbNodes); + vector nodes (nbNodes); for (int i = 0; i < nbNodes; i++) { nodes[i] = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(nodes_ids[i]); if (!nodes[i]) return NULL; } - return SMDS_Mesh::AddPolygonalFaceWithID(nodes, ID); + return SMDS_Mesh::AddPolygonalFaceWithID(nodes, ID); } /////////////////////////////////////////////////////////////////////////////// @@ -912,31 +1336,56 @@ SMDS_MeshFace* SMDS_Mesh::AddPolygonalFaceWithID (std::vector nodes_ids, /////////////////////////////////////////////////////////////////////////////// SMDS_MeshFace* SMDS_Mesh::AddPolygonalFaceWithID - (std::vector nodes, - const int ID) + (const vector & nodes, + const int ID) { SMDS_MeshFace * face; - if ( myFaces.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbFaces() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if (hasConstructionEdges()) - { - MESSAGE("Error : Not implemented"); - return NULL; - } + { + MESSAGE("Error : Not implemented"); + return NULL; + } else - { - for ( int i = 0; i < nodes.size(); ++i ) - if ( !nodes[ i ] ) return 0; - face = new SMDS_PolygonalFaceOfNodes(nodes); - myFaces.Add(face); - myInfo.myNbPolygons++; - } + { +//#ifdef VTK_HAVE_POLYHEDRON + //MESSAGE("AddPolygonalFaceWithID vtk " << ID); + vector nodeIds; + nodeIds.clear(); + vector::const_iterator it = nodes.begin(); + for ( ; it != nodes.end(); ++it) + nodeIds.push_back((*it)->getVtkId()); + + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->initPoly(nodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; +//#else +// MESSAGE("AddPolygonalFaceWithID smds " << ID); +// for ( int i = 0; i < nodes.size(); ++i ) +// if ( !nodes[ i ] ) return 0; +// face = new SMDS_PolygonalFaceOfNodes(nodes); +//#endif + adjustmyCellsCapacity(ID); + myCells[ID] = face; + myInfo.myNbPolygons++; + } - if (!registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; - } - return face; +//#ifndef VTK_HAVE_POLYHEDRON +// if (!registerElement(ID, face)) +// { +// registerElement(myElementIDFactory->GetFreeID(), face); +// //RemoveElement(face, false); +// //face = NULL; +// } +//#endif + return face; } /////////////////////////////////////////////////////////////////////////////// @@ -944,25 +1393,25 @@ SMDS_MeshFace* SMDS_Mesh::AddPolygonalFaceWithID /// An ID is automatically affected to the created face. /////////////////////////////////////////////////////////////////////////////// -SMDS_MeshFace* SMDS_Mesh::AddPolygonalFace (std::vector nodes) +SMDS_MeshFace* SMDS_Mesh::AddPolygonalFace (const vector & nodes) { return SMDS_Mesh::AddPolygonalFaceWithID(nodes, myElementIDFactory->GetFreeID()); } /////////////////////////////////////////////////////////////////////////////// -/// Create a new polyhedral volume and add it to the mesh. +/// Create a new polyhedral volume and add it to the mesh. /// @param ID The ID of the new volume /// @return The created volume or NULL if an element with this ID already exists /// or if input nodes are not found. /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume * SMDS_Mesh::AddPolyhedralVolumeWithID - (std::vector nodes_ids, - std::vector quantities, - const int ID) + (const vector & nodes_ids, + const vector & quantities, + const int ID) { int nbNodes = nodes_ids.size(); - std::vector nodes (nbNodes); + vector nodes (nbNodes); for (int i = 0; i < nbNodes; i++) { nodes[i] = (SMDS_MeshNode *)myNodeIDFactory->MeshElement(nodes_ids[i]); if (!nodes[i]) return NULL; @@ -971,47 +1420,79 @@ SMDS_MeshVolume * SMDS_Mesh::AddPolyhedralVolumeWithID } /////////////////////////////////////////////////////////////////////////////// -/// Create a new polyhedral volume and add it to the mesh. +/// Create a new polyhedral volume and add it to the mesh. /// @param ID The ID of the new volume /// @return The created volume /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddPolyhedralVolumeWithID - (std::vector nodes, - std::vector quantities, - const int ID) + (const vector& nodes, + const vector & quantities, + const int ID) { - SMDS_MeshVolume* volume; - if ( myVolumes.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); - if (hasConstructionFaces()) { - MESSAGE("Error : Not implemented"); - return NULL; - } else if (hasConstructionEdges()) { - MESSAGE("Error : Not implemented"); + SMDS_MeshVolume* volume = 0; + if ( nodes.empty() || quantities.empty() ) return NULL; - } else { - for ( int i = 0; i < nodes.size(); ++i ) - if ( !nodes[ i ] ) return 0; - volume = new SMDS_PolyhedralVolumeOfNodes(nodes, quantities); - myVolumes.Add(volume); - myInfo.myNbPolyhedrons++; - } + if ( NbVolumes() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if (hasConstructionFaces()) + { + MESSAGE("Error : Not implemented"); + return NULL; + } + else if (hasConstructionEdges()) + { + MESSAGE("Error : Not implemented"); + return NULL; + } + else + { +//#ifdef VTK_HAVE_POLYHEDRON + //MESSAGE("AddPolyhedralVolumeWithID vtk " << ID); + vector nodeIds; + nodeIds.clear(); + vector::const_iterator it = nodes.begin(); + for (; it != nodes.end(); ++it) + nodeIds.push_back((*it)->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->initPoly(nodeIds, quantities, this); + if (!this->registerElement(ID, volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + volume = volvtk; +//#else +// MESSAGE("AddPolyhedralVolumeWithID smds " << ID); +// for ( int i = 0; i < nodes.size(); ++i ) +// if ( !nodes[ i ] ) return 0; +// volume = new SMDS_PolyhedralVolumeOfNodes(nodes, quantities); +//#endif + adjustmyCellsCapacity(ID); + myCells[ID] = volume; + myInfo.myNbPolyhedrons++; + } - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } +//#ifndef VTK_HAVE_POLYHEDRON +// if (!registerElement(ID, volume)) +// { +// registerElement(myElementIDFactory->GetFreeID(), volume); +// //RemoveElement(volume, false); +// //volume = NULL; +// } +//#endif return volume; } /////////////////////////////////////////////////////////////////////////////// -/// Create a new polyhedral volume and add it to the mesh. +/// Create a new polyhedral volume and add it to the mesh. /// @return The created volume /////////////////////////////////////////////////////////////////////////////// SMDS_MeshVolume* SMDS_Mesh::AddPolyhedralVolume - (std::vector nodes, - std::vector quantities) + (const vector & nodes, + const vector & quantities) { int ID = myElementIDFactory->GetFreeID(); SMDS_MeshVolume * v = SMDS_Mesh::AddPolyhedralVolumeWithID(nodes, quantities, ID); @@ -1019,41 +1500,186 @@ SMDS_MeshVolume* SMDS_Mesh::AddPolyhedralVolume return v; } +SMDS_MeshVolume* SMDS_Mesh::AddVolumeFromVtkIds(const std::vector& vtkNodeIds) +{ + int ID = myElementIDFactory->GetFreeID(); + SMDS_MeshVolume * v = SMDS_Mesh::AddVolumeFromVtkIdsWithID(vtkNodeIds, ID); + if (v == NULL) myElementIDFactory->ReleaseID(ID); + return v; +} + +SMDS_MeshVolume* SMDS_Mesh::AddVolumeFromVtkIdsWithID(const std::vector& vtkNodeIds, const int ID) +{ + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(vtkNodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; + vtkIdType aVtkType = volvtk->GetVtkType(); + switch (aVtkType) + { + case VTK_TETRA: + myInfo.myNbTetras++; + break; + case VTK_PYRAMID: + myInfo.myNbPyramids++; + break; + case VTK_WEDGE: + myInfo.myNbPrisms++; + break; + case VTK_HEXAHEDRON: + myInfo.myNbHexas++; + break; + case VTK_QUADRATIC_TETRA: + myInfo.myNbQuadTetras++; + break; + case VTK_QUADRATIC_PYRAMID: + myInfo.myNbQuadPyramids++; + break; + case VTK_QUADRATIC_WEDGE: + myInfo.myNbQuadPrisms++; + break; + case VTK_QUADRATIC_HEXAHEDRON: + myInfo.myNbQuadHexas++; + break; +//#ifdef VTK_HAVE_POLYHEDRON + case VTK_POLYHEDRON: + myInfo.myNbPolyhedrons++; + break; +//#endif + default: + myInfo.myNbPolyhedrons++; + break; + } + return volvtk; +} + +SMDS_MeshFace* SMDS_Mesh::AddFaceFromVtkIds(const std::vector& vtkNodeIds) +{ + int ID = myElementIDFactory->GetFreeID(); + SMDS_MeshFace * f = SMDS_Mesh::AddFaceFromVtkIdsWithID(vtkNodeIds, ID); + if (f == NULL) myElementIDFactory->ReleaseID(ID); + return f; +} + +SMDS_MeshFace* SMDS_Mesh::AddFaceFromVtkIdsWithID(const std::vector& vtkNodeIds, const int ID) +{ + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(vtkNodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = facevtk; + vtkIdType aVtkType = facevtk->GetVtkType(); + switch (aVtkType) + { + case VTK_TRIANGLE: + myInfo.myNbTriangles++; + break; + case VTK_QUAD: + myInfo.myNbQuadrangles++; + break; + case VTK_QUADRATIC_TRIANGLE: + myInfo.myNbQuadTriangles++; + break; + case VTK_QUADRATIC_QUAD: + myInfo.myNbQuadQuadrangles++; + break; + case VTK_BIQUADRATIC_QUAD: + myInfo.myNbBiQuadQuadrangles++; + break; + case VTK_POLYGON: + myInfo.myNbPolygons++; + break; + default: + myInfo.myNbPolygons++; + } + return facevtk; +} + /////////////////////////////////////////////////////////////////////////////// /// Registers element with the given ID, maintains inverse connections /////////////////////////////////////////////////////////////////////////////// -bool SMDS_Mesh::registerElement(int ID, SMDS_MeshElement * element) +bool SMDS_Mesh::registerElement(int ID, SMDS_MeshElement* element) { - if (myElementIDFactory->BindID(ID, element)) { - SMDS_ElemIteratorPtr it = element->nodesIterator(); - while (it->more()) { - SMDS_MeshNode *node = static_cast - (const_cast(it->next())); - node->AddInverseElement(element); - } - return true; + //MESSAGE("registerElement " << ID); + if ((ID >=0) && (ID < myCells.size()) && myCells[ID]) // --- already bound + { + MESSAGE(" ------------------ already bound "<< ID << " " << myCells[ID]->getVtkId()); + return false; } - return false; + + element->myID = ID; + element->myMeshId = myMeshId; + + SMDS_MeshCell *cell = dynamic_cast(element); + MYASSERT(cell); + int vtkId = cell->getVtkId(); + if (vtkId == -1) + vtkId = myElementIDFactory->SetInVtkGrid(element); + + if (vtkId >= myCellIdVtkToSmds.size()) // --- resize local vector + { +// MESSAGE(" --------------------- resize myCellIdVtkToSmds " << vtkId << " --> " << vtkId + SMDS_Mesh::chunkSize); + myCellIdVtkToSmds.resize(vtkId + SMDS_Mesh::chunkSize, -1); + } + myCellIdVtkToSmds[vtkId] = ID; + + myElementIDFactory->updateMinMax(ID); + return true; } /////////////////////////////////////////////////////////////////////////////// -/// Return the node whose ID is 'ID'. +/// Return the node whose SMDS ID is 'ID'. /////////////////////////////////////////////////////////////////////////////// const SMDS_MeshNode * SMDS_Mesh::FindNode(int ID) const { - return (const SMDS_MeshNode *)myNodeIDFactory->MeshElement(ID); + if (ID < 1 || ID >= myNodes.size()) + { +// MESSAGE("------------------------------------------------------------------------- "); +// MESSAGE("----------------------------------- bad ID " << ID << " " << myNodes.size()); +// MESSAGE("------------------------------------------------------------------------- "); + return 0; + } + return (const SMDS_MeshNode *)myNodes[ID]; +} + +/////////////////////////////////////////////////////////////////////////////// +/// Return the node whose VTK ID is 'vtkId'. +/////////////////////////////////////////////////////////////////////////////// +const SMDS_MeshNode * SMDS_Mesh::FindNodeVtk(int vtkId) const +{ + // TODO if needed use mesh->nodeIdFromVtkToSmds + if (vtkId < 0 || vtkId >= (myNodes.size() -1)) + { + MESSAGE("------------------------------------------------------------------------- "); + MESSAGE("---------------------------- bad VTK ID " << vtkId << " " << myNodes.size()); + MESSAGE("------------------------------------------------------------------------- "); + return 0; + } + return (const SMDS_MeshNode *)myNodes[vtkId+1]; } /////////////////////////////////////////////////////////////////////////////// -///Create a triangle and add it to the current mesh. This methode do not bind a +///Create a triangle and add it to the current mesh. This method do not bind an ///ID to the create triangle. /////////////////////////////////////////////////////////////////////////////// SMDS_MeshFace * SMDS_Mesh::createTriangle(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3) + const SMDS_MeshNode * node3, + int ID) { if ( !node1 || !node2 || !node3) return 0; - if ( myFaces.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbFaces() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionEdges()) { SMDS_MeshEdge *edge1, *edge2, *edge3; @@ -1061,15 +1687,35 @@ SMDS_MeshFace * SMDS_Mesh::createTriangle(const SMDS_MeshNode * node1, edge2=FindEdgeOrCreate(node2,node3); edge3=FindEdgeOrCreate(node3,node1); + //int ID = myElementIDFactory->GetFreeID(); // -PR- voir si on range cet element SMDS_MeshFace * face = new SMDS_FaceOfEdges(edge1,edge2,edge3); - myFaces.Add(face); + adjustmyCellsCapacity(ID); + myCells[ID] = face; myInfo.myNbTriangles++; return face; } else { - SMDS_MeshFace * face = new SMDS_FaceOfNodes(node1,node2,node3); - myFaces.Add(face); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(node1->getVtkId()); + nodeIds.push_back(node2->getVtkId()); + nodeIds.push_back(node3->getVtkId()); + + SMDS_MeshFace * face = 0; + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(nodeIds, this); // put in vtkUnstructuredGrid + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = face; + //MESSAGE("createTriangle " << ID << " " << face); myInfo.myNbTriangles++; return face; } @@ -1080,14 +1726,16 @@ SMDS_MeshFace * SMDS_Mesh::createTriangle(const SMDS_MeshNode * node1, ///a ID to the create triangle. /////////////////////////////////////////////////////////////////////////////// SMDS_MeshFace * SMDS_Mesh::createQuadrangle(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4) + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + int ID) { if ( !node1 || !node2 || !node3 || !node4 ) return 0; - if ( myFaces.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + if ( NbFaces() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); if(hasConstructionEdges()) { + //MESSAGE("createQuadrangle hasConstructionEdges "<< ID); SMDS_MeshEdge *edge1, *edge2, *edge3, *edge4; edge1=FindEdgeOrCreate(node1,node2); edge2=FindEdgeOrCreate(node2,node3); @@ -1095,14 +1743,33 @@ SMDS_MeshFace * SMDS_Mesh::createQuadrangle(const SMDS_MeshNode * node1, edge4=FindEdgeOrCreate(node4,node1); SMDS_MeshFace * face = new SMDS_FaceOfEdges(edge1,edge2,edge3,edge4); - myFaces.Add(face); + adjustmyCellsCapacity(ID); + myCells[ID] = face; myInfo.myNbQuadrangles++; return face; } else { - SMDS_MeshFace * face = new SMDS_FaceOfNodes(node1,node2,node3,node4); - myFaces.Add(face); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(node1->getVtkId()); + nodeIds.push_back(node2->getVtkId()); + nodeIds.push_back(node3->getVtkId()); + nodeIds.push_back(node4->getVtkId()); + + SMDS_MeshFace * face = 0; + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(nodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = face; myInfo.myNbQuadrangles++; return face; } @@ -1114,7 +1781,18 @@ SMDS_MeshFace * SMDS_Mesh::createQuadrangle(const SMDS_MeshNode * node1, void SMDS_Mesh::RemoveNode(const SMDS_MeshNode * node) { - RemoveElement(node, true); + MESSAGE("RemoveNode"); + RemoveElement(node, true); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Remove an edge and all the elements which own this edge +/////////////////////////////////////////////////////////////////////////////// + +void SMDS_Mesh::Remove0DElement(const SMDS_Mesh0DElement * elem0d) +{ + MESSAGE("Remove0DElement"); + RemoveElement(elem0d,true); } /////////////////////////////////////////////////////////////////////////////// @@ -1123,7 +1801,8 @@ void SMDS_Mesh::RemoveNode(const SMDS_MeshNode * node) void SMDS_Mesh::RemoveEdge(const SMDS_MeshEdge * edge) { - RemoveElement(edge,true); + MESSAGE("RemoveEdge"); + RemoveElement(edge,true); } /////////////////////////////////////////////////////////////////////////////// @@ -1132,7 +1811,8 @@ void SMDS_Mesh::RemoveEdge(const SMDS_MeshEdge * edge) void SMDS_Mesh::RemoveFace(const SMDS_MeshFace * face) { - RemoveElement(face, true); + MESSAGE("RemoveFace"); + RemoveElement(face, true); } /////////////////////////////////////////////////////////////////////////////// @@ -1141,7 +1821,8 @@ void SMDS_Mesh::RemoveFace(const SMDS_MeshFace * face) void SMDS_Mesh::RemoveVolume(const SMDS_MeshVolume * volume) { - RemoveElement(volume, true); + MESSAGE("RemoveVolume"); + RemoveElement(volume, true); } //======================================================================= @@ -1151,8 +1832,8 @@ void SMDS_Mesh::RemoveVolume(const SMDS_MeshVolume * volume) bool SMDS_Mesh::RemoveFromParent() { - if (myParent==NULL) return false; - else return (myParent->RemoveSubMesh(this)); + if (myParent==NULL) return false; + else return (myParent->RemoveSubMesh(this)); } //======================================================================= @@ -1162,89 +1843,54 @@ bool SMDS_Mesh::RemoveFromParent() bool SMDS_Mesh::RemoveSubMesh(const SMDS_Mesh * aMesh) { - bool found = false; + bool found = false; - list::iterator itmsh=myChildren.begin(); - for (; itmsh!=myChildren.end() && !found; itmsh++) - { - SMDS_Mesh * submesh = *itmsh; - if (submesh == aMesh) - { - found = true; - myChildren.erase(itmsh); - } - } + list::iterator itmsh=myChildren.begin(); + for (; itmsh!=myChildren.end() && !found; itmsh++) + { + SMDS_Mesh * submesh = *itmsh; + if (submesh == aMesh) + { + found = true; + myChildren.erase(itmsh); + } + } - return found; + return found; } //======================================================================= //function : ChangeElementNodes -//purpose : +//purpose : //======================================================================= bool SMDS_Mesh::ChangeElementNodes(const SMDS_MeshElement * element, const SMDS_MeshNode * nodes[], const int nbnodes) { + MESSAGE("SMDS_Mesh::ChangeElementNodes"); // keep current nodes of elem - set oldNodes; - SMDS_ElemIteratorPtr itn = element->nodesIterator(); - while(itn->more()) - oldNodes.insert( itn->next() ); - - if ( !element->IsPoly() ) - myInfo.remove( element ); // element may change type + set oldNodes( element->begin_nodes(), element->end_nodes() ); // change nodes bool Ok = false; - SMDS_MeshElement* elem = const_cast(element); - switch ( elem->GetType() ) - { - case SMDSAbs_Edge: { - if ( nbnodes == 2 ) { - if ( SMDS_MeshEdge* edge = dynamic_cast( elem )) - Ok = edge->ChangeNodes( nodes[0], nodes[1] ); - } - else if ( nbnodes == 3 ) { - if ( SMDS_QuadraticEdge* edge = dynamic_cast( elem )) - Ok = edge->ChangeNodes( nodes[0], nodes[1], nodes[2] ); + SMDS_MeshCell* cell = dynamic_cast((SMDS_MeshElement*) element); + if (cell) + { + Ok = cell->vtkOrder(nodes, nbnodes); + Ok = cell->ChangeNodes(nodes, nbnodes); } - break; - } - case SMDSAbs_Face: { - if ( SMDS_FaceOfNodes* face = dynamic_cast( elem )) - Ok = face->ChangeNodes( nodes, nbnodes ); - else - if ( SMDS_QuadraticFaceOfNodes* QF = dynamic_cast( elem )) - Ok = QF->ChangeNodes( nodes, nbnodes ); - else - if (SMDS_PolygonalFaceOfNodes* face = dynamic_cast(elem)) - Ok = face->ChangeNodes(nodes, nbnodes); - break; - } - case SMDSAbs_Volume: { - if ( SMDS_VolumeOfNodes* vol = dynamic_cast( elem )) - Ok = vol->ChangeNodes( nodes, nbnodes ); - else - if ( SMDS_QuadraticVolumeOfNodes* QV = dynamic_cast( elem )) - Ok = QV->ChangeNodes( nodes, nbnodes ); - break; - } - default: - MESSAGE ( "WRONG ELEM TYPE"); - } if ( Ok ) { // update InverseElements - set::iterator it; + set::iterator it; // AddInverseElement to new nodes for ( int i = 0; i < nbnodes; i++ ) { it = oldNodes.find( nodes[i] ); if ( it == oldNodes.end() ) // new node - const_cast( nodes[i] )->AddInverseElement( elem ); + const_cast( nodes[i] )->AddInverseElement( cell ); else // remove from oldNodes a node that remains in elem oldNodes.erase( it ); @@ -1252,15 +1898,11 @@ bool SMDS_Mesh::ChangeElementNodes(const SMDS_MeshElement * element, // RemoveInverseElement from the nodes removed from elem for ( it = oldNodes.begin(); it != oldNodes.end(); it++ ) { - SMDS_MeshNode * n = static_cast - (const_cast( *it )); - n->RemoveInverseElement( elem ); + SMDS_MeshNode * n = const_cast( *it ); + n->RemoveInverseElement( cell ); } } - if ( !element->IsPoly() ) - myInfo.add( element ); // element may change type - return Ok; } @@ -1277,7 +1919,7 @@ bool SMDS_Mesh::ChangePolyhedronNodes (const SMDS_MeshElement * elem, return false; } - const SMDS_PolyhedralVolumeOfNodes* vol = dynamic_cast(elem); + const SMDS_VtkVolume* vol = dynamic_cast(elem); if (!vol) { return false; } @@ -1290,7 +1932,9 @@ bool SMDS_Mesh::ChangePolyhedronNodes (const SMDS_MeshElement * elem, } // change nodes - bool Ok = const_cast(vol)->ChangeNodes(nodes, quantities); + // TODO remove this function + //bool Ok = const_cast(vol)->ChangeNodes(nodes, quantities); + bool Ok = false; if (!Ok) { return false; } @@ -1322,6 +1966,75 @@ bool SMDS_Mesh::ChangePolyhedronNodes (const SMDS_MeshElement * elem, } +//======================================================================= +//function : Find0DElement +//purpose : +//======================================================================= +const SMDS_Mesh0DElement* SMDS_Mesh::Find0DElement(int idnode) const +{ + const SMDS_MeshNode * node = FindNode(idnode); + if(node == NULL) return NULL; + return Find0DElement(node); +} + +const SMDS_Mesh0DElement* SMDS_Mesh::Find0DElement(const SMDS_MeshNode * node) +{ + if (!node) return 0; + const SMDS_Mesh0DElement* toReturn = NULL; + SMDS_ElemIteratorPtr it1 = node->GetInverseElementIterator(SMDSAbs_0DElement); + while (it1->more() && (toReturn == NULL)) { + const SMDS_MeshElement* e = it1->next(); + if (e->NbNodes() == 1) { + toReturn = static_cast(e); + } + } + return toReturn; +} + +//======================================================================= +//function : FindBall +//purpose : +//======================================================================= + +const SMDS_BallElement* SMDS_Mesh::FindBall(int idnode) const +{ + const SMDS_MeshNode * node = FindNode(idnode); + if(node == NULL) return NULL; + return FindBall(node); +} + +const SMDS_BallElement* SMDS_Mesh::FindBall(const SMDS_MeshNode * node) +{ + if (!node) return 0; + const SMDS_BallElement* toReturn = NULL; + SMDS_ElemIteratorPtr it1 = node->GetInverseElementIterator(SMDSAbs_Ball); + while (it1->more() && (toReturn == NULL)) { + const SMDS_MeshElement* e = it1->next(); + if (e->GetGeomType() == SMDSGeom_BALL) + toReturn = static_cast(e); + } + return toReturn; +} + +//======================================================================= +//function : Find0DElementOrCreate +//purpose : +//======================================================================= +//SMDS_Mesh0DElement* SMDS_Mesh::Find0DElementOrCreate(const SMDS_MeshNode * node) +//{ +// if (!node) return 0; +// SMDS_Mesh0DElement * toReturn = NULL; +// toReturn = const_cast(Find0DElement(node)); +// if (toReturn == NULL) { +// //if (my0DElements.Extent() % CHECKMEMORY_INTERVAL == 0) CheckMemory(); +// toReturn = new SMDS_Mesh0DElement(node); +// my0DElements.Add(toReturn); +// myInfo.myNb0DElements++; +// } +// return toReturn; +//} + + //======================================================================= //function : FindEdge //purpose : @@ -1364,15 +2077,30 @@ const SMDS_MeshEdge* SMDS_Mesh::FindEdge(const SMDS_MeshNode * node1, //======================================================================= SMDS_MeshEdge* SMDS_Mesh::FindEdgeOrCreate(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2) + const SMDS_MeshNode * node2) { if ( !node1 || !node2) return 0; SMDS_MeshEdge * toReturn=NULL; toReturn=const_cast(FindEdge(node1,node2)); if(toReturn==NULL) { - if ( myEdges.Extent() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); - toReturn=new SMDS_MeshEdge(node1,node2); - myEdges.Add(toReturn); + if ( NbEdges() % CHECKMEMORY_INTERVAL == 0 ) CheckMemory(); + int ID = myElementIDFactory->GetFreeID(); // -PR- voir si on range cet element + adjustmyCellsCapacity(ID); + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(node1->getVtkId()); + nodeIds.push_back(node2->getVtkId()); + + SMDS_VtkEdge *edgevtk = myEdgePool->getNew(); + edgevtk->init(nodeIds, this); + if (!this->registerElement(ID,edgevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(edgevtk->getVtkId(), VTK_EMPTY_CELL); + myEdgePool->destroy(edgevtk); + return 0; + } + toReturn = edgevtk; + myCells[ID] = toReturn; myInfo.myNbEdges++; } return toReturn; @@ -1427,7 +2155,7 @@ const SMDS_MeshEdge* SMDS_Mesh::FindEdge(const SMDS_MeshNode * node1, //======================================================================= const SMDS_MeshFace* SMDS_Mesh::FindFace(int idnode1, int idnode2, - int idnode3) const + int idnode3) const { const SMDS_MeshNode * node1=FindNode(idnode1); const SMDS_MeshNode * node2=FindNode(idnode2); @@ -1469,7 +2197,8 @@ SMDS_MeshFace* SMDS_Mesh::FindFaceOrCreate(const SMDS_MeshNode *node1, SMDS_MeshFace * toReturn=NULL; toReturn = const_cast(FindFace(node1,node2,node3)); if(toReturn==NULL) { - toReturn = createTriangle(node1,node2,node3); + int ID = myElementIDFactory->GetFreeID(); + toReturn = createTriangle(node1,node2,node3, ID); } return toReturn; } @@ -1527,7 +2256,8 @@ SMDS_MeshFace* SMDS_Mesh::FindFaceOrCreate(const SMDS_MeshNode *node1, SMDS_MeshFace * toReturn=NULL; toReturn=const_cast(FindFace(node1,node2,node3,node4)); if(toReturn==NULL) { - toReturn=createQuadrangle(node1,node2,node3,node4); + int ID = myElementIDFactory->GetFreeID(); + toReturn=createQuadrangle(node1,node2,node3,node4,ID); } return toReturn; } @@ -1651,7 +2381,16 @@ const SMDS_MeshFace* SMDS_Mesh::FindFace(const SMDS_MeshNode *node1, const SMDS_MeshElement* SMDS_Mesh::FindElement(int IDelem) const { - return myElementIDFactory->MeshElement(IDelem); + if ((IDelem <= 0) || IDelem >= myCells.size()) + { + MESSAGE("--------------------------------------------------------------------------------- "); + MESSAGE("----------------------------------- bad IDelem " << IDelem << " " << myCells.size()); + MESSAGE("--------------------------------------------------------------------------------- "); + // TODO raise an exception + //assert(0); + return 0; + } + return myCells[IDelem]; } //======================================================================= @@ -1659,33 +2398,55 @@ const SMDS_MeshElement* SMDS_Mesh::FindElement(int IDelem) const //purpose : find polygon //======================================================================= -const SMDS_MeshFace* SMDS_Mesh::FindFace (std::vector nodes_ids) const +const SMDS_MeshFace* SMDS_Mesh::FindFace (const vector& nodes_ids) const { int nbnodes = nodes_ids.size(); - std::vector poly_nodes (nbnodes); + vector poly_nodes (nbnodes); for (int inode = 0; inode < nbnodes; inode++) { const SMDS_MeshNode * node = FindNode(nodes_ids[inode]); if (node == NULL) return NULL; + poly_nodes[inode] = node; } return FindFace(poly_nodes); } -const SMDS_MeshFace* SMDS_Mesh::FindFace (std::vector nodes) +const SMDS_MeshFace* SMDS_Mesh::FindFace (const vector& nodes) { - if ( nodes.size() > 2 && nodes[0] ) { - SMDS_ElemIteratorPtr itF = nodes[0]->GetInverseElementIterator(SMDSAbs_Face); - while (itF->more()) { - const SMDS_MeshElement* f = itF->next(); - if ( f->NbNodes() == nodes.size() ) { - SMDS_ElemIteratorPtr it2 = f->nodesIterator(); - while(it2->more()) { - if ( find( nodes.begin(), nodes.end(), it2->next() ) == nodes.end() ) { - f = 0; - break; - } + return (const SMDS_MeshFace*) FindElement( nodes, SMDSAbs_Face ); +} + + +//================================================================================ +/*! + * \brief Return element based on all given nodes + * \param nodes - node of element + * \param type - type of element + * \param noMedium - true if medium nodes of quadratic element are not included in + * \retval const SMDS_MeshElement* - found element or NULL + */ +//================================================================================ + +const SMDS_MeshElement* SMDS_Mesh::FindElement (const vector& nodes, + const SMDSAbs_ElementType type, + const bool noMedium) +{ + if ( nodes.size() > 0 && nodes[0] ) + { + SMDS_ElemIteratorPtr itF = nodes[0]->GetInverseElementIterator(type); + while (itF->more()) + { + const SMDS_MeshElement* e = itF->next(); + int nbNodesToCheck = noMedium ? e->NbCornerNodes() : e->NbNodes(); + if ( nbNodesToCheck == nodes.size() ) + { + for ( int i = 1; e && i < nodes.size(); ++ i ) + { + int nodeIndex = e->GetNodeIndex( nodes[ i ]); + if ( nodeIndex < 0 || nodeIndex >= nbNodesToCheck ) + e = 0; } - if ( f ) - return static_cast (f); + if ( e ) + return static_cast (e); } } } @@ -1694,100 +2455,111 @@ const SMDS_MeshFace* SMDS_Mesh::FindFace (std::vector nod //======================================================================= //function : DumpNodes -//purpose : +//purpose : //======================================================================= void SMDS_Mesh::DumpNodes() const { - MESSAGE("dump nodes of mesh : "); - SMDS_NodeIteratorPtr itnode=nodesIterator(); - while(itnode->more()) MESSAGE(itnode->next()); + MESSAGE("dump nodes of mesh : "); + SMDS_NodeIteratorPtr itnode=nodesIterator(); + while(itnode->more()) ; //MESSAGE(itnode->next()); +} + +//======================================================================= +//function : Dump0DElements +//purpose : +//======================================================================= +void SMDS_Mesh::Dump0DElements() const +{ + MESSAGE("dump 0D elements of mesh : "); + SMDS_ElemIteratorPtr it0d = elementsIterator(SMDSAbs_0DElement); + while(it0d->more()) ; //MESSAGE(it0d->next()); } //======================================================================= //function : DumpEdges -//purpose : +//purpose : //======================================================================= void SMDS_Mesh::DumpEdges() const { - MESSAGE("dump edges of mesh : "); - SMDS_EdgeIteratorPtr itedge=edgesIterator(); - while(itedge->more()) MESSAGE(itedge->next()); + MESSAGE("dump edges of mesh : "); + SMDS_EdgeIteratorPtr itedge=edgesIterator(); + while(itedge->more()) ; //MESSAGE(itedge->next()); } //======================================================================= //function : DumpFaces -//purpose : +//purpose : //======================================================================= void SMDS_Mesh::DumpFaces() const { - MESSAGE("dump faces of mesh : "); - SMDS_FaceIteratorPtr itface=facesIterator(); - while(itface->more()) MESSAGE(itface->next()); + MESSAGE("dump faces of mesh : "); + SMDS_FaceIteratorPtr itface=facesIterator(); + while(itface->more()) ; //MESSAGE(itface->next()); } //======================================================================= //function : DumpVolumes -//purpose : +//purpose : //======================================================================= void SMDS_Mesh::DumpVolumes() const { - MESSAGE("dump volumes of mesh : "); - SMDS_VolumeIteratorPtr itvol=volumesIterator(); - while(itvol->more()) MESSAGE(itvol->next()); + MESSAGE("dump volumes of mesh : "); + SMDS_VolumeIteratorPtr itvol=volumesIterator(); + while(itvol->more()) ; //MESSAGE(itvol->next()); } //======================================================================= //function : DebugStats -//purpose : +//purpose : //======================================================================= void SMDS_Mesh::DebugStats() const { - MESSAGE("Debug stats of mesh : "); + MESSAGE("Debug stats of mesh : "); - MESSAGE("===== NODES ====="<more()) - { - const SMDS_MeshNode *node = itnode->next(); + SMDS_NodeIteratorPtr itnode=nodesIterator(); + int sizeofnodes = 0; + int sizeoffaces = 0; - sizeofnodes += sizeof(*node); - - SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(); - while(it->more()) - { - const SMDS_MeshElement *me = it->next(); - sizeofnodes += sizeof(me); - } + while(itnode->more()) + { + const SMDS_MeshNode *node = itnode->next(); + + sizeofnodes += sizeof(*node); - } + SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(); + while(it->more()) + { + const SMDS_MeshElement *me = it->next(); + sizeofnodes += sizeof(me); + } + } - SMDS_FaceIteratorPtr itface=facesIterator(); - while(itface->more()) - { - const SMDS_MeshElement *face = itface->next(); - sizeoffaces += sizeof(*face); + SMDS_FaceIteratorPtr itface=facesIterator(); + while(itface->more()) + { + const SMDS_MeshElement *face = itface->next(); + sizeoffaces += sizeof(*face); + } - } - MESSAGE("total size of node elements = " << sizeofnodes);; - MESSAGE("total size of face elements = " << sizeoffaces);; + MESSAGE("total size of node elements = " << sizeofnodes);; + MESSAGE("total size of face elements = " << sizeoffaces);; - //#endif + //#endif } /////////////////////////////////////////////////////////////////////////////// @@ -1795,7 +2567,26 @@ void SMDS_Mesh::DebugStats() const /////////////////////////////////////////////////////////////////////////////// int SMDS_Mesh::NbNodes() const { - return myNodes.Size(); + //MESSAGE(myGrid->GetNumberOfPoints()); + //MESSAGE(myInfo.NbNodes()); + //MESSAGE(myNodeMax); + return myInfo.NbNodes(); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Return the number of 0D elements +/////////////////////////////////////////////////////////////////////////////// +int SMDS_Mesh::Nb0DElements() const +{ + return myInfo.Nb0DElements(); +} + +/////////////////////////////////////////////////////////////////////////////// +/// Return the number of 0D elements +/////////////////////////////////////////////////////////////////////////////// +int SMDS_Mesh::NbBalls() const +{ + return myInfo.NbBalls(); } /////////////////////////////////////////////////////////////////////////////// @@ -1803,7 +2594,7 @@ int SMDS_Mesh::NbNodes() const /////////////////////////////////////////////////////////////////////////////// int SMDS_Mesh::NbEdges() const { - return myEdges.Size(); + return myInfo.NbEdges(); } /////////////////////////////////////////////////////////////////////////////// @@ -1811,7 +2602,7 @@ int SMDS_Mesh::NbEdges() const /////////////////////////////////////////////////////////////////////////////// int SMDS_Mesh::NbFaces() const { - return myFaces.Size(); + return myInfo.NbFaces(); } /////////////////////////////////////////////////////////////////////////////// @@ -1819,17 +2610,16 @@ int SMDS_Mesh::NbFaces() const /////////////////////////////////////////////////////////////////////////////// int SMDS_Mesh::NbVolumes() const { - return myVolumes.Size(); + return myInfo.NbVolumes(); } /////////////////////////////////////////////////////////////////////////////// /// Return the number of child mesh of this mesh. -/// Note that the tree structure of SMDS_Mesh seems to be unused in this version -/// (2003-09-08) of SMESH +/// Note that the tree structure of SMDS_Mesh is unused in SMESH /////////////////////////////////////////////////////////////////////////////// int SMDS_Mesh::NbSubMesh() const { - return myChildren.size(); + return myChildren.size(); } /////////////////////////////////////////////////////////////////////////////// @@ -1854,36 +2644,18 @@ SMDS_Mesh::~SMDS_Mesh() { SMDS_ElemIteratorPtr eIt = elementsIterator(); while ( eIt->more() ) - myElementIDFactory->ReleaseID(eIt->next()->GetID()); + { + const SMDS_MeshElement *elem = eIt->next(); + myElementIDFactory->ReleaseID(elem->GetID(), elem->getVtkId()); + } SMDS_NodeIteratorPtr itn = nodesIterator(); while (itn->more()) - myNodeIDFactory->ReleaseID(itn->next()->GetID()); - } - SetOfNodes::Iterator itn(myNodes); - for (; itn.More(); itn.Next()) - delete itn.Value(); - - SetOfEdges::Iterator ite(myEdges); - for (; ite.More(); ite.Next()) - { - SMDS_MeshElement* elem = ite.Value(); - delete elem; - } - - SetOfFaces::Iterator itf(myFaces); - for (; itf.More(); itf.Next()) - { - SMDS_MeshElement* elem = itf.Value(); - delete elem; - } - - SetOfVolumes::Iterator itv(myVolumes); - for (; itv.More(); itv.Next()) - { - SMDS_MeshElement* elem = itv.Value(); - delete elem; + { + const SMDS_MeshNode *node = itn->next(); + ((SMDS_MeshNode*)node)->SetPosition(SMDS_SpacePosition::originSpacePosition()); + myNodeIDFactory->ReleaseID(node->GetID(), node->getVtkId()); + } } - } //================================================================================ @@ -1894,43 +2666,88 @@ SMDS_Mesh::~SMDS_Mesh() void SMDS_Mesh::Clear() { - if (myParent!=NULL) { + MESSAGE("SMDS_Mesh::Clear"); + if (myParent!=NULL) + { SMDS_ElemIteratorPtr eIt = elementsIterator(); while ( eIt->more() ) - myElementIDFactory->ReleaseID(eIt->next()->GetID()); + { + const SMDS_MeshElement *elem = eIt->next(); + myElementIDFactory->ReleaseID(elem->GetID(), elem->getVtkId()); + } SMDS_NodeIteratorPtr itn = nodesIterator(); while (itn->more()) - myNodeIDFactory->ReleaseID(itn->next()->GetID()); - } - else { + { + const SMDS_MeshNode *node = itn->next(); + myNodeIDFactory->ReleaseID(node->GetID(), node->getVtkId()); + } + } + else + { myNodeIDFactory->Clear(); myElementIDFactory->Clear(); - } - SMDS_VolumeIteratorPtr itv = volumesIterator(); + } + + SMDS_ElemIteratorPtr itv = elementsIterator(); while (itv->more()) - delete itv->next(); - myVolumes.Clear(); - - SMDS_FaceIteratorPtr itf = facesIterator(); - while (itf->more()) - delete itf->next(); - myFaces.Clear(); - - SMDS_EdgeIteratorPtr ite = edgesIterator(); - while (ite->more()) - delete ite->next(); - myEdges.Clear(); + { + SMDS_MeshElement* elem = (SMDS_MeshElement*)(itv->next()); + SMDSAbs_ElementType aType = elem->GetType(); + switch (aType) + { + case SMDSAbs_0DElement: + delete elem; + break; + case SMDSAbs_Edge: + myEdgePool->destroy(static_cast(elem)); + break; + case SMDSAbs_Face: + myFacePool->destroy(static_cast(elem)); + break; + case SMDSAbs_Volume: + myVolumePool->destroy(static_cast(elem)); + break; + case SMDSAbs_Ball: + myBallPool->destroy(static_cast(elem)); + break; + default: + break; + } + } + myCells.clear(); + myCellIdVtkToSmds.clear(); + //myCellIdSmdsToVtk.clear(); SMDS_NodeIteratorPtr itn = nodesIterator(); while (itn->more()) - delete itn->next(); - myNodes.Clear(); + { + SMDS_MeshNode *node = (SMDS_MeshNode*)(itn->next()); + node->SetPosition(SMDS_SpacePosition::originSpacePosition()); + myNodePool->destroy(node); + } + myNodes.clear(); list::iterator itc=myChildren.begin(); while(itc!=myChildren.end()) (*itc)->Clear(); + myModified = false; + xmin = 0; xmax = 0; + ymin = 0; ymax = 0; + zmin = 0; zmax = 0; + myInfo.Clear(); + + myGrid->Initialize(); + myGrid->Allocate(); + vtkPoints* points = vtkPoints::New(); + // rnv: to fix bug "21125: EDF 1233 SMESH: Degrardation of precision in a test case for quadratic conversion" + // using double type for storing coordinates of nodes instead float. + points->SetDataType(VTK_DOUBLE); + points->SetNumberOfPoints(0 /*SMDS_Mesh::chunkSize*/); + myGrid->SetPoints( points ); + points->Delete(); + myGrid->BuildLinks(); } /////////////////////////////////////////////////////////////////////////////// @@ -1940,7 +2757,7 @@ void SMDS_Mesh::Clear() /////////////////////////////////////////////////////////////////////////////// bool SMDS_Mesh::hasConstructionEdges() { - return myHasConstructionEdges; + return myHasConstructionEdges; } /////////////////////////////////////////////////////////////////////////////// @@ -1952,7 +2769,7 @@ bool SMDS_Mesh::hasConstructionEdges() /////////////////////////////////////////////////////////////////////////////// bool SMDS_Mesh::hasConstructionFaces() { - return myHasConstructionFaces; + return myHasConstructionFaces; } /////////////////////////////////////////////////////////////////////////////// @@ -1961,7 +2778,7 @@ bool SMDS_Mesh::hasConstructionFaces() /////////////////////////////////////////////////////////////////////////////// bool SMDS_Mesh::hasInverseElements() { - return myHasInverseElements; + return myHasInverseElements; } /////////////////////////////////////////////////////////////////////////////// @@ -1970,7 +2787,7 @@ bool SMDS_Mesh::hasInverseElements() /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::setConstructionEdges(bool b) { - myHasConstructionEdges=b; + myHasConstructionEdges=b; } /////////////////////////////////////////////////////////////////////////////// @@ -1979,7 +2796,7 @@ void SMDS_Mesh::setConstructionEdges(bool b) /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::setConstructionFaces(bool b) { - myHasConstructionFaces=b; + myHasConstructionFaces=b; } /////////////////////////////////////////////////////////////////////////////// @@ -1988,283 +2805,339 @@ void SMDS_Mesh::setConstructionFaces(bool b) /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::setInverseElements(bool b) { - if(!b) MESSAGE("Error : inverseElement=false not implemented"); - myHasInverseElements=b; + if(!b) MESSAGE("Error : inverseElement=false not implemented"); + myHasInverseElements=b; +} + +namespace { + + //================================================================================ + /*! + * \brief Iterator on elements in id increasing order + */ + //================================================================================ + + template + class IdSortedIterator : public SMDS_Iterator + { + SMDS_MeshElementIDFactory& myIDFact; + int myID, myMaxID, myNbFound, myTotalNb; + SMDSAbs_ElementType myType; + ELEM myElem; + + public: + IdSortedIterator(const SMDS_MeshElementIDFactory& fact, + const SMDSAbs_ElementType type, // SMDSAbs_All NOT allowed!!! + const int totalNb) + :myIDFact( fact ), + myID(1), myMaxID( myIDFact.GetMaxID() ),myNbFound(0), myTotalNb( totalNb ), + myType( type ), + myElem(0) + { + next(); + } + bool more() + { + return myElem; + } + ELEM next() + { + ELEM current = myElem; + + for ( myElem = 0; !myElem && myNbFound < myTotalNb && myID <= myMaxID; ++myID ) + if ((myElem = (ELEM) myIDFact.MeshElement( myID )) + && myElem->GetType() != myType ) + myElem = 0; + + myNbFound += bool(myElem); + + return current; + } + }; + + //================================================================================ + /*! + * \brief Iterator on vector of elements, possibly being resized while iteration + */ + //================================================================================ + + template > + class ElemVecIterator: public SMDS_Iterator + { + const std::vector& _vector; + size_t _index; + bool _more; + VALUE_FILTER _filter; + public: + ElemVecIterator(const std::vector& vec, + const VALUE_FILTER& filter=VALUE_FILTER() ) + :_vector( vec ), _index(0), _more( !vec.empty() ), _filter( filter ) + { + if ( _more && !_filter( _vector[ _index ])) + next(); + } + virtual bool more() + { + return _more; + } + virtual RETURN_VALUE next() + { + if ( !_more ) return NULL; + VECTOR_VALUE current = _vector[ _index ]; + _more = 0; + while ( !_more && ++_index < _vector.size() ) + _more = _filter( _vector[ _index ]); + return (RETURN_VALUE) current; + } + }; } /////////////////////////////////////////////////////////////////////////////// /// Return an iterator on nodes of the current mesh factory /////////////////////////////////////////////////////////////////////////////// -class SMDS_Mesh_MyNodeIterator:public SMDS_NodeIterator -{ - SMDS_ElemIteratorPtr myIterator; - public: - SMDS_Mesh_MyNodeIterator(const SMDS_ElemIteratorPtr& it):myIterator(it) - {} - bool more() - { - return myIterator->more(); - } +SMDS_NodeIteratorPtr SMDS_Mesh::nodesIterator(bool idInceasingOrder) const +{ + // naturally always sorted by ID + typedef ElemVecIterator TIterator; + return SMDS_NodeIteratorPtr( new TIterator(myNodes)); +} - const SMDS_MeshNode* next() - { - return static_cast(myIterator->next()); - } -}; +SMDS_ElemIteratorPtr SMDS_Mesh::elementGeomIterator(SMDSAbs_GeometryType type) const +{ + // naturally always sorted by ID + typedef ElemVecIterator + < const SMDS_MeshElement*, SMDS_MeshCell*, SMDS_MeshElement::GeomFilter > TIterator; + return SMDS_ElemIteratorPtr + (new TIterator(myCells, SMDS_MeshElement::GeomFilter( type ))); +} -SMDS_NodeIteratorPtr SMDS_Mesh::nodesIterator() const +SMDS_ElemIteratorPtr SMDS_Mesh::elementEntityIterator(SMDSAbs_EntityType type) const { - return SMDS_NodeIteratorPtr - (new SMDS_Mesh_MyNodeIterator(myNodeIDFactory->elementsIterator())); + // naturally always sorted by ID + typedef ElemVecIterator + < const SMDS_MeshElement*, SMDS_MeshCell*, SMDS_MeshElement::EntityFilter > TIterator; + return SMDS_ElemIteratorPtr + (new TIterator(myCells, SMDS_MeshElement::EntityFilter( type ))); } /////////////////////////////////////////////////////////////////////////////// /// Return an iterator on elements of the current mesh factory /////////////////////////////////////////////////////////////////////////////// -SMDS_ElemIteratorPtr SMDS_Mesh::elementsIterator() const +SMDS_ElemIteratorPtr SMDS_Mesh::elementsIterator(SMDSAbs_ElementType type) const { - return myElementIDFactory->elementsIterator(); + // naturally always sorted by ID + switch ( type ) { + + case SMDSAbs_All: + return SMDS_ElemIteratorPtr (new ElemVecIterator(myCells)); + + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr + ( new ElemVecIterator( myNodes )); + + default: + typedef ElemVecIterator + < const SMDS_MeshElement*, SMDS_MeshCell*, SMDS_MeshElement::TypeFilter > TIterator; + return SMDS_ElemIteratorPtr (new TIterator(myCells, SMDS_MeshElement::TypeFilter( type ))); + } + return SMDS_ElemIteratorPtr(); } /////////////////////////////////////////////////////////////////////////////// ///Return an iterator on edges of the current mesh. /////////////////////////////////////////////////////////////////////////////// -class SMDS_Mesh_MyEdgeIterator:public SMDS_EdgeIterator -{ - typedef SMDS_Mesh::SetOfEdges SetOfEdges; - SetOfEdges::Iterator myIterator; - public: - SMDS_Mesh_MyEdgeIterator(const SetOfEdges& s):myIterator(s) - {} - bool more() - { - while(myIterator.More()) - { - if(myIterator.Value()->GetID()!=-1) - return true; - myIterator.Next(); - } - return false; - } - - const SMDS_MeshEdge* next() - { - const SMDS_MeshEdge* current = myIterator.Value(); - myIterator.Next(); - return current; - } -}; - -SMDS_EdgeIteratorPtr SMDS_Mesh::edgesIterator() const +SMDS_EdgeIteratorPtr SMDS_Mesh::edgesIterator(bool idInceasingOrder) const { - return SMDS_EdgeIteratorPtr(new SMDS_Mesh_MyEdgeIterator(myEdges)); + // naturally always sorted by ID + typedef ElemVecIterator + < const SMDS_MeshEdge*, SMDS_MeshCell*, SMDS_MeshElement::TypeFilter > TIterator; + return SMDS_EdgeIteratorPtr + (new TIterator(myCells, SMDS_MeshElement::TypeFilter( SMDSAbs_Edge ))); } /////////////////////////////////////////////////////////////////////////////// ///Return an iterator on faces of the current mesh. /////////////////////////////////////////////////////////////////////////////// -class SMDS_Mesh_MyFaceIterator:public SMDS_FaceIterator -{ - typedef SMDS_Mesh::SetOfFaces SetOfFaces; - SetOfFaces::Iterator myIterator; - public: - SMDS_Mesh_MyFaceIterator(const SetOfFaces& s):myIterator(s) - {} - - bool more() - { - while(myIterator.More()) - { - if(myIterator.Value()->GetID()!=-1) - return true; - myIterator.Next(); - } - return false; - } - const SMDS_MeshFace* next() - { - const SMDS_MeshFace* current = myIterator.Value(); - myIterator.Next(); - return current; - } -}; - -SMDS_FaceIteratorPtr SMDS_Mesh::facesIterator() const +SMDS_FaceIteratorPtr SMDS_Mesh::facesIterator(bool idInceasingOrder) const { - return SMDS_FaceIteratorPtr(new SMDS_Mesh_MyFaceIterator(myFaces)); + // naturally always sorted by ID + typedef ElemVecIterator + < const SMDS_MeshFace*, SMDS_MeshCell*, SMDS_MeshElement::TypeFilter > TIterator; + return SMDS_FaceIteratorPtr + (new TIterator(myCells, SMDS_MeshElement::TypeFilter( SMDSAbs_Face ))); } /////////////////////////////////////////////////////////////////////////////// ///Return an iterator on volumes of the current mesh. /////////////////////////////////////////////////////////////////////////////// -class SMDS_Mesh_MyVolumeIterator:public SMDS_VolumeIterator -{ - typedef SMDS_Mesh::SetOfVolumes SetOfVolumes; - SetOfVolumes::Iterator myIterator; - public: - SMDS_Mesh_MyVolumeIterator(const SetOfVolumes& s):myIterator(s) - {} - - bool more() - { - return myIterator.More() != Standard_False; - } - - const SMDS_MeshVolume* next() - { - const SMDS_MeshVolume* current = myIterator.Value(); - myIterator.Next(); - return current; - } -}; -SMDS_VolumeIteratorPtr SMDS_Mesh::volumesIterator() const +SMDS_VolumeIteratorPtr SMDS_Mesh::volumesIterator(bool idInceasingOrder) const { - return SMDS_VolumeIteratorPtr(new SMDS_Mesh_MyVolumeIterator(myVolumes)); + // naturally always sorted by ID + typedef ElemVecIterator + < const SMDS_MeshVolume*, SMDS_MeshCell*, SMDS_MeshElement::TypeFilter > TIterator; + return SMDS_VolumeIteratorPtr + (new TIterator(myCells, SMDS_MeshElement::TypeFilter( SMDSAbs_Volume ))); } /////////////////////////////////////////////////////////////////////////////// /// Do intersection of sets (more than 2) /////////////////////////////////////////////////////////////////////////////// static set * intersectionOfSets( - set vs[], int numberOfSets) + set vs[], int numberOfSets) { - set* rsetA=new set(vs[0]); - set* rsetB; + set* rsetA=new set(vs[0]); + set* rsetB; - for(int i=0; i(); - set_intersection( - rsetA->begin(), rsetA->end(), - vs[i+1].begin(), vs[i+1].end(), - inserter(*rsetB, rsetB->begin())); - delete rsetA; - rsetA=rsetB; - } - return rsetA; + for(int i=0; i(); + set_intersection( + rsetA->begin(), rsetA->end(), + vs[i+1].begin(), vs[i+1].end(), + inserter(*rsetB, rsetB->begin())); + delete rsetA; + rsetA=rsetB; + } + return rsetA; } /////////////////////////////////////////////////////////////////////////////// -/// Return the list of finit elements owning the given element +/// Return the list of finite elements owning the given element: elements +/// containing all the nodes of the given element, for instance faces and +/// volumes containing a given edge. /////////////////////////////////////////////////////////////////////////////// static set * getFinitElements(const SMDS_MeshElement * element) { - int numberOfSets=element->NbNodes(); - set *initSet = new set[numberOfSets]; + int numberOfSets=element->NbNodes(); + set *initSet = new set[numberOfSets]; - SMDS_ElemIteratorPtr itNodes=element->nodesIterator(); + SMDS_ElemIteratorPtr itNodes=element->nodesIterator(); - int i=0; - while(itNodes->more()) - { - const SMDS_MeshNode * n=static_cast(itNodes->next()); - SMDS_ElemIteratorPtr itFe = n->GetInverseElementIterator(); + int i=0; + while(itNodes->more()) + { + const SMDS_MeshElement* node = itNodes->next(); + MYASSERT(node); + const SMDS_MeshNode * n=static_cast(node); + SMDS_ElemIteratorPtr itFe = n->GetInverseElementIterator(); + + //initSet[i]=set(); + while(itFe->more()) + { + const SMDS_MeshElement* elem = itFe->next(); + MYASSERT(elem); + initSet[i].insert(elem); - //initSet[i]=set(); - while(itFe->more()) - initSet[i].insert(itFe->next()); + } - i++; - } - set *retSet=intersectionOfSets(initSet, numberOfSets); + i++; + } + set *retSet=intersectionOfSets(initSet, numberOfSets); +// MESSAGE("nb elems " << i << " intersection " << retSet->size()); delete [] initSet; - return retSet; + return retSet; } /////////////////////////////////////////////////////////////////////////////// /// Return the list of nodes used only by the given elements /////////////////////////////////////////////////////////////////////////////// static set * getExclusiveNodes( - set& elements) -{ - set * toReturn=new set(); - set::iterator itElements=elements.begin(); - - while(itElements!=elements.end()) - { - SMDS_ElemIteratorPtr itNodes = (*itElements)->nodesIterator(); - itElements++; - - while(itNodes->more()) - { - const SMDS_MeshNode * n=static_cast(itNodes->next()); - SMDS_ElemIteratorPtr itFe = n->GetInverseElementIterator(); - set s; - while(itFe->more()) + set& elements) +{ + set * toReturn=new set(); + set::iterator itElements=elements.begin(); + + while(itElements!=elements.end()) + { + SMDS_ElemIteratorPtr itNodes = (*itElements)->nodesIterator(); + itElements++; + + while(itNodes->more()) + { + const SMDS_MeshNode * n=static_cast(itNodes->next()); + SMDS_ElemIteratorPtr itFe = n->GetInverseElementIterator(); + set s; + while(itFe->more()) s.insert(itFe->next()); - if(s==elements) toReturn->insert(n); - } - } - return toReturn; + if(s==elements) toReturn->insert(n); + } + } + return toReturn; } /////////////////////////////////////////////////////////////////////////////// -///Find the children of an element that are made of given nodes +///Find the children of an element that are made of given nodes ///@param setOfChildren The set in which matching children will be inserted ///@param element The element were to search matching children ///@param nodes The nodes that the children must have to be selected /////////////////////////////////////////////////////////////////////////////// -void SMDS_Mesh::addChildrenWithNodes(set& setOfChildren, - const SMDS_MeshElement * element, set& nodes) -{ - - switch(element->GetType()) - { - case SMDSAbs_Node: - MESSAGE("Internal Error: This should not append"); - break; - case SMDSAbs_Edge: - { - SMDS_ElemIteratorPtr itn=element->nodesIterator(); - while(itn->more()) - { - const SMDS_MeshElement * e=itn->next(); - if(nodes.find(e)!=nodes.end()) +void SMDS_Mesh::addChildrenWithNodes(set& setOfChildren, + const SMDS_MeshElement * element, + set& nodes) +{ + switch(element->GetType()) + { + case SMDSAbs_Node: + MESSAGE("Internal Error: This should not happen"); + break; + case SMDSAbs_0DElement: + { + } + break; + case SMDSAbs_Edge: + { + SMDS_ElemIteratorPtr itn=element->nodesIterator(); + while(itn->more()) + { + const SMDS_MeshElement * e=itn->next(); + if(nodes.find(e)!=nodes.end()) { setOfChildren.insert(element); break; } - } - } break; - case SMDSAbs_Face: - { - SMDS_ElemIteratorPtr itn=element->nodesIterator(); - while(itn->more()) - { - const SMDS_MeshElement * e=itn->next(); - if(nodes.find(e)!=nodes.end()) + } + } break; + case SMDSAbs_Face: + { + SMDS_ElemIteratorPtr itn=element->nodesIterator(); + while(itn->more()) + { + const SMDS_MeshElement * e=itn->next(); + if(nodes.find(e)!=nodes.end()) { setOfChildren.insert(element); break; } - } - if(hasConstructionEdges()) - { - SMDS_ElemIteratorPtr ite=element->edgesIterator(); - while(ite->more()) - addChildrenWithNodes(setOfChildren, ite->next(), nodes); - } - } break; - case SMDSAbs_Volume: - { - if(hasConstructionFaces()) - { - SMDS_ElemIteratorPtr ite=element->facesIterator(); - while(ite->more()) - addChildrenWithNodes(setOfChildren, ite->next(), nodes); - } - else if(hasConstructionEdges()) - { - SMDS_ElemIteratorPtr ite=element->edgesIterator(); - while(ite->more()) - addChildrenWithNodes(setOfChildren, ite->next(), nodes); - } - } - } + } + if(hasConstructionEdges()) + { + SMDS_ElemIteratorPtr ite=element->edgesIterator(); + while(ite->more()) + addChildrenWithNodes(setOfChildren, ite->next(), nodes); + } + } break; + case SMDSAbs_Volume: + { + if(hasConstructionFaces()) + { + SMDS_ElemIteratorPtr ite=element->facesIterator(); + while(ite->more()) + addChildrenWithNodes(setOfChildren, ite->next(), nodes); + } + else if(hasConstructionEdges()) + { + SMDS_ElemIteratorPtr ite=element->edgesIterator(); + while(ite->more()) + addChildrenWithNodes(setOfChildren, ite->next(), nodes); + } + } + } } /////////////////////////////////////////////////////////////////////////////// @@ -2272,17 +3145,17 @@ void SMDS_Mesh::addChildrenWithNodes(set& setOfChildren ///@param removenodes if true remaining nodes will be removed /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::RemoveElement(const SMDS_MeshElement * elem, - const bool removenodes) + const bool removenodes) { list removedElems; list removedNodes; RemoveElement( elem, removedElems, removedNodes, removenodes ); } - + /////////////////////////////////////////////////////////////////////////////// ///@param elem The element to delete -///@param removedElems contains all removed elements -///@param removedNodes contains all removed nodes +///@param removedElems to be filled with all removed elements +///@param removedNodes to be filled with all removed nodes ///@param removenodes if true remaining nodes will be removed /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::RemoveElement(const SMDS_MeshElement * elem, @@ -2290,125 +3163,195 @@ void SMDS_Mesh::RemoveElement(const SMDS_MeshElement * elem, list& removedNodes, bool removenodes) { + //MESSAGE("SMDS_Mesh::RemoveElement " << elem->getVtkId() << " " << removenodes); // get finite elements built on elem set * s1; - if (!hasConstructionEdges() && elem->GetType() == SMDSAbs_Edge || - !hasConstructionFaces() && elem->GetType() == SMDSAbs_Face || - elem->GetType() == SMDSAbs_Volume) - { - s1 = new set(); - s1->insert(elem); - } + if ( (elem->GetType() == SMDSAbs_0DElement) + || ((elem->GetType() == SMDSAbs_Edge) && !hasConstructionEdges()) + || ((elem->GetType() == SMDSAbs_Face) && !hasConstructionFaces()) + || (elem->GetType() == SMDSAbs_Volume) ) + { + s1 = new set (); + s1->insert(elem); + } else s1 = getFinitElements(elem); // get exclusive nodes (which would become free afterwards) set * s2; if (elem->GetType() == SMDSAbs_Node) // a node is removed - { - // do not remove nodes except elem - s2 = new set(); - s2->insert(elem); - removenodes = true; - } + { + // do not remove nodes except elem + s2 = new set (); + s2->insert(elem); + removenodes = true; + } else s2 = getExclusiveNodes(*s1); // form the set of finite and construction elements to remove set s3; - set::iterator it=s1->begin(); - while(it!=s1->end()) - { - addChildrenWithNodes(s3, *it ,*s2); - s3.insert(*it); - it++; - } - if(elem->GetType()!=SMDSAbs_Node) s3.insert(elem); + set::iterator it = s1->begin(); + while (it != s1->end()) + { + addChildrenWithNodes(s3, *it, *s2); + s3.insert(*it); + it++; + } + if (elem->GetType() != SMDSAbs_Node) + s3.insert(elem); // remove finite and construction elements - it=s3.begin(); - while(it!=s3.end()) - { - // Remove element from of its nodes - SMDS_ElemIteratorPtr itn=(*it)->nodesIterator(); - while(itn->more()) + it = s3.begin(); + while (it != s3.end()) { - SMDS_MeshNode * n = static_cast - (const_cast(itn->next())); - n->RemoveInverseElement( (*it) ); + // Remove element from of its nodes + SMDS_ElemIteratorPtr itn = (*it)->nodesIterator(); + while (itn->more()) + { + SMDS_MeshNode * n = static_cast (const_cast (itn->next())); + n->RemoveInverseElement((*it)); + } + int IdToRemove = (*it)->GetID(); + int vtkid = (*it)->getVtkId(); + //MESSAGE("elem Id to remove " << IdToRemove << " vtkid " << vtkid << + // " vtktype " << (*it)->GetVtkType() << " type " << (*it)->GetType()); + switch ((*it)->GetType()) + { + case SMDSAbs_Node: + MYASSERT("Internal Error: This should not happen") + ; + break; + case SMDSAbs_0DElement: + if (IdToRemove >= 0) + { + myCells[IdToRemove] = 0; // -PR- ici ou dans myElementIDFactory->ReleaseID ? + myInfo.remove(*it); + } + removedElems.push_back((*it)); + myElementIDFactory->ReleaseID(IdToRemove, vtkid); + delete (*it); + break; + case SMDSAbs_Edge: + if (IdToRemove >= 0) + { + myCells[IdToRemove] = 0; + myInfo.RemoveEdge(*it); + } + removedElems.push_back((*it)); + myElementIDFactory->ReleaseID(IdToRemove, vtkid); + if (const SMDS_VtkEdge* vtkElem = dynamic_cast(*it)) + myEdgePool->destroy((SMDS_VtkEdge*) vtkElem); + else + delete (*it); + break; + case SMDSAbs_Face: + if (IdToRemove >= 0) + { + myCells[IdToRemove] = 0; + myInfo.RemoveFace(*it); + } + removedElems.push_back((*it)); + myElementIDFactory->ReleaseID(IdToRemove, vtkid); + if (const SMDS_VtkFace* vtkElem = dynamic_cast(*it)) + myFacePool->destroy((SMDS_VtkFace*) vtkElem); + else + delete (*it); + break; + case SMDSAbs_Volume: + if (IdToRemove >= 0) + { + myCells[IdToRemove] = 0; + myInfo.RemoveVolume(*it); + } + removedElems.push_back((*it)); + myElementIDFactory->ReleaseID(IdToRemove, vtkid); + if (const SMDS_VtkVolume* vtkElem = dynamic_cast(*it)) + myVolumePool->destroy((SMDS_VtkVolume*) vtkElem); + else + delete (*it); + break; + case SMDSAbs_Ball: + if (IdToRemove >= 0) + { + myCells[IdToRemove] = 0; + myInfo.remove(*it); + } + removedElems.push_back((*it)); + myElementIDFactory->ReleaseID(IdToRemove, vtkid); + if (const SMDS_BallElement* vtkElem = dynamic_cast(*it)) + myBallPool->destroy(const_cast( vtkElem )); + else + delete (*it); + break; + } + if (vtkid >= 0) + { + //MESSAGE("VTK_EMPTY_CELL in " << vtkid); + this->myGrid->GetCellTypesArray()->SetValue(vtkid, VTK_EMPTY_CELL); + } + it++; } - switch((*it)->GetType()) - { - case SMDSAbs_Node: - MESSAGE("Internal Error: This should not happen"); - break; - case SMDSAbs_Edge: - myEdges.Remove(static_cast - (const_cast(*it))); - myInfo.RemoveEdge(*it); - break; - case SMDSAbs_Face: - myFaces.Remove(static_cast - (const_cast(*it))); - myInfo.RemoveFace(*it); - break; - case SMDSAbs_Volume: - myVolumes.Remove(static_cast - (const_cast(*it))); - myInfo.RemoveVolume(*it); - break; - } - //MESSAGE( "SMDS: RM elem " << (*it)->GetID() ); - removedElems.push_back( (*it) ); - myElementIDFactory->ReleaseID((*it)->GetID()); - delete (*it); - it++; - } - // remove exclusive (free) nodes - if(removenodes) - { - it=s2->begin(); - while(it!=s2->end()) + if (removenodes) { - //MESSAGE( "SMDS: RM node " << (*it)->GetID() ); - myNodes.Remove(static_cast - (const_cast(*it))); - myInfo.myNbNodes--; - myNodeIDFactory->ReleaseID((*it)->GetID()); - removedNodes.push_back( (*it) ); - delete *it; - it++; + it = s2->begin(); + while (it != s2->end()) + { + int IdToRemove = (*it)->GetID(); + //MESSAGE( "SMDS: RM node " << IdToRemove); + if (IdToRemove >= 0) + { + myNodes[IdToRemove] = 0; + myInfo.myNbNodes--; + } + myNodeIDFactory->ReleaseID((*it)->GetID(), (*it)->getVtkId()); + removedNodes.push_back((*it)); + if (const SMDS_MeshNode* vtkElem = dynamic_cast(*it)) + { + ((SMDS_MeshNode*)vtkElem)->SetPosition(SMDS_SpacePosition::originSpacePosition()); + myNodePool->destroy((SMDS_MeshNode*) vtkElem); + } + else + delete (*it); + it++; + } } - } delete s2; delete s1; } - + /////////////////////////////////////////////////////////////////////////////// ///@param elem The element to delete /////////////////////////////////////////////////////////////////////////////// void SMDS_Mesh::RemoveFreeElement(const SMDS_MeshElement * elem) { + int elemId = elem->GetID(); + int vtkId = elem->getVtkId(); + //MESSAGE("RemoveFreeElement " << elemId); SMDSAbs_ElementType aType = elem->GetType(); + SMDS_MeshElement* todest = (SMDS_MeshElement*)(elem); if (aType == SMDSAbs_Node) { + //MESSAGE("Remove free node " << elemId); // only free node can be removed by this method - const SMDS_MeshNode* n = static_cast(elem); + const SMDS_MeshNode* n = static_cast(todest); SMDS_ElemIteratorPtr itFe = n->GetInverseElementIterator(); if (!itFe->more()) { // free node - myNodes.Remove(const_cast(n)); + myNodes[elemId] = 0; myInfo.myNbNodes--; - myNodeIDFactory->ReleaseID(elem->GetID()); - delete elem; + ((SMDS_MeshNode*) n)->SetPosition(SMDS_SpacePosition::originSpacePosition()); + myNodePool->destroy(static_cast(todest)); + myNodeIDFactory->ReleaseID(elemId, vtkId); } } else { if (hasConstructionEdges() || hasConstructionFaces()) // this methods is only for meshes without descendants return; + //MESSAGE("Remove free element " << elemId); // Remove element from of its nodes SMDS_ElemIteratorPtr itn = elem->nodesIterator(); while (itn->more()) { @@ -2418,27 +3361,39 @@ void SMDS_Mesh::RemoveFreeElement(const SMDS_MeshElement * elem) } // in meshes without descendants elements are always free - switch (aType) { + switch (aType) { + case SMDSAbs_0DElement: + myCells[elemId] = 0; + myInfo.remove(elem); + delete elem; + break; case SMDSAbs_Edge: - myEdges.Remove(static_cast - (const_cast(elem))); + myCells[elemId] = 0; myInfo.RemoveEdge(elem); + myEdgePool->destroy(static_cast(todest)); break; case SMDSAbs_Face: - myFaces.Remove(static_cast - (const_cast(elem))); + myCells[elemId] = 0; myInfo.RemoveFace(elem); + myFacePool->destroy(static_cast(todest)); break; case SMDSAbs_Volume: - myVolumes.Remove(static_cast - (const_cast(elem))); + myCells[elemId] = 0; myInfo.RemoveVolume(elem); + myVolumePool->destroy(static_cast(todest)); + break; + case SMDSAbs_Ball: + myCells[elemId] = 0; + myInfo.remove(elem); + myBallPool->destroy(static_cast(todest)); break; default: break; } - myElementIDFactory->ReleaseID(elem->GetID()); - delete elem; + myElementIDFactory->ReleaseID(elemId, vtkId); + + this->myGrid->GetCellTypesArray()->SetValue(vtkId, VTK_EMPTY_CELL); + // --- to do: keep vtkid in a list of reusable cells } } @@ -2454,44 +3409,36 @@ bool SMDS_Mesh::Contains (const SMDS_MeshElement* elem) const while (itn->more()) if (elem == itn->next()) return true; - SMDS_EdgeIteratorPtr ite = edgesIterator(); + SMDS_ElemIteratorPtr ite = elementsIterator(); while (ite->more()) if (elem == ite->next()) return true; - SMDS_FaceIteratorPtr itf = facesIterator(); - while (itf->more()) - if (elem == itf->next()) - return true; - SMDS_VolumeIteratorPtr itv = volumesIterator(); - while (itv->more()) - if (elem == itv->next()) - return true; return false; } //======================================================================= //function : MaxNodeID -//purpose : +//purpose : //======================================================================= int SMDS_Mesh::MaxNodeID() const { - return myNodeIDFactory->GetMaxID(); + return myNodeMax; } //======================================================================= //function : MinNodeID -//purpose : +//purpose : //======================================================================= int SMDS_Mesh::MinNodeID() const { - return myNodeIDFactory->GetMinID(); + return myNodeMin; } //======================================================================= //function : MaxElementID -//purpose : +//purpose : //======================================================================= int SMDS_Mesh::MaxElementID() const @@ -2501,7 +3448,7 @@ int SMDS_Mesh::MaxElementID() const //======================================================================= //function : MinElementID -//purpose : +//purpose : //======================================================================= int SMDS_Mesh::MinElementID() const @@ -2516,10 +3463,11 @@ int SMDS_Mesh::MinElementID() const void SMDS_Mesh::Renumber (const bool isNodes, const int startID, const int deltaID) { + MESSAGE("Renumber"); if ( deltaID == 0 ) return; - SMDS_MeshElementIDFactory * idFactory = + SMDS_MeshNodeIDFactory * idFactory = isNodes ? myNodeIDFactory : myElementIDFactory; // get existing elements in the order of ID increasing @@ -2582,9 +3530,9 @@ SMDSAbs_ElementType SMDS_Mesh::GetElementType( const int id, const bool iselem ) //======================================================================= //function : AddEdgeWithID -//purpose : +//purpose : //======================================================================= -SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int n1, int n2, int n12, int ID) +SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int n1, int n2, int n12, int ID) { return SMDS_Mesh::AddEdgeWithID ((SMDS_MeshNode*) myNodeIDFactory->MeshElement(n1), @@ -2595,10 +3543,10 @@ SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(int n1, int n2, int n12, int ID) //======================================================================= //function : AddEdge -//purpose : +//purpose : //======================================================================= SMDS_MeshEdge* SMDS_Mesh::AddEdge(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, + const SMDS_MeshNode* n2, const SMDS_MeshNode* n12) { return SMDS_Mesh::AddEdgeWithID(n1, n2, n12, myElementIDFactory->GetFreeID()); @@ -2606,41 +3554,52 @@ SMDS_MeshEdge* SMDS_Mesh::AddEdge(const SMDS_MeshNode* n1, //======================================================================= //function : AddEdgeWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshEdge* SMDS_Mesh::AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n12, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n12, + int ID) { if ( !n1 || !n2 || !n12 ) return 0; - SMDS_QuadraticEdge* edge = new SMDS_QuadraticEdge(n1,n2,n12); - if(myElementIDFactory->BindID(ID, edge)) { - SMDS_MeshNode *node1,*node2, *node12; - node1 = const_cast(n1); - node2 = const_cast(n2); - node12 = const_cast(n12); - node1->AddInverseElement(edge); - node2->AddInverseElement(edge); - node12->AddInverseElement(edge); - myEdges.Add(edge); - myInfo.myNbQuadEdges++; - return edge; - } - else { - delete edge; - return NULL; - } + + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + + SMDS_MeshEdge * edge = 0; + SMDS_VtkEdge *edgevtk = myEdgePool->getNew(); + edgevtk->init(nodeIds, this); + if (!this->registerElement(ID,edgevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(edgevtk->getVtkId(), VTK_EMPTY_CELL); + myEdgePool->destroy(edgevtk); + return 0; + } + edge = edgevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = edge; + myInfo.myNbQuadEdges++; + +// if (!registerElement(ID, edge)) { +// RemoveElement(edge, false); +// edge = NULL; +// } + return edge; + } //======================================================================= //function : AddFace -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31) @@ -2651,7 +3610,7 @@ SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, //======================================================================= //function : AddFaceWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n12,int n23,int n31, int ID) @@ -2668,14 +3627,14 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int n1, int n2, int n3, //======================================================================= //function : AddFaceWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n3, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, + const SMDS_MeshNode * n31, int ID) { if ( !n1 || !n2 || !n3 || !n12 || !n23 || !n31) return 0; @@ -2683,27 +3642,49 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, // creation quadratic edges - not implemented return 0; } - SMDS_QuadraticFaceOfNodes* face = - new SMDS_QuadraticFaceOfNodes(n1,n2,n3,n12,n23,n31); - myFaces.Add(face); - myInfo.myNbQuadTriangles++; - - if (!registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; + else + { + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n31->getVtkId()); + + SMDS_MeshFace * face = 0; + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(nodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = face; + myInfo.myNbQuadTriangles++; + +// if (!registerElement(ID, face)) { +// RemoveElement(face, false); +// face = NULL; +// } + return face; } - return face; } //======================================================================= //function : AddFace -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, @@ -2715,7 +3696,7 @@ SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, //======================================================================= //function : AddFaceWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n34,int n41, int ID) @@ -2734,7 +3715,7 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n4, //======================================================================= //function : AddFaceWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, @@ -2742,39 +3723,159 @@ SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, int ID) { if ( !n1 || !n2 || !n3 || !n4 || !n12 || !n23 || !n34 || !n41) return 0; if(hasConstructionEdges()) { // creation quadratic edges - not implemented + return 0; + } + else + { + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n41->getVtkId()); + + SMDS_MeshFace * face = 0; + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(nodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = face; + myInfo.myNbQuadQuadrangles++; + +// if (!registerElement(ID, face)) { +// RemoveElement(face, false); +// face = NULL; +// } + return face; } - SMDS_QuadraticFaceOfNodes* face = - new SMDS_QuadraticFaceOfNodes(n1,n2,n3,n4,n12,n23,n34,n41); - myFaces.Add(face); - myInfo.myNbQuadQuadrangles++; +} - if (!registerElement(ID, face)) { - RemoveElement(face, false); - face = NULL; +//======================================================================= +//function : AddFace +//purpose : +//======================================================================= +SMDS_MeshFace* SMDS_Mesh::AddFace(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter) +{ + return SMDS_Mesh::AddFaceWithID(n1,n2,n3,n4,n12,n23,n34,n41,nCenter, + myElementIDFactory->GetFreeID()); +} + +//======================================================================= +//function : AddFaceWithID +//purpose : +//======================================================================= +SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n4, + int n12,int n23,int n34,int n41, int nCenter, int ID) +{ + return SMDS_Mesh::AddFaceWithID + ((SMDS_MeshNode *)myNodeIDFactory->MeshElement(n1) , + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n2) , + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n3) , + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n4) , + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n12), + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n23), + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n34), + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(n41), + (SMDS_MeshNode *)myNodeIDFactory->MeshElement(nCenter), + ID); +} + +//======================================================================= +//function : AddFaceWithID +//purpose : +//======================================================================= +SMDS_MeshFace* SMDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter, + int ID) +{ + if ( !n1 || !n2 || !n3 || !n4 || !n12 || !n23 || !n34 || !n41 || !nCenter) return 0; + if(hasConstructionEdges()) { + // creation quadratic edges - not implemented + return 0; + } + else + { + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n41->getVtkId()); + nodeIds.push_back(nCenter->getVtkId()); + + SMDS_MeshFace * face = 0; + SMDS_VtkFace *facevtk = myFacePool->getNew(); + facevtk->init(nodeIds, this); + if (!this->registerElement(ID,facevtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(facevtk->getVtkId(), VTK_EMPTY_CELL); + myFacePool->destroy(facevtk); + return 0; + } + face = facevtk; + adjustmyCellsCapacity(ID); + myCells[ID] = face; + myInfo.myNbBiQuadQuadrangles++; + +// if (!registerElement(ID, face)) { +// RemoveElement(face, false); +// face = NULL; +// } + return face; } - return face; } //======================================================================= //function : AddVolume -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31, - const SMDS_MeshNode * n14, + const SMDS_MeshNode * n14, const SMDS_MeshNode * n24, const SMDS_MeshNode * n34) { @@ -2787,7 +3888,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, //======================================================================= //function : AddVolumeWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n31, @@ -2818,7 +3919,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31, - const SMDS_MeshNode * n14, + const SMDS_MeshNode * n14, const SMDS_MeshNode * n24, const SMDS_MeshNode * n34, int ID) @@ -2829,33 +3930,56 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, // creation quadratic faces - not implemented return 0; } - SMDS_QuadraticVolumeOfNodes * volume = - new SMDS_QuadraticVolumeOfNodes(n1,n2,n3,n4,n12,n23,n31,n14,n24,n34); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + + nodeIds.push_back(n31->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + + nodeIds.push_back(n14->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n24->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; myInfo.myNbQuadTetras++; - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } - return volume; +// if (!registerElement(ID, volvtk)) { +// RemoveElement(volvtk, false); +// volvtk = NULL; +// } + return volvtk; } //======================================================================= //function : AddVolume -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, const SMDS_MeshNode * n41, - const SMDS_MeshNode * n15, + const SMDS_MeshNode * n15, const SMDS_MeshNode * n25, const SMDS_MeshNode * n35, const SMDS_MeshNode * n45) @@ -2870,7 +3994,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, //======================================================================= //function : AddVolumeWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n12,int n23,int n34,int n41, @@ -2901,12 +4025,12 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n3, const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, + const SMDS_MeshNode * n5, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, const SMDS_MeshNode * n41, - const SMDS_MeshNode * n15, + const SMDS_MeshNode * n15, const SMDS_MeshNode * n25, const SMDS_MeshNode * n35, const SMDS_MeshNode * n45, @@ -2919,36 +4043,61 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, // creation quadratic faces - not implemented return 0; } - SMDS_QuadraticVolumeOfNodes * volume = - new SMDS_QuadraticVolumeOfNodes(n1,n2,n3,n4,n5,n12,n23, - n34,n41,n15,n25,n35,n45); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + + nodeIds.push_back(n41->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + + nodeIds.push_back(n15->getVtkId()); + nodeIds.push_back(n45->getVtkId()); + nodeIds.push_back(n35->getVtkId()); + nodeIds.push_back(n25->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; myInfo.myNbQuadPyramids++; - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } - return volume; +// if (!registerElement(ID, volvtk)) { +// RemoveElement(volvtk, false); +// volvtk = NULL; +// } + return volvtk; } //======================================================================= //function : AddVolume -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, + const SMDS_MeshNode * n31, const SMDS_MeshNode * n45, const SMDS_MeshNode * n56, - const SMDS_MeshNode * n64, + const SMDS_MeshNode * n64, const SMDS_MeshNode * n14, const SMDS_MeshNode * n25, const SMDS_MeshNode * n36) @@ -2963,7 +4112,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, //======================================================================= //function : AddVolumeWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, @@ -2998,14 +4147,14 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n3, const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, + const SMDS_MeshNode * n31, const SMDS_MeshNode * n45, const SMDS_MeshNode * n56, - const SMDS_MeshNode * n64, + const SMDS_MeshNode * n64, const SMDS_MeshNode * n14, const SMDS_MeshNode * n25, const SMDS_MeshNode * n36, @@ -3018,40 +4167,69 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, // creation quadratic faces - not implemented return 0; } - SMDS_QuadraticVolumeOfNodes * volume = - new SMDS_QuadraticVolumeOfNodes(n1,n2,n3,n4,n5,n6,n12,n23,n31, - n45,n56,n64,n14,n25,n36); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + + nodeIds.push_back(n12->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n31->getVtkId()); + + nodeIds.push_back(n45->getVtkId()); + nodeIds.push_back(n56->getVtkId()); + nodeIds.push_back(n64->getVtkId()); + + nodeIds.push_back(n14->getVtkId()); + nodeIds.push_back(n25->getVtkId()); + nodeIds.push_back(n36->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; myInfo.myNbQuadPrisms++; - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; - } - return volume; +// if (!registerElement(ID, volvtk)) { +// RemoveElement(volvtk, false); +// volvtk = NULL; +// } + return volvtk; } //======================================================================= //function : AddVolume -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, + const SMDS_MeshNode * n8, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, + const SMDS_MeshNode * n41, const SMDS_MeshNode * n56, const SMDS_MeshNode * n67, const SMDS_MeshNode * n78, - const SMDS_MeshNode * n85, + const SMDS_MeshNode * n85, const SMDS_MeshNode * n15, const SMDS_MeshNode * n26, const SMDS_MeshNode * n37, @@ -3067,7 +4245,7 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, //======================================================================= //function : AddVolumeWithID -//purpose : +//purpose : //======================================================================= SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, @@ -3107,18 +4285,18 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n3, const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, + const SMDS_MeshNode * n8, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, + const SMDS_MeshNode * n41, const SMDS_MeshNode * n56, const SMDS_MeshNode * n67, const SMDS_MeshNode * n78, - const SMDS_MeshNode * n85, + const SMDS_MeshNode * n85, const SMDS_MeshNode * n15, const SMDS_MeshNode * n26, const SMDS_MeshNode * n37, @@ -3132,15 +4310,391 @@ SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, return 0; // creation quadratic faces - not implemented } - SMDS_QuadraticVolumeOfNodes * volume = - new SMDS_QuadraticVolumeOfNodes(n1,n2,n3,n4,n5,n6,n7,n8,n12,n23,n34,n41, - n56,n67,n78,n85,n15,n26,n37,n48); - myVolumes.Add(volume); + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n8->getVtkId()); + nodeIds.push_back(n7->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + + nodeIds.push_back(n41->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + + nodeIds.push_back(n85->getVtkId()); + nodeIds.push_back(n78->getVtkId()); + nodeIds.push_back(n67->getVtkId()); + nodeIds.push_back(n56->getVtkId()); + + nodeIds.push_back(n15->getVtkId()); + nodeIds.push_back(n48->getVtkId()); + nodeIds.push_back(n37->getVtkId()); + nodeIds.push_back(n26->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; myInfo.myNbQuadHexas++; - if (!registerElement(ID, volume)) { - RemoveElement(volume, false); - volume = NULL; +// if (!registerElement(ID, volvtk)) { +// RemoveElement(volvtk, false); +// volvtk = NULL; +// } + return volvtk; +} + +//======================================================================= +//function : AddVolume +//purpose : +//======================================================================= +SMDS_MeshVolume* SMDS_Mesh::AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter) +{ + int ID = myElementIDFactory->GetFreeID(); + SMDS_MeshVolume * v = + SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8, n12, n23, n34, n41, + n56, n67, n78, n85, n15, n26, n37, n48, + n1234, n1256, n2367, n3478, n1458, n5678, nCenter, + ID); + if(v==NULL) myElementIDFactory->ReleaseID(ID); + return v; +} + +//======================================================================= +//function : AddVolumeWithID +//purpose : +//======================================================================= +SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12,int n23,int n34,int n41, + int n56,int n67,int n78,int n85, + int n15,int n26,int n37,int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter, int ID) +{ + return SMDS_Mesh::AddVolumeWithID + ((SMDS_MeshNode*) myNodeIDFactory->MeshElement(n1), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n2), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n3), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n4), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n5), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n6), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n7), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n8), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n12), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n23), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n34), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n41), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n56), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n67), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n78), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n85), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n15), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n26), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n37), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n48), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n1234), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n1256), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n2367), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n3478), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n1458), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(n5678), + (SMDS_MeshNode*) myNodeIDFactory->MeshElement(nCenter), + ID); +} + +//======================================================================= +//function : AddVolumeWithID +//purpose : 2d order Hexahedrons with 20 nodes +//======================================================================= +SMDS_MeshVolume* SMDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter, + int ID) +{ + if (!n1 || !n2 || !n3 || !n4 || !n5 || !n6 || !n7 || !n8 || !n12 || !n23 || + !n34 || !n41 || !n56 || !n67 || !n78 || !n85 || !n15 || !n26 || !n37 || !n48 || + !n1234 || !n1256 || !n2367 || !n3478 || !n1458 || !n5678 || !nCenter ) + return 0; + if(hasConstructionFaces()) { + return 0; + // creation quadratic faces - not implemented } - return volume; + // --- retrieve nodes ID + vector nodeIds; + nodeIds.clear(); + nodeIds.push_back(n1->getVtkId()); + nodeIds.push_back(n4->getVtkId()); + nodeIds.push_back(n3->getVtkId()); + nodeIds.push_back(n2->getVtkId()); + + nodeIds.push_back(n5->getVtkId()); + nodeIds.push_back(n8->getVtkId()); + nodeIds.push_back(n7->getVtkId()); + nodeIds.push_back(n6->getVtkId()); + + nodeIds.push_back(n41->getVtkId()); + nodeIds.push_back(n34->getVtkId()); + nodeIds.push_back(n23->getVtkId()); + nodeIds.push_back(n12->getVtkId()); + + nodeIds.push_back(n85->getVtkId()); + nodeIds.push_back(n78->getVtkId()); + nodeIds.push_back(n67->getVtkId()); + nodeIds.push_back(n56->getVtkId()); + + nodeIds.push_back(n15->getVtkId()); + nodeIds.push_back(n48->getVtkId()); + nodeIds.push_back(n37->getVtkId()); + nodeIds.push_back(n26->getVtkId()); + + nodeIds.push_back(n1256->getVtkId()); + nodeIds.push_back(n3478->getVtkId()); + nodeIds.push_back(n1458->getVtkId()); + nodeIds.push_back(n2367->getVtkId()); + nodeIds.push_back(n1234->getVtkId()); + nodeIds.push_back(n5678->getVtkId()); + nodeIds.push_back(nCenter->getVtkId()); + + SMDS_VtkVolume *volvtk = myVolumePool->getNew(); + volvtk->init(nodeIds, this); + if (!this->registerElement(ID,volvtk)) + { + this->myGrid->GetCellTypesArray()->SetValue(volvtk->getVtkId(), VTK_EMPTY_CELL); + myVolumePool->destroy(volvtk); + return 0; + } + adjustmyCellsCapacity(ID); + myCells[ID] = volvtk; + myInfo.myNbTriQuadHexas++; + + return volvtk; +} + + +void SMDS_Mesh::updateNodeMinMax() +{ + myNodeMin = 0; + if (myNodes.size() == 0) + { + myNodeMax=0; + return; + } + while (!myNodes[myNodeMin] && (myNodeMin=0)) + myNodeMin--; +} + +void SMDS_Mesh::incrementNodesCapacity(int nbNodes) +{ +// int val = myCellIdSmdsToVtk.size(); +// MESSAGE(" ------------------- resize myCellIdSmdsToVtk " << val << " --> " << val + nbNodes); +// myCellIdSmdsToVtk.resize(val + nbNodes, -1); // fill new elements with -1 + int val = myNodes.size(); + MESSAGE(" ------------------- resize myNodes " << val << " --> " << val + nbNodes); + myNodes.resize(val +nbNodes, 0); +} + +void SMDS_Mesh::incrementCellsCapacity(int nbCells) +{ + int val = myCellIdVtkToSmds.size(); + MESSAGE(" ------------------- resize myCellIdVtkToSmds " << val << " --> " << val + nbCells); + myCellIdVtkToSmds.resize(val + nbCells, -1); // fill new elements with -1 + val = myCells.size(); + MESSAGE(" ------------------- resize myCells " << val << " --> " << val + nbCells); + myNodes.resize(val +nbCells, 0); +} + +void SMDS_Mesh::adjustStructure() +{ + myGrid->GetPoints()->GetData()->SetNumberOfTuples(myNodeIDFactory->GetMaxID()); +} + +void SMDS_Mesh::dumpGrid(string ficdump) +{ + MESSAGE("SMDS_Mesh::dumpGrid " << ficdump); +// vtkUnstructuredGridWriter* aWriter = vtkUnstructuredGridWriter::New(); +// aWriter->SetFileName(ficdump.c_str()); +// aWriter->SetInput(myGrid); +// if(myGrid->GetNumberOfCells()) +// { +// aWriter->Write(); +// } +// aWriter->Delete(); + ficdump = ficdump + "_connectivity"; + ofstream ficcon(ficdump.c_str(), ios::out); + int nbPoints = myGrid->GetNumberOfPoints(); + ficcon << "-------------------------------- points " << nbPoints << endl; + for (int i=0; iGetPoint(i)) << " " << *(myGrid->GetPoint(i)+1) << " " << " " << *(myGrid->GetPoint(i)+2) << endl; + } + int nbCells = myGrid->GetNumberOfCells(); + ficcon << "-------------------------------- cells " << nbCells << endl; + for (int i=0; iGetCell(i)); +// MESSAGE(" " << myGrid->GetCell(i)->GetCellType()); + ficcon << i << " - " << myGrid->GetCell(i)->GetCellType() << " -"; + int nbptcell = myGrid->GetCell(i)->GetNumberOfPoints(); + vtkIdList *listid = myGrid->GetCell(i)->GetPointIds(); + for (int j=0; jGetId(j); + } + ficcon << endl; + } + ficcon << "-------------------------------- connectivity " << nbPoints << endl; + vtkCellLinks *links = myGrid->GetCellLinks(); + for (int i=0; iGetNcells(i); + vtkIdType *cells = links->GetCells(i); + ficcon << i << " - " << ncells << " -"; + for (int j=0; j= 0 && vtkid < myCellIdVtkToSmds.size()) + return myCellIdVtkToSmds[vtkid]; + throw SALOME_Exception(LOCALIZED ("vtk id out of bounds")); +} + +void SMDS_Mesh::updateBoundingBox() +{ + xmin = 0; xmax = 0; + ymin = 0; ymax = 0; + zmin = 0; zmax = 0; + vtkPoints *points = myGrid->GetPoints(); + int myNodesSize = this->myNodes.size(); + for (int i = 0; i < myNodesSize; i++) + { + if (SMDS_MeshNode *n = myNodes[i]) + { + double coords[3]; + points->GetPoint(n->myVtkID, coords); + if (coords[0] < xmin) xmin = coords[0]; + else if (coords[0] > xmax) xmax = coords[0]; + if (coords[1] < ymin) ymin = coords[1]; + else if (coords[1] > ymax) ymax = coords[1]; + if (coords[2] < zmin) zmin = coords[2]; + else if (coords[2] > zmax) zmax = coords[2]; + } + } +} + +double SMDS_Mesh::getMaxDim() +{ + double dmax = 1.e-3; + if ((xmax - xmin) > dmax) dmax = xmax -xmin; + if ((ymax - ymin) > dmax) dmax = ymax -ymin; + if ((zmax - zmin) > dmax) dmax = zmax -zmin; + MESSAGE("getMaxDim " << dmax); + return dmax; +} + +//! modification that needs compact structure and redraw +void SMDS_Mesh::Modified() +{ + if (this->myModified) + { + this->myModifTime++; + MESSAGE("modified"); + myModified = false; + } +} + +//! get last modification timeStamp +unsigned long SMDS_Mesh::GetMTime() const +{ + return this->myModifTime; +} + +bool SMDS_Mesh::isCompacted() +{ + if (this->myModifTime > this->myCompactTime) + { + MESSAGE(" *** isCompacted " << myCompactTime << " < " << myModifTime); + this->myCompactTime = this->myModifTime; + return false; + } + return true; } diff --git a/src/SMDS/SMDS_Mesh.hxx b/src/SMDS/SMDS_Mesh.hxx index a0def42ba..7e0361286 100644 --- a/src/SMDS/SMDS_Mesh.hxx +++ b/src/SMDS/SMDS_Mesh.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_Mesh.hxx // Module : SMESH @@ -29,73 +30,113 @@ #include "SMESH_SMDS.hxx" #include "SMDS_MeshNode.hxx" +#include "SMDS_MeshCell.hxx" +#include "SMDS_Mesh0DElement.hxx" #include "SMDS_MeshEdge.hxx" #include "SMDS_MeshFace.hxx" #include "SMDS_MeshVolume.hxx" +#include "SMDS_MeshNodeIDFactory.hxx" #include "SMDS_MeshElementIDFactory.hxx" #include "SMDS_MeshInfo.hxx" #include "SMDS_ElemIterator.hxx" -#include +#include "SMDS_VolumeOfNodes.hxx" +#include "SMDS_VtkEdge.hxx" +#include "SMDS_VtkFace.hxx" +#include "SMDS_VtkVolume.hxx" +#include "ObjectPool.hxx" +#include "SMDS_UnstructuredGrid.hxx" +#include "SMDS_BallElement.hxx" #include #include #include +#include +#include +#include + +#include "Utils_SALOME_Exception.hxx" -class SMDS_EXPORT SMDS_Mesh:public SMDS_MeshObject{ +#define MYASSERT(val) if (!(val)) throw SALOME_Exception(LOCALIZED("assertion not verified")); + +class SMDS_EXPORT SMDS_Mesh:public SMDS_MeshObject +{ public: - + friend class SMDS_MeshIDFactory; + friend class SMDS_MeshNodeIDFactory; + friend class SMDS_MeshElementIDFactory; + friend class SMDS_MeshVolumeVtkNodes; + friend class SMDS_MeshNode; + SMDS_Mesh(); - SMDS_NodeIteratorPtr nodesIterator() const; - SMDS_EdgeIteratorPtr edgesIterator() const; - SMDS_FaceIteratorPtr facesIterator() const; - SMDS_VolumeIteratorPtr volumesIterator() const; - SMDS_ElemIteratorPtr elementsIterator() const; - + //! to retreive this SMDS_Mesh instance from its elements (index stored in SMDS_Elements) + static std::vector _meshList; + + //! actual nodes coordinates, cells definition and reverse connectivity are stored in a vtkUnstructuredGrid + inline SMDS_UnstructuredGrid* getGrid() {return myGrid; } + inline int getMeshId() {return myMeshId; } + + virtual SMDS_NodeIteratorPtr nodesIterator (bool idInceasingOrder=false) const; + virtual SMDS_EdgeIteratorPtr edgesIterator (bool idInceasingOrder=false) const; + virtual SMDS_FaceIteratorPtr facesIterator (bool idInceasingOrder=false) const; + virtual SMDS_VolumeIteratorPtr volumesIterator (bool idInceasingOrder=false) const; + + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type=SMDSAbs_All) const; + virtual SMDS_ElemIteratorPtr elementGeomIterator(SMDSAbs_GeometryType type) const; + virtual SMDS_ElemIteratorPtr elementEntityIterator(SMDSAbs_EntityType type) const; + SMDSAbs_ElementType GetElementType( const int id, const bool iselem ) const; SMDS_Mesh *AddSubMesh(); - + virtual SMDS_MeshNode* AddNodeWithID(double x, double y, double z, int ID); - virtual SMDS_MeshNode* AddNode(double x, double y, double z); - + virtual SMDS_MeshNode* AddNode (double x, double y, double z); + + virtual SMDS_Mesh0DElement* Add0DElementWithID(int n, int ID); + virtual SMDS_Mesh0DElement* Add0DElementWithID(const SMDS_MeshNode * n, int ID); + virtual SMDS_Mesh0DElement* Add0DElement (const SMDS_MeshNode * n); + + virtual SMDS_BallElement* AddBallWithID(int n, double diameter, int ID); + virtual SMDS_BallElement* AddBallWithID(const SMDS_MeshNode * n, double diameter, int ID); + virtual SMDS_BallElement* AddBall (const SMDS_MeshNode * n, double diameter); + virtual SMDS_MeshEdge* AddEdgeWithID(int n1, int n2, int ID); virtual SMDS_MeshEdge* AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - int ID); + const SMDS_MeshNode * n2, + int ID); virtual SMDS_MeshEdge* AddEdge(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2); - + const SMDS_MeshNode * n2); + // 2d order edge with 3 nodes: n12 - node between n1 and n2 virtual SMDS_MeshEdge* AddEdgeWithID(int n1, int n2, int n12, int ID); virtual SMDS_MeshEdge* AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n12, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n12, + int ID); virtual SMDS_MeshEdge* AddEdge(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n12); virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3); + virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4); + virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshEdge * e1, const SMDS_MeshEdge * e2, const SMDS_MeshEdge * e3, int ID); @@ -116,100 +157,122 @@ public: virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n12,int n23,int n31, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31); + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31); // 2d order quadrangle virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n34,int n41, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41); + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41); + + virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, + int n12,int n23,int n34,int n41, int nCenter, int ID); + virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter, + int ID); + virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter); virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4); + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5); + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6); + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshFace * f1, const SMDS_MeshFace * f2, @@ -244,29 +307,59 @@ public: const SMDS_MeshFace * f5, const SMDS_MeshFace * f6); + // hexagonal prism + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, + int n7, int n8, int n9, int n10, int n11, int n12, + int ID); + virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12, + int ID); + virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12); + // 2d order tetrahedron of 10 nodes virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n31, int n14,int n24,int n34, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - const SMDS_MeshNode * n14, - const SMDS_MeshNode * n24, - const SMDS_MeshNode * n34, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + const SMDS_MeshNode * n14, + const SMDS_MeshNode * n24, + const SMDS_MeshNode * n34, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31, - const SMDS_MeshNode * n14, + const SMDS_MeshNode * n14, const SMDS_MeshNode * n24, const SMDS_MeshNode * n34); @@ -276,28 +369,28 @@ public: int n15,int n25,int n35,int n45, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - const SMDS_MeshNode * n15, - const SMDS_MeshNode * n25, - const SMDS_MeshNode * n35, - const SMDS_MeshNode * n45, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n25, + const SMDS_MeshNode * n35, + const SMDS_MeshNode * n45, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, + const SMDS_MeshNode * n41, const SMDS_MeshNode * n15, const SMDS_MeshNode * n25, const SMDS_MeshNode * n35, @@ -311,33 +404,33 @@ public: int n14,int n25,int n36, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - const SMDS_MeshNode * n45, - const SMDS_MeshNode * n56, - const SMDS_MeshNode * n64, - const SMDS_MeshNode * n14, - const SMDS_MeshNode * n25, - const SMDS_MeshNode * n36, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + const SMDS_MeshNode * n45, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n64, + const SMDS_MeshNode * n14, + const SMDS_MeshNode * n25, + const SMDS_MeshNode * n36, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, + const SMDS_MeshNode * n31, const SMDS_MeshNode * n45, const SMDS_MeshNode * n56, - const SMDS_MeshNode * n64, + const SMDS_MeshNode * n64, const SMDS_MeshNode * n14, const SMDS_MeshNode * n25, const SMDS_MeshNode * n36); @@ -350,68 +443,143 @@ public: int n15,int n26,int n37,int n48, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - const SMDS_MeshNode * n56, - const SMDS_MeshNode * n67, - const SMDS_MeshNode * n78, - const SMDS_MeshNode * n85, - const SMDS_MeshNode * n15, - const SMDS_MeshNode * n26, - const SMDS_MeshNode * n37, - const SMDS_MeshNode * n48, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, + const SMDS_MeshNode * n41, const SMDS_MeshNode * n56, const SMDS_MeshNode * n67, const SMDS_MeshNode * n78, - const SMDS_MeshNode * n85, + const SMDS_MeshNode * n85, const SMDS_MeshNode * n15, const SMDS_MeshNode * n26, const SMDS_MeshNode * n37, const SMDS_MeshNode * n48); - virtual SMDS_MeshFace* AddPolygonalFaceWithID (std::vector nodes_ids, - const int ID); + // 2d oreder Hexahedrons with 27 nodes + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12,int n23,int n34,int n41, + int n56,int n67,int n78,int n85, + int n15,int n26,int n37,int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter, + int ID); + virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter, + int ID); + virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter); + + virtual SMDS_MeshFace* AddPolygonalFaceWithID (const std::vector & nodes_ids, + const int ID); - virtual SMDS_MeshFace* AddPolygonalFaceWithID (std::vector nodes, - const int ID); + virtual SMDS_MeshFace* AddPolygonalFaceWithID (const std::vector & nodes, + const int ID); - virtual SMDS_MeshFace* AddPolygonalFace (std::vector nodes); + virtual SMDS_MeshFace* AddPolygonalFace (const std::vector & nodes); virtual SMDS_MeshVolume* AddPolyhedralVolumeWithID - (std::vector nodes_ids, - std::vector quantities, - const int ID); + (const std::vector & nodes_ids, + const std::vector & quantities, + const int ID); virtual SMDS_MeshVolume* AddPolyhedralVolumeWithID - (std::vector nodes, - std::vector quantities, - const int ID); + (const std::vector & nodes, + const std::vector & quantities, + const int ID); virtual SMDS_MeshVolume* AddPolyhedralVolume - (std::vector nodes, - std::vector quantities); + (const std::vector & nodes, + const std::vector & quantities); + + virtual SMDS_MeshVolume* AddVolumeFromVtkIds(const std::vector& vtkNodeIds); + + virtual SMDS_MeshVolume* AddVolumeFromVtkIdsWithID(const std::vector& vtkNodeIds, + const int ID); + + virtual SMDS_MeshFace* AddFaceFromVtkIds(const std::vector& vtkNodeIds); + + virtual SMDS_MeshFace* AddFaceFromVtkIdsWithID(const std::vector& vtkNodeIds, + const int ID); virtual void RemoveElement(const SMDS_MeshElement * elem, std::list& removedElems, @@ -419,6 +587,7 @@ public: const bool removenodes = false); virtual void RemoveElement(const SMDS_MeshElement * elem, bool removenodes = false); virtual void RemoveNode(const SMDS_MeshNode * node); + virtual void Remove0DElement(const SMDS_Mesh0DElement * elem0d); virtual void RemoveEdge(const SMDS_MeshEdge * edge); virtual void RemoveFace(const SMDS_MeshFace * face); virtual void RemoveVolume(const SMDS_MeshVolume * volume); @@ -430,7 +599,7 @@ public: virtual void RemoveFreeElement(const SMDS_MeshElement * elem); virtual void Clear(); - + virtual bool RemoveFromParent(); virtual bool RemoveSubMesh(const SMDS_Mesh * aMesh); @@ -443,8 +612,12 @@ public: virtual void Renumber (const bool isNodes, const int startID = 1, const int deltaID = 1); // Renumber all nodes or elements. + virtual void compactMesh(); const SMDS_MeshNode *FindNode(int idnode) const; + const SMDS_MeshNode *FindNodeVtk(int idnode) const; + const SMDS_Mesh0DElement* Find0DElement(int idnode) const; + const SMDS_BallElement* FindBall(int idnode) const; const SMDS_MeshEdge *FindEdge(int idnode1, int idnode2) const; const SMDS_MeshEdge *FindEdge(int idnode1, int idnode2, int idnode3) const; const SMDS_MeshFace *FindFace(int idnode1, int idnode2, int idnode3) const; @@ -454,6 +627,8 @@ public: const SMDS_MeshFace *FindFace(int idnode1, int idnode2, int idnode3, int idnode4, int idnode5, int idnode6, int idnode7, int idnode8) const; const SMDS_MeshElement *FindElement(int IDelem) const; + static const SMDS_Mesh0DElement* Find0DElement(const SMDS_MeshNode * n); + static const SMDS_BallElement* FindBall(const SMDS_MeshNode * n); static const SMDS_MeshEdge* FindEdge(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2); static const SMDS_MeshEdge* FindEdge(const SMDS_MeshNode * n1, @@ -481,8 +656,11 @@ public: const SMDS_MeshNode *n7, const SMDS_MeshNode *n8); - const SMDS_MeshFace *FindFace(std::vector nodes_ids) const; - static const SMDS_MeshFace* FindFace(std::vector nodes); + const SMDS_MeshFace *FindFace(const std::vector& nodes_ids) const; + static const SMDS_MeshFace* FindFace(const std::vector& nodes); + static const SMDS_MeshElement* FindElement(const std::vector& nodes, + const SMDSAbs_ElementType type=SMDSAbs_All, + const bool noMedium=true); /*! * \brief Raise an exception if free memory (ram+swap) too low @@ -498,19 +676,23 @@ public: const SMDS_MeshInfo& GetMeshInfo() const { return myInfo; } - int NbNodes() const; - int NbEdges() const; - int NbFaces() const; - int NbVolumes() const; - int NbSubMesh() const; + virtual int NbNodes() const; + virtual int Nb0DElements() const; + virtual int NbBalls() const; + virtual int NbEdges() const; + virtual int NbFaces() const; + virtual int NbVolumes() const; + virtual int NbSubMesh() const; + void DumpNodes() const; + void Dump0DElements() const; void DumpEdges() const; void DumpFaces() const; void DumpVolumes() const; void DebugStats() const; - SMDS_Mesh *boundaryFaces(); - SMDS_Mesh *boundaryEdges(); + virtual ~SMDS_Mesh(); + bool hasConstructionEdges(); bool hasConstructionFaces(); bool hasInverseElements(); @@ -526,52 +708,127 @@ public: */ bool Contains (const SMDS_MeshElement* elem) const; - typedef NCollection_Map SetOfNodes; - typedef NCollection_Map SetOfEdges; - typedef NCollection_Map SetOfFaces; - typedef NCollection_Map SetOfVolumes; + typedef std::vector SetOfNodes; + typedef std::vector SetOfCells; + + void updateNodeMinMax(); + void updateBoundingBox(); + double getMaxDim(); + int fromVtkToSmds(int vtkid); + + void incrementNodesCapacity(int nbNodes); + void incrementCellsCapacity(int nbCells); + void adjustStructure(); + void dumpGrid(string ficdump="dumpGrid"); + static int chunkSize; -private: + //! low level modification: add, change or remove node or element + inline void setMyModified() { this->myModified = true; } + + void Modified(); + unsigned long GetMTime() const; + bool isCompacted(); + +protected: SMDS_Mesh(SMDS_Mesh * parent); - SMDS_MeshFace * createTriangle(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3); + SMDS_MeshFace * createTriangle(const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + int ID); SMDS_MeshFace * createQuadrangle(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4); + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + int ID); SMDS_MeshEdge* FindEdgeOrCreate(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2); + const SMDS_MeshNode * n2); SMDS_MeshFace* FindFaceOrCreate(const SMDS_MeshNode *n1, - const SMDS_MeshNode *n2, - const SMDS_MeshNode *n3); + const SMDS_MeshNode *n2, + const SMDS_MeshNode *n3); SMDS_MeshFace* FindFaceOrCreate(const SMDS_MeshNode *n1, - const SMDS_MeshNode *n2, - const SMDS_MeshNode *n3, - const SMDS_MeshNode *n4); + const SMDS_MeshNode *n2, + const SMDS_MeshNode *n3, + const SMDS_MeshNode *n4); bool registerElement(int ID, SMDS_MeshElement * element); - void addChildrenWithNodes(std::set& setOfChildren, - const SMDS_MeshElement * element, - std::set& nodes); + void addChildrenWithNodes(std::set& setOfChildren, + const SMDS_MeshElement * element, + std::set& nodes); + + inline void adjustmyCellsCapacity(int ID) + { + assert(ID >= 0); + myElementIDFactory->adjustMaxId(ID); + if (ID >= myCells.size()) + myCells.resize(ID+SMDS_Mesh::chunkSize,0); + } + + inline void adjustBoundingBox(double x, double y, double z) + { + if (x > xmax) xmax = x; + else if (x < xmin) xmin = x; + if (y > ymax) ymax = y; + else if (y < ymin) ymin = y; + if (z > zmax) zmax = z; + else if (z < zmin) zmin = z; + } // Fields PRIVATE - + + //! index of this SMDS_mesh in the static vector _meshList + int myMeshId; + + //! actual nodes coordinates, cells definition and reverse connectivity are stored in a vtkUnstructuredGrid + SMDS_UnstructuredGrid* myGrid; + + //! Small objects like SMDS_MeshNode are allocated by chunks to limit memory costs of new + ObjectPool* myNodePool; + + //! Small objects like SMDS_VtkVolume are allocated by chunks to limit memory costs of new + ObjectPool* myVolumePool; + ObjectPool* myFacePool; + ObjectPool* myEdgePool; + ObjectPool* myBallPool; + + //! SMDS_MeshNodes refer to vtk nodes (vtk id = index in myNodes),store reference to this mesh, and sub-shape SetOfNodes myNodes; - SetOfEdges myEdges; - SetOfFaces myFaces; - SetOfVolumes myVolumes; + + //! SMDS_MeshCells refer to vtk cells (vtk id != index in myCells),store reference to this mesh, and sub-shape + SetOfCells myCells; + + //! for cells only: index = ID for SMDS users, value = ID in vtkUnstructuredGrid + //std::vector myCellIdSmdsToVtk; + + //! for cells only: index = ID in vtkUnstructuredGrid, value = ID for SMDS users + std::vector myCellIdVtkToSmds; + SMDS_Mesh * myParent; std::list myChildren; - SMDS_MeshElementIDFactory *myNodeIDFactory; + SMDS_MeshNodeIDFactory *myNodeIDFactory; SMDS_MeshElementIDFactory *myElementIDFactory; SMDS_MeshInfo myInfo; + //! use a counter to keep track of modifications + unsigned long myModifTime, myCompactTime; + + int myNodeMin; + int myNodeMax; + bool myHasConstructionEdges; bool myHasConstructionFaces; bool myHasInverseElements; + + //! any add, remove or change of node or cell + bool myModified; + + double xmin; + double xmax; + double ymin; + double ymax; + double zmin; + double zmax; }; diff --git a/src/SMDS/SMDS_Mesh0DElement.cxx b/src/SMDS/SMDS_Mesh0DElement.cxx new file mode 100644 index 000000000..a1a244c6d --- /dev/null +++ b/src/SMDS/SMDS_Mesh0DElement.cxx @@ -0,0 +1,147 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_Mesh0DElement.cxx +// Module : SMESH +// +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#include "SMDS_Mesh0DElement.hxx" +#include "SMDS_IteratorOfElements.hxx" +#include "SMDS_MeshNode.hxx" +#include "utilities.h" + +using namespace std; + +//======================================================================= +//function : SMDS_Mesh0DElement +//purpose : +//======================================================================= +SMDS_Mesh0DElement::SMDS_Mesh0DElement (const SMDS_MeshNode * node) +{ + MESSAGE("SMDS_Mesh0DElement " << GetID()); + myNode = node; +} + +//======================================================================= +//function : Print +//purpose : +//======================================================================= +void SMDS_Mesh0DElement::Print (ostream & OS) const +{ + OS << "0D Element <" << GetID() << "> : (" << myNode << ") " << endl; +} + +//======================================================================= +//function : NbNodes +//purpose : +//======================================================================= +int SMDS_Mesh0DElement::NbNodes() const +{ + return 1; +} + +//======================================================================= +//function : NbEdges +//purpose : +//======================================================================= +int SMDS_Mesh0DElement::NbEdges() const +{ + return 0; +} + +//======================================================================= +//function : GetType +//purpose : +//======================================================================= +SMDSAbs_ElementType SMDS_Mesh0DElement::GetType() const +{ + return SMDSAbs_0DElement; +} + +vtkIdType SMDS_Mesh0DElement::GetVtkType() const +{ + return VTK_VERTEX; +} + +//======================================================================= +//function : elementsIterator +//purpose : +//======================================================================= +class SMDS_Mesh0DElement_MyNodeIterator: public SMDS_ElemIterator +{ + const SMDS_MeshNode * myNode; + int myIndex; + public: + SMDS_Mesh0DElement_MyNodeIterator(const SMDS_MeshNode * node): + myNode(node),myIndex(0) {} + + bool more() + { + return myIndex < 1; + } + + const SMDS_MeshElement* next() + { + myIndex++; + if (myIndex == 1) + return myNode; + return NULL; + } +}; + +SMDS_ElemIteratorPtr SMDS_Mesh0DElement::elementsIterator (SMDSAbs_ElementType type) const +{ + switch(type) + { + case SMDSAbs_0DElement: + return SMDS_MeshElement::elementsIterator(SMDSAbs_0DElement); + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr(new SMDS_Mesh0DElement_MyNodeIterator(myNode)); + default: + return SMDS_ElemIteratorPtr + (new SMDS_IteratorOfElements + (this,type, SMDS_ElemIteratorPtr(new SMDS_Mesh0DElement_MyNodeIterator(myNode)))); + } +} + +/*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ +const SMDS_MeshNode* SMDS_Mesh0DElement::GetNode(const int ind) const +{ + if (ind == 0) + return myNode; + return NULL; +} + +//======================================================================= +//function : ChangeNode +//purpose : +//======================================================================= +bool SMDS_Mesh0DElement::ChangeNode (const SMDS_MeshNode * node) +{ + myNode = node; + return true; +} diff --git a/src/SMDS/SMDS_Mesh0DElement.hxx b/src/SMDS/SMDS_Mesh0DElement.hxx new file mode 100644 index 000000000..81578a816 --- /dev/null +++ b/src/SMDS/SMDS_Mesh0DElement.hxx @@ -0,0 +1,56 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_Mesh0DElement.hxx +// Module : SMESH +// +#ifndef _SMDS_Mesh0DElement_HeaderFile +#define _SMDS_Mesh0DElement_HeaderFile + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshCell.hxx" + +#include + +class SMDS_EXPORT SMDS_Mesh0DElement: public SMDS_MeshCell +{ + public: + SMDS_Mesh0DElement (const SMDS_MeshNode * node); + bool ChangeNode (const SMDS_MeshNode * node); + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) {return false;}; + virtual void Print (std::ostream & OS) const; + + virtual SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_EntityType GetEntityType() const {return SMDSEntity_0D;} + virtual SMDSAbs_GeometryType GetGeomType() const { return SMDSGeom_POINT; } + virtual const SMDS_MeshNode* GetNode (const int ind) const; + virtual int NbNodes() const; + virtual int NbEdges() const; + + protected: + virtual SMDS_ElemIteratorPtr elementsIterator (SMDSAbs_ElementType type) const; + + protected: + const SMDS_MeshNode* myNode; +}; + +#endif diff --git a/src/SMDS/SMDS_MeshCell.cxx b/src/SMDS/SMDS_MeshCell.cxx new file mode 100644 index 000000000..521c976ff --- /dev/null +++ b/src/SMDS/SMDS_MeshCell.cxx @@ -0,0 +1,428 @@ +// Copyright (C) 2010-2012 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 "SMDS_MeshCell.hxx" +#include "utilities.h" + +int SMDS_MeshCell::nbCells = 0; + +SMDS_MeshCell::SMDS_MeshCell() : + SMDS_MeshElement(-1) +{ + nbCells++; + myVtkID = -1; +} + +SMDS_MeshCell::~SMDS_MeshCell() +{ + nbCells--; +} +//================================================================================ +/*! + * \brief Return VTKCellType corresponding to SMDSAbs_EntityType + */ +//================================================================================ + +VTKCellType SMDS_MeshCell::toVtkType (SMDSAbs_EntityType smdsType) +{ + static std::vector< VTKCellType > vtkTypes; + if ( vtkTypes.empty() ) + { + vtkTypes.resize( SMDSEntity_Last+1, VTK_EMPTY_CELL ); + vtkTypes[ SMDSEntity_Node ] = VTK_VERTEX; + vtkTypes[ SMDSEntity_0D ] = VTK_VERTEX; + vtkTypes[ SMDSEntity_Edge ] = VTK_LINE; + vtkTypes[ SMDSEntity_Quad_Edge ] = VTK_QUADRATIC_EDGE; + vtkTypes[ SMDSEntity_Triangle ] = VTK_TRIANGLE; + vtkTypes[ SMDSEntity_Quad_Triangle ] = VTK_QUADRATIC_TRIANGLE; + vtkTypes[ SMDSEntity_Quadrangle ] = VTK_QUAD; + vtkTypes[ SMDSEntity_Quad_Quadrangle ] = VTK_QUADRATIC_QUAD; + vtkTypes[ SMDSEntity_BiQuad_Quadrangle ] = VTK_BIQUADRATIC_QUAD; + vtkTypes[ SMDSEntity_Polygon ] = VTK_POLYGON; + //vtkTypes[ SMDSEntity_Quad_Polygon ] = ; + vtkTypes[ SMDSEntity_Tetra ] = VTK_TETRA; + vtkTypes[ SMDSEntity_Quad_Tetra ] = VTK_QUADRATIC_TETRA; + vtkTypes[ SMDSEntity_Pyramid ] = VTK_PYRAMID; + vtkTypes[ SMDSEntity_Quad_Pyramid ] = VTK_QUADRATIC_PYRAMID; + vtkTypes[ SMDSEntity_Hexa ] = VTK_HEXAHEDRON; + vtkTypes[ SMDSEntity_Quad_Hexa ] = VTK_QUADRATIC_HEXAHEDRON; + vtkTypes[ SMDSEntity_TriQuad_Hexa ] = VTK_TRIQUADRATIC_HEXAHEDRON; + vtkTypes[ SMDSEntity_Penta ] = VTK_WEDGE; + vtkTypes[ SMDSEntity_Quad_Penta ] = VTK_QUADRATIC_WEDGE; + vtkTypes[ SMDSEntity_Hexagonal_Prism ] = VTK_HEXAGONAL_PRISM; + vtkTypes[ SMDSEntity_Polyhedra ] = VTK_POLYHEDRON; + //vtkTypes[ SMDSEntity_Quad_Polyhedra ] = ; + vtkTypes[ SMDSEntity_Ball ] = VTK_POLY_VERTEX; + } + return vtkTypes[ smdsType ]; +} + +//================================================================================ +/*! + * \brief Return indices to transform cell connectivity from SMDS to VTK + * Usage: vtkIDs[i] = smdsIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector< int >& SMDS_MeshCell::toVtkOrder(SMDSAbs_EntityType smdsType) +{ + static std::vector< std::vector< int > > toVtkInterlaces; + if ( toVtkInterlaces.empty() ) + { + toVtkInterlaces.resize( SMDSEntity_Last+1 ); + // { + // const int ids[] = {0}; + // toVtkInterlaces[SMDSEntity_0D].assign( &ids[0], &ids[0]+1 ); + // toVtkInterlaces[SMDSEntity_Node].assign( &ids[0], &ids[0]+1 ); + // } + // { + // const int ids[] = {0,1}; + // toVtkInterlaces[SMDSEntity_Edge].assign( &ids[0], &ids[0]+2 ); + // } + // { + // const int ids[] = {0,1,2}; + // toVtkInterlaces[SMDSEntity_Quad_Edge].assign( &ids[0], &ids[0]+3 ); + // } + // { + // const int ids[] = {0,1,2}; + // toVtkInterlaces[SMDSEntity_Triangle].assign( &ids[0], &ids[0]+3 ); + // } + // { + // const int ids[] = {0,1,2,3,4,5}; + // toVtkInterlaces[SMDSEntity_Quad_Triangle].assign( &ids[0], &ids[0]+6 ); + // } + // { + // const int ids[] = {0,1,2,3}; + // toVtkInterlaces[SMDSEntity_Quadrangle].assign( &ids[0], &ids[0]+4 ); + // } + // { + // const int ids[] = {0,1,2,3,4,5,6,7}; + // toVtkInterlaces[SMDSEntity_Quad_Quadrangle].assign( &ids[0], &ids[0]+8 ); + // } + // { + // const int ids[] = {0,1,2,3,4,5,6,7,8}; + // toVtkInterlaces[SMDSEntity_BiQuad_Quadrangle].assign( &ids[0], &ids[0]+9 ); + // } + { + const int ids[] = {0,2,1,3}; + toVtkInterlaces[SMDSEntity_Tetra].assign( &ids[0], &ids[0]+4 ); + } + { + const int ids[] = {0,2,1,3,6,5,4,7,9,8}; + toVtkInterlaces[SMDSEntity_Quad_Tetra].assign( &ids[0], &ids[0]+10 ); + } + { + const int ids[] = {0,3,2,1,4}; + toVtkInterlaces[SMDSEntity_Pyramid].assign( &ids[0], &ids[0]+5 ); + } + { + const int ids[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; + toVtkInterlaces[SMDSEntity_Quad_Pyramid].assign( &ids[0], &ids[0]+13 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5}; + toVtkInterlaces[SMDSEntity_Hexa].assign( &ids[0], &ids[0]+8 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; + toVtkInterlaces[SMDSEntity_Quad_Hexa].assign( &ids[0], &ids[0]+20 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17, 21,23,24,22,20,25,26}; + toVtkInterlaces[SMDSEntity_TriQuad_Hexa].assign( &ids[0], &ids[0]+27 ); + } + { + const int ids[] = {0,1,2,3,4,5}; + toVtkInterlaces[SMDSEntity_Penta].assign( &ids[0], &ids[0]+6 ); + } + { + const int ids[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14}; + toVtkInterlaces[SMDSEntity_Quad_Penta].assign( &ids[0], &ids[0]+15 ); + } + { + const int ids[] = {0,5,4,3,2,1,6,11,10,9,8,7}; + toVtkInterlaces[SMDSEntity_Hexagonal_Prism].assign( &ids[0], &ids[0]+12 ); + } + } + return toVtkInterlaces[smdsType]; +} + +//================================================================================ +/*! + * \brief Return indices to reverse an SMDS cell of given type + * Usage: reverseIDs[i] = forwardIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector& SMDS_MeshCell::reverseSmdsOrder(SMDSAbs_EntityType smdsType) +{ + static std::vector< std::vector< int > > reverseInterlaces; + if ( reverseInterlaces.empty() ) + { + reverseInterlaces.resize( SMDSEntity_Last+1 ); + { + const int ids[] = {0}; + reverseInterlaces[SMDSEntity_0D].assign( &ids[0], &ids[0]+1 ); + reverseInterlaces[SMDSEntity_Node].assign( &ids[0], &ids[0]+1 ); + reverseInterlaces[SMDSEntity_Ball].assign( &ids[0], &ids[0]+1 ); + } + { + const int ids[] = {1,0}; + reverseInterlaces[SMDSEntity_Edge].assign( &ids[0], &ids[0]+2 ); + } + { + const int ids[] = {1,0,2}; + reverseInterlaces[SMDSEntity_Quad_Edge].assign( &ids[0], &ids[0]+3 ); + } + { + const int ids[] = {0,2,1}; + reverseInterlaces[SMDSEntity_Triangle].assign( &ids[0], &ids[0]+3 ); + } + { + const int ids[] = {0,2,1,5,4,3}; + reverseInterlaces[SMDSEntity_Quad_Triangle].assign( &ids[0], &ids[0]+6 ); + } + { + const int ids[] = {0,3,2,1}; + reverseInterlaces[SMDSEntity_Quadrangle].assign( &ids[0], &ids[0]+4 ); + } + { + const int ids[] = {0,3,2,1,7,6,5,4}; + reverseInterlaces[SMDSEntity_Quad_Quadrangle].assign( &ids[0], &ids[0]+8 ); + } + { + const int ids[] = {0,3,2,1,7,6,5,4,8}; + reverseInterlaces[SMDSEntity_BiQuad_Quadrangle].assign( &ids[0], &ids[0]+9 ); + } + { + const int ids[] = {0,2,1,3}; + reverseInterlaces[SMDSEntity_Tetra].assign( &ids[0], &ids[0]+4 ); + } + { + const int ids[] = {0,2,1,3,6,5,4,7,9,8}; + reverseInterlaces[SMDSEntity_Quad_Tetra].assign( &ids[0], &ids[0]+10 ); + } + { + const int ids[] = {0,3,2,1,4}; + reverseInterlaces[SMDSEntity_Pyramid].assign( &ids[0], &ids[0]+5 ); + } + { + const int ids[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; + reverseInterlaces[SMDSEntity_Quad_Pyramid].assign( &ids[0], &ids[0]+13 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5}; + reverseInterlaces[SMDSEntity_Hexa].assign( &ids[0], &ids[0]+8 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; + reverseInterlaces[SMDSEntity_Quad_Hexa].assign( &ids[0], &ids[0]+20 ); + } + { + const int ids[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17, 20,24,23,22,21,25,26}; + reverseInterlaces[SMDSEntity_TriQuad_Hexa].assign( &ids[0], &ids[0]+27 ); + } + { + const int ids[] = {0,2,1,3,5,4}; + reverseInterlaces[SMDSEntity_Penta].assign( &ids[0], &ids[0]+6 ); + } + { + const int ids[] = {0,2,1,3,5,4, 8,7,6,11,10,9,12,14,13}; + reverseInterlaces[SMDSEntity_Quad_Penta].assign( &ids[0], &ids[0]+15 ); + } + { + const int ids[] = {0,5,4,3,2,1,6,11,10,9,8,7}; + reverseInterlaces[SMDSEntity_Hexagonal_Prism].assign( &ids[0], &ids[0]+12 ); + } + } + return reverseInterlaces[smdsType]; +} + +//================================================================================ +/*! + * \brief Return indices to set nodes of a quadratic 1D or 2D element in interlaced order + * Usage: interlacedIDs[i] = smdsIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector& SMDS_MeshCell::interlacedSmdsOrder(SMDSAbs_EntityType smdsType) +{ + static std::vector< std::vector< int > > interlace; + if ( interlace.empty() ) + { + interlace.resize( SMDSEntity_Last+1 ); + { + const int ids[] = {0,2,1}; + interlace[SMDSEntity_Quad_Edge].assign( &ids[0], &ids[0]+3 ); + } + { + const int ids[] = {0,3,1,4,2,5}; + interlace[SMDSEntity_Quad_Triangle].assign( &ids[0], &ids[0]+6 ); + } + { + const int ids[] = {0,4,1,5,2,6,3,7,8}; + interlace[SMDSEntity_Quad_Quadrangle].assign( &ids[0], &ids[0]+8 ); + interlace[SMDSEntity_BiQuad_Quadrangle].assign( &ids[0], &ids[0]+9 ); + } + } + return interlace[smdsType]; +} + +//================================================================================ +/*! + * \brief Return SMDSAbs_EntityType corresponding to VTKCellType + */ +//================================================================================ + +SMDSAbs_EntityType SMDS_MeshCell::toSmdsType(VTKCellType vtkType) +{ + static std::vector< SMDSAbs_EntityType > smdsTypes; + if ( smdsTypes.empty() ) + { + smdsTypes.resize( VTK_NUMBER_OF_CELL_TYPES, SMDSEntity_Last ); + for ( int iSMDS = 0; iSMDS < SMDSEntity_Last; ++iSMDS ) + smdsTypes[ toVtkType( SMDSAbs_EntityType( iSMDS ))] = SMDSAbs_EntityType( iSMDS ); + } + return smdsTypes[ vtkType ]; +} + +//================================================================================ +/*! + * \brief Return SMDSAbs_ElementType by SMDSAbs_GeometryType + */ +//================================================================================ + +SMDSAbs_ElementType SMDS_MeshCell::toSmdsType(SMDSAbs_GeometryType geomType) +{ + switch ( geomType ) { + case SMDSGeom_POINT: return SMDSAbs_0DElement; + + case SMDSGeom_EDGE: return SMDSAbs_Edge; + + case SMDSGeom_TRIANGLE: + case SMDSGeom_QUADRANGLE: + case SMDSGeom_POLYGON: return SMDSAbs_Face; + + case SMDSGeom_TETRA: + case SMDSGeom_PYRAMID: + case SMDSGeom_HEXA: + case SMDSGeom_PENTA: + case SMDSGeom_HEXAGONAL_PRISM: + case SMDSGeom_POLYHEDRA: return SMDSAbs_Volume; + + case SMDSGeom_BALL: return SMDSAbs_Ball; + + case SMDSGeom_NONE: ; + } + return SMDSAbs_All; +} + +//================================================================================ +/*! + * \brief Return SMDSAbs_ElementType by SMDSAbs_EntityType + */ +//================================================================================ + +SMDSAbs_ElementType SMDS_MeshCell::toSmdsType(SMDSAbs_EntityType entityType) +{ + switch ( entityType ) { + case SMDSEntity_Node: return SMDSAbs_Node; + + case SMDSEntity_0D: return SMDSAbs_0DElement; + + case SMDSEntity_Edge: + case SMDSEntity_Quad_Edge: return SMDSAbs_Edge; + + case SMDSEntity_Triangle: + case SMDSEntity_Quad_Triangle: + case SMDSEntity_Quadrangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + case SMDSEntity_Polygon: + case SMDSEntity_Quad_Polygon: return SMDSAbs_Face; + + case SMDSEntity_Tetra: + case SMDSEntity_Quad_Tetra: + case SMDSEntity_Pyramid: + case SMDSEntity_Quad_Pyramid: + case SMDSEntity_Hexa: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + case SMDSEntity_Penta: + case SMDSEntity_Quad_Penta: + case SMDSEntity_Hexagonal_Prism: + case SMDSEntity_Polyhedra: + case SMDSEntity_Quad_Polyhedra: return SMDSAbs_Volume; + + case SMDSEntity_Ball: return SMDSAbs_Ball; + + case SMDSEntity_Last:; + } + return SMDSAbs_All; +} + + +//================================================================================ +/*! + * \brief Return indices to transform cell connectivity from VTK to SMDS + * Usage: smdsIDs[i] = vtkIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector& SMDS_MeshCell::fromVtkOrder(SMDSAbs_EntityType smdsType) +{ + static std::vector< std::vector > fromVtkInterlaces; + if ( fromVtkInterlaces.empty() ) + { + fromVtkInterlaces.resize( SMDSEntity_Last+1 ); + for ( int iSMDS = 0; iSMDS < SMDSEntity_Last; ++iSMDS ) + { + const std::vector & toVtk = toVtkOrder( SMDSAbs_EntityType( iSMDS )); + std::vector & toSmds = fromVtkInterlaces[ iSMDS ]; + toSmds.resize( toVtk.size() ); + for ( size_t i = 0; i < toVtk.size(); ++i ) + toSmds[ toVtk[i] ] = i; + } + } + return fromVtkInterlaces[ smdsType ]; +} + +//================================================================================ +/*! + * \brief Return indices to transform cell connectivity from SMDS to VTK + * Usage: vtkIDs[i] = smdsIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector& SMDS_MeshCell::toVtkOrder(VTKCellType vtkType) +{ + return toVtkOrder( toSmdsType( vtkType )); +} + +//================================================================================ +/*! + * \brief Return indices to transform cell connectivity from VTK to SMDS + * Usage: smdsIDs[i] = vtkIDs[ indices[ i ]] + */ +//================================================================================ + +const std::vector& SMDS_MeshCell::fromVtkOrder(VTKCellType vtkType) +{ + return fromVtkOrder( toSmdsType( vtkType )); +} diff --git a/src/SMDS/SMDS_MeshCell.hxx b/src/SMDS/SMDS_MeshCell.hxx new file mode 100644 index 000000000..ca006b5ff --- /dev/null +++ b/src/SMDS/SMDS_MeshCell.hxx @@ -0,0 +1,72 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _SMDS_MESHCELL_HXX_ +#define _SMDS_MESHCELL_HXX_ + +#include "SMDS_MeshElement.hxx" + +/*! + * \brief Base class for all cells + */ + +class SMDS_EXPORT SMDS_MeshCell: public SMDS_MeshElement +{ +public: + SMDS_MeshCell(); + virtual ~SMDS_MeshCell(); + + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes)= 0; + virtual bool vtkOrder(const SMDS_MeshNode* nodes[], const int nbNodes) {return true; } + + static VTKCellType toVtkType (SMDSAbs_EntityType vtkType); + static SMDSAbs_EntityType toSmdsType(VTKCellType vtkType); + static SMDSAbs_ElementType toSmdsType(SMDSAbs_GeometryType geomType); + static SMDSAbs_ElementType toSmdsType(SMDSAbs_EntityType entityType); + + static const std::vector& toVtkOrder(VTKCellType vtkType); + static const std::vector& toVtkOrder(SMDSAbs_EntityType smdsType); + static const std::vector& fromVtkOrder(VTKCellType vtkType); + static const std::vector& fromVtkOrder(SMDSAbs_EntityType smdsType); + + static const std::vector& reverseSmdsOrder(SMDSAbs_EntityType smdsType); + static const std::vector& interlacedSmdsOrder(SMDSAbs_EntityType smdsType); + + template< class VECT > + static void applyInterlace( const std::vector& interlace, VECT & data) + { + if ( interlace.empty() ) return; + VECT tmpData( data.size() ); + for ( size_t i = 0; i < data.size(); ++i ) + tmpData[i] = data[ interlace[i] ]; + data.swap( tmpData ); + } + + static int nbCells; + +protected: + inline void exchange(const SMDS_MeshNode* nodes[],int a, int b) + { + const SMDS_MeshNode* noda = nodes[a]; + nodes[a] = nodes[b]; + nodes[b] = noda; + } +}; + +#endif diff --git a/src/SMDS/SMDS_MeshEdge.cxx b/src/SMDS/SMDS_MeshEdge.cxx index 9d17cd439..859061842 100644 --- a/src/SMDS/SMDS_MeshEdge.cxx +++ b/src/SMDS/SMDS_MeshEdge.cxx @@ -1,161 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2010-2012 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 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. // -// 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 // -// 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 // -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// -// SMESH SMDS : implementaion of Salome mesh data structure -// File : SMDS_MeshEdge.cxx -// Author : Jean-Michel BOULCOURT -// Module : SMESH -// -#ifdef _MSC_VER -#pragma warning(disable:4786) -#endif #include "SMDS_MeshEdge.hxx" -#include "SMDS_IteratorOfElements.hxx" -#include "SMDS_MeshNode.hxx" - -using namespace std; - -//======================================================================= -//function : SMDS_MeshEdge -//purpose : -//======================================================================= - -SMDS_MeshEdge::SMDS_MeshEdge(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2) -{ - myNodes[0]=node1; - myNodes[1]=node2; -} - -//======================================================================= -//function : Print -//purpose : -//======================================================================= - -void SMDS_MeshEdge::Print(ostream & OS) const -{ - OS << "edge <" << GetID() << "> : (" << myNodes[0] << " , " << myNodes[1] << - ") " << endl; -} - -int SMDS_MeshEdge::NbNodes() const -{ - return 2; -} - -int SMDS_MeshEdge::NbEdges() const -{ - return 1; -} SMDSAbs_ElementType SMDS_MeshEdge::GetType() const { - return SMDSAbs_Edge; + return SMDSAbs_Edge; } -class SMDS_MeshEdge_MyNodeIterator:public SMDS_ElemIterator +vtkIdType SMDS_MeshEdge::GetVtkType() const { - const SMDS_MeshNode *const* myNodes; - int myIndex; - public: - SMDS_MeshEdge_MyNodeIterator(const SMDS_MeshNode * const* nodes): - myNodes(nodes),myIndex(0) {} - - bool more() - { - return myIndex<2; - } - - const SMDS_MeshElement* next() - { - myIndex++; - return myNodes[myIndex-1]; - } -}; - -SMDS_ElemIteratorPtr SMDS_MeshEdge:: - elementsIterator(SMDSAbs_ElementType type) const -{ - switch(type) - { - case SMDSAbs_Edge: - return SMDS_MeshElement::elementsIterator(SMDSAbs_Edge); - case SMDSAbs_Node: - return SMDS_ElemIteratorPtr(new SMDS_MeshEdge_MyNodeIterator(myNodes)); - default: - return SMDS_ElemIteratorPtr - (new SMDS_IteratorOfElements - (this,type, SMDS_ElemIteratorPtr(new SMDS_MeshEdge_MyNodeIterator(myNodes)))); - } -} - -bool operator<(const SMDS_MeshEdge & e1, const SMDS_MeshEdge & e2) -{ - int id11=e1.myNodes[0]->GetID(); - int id21=e2.myNodes[0]->GetID(); - int id12=e1.myNodes[1]->GetID(); - int id22=e2.myNodes[1]->GetID(); - int tmp; - - if(id11>=id12) - { - tmp=id11; - id11=id12; - id12=tmp; - } - if(id21>=id22) - { - tmp=id21; - id21=id22; - id22=tmp; - } - - if(id11 +#include "SMDS_MeshCell.hxx" -class SMDS_EXPORT SMDS_MeshEdge:public SMDS_MeshElement +class SMDS_EXPORT SMDS_MeshEdge: public SMDS_MeshCell { - - public: - SMDS_MeshEdge(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2); - bool ChangeNodes(const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2); - void Print(std::ostream & OS) const; - - SMDSAbs_ElementType GetType() const; - int NbNodes() const; - int NbEdges() const; - friend bool operator<(const SMDS_MeshEdge& e1, const SMDS_MeshEdge& e2); - - /*! - * \brief Return node by its index - * \param ind - node index - * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range - */ - virtual const SMDS_MeshNode* GetNode(const int ind) const; - - protected: - SMDS_ElemIteratorPtr - elementsIterator(SMDSAbs_ElementType type) const; - - protected: - const SMDS_MeshNode* myNodes[3]; - + + public: + virtual SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_GeometryType GetGeomType() const { return SMDSGeom_EDGE; } }; #endif diff --git a/src/SMDS/SMDS_MeshElement.cxx b/src/SMDS/SMDS_MeshElement.cxx index 646f92d10..1f422d382 100644 --- a/src/SMDS/SMDS_MeshElement.cxx +++ b/src/SMDS/SMDS_MeshElement.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -34,19 +35,33 @@ using namespace std; -SMDS_MeshElement::SMDS_MeshElement(int ID):myID(ID) +SMDS_MeshElement::SMDS_MeshElement(int ID) +{ + init(ID); +} + +SMDS_MeshElement::SMDS_MeshElement(int id, ShortType meshId, LongType shapeId) +{ + init(id, meshId, shapeId); +} + +void SMDS_MeshElement::init(int id, ShortType meshId, LongType shapeId ) { + myID = id; + myMeshId = meshId; + myShapeId = shapeId; + myIdInShape = -1; } void SMDS_MeshElement::Print(ostream & OS) const { - OS << "dump of mesh element" << endl; + OS << "dump of mesh element" << endl; } ostream & operator <<(ostream & OS, const SMDS_MeshElement * ME) { - ME->Print(OS); - return OS; + ME->Print(OS); + return OS; } /////////////////////////////////////////////////////////////////////////////// @@ -55,7 +70,7 @@ ostream & operator <<(ostream & OS, const SMDS_MeshElement * ME) /////////////////////////////////////////////////////////////////////////////// SMDS_ElemIteratorPtr SMDS_MeshElement::nodesIterator() const { - return elementsIterator(SMDSAbs_Node); + return elementsIterator(SMDSAbs_Node); } /////////////////////////////////////////////////////////////////////////////// @@ -64,7 +79,7 @@ SMDS_ElemIteratorPtr SMDS_MeshElement::nodesIterator() const /////////////////////////////////////////////////////////////////////////////// SMDS_ElemIteratorPtr SMDS_MeshElement::edgesIterator() const { - return elementsIterator(SMDSAbs_Edge); + return elementsIterator(SMDSAbs_Edge); } /////////////////////////////////////////////////////////////////////////////// @@ -73,7 +88,7 @@ SMDS_ElemIteratorPtr SMDS_MeshElement::edgesIterator() const /////////////////////////////////////////////////////////////////////////////// SMDS_ElemIteratorPtr SMDS_MeshElement::facesIterator() const { - return elementsIterator(SMDSAbs_Face); + return elementsIterator(SMDSAbs_Face); } /////////////////////////////////////////////////////////////////////////////// @@ -81,14 +96,14 @@ SMDS_ElemIteratorPtr SMDS_MeshElement::facesIterator() const /////////////////////////////////////////////////////////////////////////////// int SMDS_MeshElement::NbNodes() const { - int nbnodes=0; - SMDS_ElemIteratorPtr it=nodesIterator(); - while(it->more()) - { - it->next(); - nbnodes++; - } - return nbnodes; + int nbnodes=0; + SMDS_ElemIteratorPtr it=nodesIterator(); + while(it->more()) + { + it->next(); + nbnodes++; + } + return nbnodes; } /////////////////////////////////////////////////////////////////////////////// @@ -96,14 +111,14 @@ int SMDS_MeshElement::NbNodes() const /////////////////////////////////////////////////////////////////////////////// int SMDS_MeshElement::NbEdges() const { - int nbedges=0; - SMDS_ElemIteratorPtr it=edgesIterator(); - while(it->more()) - { - it->next(); - nbedges++; - } - return nbedges; + int nbedges=0; + SMDS_ElemIteratorPtr it=edgesIterator(); + while(it->more()) + { + it->next(); + nbedges++; + } + return nbedges; } /////////////////////////////////////////////////////////////////////////////// @@ -111,14 +126,14 @@ int SMDS_MeshElement::NbEdges() const /////////////////////////////////////////////////////////////////////////////// int SMDS_MeshElement::NbFaces() const { - int nbfaces=0; - SMDS_ElemIteratorPtr it=facesIterator(); - while(it->more()) - { - it->next(); - nbfaces++; - } - return nbfaces; + int nbfaces=0; + SMDS_ElemIteratorPtr it=facesIterator(); + while(it->more()) + { + it->next(); + nbfaces++; + } + return nbfaces; } /////////////////////////////////////////////////////////////////////////////// @@ -142,56 +157,63 @@ class SMDS_MeshElement_MyIterator:public SMDS_ElemIterator const SMDS_MeshElement* next() { myMore=false; - return myElement; - } + return myElement; + } }; + SMDS_ElemIteratorPtr SMDS_MeshElement:: - elementsIterator(SMDSAbs_ElementType type) const + elementsIterator(SMDSAbs_ElementType type) const { - /** @todo Check that iterator in the child classes return elements - in the same order for each different implementation (i.e: SMDS_VolumeOfNodes - and SMDS_VolumeOfFaces */ - - if(type==GetType()) + /** @todo Check that iterator in the child classes return elements + in the same order for each different implementation (i.e: SMDS_VolumeOfNodes + and SMDS_VolumeOfFaces */ + + if(type==GetType()) return SMDS_ElemIteratorPtr(new SMDS_MeshElement_MyIterator(this)); - else - { + else + { MESSAGE("Iterator not implemented"); return SMDS_ElemIteratorPtr((SMDS_ElemIterator*)NULL); - } + } } -/////////////////////////////////////////////////////////////////////////////// -///Return the ID of the element -/////////////////////////////////////////////////////////////////////////////// -int SMDS_MeshElement::GetID() const +//! virtual, redefined in vtkEdge, vtkFace and vtkVolume classes +SMDS_ElemIteratorPtr SMDS_MeshElement::nodesIteratorToUNV() const { - return myID; + MESSAGE("Iterator not implemented"); + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); +} + +//! virtual, redefined in vtkEdge, vtkFace and vtkVolume classes +SMDS_ElemIteratorPtr SMDS_MeshElement::interlacedNodesElemIterator() const +{ + MESSAGE("Iterator not implemented"); + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); } bool operator<(const SMDS_MeshElement& e1, const SMDS_MeshElement& e2) { - if(e1.GetType()!=e2.GetType()) return false; - switch(e1.GetType()) - { - case SMDSAbs_Node: - return static_cast(e1) < - static_cast(e2); + if(e1.GetType()!=e2.GetType()) return false; + switch(e1.GetType()) + { + case SMDSAbs_Node: + return static_cast(e1) < + static_cast(e2); - case SMDSAbs_Edge: - return static_cast(e1) < - static_cast(e2); + case SMDSAbs_Edge: + return static_cast(e1) < + static_cast(e2); - case SMDSAbs_Face: - return static_cast(e1) < - static_cast(e2); + case SMDSAbs_Face: + return static_cast(e1) < + static_cast(e2); - case SMDSAbs_Volume: - return static_cast(e1) < - static_cast(e2); + case SMDSAbs_Volume: + return static_cast(e1) < + static_cast(e2); - default : MESSAGE("Internal Error"); - } + default : MESSAGE("Internal Error"); + } return false; } @@ -202,12 +224,13 @@ bool SMDS_MeshElement::IsValidIndex(const int ind) const const SMDS_MeshNode* SMDS_MeshElement::GetNode(const int ind) const { - SMDS_ElemIteratorPtr it = nodesIterator(); - int i = 0, index = WrappedIndex( ind ); - while ( index != i++ ) - it->next(); - if ( it->more() ) - return static_cast (it->next()); + if ( ind >= 0 ) { + SMDS_ElemIteratorPtr it = nodesIterator(); + for ( int i = 0; i < ind; ++i ) + it->next(); + if ( it->more() ) + return static_cast (it->next()); + } return 0; } @@ -221,6 +244,17 @@ bool SMDS_MeshElement::IsMediumNode(const SMDS_MeshNode* node) const return false; } +//================================================================================ +/*! + * \brief Return number of nodes excluding medium ones + */ +//================================================================================ + +int SMDS_MeshElement::NbCornerNodes() const +{ + return IsQuadratic() ? NbNodes() - NbEdges() : NbNodes(); +} + //================================================================================ /*! * \brief Check if a node belongs to the element diff --git a/src/SMDS/SMDS_MeshElement.hxx b/src/SMDS/SMDS_MeshElement.hxx index 2716456d3..918ebb668 100644 --- a/src/SMDS/SMDS_MeshElement.hxx +++ b/src/SMDS/SMDS_MeshElement.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshElement.hxx // Module : SMESH @@ -27,22 +28,35 @@ #define _SMDS_MeshElement_HeaderFile #include "SMESH_SMDS.hxx" - + #include "SMDSAbs_ElementType.hxx" #include "SMDS_MeshObject.hxx" #include "SMDS_ElemIterator.hxx" #include "SMDS_MeshElementIDFactory.hxx" +#include "SMDS_StdIterator.hxx" #include #include +#include +#include + +//typedef unsigned short UShortType; +typedef short ShortType; +typedef int LongType; + class SMDS_MeshNode; class SMDS_MeshEdge; -class SMDS_MeshFace; +class SMDS_MeshFace; +class SMDS_Mesh; + +// ============================================================ +/*! + * \brief Base class for elements + */ +// ============================================================ + -/////////////////////////////////////////////////////////////////////////////// -/// Base class for elements -/////////////////////////////////////////////////////////////////////////////// class SMDS_EXPORT SMDS_MeshElement:public SMDS_MeshObject { public: @@ -51,25 +65,47 @@ public: SMDS_ElemIteratorPtr edgesIterator() const; SMDS_ElemIteratorPtr facesIterator() const; virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; + virtual SMDS_ElemIteratorPtr nodesIteratorToUNV() const; + virtual SMDS_ElemIteratorPtr interlacedNodesElemIterator() const; + + // std-like iteration on nodes + typedef SMDS_StdIterator< const SMDS_MeshNode*, SMDS_ElemIteratorPtr > iterator; + iterator begin_nodes() const { return iterator( nodesIterator() ); } + iterator end_nodes() const { return iterator(); } virtual int NbNodes() const; virtual int NbEdges() const; virtual int NbFaces() const; - int GetID() const; + inline int GetID() const { return myID; }; ///Return the type of the current element virtual SMDSAbs_ElementType GetType() const = 0; - virtual bool IsPoly() const { return false; }; + virtual SMDSAbs_EntityType GetEntityType() const = 0; + virtual SMDSAbs_GeometryType GetGeomType() const = 0; + virtual vtkIdType GetVtkType() const = 0; + virtual bool IsPoly() const { return false; } virtual bool IsQuadratic() const; virtual bool IsMediumNode(const SMDS_MeshNode* node) const; + virtual int NbCornerNodes() const; friend SMDS_EXPORT std::ostream & operator <<(std::ostream & OS, const SMDS_MeshElement *); - friend SMDS_EXPORT bool SMDS_MeshElementIDFactory::BindID(int ID,SMDS_MeshElement*elem); + friend SMDS_EXPORT bool SMDS_MeshElementIDFactory::BindID(int ID,SMDS_MeshElement* elem); + friend class SMDS_Mesh; + friend class SMESHDS_Mesh; + friend class SMESHDS_SubMesh; + friend class SMDS_MeshElementIDFactory; // =========================== // Access to nodes by index // =========================== + /*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ + virtual const SMDS_MeshNode* GetNode(const int ind) const; + /*! * \brief Return node by its index * \param ind - node index @@ -77,7 +113,7 @@ public: * * Index is wrapped if it is out of a valid range */ - virtual const SMDS_MeshNode* GetNode(const int ind) const; + const SMDS_MeshNode* GetNodeWrap(const int ind) const { return GetNode( WrappedIndex( ind )); } /*! * \brief Return true if index of node is valid (0 <= ind < NbNodes()) @@ -104,12 +140,65 @@ public: */ int GetNodeIndex( const SMDS_MeshNode* node ) const; + inline ShortType getMeshId() const { return myMeshId; } + inline LongType getshapeId() const { return myShapeId; } + inline int getIdInShape() const { return myIdInShape; } + inline int getVtkId() const { return myVtkID; } + + /*! + * \brief Filters of elements, to be used with SMDS_SetIterator + */ + struct TypeFilter + { + SMDSAbs_ElementType _type; + TypeFilter( SMDSAbs_ElementType t = SMDSAbs_NbElementTypes ):_type(t) {} + bool operator()(const SMDS_MeshElement* e) const { return e && e->GetType() == _type; } + }; + struct EntityFilter + { + SMDSAbs_EntityType _type; + EntityFilter( SMDSAbs_EntityType t = SMDSEntity_Last ):_type(t) {} + bool operator()(const SMDS_MeshElement* e) const { return e && e->GetEntityType() == _type; } + }; + struct GeomFilter + { + SMDSAbs_GeometryType _type; + GeomFilter( SMDSAbs_GeometryType t = SMDSGeom_NONE ):_type(t) {} + bool operator()(const SMDS_MeshElement* e) const { return e && e->GetGeomType() == _type; } + }; + protected: + inline void setId(int id) {myID = id; } + inline void setShapeId(LongType shapeId) {myShapeId = shapeId; } + inline void setIdInShape(int id) { myIdInShape = id; } + inline void setVtkId(int vtkId) { myVtkID = vtkId; } SMDS_MeshElement(int ID=-1); + SMDS_MeshElement(int id, ShortType meshId, LongType shapeId = 0); + virtual void init(int id = -1, ShortType meshId = -1, LongType shapeId = 0); virtual void Print(std::ostream & OS) const; -private: + //! Element index in vector SMDS_Mesh::myNodes or SMDS_Mesh::myCells int myID; + //! index in vtkUnstructuredGrid + int myVtkID; + //! SMDS_Mesh identification in SMESH + ShortType myMeshId; + //! SubShape and SubMesh identification in SMESHDS + LongType myShapeId; + //! Element index in SMESHDS_SubMesh vector + int myIdInShape; +}; + + +// ============================================================ +/*! + * \brief Comparator of elements by ID for usage in std containers + */ +// ============================================================ + +struct TIDCompare { + bool operator () (const SMDS_MeshElement* e1, const SMDS_MeshElement* e2) const + { return e1->GetID() < e2->GetID(); } }; #endif diff --git a/src/SMDS/SMDS_MeshElementIDFactory.cxx b/src/SMDS/SMDS_MeshElementIDFactory.cxx index 7ecfba5b0..204d7215d 100644 --- a/src/SMDS/SMDS_MeshElementIDFactory.cxx +++ b/src/SMDS/SMDS_MeshElementIDFactory.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshElementIDFactory.cxx // Author : Jean-Michel BOULCOURT @@ -30,6 +31,14 @@ #include "SMDS_MeshElementIDFactory.hxx" #include "SMDS_MeshElement.hxx" +#include "SMDS_Mesh.hxx" + +#include "utilities.h" + +#include "SMDS_UnstructuredGrid.hxx" +#include + +#include using namespace std; @@ -38,23 +47,46 @@ using namespace std; //purpose : //======================================================================= SMDS_MeshElementIDFactory::SMDS_MeshElementIDFactory(): - SMDS_MeshIDFactory(), - myMin(0), myMax(0) + SMDS_MeshNodeIDFactory() +{ +} + +int SMDS_MeshElementIDFactory::SetInVtkGrid(SMDS_MeshElement * elem) { + // --- retrieve nodes ID + + SMDS_MeshCell *cell = dynamic_cast(elem); + assert(cell); + vector nodeIds; + SMDS_ElemIteratorPtr it = elem->nodesIterator(); + while(it->more()) + { + int nodeId = (static_cast(it->next()))->getVtkId(); + MESSAGE(" node in cell " << cell->getVtkId() << " : " << nodeId) + nodeIds.push_back(nodeId); + } + + // --- insert cell in vtkUnstructuredGrid + + vtkUnstructuredGrid * grid = myMesh->getGrid(); + //int locType = elem->GetType(); + int typ = VTK_VERTEX;//GetVtkCellType(locType); + int cellId = grid->InsertNextLinkedCell(typ, nodeIds.size(), &nodeIds[0]); + cell->setVtkId(cellId); + //MESSAGE("SMDS_MeshElementIDFactory::SetInVtkGrid " << cellId); + return cellId; } //======================================================================= //function : BindID //purpose : //======================================================================= + bool SMDS_MeshElementIDFactory::BindID(int ID, SMDS_MeshElement * elem) { - if (myIDElements.IsBound(ID)) - return false; - myIDElements.Bind(ID,elem); - elem->myID=ID; - updateMinMax (ID); - return true; + MESSAGE("SMDS_MeshElementIDFactory::BindID " << ID); + SetInVtkGrid(elem); + return myMesh->registerElement(ID, elem); } //======================================================================= @@ -63,22 +95,23 @@ bool SMDS_MeshElementIDFactory::BindID(int ID, SMDS_MeshElement * elem) //======================================================================= SMDS_MeshElement* SMDS_MeshElementIDFactory::MeshElement(int ID) { - if (!myIDElements.IsBound(ID)) + if ((ID<1) || (ID>=myMesh->myCells.size())) return NULL; - return myIDElements.Find(ID); + const SMDS_MeshElement* elem = GetMesh()->FindElement(ID); + return (SMDS_MeshElement*)(elem); } - //======================================================================= //function : GetFreeID //purpose : //======================================================================= + int SMDS_MeshElementIDFactory::GetFreeID() { int ID; do { ID = SMDS_MeshIDFactory::GetFreeID(); - } while (myIDElements.IsBound(ID)); + } while ( MeshElement( ID )); return ID; } @@ -86,38 +119,25 @@ int SMDS_MeshElementIDFactory::GetFreeID() //function : ReleaseID //purpose : //======================================================================= -void SMDS_MeshElementIDFactory::ReleaseID(const int ID) +void SMDS_MeshElementIDFactory::ReleaseID(int ID, int vtkId) { - myIDElements.UnBind(ID); + if (ID < 1) // TODO check case ID == O + { + MESSAGE("~~~~~~~~~~~~~~ SMDS_MeshElementIDFactory::ReleaseID ID = " << ID); + return; + } + //MESSAGE("~~~~~~~~~~~~~~ SMDS_MeshElementIDFactory::ReleaseID smdsId vtkId " << ID << " " << vtkId); + if (vtkId >= 0) + { + assert(vtkId < myMesh->myCellIdVtkToSmds.size()); + myMesh->myCellIdVtkToSmds[vtkId] = -1; + myMesh->setMyModified(); + } SMDS_MeshIDFactory::ReleaseID(ID); if (ID == myMax) myMax = 0; if (ID == myMin) - myMin = 0; -} - -//======================================================================= -//function : GetMaxID -//purpose : -//======================================================================= - -int SMDS_MeshElementIDFactory::GetMaxID() const -{ - if (myMax == 0) - updateMinMax(); - return myMax; -} - -//======================================================================= -//function : GetMinID -//purpose : -//======================================================================= - -int SMDS_MeshElementIDFactory::GetMinID() const -{ - if (myMin == 0) - updateMinMax(); - return myMin; + myMax = 0; } //======================================================================= @@ -127,12 +147,20 @@ int SMDS_MeshElementIDFactory::GetMinID() const void SMDS_MeshElementIDFactory::updateMinMax() const { - myMin = IntegerLast(); + myMin = INT_MAX; myMax = 0; - SMDS_IdElementMap::Iterator it(myIDElements); - for (; it.More(); it.Next()) - updateMinMax (it.Key()); - if (myMin == IntegerLast()) + for (int i = 0; i < myMesh->myCells.size(); i++) + { + if (myMesh->myCells[i]) + { + int id = myMesh->myCells[i]->GetID(); + if (id > myMax) + myMax = id; + if (id < myMin) + myMin = id; + } + } + if (myMin == INT_MAX) myMin = 0; } @@ -141,35 +169,15 @@ void SMDS_MeshElementIDFactory::updateMinMax() const //purpose : Return an iterator on elements of the factory //======================================================================= -class SMDS_Fact_MyElemIterator:public SMDS_ElemIterator -{ - SMDS_IdElementMap::Iterator myIterator; - public: - SMDS_Fact_MyElemIterator(const SMDS_IdElementMap& s):myIterator(s) - {} - - bool more() - { - return myIterator.More() != Standard_False; - } - - const SMDS_MeshElement* next() - { - const SMDS_MeshElement* current = myIterator.Value(); - myIterator.Next(); - return current; - } -}; - SMDS_ElemIteratorPtr SMDS_MeshElementIDFactory::elementsIterator() const { - return SMDS_ElemIteratorPtr - (new SMDS_Fact_MyElemIterator(myIDElements)); + return myMesh->elementsIterator(SMDSAbs_All); } void SMDS_MeshElementIDFactory::Clear() { - myIDElements.Clear(); + //myMesh->myCellIdSmdsToVtk.clear(); + myMesh->myCellIdVtkToSmds.clear(); myMin = myMax = 0; SMDS_MeshIDFactory::Clear(); } diff --git a/src/SMDS/SMDS_MeshElementIDFactory.hxx b/src/SMDS/SMDS_MeshElementIDFactory.hxx index a574def92..793ff0b32 100644 --- a/src/SMDS/SMDS_MeshElementIDFactory.hxx +++ b/src/SMDS/SMDS_MeshElementIDFactory.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshElementIDFactory.hxx // Module : SMESH @@ -28,28 +29,28 @@ #include "SMESH_SMDS.hxx" -#include "SMDS_MeshIDFactory.hxx" -#include "SMDS_ElemIterator.hxx" +#include "SMDS_MeshNodeIDFactory.hxx" -#include +#include class SMDS_MeshElement; +class SMDS_Mesh; -typedef NCollection_DataMap SMDS_IdElementMap; - -class SMDS_EXPORT SMDS_MeshElementIDFactory:public SMDS_MeshIDFactory +class SMDS_EXPORT SMDS_MeshElementIDFactory:public SMDS_MeshNodeIDFactory { public: + friend class SMDS_Mesh; + SMDS_MeshElementIDFactory(); bool BindID(int ID, SMDS_MeshElement * elem); + int SetInVtkGrid(SMDS_MeshElement * elem); SMDS_MeshElement * MeshElement(int ID); virtual int GetFreeID(); - virtual void ReleaseID(int ID); - int GetMaxID() const; - int GetMinID() const; + virtual void ReleaseID(int ID, int vtkId = -1); SMDS_ElemIteratorPtr elementsIterator() const; virtual void Clear(); -private: + +protected: void updateMinMax() const; void updateMinMax(int id) const { @@ -57,9 +58,6 @@ private: if (id < myMin) myMin = id; } - SMDS_IdElementMap myIDElements; - mutable int myMin, myMax; - }; #endif diff --git a/src/SMDS/SMDS_MeshFace.cxx b/src/SMDS/SMDS_MeshFace.cxx index a4a19f03f..a3e4cf521 100644 --- a/src/SMDS/SMDS_MeshFace.cxx +++ b/src/SMDS/SMDS_MeshFace.cxx @@ -1,29 +1,35 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #include "SMDS_MeshFace.hxx" SMDSAbs_ElementType SMDS_MeshFace::GetType() const { - return SMDSAbs_Face; + return SMDSAbs_Face; +} + +vtkIdType SMDS_MeshFace::GetVtkType() const +{ + return VTK_POLY_LINE; // --- must be reimplemented in derived classes } diff --git a/src/SMDS/SMDS_MeshFace.hxx b/src/SMDS/SMDS_MeshFace.hxx index a633e4ecf..15e06572a 100644 --- a/src/SMDS/SMDS_MeshFace.hxx +++ b/src/SMDS/SMDS_MeshFace.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshFace.hxx // Module : SMESH @@ -28,12 +29,13 @@ #include "SMESH_SMDS.hxx" -#include "SMDS_MeshElement.hxx" +#include "SMDS_MeshCell.hxx" -class SMDS_EXPORT SMDS_MeshFace:public SMDS_MeshElement +class SMDS_EXPORT SMDS_MeshFace:public SMDS_MeshCell { public: - SMDSAbs_ElementType GetType() const; + SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; }; #endif diff --git a/src/SMDS/SMDS_MeshGroup.cxx b/src/SMDS/SMDS_MeshGroup.cxx index 67c1df426..b81ffe55a 100644 --- a/src/SMDS/SMDS_MeshGroup.cxx +++ b/src/SMDS/SMDS_MeshGroup.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshGroup.cxx // Author : Jean-Michel BOULCOURT @@ -40,7 +41,7 @@ using namespace std; SMDS_MeshGroup::SMDS_MeshGroup(const SMDS_Mesh * theMesh, const SMDSAbs_ElementType theType) - :myMesh(theMesh),myType(theType), myParent(NULL) + :myMesh(theMesh),myType(theType), myParent(NULL), myTic(0) { } @@ -51,7 +52,7 @@ SMDS_MeshGroup::SMDS_MeshGroup(const SMDS_Mesh * theMesh, SMDS_MeshGroup::SMDS_MeshGroup(SMDS_MeshGroup * theParent, const SMDSAbs_ElementType theType) - :myMesh(theParent->myMesh),myType(theType), myParent(theParent) + :myMesh(theParent->myMesh),myType(theType), myParent(theParent) { } @@ -63,9 +64,9 @@ SMDS_MeshGroup::SMDS_MeshGroup(SMDS_MeshGroup * theParent, const SMDS_MeshGroup *SMDS_MeshGroup::AddSubGroup (const SMDSAbs_ElementType theType) { - const SMDS_MeshGroup * subgroup = new SMDS_MeshGroup(this,theType); - myChildren.insert(myChildren.end(),subgroup); - return subgroup; + const SMDS_MeshGroup * subgroup = new SMDS_MeshGroup(this,theType); + myChildren.insert(myChildren.end(),subgroup); + return subgroup; } //======================================================================= @@ -75,19 +76,19 @@ const SMDS_MeshGroup *SMDS_MeshGroup::AddSubGroup bool SMDS_MeshGroup::RemoveSubGroup(const SMDS_MeshGroup * theGroup) { - bool found = false; - list::iterator itgroup; - for(itgroup=myChildren.begin(); itgroup!=myChildren.end(); itgroup++) - { - const SMDS_MeshGroup* subgroup=*itgroup; - if (subgroup == theGroup) - { - found = true; - myChildren.erase(itgroup); - } - } - - return found; + bool found = false; + list::iterator itgroup; + for(itgroup=myChildren.begin(); itgroup!=myChildren.end(); itgroup++) + { + const SMDS_MeshGroup* subgroup=*itgroup; + if (subgroup == theGroup) + { + found = true; + myChildren.erase(itgroup); + } + } + + return found; } //======================================================================= @@ -97,12 +98,12 @@ bool SMDS_MeshGroup::RemoveSubGroup(const SMDS_MeshGroup * theGroup) bool SMDS_MeshGroup::RemoveFromParent() { - - if (myParent==NULL) return false; - else - { - return (myParent->RemoveSubGroup(this)); - } + + if (myParent==NULL) return false; + else + { + return (myParent->RemoveSubGroup(this)); + } } //======================================================================= //function : Clear @@ -111,8 +112,9 @@ bool SMDS_MeshGroup::RemoveFromParent() void SMDS_MeshGroup::Clear() { - myElements.clear(); - myType = SMDSAbs_All; + myElements.clear(); + myType = SMDSAbs_All; + ++myTic; } //======================================================================= @@ -122,14 +124,15 @@ void SMDS_MeshGroup::Clear() void SMDS_MeshGroup::Add(const SMDS_MeshElement * theElem) { - // the type of the group is determined by the first element added - if (myElements.empty()) myType = theElem->GetType(); - else if (theElem->GetType() != myType) { - MESSAGE("SMDS_MeshGroup::Add : Type Mismatch "<GetType()<<"!="<GetType(); + else if (theElem->GetType() != myType) { + MESSAGE("SMDS_MeshGroup::Add : Type Mismatch "<GetType()<<"!="<::iterator found - = myElements.find(theElem); + set::iterator found = myElements.find(theElem); if ( found != myElements.end() ) { myElements.erase(found); if (myElements.empty()) myType = SMDSAbs_All; + ++myTic; return true; } return false; @@ -156,7 +159,7 @@ bool SMDS_MeshGroup::Remove(const SMDS_MeshElement * theElem) bool SMDS_MeshGroup::Contains(const SMDS_MeshElement * theElem) const { - return myElements.find(theElem)!=myElements.end(); + return myElements.find(theElem)!=myElements.end(); } //======================================================================= diff --git a/src/SMDS/SMDS_MeshGroup.hxx b/src/SMDS/SMDS_MeshGroup.hxx index 788aaa79a..c46184ea9 100644 --- a/src/SMDS/SMDS_MeshGroup.hxx +++ b/src/SMDS/SMDS_MeshGroup.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshGroup.hxx // Module : SMESH @@ -34,27 +35,28 @@ class SMDS_EXPORT SMDS_MeshGroup:public SMDS_MeshObject { public: - SMDS_MeshGroup(const SMDS_Mesh * theMesh, + SMDS_MeshGroup(const SMDS_Mesh * theMesh, const SMDSAbs_ElementType theType = SMDSAbs_All); - const SMDS_MeshGroup * AddSubGroup + const SMDS_MeshGroup * AddSubGroup (const SMDSAbs_ElementType theType = SMDSAbs_All); - virtual bool RemoveSubGroup(const SMDS_MeshGroup* theGroup); - virtual bool RemoveFromParent(); + virtual bool RemoveSubGroup(const SMDS_MeshGroup* theGroup); + virtual bool RemoveFromParent(); const SMDS_Mesh* GetMesh() const { return myMesh; } void SetType (const SMDSAbs_ElementType theType); void Clear(); - void Add(const SMDS_MeshElement * theElem); - bool Remove(const SMDS_MeshElement * theElem); - bool IsEmpty() const { return myElements.empty(); } - int Extent() const { return myElements.size(); } + void Add(const SMDS_MeshElement * theElem); + bool Remove(const SMDS_MeshElement * theElem); + bool IsEmpty() const { return myElements.empty(); } + int Extent() const { return myElements.size(); } + int Tic() const { return myTic; } - int SubGroupsNb() const { return myChildren.size(); } + int SubGroupsNb() const { return myChildren.size(); } SMDSAbs_ElementType GetType() const { return myType; } - bool Contains(const SMDS_MeshElement * theElem) const; + bool Contains(const SMDS_MeshElement * theElem) const; void InitIterator() const { const_cast(myIterator) = myElements.begin(); } @@ -73,18 +75,19 @@ class SMDS_EXPORT SMDS_MeshGroup:public SMDS_MeshObject { return *(const_cast(myGroupIterator))++; } private: - SMDS_MeshGroup(SMDS_MeshGroup* theParent, + SMDS_MeshGroup(SMDS_MeshGroup* theParent, const SMDSAbs_ElementType theType = SMDSAbs_All); typedef std::set::const_iterator TIterator; typedef std::list::const_iterator TGroupIterator; - const SMDS_Mesh * myMesh; - SMDSAbs_ElementType myType; - std::set myElements; - SMDS_MeshGroup * myParent; - std::list myChildren; + const SMDS_Mesh * myMesh; + SMDSAbs_ElementType myType; + std::set myElements; /* - not sorted by ID because it */ + SMDS_MeshGroup * myParent; /* can contain deleted elements */ + std::list myChildren; TIterator myIterator; TGroupIterator myGroupIterator; + int myTic; // to track changes }; #endif diff --git a/src/SMDS/SMDS_MeshIDFactory.cxx b/src/SMDS/SMDS_MeshIDFactory.cxx index 88b9ce38d..ce8f45d2f 100644 --- a/src/SMDS/SMDS_MeshIDFactory.cxx +++ b/src/SMDS/SMDS_MeshIDFactory.cxx @@ -1,30 +1,33 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshIDFactory.cxx // Author : Jean-Michel BOULCOURT // Module : SMESH // #include "SMDS_MeshIDFactory.hxx" +#include "SMDS_Mesh.hxx" +#include "utilities.h" using namespace std; @@ -33,27 +36,33 @@ using namespace std; //purpose : //======================================================================= -SMDS_MeshIDFactory::SMDS_MeshIDFactory():myMaxID(0) +SMDS_MeshIDFactory::SMDS_MeshIDFactory():myMaxID(0), myMesh(0) { } int SMDS_MeshIDFactory::GetFreeID() { - if (myPoolOfID.empty()) return ++myMaxID; - else - { + int newid; + if (myPoolOfID.empty()) + { + newid = ++myMaxID; + //MESSAGE("GetFreeID new " << newid); + } + else + { set::iterator i = myPoolOfID.begin(); - int ID = *i;//myPoolOfID.top(); - myPoolOfID.erase( i );//myPoolOfID.pop(); - return ID; - } + newid = *i;//myPoolOfID.top(); + myPoolOfID.erase( i );//myPoolOfID.pop(); + //MESSAGE("GetFreeID pool " << newid); + } + return newid; } //======================================================================= //function : ReleaseID //purpose : //======================================================================= -void SMDS_MeshIDFactory::ReleaseID(const int ID) +void SMDS_MeshIDFactory::ReleaseID(int ID, int vtkId) { if ( ID > 0 ) { @@ -82,6 +91,24 @@ void SMDS_MeshIDFactory::ReleaseID(const int ID) void SMDS_MeshIDFactory::Clear() { - myMaxID = 0; - myPoolOfID.clear(); + myMaxID = 0; + myPoolOfID.clear(); +} + +void SMDS_MeshIDFactory::SetMesh(SMDS_Mesh *mesh) +{ + myMesh = mesh; } + +SMDS_Mesh* SMDS_MeshIDFactory::GetMesh() +{ + return myMesh; +} + +void SMDS_MeshIDFactory::emptyPool(int maxId) +{ + MESSAGE("SMDS_MeshIDFactory::emptyPool " << myMaxID << " --> " << maxId); + myMaxID = maxId; + myPoolOfID.clear(); +} + diff --git a/src/SMDS/SMDS_MeshIDFactory.hxx b/src/SMDS/SMDS_MeshIDFactory.hxx index e81b41890..c79e17aaa 100644 --- a/src/SMDS/SMDS_MeshIDFactory.hxx +++ b/src/SMDS/SMDS_MeshIDFactory.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshIDFactory.hxx // Module : SMESH @@ -31,18 +32,25 @@ #include "SMDS_MeshObject.hxx" #include +class SMDS_Mesh; class SMDS_EXPORT SMDS_MeshIDFactory:public SMDS_MeshObject { public: - virtual int GetFreeID(); - virtual void ReleaseID(int ID); + int GetFreeID(); + virtual void ReleaseID(int ID, int vtkId = -1); virtual void Clear(); - protected: - SMDS_MeshIDFactory(); - int myMaxID; - std::set myPoolOfID; + void SetMesh(SMDS_Mesh *mesh); + SMDS_Mesh* GetMesh(); + inline bool isPoolIdEmpty() { return myPoolOfID.empty(); }; + virtual void emptyPool(int maxId); + inline void adjustMaxId(int ID) { if (ID > myMaxID) myMaxID = ID;}; +protected: + SMDS_MeshIDFactory(); + int myMaxID; + std::set myPoolOfID; + SMDS_Mesh *myMesh; }; #endif diff --git a/src/SMDS/SMDS_MeshInfo.hxx b/src/SMDS/SMDS_MeshInfo.hxx index 6b00427d9..026590a2e 100644 --- a/src/SMDS/SMDS_MeshInfo.hxx +++ b/src/SMDS/SMDS_MeshInfo.hxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMDS_MeshInfo.hxx // Created : Mon Sep 24 18:32:41 2007 // Author : Edward AGAPOV (eap) @@ -26,6 +24,7 @@ #ifndef SMDS_MeshInfo_HeaderFile #define SMDS_MeshInfo_HeaderFile +#include using namespace std; #include "SMESH_SMDS.hxx" @@ -37,14 +36,21 @@ class SMDS_EXPORT SMDS_MeshInfo public: inline SMDS_MeshInfo(); + inline SMDS_MeshInfo& operator=(const SMDS_MeshInfo& other); inline void Clear(); - int NbNodes() const { return myNbNodes; } + inline int NbElements(SMDSAbs_ElementType type=SMDSAbs_All) const; + inline int NbEntities(SMDSAbs_EntityType type) const; + int NbNodes() const { return myNbNodes; } + int Nb0DElements() const { return myNb0DElements; } + int NbBalls() const { return myNbBalls; } inline int NbEdges (SMDSAbs_ElementOrder order = ORDER_ANY) const; + inline int NbFaces (SMDSAbs_ElementOrder order = ORDER_ANY) const; inline int NbTriangles (SMDSAbs_ElementOrder order = ORDER_ANY) const; inline int NbQuadrangles(SMDSAbs_ElementOrder order = ORDER_ANY) const; + int NbBiQuadQuadrangles() const { return myNbBiQuadQuadrangles; } int NbPolygons() const { return myNbPolygons; } inline int NbVolumes (SMDSAbs_ElementOrder order = ORDER_ANY) const; @@ -52,15 +58,21 @@ public: inline int NbHexas (SMDSAbs_ElementOrder order = ORDER_ANY) const; inline int NbPyramids(SMDSAbs_ElementOrder order = ORDER_ANY) const; inline int NbPrisms (SMDSAbs_ElementOrder order = ORDER_ANY) const; + inline int NbHexPrisms(SMDSAbs_ElementOrder order = ORDER_ANY) const; + int NbTriQuadHexas() const { return myNbTriQuadHexas; } int NbPolyhedrons() const { return myNbPolyhedrons; } +protected: + inline void addWithPoly(const SMDS_MeshElement* el); + inline void setNb(const SMDSAbs_EntityType geomType, const int nb); + private: friend class SMDS_Mesh; // methods to count NOT POLY elements inline void remove(const SMDS_MeshElement* el); inline void add (const SMDS_MeshElement* el); - inline int index(SMDSAbs_ElementType type, int nbNodes); + inline int index(SMDSAbs_ElementType type, int nbNodes) const; // methods to remove elements of ANY kind inline void RemoveEdge(const SMDS_MeshElement* el); inline void RemoveFace(const SMDS_MeshElement* el); @@ -68,15 +80,18 @@ private: int myNbNodes; + int myNb0DElements; + int myNbBalls; int myNbEdges , myNbQuadEdges ; int myNbTriangles , myNbQuadTriangles ; - int myNbQuadrangles, myNbQuadQuadrangles; + int myNbQuadrangles, myNbQuadQuadrangles, myNbBiQuadQuadrangles; int myNbPolygons; int myNbTetras , myNbQuadTetras ; - int myNbHexas , myNbQuadHexas ; + int myNbHexas , myNbQuadHexas, myNbTriQuadHexas; int myNbPyramids, myNbQuadPyramids; int myNbPrisms , myNbQuadPrisms ; + int myNbHexPrism; int myNbPolyhedrons; std::vector myNb; // pointers to myNb... fields @@ -84,51 +99,69 @@ private: }; inline SMDS_MeshInfo::SMDS_MeshInfo(): - myNbNodes(0), + myNbNodes (0), + myNb0DElements (0), + myNbBalls (0), myNbEdges (0), myNbQuadEdges (0), myNbTriangles (0), myNbQuadTriangles (0), - myNbQuadrangles(0), myNbQuadQuadrangles(0), - myNbPolygons(0), - myNbTetras (0), myNbQuadTetras (0), - myNbHexas (0), myNbQuadHexas (0), - myNbPyramids(0), myNbQuadPyramids(0), - myNbPrisms (0), myNbQuadPrisms (0), + myNbQuadrangles(0), myNbQuadQuadrangles(0), myNbBiQuadQuadrangles(0), + myNbPolygons (0), + myNbTetras (0), myNbQuadTetras (0), + myNbHexas (0), myNbQuadHexas (0), myNbTriQuadHexas(0), + myNbPyramids (0), myNbQuadPyramids(0), + myNbPrisms (0), myNbQuadPrisms (0), + myNbHexPrism (0), myNbPolyhedrons(0) { - // Number of nodes in standard element types - // n v f e - // o o a d - // d l c g - // e e e - // ----------- - // 1 - // 2 * - // 3 * - // 4 * * * - // 5 * - // 6 * * - // 7 - // 8 * * - // 9 - // 10 * - // 11 - // 12 - // 13 * - // 14 - // 15 * - // 16 - // 17 - // 18 - // 19 + // Number of nodes in standard element types (. - actual nb, * - after the shift) + // n v f e 0 n b + // o o a d d o a + // d l c g d l + // e e e e l + // s + // ==================== + // 0 ------------------ - DON't USE 0!!! + // 1 . * . + // 2 . * + // 3 . * + // 4 * . . + // 5 * + // 6 * . + // 7 * + // 8 * . + // 9 . * + // 10 * + // 11 + // 12 * + // 13 * + // 14 + // 15 * + // 16 * + // 17 * + // 18 + // 19 * // 20 * + // 21 * + // 22 * + // 23 + // 24 + // 25 + // 26 + // 27 * // // So to have a unique index for each type basing on nb of nodes, we use a shift: - myShift.resize(SMDSAbs_Volume + 1, 0); - myShift[ SMDSAbs_Face ] = +8; // 3->11, 4->12, 6->14, 8->16 - myShift[ SMDSAbs_Edge ] = -2; // 2->0, 4->2 + myShift.resize(SMDSAbs_NbElementTypes, 0); + + myShift[ SMDSAbs_Face ] = +13;// 3->16, 4->17, 6->19, 8->21, 9->22 + myShift[ SMDSAbs_Edge ] = +5; // 2->7, 4->9 + myShift[ SMDSAbs_0DElement ] = +2; // 1->3 + myShift[ SMDSAbs_Ball ] = +1; // 1->2 + + myNb.resize( index( SMDSAbs_Volume,27 ) + 1, NULL); - myNb.resize( index( SMDSAbs_Volume,20 ) + 1, NULL); myNb[ index( SMDSAbs_Node,1 )] = & myNbNodes; + myNb[ index( SMDSAbs_0DElement,1 )] = & myNb0DElements; + myNb[ index( SMDSAbs_Ball,1 )] = & myNbBalls; myNb[ index( SMDSAbs_Edge,2 )] = & myNbEdges; myNb[ index( SMDSAbs_Edge,4 )] = & myNbQuadEdges; @@ -137,22 +170,36 @@ inline SMDS_MeshInfo::SMDS_MeshInfo(): myNb[ index( SMDSAbs_Face,4 )] = & myNbQuadrangles; myNb[ index( SMDSAbs_Face,6 )] = & myNbQuadTriangles; myNb[ index( SMDSAbs_Face,8 )] = & myNbQuadQuadrangles; + myNb[ index( SMDSAbs_Face,9 )] = & myNbBiQuadQuadrangles; myNb[ index( SMDSAbs_Volume, 4)] = & myNbTetras; myNb[ index( SMDSAbs_Volume, 5)] = & myNbPyramids; myNb[ index( SMDSAbs_Volume, 6)] = & myNbPrisms; myNb[ index( SMDSAbs_Volume, 8)] = & myNbHexas; myNb[ index( SMDSAbs_Volume, 10)] = & myNbQuadTetras; + myNb[ index( SMDSAbs_Volume, 12)] = & myNbHexPrism; myNb[ index( SMDSAbs_Volume, 13)] = & myNbQuadPyramids; myNb[ index( SMDSAbs_Volume, 15)] = & myNbQuadPrisms; myNb[ index( SMDSAbs_Volume, 20)] = & myNbQuadHexas; + myNb[ index( SMDSAbs_Volume, 27)] = & myNbTriQuadHexas; +} + +inline SMDS_MeshInfo& // operator= +SMDS_MeshInfo::operator=(const SMDS_MeshInfo& other) +{ for ( int i=0; iGetType(), el->NbNodes()) ]); } +inline void // addWithPoly +SMDS_MeshInfo::addWithPoly(const SMDS_MeshElement* el) +{ + if ( el->IsPoly() ) + ++( el->GetType()==SMDSAbs_Face ? myNbPolygons : myNbPolyhedrons ); + else + add(el); +} inline void // RemoveEdge SMDS_MeshInfo::RemoveEdge(const SMDS_MeshElement* el) { if ( el->IsQuadratic() ) --myNbQuadEdges; else --myNbEdges; } @@ -189,11 +244,11 @@ SMDS_MeshInfo::NbTriangles (SMDSAbs_ElementOrder order) const inline int // NbQuadrangles SMDS_MeshInfo::NbQuadrangles(SMDSAbs_ElementOrder order) const -{ return order == ORDER_ANY ? myNbQuadrangles+myNbQuadQuadrangles : order == ORDER_LINEAR ? myNbQuadrangles : myNbQuadQuadrangles; } +{ return order == ORDER_ANY ? myNbQuadrangles+myNbQuadQuadrangles+myNbBiQuadQuadrangles : order == ORDER_LINEAR ? myNbQuadrangles : myNbQuadQuadrangles+myNbBiQuadQuadrangles; } inline int // NbVolumes SMDS_MeshInfo::NbVolumes (SMDSAbs_ElementOrder order) const -{ return NbTetras(order) + NbHexas(order) + NbPyramids(order) + NbPrisms(order) + (order == ORDER_QUADRATIC ? 0 : myNbPolyhedrons); } +{ return NbTetras(order) + NbHexas(order) + NbPyramids(order) + NbPrisms(order) + NbHexPrisms(order) + (order == ORDER_QUADRATIC ? 0 : myNbPolyhedrons); } inline int // NbTetras SMDS_MeshInfo::NbTetras (SMDSAbs_ElementOrder order) const @@ -201,7 +256,7 @@ SMDS_MeshInfo::NbTetras (SMDSAbs_ElementOrder order) const inline int // NbHexas SMDS_MeshInfo::NbHexas (SMDSAbs_ElementOrder order) const -{ return order == ORDER_ANY ? myNbHexas+myNbQuadHexas : order == ORDER_LINEAR ? myNbHexas : myNbQuadHexas; } +{ return order == ORDER_ANY ? myNbHexas+myNbQuadHexas+myNbTriQuadHexas : order == ORDER_LINEAR ? myNbHexas : myNbQuadHexas+myNbTriQuadHexas; } inline int // NbPyramids SMDS_MeshInfo::NbPyramids(SMDSAbs_ElementOrder order) const @@ -211,4 +266,108 @@ inline int // NbPrisms SMDS_MeshInfo::NbPrisms (SMDSAbs_ElementOrder order) const { return order == ORDER_ANY ? myNbPrisms+myNbQuadPrisms : order == ORDER_LINEAR ? myNbPrisms : myNbQuadPrisms; } +inline int // NbHexPrisms +SMDS_MeshInfo::NbHexPrisms (SMDSAbs_ElementOrder order) const +{ return order == ORDER_ANY ? myNbHexPrism : order == ORDER_LINEAR ? myNbHexPrism : 0; } + +inline int // NbElements +SMDS_MeshInfo::NbElements(SMDSAbs_ElementType type) const +{ + int nb = 0; + switch (type) { + case SMDSAbs_All: + for ( int i=1+index( SMDSAbs_Node,1 ); i + +#include "utilities.h" +#include "Utils_SALOME_Exception.hxx" +#include using namespace std; +int SMDS_MeshNode::nbNodes =0; + //======================================================================= //function : SMDS_MeshNode //purpose : //======================================================================= +SMDS_MeshNode::SMDS_MeshNode() : + SMDS_MeshElement(-1, -1, 0), + myPosition(SMDS_SpacePosition::originSpacePosition()) +{ + nbNodes++; +} + +SMDS_MeshNode::SMDS_MeshNode(int id, int meshId, int shapeId, double x, double y, double z): + SMDS_MeshElement(id, meshId, shapeId), + myPosition(SMDS_SpacePosition::originSpacePosition()) +{ + nbNodes++; + init(id, meshId, shapeId, x, y ,z); +} + +void SMDS_MeshNode::init(int id, int meshId, int shapeId, double x, double y, double z) +{ + SMDS_MeshElement::init(id, meshId, shapeId); + myVtkID = id -1; + assert(myVtkID >= 0); + //MESSAGE("Node " << myID << " " << myVtkID << " (" << x << ", " << y << ", " << z << ")"); + SMDS_Mesh* mesh = SMDS_Mesh::_meshList[myMeshId]; + SMDS_UnstructuredGrid * grid = mesh->getGrid(); + vtkPoints *points = grid->GetPoints(); + points->InsertPoint(myVtkID, x, y, z); + SMDS_CellLinks *cellLinks = dynamic_cast(grid->GetCellLinks()); + assert(cellLinks); + if (myVtkID >= cellLinks->GetLinksSize()) + cellLinks->ResizeL(myVtkID+SMDS_Mesh::chunkSize); +} -SMDS_MeshNode::SMDS_MeshNode(double x, double y, double z): - myX(x), myY(y), myZ(z), - myPosition(SMDS_SpacePosition::originSpacePosition()) +SMDS_MeshNode::~SMDS_MeshNode() { + nbNodes--; + if ( myPosition && myPosition != SMDS_SpacePosition::originSpacePosition() ) + delete myPosition, myPosition = 0; } //======================================================================= @@ -49,14 +89,10 @@ SMDS_MeshNode::SMDS_MeshNode(double x, double y, double z): void SMDS_MeshNode::RemoveInverseElement(const SMDS_MeshElement * parent) { - NCollection_List::Iterator it(myInverseElements); - while (it.More()) { - const SMDS_MeshElement* elem = it.Value(); - if (elem == parent) - myInverseElements.Remove(it); - else - it.Next(); - } + //MESSAGE("RemoveInverseElement " << myID << " " << parent->GetID()); + const SMDS_MeshCell* cell = dynamic_cast(parent); + MYASSERT(cell); + SMDS_Mesh::_meshList[myMeshId]->getGrid()->RemoveReferenceToCell(myVtkID, cell->getVtkId()); } //======================================================================= @@ -66,8 +102,8 @@ void SMDS_MeshNode::RemoveInverseElement(const SMDS_MeshElement * parent) void SMDS_MeshNode::Print(ostream & OS) const { - OS << "Node <" << GetID() << "> : X = " << myX << " Y = " - << myY << " Z = " << myZ << endl; + OS << "Node <" << myID << "> : X = " << X() << " Y = " + << Y() << " Z = " << Z() << endl; } //======================================================================= @@ -77,7 +113,11 @@ void SMDS_MeshNode::Print(ostream & OS) const void SMDS_MeshNode::SetPosition(const SMDS_PositionPtr& aPos) { - myPosition = aPos; + if ( myPosition && + myPosition != SMDS_SpacePosition::originSpacePosition() && + myPosition != aPos ) + delete myPosition; + myPosition = aPos; } //======================================================================= @@ -87,7 +127,7 @@ void SMDS_MeshNode::SetPosition(const SMDS_PositionPtr& aPos) const SMDS_PositionPtr& SMDS_MeshNode::GetPosition() const { - return myPosition; + return myPosition; } //======================================================================= @@ -96,121 +136,186 @@ const SMDS_PositionPtr& SMDS_MeshNode::GetPosition() const */ //======================================================================= -class SMDS_MeshNode_MyInvIterator:public SMDS_ElemIterator +class SMDS_MeshNode_MyInvIterator: public SMDS_ElemIterator { - NCollection_List::Iterator myIterator; - SMDSAbs_ElementType myType; - public: - SMDS_MeshNode_MyInvIterator(const NCollection_List& s, - SMDSAbs_ElementType type): - myIterator(s), myType(type) - {} +private: + SMDS_Mesh* myMesh; + vtkIdType* myCells; + int myNcells; + SMDSAbs_ElementType myType; + int iter; + vector cellList; + +public: + SMDS_MeshNode_MyInvIterator(SMDS_Mesh *mesh, vtkIdType* cells, int ncells, SMDSAbs_ElementType type) : + myMesh(mesh), myCells(cells), myNcells(ncells), myType(type), iter(0) + { + //MESSAGE("SMDS_MeshNode_MyInvIterator : ncells " << myNcells); + cellList.clear(); + if (type == SMDSAbs_All) + for (int i = 0; i < ncells; i++) + cellList.push_back(cells[i]); + else for (int i = 0; i < ncells; i++) + { + int vtkId = cells[i]; + int smdsId = myMesh->fromVtkToSmds(vtkId); + const SMDS_MeshElement* elem = myMesh->FindElement(smdsId); + if (elem->GetType() == type) + { + //MESSAGE("Add element vtkId " << vtkId << " " << elem->GetType()) + cellList.push_back(vtkId); + } + } + myCells = &cellList[0]; + myNcells = cellList.size(); + //MESSAGE("myNcells="<GetType() != myType) - myIterator.Next(); - } - return myIterator.More() != Standard_False; + //MESSAGE("iter " << iter << " ncells " << myNcells); + return (iter < myNcells); } const SMDS_MeshElement* next() { - const SMDS_MeshElement* current=myIterator.Value(); - myIterator.Next(); - return current; - } + int vtkId = myCells[iter]; + int smdsId = myMesh->fromVtkToSmds(vtkId); + const SMDS_MeshElement* elem = myMesh->FindElement(smdsId); + if (!elem) + { + MESSAGE("SMDS_MeshNode_MyInvIterator problem Null element"); + throw SALOME_Exception("SMDS_MeshNode_MyInvIterator problem Null element"); + } + //MESSAGE("vtkId " << vtkId << " smdsId " << smdsId << " " << elem->GetType()); + iter++; + return elem; + } }; SMDS_ElemIteratorPtr SMDS_MeshNode:: - GetInverseElementIterator(SMDSAbs_ElementType type) const + GetInverseElementIterator(SMDSAbs_ElementType type) const { - return SMDS_ElemIteratorPtr(new SMDS_MeshNode_MyInvIterator(myInverseElements,type)); + vtkCellLinks::Link l = SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetCellLinks()->GetLink(myVtkID); + //MESSAGE("myID " << myID << " ncells " << l.ncells); + return SMDS_ElemIteratorPtr(new SMDS_MeshNode_MyInvIterator(SMDS_Mesh::_meshList[myMeshId], l.cells, l.ncells, type)); } // Same as GetInverseElementIterator but the create iterator only return // wanted type elements. class SMDS_MeshNode_MyIterator:public SMDS_ElemIterator { - NCollection_List mySet; - NCollection_List::Iterator myIterator; +private: + SMDS_Mesh* myMesh; + vtkIdType* myCells; + int myNcells; + SMDSAbs_ElementType myType; + int iter; + vector myFiltCells; + public: - SMDS_MeshNode_MyIterator(SMDSAbs_ElementType type, - const NCollection_List& s) + SMDS_MeshNode_MyIterator(SMDS_Mesh *mesh, + vtkIdType* cells, + int ncells, + SMDSAbs_ElementType type): + myMesh(mesh), myCells(cells), myNcells(ncells), myType(type), iter(0) { - const SMDS_MeshElement * e; - bool toInsert; - NCollection_List::Iterator it(s); - for(; it.More(); it.Next()) - { - e=it.Value(); - switch(type) - { - case SMDSAbs_Edge: toInsert=true; break; - case SMDSAbs_Face: toInsert=(e->GetType()!=SMDSAbs_Edge); break; - case SMDSAbs_Volume: toInsert=(e->GetType()==SMDSAbs_Volume); break; - } - if(toInsert) mySet.Append(e); - } - myIterator.Init(mySet); + //MESSAGE("myNcells " << myNcells); + for (; iterfromVtkToSmds(vtkId); + //MESSAGE("vtkId " << vtkId << " smdsId " << smdsId); + const SMDS_MeshElement* elem = myMesh->FindElement(smdsId); + if (elem->GetType() == type) + myFiltCells.push_back((SMDS_MeshElement*)elem); + } + myNcells = myFiltCells.size(); + //MESSAGE("myNcells " << myNcells); + iter = 0; + //MESSAGE("SMDS_MeshNode_MyIterator (filter) " << ncells << " " << myNcells); } bool more() { - return myIterator.More() != Standard_False; + return (iter< myNcells); } const SMDS_MeshElement* next() { - const SMDS_MeshElement* current=myIterator.Value(); - myIterator.Next(); - return current; + const SMDS_MeshElement* elem = myFiltCells[iter]; + iter++; + return elem; } }; SMDS_ElemIteratorPtr SMDS_MeshNode:: - elementsIterator(SMDSAbs_ElementType type) const + elementsIterator(SMDSAbs_ElementType type) const { if(type==SMDSAbs_Node) return SMDS_MeshElement::elementsIterator(SMDSAbs_Node); else - return SMDS_ElemIteratorPtr - (new SMDS_IteratorOfElements - (this,type, - SMDS_ElemIteratorPtr(new SMDS_MeshNode_MyIterator(type, myInverseElements)))); + { + vtkCellLinks::Link l = SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetCellLinks()->GetLink(myVtkID); + return SMDS_ElemIteratorPtr(new SMDS_MeshNode_MyIterator(SMDS_Mesh::_meshList[myMeshId], l.cells, l.ncells, type)); + } } int SMDS_MeshNode::NbNodes() const { - return 1; + return 1; +} + +double* SMDS_MeshNode::getCoord() const +{ + return SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetPoint(myVtkID); } double SMDS_MeshNode::X() const { - return myX; + double *coord = getCoord(); + return coord[0]; } double SMDS_MeshNode::Y() const { - return myY; + double *coord = getCoord(); + return coord[1]; } double SMDS_MeshNode::Z() const { - return myZ; + double *coord = getCoord(); + return coord[2]; } +//================================================================================ +/*! + * \brief thread safe getting coords + */ +void SMDS_MeshNode::GetXYZ(double xyz[3]) const +{ + return SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetPoint(myVtkID,xyz); +} + +//* resize the vtkPoints structure every SMDS_Mesh::chunkSize points void SMDS_MeshNode::setXYZ(double x, double y, double z) { - myX=x; - myY=y; - myZ=z; + SMDS_Mesh *mesh = SMDS_Mesh::_meshList[myMeshId]; + vtkPoints *points = mesh->getGrid()->GetPoints(); + points->InsertPoint(myVtkID, x, y, z); + mesh->adjustBoundingBox(x, y, z); + mesh->setMyModified(); } SMDSAbs_ElementType SMDS_MeshNode::GetType() const { - return SMDSAbs_Node; + return SMDSAbs_Node; +} + +vtkIdType SMDS_MeshNode::GetVtkType() const +{ + return VTK_VERTEX; } //======================================================================= @@ -219,13 +324,12 @@ SMDSAbs_ElementType SMDS_MeshNode::GetType() const //======================================================================= void SMDS_MeshNode::AddInverseElement(const SMDS_MeshElement* ME) { - NCollection_List::Iterator it(myInverseElements); - for (; it.More(); it.Next()) { - const SMDS_MeshElement* elem = it.Value(); - if (elem == ME) - return; - } - myInverseElements.Append(ME); + const SMDS_MeshCell *cell = dynamic_cast (ME); + assert(cell); + SMDS_UnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkCellLinks *Links = grid->GetCellLinks(); + Links->ResizeCellList(myVtkID, 1); + Links->AddCellReference(cell->getVtkId(), myVtkID); } //======================================================================= @@ -234,12 +338,13 @@ void SMDS_MeshNode::AddInverseElement(const SMDS_MeshElement* ME) //======================================================================= void SMDS_MeshNode::ClearInverseElements() { - myInverseElements.Clear(); + SMDS_Mesh::_meshList[myMeshId]->getGrid()->ResizeCellList(myVtkID, 0); } bool SMDS_MeshNode::emptyInverseElements() { - return myInverseElements.IsEmpty() != Standard_False; + vtkCellLinks::Link l = SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetCellLinks()->GetLink(myVtkID); + return (l.ncells == 0); } //================================================================================ @@ -250,13 +355,19 @@ bool SMDS_MeshNode::emptyInverseElements() int SMDS_MeshNode::NbInverseElements(SMDSAbs_ElementType type) const { + vtkCellLinks::Link l = SMDS_Mesh::_meshList[myMeshId]->getGrid()->GetCellLinks()->GetLink(myVtkID); + if ( type == SMDSAbs_All ) - return myInverseElements.Extent(); + return l.ncells; + int nb = 0; - NCollection_List::Iterator it( myInverseElements ); - for ( ; it.More(); it.Next() ) - if ( it.Value()->GetType() == type ) - nb++; + SMDS_Mesh *mesh = SMDS_Mesh::_meshList[myMeshId]; + for (int i=0; iFindElement(mesh->fromVtkToSmds(l.cells[i])); + if (elem->GetType() == type) + nb++; + } return nb; } @@ -265,14 +376,14 @@ int SMDS_MeshNode::NbInverseElements(SMDSAbs_ElementType type) const /////////////////////////////////////////////////////////////////////////////// bool operator<(const SMDS_MeshNode& e1, const SMDS_MeshNode& e2) { - return e1.GetID() +#include "ObjectPool.hxx" -class SMDS_EXPORT SMDS_MeshNode:public SMDS_MeshElement +class SMDS_EXPORT SMDS_MeshNode: public SMDS_MeshElement { +public: + friend class SMESHDS_Mesh; + friend class SMDS_Mesh; + friend class ObjectPool; + friend class SMDS_VtkFace; + + void Print(std::ostream & OS) const; + double X() const; // ! NOT thread safe methods ! + double Y() const; + double Z() const; + void GetXYZ(double xyx[3]) const; // thread safe getting coords + SMDS_ElemIteratorPtr GetInverseElementIterator(SMDSAbs_ElementType type=SMDSAbs_All) const; + int NbInverseElements(SMDSAbs_ElementType type=SMDSAbs_All) const; + const SMDS_PositionPtr& GetPosition() const; + virtual SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Node;} + virtual SMDSAbs_GeometryType GetGeomType() const { return SMDSGeom_NONE; } + virtual int NbNodes() const; + + void SetPosition(const SMDS_PositionPtr& aPos); + void setXYZ(double x, double y, double z); - public: - SMDS_MeshNode(double x, double y, double z); - void Print(std::ostream & OS) const; - double X() const; - double Y() const; - double Z() const; - void AddInverseElement(const SMDS_MeshElement * ME); - void RemoveInverseElement(const SMDS_MeshElement * parent); - void ClearInverseElements(); - bool emptyInverseElements(); - SMDS_ElemIteratorPtr GetInverseElementIterator(SMDSAbs_ElementType type=SMDSAbs_All) const; - int NbInverseElements(SMDSAbs_ElementType type=SMDSAbs_All) const; - void SetPosition(const SMDS_PositionPtr& aPos); - const SMDS_PositionPtr& GetPosition() const; - SMDSAbs_ElementType GetType() const; - int NbNodes() const; - void setXYZ(double x, double y, double z); - friend bool operator<(const SMDS_MeshNode& e1, const SMDS_MeshNode& e2); + static int nbNodes; - /*! - * \brief Return node by its index - * \param ind - node index - * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range - */ - virtual const SMDS_MeshNode* GetNode(const int) const { return this; } +protected: + SMDS_MeshNode(); + SMDS_MeshNode(int id, int meshId, int shapeId = -1, double x=0, double y=0, double z=0); + virtual ~SMDS_MeshNode(); + void init(int id, int meshId, int shapeId = -1, double x=0, double y=0, double z=0); + inline void setVtkId(int vtkId) { myVtkID = vtkId; }; + double* getCoord() const; + void AddInverseElement(const SMDS_MeshElement * ME); + void RemoveInverseElement(const SMDS_MeshElement * parent); + void ClearInverseElements(); + bool emptyInverseElements(); - protected: - SMDS_ElemIteratorPtr - elementsIterator(SMDSAbs_ElementType type) const; + SMDS_ElemIteratorPtr + elementsIterator(SMDSAbs_ElementType type) const; - private: - double myX, myY, myZ; - SMDS_PositionPtr myPosition; - NCollection_List myInverseElements; +private: + SMDS_PositionPtr myPosition; }; #endif diff --git a/src/SMDS/SMDS_MeshNodeIDFactory.cxx b/src/SMDS/SMDS_MeshNodeIDFactory.cxx new file mode 100644 index 000000000..0884a5b98 --- /dev/null +++ b/src/SMDS/SMDS_MeshNodeIDFactory.cxx @@ -0,0 +1,149 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMDS_MeshNodeIDFactory.cxx +// Author : Jean-Michel BOULCOURT +// Module : SMESH +// +#ifdef _MSC_VER +#pragma warning(disable:4786) +#endif + +#include "SMDS_MeshNodeIDFactory.hxx" +#include "SMDS_MeshElement.hxx" +#include "SMDS_Mesh.hxx" + +#include +#include + +using namespace std; + +//======================================================================= +//function : SMDS_MeshNodeIDFactory +//purpose : +//======================================================================= +SMDS_MeshNodeIDFactory::SMDS_MeshNodeIDFactory() : + SMDS_MeshIDFactory(), myMin(0), myMax(0) +{ +} + +//======================================================================= +//function : BindID +//purpose : +//======================================================================= +bool SMDS_MeshNodeIDFactory::BindID(int ID, SMDS_MeshElement * elem) +{ + updateMinMax(ID); + return true; +} + +//======================================================================= +//function : MeshElement +//purpose : +//======================================================================= +SMDS_MeshElement* SMDS_MeshNodeIDFactory::MeshElement(int ID) +{ + // commented since myMax can be 0 after ReleaseID() +// if ((ID < 1) || (ID > myMax)) +// return NULL; + const SMDS_MeshElement* elem = GetMesh()->FindNode(ID); + return (SMDS_MeshElement*) (elem); +} + +//======================================================================= +//function : GetFreeID +//purpose : +//======================================================================= +int SMDS_MeshNodeIDFactory::GetFreeID() +{ + int ID; + do { + ID = SMDS_MeshIDFactory::GetFreeID(); + } while ( MeshElement( ID )); + return ID; +} + +//======================================================================= +//function : ReleaseID +//purpose : +//======================================================================= +void SMDS_MeshNodeIDFactory::ReleaseID(const int ID, int vtkId) +{ + SMDS_MeshIDFactory::ReleaseID(ID); + myMesh->setMyModified(); + if (ID == myMax) + myMax = 0; // --- force updateMinMax + if (ID == myMin) + myMax = 0; // --- force updateMinMax +} + +//======================================================================= +//function : GetMaxID +//purpose : +//======================================================================= + +int SMDS_MeshNodeIDFactory::GetMaxID() const +{ + if (myMax == 0) + updateMinMax(); + return myMax; +} + +//======================================================================= +//function : GetMinID +//purpose : +//======================================================================= + +int SMDS_MeshNodeIDFactory::GetMinID() const +{ + if (myMax == 0) + updateMinMax(); + return myMin; +} + +//======================================================================= +//function : updateMinMax +//purpose : +//======================================================================= + +void SMDS_MeshNodeIDFactory::updateMinMax() const +{ + myMesh->updateNodeMinMax(); + myMin = myMesh->MinNodeID(); + myMax = myMesh->MaxNodeID(); +} + +SMDS_ElemIteratorPtr SMDS_MeshNodeIDFactory::elementsIterator() const +{ + return myMesh->elementsIterator(SMDSAbs_Node); +} + +void SMDS_MeshNodeIDFactory::Clear() +{ + myMin = myMax = 0; + SMDS_MeshIDFactory::Clear(); +} + +void SMDS_MeshNodeIDFactory::emptyPool(int maxId) +{ + SMDS_MeshIDFactory::emptyPool(maxId); + myMax = maxId; +} diff --git a/src/SMDS/SMDS_MeshNodeIDFactory.hxx b/src/SMDS/SMDS_MeshNodeIDFactory.hxx new file mode 100644 index 000000000..07c0341e1 --- /dev/null +++ b/src/SMDS/SMDS_MeshNodeIDFactory.hxx @@ -0,0 +1,65 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMDS_MeshElementIDFactory.hxx +// Module : SMESH +// +#ifndef _SMDS_MeshNodeIDFactory_HeaderFile +#define _SMDS_MeshNodeIDFactory_HeaderFile + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshIDFactory.hxx" +#include "SMDS_ElemIterator.hxx" + +#include + +class SMDS_MeshElement; + +class SMDS_EXPORT SMDS_MeshNodeIDFactory: public SMDS_MeshIDFactory +{ +public: + SMDS_MeshNodeIDFactory(); + bool BindID(int ID, SMDS_MeshElement * elem); + SMDS_MeshElement * MeshElement(int ID); + virtual int GetFreeID(); + virtual void ReleaseID(int ID, int vtkId = -1); + int GetMaxID() const; + int GetMinID() const; + SMDS_ElemIteratorPtr elementsIterator() const; + virtual void Clear(); + virtual void emptyPool(int maxId); + +protected: + void updateMinMax() const; + void updateMinMax(int id) const + { + if (id > myMax) + myMax = id; + if (id < myMin) + myMin = id; + } + + mutable int myMin, myMax; + +}; + +#endif diff --git a/src/SMDS/SMDS_MeshObject.cxx b/src/SMDS/SMDS_MeshObject.cxx index b023c73e9..f880cff6c 100644 --- a/src/SMDS/SMDS_MeshObject.cxx +++ b/src/SMDS/SMDS_MeshObject.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshObject.cxx // Author : Jean-Michel BOULCOURT diff --git a/src/SMDS/SMDS_MeshObject.hxx b/src/SMDS/SMDS_MeshObject.hxx index 8004fcbf9..c3bd07de6 100644 --- a/src/SMDS/SMDS_MeshObject.hxx +++ b/src/SMDS/SMDS_MeshObject.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshObject.hxx // Module : SMESH diff --git a/src/SMDS/SMDS_MeshVolume.cxx b/src/SMDS/SMDS_MeshVolume.cxx index 574b5b0ff..7cf865a32 100644 --- a/src/SMDS/SMDS_MeshVolume.cxx +++ b/src/SMDS/SMDS_MeshVolume.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshVolume.cxx // Author : Jean-Michel BOULCOURT @@ -32,6 +33,10 @@ SMDSAbs_ElementType SMDS_MeshVolume::GetType() const { - return SMDSAbs_Volume; + return SMDSAbs_Volume; } +vtkIdType SMDS_MeshVolume::GetVtkType() const +{ + return VTK_CONVEX_POINT_SET; // --- must be reimplemented in derived classes +} diff --git a/src/SMDS/SMDS_MeshVolume.hxx b/src/SMDS/SMDS_MeshVolume.hxx index aa3498eac..1f928c610 100644 --- a/src/SMDS/SMDS_MeshVolume.hxx +++ b/src/SMDS/SMDS_MeshVolume.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshVolume.hxx // Module : SMESH @@ -28,12 +29,13 @@ #include "SMESH_SMDS.hxx" -#include "SMDS_MeshElement.hxx" +#include "SMDS_MeshCell.hxx" -class SMDS_EXPORT SMDS_MeshVolume:public SMDS_MeshElement +class SMDS_EXPORT SMDS_MeshVolume:public SMDS_MeshCell { - + public: - SMDSAbs_ElementType GetType() const; + SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; }; #endif diff --git a/src/SMDS/SMDS_PolygonalFaceOfNodes.cxx b/src/SMDS/SMDS_PolygonalFaceOfNodes.cxx index 282256c7f..4738a0220 100644 --- a/src/SMDS/SMDS_PolygonalFaceOfNodes.cxx +++ b/src/SMDS/SMDS_PolygonalFaceOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -42,6 +43,7 @@ using namespace std; SMDS_PolygonalFaceOfNodes::SMDS_PolygonalFaceOfNodes (std::vector nodes) { + //MESSAGE("******************************************** SMDS_PolygonalFaceOfNodes"); myNodes = nodes; } @@ -154,7 +156,7 @@ public: myElems.reserve( face->NbNodes() ); for ( int i = 0; i < face->NbNodes(); ++i ) { const SMDS_MeshElement* edge = - SMDS_Mesh::FindEdge( face->GetNode( i ), face->GetNode( i + 1 )); + SMDS_Mesh::FindEdge( face->GetNode( i ), face->GetNodeWrap( i + 1 )); if ( edge ) myElems.push_back( edge ); } @@ -191,11 +193,8 @@ SMDS_ElemIteratorPtr SMDS_PolygonalFaceOfNodes::elementsIterator * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ const SMDS_MeshNode* SMDS_PolygonalFaceOfNodes::GetNode(const int ind) const { return myNodes[ WrappedIndex( ind )]; } - diff --git a/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx b/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx index 3c9f9a508..1586f2514 100644 --- a/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx +++ b/src/SMDS/SMDS_PolygonalFaceOfNodes.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifndef _SMDS_PolygonalFaceOfNodes_HeaderFile @@ -40,6 +41,7 @@ class SMDS_EXPORT SMDS_PolygonalFaceOfNodes:public SMDS_MeshFace SMDS_PolygonalFaceOfNodes (std::vector nodes); virtual SMDSAbs_ElementType GetType() const; + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Polygon; } virtual bool IsPoly() const { return true; }; bool ChangeNodes (std::vector nodes); @@ -58,8 +60,6 @@ class SMDS_EXPORT SMDS_PolygonalFaceOfNodes:public SMDS_MeshFace * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ virtual const SMDS_MeshNode* GetNode(const int ind) const; diff --git a/src/SMDS/SMDS_PolyhedralVolumeOfNodes.cxx b/src/SMDS/SMDS_PolyhedralVolumeOfNodes.cxx index 4b20720f6..b030440ec 100644 --- a/src/SMDS/SMDS_PolyhedralVolumeOfNodes.cxx +++ b/src/SMDS/SMDS_PolyhedralVolumeOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -44,6 +45,7 @@ SMDS_PolyhedralVolumeOfNodes::SMDS_PolyhedralVolumeOfNodes vector quantities) : SMDS_VolumeOfNodes(NULL, NULL, NULL, NULL) { + //MESSAGE("****************************************** SMDS_PolyhedralVolumeOfNodes"); ChangeNodes(nodes, quantities); } @@ -259,5 +261,5 @@ SMDS_ElemIteratorPtr SMDS_PolyhedralVolumeOfNodes::uniqueNodesIterator() const const SMDS_MeshNode* SMDS_PolyhedralVolumeOfNodes::GetNode(const int ind) const { - return myNodesByFaces[ WrappedIndex( ind )]; + return myNodesByFaces[ ind ]; } diff --git a/src/SMDS/SMDS_PolyhedralVolumeOfNodes.hxx b/src/SMDS/SMDS_PolyhedralVolumeOfNodes.hxx index 9cddb1f02..237c92571 100644 --- a/src/SMDS/SMDS_PolyhedralVolumeOfNodes.hxx +++ b/src/SMDS/SMDS_PolyhedralVolumeOfNodes.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_PolyhedralVolumeOfNodes.hxx // Module : SMESH @@ -38,7 +39,8 @@ class SMDS_EXPORT SMDS_PolyhedralVolumeOfNodes:public SMDS_VolumeOfNodes //virtual ~SMDS_PolyhedralVolumeOfNodes(); - virtual SMDSAbs_ElementType GetType() const; + virtual SMDSAbs_ElementType GetType() const; + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Polyhedra; } virtual bool IsPoly() const { return true; }; bool ChangeNodes (const std::vector & nodes, diff --git a/src/SMDS/SMDS_Position.cxx b/src/SMDS/SMDS_Position.cxx index 46b63e1f8..939b50fe9 100644 --- a/src/SMDS/SMDS_Position.cxx +++ b/src/SMDS/SMDS_Position.cxx @@ -1,58 +1,41 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_Position.cxx // Author : Jean-Michel BOULCOURT // Module : SMESH // #include "SMDS_Position.hxx" +#include "utilities.h" //======================================================================= //function : SMDS_Position //purpose : //======================================================================= -SMDS_Position::SMDS_Position(int aShapeId) :myShapeId(aShapeId) -{ -} - -//======================================================================= -//function : SetShapeId -//purpose : -//======================================================================= - -void SMDS_Position::SetShapeId(int aShapeId) -{ - myShapeId = aShapeId; -} - -//======================================================================= -//function : GetShapeId -//purpose : -//======================================================================= - -int SMDS_Position::GetShapeId() const +SMDS_Position::SMDS_Position() { - return myShapeId; + //MESSAGE("########################## SMDS_Position "); } //======================================================================= diff --git a/src/SMDS/SMDS_Position.hxx b/src/SMDS/SMDS_Position.hxx index 687bc098e..3b4530e1d 100644 --- a/src/SMDS/SMDS_Position.hxx +++ b/src/SMDS/SMDS_Position.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_Position.hxx // Module : SMESH @@ -32,24 +33,19 @@ #include class SMDS_Position; -typedef boost::shared_ptr SMDS_PositionPtr; +//typedef boost::shared_ptr SMDS_PositionPtr; +typedef SMDS_Position* SMDS_PositionPtr; class SMDS_EXPORT SMDS_Position { public: - const virtual double * Coords() const = 0; - virtual SMDS_TypeOfPosition GetTypeOfPosition() const = 0; - virtual int GetDim() const; - void SetShapeId(int aShapeId); - int GetShapeId() const; - virtual ~SMDS_Position() {} + virtual SMDS_TypeOfPosition GetTypeOfPosition() const = 0; + virtual int GetDim() const; + virtual ~SMDS_Position() {} protected: - SMDS_Position(int aShapeId); - - private: - int myShapeId; + SMDS_Position(); }; diff --git a/src/SMDS/SMDS_QuadraticEdge.cxx b/src/SMDS/SMDS_QuadraticEdge.cxx index d314cf281..78a3bc1f0 100644 --- a/src/SMDS/SMDS_QuadraticEdge.cxx +++ b/src/SMDS/SMDS_QuadraticEdge.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File: SMDS_QuadraticEdge.cxx // Created: 16.01.06 16:25:42 @@ -29,6 +30,7 @@ #include "SMDS_SetIterator.hxx" #include "SMDS_IteratorOfElements.hxx" #include "SMDS_MeshNode.hxx" +#include "utilities.h" using namespace std; @@ -40,8 +42,9 @@ using namespace std; SMDS_QuadraticEdge::SMDS_QuadraticEdge(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2, const SMDS_MeshNode * node12) - :SMDS_MeshEdge(node1,node2) -{ + :SMDS_LinearEdge(node1,node2) +{ + //MESSAGE("******************************************************* SMDS_QuadraticEdge"); myNodes[2]=node12; } @@ -182,4 +185,3 @@ SMDS_ElemIteratorPtr SMDS_QuadraticEdge::elementsIterator(SMDSAbs_ElementType ty (this,type, SMDS_ElemIteratorPtr(new _MyNodeIterator(myNodes)))); } } - diff --git a/src/SMDS/SMDS_QuadraticEdge.hxx b/src/SMDS/SMDS_QuadraticEdge.hxx index 83877d6a1..2a0d0bc40 100644 --- a/src/SMDS/SMDS_QuadraticEdge.hxx +++ b/src/SMDS/SMDS_QuadraticEdge.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_QuadraticEdge.hxx // Module : SMESH @@ -28,10 +29,10 @@ #include "SMESH_SMDS.hxx" -#include "SMDS_MeshEdge.hxx" +#include "SMDS_LinearEdge.hxx" #include -class SMDS_EXPORT SMDS_QuadraticEdge: public SMDS_MeshEdge +class SMDS_EXPORT SMDS_QuadraticEdge: public SMDS_LinearEdge { public: @@ -47,6 +48,8 @@ public: int NbNodes() const; + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Quad_Edge; } + virtual bool IsQuadratic() const { return true; } virtual bool IsMediumNode(const SMDS_MeshNode* node) const; diff --git a/src/SMDS/SMDS_QuadraticFaceOfNodes.cxx b/src/SMDS/SMDS_QuadraticFaceOfNodes.cxx index b981bd92f..6a70243c0 100644 --- a/src/SMDS/SMDS_QuadraticFaceOfNodes.cxx +++ b/src/SMDS/SMDS_QuadraticFaceOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File: SMDS_QuadraticFaceOfNodes.cxx // Created: 16.01.06 17:12:58 @@ -48,6 +49,7 @@ SMDS_QuadraticFaceOfNodes::SMDS_QuadraticFaceOfNodes(const SMDS_MeshNode * n1, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31) { + //MESSAGE("********************************************** SMDS_QuadraticFaceOfNodes 1"); myNodes.resize( 6 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -72,6 +74,7 @@ SMDS_QuadraticFaceOfNodes::SMDS_QuadraticFaceOfNodes(const SMDS_MeshNode * n1, const SMDS_MeshNode * n34, const SMDS_MeshNode * n41) { + //MESSAGE("********************************************* SMDS_QuadraticFaceOfNodes 2"); myNodes.resize( 8 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -255,7 +258,7 @@ public: _MyEdgeIterator(const SMDS_QuadraticFaceOfNodes* face):myIndex(0) { myElems.reserve( face->NbNodes() ); SMDS_ElemIteratorPtr nIt = face->interlacedNodesElemIterator(); - const SMDS_MeshNode* n0 = face->GetNode( -1 ); + const SMDS_MeshNode* n0 = face->GetNodeWrap( -1 ); while ( nIt->more() ) { const SMDS_MeshNode* n1 = static_cast( nIt->next() ); const SMDS_MeshElement* edge = SMDS_Mesh::FindEdge( n0, n1 ); @@ -300,11 +303,13 @@ SMDS_ElemIteratorPtr SMDS_QuadraticFaceOfNodes::elementsIterator * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ const SMDS_MeshNode* SMDS_QuadraticFaceOfNodes::GetNode(const int ind) const { - return myNodes[ WrappedIndex( ind )]; + return myNodes[ ind ]; } +SMDSAbs_EntityType SMDS_QuadraticFaceOfNodes::GetEntityType() const +{ + return NbNodes() == 6 ? SMDSEntity_Quad_Triangle : SMDSEntity_Quad_Quadrangle; +} diff --git a/src/SMDS/SMDS_QuadraticFaceOfNodes.hxx b/src/SMDS/SMDS_QuadraticFaceOfNodes.hxx index f4284918a..21c2a7e6b 100644 --- a/src/SMDS/SMDS_QuadraticFaceOfNodes.hxx +++ b/src/SMDS/SMDS_QuadraticFaceOfNodes.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_QuadraticVolumeOfNodes.hxx // Module : SMESH @@ -49,6 +50,7 @@ public: const SMDS_MeshNode * n34, const SMDS_MeshNode * n41); + virtual SMDSAbs_EntityType GetEntityType() const; virtual bool IsQuadratic() const { return true; } virtual bool IsMediumNode(const SMDS_MeshNode* node) const; @@ -70,8 +72,6 @@ public: * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ virtual const SMDS_MeshNode* GetNode(const int ind) const; diff --git a/src/SMDS/SMDS_QuadraticVolumeOfNodes.cxx b/src/SMDS/SMDS_QuadraticVolumeOfNodes.cxx index 4a0ae2e7e..579b3e420 100644 --- a/src/SMDS/SMDS_QuadraticVolumeOfNodes.cxx +++ b/src/SMDS/SMDS_QuadraticVolumeOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File: SMDS_QuadraticVolumeOfNodes.cxx // Created: 17.01.06 09:46:11 @@ -53,6 +54,7 @@ SMDS_QuadraticVolumeOfNodes::SMDS_QuadraticVolumeOfNodes const SMDS_MeshNode * n24, const SMDS_MeshNode * n34) { + //MESSAGE("*********************************************** SMDS_QuadraticVolumeOfNodes"); myNodes.resize( 10 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -87,6 +89,7 @@ SMDS_QuadraticVolumeOfNodes::SMDS_QuadraticVolumeOfNodes const SMDS_MeshNode * n35, const SMDS_MeshNode * n45) { + //MESSAGE("*********************************************** SMDS_QuadraticVolumeOfNodes"); myNodes.resize( 13 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -126,6 +129,7 @@ SMDS_QuadraticVolumeOfNodes::SMDS_QuadraticVolumeOfNodes const SMDS_MeshNode * n25, const SMDS_MeshNode * n36) { + //MESSAGE("*********************************************** SMDS_QuadraticVolumeOfNodes"); myNodes.resize( 15 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -172,6 +176,7 @@ SMDS_QuadraticVolumeOfNodes::SMDS_QuadraticVolumeOfNodes const SMDS_MeshNode * n37, const SMDS_MeshNode * n48) { + //MESSAGE("*********************************************** SMDS_QuadraticVolumeOfNodes"); myNodes.resize( 20 ); myNodes[ 0 ] = n1; myNodes[ 1 ] = n2; @@ -362,11 +367,22 @@ SMDS_ElemIteratorPtr SMDS_QuadraticVolumeOfNodes::elementsIterator * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ const SMDS_MeshNode* SMDS_QuadraticVolumeOfNodes::GetNode(const int ind) const { - return myNodes[ WrappedIndex( ind )]; + return myNodes[ ind ]; } +SMDSAbs_EntityType SMDS_QuadraticVolumeOfNodes::GetEntityType() const +{ + SMDSAbs_EntityType aType = SMDSEntity_Quad_Tetra; + switch(NbNodes()) + { + case 10: aType = SMDSEntity_Quad_Tetra; break; + case 13: aType = SMDSEntity_Quad_Pyramid; break; + case 15: aType = SMDSEntity_Quad_Penta; break; + case 20: + default: aType = SMDSEntity_Quad_Hexa; break; + } + return aType; +} diff --git a/src/SMDS/SMDS_QuadraticVolumeOfNodes.hxx b/src/SMDS/SMDS_QuadraticVolumeOfNodes.hxx index 34ce542a6..80312496c 100644 --- a/src/SMDS/SMDS_QuadraticVolumeOfNodes.hxx +++ b/src/SMDS/SMDS_QuadraticVolumeOfNodes.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_QuadraticVolumeOfNodes.hxx // Module : SMESH @@ -99,6 +100,7 @@ public: const SMDS_MeshNode * n37, const SMDS_MeshNode * n48); + virtual SMDSAbs_EntityType GetEntityType() const; virtual bool IsQuadratic() const { return true; } virtual bool IsMediumNode(const SMDS_MeshNode* node) const; @@ -116,8 +118,6 @@ public: * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ virtual const SMDS_MeshNode* GetNode(const int ind) const; diff --git a/src/SMDS/SMDS_SetIterator.hxx b/src/SMDS/SMDS_SetIterator.hxx index e76ea8ce5..a200bdd9f 100644 --- a/src/SMDS/SMDS_SetIterator.hxx +++ b/src/SMDS/SMDS_SetIterator.hxx @@ -1,27 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_SetIterator.hxx -// Created : Mon Feb 27 16:57:43 2006 +// Created : Feb 27 2006 // Author : Edward AGAPOV (eap) // #ifndef SMDS_SetIterator_HeaderFile @@ -29,12 +30,12 @@ #include "SMDS_Iterator.hxx" -/////////////////////////////////////////////////////////////////////////////// -/// Accessors to value pointed by iterator -/////////////////////////////////////////////////////////////////////////////// - namespace SMDS { + /////////////////////////////////////////////////////////////////////////////// + /// Accessors to value pointed by iterator + /////////////////////////////////////////////////////////////////////////////// + template struct SimpleAccessor { static VALUE value(VALUE_SET_ITERATOR it) { return (VALUE) *it; } @@ -49,6 +50,22 @@ namespace SMDS { struct ValueAccessor { static VALUE value(VALUE_SET_ITERATOR it) { return (VALUE) it->second; } }; + + /////////////////////////////////////////////////////////////////////////////// + /// Filters of value pointed by iterator + /////////////////////////////////////////////////////////////////////////////// + + template + struct PassAllValueFilter + { + bool operator()(const VALUE& t ) { return true; } + }; + + template + struct NonNullFilter + { + bool operator()(const VALUE& t ) { return bool( t ); } + }; } /////////////////////////////////////////////////////////////////////////////// @@ -60,26 +77,43 @@ namespace SMDS { template > + typename ACCESOR=SMDS::SimpleAccessor, + typename VALUE_FILTER=SMDS::PassAllValueFilter > class SMDS_SetIterator : public SMDS_Iterator { protected: VALUE_SET_ITERATOR _beg, _end; + VALUE_FILTER _filter; public: SMDS_SetIterator(const VALUE_SET_ITERATOR & begin, - const VALUE_SET_ITERATOR & end) - { init ( begin, end ); } + const VALUE_SET_ITERATOR & end, + const VALUE_FILTER& filter=VALUE_FILTER()) + { init ( begin, end, filter ); } /// Initialization virtual void init(const VALUE_SET_ITERATOR & begin, - const VALUE_SET_ITERATOR & end) - { _beg = begin; _end = end; } - - /// Return true if and only if there are other object in this iterator - virtual bool more() { return _beg != _end; } - + const VALUE_SET_ITERATOR & end, + const VALUE_FILTER& filter=VALUE_FILTER()) + { + _beg = begin; + _end = end; + _filter = filter; + if ( more() && !_filter( ACCESOR::value( _beg ))) + next(); + } + /// Return true iff there are other object in this iterator + virtual bool more() + { + return _beg != _end; + } /// Return the current object and step to the next one - virtual VALUE next() { return ACCESOR::value( _beg++ ); } + virtual VALUE next() + { + VALUE ret = ACCESOR::value( _beg++ ); + while ( more() && !_filter( ACCESOR::value( _beg ))) + ++_beg; + return ret; + } }; /////////////////////////////////////////////////////////////////////////////// diff --git a/src/SMDS/SMDS_SpacePosition.cxx b/src/SMDS/SMDS_SpacePosition.cxx index 09532f347..422de6a3f 100644 --- a/src/SMDS/SMDS_SpacePosition.cxx +++ b/src/SMDS/SMDS_SpacePosition.cxx @@ -1,58 +1,45 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_SpacePosition.cxx // Author : Jean-Michel BOULCOURT // Module : SMESH // + #include "SMDS_SpacePosition.hxx" -//======================================================================= -//function : SMDS_SpacePosition -//purpose : -//======================================================================= +SMDS_SpacePosition* SMDS_SpacePosition::_originPosition = new SMDS_SpacePosition(); -SMDS_SpacePosition::SMDS_SpacePosition(double x, double y, double z): - SMDS_Position(0) +SMDS_SpacePosition::SMDS_SpacePosition(double x, double y, double z) { - myCoords[0]=x; - myCoords[1]=y; - myCoords[2]=z; } -/** -*/ SMDS_TypeOfPosition SMDS_SpacePosition::GetTypeOfPosition() const { - return SMDS_TOP_3DSPACE; -} - -const double * SMDS_SpacePosition::Coords() const -{ - return myCoords; + return SMDS_TOP_3DSPACE; } SMDS_PositionPtr SMDS_SpacePosition::originSpacePosition() { - static SMDS_PositionPtr staticpos (new SMDS_SpacePosition()); - return staticpos; + return _originPosition; } diff --git a/src/SMDS/SMDS_SpacePosition.hxx b/src/SMDS/SMDS_SpacePosition.hxx index f80f3210a..1313ed5cc 100644 --- a/src/SMDS/SMDS_SpacePosition.hxx +++ b/src/SMDS/SMDS_SpacePosition.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_SpacePosition.hxx // Module : SMESH @@ -33,14 +34,12 @@ class SMDS_EXPORT SMDS_SpacePosition:public SMDS_Position { - public: - SMDS_SpacePosition(double x=0, double y=0, double z=0); - const virtual double * Coords() const; - virtual inline SMDS_TypeOfPosition GetTypeOfPosition() const; - inline void SetCoords(const double x, const double y, const double z); - static SMDS_PositionPtr originSpacePosition(); - private: - double myCoords[3]; +public: + SMDS_SpacePosition(double x=0, double y=0, double z=0); + virtual inline SMDS_TypeOfPosition GetTypeOfPosition() const; + static SMDS_PositionPtr originSpacePosition(); +private: + static SMDS_SpacePosition* _originPosition; }; #endif diff --git a/src/SMDS/SMDS_StdIterator.hxx b/src/SMDS/SMDS_StdIterator.hxx new file mode 100644 index 000000000..f09647f1f --- /dev/null +++ b/src/SMDS/SMDS_StdIterator.hxx @@ -0,0 +1,80 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_StdIterator.hxx +// Created : Fri Feb 5 11:03:46 2010 +// Author : Edward AGAPOV (eap) +// +#ifndef __SMDS_StdIterator_HXX__ +#define __SMDS_StdIterator_HXX__ + + +/////////////////////////////////////////////////////////////////////////////// +/*! + * \brief Wrapper over pointer to SMDS_Iterator, like SMDS_ElemIteratorPtr, enabling + * its usage in std-like way: provide operators ++, *, etc. + */ +/////////////////////////////////////////////////////////////////////////////// + +template > +class SMDS_StdIterator : public std::iterator< std::input_iterator_tag, VALUE > +{ + VALUE _value; + PtrSMDSIterator _piterator; + EqualVALUE _EqualVALUE; + +public: + typedef SMDS_StdIterator _Self; + + // constructor to use as return from begin() + SMDS_StdIterator( PtrSMDSIterator pItr ) + : _value( pItr->more() ? (VALUE)(pItr->next()) : 0 ), _piterator(pItr) + {} + // constructor to use as return from end() + SMDS_StdIterator(): _value( 0 ) + {} + + /// Return the current object + VALUE operator*() const + { return _value; } + + // Step to the next one + _Self& + operator++() + { _value = _piterator->more() ? VALUE( _piterator->next()) : 0; return *this; } + + // Step to the next one + _Self + operator++(int) + { _Self res = *this; _value = _piterator->more() ? VALUE( _piterator->next()) : 0; return res; } + + // Test of end + bool + operator!=(const _Self& __x) const + { return !_EqualVALUE( _value, __x._value); } + + // Test of equality + bool + operator==(const _Self& __x) const + { return _EqualVALUE( _value, __x._value); } + +}; + +#endif diff --git a/src/SMDS/SMDS_TypeOfPosition.hxx b/src/SMDS/SMDS_TypeOfPosition.hxx index da5b39c48..c1c7249b4 100644 --- a/src/SMDS/SMDS_TypeOfPosition.hxx +++ b/src/SMDS/SMDS_TypeOfPosition.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_TypeOfPosition.hxx // Module : SMESH @@ -28,11 +29,11 @@ enum SMDS_TypeOfPosition // Value is equal to shape dimention { - SMDS_TOP_UNSPEC = -1, - SMDS_TOP_VERTEX = 0, - SMDS_TOP_EDGE = 1, - SMDS_TOP_FACE = 2, - SMDS_TOP_3DSPACE = 3 + SMDS_TOP_UNSPEC = -1, + SMDS_TOP_VERTEX = 0, + SMDS_TOP_EDGE = 1, + SMDS_TOP_FACE = 2, + SMDS_TOP_3DSPACE = 3 }; #endif diff --git a/src/SMDS/SMDS_UnstructuredGrid.cxx b/src/SMDS/SMDS_UnstructuredGrid.cxx new file mode 100644 index 000000000..0d9b1e6c3 --- /dev/null +++ b/src/SMDS/SMDS_UnstructuredGrid.cxx @@ -0,0 +1,1145 @@ +// Copyright (C) 2010-2012 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 +// + +#define CHRONODEF +#include "SMDS_UnstructuredGrid.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_MeshInfo.hxx" +#include "SMDS_Downward.hxx" +#include "SMDS_MeshVolume.hxx" + +#include "utilities.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; + +SMDS_CellLinks* SMDS_CellLinks::New() +{ + MESSAGE("SMDS_CellLinks::New"); + return new SMDS_CellLinks(); +} + +vtkCellLinks::Link* SMDS_CellLinks::ResizeL(vtkIdType sz) +{ + return vtkCellLinks::Resize(sz); +} + +vtkIdType SMDS_CellLinks::GetLinksSize() +{ + return this->Size; +} + +SMDS_CellLinks::SMDS_CellLinks() : + vtkCellLinks() +{ +} + +SMDS_CellLinks::~SMDS_CellLinks() +{ +} + +SMDS_UnstructuredGrid* SMDS_UnstructuredGrid::New() +{ + MESSAGE("SMDS_UnstructuredGrid::New"); + return new SMDS_UnstructuredGrid(); +} + +SMDS_UnstructuredGrid::SMDS_UnstructuredGrid() : + vtkUnstructuredGrid() +{ + _cellIdToDownId.clear(); + _downTypes.clear(); + _downArray.clear(); + _mesh = 0; +} + +SMDS_UnstructuredGrid::~SMDS_UnstructuredGrid() +{ +} + +unsigned long SMDS_UnstructuredGrid::GetMTime() +{ + unsigned long mtime = vtkUnstructuredGrid::GetMTime(); + MESSAGE("vtkUnstructuredGrid::GetMTime: " << mtime); + return mtime; +} + +void SMDS_UnstructuredGrid::Update() +{ + MESSAGE("SMDS_UnstructuredGrid::Update"); + return vtkUnstructuredGrid::Update(); +} + +void SMDS_UnstructuredGrid::UpdateInformation() +{ + MESSAGE("SMDS_UnstructuredGrid::UpdateInformation"); + return vtkUnstructuredGrid::UpdateInformation(); +} + +vtkPoints* SMDS_UnstructuredGrid::GetPoints() +{ + // TODO erreur incomprehensible de la macro vtk GetPoints apparue avec la version paraview de fin aout 2010 + //MESSAGE("*********************** SMDS_UnstructuredGrid::GetPoints " << this->Points << " " << vtkUnstructuredGrid::GetPoints()); + return this->Points; +} + +//#ifdef VTK_HAVE_POLYHEDRON +int SMDS_UnstructuredGrid::InsertNextLinkedCell(int type, int npts, vtkIdType *pts) +{ + if (type != VTK_POLYHEDRON) + return vtkUnstructuredGrid::InsertNextLinkedCell(type, npts, pts); + + // --- type = VTK_POLYHEDRON + //MESSAGE("InsertNextLinkedCell VTK_POLYHEDRON"); + int cellid = this->InsertNextCell(type, npts, pts); + + set setOfNodes; + setOfNodes.clear(); + int nbfaces = npts; + int i = 0; + for (int nf = 0; nf < nbfaces; nf++) + { + int nbnodes = pts[i]; + i++; + for (int k = 0; k < nbnodes; k++) + { + //MESSAGE(" cell " << cellid << " face " << nf << " node " << pts[i]); + setOfNodes.insert(pts[i]); + i++; + } + } + + set::iterator it = setOfNodes.begin(); + for (; it != setOfNodes.end(); ++it) + { + //MESSAGE("reverse link for node " << *it << " cell " << cellid); + this->Links->ResizeCellList(*it, 1); + this->Links->AddCellReference(cellid, *it); + } + + return cellid; +} +//#endif + +void SMDS_UnstructuredGrid::setSMDS_mesh(SMDS_Mesh *mesh) +{ + _mesh = mesh; +} + +void SMDS_UnstructuredGrid::compactGrid(std::vector& idNodesOldToNew, int newNodeSize, + std::vector& idCellsOldToNew, int newCellSize) +{ + MESSAGE("------------------------- SMDS_UnstructuredGrid::compactGrid " << newNodeSize << " " << newCellSize);CHRONO(1); + int alreadyCopied = 0; + + // --- if newNodeSize, create a new compacted vtkPoints + + vtkPoints *newPoints = vtkPoints::New(); + newPoints->SetDataType(VTK_DOUBLE); + newPoints->SetNumberOfPoints(newNodeSize); + if (newNodeSize) + { + MESSAGE("-------------- compactGrid, newNodeSize " << newNodeSize); + // rnv: to fix bug "21125: EDF 1233 SMESH: Degradation of precision in a test case for quadratic conversion" + // using double type for storing coordinates of nodes instead float. + int oldNodeSize = idNodesOldToNew.size(); + + int i = 0; + while ( i < oldNodeSize ) + { + // skip a hole if any + while ( i < oldNodeSize && idNodesOldToNew[i] < 0 ) + ++i; + int startBloc = i; + // look for a block end + while ( i < oldNodeSize && idNodesOldToNew[i] >= 0 ) + ++i; + int endBloc = i; + copyNodes(newPoints, idNodesOldToNew, alreadyCopied, startBloc, endBloc); + } + newPoints->Squeeze(); + } + + // --- create new compacted Connectivity, Locations and Types + + int oldCellSize = this->Types->GetNumberOfTuples(); + + vtkCellArray *newConnectivity = vtkCellArray::New(); + newConnectivity->Initialize(); + int oldCellDataSize = this->Connectivity->GetData()->GetSize(); + newConnectivity->Allocate(oldCellDataSize); + MESSAGE("oldCellSize="<< oldCellSize << " oldCellDataSize=" << oldCellDataSize); + + vtkUnsignedCharArray *newTypes = vtkUnsignedCharArray::New(); + newTypes->Initialize(); + newTypes->SetNumberOfValues(newCellSize); + + vtkIdTypeArray *newLocations = vtkIdTypeArray::New(); + newLocations->Initialize(); + newLocations->SetNumberOfValues(newCellSize); + + // TODO some polyhedron may be huge (only in some tests) + vtkIdType tmpid[NBMAXNODESINCELL]; + vtkIdType *pointsCell = &tmpid[0]; // --- points id to fill a new cell + + alreadyCopied = 0; + int i = 0; + while ( i < oldCellSize ) + { + // skip a hole if any + while ( i < oldCellSize && this->Types->GetValue(i) == VTK_EMPTY_CELL ) + ++i; + int startBloc = i; + // look for a block end + while ( i < oldCellSize && this->Types->GetValue(i) != VTK_EMPTY_CELL ) + ++i; + int endBloc = i; + if ( endBloc > startBloc ) + copyBloc(newTypes, + idCellsOldToNew, idNodesOldToNew, + newConnectivity, newLocations, + pointsCell, alreadyCopied, + startBloc, endBloc); + } + newConnectivity->Squeeze(); + + if (1/*newNodeSize*/) + { + MESSAGE("------- newNodeSize, setPoints"); + this->SetPoints(newPoints); + MESSAGE("NumberOfPoints: " << this->GetNumberOfPoints()); + } + + if (vtkDoubleArray* diameters = + vtkDoubleArray::SafeDownCast( vtkDataSet::CellData->GetScalars() )) // Balls + { + for (int oldCellID = 0; oldCellID < oldCellSize; oldCellID++) + { + if (this->Types->GetValue(oldCellID) == VTK_EMPTY_CELL) + continue; + int newCellId = idCellsOldToNew[ oldCellID ]; + if (newTypes->GetValue(newCellId) == VTK_POLY_VERTEX) + diameters->SetValue( newCellId, diameters->GetValue( oldCellID )); + } + } + + if (this->FaceLocations) + { + vtkIdTypeArray *newFaceLocations = vtkIdTypeArray::New(); + newFaceLocations->Initialize(); + newFaceLocations->Allocate(newTypes->GetSize()); + vtkIdTypeArray *newFaces = vtkIdTypeArray::New(); + newFaces->Initialize(); + newFaces->Allocate(this->Faces->GetSize()); + for (int i = 0; i < oldCellSize; i++) + { + if (this->Types->GetValue(i) == VTK_EMPTY_CELL) + continue; + int newCellId = idCellsOldToNew[i]; + if (newTypes->GetValue(newCellId) == VTK_POLYHEDRON) + { + newFaceLocations->InsertNextValue(newFaces->GetMaxId()+1); + int oldFaceLoc = this->FaceLocations->GetValue(i); + int nCellFaces = this->Faces->GetValue(oldFaceLoc++); + newFaces->InsertNextValue(nCellFaces); + for (int n=0; nFaces->GetValue(oldFaceLoc++); + newFaces->InsertNextValue(nptsInFace); + for (int k=0; kFaces->GetValue(oldFaceLoc++); + newFaces->InsertNextValue(idNodesOldToNew[oldpt]); + } + } + } + else + { + newFaceLocations->InsertNextValue(-1); + } + } + newFaceLocations->Squeeze(); + newFaces->Squeeze(); + newFaceLocations->Register(this); + newFaces->Register(this); + this->SetCells(newTypes, newLocations, newConnectivity, newFaceLocations, newFaces); + newFaceLocations->Delete(); + newFaces->Delete(); + } + else + { + this->SetCells(newTypes, newLocations, newConnectivity, FaceLocations, Faces); + } + + newPoints->Delete(); + newTypes->Delete(); + newLocations->Delete(); + newConnectivity->Delete(); + this->BuildLinks(); +} + +void SMDS_UnstructuredGrid::copyNodes(vtkPoints *newPoints, std::vector& idNodesOldToNew, int& alreadyCopied, + int start, int end) +{ + MESSAGE("copyNodes " << alreadyCopied << " " << start << " " << end << " size: " << end - start << " total: " << alreadyCopied + end - start); + void *target = newPoints->GetVoidPointer(3 * alreadyCopied); + void *source = this->Points->GetVoidPointer(3 * start); + int nbPoints = end - start; + if (nbPoints > 0) + { + memcpy(target, source, 3 * sizeof(double) * nbPoints); + for (int j = start; j < end; j++) + idNodesOldToNew[j] = alreadyCopied++; // old vtkId --> new vtkId + } +} + +void SMDS_UnstructuredGrid::copyBloc(vtkUnsignedCharArray *newTypes, + std::vector& idCellsOldToNew, + std::vector& idNodesOldToNew, + vtkCellArray* newConnectivity, + vtkIdTypeArray* newLocations, + vtkIdType* pointsCell, + int& alreadyCopied, + int start, + int end) +{ + MESSAGE("copyBloc " << alreadyCopied << " " << start << " " << end << " size: " << end - start << " total: " << alreadyCopied + end - start); + for (int j = start; j < end; j++) + { + newTypes->SetValue(alreadyCopied, this->Types->GetValue(j)); + idCellsOldToNew[j] = alreadyCopied; // old vtkId --> new vtkId + vtkIdType oldLoc = this->Locations->GetValue(j); + vtkIdType nbpts; + vtkIdType *oldPtsCell = 0; + this->Connectivity->GetCell(oldLoc, nbpts, oldPtsCell); + assert(nbpts < NBMAXNODESINCELL); + //MESSAGE(j << " " << alreadyCopied << " " << (int)this->Types->GetValue(j) << " " << oldLoc << " " << nbpts ); + for (int l = 0; l < nbpts; l++) + { + int oldval = oldPtsCell[l]; + pointsCell[l] = idNodesOldToNew[oldval]; + //MESSAGE(" " << oldval << " " << pointsCell[l]); + } + /*int newcnt = */newConnectivity->InsertNextCell(nbpts, pointsCell); + int newLoc = newConnectivity->GetInsertLocation(nbpts); + //MESSAGE(newcnt << " " << newLoc); + newLocations->SetValue(alreadyCopied, newLoc); + alreadyCopied++; + } +} + +int SMDS_UnstructuredGrid::CellIdToDownId(int vtkCellId) +{ + if((vtkCellId < 0) || (vtkCellId >= _cellIdToDownId.size())) + { + //MESSAGE("SMDS_UnstructuredGrid::CellIdToDownId structure not up to date: vtkCellId=" + // << vtkCellId << " max="<< _cellIdToDownId.size()); + return -1; + } + return _cellIdToDownId[vtkCellId]; +} + +void SMDS_UnstructuredGrid::setCellIdToDownId(int vtkCellId, int downId) +{ + // ASSERT((vtkCellId >= 0) && (vtkCellId < _cellIdToDownId.size())); + _cellIdToDownId[vtkCellId] = downId; +} + +void SMDS_UnstructuredGrid::CleanDownwardConnectivity() +{ + for (int i = 0; i < _downArray.size(); i++) + { + if (_downArray[i]) + delete _downArray[i]; + _downArray[i] = 0; + } + _cellIdToDownId.clear(); +} + +/*! Build downward connectivity: to do only when needed because heavy memory load. + * Downward connectivity is no more valid if vtkUnstructuredGrid is modified. + * + */ +void SMDS_UnstructuredGrid::BuildDownwardConnectivity(bool withEdges) +{ + MESSAGE("SMDS_UnstructuredGrid::BuildDownwardConnectivity");CHRONO(2); + // TODO calcul partiel sans edges + + // --- erase previous data if any + + this->CleanDownwardConnectivity(); + + // --- create SMDS_Downward structures (in _downArray vector[vtkCellType]) + + _downArray.resize(VTK_MAXTYPE + 1, 0); + + _downArray[VTK_LINE] = new SMDS_DownEdge(this); + _downArray[VTK_QUADRATIC_EDGE] = new SMDS_DownQuadEdge(this); + _downArray[VTK_TRIANGLE] = new SMDS_DownTriangle(this); + _downArray[VTK_QUADRATIC_TRIANGLE] = new SMDS_DownQuadTriangle(this); + _downArray[VTK_QUAD] = new SMDS_DownQuadrangle(this); + _downArray[VTK_QUADRATIC_QUAD] = new SMDS_DownQuadQuadrangle(this); + _downArray[VTK_BIQUADRATIC_QUAD] = new SMDS_DownQuadQuadrangle(this); + _downArray[VTK_TETRA] = new SMDS_DownTetra(this); + _downArray[VTK_QUADRATIC_TETRA] = new SMDS_DownQuadTetra(this); + _downArray[VTK_PYRAMID] = new SMDS_DownPyramid(this); + _downArray[VTK_QUADRATIC_PYRAMID] = new SMDS_DownQuadPyramid(this); + _downArray[VTK_WEDGE] = new SMDS_DownPenta(this); + _downArray[VTK_QUADRATIC_WEDGE] = new SMDS_DownQuadPenta(this); + _downArray[VTK_HEXAHEDRON] = new SMDS_DownHexa(this); + _downArray[VTK_QUADRATIC_HEXAHEDRON] = new SMDS_DownQuadHexa(this); + _downArray[VTK_TRIQUADRATIC_HEXAHEDRON] = new SMDS_DownQuadHexa(this); + _downArray[VTK_HEXAGONAL_PRISM] = new SMDS_DownPenta(this); + + // --- get detailed info of number of cells of each type, allocate SMDS_downward structures + + const SMDS_MeshInfo &meshInfo = _mesh->GetMeshInfo(); + + int nbLinTetra = meshInfo.NbTetras (ORDER_LINEAR); + int nbQuadTetra = meshInfo.NbTetras (ORDER_QUADRATIC); + int nbLinPyra = meshInfo.NbPyramids(ORDER_LINEAR); + int nbQuadPyra = meshInfo.NbPyramids(ORDER_QUADRATIC); + int nbLinPrism = meshInfo.NbPrisms (ORDER_LINEAR); + int nbQuadPrism = meshInfo.NbPrisms (ORDER_QUADRATIC); + int nbLinHexa = meshInfo.NbHexas (ORDER_LINEAR); + int nbQuadHexa = meshInfo.NbHexas (ORDER_QUADRATIC); + int nbHexPrism = meshInfo.NbHexPrisms(); + + int nbLineGuess = int((4.0 / 3.0) * nbLinTetra + 2 * nbLinPrism + 2.5 * nbLinPyra + 3 * nbLinHexa); + int nbQuadEdgeGuess = int((4.0 / 3.0) * nbQuadTetra + 2 * nbQuadPrism + 2.5 * nbQuadPyra + 3 * nbQuadHexa); + int nbLinTriaGuess = 2 * nbLinTetra + nbLinPrism + 2 * nbLinPyra; + int nbQuadTriaGuess = 2 * nbQuadTetra + nbQuadPrism + 2 * nbQuadPyra; + int nbLinQuadGuess = int((2.0 / 3.0) * nbLinPrism + (1.0 / 2.0) * nbLinPyra + 3 * nbLinHexa); + int nbQuadQuadGuess = int((2.0 / 3.0) * nbQuadPrism + (1.0 / 2.0) * nbQuadPyra + 3 * nbQuadHexa); + + int GuessSize[VTK_MAXTYPE]; + GuessSize[VTK_LINE] = nbLineGuess; + GuessSize[VTK_QUADRATIC_EDGE] = nbQuadEdgeGuess; + GuessSize[VTK_TRIANGLE] = nbLinTriaGuess; + GuessSize[VTK_QUADRATIC_TRIANGLE] = nbQuadTriaGuess; + GuessSize[VTK_QUAD] = nbLinQuadGuess; + GuessSize[VTK_QUADRATIC_QUAD] = nbQuadQuadGuess; + GuessSize[VTK_BIQUADRATIC_QUAD] = nbQuadQuadGuess; + GuessSize[VTK_TETRA] = nbLinTetra; + GuessSize[VTK_QUADRATIC_TETRA] = nbQuadTetra; + GuessSize[VTK_PYRAMID] = nbLinPyra; + GuessSize[VTK_QUADRATIC_PYRAMID] = nbQuadPyra; + GuessSize[VTK_WEDGE] = nbLinPrism; + GuessSize[VTK_QUADRATIC_WEDGE] = nbQuadPrism; + GuessSize[VTK_HEXAHEDRON] = nbLinHexa; + GuessSize[VTK_QUADRATIC_HEXAHEDRON] = nbQuadHexa; + GuessSize[VTK_TRIQUADRATIC_HEXAHEDRON] = nbQuadHexa; + GuessSize[VTK_HEXAGONAL_PRISM] = nbHexPrism; + + _downArray[VTK_LINE] ->allocate(nbLineGuess); + _downArray[VTK_QUADRATIC_EDGE] ->allocate(nbQuadEdgeGuess); + _downArray[VTK_TRIANGLE] ->allocate(nbLinTriaGuess); + _downArray[VTK_QUADRATIC_TRIANGLE] ->allocate(nbQuadTriaGuess); + _downArray[VTK_QUAD] ->allocate(nbLinQuadGuess); + _downArray[VTK_QUADRATIC_QUAD] ->allocate(nbQuadQuadGuess); + _downArray[VTK_BIQUADRATIC_QUAD] ->allocate(nbQuadQuadGuess); + _downArray[VTK_TETRA] ->allocate(nbLinTetra); + _downArray[VTK_QUADRATIC_TETRA] ->allocate(nbQuadTetra); + _downArray[VTK_PYRAMID] ->allocate(nbLinPyra); + _downArray[VTK_QUADRATIC_PYRAMID] ->allocate(nbQuadPyra); + _downArray[VTK_WEDGE] ->allocate(nbLinPrism); + _downArray[VTK_QUADRATIC_WEDGE] ->allocate(nbQuadPrism); + _downArray[VTK_HEXAHEDRON] ->allocate(nbLinHexa); + _downArray[VTK_QUADRATIC_HEXAHEDRON] ->allocate(nbQuadHexa); + _downArray[VTK_TRIQUADRATIC_HEXAHEDRON]->allocate(nbQuadHexa); + _downArray[VTK_HEXAGONAL_PRISM] ->allocate(nbHexPrism); + + // --- iteration on vtkUnstructuredGrid cells, only faces + // for each vtk face: + // create a downward face entry with its downward id. + // compute vtk volumes, create downward volumes entry. + // mark face in downward volumes + // mark volumes in downward face + + MESSAGE("--- iteration on vtkUnstructuredGrid cells, only faces");CHRONO(20); + int cellSize = this->Types->GetNumberOfTuples(); + _cellIdToDownId.resize(cellSize, -1); + + for (int i = 0; i < cellSize; i++) + { + int vtkFaceType = this->GetCellType(i); + if (SMDS_Downward::getCellDimension(vtkFaceType) == 2) + { + int vtkFaceId = i; + //ASSERT(_downArray[vtkFaceType]); + int connFaceId = _downArray[vtkFaceType]->addCell(vtkFaceId); + SMDS_Down2D* downFace = static_cast (_downArray[vtkFaceType]); + downFace->setTempNodes(connFaceId, vtkFaceId); + int vols[2] = { -1, -1 }; + int nbVolumes = downFace->computeVolumeIds(vtkFaceId, vols); + //MESSAGE("nbVolumes="<< nbVolumes); + for (int ivol = 0; ivol < nbVolumes; ivol++) + { + int vtkVolId = vols[ivol]; + int vtkVolType = this->GetCellType(vtkVolId); + //ASSERT(_downArray[vtkVolType]); + int connVolId = _downArray[vtkVolType]->addCell(vtkVolId); + _downArray[vtkVolType]->addDownCell(connVolId, connFaceId, vtkFaceType); + _downArray[vtkFaceType]->addUpCell(connFaceId, connVolId, vtkVolType); + // MESSAGE("Face " << vtkFaceId << " belongs to volume " << vtkVolId); + } + } + } + + // --- iteration on vtkUnstructuredGrid cells, only volumes + // for each vtk volume: + // create downward volumes entry if not already done + // build a temporary list of faces described with their nodes + // for each face + // compute the vtk volumes containing this face + // check if the face is already listed in the volumes (comparison of ordered list of nodes) + // if not, create a downward face entry (resizing of structure required) + // (the downward faces store a temporary list of nodes to ease the comparison) + // create downward volumes entry if not already done + // mark volumes in downward face + // mark face in downward volumes + + CHRONOSTOP(20); + MESSAGE("--- iteration on vtkUnstructuredGrid cells, only volumes");CHRONO(21); + + for (int i = 0; i < cellSize; i++) + { + int vtkType = this->GetCellType(i); + if (SMDS_Downward::getCellDimension(vtkType) == 3) + { + //CHRONO(31); + int vtkVolId = i; + // MESSAGE("vtk volume " << vtkVolId); + //ASSERT(_downArray[vtkType]); + /*int connVolId = */_downArray[vtkType]->addCell(vtkVolId); + + // --- find all the faces of the volume, describe the faces by their nodes + + SMDS_Down3D* downVol = static_cast (_downArray[vtkType]); + ListElemByNodesType facesWithNodes; + downVol->computeFacesWithNodes(vtkVolId, facesWithNodes); + // MESSAGE("vtk volume " << vtkVolId << " contains " << facesWithNodes.nbElems << " faces"); + //CHRONOSTOP(31); + for (int iface = 0; iface < facesWithNodes.nbElems; iface++) + { + // --- find the volumes containing the face + + //CHRONO(32); + int vtkFaceType = facesWithNodes.elems[iface].vtkType; + SMDS_Down2D* downFace = static_cast (_downArray[vtkFaceType]); + int vols[2] = { -1, -1 }; + int *nodes = &facesWithNodes.elems[iface].nodeIds[0]; + int lg = facesWithNodes.elems[iface].nbNodes; + int nbVolumes = downFace->computeVolumeIdsFromNodesFace(nodes, lg, vols); + // MESSAGE("vtk volume " << vtkVolId << " face " << iface << " belongs to " << nbVolumes << " volumes"); + + // --- check if face is registered in the volumes + //CHRONOSTOP(32); + + //CHRONO(33); + int connFaceId = -1; + for (int ivol = 0; ivol < nbVolumes; ivol++) + { + int vtkVolId2 = vols[ivol]; + int vtkVolType = this->GetCellType(vtkVolId2); + //ASSERT(_downArray[vtkVolType]); + int connVolId2 = _downArray[vtkVolType]->addCell(vtkVolId2); + SMDS_Down3D* downVol2 = static_cast (_downArray[vtkVolType]); + connFaceId = downVol2->FindFaceByNodes(connVolId2, facesWithNodes.elems[iface]); + if (connFaceId >= 0) + break; // --- face already created + }//CHRONOSTOP(33); + + // --- if face is not registered in the volumes, create face + + //CHRONO(34); + if (connFaceId < 0) + { + connFaceId = _downArray[vtkFaceType]->addCell(); + downFace->setTempNodes(connFaceId, facesWithNodes.elems[iface]); + }//CHRONOSTOP(34); + + // --- mark volumes in downward face and mark face in downward volumes + + //CHRONO(35); + for (int ivol = 0; ivol < nbVolumes; ivol++) + { + int vtkVolId2 = vols[ivol]; + int vtkVolType = this->GetCellType(vtkVolId2); + //ASSERT(_downArray[vtkVolType]); + int connVolId2 = _downArray[vtkVolType]->addCell(vtkVolId2); + _downArray[vtkVolType]->addDownCell(connVolId2, connFaceId, vtkFaceType); + _downArray[vtkFaceType]->addUpCell(connFaceId, connVolId2, vtkVolType); + // MESSAGE(" From volume " << vtkVolId << " face " << connFaceId << " belongs to volume " << vtkVolId2); + }//CHRONOSTOP(35); + } + } + } + + // --- iteration on vtkUnstructuredGrid cells, only edges + // for each vtk edge: + // create downward edge entry + // store the nodes id's in downward edge (redundant with vtkUnstructuredGrid) + // find downward faces + // (from vtk faces or volumes, get downward faces, they have a temporary list of nodes) + // mark edge in downward faces + // mark faces in downward edge + + CHRONOSTOP(21); + MESSAGE("--- iteration on vtkUnstructuredGrid cells, only edges");CHRONO(22); + + for (int i = 0; i < cellSize; i++) + { + int vtkEdgeType = this->GetCellType(i); + if (SMDS_Downward::getCellDimension(vtkEdgeType) == 1) + { + int vtkEdgeId = i; + //ASSERT(_downArray[vtkEdgeType]); + int connEdgeId = _downArray[vtkEdgeType]->addCell(vtkEdgeId); + SMDS_Down1D* downEdge = static_cast (_downArray[vtkEdgeType]); + downEdge->setNodes(connEdgeId, vtkEdgeId); + vector vtkIds; + int nbVtkCells = downEdge->computeVtkCells(connEdgeId, vtkIds); + int downFaces[1000]; + unsigned char downTypes[1000]; + int nbDownFaces = downEdge->computeFaces(connEdgeId, &vtkIds[0], nbVtkCells, downFaces, downTypes); + for (int n = 0; n < nbDownFaces; n++) + { + _downArray[downTypes[n]]->addDownCell(downFaces[n], connEdgeId, vtkEdgeType); + _downArray[vtkEdgeType]->addUpCell(connEdgeId, downFaces[n], downTypes[n]); + } + } + } + + // --- iteration on downward faces (they are all listed now) + // for each downward face: + // build a temporary list of edges with their ordered list of nodes + // for each edge: + // find all the vtk cells containing this edge + // then identify all the downward faces containing the edge, from the vtk cells + // check if the edge is already listed in the faces (comparison of ordered list of nodes) + // if not, create a downward edge entry with the node id's + // mark edge in downward faces + // mark downward faces in edge (size of list unknown, to be allocated) + + CHRONOSTOP(22);CHRONO(23); + + for (int vtkFaceType = 0; vtkFaceType < VTK_QUADRATIC_PYRAMID; vtkFaceType++) + { + if (SMDS_Downward::getCellDimension(vtkFaceType) != 2) + continue; + + // --- find all the edges of the face, describe the edges by their nodes + + SMDS_Down2D* downFace = static_cast (_downArray[vtkFaceType]); + int maxId = downFace->getMaxId(); + for (int faceId = 0; faceId < maxId; faceId++) + { + //CHRONO(40); + ListElemByNodesType edgesWithNodes; + downFace->computeEdgesWithNodes(faceId, edgesWithNodes); + // MESSAGE("downward face type " << vtkFaceType << " num " << faceId << " contains " << edgesWithNodes.nbElems << " edges"); + + //CHRONOSTOP(40); + for (int iedge = 0; iedge < edgesWithNodes.nbElems; iedge++) + { + + // --- check if the edge is already registered by exploration of the faces + + //CHRONO(41); + vector vtkIds; + unsigned char vtkEdgeType = edgesWithNodes.elems[iedge].vtkType; + int *pts = &edgesWithNodes.elems[iedge].nodeIds[0]; + SMDS_Down1D* downEdge = static_cast (_downArray[vtkEdgeType]); + int nbVtkCells = downEdge->computeVtkCells(pts, vtkIds); + //CHRONOSTOP(41);CHRONO(42); + int downFaces[1000]; + unsigned char downTypes[1000]; + int nbDownFaces = downEdge->computeFaces(pts, &vtkIds[0], nbVtkCells, downFaces, downTypes); + //CHRONOSTOP(42); + + //CHRONO(43); + int connEdgeId = -1; + for (int idf = 0; idf < nbDownFaces; idf++) + { + int faceId2 = downFaces[idf]; + int faceType = downTypes[idf]; + //ASSERT(_downArray[faceType]); + SMDS_Down2D* downFace2 = static_cast (_downArray[faceType]); + connEdgeId = downFace2->FindEdgeByNodes(faceId2, edgesWithNodes.elems[iedge]); + if (connEdgeId >= 0) + break; // --- edge already created + }//CHRONOSTOP(43); + + // --- if edge is not registered in the faces, create edge + + if (connEdgeId < 0) + { + //CHRONO(44); + connEdgeId = _downArray[vtkEdgeType]->addCell(); + downEdge->setNodes(connEdgeId, edgesWithNodes.elems[iedge].nodeIds); + //CHRONOSTOP(44); + } + + // --- mark faces in downward edge and mark edge in downward faces + + //CHRONO(45); + for (int idf = 0; idf < nbDownFaces; idf++) + { + int faceId2 = downFaces[idf]; + int faceType = downTypes[idf]; + //ASSERT(_downArray[faceType]); + //SMDS_Down2D* downFace2 = static_cast (_downArray[faceType]); + _downArray[vtkEdgeType]->addUpCell(connEdgeId, faceId2, faceType); + _downArray[faceType]->addDownCell(faceId2, connEdgeId, vtkEdgeType); + // MESSAGE(" From face t:" << vtkFaceType << " " << faceId << + // " edge " << connEdgeId << " belongs to face t:" << faceType << " " << faceId2); + }//CHRONOSTOP(45); + } + } + } + + CHRONOSTOP(23);CHRONO(24); + + // compact downward connectivity structure: adjust downward arrays size, replace vector> by a single vector + // 3D first then 2D and last 1D to release memory before edge upCells reorganization, (temporary memory use) + + for (int vtkType = VTK_QUADRATIC_PYRAMID; vtkType >= 0; vtkType--) + { + if (SMDS_Downward *down = _downArray[vtkType]) + { + down->compactStorage(); + } + } + + // --- Statistics + + for (int vtkType = 0; vtkType <= VTK_QUADRATIC_PYRAMID; vtkType++) + { + if (SMDS_Downward *down = _downArray[vtkType]) + { + if (down->getMaxId()) + { + MESSAGE("Cells of Type " << vtkType << " : number of entities, est: " + << GuessSize[vtkType] << " real: " << down->getMaxId()); + } + } + }CHRONOSTOP(24);CHRONOSTOP(2); + counters::stats(); +} + +/*! Get the neighbors of a cell. + * Only the neighbors having the dimension of the cell are taken into account + * (neighbors of a volume are the volumes sharing a face with this volume, + * neighbors of a face are the faces sharing an edge with this face...). + * @param neighborsVtkIds vector of neighbors vtk id's to fill (reserve enough space). + * @param downIds downward id's of cells of dimension n-1, to fill (reserve enough space). + * @param downTypes vtk types of cells of dimension n-1, to fill (reserve enough space). + * @param vtkId the vtk id of the cell + * @return number of neighbors + */ +int SMDS_UnstructuredGrid::GetNeighbors(int* neighborsVtkIds, int* downIds, unsigned char* downTypes, int vtkId) +{ + int vtkType = this->GetCellType(vtkId); + int cellDim = SMDS_Downward::getCellDimension(vtkType); + if (cellDim <2) + return 0; // TODO voisins des edges = edges connectees + int cellId = this->_cellIdToDownId[vtkId]; + + int nbCells = _downArray[vtkType]->getNumberOfDownCells(cellId); + const int *downCells = _downArray[vtkType]->getDownCells(cellId); + const unsigned char* downTyp = _downArray[vtkType]->getDownTypes(cellId); + + // --- iteration on faces of the 3D cell (or edges on the 2D cell). + + int nb = 0; + for (int i = 0; i < nbCells; i++) + { + int downId = downCells[i]; + int cellType = downTyp[i]; + int nbUp = _downArray[cellType]->getNumberOfUpCells(downId); + const int *upCells = _downArray[cellType]->getUpCells(downId); + const unsigned char* upTypes = _downArray[cellType]->getUpTypes(downId); + + // ---for a volume, max 2 upCells, one is this cell, the other is a neighbor + // for a face, number of neighbors (connected faces) not known + + for (int j = 0; j < nbUp; j++) + { + if ((upCells[j] == cellId) && (upTypes[j] == vtkType)) + continue; + int vtkNeighbor = _downArray[upTypes[j]]->getVtkCellId(upCells[j]); + neighborsVtkIds[nb] = vtkNeighbor; + downIds[nb] = downId; + downTypes[nb] = cellType; + nb++; + } + if (nb >= NBMAXNEIGHBORS) + assert(0); + } + return nb; +} + +/*! get the volumes containing a face or an edge of the grid + * The edge or face belongs to the vtkUnstructuredGrid + * @param volVtkIds vector of parent volume ids to fill (reserve enough space!) + * @param vtkId vtk id of the face or edge + */ +int SMDS_UnstructuredGrid::GetParentVolumes(int* volVtkIds, int vtkId) +{ + int vtkType = this->GetCellType(vtkId); + int dim = SMDS_Downward::getCellDimension(vtkType); + int nbFaces = 0; + unsigned char cellTypes[1000]; + int downCellId[1000]; + if (dim == 1) + { + int downId = this->CellIdToDownId(vtkId); + if (downId < 0) + { + MESSAGE("Downward structure not up to date: new edge not taken into account"); + return 0; + } + nbFaces = _downArray[vtkType]->getNumberOfUpCells(downId); + const int *upCells = _downArray[vtkType]->getUpCells(downId); + const unsigned char* upTypes = _downArray[vtkType]->getUpTypes(downId); + for (int i=0; i< nbFaces; i++) + { + cellTypes[i] = upTypes[i]; + downCellId[i] = upCells[i]; + } + } + else if (dim == 2) + { + nbFaces = 1; + cellTypes[0] = this->GetCellType(vtkId); + int downId = this->CellIdToDownId(vtkId); + if (downId < 0) + { + MESSAGE("Downward structure not up to date: new face not taken into account"); + return 0; + } + downCellId[0] = downId; + } + + int nbvol =0; + for (int i=0; igetNumberOfUpCells(downId); + const int *upCells = _downArray[vtkTypeFace]->getUpCells(downId); + const unsigned char* upTypes = _downArray[vtkTypeFace]->getUpTypes(downId); + for (int j=0; jgetVtkCellId(upCells[j]); + if (vtkVolId >= 0) + volVtkIds[nbvol++] = vtkVolId; + } + } + return nbvol; +} + +/*! get the volumes containing a face or an edge of the downward structure + * The edge or face does not necessary belong to the vtkUnstructuredGrid + * @param volVtkIds vector of parent volume ids to fill (reserve enough space!) + * @param downId id in the downward structure + * @param downType type of cell + */ +int SMDS_UnstructuredGrid::GetParentVolumes(int* volVtkIds, int downId, unsigned char downType) +{ + int vtkType = downType; + int dim = SMDS_Downward::getCellDimension(vtkType); + int nbFaces = 0; + unsigned char cellTypes[1000]; + int downCellId[1000]; + if (dim == 1) + { + nbFaces = _downArray[vtkType]->getNumberOfUpCells(downId); + const int *upCells = _downArray[vtkType]->getUpCells(downId); + const unsigned char* upTypes = _downArray[vtkType]->getUpTypes(downId); + for (int i=0; i< nbFaces; i++) + { + cellTypes[i] = upTypes[i]; + downCellId[i] = upCells[i]; + } + } + else if (dim == 2) + { + nbFaces = 1; + cellTypes[0] = vtkType; + downCellId[0] = downId; + } + + int nbvol =0; + for (int i=0; igetNumberOfUpCells(downId); + const int *upCells = _downArray[vtkTypeFace]->getUpCells(downId); + const unsigned char* upTypes = _downArray[vtkTypeFace]->getUpTypes(downId); + for (int j=0; jgetVtkCellId(upCells[j]); + if (vtkVolId >= 0) + volVtkIds[nbvol++] = vtkVolId; + } + } + return nbvol; +} + +/*! get the node id's of a cell. + * The cell is defined by it's downward connectivity id and type. + * @param nodeSet set of of vtk node id's to fill. + * @param downId downward connectivity id of the cell. + * @param downType type of cell. + */ +void SMDS_UnstructuredGrid::GetNodeIds(std::set& nodeSet, int downId, unsigned char downType) +{ + _downArray[downType]->getNodeIds(downId, nodeSet); +} + +/*! change some nodes in cell without modifying type or internal connectivity. + * Nodes inverse connectivity is maintained up to date. + * @param vtkVolId vtk id of the cell + * @param localClonedNodeIds map old node id to new node id. + */ +void SMDS_UnstructuredGrid::ModifyCellNodes(int vtkVolId, std::map localClonedNodeIds) +{ + vtkIdType npts = 0; + vtkIdType *pts; // will refer to the point id's of the face + this->GetCellPoints(vtkVolId, npts, pts); + for (int i = 0; i < npts; i++) + { + if (localClonedNodeIds.count(pts[i])) + { + vtkIdType oldpt = pts[i]; + pts[i] = localClonedNodeIds[oldpt]; + //MESSAGE(oldpt << " --> " << pts[i]); + //this->RemoveReferenceToCell(oldpt, vtkVolId); + //this->AddReferenceToCell(pts[i], vtkVolId); + } + } +} + +/*! reorder the nodes of a face + * @param vtkVolId vtk id of a volume containing the face, to get an orientation for the face. + * @param orderedNodes list of nodes to reorder (in out) + * @return size of the list + */ +int SMDS_UnstructuredGrid::getOrderedNodesOfFace(int vtkVolId, int& dim, std::vector& orderedNodes) +{ + int vtkType = this->GetCellType(vtkVolId); + dim = SMDS_Downward::getCellDimension(vtkType); + if (dim == 3) + { + SMDS_Down3D *downvol = static_cast (_downArray[vtkType]); + int downVolId = this->_cellIdToDownId[vtkVolId]; + downvol->getOrderedNodesOfFace(downVolId, orderedNodes); + } + // else nothing to do; + return orderedNodes.size(); +} + +void SMDS_UnstructuredGrid::BuildLinks() +{ + // Remove the old links if they are already built + if (this->Links) + { + this->Links->UnRegister(this); + } + + this->Links = SMDS_CellLinks::New(); + this->Links->Allocate(this->GetNumberOfPoints()); + this->Links->Register(this); + this->Links->BuildLinks(this, this->Connectivity); + this->Links->Delete(); +} + +/*! Create a volume (prism or hexahedron) by duplication of a face. + * Designed for use in creation of flat elements separating volume domains. + * A face separating two domains is shared by two volume cells. + * All the nodes are already created (for the two faces). + * Each original Node is associated to corresponding nodes in the domains. + * Some nodes may be duplicated for more than two domains, when domain separations intersect. + * In that case, even some of the nodes to use for the original face may be changed. + * @param vtkVolId: vtk id of a volume containing the face, to get an orientation for the face. + * @param domain1: domain of the original face + * @param domain2: domain of the duplicated face + * @param originalNodes: the vtk node ids of the original face + * @param nodeDomains: map(original id --> map(domain --> duplicated node id)) + * @return ok if success. + */ +SMDS_MeshCell* SMDS_UnstructuredGrid::extrudeVolumeFromFace(int vtkVolId, + int domain1, + int domain2, + std::set& originalNodes, + std::map >& nodeDomains, + std::map >& nodeQuadDomains) +{ + //MESSAGE("extrudeVolumeFromFace " << vtkVolId); + vector orderedOriginals; + orderedOriginals.clear(); + set::const_iterator it = originalNodes.begin(); + for (; it != originalNodes.end(); ++it) + orderedOriginals.push_back(*it); + + int dim = 0; + int nbNodes = this->getOrderedNodesOfFace(vtkVolId, dim, orderedOriginals); + vector orderedNodes; + + bool isQuadratic = false; + switch (orderedOriginals.size()) + { + case 3: + if (dim == 2) + isQuadratic = true; + break; + case 6: + case 8: + isQuadratic = true; + break; + default: + isQuadratic = false; + break; + } + + if (isQuadratic) + { + long dom1 = domain1; + long dom2 = domain2; + long dom1_2; // for nodeQuadDomains + if (domain1 < domain2) + dom1_2 = dom1 + INT_MAX * dom2; + else + dom1_2 = dom2 + INT_MAX * dom1; + //cerr << "dom1=" << dom1 << " dom2=" << dom2 << " dom1_2=" << dom1_2 << endl; + int ima = orderedOriginals.size(); + int mid = orderedOriginals.size() / 2; + //cerr << "ima=" << ima << " mid=" << mid << endl; + for (int i = 0; i < mid; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain1]); + for (int i = 0; i < mid; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain2]); + for (int i = mid; i < ima; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain1]); + for (int i = mid; i < ima; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain2]); + for (int i = 0; i < mid; i++) + { + int oldId = orderedOriginals[i]; + int newId; + if (nodeQuadDomains.count(oldId) && nodeQuadDomains[oldId].count(dom1_2)) + newId = nodeQuadDomains[oldId][dom1_2]; + else + { + double *coords = this->GetPoint(oldId); + SMDS_MeshNode *newNode = _mesh->AddNode(coords[0], coords[1], coords[2]); + newId = newNode->getVtkId(); + std::map emptyMap; + nodeQuadDomains[oldId] = emptyMap; + nodeQuadDomains[oldId][dom1_2] = newId; + } + orderedNodes.push_back(newId); + } + } + else + { + for (int i = 0; i < nbNodes; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain1]); + if (dim == 3) + for (int i = 0; i < nbNodes; i++) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain2]); + else + for (int i = nbNodes-1; i >= 0; i--) + orderedNodes.push_back(nodeDomains[orderedOriginals[i]][domain2]); + + } + + if (dim == 3) + { + SMDS_MeshVolume *vol = _mesh->AddVolumeFromVtkIds(orderedNodes); + return vol; + } + else if (dim == 2) + { + SMDS_MeshFace *face = _mesh->AddFaceFromVtkIds(orderedNodes); + return face; + } + + // TODO update sub-shape list of elements and nodes + return 0; +} + +//================================================================================ +/*! + * \brief Allocates data array for ball diameters + * \param MaxVtkID - max ID of a ball element + */ +//================================================================================ + +void SMDS_UnstructuredGrid::AllocateDiameters( vtkIdType MaxVtkID ) +{ + SetBallDiameter( MaxVtkID, 0 ); +} + +//================================================================================ +/*! + * \brief Sets diameter of a ball element + * \param vtkID - vtk id of the ball element + * \param diameter - diameter of the ball element + */ +//================================================================================ + +void SMDS_UnstructuredGrid::SetBallDiameter( vtkIdType vtkID, double diameter ) +{ + vtkDoubleArray* array = vtkDoubleArray::SafeDownCast( vtkDataSet::CellData->GetScalars() ); + if ( !array ) + { + array = vtkDoubleArray::New(); + array->SetNumberOfComponents(1); + vtkDataSet::CellData->SetScalars( array ); + } + array->InsertValue( vtkID, diameter ); +} + +//================================================================================ +/*! + * \brief Returns diameter of a ball element + * \param vtkID - vtk id of the ball element + */ +//================================================================================ + +double SMDS_UnstructuredGrid::GetBallDiameter( vtkIdType vtkID ) const +{ + if ( vtkDataSet::CellData ) + return vtkDoubleArray::SafeDownCast( vtkDataSet::CellData->GetScalars() )->GetValue( vtkID ); + return 0; +} + diff --git a/src/SMDS/SMDS_UnstructuredGrid.hxx b/src/SMDS/SMDS_UnstructuredGrid.hxx new file mode 100644 index 000000000..4c1cc7b80 --- /dev/null +++ b/src/SMDS/SMDS_UnstructuredGrid.hxx @@ -0,0 +1,125 @@ +// Copyright (C) 2010-2012 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 +// + +// File: SMDS_UnstructuredGrid.hxx +// Author: prascle +// Created: September 16, 2009, 10:28 PM + +#ifndef _SMDS_UNSTRUCTUREDGRID_HXX +#define _SMDS_UNSTRUCTUREDGRID_HXX + +#include "SMESH_SMDS.hxx" + +#include +#include +#include "chrono.hxx" + +#include +#include +#include + +//#define VTK_HAVE_POLYHEDRON +//#ifdef VTK_HAVE_POLYHEDRON +#define VTK_MAXTYPE VTK_POLYHEDRON +//#else +// #define VTK_MAXTYPE VTK_QUADRATIC_PYRAMID +//#endif + +#define NBMAXNEIGHBORS 100 + +// allow very huge polyhedrons in tests +#define NBMAXNODESINCELL 5000 + +class SMDS_Downward; +class SMDS_Mesh; +class SMDS_MeshCell; +class SMDS_MeshVolume; + +class SMDS_EXPORT SMDS_CellLinks: public vtkCellLinks +{ +public: + vtkCellLinks::Link* ResizeL(vtkIdType sz); + vtkIdType GetLinksSize(); + static SMDS_CellLinks* New(); +protected: + SMDS_CellLinks(); + ~SMDS_CellLinks(); +}; + +class SMDS_EXPORT SMDS_UnstructuredGrid: public vtkUnstructuredGrid +{ +public: + void setSMDS_mesh(SMDS_Mesh *mesh); + void compactGrid(std::vector& idNodesOldToNew, + int newNodeSize, + std::vector& idCellsOldToNew, + int newCellSize); + virtual unsigned long GetMTime(); + virtual void Update(); + virtual void UpdateInformation(); + virtual vtkPoints *GetPoints(); + + //#ifdef VTK_HAVE_POLYHEDRON + int InsertNextLinkedCell(int type, int npts, vtkIdType *pts); + //#endif + + int CellIdToDownId(int vtkCellId); + void setCellIdToDownId(int vtkCellId, int downId); + void CleanDownwardConnectivity(); + void BuildDownwardConnectivity(bool withEdges); + int GetNeighbors(int* neighborsVtkIds, int* downIds, unsigned char* downTypes, int vtkId); + int GetParentVolumes(int* volVtkIds, int vtkId); + int GetParentVolumes(int* volVtkIds, int downId, unsigned char downType); + void GetNodeIds(std::set& nodeSet, int downId, unsigned char downType); + void ModifyCellNodes(int vtkVolId, std::map localClonedNodeIds); + int getOrderedNodesOfFace(int vtkVolId, int& dim, std::vector& orderedNodes); + void BuildLinks(); + SMDS_MeshCell* extrudeVolumeFromFace(int vtkVolId, int domain1, int domain2, + std::set& originalNodes, + std::map >& nodeDomains, + std::map >& nodeQuadDomains); + vtkCellLinks* GetLinks() + { + return Links; + } + SMDS_Downward* getDownArray(unsigned char vtkType) + { + return _downArray[vtkType]; + } + void AllocateDiameters( vtkIdType maxVtkID ); + void SetBallDiameter( vtkIdType vtkID, double diameter ); + double GetBallDiameter( vtkIdType vtkID ) const; + + static SMDS_UnstructuredGrid* New(); + SMDS_Mesh *_mesh; +protected: + SMDS_UnstructuredGrid(); + ~SMDS_UnstructuredGrid(); + void copyNodes(vtkPoints *newPoints, std::vector& idNodesOldToNew, int& alreadyCopied, int start, int end); + void copyBloc(vtkUnsignedCharArray *newTypes, std::vector& idCellsOldToNew, std::vector& idNodesOldToNew, + vtkCellArray* newConnectivity, vtkIdTypeArray* newLocations, vtkIdType* pointsCell, int& alreadyCopied, + int start, int end); + + std::vector _cellIdToDownId; //!< convert vtk Id to downward[vtkType] id, initialized with -1 + std::vector _downTypes; + std::vector _downArray; +}; + +#endif /* _SMDS_UNSTRUCTUREDGRID_HXX */ + diff --git a/src/SMDS/SMDS_VertexPosition.cxx b/src/SMDS/SMDS_VertexPosition.cxx index c3311b5a5..20584970c 100644 --- a/src/SMDS/SMDS_VertexPosition.cxx +++ b/src/SMDS/SMDS_VertexPosition.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_VertexPosition.cxx // Author : Jean-Michel BOULCOURT @@ -35,25 +36,13 @@ using namespace std; //purpose : //======================================================================= -SMDS_VertexPosition:: SMDS_VertexPosition(const int aVertexId) - :SMDS_Position(aVertexId) +SMDS_VertexPosition:: SMDS_VertexPosition() { + //MESSAGE("*********************************************** SMDS_VertexPosition " << aVertexId); } -//======================================================================= -//function : Coords -//purpose : -//======================================================================= - -const double *SMDS_VertexPosition::Coords() const -{ - const static double origin[]={0,0,0}; - MESSAGE("SMDS_VertexPosition::Coords not implemented"); - return origin; -} - - SMDS_TypeOfPosition SMDS_VertexPosition::GetTypeOfPosition() const { - return SMDS_TOP_VERTEX; + //MESSAGE("################################################# GetTypeOfPosition"); + return SMDS_TOP_VERTEX; } diff --git a/src/SMDS/SMDS_VertexPosition.hxx b/src/SMDS/SMDS_VertexPosition.hxx index dad7b5a13..6b574cd11 100644 --- a/src/SMDS/SMDS_VertexPosition.hxx +++ b/src/SMDS/SMDS_VertexPosition.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_VertexPosition.hxx // Module : SMESH @@ -33,10 +34,9 @@ class SMDS_EXPORT SMDS_VertexPosition:public SMDS_Position { - public: - SMDS_TypeOfPosition GetTypeOfPosition() const; - SMDS_VertexPosition(int aVertexId=0); - const double *Coords() const; + public: + SMDS_TypeOfPosition GetTypeOfPosition() const; + SMDS_VertexPosition(); }; #endif diff --git a/src/SMDS/SMDS_VolumeOfFaces.cxx b/src/SMDS/SMDS_VolumeOfFaces.cxx index c993dcc66..e5aa46096 100644 --- a/src/SMDS/SMDS_VolumeOfFaces.cxx +++ b/src/SMDS/SMDS_VolumeOfFaces.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_VolumeOfFaces.cxx // Author : Jean-Michel BOULCOURT @@ -30,6 +31,7 @@ #include "SMDS_VolumeOfFaces.hxx" #include "SMDS_IteratorOfElements.hxx" +#include "utilities.h" using namespace std; @@ -40,16 +42,16 @@ using namespace std; void SMDS_VolumeOfFaces::Print(ostream & OS) const { - OS << "volume <" << GetID() << "> : "; - int i; - for (i = 0; i < NbFaces()-1; ++i) OS << myFaces[i] << ","; - OS << myFaces[i]<< ") " << endl; + OS << "volume <" << GetID() << "> : "; + int i; + for (i = 0; i < NbFaces()-1; ++i) OS << myFaces[i] << ","; + OS << myFaces[i]<< ") " << endl; } int SMDS_VolumeOfFaces::NbFaces() const { - return myNbFaces; + return myNbFaces; } class SMDS_VolumeOfFaces_MyIterator:public SMDS_ElemIterator @@ -74,7 +76,7 @@ class SMDS_VolumeOfFaces_MyIterator:public SMDS_ElemIterator }; SMDS_ElemIteratorPtr SMDS_VolumeOfFaces:: - elementsIterator(SMDSAbs_ElementType type) const + elementsIterator(SMDSAbs_ElementType type) const { switch(type) { @@ -95,13 +97,14 @@ SMDS_VolumeOfFaces::SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face3, const SMDS_MeshFace * face4) { - myNbFaces = 4; - myFaces[0]=face1; - myFaces[1]=face2; - myFaces[2]=face3; - myFaces[3]=face4; - myFaces[4]=0; - myFaces[5]=0; + //MESSAGE("****************************************************** SMDS_VolumeOfFaces"); + myNbFaces = 4; + myFaces[0]=face1; + myFaces[1]=face2; + myFaces[2]=face3; + myFaces[3]=face4; + myFaces[4]=0; + myFaces[5]=0; } SMDS_VolumeOfFaces::SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, @@ -110,13 +113,14 @@ SMDS_VolumeOfFaces::SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face4, const SMDS_MeshFace * face5) { - myNbFaces = 5; - myFaces[0]=face1; - myFaces[1]=face2; - myFaces[2]=face3; - myFaces[3]=face4; - myFaces[4]=face5; - myFaces[5]=0; + //MESSAGE("****************************************************** SMDS_VolumeOfFaces"); + myNbFaces = 5; + myFaces[0]=face1; + myFaces[1]=face2; + myFaces[2]=face3; + myFaces[3]=face4; + myFaces[4]=face5; + myFaces[5]=0; } SMDS_VolumeOfFaces::SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, @@ -126,12 +130,40 @@ SMDS_VolumeOfFaces::SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face5, const SMDS_MeshFace * face6) { - myNbFaces = 6; - myFaces[0]=face1; - myFaces[1]=face2; - myFaces[2]=face3; - myFaces[3]=face4; - myFaces[4]=face5; - myFaces[5]=face6; + //MESSAGE("****************************************************** SMDS_VolumeOfFaces"); + myNbFaces = 6; + myFaces[0]=face1; + myFaces[1]=face2; + myFaces[2]=face3; + myFaces[3]=face4; + myFaces[4]=face5; + myFaces[5]=face6; +} + +SMDSAbs_EntityType SMDS_VolumeOfFaces::GetEntityType() const +{ + SMDSAbs_EntityType aType = SMDSEntity_Tetra; + switch(myNbFaces) + { + case 4: aType = SMDSEntity_Tetra; break; + case 5: aType = SMDSEntity_Pyramid; break; + case 6: aType = SMDSEntity_Penta; break; + case 8: + default: aType = SMDSEntity_Hexa; break; + } + return aType; } +SMDSAbs_GeometryType SMDS_VolumeOfFaces::GetGeomType() const +{ + SMDSAbs_GeometryType aType = SMDSGeom_NONE; + switch(myNbFaces) + { + case 4: aType = SMDSGeom_TETRA; break; + case 5: aType = SMDSGeom_PYRAMID; break; + case 6: aType = SMDSGeom_PENTA; break; + case 8: + default: aType = SMDSGeom_HEXA; break; + } + return aType; +} diff --git a/src/SMDS/SMDS_VolumeOfFaces.hxx b/src/SMDS/SMDS_VolumeOfFaces.hxx index 074fbec23..a64d53ab5 100644 --- a/src/SMDS/SMDS_VolumeOfFaces.hxx +++ b/src/SMDS/SMDS_VolumeOfFaces.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_VolumeOfFaces.hxx // Module : SMESH @@ -36,32 +37,35 @@ class SMDS_EXPORT SMDS_VolumeOfFaces:public SMDS_MeshVolume { - + public: - SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, + SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face2, const SMDS_MeshFace * face3, const SMDS_MeshFace * face4); - SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, + SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face2, const SMDS_MeshFace * face3, const SMDS_MeshFace * face4, const SMDS_MeshFace * face5); - SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, + SMDS_VolumeOfFaces(const SMDS_MeshFace * face1, const SMDS_MeshFace * face2, const SMDS_MeshFace * face3, const SMDS_MeshFace * face4, const SMDS_MeshFace * face5, const SMDS_MeshFace * face6); - void Print(std::ostream & OS) const; - - int NbFaces() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual SMDSAbs_GeometryType GetGeomType() const; + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], + const int nbNodes) {return false;} + virtual void Print(std::ostream & OS) const; + + virtual int NbFaces() const; protected: - SMDS_ElemIteratorPtr - elementsIterator(SMDSAbs_ElementType type) const; - const SMDS_MeshFace * myFaces[6]; - int myNbFaces; + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; + const SMDS_MeshFace * myFaces[6]; + int myNbFaces; }; #endif diff --git a/src/SMDS/SMDS_VolumeOfNodes.cxx b/src/SMDS/SMDS_VolumeOfNodes.cxx index ff46fc318..cb4cdfa34 100644 --- a/src/SMDS/SMDS_VolumeOfNodes.cxx +++ b/src/SMDS/SMDS_VolumeOfNodes.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // #ifdef _MSC_VER @@ -29,10 +30,9 @@ #include "SMDS_MeshNode.hxx" #include "SMDS_SetIterator.hxx" #include "SMDS_VolumeTool.hxx" +#include "SMDS_Mesh.hxx" #include "utilities.h" -#include - using namespace std; /////////////////////////////////////////////////////////////////////////////// @@ -40,73 +40,77 @@ using namespace std; /// 5,1 and 7,3 are an edges. /////////////////////////////////////////////////////////////////////////////// SMDS_VolumeOfNodes::SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5, - const SMDS_MeshNode * node6, - const SMDS_MeshNode * node7, - const SMDS_MeshNode * node8) -{ - myNbNodes = 8; - myNodes = new const SMDS_MeshNode* [myNbNodes]; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=node4; - myNodes[4]=node5; - myNodes[5]=node6; - myNodes[6]=node7; - myNodes[7]=node8; + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5, + const SMDS_MeshNode * node6, + const SMDS_MeshNode * node7, + const SMDS_MeshNode * node8) +{ + //MESSAGE("***************************************************** SMDS_VolumeOfNodes"); + myNbNodes = 8; + myNodes = new const SMDS_MeshNode* [myNbNodes]; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=node4; + myNodes[4]=node5; + myNodes[5]=node6; + myNodes[6]=node7; + myNodes[7]=node8; } SMDS_VolumeOfNodes::SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4) -{ - myNbNodes = 4; - myNodes = new const SMDS_MeshNode* [myNbNodes]; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=node4; + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4) +{ + //MESSAGE("***************************************************** SMDS_VolumeOfNodes"); + myNbNodes = 4; + myNodes = new const SMDS_MeshNode* [myNbNodes]; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=node4; } SMDS_VolumeOfNodes::SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5) -{ - myNbNodes = 5; - myNodes = new const SMDS_MeshNode* [myNbNodes]; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=node4; - myNodes[4]=node5; + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5) +{ + //MESSAGE("***************************************************** SMDS_VolumeOfNodes"); + myNbNodes = 5; + myNodes = new const SMDS_MeshNode* [myNbNodes]; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=node4; + myNodes[4]=node5; } SMDS_VolumeOfNodes::SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5, - const SMDS_MeshNode * node6) -{ - myNbNodes = 6; - myNodes = new const SMDS_MeshNode* [myNbNodes]; - myNodes[0]=node1; - myNodes[1]=node2; - myNodes[2]=node3; - myNodes[3]=node4; - myNodes[4]=node5; - myNodes[5]=node6; + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5, + const SMDS_MeshNode * node6) +{ + //MESSAGE("***************************************************** SMDS_VolumeOfNodes"); + myNbNodes = 6; + myNodes = new const SMDS_MeshNode* [myNbNodes]; + myNodes[0]=node1; + myNodes[1]=node2; + myNodes[2]=node3; + myNodes[3]=node4; + myNodes[4]=node5; + myNodes[5]=node6; } bool SMDS_VolumeOfNodes::ChangeNodes(const SMDS_MeshNode* nodes[], @@ -132,56 +136,48 @@ SMDS_VolumeOfNodes::~SMDS_VolumeOfNodes() } } -//======================================================================= -//function : Print -//purpose : -//======================================================================= - void SMDS_VolumeOfNodes::Print(ostream & OS) const { - OS << "volume <" << GetID() << "> : "; - int i; - for (i = 0; i < NbNodes(); ++i) OS << myNodes[i] << ","; - OS << myNodes[NbNodes()-1]<< ") " << endl; + OS << "volume <" << GetID() << "> : "; + int i; + for (i = 0; i < NbNodes()-1; ++i) OS << myNodes[i] << ","; + OS << myNodes[NbNodes()-1]<< ") " << endl; } int SMDS_VolumeOfNodes::NbFaces() const { - switch(NbNodes()) - { - case 4: return 4; - case 5: return 5; - case 6: return 5; - case 8: return 6; - default: MESSAGE("invalid number of nodes"); - } + switch(NbNodes()) + { + case 4: return 4; + case 5: return 5; + case 6: return 5; + case 8: return 6; + default: MESSAGE("invalid number of nodes"); + } return 0; } int SMDS_VolumeOfNodes::NbNodes() const { - return myNbNodes; + return myNbNodes; } int SMDS_VolumeOfNodes::NbEdges() const { - switch(NbNodes()) - { - case 4: return 6; - case 5: return 8; - case 6: return 9; - case 8: return 12; - default: MESSAGE("invalid number of nodes"); - } + switch(NbNodes()) + { + case 4: return 6; + case 5: return 8; + case 6: return 9; + case 8: return 12; + default: MESSAGE("invalid number of nodes"); + } return 0; } -/// =================================================================== /*! * \brief Iterator on node of volume */ -/// =================================================================== - class SMDS_VolumeOfNodes_MyIterator:public SMDS_NodeArrayElemIterator { public: @@ -189,12 +185,9 @@ class SMDS_VolumeOfNodes_MyIterator:public SMDS_NodeArrayElemIterator SMDS_NodeArrayElemIterator( s, & s[ l ]) {} }; -/// =================================================================== /*! * \brief Iterator on faces or edges of volume */ -/// =================================================================== - class _MySubIterator : public SMDS_ElemIterator { vector< const SMDS_MeshElement* > myElems; @@ -234,17 +227,45 @@ SMDS_ElemIteratorPtr SMDS_VolumeOfNodes::elementsIterator(SMDSAbs_ElementType ty SMDSAbs_ElementType SMDS_VolumeOfNodes::GetType() const { - return SMDSAbs_Volume; + return SMDSAbs_Volume; } /*! * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ const SMDS_MeshNode* SMDS_VolumeOfNodes::GetNode(const int ind) const { - return myNodes[ WrappedIndex( ind )]; + return myNodes[ ind ]; +} + +SMDSAbs_EntityType SMDS_VolumeOfNodes::GetEntityType() const +{ + SMDSAbs_EntityType aType = SMDSEntity_Tetra; + switch(myNbNodes) + { + case 4: aType = SMDSEntity_Tetra; break; + case 5: aType = SMDSEntity_Pyramid; break; + case 6: aType = SMDSEntity_Penta; break; + case 8: + default: aType = SMDSEntity_Hexa; break; + } + return aType; +} + +SMDSAbs_GeometryType SMDS_VolumeOfNodes::GetGeomType() const +{ + SMDSAbs_GeometryType aType = SMDSGeom_NONE; + switch(myNbNodes) + { + case 4: aType = SMDSGeom_TETRA; break; + case 5: aType = SMDSGeom_PYRAMID; break; + case 6: aType = SMDSGeom_PENTA; break; + case 12: aType = SMDSGeom_HEXAGONAL_PRISM; break; + case 8: + default: aType = SMDSGeom_HEXA; break; + } + return aType; } + diff --git a/src/SMDS/SMDS_VolumeOfNodes.hxx b/src/SMDS/SMDS_VolumeOfNodes.hxx index ee773285b..86e22b622 100644 --- a/src/SMDS/SMDS_VolumeOfNodes.hxx +++ b/src/SMDS/SMDS_VolumeOfNodes.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_MeshVolume.hxx // Module : SMESH @@ -32,58 +33,60 @@ class SMDS_EXPORT SMDS_VolumeOfNodes:public SMDS_MeshVolume { - + public: - SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4); - SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5); - SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5, - const SMDS_MeshNode * node6); - SMDS_VolumeOfNodes( - const SMDS_MeshNode * node1, - const SMDS_MeshNode * node2, - const SMDS_MeshNode * node3, - const SMDS_MeshNode * node4, - const SMDS_MeshNode * node5, - const SMDS_MeshNode * node6, - const SMDS_MeshNode * node7, - const SMDS_MeshNode * node8); + SMDS_VolumeOfNodes( + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4); + SMDS_VolumeOfNodes( + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5); + SMDS_VolumeOfNodes( + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5, + const SMDS_MeshNode * node6); + SMDS_VolumeOfNodes( + const SMDS_MeshNode * node1, + const SMDS_MeshNode * node2, + const SMDS_MeshNode * node3, + const SMDS_MeshNode * node4, + const SMDS_MeshNode * node5, + const SMDS_MeshNode * node6, + const SMDS_MeshNode * node7, + const SMDS_MeshNode * node8); bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes); ~SMDS_VolumeOfNodes(); - void Print(std::ostream & OS) const; - int NbFaces() const; - int NbNodes() const; - int NbEdges() const; - SMDSAbs_ElementType GetType() const; + void Print(std::ostream & OS) const; + int NbFaces() const; + int NbNodes() const; + int NbEdges() const; + virtual SMDSAbs_ElementType GetType() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual SMDSAbs_GeometryType GetGeomType() const; /*! * \brief Return node by its index * \param ind - node index * \retval const SMDS_MeshNode* - the node - * - * Index is wrapped if it is out of a valid range */ virtual const SMDS_MeshNode* GetNode(const int ind) const; protected: - SMDS_ElemIteratorPtr - elementsIterator(SMDSAbs_ElementType type) const; - const SMDS_MeshNode** myNodes; - int myNbNodes; + SMDS_ElemIteratorPtr + elementsIterator(SMDSAbs_ElementType type) const; + const SMDS_MeshNode** myNodes; + int myNbNodes; + }; + #endif diff --git a/src/SMDS/SMDS_VolumeTool.cxx b/src/SMDS/SMDS_VolumeTool.cxx index 0984cb505..841d23540 100644 --- a/src/SMDS/SMDS_VolumeTool.cxx +++ b/src/SMDS/SMDS_VolumeTool.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMDS_VolumeTool.cxx // Created : Tue Jul 13 12:22:13 2004 // Author : Edward AGAPOV (eap) @@ -31,14 +32,14 @@ #include "SMDS_MeshElement.hxx" #include "SMDS_MeshNode.hxx" -#include "SMDS_PolyhedralVolumeOfNodes.hxx" +#include "SMDS_VtkVolume.hxx" #include "SMDS_Mesh.hxx" #include "utilities.h" #include -#include -#include +#include +#include using namespace std; @@ -46,6 +47,10 @@ using namespace std; // Node indices in faces depending on volume orientation // making most faces normals external // ====================================================== +// For all elements, 0-th face is bottom based on the first nodes. +// For prismatic elements (tetra,hexa,prisms), 1-th face is a top one. +// For all elements, side faces follow order of bottom nodes +// ====================================================== /* // N3 @@ -66,11 +71,6 @@ static int Tetra_F [4][4] = { // FORWARD == EXTERNAL { 0, 3, 1, 0 }, { 1, 3, 2, 1 }, { 0, 2, 3, 0 }}; -static int Tetra_R [4][4] = { // REVERSED - { 0, 1, 2, 0 }, // All faces but a bottom have external normals - { 0, 1, 3, 0 }, - { 1, 2, 3, 1 }, - { 0, 3, 2, 0 }}; static int Tetra_RE [4][4] = { // REVERSED -> FORWARD (EXTERNAL) { 0, 2, 1, 0 }, // All faces have external normals { 0, 1, 3, 0 }, @@ -86,13 +86,8 @@ static int Pyramid_F [5][5] = { // FORWARD == EXTERNAL { 0, 4, 1, 0, 4 }, { 1, 4, 2, 1, 4 }, { 2, 4, 3, 2, 4 }, - { 3, 4, 0, 3, 4 }}; -static int Pyramid_R [5][5] = { // REVERSED - { 0, 1, 2, 3, 0 }, // All faces but a bottom have external normals - { 0, 1, 4, 0, 4 }, - { 1, 2, 4, 1, 4 }, - { 2, 3, 4, 2, 4 }, - { 3, 0, 4, 3, 4 }}; + { 3, 4, 0, 3, 4 } +}; static int Pyramid_RE [5][5] = { // REVERSED -> FORWARD (EXTERNAL) { 0, 3, 2, 1, 0 }, // All faces but a bottom have external normals { 0, 1, 4, 0, 4 }, @@ -117,29 +112,17 @@ static int Pyramid_nbN [] = { 4, 3, 3, 3, 3 }; // N0 +---------+ N2 */ static int Penta_F [5][5] = { // FORWARD - { 0, 1, 2, 0, 0 }, // Top face has an internal normal, other - external - { 3, 4, 5, 3, 3 }, // 0 is bottom, 1 is top face - { 0, 2, 5, 3, 0 }, - { 1, 4, 5, 2, 1 }, - { 0, 3, 4, 1, 0 }}; -static int Penta_R [5][5] = { // REVERSED - { 0, 1, 2, 0, 0 }, // Bottom face has an internal normal, other - external - { 3, 4, 5, 3, 3 }, // 0 is bottom, 1 is top face - { 0, 3, 5, 2, 0 }, - { 1, 2, 5, 4, 1 }, - { 0, 1, 4, 3, 0 }}; -static int Penta_FE [5][5] = { // FORWARD -> EXTERNAL - { 0, 1, 2, 0, 0 }, - { 3, 5, 4, 3, 3 }, - { 0, 2, 5, 3, 0 }, + { 0, 1, 2, 0, 0 }, // All faces have external normals + { 3, 5, 4, 3, 3 }, // 0 is bottom, 1 is top face + { 0, 3, 4, 1, 0 }, { 1, 4, 5, 2, 1 }, - { 0, 3, 4, 1, 0 }}; + { 0, 2, 5, 3, 0 }}; static int Penta_RE [5][5] = { // REVERSED -> EXTERNAL { 0, 2, 1, 0, 0 }, { 3, 4, 5, 3, 3 }, - { 0, 3, 5, 2, 0 }, + { 0, 1, 4, 3, 0 }, { 1, 2, 5, 4, 1 }, - { 0, 1, 4, 3, 0 }}; + { 0, 3, 5, 2, 0 }}; static int Penta_nbN [] = { 3, 3, 4, 4, 4 }; /* @@ -158,35 +141,58 @@ static int Penta_nbN [] = { 3, 3, 4, 4, 4 }; // N0+----------+N3 */ static int Hexa_F [6][5] = { // FORWARD - { 0, 1, 2, 3, 0 }, // opposite faces are neighbouring, - { 4, 5, 6, 7, 4 }, // odd face(1,3,5) normal is internal, even(0,2,4) - external - { 1, 0, 4, 5, 1 }, // same index nodes of opposite faces are linked - { 2, 3, 7, 6, 2 }, - { 0, 3, 7, 4, 0 }, - { 1, 2, 6, 5, 1 }}; -// static int Hexa_R [6][5] = { // REVERSED -// { 0, 3, 2, 1, 0 }, // opposite faces are neighbouring, -// { 4, 7, 6, 5, 4 }, // odd face(1,3,5) normal is external, even(0,2,4) - internal -// { 1, 5, 4, 0, 1 }, // same index nodes of opposite faces are linked -// { 2, 6, 7, 3, 2 }, -// { 0, 4, 7, 3, 0 }, -// { 1, 5, 6, 2, 1 }}; -static int Hexa_FE [6][5] = { // FORWARD -> EXTERNAL - { 0, 1, 2, 3, 0 } , // opposite faces are neighbouring, - { 4, 7, 6, 5, 4 }, // all face normals are external, - { 0, 4, 5, 1, 0 }, // links in opposite faces: 0-0, 1-3, 2-2, 3-1 + { 0, 1, 2, 3, 0 }, + { 4, 7, 6, 5, 4 }, // all face normals are external + { 0, 4, 5, 1, 0 }, + { 1, 5, 6, 2, 1 }, { 3, 2, 6, 7, 3 }, - { 0, 3, 7, 4, 0 }, - { 1, 5, 6, 2, 1 }}; + { 0, 3, 7, 4, 0 }}; static int Hexa_RE [6][5] = { // REVERSED -> EXTERNAL - { 0, 3, 2, 1, 0 }, // opposite faces are neighbouring, - { 4, 5, 6, 7, 4 }, // all face normals are external, - { 0, 1, 5, 4, 0 }, // links in opposite faces: 0-0, 1-3, 2-2, 3-1 + { 0, 3, 2, 1, 0 }, + { 4, 5, 6, 7, 4 }, // all face normals are external + { 0, 1, 5, 4, 0 }, + { 1, 2, 6, 5, 1 }, { 3, 7, 6, 2, 3 }, - { 0, 4, 7, 3, 0 }, - { 1, 2, 6, 5, 1 }}; + { 0, 4, 7, 3, 0 }}; static int Hexa_nbN [] = { 4, 4, 4, 4, 4, 4 }; +/* +// N8 +------+ N9 +// / \ +// / \ +// N7 + + N10 +// \ / +// \ / +// N6 +------+ N11 +// HEXAGONAL PRISM +// N2 +------+ N3 +// / \ +// / \ +// N1 + + N4 +// \ / +// \ / +// N0 +------+ N5 +*/ +static int HexPrism_F [8][7] = { // FORWARD + { 0, 1, 2, 3, 4, 5, 0 }, + { 6,11,10, 9, 8, 7, 6 }, + { 0, 6, 7, 1, 0, 0, 0 }, + { 1, 7, 8, 2, 1, 1, 1 }, + { 2, 8, 9, 3, 2, 2, 2 }, + { 3, 9,10, 4, 3, 3, 3 }, + { 4,10,11, 5, 4, 4, 4 }, + { 5,11, 6, 0, 5, 5, 5 }}; +static int HexPrism_RE [8][7] = { // REVERSED -> EXTERNAL + { 0, 5, 4, 3, 2, 1, 0 }, + { 6,11,10, 9, 8, 7, 6 }, + { 0, 6, 7, 1, 0, 0, 0 }, + { 1, 7, 8, 2, 1, 1, 1 }, + { 2, 8, 9, 3, 2, 2, 2 }, + { 3, 9,10, 4, 3, 3, 3 }, + { 4,10,11, 5, 4, 4, 4 }, + { 5,11, 6, 0, 5, 5, 5 }}; +static int HexPrism_nbN [] = { 6, 6, 4, 4, 4, 4, 4, 4 }; + /* // N3 @@ -202,18 +208,13 @@ static int Hexa_nbN [] = { 4, 4, 4, 4, 4, 4 }; // + // N2 */ -static int QuadTetra_F [4][7] = { // FORWARD == EXTERNAL +static int QuadTetra_F [4][7] = { // FORWARD { 0, 4, 1, 5, 2, 6, 0 }, // All faces have external normals { 0, 7, 3, 8, 1, 4, 0 }, { 1, 8, 3, 9, 2, 5, 1 }, { 0, 6, 2, 9, 3, 7, 0 }}; -static int QuadTetra_R [4][7] = { // REVERSED - { 0, 4, 1, 5, 2, 6, 0 }, // All faces but a bottom have external normals - { 0, 4, 1, 8, 3, 7, 0 }, - { 1, 5, 2, 9, 3, 8, 1 }, - { 0, 7, 3, 9, 2, 6, 0 }}; static int QuadTetra_RE [4][7] = { // REVERSED -> FORWARD (EXTERNAL) - { 0, 6, 2, 5, 1, 4, 0 }, // All faces have external normals + { 0, 6, 2, 5, 1, 4, 0 }, // All faces have external normals { 0, 4, 1, 8, 3, 7, 0 }, { 1, 5, 2, 9, 3, 8, 1 }, { 0, 7, 3, 9, 2, 6, 0 }}; @@ -240,18 +241,12 @@ static int QuadTetra_nbN [] = { 6, 6, 6, 6 }; // | | // 0+----+----+3 // 8 -static int QuadPyram_F [5][9] = { // FORWARD == EXTERNAL +static int QuadPyram_F [5][9] = { // FORWARD { 0, 5, 1, 6, 2, 7, 3, 8, 0 }, // All faces have external normals { 0, 9, 4, 10,1, 5, 0, 4, 4 }, { 1, 10,4, 11,2, 6, 1, 4, 4 }, { 2, 11,4, 12,3, 7, 2, 4, 4 }, { 3, 12,4, 9, 0, 8, 3, 4, 4 }}; -static int QuadPyram_R [5][9] = { // REVERSED - { 0, 5, 1, 6, 2, 7, 3, 8, 0 }, // All faces but a bottom have external normals - { 0, 5, 1, 10,4, 9, 0, 4, 4 }, - { 1, 6, 2, 11,4, 10,1, 4, 4 }, - { 2, 7, 3, 12,4, 11,2, 4, 4 }, - { 3, 8, 0, 9, 4, 12,3, 4, 4 }}; static int QuadPyram_RE [5][9] = { // REVERSED -> FORWARD (EXTERNAL) { 0, 8, 3, 7, 2, 6, 1, 5, 0 }, // All faces but a bottom have external normals { 0, 5, 1, 10,4, 9, 0, 4, 4 }, @@ -271,9 +266,6 @@ static int QuadPyram_nbN [] = { 8, 6, 6, 6, 6 }; // | | | // | +13 | QUADRATIC // | | | PENTAHEDRON -// | | | -// | | | -// | | | // 12+ | +14 // | | | // | | | @@ -286,29 +278,17 @@ static int QuadPyram_nbN [] = { 8, 6, 6, 6, 6 }; // 8 */ static int QuadPenta_F [5][9] = { // FORWARD - { 0, 6, 1, 7, 2, 8, 0, 0, 0 }, // Top face has an internal normal, other - external - { 3, 9, 4, 10,5, 11,3, 3, 3 }, // 0 is bottom, 1 is top face - { 0, 8, 2, 14,5, 11,3, 12,0 }, - { 1, 13,4, 10,5, 14,2, 7, 1 }, - { 0, 12,3, 9, 4, 13,1, 6, 0 }}; -static int QuadPenta_R [5][9] = { // REVERSED - { 0, 6, 1, 7, 2, 8, 0, 0, 0 }, // Bottom face has an internal normal, other - external - { 3, 9, 4, 10,5, 11,3, 3, 3 }, // 0 is bottom, 1 is top face - { 0, 12,3, 11,5, 14,2, 8, 0 }, - { 1, 7, 2, 14,5, 10,4, 13,1 }, - { 0, 6, 1, 13,4, 9, 3, 12,0 }}; -static int QuadPenta_FE [5][9] = { // FORWARD -> EXTERNAL { 0, 6, 1, 7, 2, 8, 0, 0, 0 }, { 3,11, 5, 10,4, 9, 3, 3, 3 }, - { 0, 8, 2, 14,5, 11,3, 12,0 }, + { 0, 12,3, 9, 4, 13,1, 6, 0 }, { 1, 13,4, 10,5, 14,2, 7, 1 }, - { 0, 12,3, 9, 4, 13,1, 6, 0 }}; + { 0, 8, 2, 14,5, 11,3, 12,0 }}; static int QuadPenta_RE [5][9] = { // REVERSED -> EXTERNAL { 0, 8, 2, 7, 1, 6, 0, 0, 0 }, { 3, 9, 4, 10,5, 11,3, 3, 3 }, - { 0, 12,3, 11,5, 14,2, 8, 0 }, + { 0, 6, 1, 13,4, 9, 3, 12,0 }, { 1, 7, 2, 14,5, 10,4, 13,1 }, - { 0, 6, 1, 13,4, 9, 3, 12,0 }}; + { 0, 12,3, 11,5, 14,2, 8, 0 }}; static int QuadPenta_nbN [] = { 6, 6, 8, 8, 8 }; /* @@ -335,39 +315,43 @@ static int QuadPenta_nbN [] = { 6, 6, 8, 8, 8 }; // 11 */ static int QuadHexa_F [6][9] = { // FORWARD - { 0, 8, 1, 9, 2, 10,3, 11,0 }, // opposite faces are neighbouring, - { 4, 12,5, 13,6, 14,7, 15,4 }, // odd face(1,3,5) normal is internal, even(0,2,4) - external - { 1, 8, 0, 16,4, 12,5, 17,1 }, // same index nodes of opposite faces are linked - { 2, 10,3, 19,7, 14,6, 18,2 }, - { 0, 11,3, 19,7, 15,4, 16,0 }, - { 1, 9, 2, 18,6, 13,5, 17,1 }}; -// static int Hexa_R [6][5] = { // REVERSED -// { 0, 3, 2, 1, 0 }, // opposite faces are neighbouring, -// { 4, 7, 6, 5, 4 }, // odd face(1,3,5) normal is external, even(0,2,4) - internal -// { 1, 5, 4, 0, 1 }, // same index nodes of opposite faces are linked -// { 2, 6, 7, 3, 2 }, -// { 0, 4, 7, 3, 0 }, -// { 1, 5, 6, 2, 1 }}; -static int QuadHexa_FE [6][9] = { // FORWARD -> EXTERNAL - { 0, 8, 1, 9, 2, 10,3, 11,0 }, // opposite faces are neighbouring, - { 4, 15,7, 14,6, 13,5, 12,4 }, // all face normals are external, - { 0, 16,4, 12,5, 17,1, 8, 0 }, // links in opposite faces: 0-0, 1-3, 2-2, 3-1 + { 0, 8, 1, 9, 2, 10,3, 11,0 }, // all face normals are external, + { 4, 15,7, 14,6, 13,5, 12,4 }, + { 0, 16,4, 12,5, 17,1, 8, 0 }, + { 1, 17,5, 13,6, 18,2, 9, 1 }, { 3, 10,2, 18,6, 14,7, 19,3 }, - { 0, 11,3, 19,7, 15,4, 16,0 }, - { 1, 17,5, 13,6, 18,2, 9, 1 }}; + { 0, 11,3, 19,7, 15,4, 16,0 }}; static int QuadHexa_RE [6][9] = { // REVERSED -> EXTERNAL - { 0, 11,3, 10,2, 9, 1, 8, 0 }, // opposite faces are neighbouring, - { 4, 12,5, 13,6, 14,7, 15,4 }, // all face normals are external, - { 0, 8, 1, 17,5, 12,4, 16,0 }, // links in opposite faces: 0-0, 1-3, 2-2, 3-1 + { 0, 11,3, 10,2, 9, 1, 8, 0 }, // all face normals are external + { 4, 12,5, 13,6, 14,7, 15,4 }, + { 0, 8, 1, 17,5, 12,4, 16,0 }, + { 1, 9, 2, 18,6, 13,5, 17,1 }, { 3, 19,7, 14,6, 18,2, 10,3 }, - { 0, 16,4, 15,7, 19,3, 11,0 }, - { 1, 9, 2, 18,6, 13,5, 17,1 }}; + { 0, 16,4, 15,7, 19,3, 11,0 }}; static int QuadHexa_nbN [] = { 8, 8, 8, 8, 8, 8 }; +static int TriQuadHexa_F [6][9] = { // FORWARD + { 0, 8, 1, 9, 2, 10,3, 11, 20 }, // all face normals are external + { 4, 15,7, 14,6, 13,5, 12, 25 }, + { 0, 16,4, 12,5, 17,1, 8, 21 }, + { 1, 17,5, 13,6, 18,2, 9, 22 }, + { 3, 10,2, 18,6, 14,7, 19, 23 }, + { 0, 11,3, 19,7, 15,4, 16, 24 }}; +static int TriQuadHexa_RE [6][9] = { // REVERSED -> EXTERNAL + { 0, 11,3, 10,2, 9, 1, 8, 20 }, // opposite faces are neighbouring, + { 4, 12,5, 13,6, 14,7, 15, 25 }, // all face normals are external + { 0, 8, 1, 17,5, 12,4, 16, 21 }, + { 1, 9, 2, 18,6, 13,5, 17, 22 }, + { 3, 19,7, 14,6, 18,2, 10, 23 }, + { 0, 16,4, 15,7, 19,3, 11, 24 }}; +static int TriQuadHexa_nbN [] = { 9, 9, 9, 9, 9, 9 }; + // ======================================================== // to perform some calculations without linkage to CASCADE // ======================================================== +namespace +{ struct XYZ { double x; double y; @@ -376,25 +360,56 @@ struct XYZ { XYZ( double X, double Y, double Z ) { x = X; y = Y; z = Z; } XYZ( const XYZ& other ) { x = other.x; y = other.y; z = other.z; } XYZ( const SMDS_MeshNode* n ) { x = n->X(); y = n->Y(); z = n->Z(); } - XYZ operator-( const XYZ& other ); - XYZ Crossed( const XYZ& other ); - double Dot( const XYZ& other ); - double Magnitude(); + inline XYZ operator-( const XYZ& other ); + inline XYZ operator+( const XYZ& other ); + inline XYZ Crossed( const XYZ& other ); + inline double Dot( const XYZ& other ); + inline double Magnitude(); + inline double SquareMagnitude(); }; -XYZ XYZ::operator-( const XYZ& Right ) { +inline XYZ XYZ::operator-( const XYZ& Right ) { return XYZ(x - Right.x, y - Right.y, z - Right.z); } -XYZ XYZ::Crossed( const XYZ& Right ) { +inline XYZ XYZ::operator+( const XYZ& Right ) { + return XYZ(x + Right.x, y + Right.y, z + Right.z); +} +inline XYZ XYZ::Crossed( const XYZ& Right ) { return XYZ (y * Right.z - z * Right.y, z * Right.x - x * Right.z, x * Right.y - y * Right.x); } -double XYZ::Dot( const XYZ& Other ) { +inline double XYZ::Dot( const XYZ& Other ) { return(x * Other.x + y * Other.y + z * Other.z); } -double XYZ::Magnitude() { +inline double XYZ::Magnitude() { return sqrt (x * x + y * y + z * z); } +inline double XYZ::SquareMagnitude() { + return (x * x + y * y + z * z); +} + + //================================================================================ + /*! + * \brief Return linear type corresponding to a quadratic one + */ + //================================================================================ + + SMDS_VolumeTool::VolumeType quadToLinear(SMDS_VolumeTool::VolumeType quadType) + { + SMDS_VolumeTool::VolumeType linType = SMDS_VolumeTool::VolumeType( int(quadType)-4 ); + const int nbCornersByQuad = SMDS_VolumeTool::NbCornerNodes( quadType ); + if ( SMDS_VolumeTool::NbCornerNodes( linType ) == nbCornersByQuad ) + return linType; + + int iLin = 0; + for ( ; iLin < SMDS_VolumeTool::NB_VOLUME_TYPES; ++iLin ) + if ( SMDS_VolumeTool::NbCornerNodes( SMDS_VolumeTool::VolumeType( iLin )) == nbCornersByQuad) + return SMDS_VolumeTool::VolumeType( iLin ); + + return SMDS_VolumeTool::UNKNOWN; + } + +} // namespace //======================================================================= //function : SMDS_VolumeTool @@ -402,18 +417,10 @@ double XYZ::Magnitude() { //======================================================================= SMDS_VolumeTool::SMDS_VolumeTool () - : myVolume( 0 ), - myPolyedre( 0 ), - myVolForward( true ), - myNbFaces( 0 ), - myVolumeNbNodes( 0 ), - myVolumeNodes( NULL ), - myExternalFaces( false ), - myFaceNbNodes( 0 ), - myCurFace( -1 ), - myFaceNodeIndices( NULL ), - myFaceNodes( NULL ) + : myVolumeNodes( NULL ), + myFaceNodes( NULL ) { + Set( 0 ); } //======================================================================= @@ -421,20 +428,12 @@ SMDS_VolumeTool::SMDS_VolumeTool () //purpose : //======================================================================= -SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume) - : myVolume( 0 ), - myPolyedre( 0 ), - myVolForward( true ), - myNbFaces( 0 ), - myVolumeNbNodes( 0 ), - myVolumeNodes( NULL ), - myExternalFaces( false ), - myFaceNbNodes( 0 ), - myCurFace( -1 ), - myFaceNodeIndices( NULL ), - myFaceNodes( NULL ) +SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume, + const bool ignoreCentralNodes) + : myVolumeNodes( NULL ), + myFaceNodes( NULL ) { - Set( theVolume ); + Set( theVolume, ignoreCentralNodes ); } //======================================================================= @@ -444,14 +443,11 @@ SMDS_VolumeTool::SMDS_VolumeTool (const SMDS_MeshElement* theVolume) SMDS_VolumeTool::~SMDS_VolumeTool() { - if (myVolumeNodes != NULL) { - delete [] myVolumeNodes; - myVolumeNodes = NULL; - } - if (myFaceNodes != NULL) { - delete [] myFaceNodes; - myFaceNodes = NULL; - } + if ( myVolumeNodes != NULL ) delete [] myVolumeNodes; + if ( myFaceNodes != NULL ) delete [] myFaceNodes; + + myFaceNodeIndices = NULL; + myVolumeNodes = myFaceNodes = NULL; } //======================================================================= @@ -459,10 +455,13 @@ SMDS_VolumeTool::~SMDS_VolumeTool() //purpose : Set volume to iterate on //======================================================================= -bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume) +bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume, + const bool ignoreCentralNodes) { + // reset fields myVolume = 0; myPolyedre = 0; + myIgnoreCentralNodes = ignoreCentralNodes; myVolForward = true; myNbFaces = 0; @@ -471,66 +470,63 @@ bool SMDS_VolumeTool::Set (const SMDS_MeshElement* theVolume) delete [] myVolumeNodes; myVolumeNodes = NULL; } + myPolyIndices.clear(); myExternalFaces = false; - myFaceNbNodes = 0; + + myAllFacesNodeIndices_F = 0; + //myAllFacesNodeIndices_FE = 0; + myAllFacesNodeIndices_RE = 0; + myAllFacesNbNodes = 0; myCurFace = -1; + myFaceNbNodes = 0; myFaceNodeIndices = NULL; if (myFaceNodes != NULL) { delete [] myFaceNodes; myFaceNodes = NULL; } - if ( theVolume && theVolume->GetType() == SMDSAbs_Volume ) - { - myVolume = theVolume; + // set volume data + if ( !theVolume || theVolume->GetType() != SMDSAbs_Volume ) + return false; - myNbFaces = theVolume->NbFaces(); - myVolumeNbNodes = theVolume->NbNodes(); + myVolume = theVolume; + if (myVolume->IsPoly()) + myPolyedre = dynamic_cast( myVolume ); - // set volume nodes - int iNode = 0; - myVolumeNodes = new const SMDS_MeshNode* [myVolumeNbNodes]; - SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator(); - while ( nodeIt->more() ) { - myVolumeNodes[ iNode++ ] = static_cast( nodeIt->next() ); - } + myNbFaces = theVolume->NbFaces(); + myVolumeNbNodes = theVolume->NbNodes(); - if (myVolume->IsPoly()) { - myPolyedre = static_cast( myVolume ); - if (!myPolyedre) { - MESSAGE("Warning: bad volumic element"); - return false; - } - } - else { - switch ( myVolumeNbNodes ) { - case 4: - case 5: - case 6: - case 8: - case 10: - case 13: - case 15: - case 20: { - // define volume orientation - XYZ botNormal; - GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z ); - const SMDS_MeshNode* topNode = myVolumeNodes[ myVolumeNbNodes - 1 ]; - const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ]; - XYZ upDir (topNode->X() - botNode->X(), - topNode->Y() - botNode->Y(), - topNode->Z() - botNode->Z() ); - myVolForward = ( botNormal.Dot( upDir ) < 0 ); - break; - } - default: - break; - } - } + // set nodes + int iNode = 0; + myVolumeNodes = new const SMDS_MeshNode* [myVolumeNbNodes]; + SMDS_ElemIteratorPtr nodeIt = myVolume->nodesIterator(); + while ( nodeIt->more() ) + myVolumeNodes[ iNode++ ] = static_cast( nodeIt->next() ); + + // check validity + if ( !setFace(0) ) + return ( myVolume = 0 ); + + if ( !myPolyedre ) + { + // define volume orientation + XYZ botNormal; + GetFaceNormal( 0, botNormal.x, botNormal.y, botNormal.z ); + const SMDS_MeshNode* botNode = myVolumeNodes[ 0 ]; + int topNodeIndex = myVolume->NbCornerNodes() - 1; + while ( !IsLinked( 0, topNodeIndex, /*ignoreMediumNodes=*/true )) --topNodeIndex; + const SMDS_MeshNode* topNode = myVolumeNodes[ topNodeIndex ]; + XYZ upDir (topNode->X() - botNode->X(), + topNode->Y() - botNode->Y(), + topNode->Z() - botNode->Z() ); + myVolForward = ( botNormal.Dot( upDir ) < 0 ); + + if ( !myVolForward ) + myCurFace = -1; // previous setFace(0) didn't take myVolForward into account } - return ( myVolume != 0 ); + return true; } //======================================================================= @@ -572,6 +568,12 @@ void SMDS_VolumeTool::Inverse () SWAP_NODES( myVolumeNodes, 1, 3 ); SWAP_NODES( myVolumeNodes, 5, 7 ); break; + case 12: + SWAP_NODES( myVolumeNodes, 1, 5 ); + SWAP_NODES( myVolumeNodes, 2, 4 ); + SWAP_NODES( myVolumeNodes, 7, 11 ); + SWAP_NODES( myVolumeNodes, 8, 10 ); + break; case 10: SWAP_NODES( myVolumeNodes, 1, 2 ); @@ -600,6 +602,17 @@ void SMDS_VolumeTool::Inverse () SWAP_NODES( myVolumeNodes, 13, 14 ); SWAP_NODES( myVolumeNodes, 17, 19 ); break; + case 27: + SWAP_NODES( myVolumeNodes, 1, 3 ); + SWAP_NODES( myVolumeNodes, 5, 7 ); + SWAP_NODES( myVolumeNodes, 8, 11 ); + SWAP_NODES( myVolumeNodes, 9, 10 ); + SWAP_NODES( myVolumeNodes, 12, 15 ); + SWAP_NODES( myVolumeNodes, 13, 14 ); + SWAP_NODES( myVolumeNodes, 17, 19 ); + SWAP_NODES( myVolumeNodes, 21, 24 ); + SWAP_NODES( myVolumeNodes, 22, 23 ); + break; default:; } } @@ -614,26 +627,18 @@ SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetVolumeType() const if ( myPolyedre ) return POLYHEDA; - if ( myVolume ) { -// static const VolumeType types[] = { -// TETRA, // myVolumeNbNodes = 4 -// PYRAM, // myVolumeNbNodes = 5 -// PENTA, // myVolumeNbNodes = 6 -// UNKNOWN, // myVolumeNbNodes = 7 -// HEXA // myVolumeNbNodes = 8 -// }; -// return types[ myVolumeNbNodes - 4 ]; - switch(myVolumeNbNodes) { - case 4: return TETRA; break; - case 5: return PYRAM; break; - case 6: return PENTA; break; - case 8: return HEXA; break; - case 10: return QUAD_TETRA; break; - case 13: return QUAD_PYRAM; break; - case 15: return QUAD_PENTA; break; - case 20: return QUAD_HEXA; break; - default: break; - } + switch( myVolumeNbNodes ) { + case 4: return TETRA; + case 5: return PYRAM; + case 6: return PENTA; + case 8: return HEXA; + case 12: return HEX_PRISM; + case 10: return QUAD_TETRA; + case 13: return QUAD_PYRAM; + case 15: return QUAD_PENTA; + case 20: return QUAD_HEXA; + case 27: return QUAD_HEXA; + default: break; } return UNKNOWN; @@ -693,30 +698,29 @@ double SMDS_VolumeTool::GetSize() const // split a polyhedron into tetrahedrons + int saveCurFace = myCurFace; SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this ); - XYZ baryCenter; - me->GetBaryCenter(baryCenter.x, baryCenter.y, baryCenter.z); - SMDS_MeshNode bcNode ( baryCenter.x, baryCenter.y, baryCenter.z ); - for ( int f = 0; f < NbFaces(); ++f ) { - bool externalFace = me->IsFaceExternal( f ); // it calls setFace() - for ( int n = 2; n < myFaceNbNodes; ++n ) + me->setFace( f ); + XYZ area (0,0,0), p1( myFaceNodes[0] ); + for ( int n = 0; n < myFaceNbNodes; ++n ) { - double Vn = getTetraVolume( myFaceNodes[ 0 ], - myFaceNodes[ n-1 ], - myFaceNodes[ n ], - & bcNode ); -/// cout <<"++++ " << Vn << " nodes " <GetID() << " " <GetID() << " " <GetID() << " < " << V << endl; - V += externalFace ? -Vn : Vn; + XYZ p2( myFaceNodes[ n+1 ]); + area = area + p1.Crossed( p2 ); + p1 = p2; } + V += p1.Dot( area ); } + V /= 6; + if ( saveCurFace > -1 && saveCurFace != myCurFace ) + me->setFace( myCurFace ); } else { const static int ind[] = { - 0, 1, 3, 6, 11, 19, 32, 46, 66}; - const static int vtab[][4] = { + 0, 1, 3, 6, 11, 23, 31, 44, 58, 78 }; + const static int vtab[][4] = { // decomposition into tetra in the order of enum VolumeType // tetrahedron { 0, 1, 2, 3 }, // pyramid @@ -732,6 +736,22 @@ double SMDS_VolumeTool::GetSize() const { 1, 3, 6, 2 }, { 4, 6, 3, 7 }, { 1, 4, 6, 3 }, + // hexagonal prism + { 0, 1, 2, 7 }, + { 0, 7, 8, 6 }, + { 2, 7, 8, 0 }, + + { 0, 3, 4, 9 }, + { 0, 9, 10, 6 }, + { 4, 9, 10, 0 }, + + { 0, 3, 4, 9 }, + { 0, 9, 10, 6 }, + { 4, 9, 10, 0 }, + + { 0, 4, 5, 10 }, + { 0, 10, 11, 6 }, + { 5, 10, 11, 0 }, // quadratic tetrahedron { 0, 4, 6, 7 }, @@ -837,6 +857,32 @@ bool SMDS_VolumeTool::GetBaryCenter(double & X, double & Y, double & Z) const return true; } +//================================================================================ +/*! + * \brief Classify a point + * \param tol - thickness of faces + */ +//================================================================================ + +bool SMDS_VolumeTool::IsOut(double X, double Y, double Z, double tol) const +{ + // LIMITATION: for convex volumes only + XYZ p( X,Y,Z ); + for ( int iF = 0; iF < myNbFaces; ++iF ) + { + XYZ faceNormal; + if ( !GetFaceNormal( iF, faceNormal.x, faceNormal.y, faceNormal.z )) + continue; + if ( !IsFaceExternal( iF )) + faceNormal = XYZ() - faceNormal; // reverse + + XYZ face2p( p - XYZ( myFaceNodes[0] )); + if ( face2p.Dot( faceNormal ) > tol ) + return true; + } + return false; +} + //======================================================================= //function : SetExternalNormal //purpose : Node order will be so that faces normals are external @@ -853,7 +899,7 @@ void SMDS_VolumeTool::SetExternalNormal () //purpose : Return number of nodes in the array of face nodes //======================================================================= -int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) +int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) const { if ( !setFace( faceIndex )) return 0; @@ -868,7 +914,7 @@ int SMDS_VolumeTool::NbFaceNodes( int faceIndex ) // the last node == the first one. //======================================================================= -const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) +const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) const { if ( !setFace( faceIndex )) return 0; @@ -883,14 +929,19 @@ const SMDS_MeshNode** SMDS_VolumeTool::GetFaceNodes( int faceIndex ) // the last node index == the first one. //======================================================================= -const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) +const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) const { - if (myVolume->IsPoly()) { - MESSAGE("Warning: attempt to obtain FaceNodesIndices of polyhedral volume"); - return NULL; - } if ( !setFace( faceIndex )) return 0; + + if (myPolyedre) + { + SMDS_VolumeTool* me = const_cast< SMDS_VolumeTool* > ( this ); + me->myPolyIndices.resize( myFaceNbNodes + 1 ); + me->myFaceNodeIndices = & me->myPolyIndices[0]; + for ( int i = 0; i <= myFaceNbNodes; ++i ) + me->myFaceNodeIndices[i] = myVolume->GetNodeIndex( myFaceNodes[i] ); + } return myFaceNodeIndices; } @@ -900,25 +951,23 @@ const int* SMDS_VolumeTool::GetFaceNodesIndices( int faceIndex ) //======================================================================= bool SMDS_VolumeTool::GetFaceNodes (int faceIndex, - set& theFaceNodes ) + set& theFaceNodes ) const { if ( !setFace( faceIndex )) return false; theFaceNodes.clear(); - int iNode, nbNode = myFaceNbNodes; - for ( iNode = 0; iNode < nbNode; iNode++ ) - theFaceNodes.insert( myFaceNodes[ iNode ]); + theFaceNodes.insert( myFaceNodes, myFaceNodes + myFaceNbNodes ); return true; } //======================================================================= //function : IsFaceExternal -//purpose : Check normal orientation of a returned face +//purpose : Check normal orientation of a given face //======================================================================= -bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) +bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) const { if ( myExternalFaces || !myVolume ) return true; @@ -933,26 +982,29 @@ bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) return true; } - switch ( myVolumeNbNodes ) { - case 4: - case 5: - case 10: - case 13: - // only the bottom of a reversed tetrahedron can be internal - return ( myVolForward || faceIndex != 0 ); - case 6: - case 15: - // in a forward pentahedron, the top is internal, in a reversed one - bottom - return ( myVolForward ? faceIndex != 1 : faceIndex != 0 ); - case 8: - case 20: { - // in a forward hexahedron, even face normal is external, odd - internal - bool odd = faceIndex % 2; - return ( myVolForward ? !odd : odd ); - } - default:; - } - return false; + // switch ( myVolumeNbNodes ) { + // case 4: + // case 5: + // case 10: + // case 13: + // // only the bottom of a reversed tetrahedron can be internal + // return ( myVolForward || faceIndex != 0 ); + // case 6: + // case 15: + // case 12: + // // in a forward prism, the top is internal, in a reversed one - bottom + // return ( myVolForward ? faceIndex != 1 : faceIndex != 0 ); + // case 8: + // case 20: + // case 27: { + // // in a forward hexahedron, even face normal is external, odd - internal + // bool odd = faceIndex % 2; + // return ( myVolForward ? !odd : odd ); + // } + // default:; + // } + // return false; + return true; } //======================================================================= @@ -960,30 +1012,28 @@ bool SMDS_VolumeTool::IsFaceExternal( int faceIndex ) //purpose : Return a normal to a face //======================================================================= -bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) +bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) const { if ( !setFace( faceIndex )) return false; - XYZ p1 ( myFaceNodes[0] ); - XYZ p2 ( myFaceNodes[1] ); - XYZ p3 ( myFaceNodes[2] ); + const int iQuad = ( myFaceNbNodes > 6 && !myPolyedre ) ? 2 : 1; + XYZ p1 ( myFaceNodes[0*iQuad] ); + XYZ p2 ( myFaceNodes[1*iQuad] ); + XYZ p3 ( myFaceNodes[2*iQuad] ); XYZ aVec12( p2 - p1 ); XYZ aVec13( p3 - p1 ); XYZ cross = aVec12.Crossed( aVec13 ); - //if ( myFaceNbNodes == 4 ) { - if ( myFaceNbNodes >3 ) { - XYZ p4 ( myFaceNodes[3] ); + if ( myFaceNbNodes >3*iQuad ) { + XYZ p4 ( myFaceNodes[3*iQuad] ); XYZ aVec14( p4 - p1 ); XYZ cross2 = aVec13.Crossed( aVec14 ); - cross.x += cross2.x; - cross.y += cross2.y; - cross.z += cross2.z; + cross = cross + cross2; } double size = cross.Magnitude(); - if ( size <= DBL_MIN ) + if ( size <= numeric_limits::min() ) return false; X = cross.x / size; @@ -993,12 +1043,33 @@ bool SMDS_VolumeTool::GetFaceNormal (int faceIndex, double & X, double & Y, doub return true; } +//================================================================================ +/*! + * \brief Return barycenter of a face + */ +//================================================================================ + +bool SMDS_VolumeTool::GetFaceBaryCenter (int faceIndex, double & X, double & Y, double & Z) const +{ + if ( !setFace( faceIndex )) + return false; + + X = Y = Z = 0.0; + for ( int i = 0; i < myFaceNbNodes; ++i ) + { + X += myFaceNodes[i]->X() / myFaceNbNodes; + Y += myFaceNodes[i]->Y() / myFaceNbNodes; + Z += myFaceNodes[i]->Z() / myFaceNbNodes; + } + return true; +} + //======================================================================= //function : GetFaceArea //purpose : Return face area //======================================================================= -double SMDS_VolumeTool::GetFaceArea( int faceIndex ) +double SMDS_VolumeTool::GetFaceArea( int faceIndex ) const { if (myVolume->IsPoly()) { MESSAGE("Warning: attempt to obtain area of a face of polyhedral volume"); @@ -1023,6 +1094,26 @@ double SMDS_VolumeTool::GetFaceArea( int faceIndex ) return area; } +//================================================================================ +/*! + * \brief Return index of the node located at face center of a quadratic element like HEX27 + */ +//================================================================================ + +int SMDS_VolumeTool::GetCenterNodeIndex( int faceIndex ) const +{ + if ( myAllFacesNbNodes && myVolumeNbNodes == 27 ) // classic element with 27 nodes + { + switch ( faceIndex ) { + case 0: return 20; + case 1: return 25; + default: + return faceIndex + 19; + } + } + return -1; +} + //======================================================================= //function : GetOppFaceIndex //purpose : Return index of the opposite face if it exists, else -1. @@ -1031,19 +1122,37 @@ double SMDS_VolumeTool::GetFaceArea( int faceIndex ) int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const { int ind = -1; - if (myVolume->IsPoly()) { + if (myPolyedre) { MESSAGE("Warning: attempt to obtain opposite face on polyhedral volume"); return ind; } + const int nbHoriFaces = 2; + if ( faceIndex >= 0 && faceIndex < NbFaces() ) { switch ( myVolumeNbNodes ) { case 6: + case 15: if ( faceIndex == 0 || faceIndex == 1 ) ind = 1 - faceIndex; break; case 8: - ind = faceIndex + ( faceIndex % 2 ? -1 : 1 ); + case 12: + if ( faceIndex <= 1 ) // top or bottom + ind = 1 - faceIndex; + else { + const int nbSideFaces = myAllFacesNbNodes[0]; + ind = ( faceIndex - nbHoriFaces + nbSideFaces/2 ) % nbSideFaces + nbHoriFaces; + } + break; + case 20: + case 27: + if ( faceIndex <= 1 ) // top or bottom + ind = 1 - faceIndex; + else { + const int nbSideFaces = myAllFacesNbNodes[0] / 2; + ind = ( faceIndex - nbHoriFaces + nbSideFaces/2 ) % nbSideFaces + nbHoriFaces; + } break; default:; } @@ -1054,10 +1163,12 @@ int SMDS_VolumeTool::GetOppFaceIndex( int faceIndex ) const //======================================================================= //function : IsLinked //purpose : return true if theNode1 is linked with theNode2 +// If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well //======================================================================= bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1, - const SMDS_MeshNode* theNode2) const + const SMDS_MeshNode* theNode2, + const bool theIgnoreMediumNodes) const { if ( !myVolume ) return false; @@ -1090,12 +1201,13 @@ bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1, } // find nodes indices - int i1 = -1, i2 = -1; - for ( int i = 0; i < myVolumeNbNodes; i++ ) { + int i1 = -1, i2 = -1, nbFound = 0; + for ( int i = 0; i < myVolumeNbNodes && nbFound < 2; i++ ) + { if ( myVolumeNodes[ i ] == theNode1 ) - i1 = i; + i1 = i, ++nbFound; else if ( myVolumeNodes[ i ] == theNode2 ) - i2 = i; + i2 = i, ++nbFound; } return IsLinked( i1, i2 ); } @@ -1104,10 +1216,12 @@ bool SMDS_VolumeTool::IsLinked (const SMDS_MeshNode* theNode1, //function : IsLinked //purpose : return true if the node with theNode1Index is linked // with the node with theNode2Index +// If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well //======================================================================= bool SMDS_VolumeTool::IsLinked (const int theNode1Index, - const int theNode2Index) const + const int theNode2Index, + bool theIgnoreMediumNodes) const { if ( myVolume->IsPoly() ) { return IsLinked(myVolumeNodes[theNode1Index], myVolumeNodes[theNode2Index]); @@ -1119,10 +1233,33 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, if ( minInd < 0 || maxInd > myVolumeNbNodes - 1 || maxInd == minInd ) return false; - switch ( myVolumeNbNodes ) { - case 4: + VolumeType type = GetVolumeType(); + if ( myVolume->IsQuadratic() ) + { + int firstMediumInd = myVolume->NbCornerNodes(); + if ( minInd >= firstMediumInd ) + return false; // both nodes are medium - not linked + if ( maxInd < firstMediumInd ) // both nodes are corners + { + if ( theIgnoreMediumNodes ) + type = quadToLinear(type); // to check linkage of corner nodes only + else + return false; // corner nodes are not linked directly in a quadratic cell + } + } + + switch ( type ) { + case TETRA: return true; - case 5: + case HEXA: + switch ( maxInd - minInd ) { + case 1: return minInd != 3; + case 3: return minInd == 0 || minInd == 4; + case 4: return true; + default:; + } + break; + case PYRAM: if ( maxInd == 4 ) return true; switch ( maxInd - minInd ) { @@ -1131,7 +1268,7 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, default:; } break; - case 6: + case PENTA: switch ( maxInd - minInd ) { case 1: return minInd != 2; case 2: return minInd == 0 || minInd == 3; @@ -1139,15 +1276,7 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, default:; } break; - case 8: - switch ( maxInd - minInd ) { - case 1: return minInd != 3; - case 3: return minInd == 0 || minInd == 4; - case 4: return true; - default:; - } - break; - case 10: + case QUAD_TETRA: { switch ( minInd ) { case 0: if( maxInd==4 || maxInd==6 || maxInd==7 ) return true; @@ -1158,7 +1287,22 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, } break; } - case 13: + case QUAD_HEXA: + { + switch ( minInd ) { + case 0: if( maxInd==8 || maxInd==11 || maxInd==16 ) return true; + case 1: if( maxInd==8 || maxInd==9 || maxInd==17 ) return true; + case 2: if( maxInd==9 || maxInd==10 || maxInd==18 ) return true; + case 3: if( maxInd==10 || maxInd==11 || maxInd==19 ) return true; + case 4: if( maxInd==12 || maxInd==15 || maxInd==16 ) return true; + case 5: if( maxInd==12 || maxInd==13 || maxInd==17 ) return true; + case 6: if( maxInd==13 || maxInd==14 || maxInd==18 ) return true; + case 7: if( maxInd==14 || maxInd==15 || maxInd==19 ) return true; + default:; + } + break; + } + case QUAD_PYRAM: { switch ( minInd ) { case 0: if( maxInd==5 || maxInd==8 || maxInd==9 ) return true; @@ -1170,7 +1314,7 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, } break; } - case 15: + case QUAD_PENTA: { switch ( minInd ) { case 0: if( maxInd==6 || maxInd==8 || maxInd==12 ) return true; @@ -1183,20 +1327,12 @@ bool SMDS_VolumeTool::IsLinked (const int theNode1Index, } break; } - case 20: + case HEX_PRISM: { - switch ( minInd ) { - case 0: if( maxInd==8 || maxInd==11 || maxInd==16 ) return true; - case 1: if( maxInd==8 || maxInd==9 || maxInd==17 ) return true; - case 2: if( maxInd==9 || maxInd==10 || maxInd==18 ) return true; - case 3: if( maxInd==10 || maxInd==11 || maxInd==19 ) return true; - case 4: if( maxInd==12 || maxInd==15 || maxInd==16 ) return true; - case 5: if( maxInd==12 || maxInd==13 || maxInd==17 ) return true; - case 6: if( maxInd==13 || maxInd==14 || maxInd==18 ) return true; - case 7: if( maxInd==14 || maxInd==15 || maxInd==19 ) return true; - default:; - } - break; + const int diff = maxInd-minInd; + if ( diff > 6 ) return false;// not linked top and bottom + if ( diff == 6 ) return true; // linked top and bottom + return diff == 1 || diff == 7; } default:; } @@ -1227,10 +1363,9 @@ int SMDS_VolumeTool::GetNodeIndex(const SMDS_MeshNode* theNode) const */ //================================================================================ -int SMDS_VolumeTool::GetAllExistingFaces(vector & faces) +int SMDS_VolumeTool::GetAllExistingFaces(vector & faces) const { faces.clear(); - faces.reserve( NbFaces() ); for ( int iF = 0; iF < NbFaces(); ++iF ) { const SMDS_MeshFace* face = 0; const SMDS_MeshNode** nodes = GetFaceNodes( iF ); @@ -1265,7 +1400,7 @@ int SMDS_VolumeTool::GetAllExistingEdges(vector & edges { edges.clear(); edges.reserve( myVolumeNbNodes * 2 ); - for ( int i = 0; i < myVolumeNbNodes; ++i ) { + for ( int i = 0; i < myVolumeNbNodes-1; ++i ) { for ( int j = i + 1; j < myVolumeNbNodes; ++j ) { if ( IsLinked( i, j )) { const SMDS_MeshElement* edge = @@ -1278,47 +1413,83 @@ int SMDS_VolumeTool::GetAllExistingEdges(vector & edges return edges.size(); } -//======================================================================= -//function : IsFreeFace -//purpose : check that only one volume is build on the face nodes -//======================================================================= +//================================================================================ +/*! + * \brief Return minimal square distance between connected corner nodes + */ +//================================================================================ -bool SMDS_VolumeTool::IsFreeFace( int faceIndex ) +double SMDS_VolumeTool::MinLinearSize2() const { - const int free = true; + double minSize = 1e+100; + int iQ = myVolume->IsQuadratic() ? 2 : 1; + + // store current face data + int curFace = myCurFace, nbN = myFaceNbNodes; + int* ind = myFaceNodeIndices; + myFaceNodeIndices = NULL; + const SMDS_MeshNode** nodes = myFaceNodes; + myFaceNodes = NULL; + + // it seems that compute distance twice is faster than organization of a sole computing + myCurFace = -1; + for ( int iF = 0; iF < myNbFaces; ++iF ) + { + setFace( iF ); + for ( int iN = 0; iN < myFaceNbNodes; iN += iQ ) + { + XYZ n1( myFaceNodes[ iN ]); + XYZ n2( myFaceNodes[(iN + iQ) % myFaceNbNodes]); + minSize = std::min( minSize, (n1 - n2).SquareMagnitude()); + } + } + // restore current face data + myCurFace = curFace; + myFaceNbNodes = nbN; + myFaceNodeIndices = ind; + delete [] myFaceNodes; myFaceNodes = nodes; + + return minSize; +} + +//================================================================================ +/*! + * \brief check that only one volume is build on the face nodes + * + * If a face is shared by one of , it is considered free + */ +//================================================================================ + +bool SMDS_VolumeTool::IsFreeFace( int faceIndex, const SMDS_MeshElement** otherVol/*=0*/ ) const +{ + const bool isFree = true; if (!setFace( faceIndex )) - return !free; + return !isFree; const SMDS_MeshNode** nodes = GetFaceNodes( faceIndex ); - int nbFaceNodes = myFaceNbNodes; + const int nbFaceNodes = myFaceNbNodes; - // evaluate nb of face nodes shared by other volume + // evaluate nb of face nodes shared by other volumes int maxNbShared = -1; typedef map< const SMDS_MeshElement*, int > TElemIntMap; TElemIntMap volNbShared; TElemIntMap::iterator vNbIt; for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) { const SMDS_MeshNode* n = nodes[ iNode ]; - SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator(); + SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume ); while ( eIt->more() ) { const SMDS_MeshElement* elem = eIt->next(); - if ( elem != myVolume && elem->GetType() == SMDSAbs_Volume ) { - int nbShared = 1; - vNbIt = volNbShared.find( elem ); - if ( vNbIt == volNbShared.end() ) { - volNbShared.insert ( TElemIntMap::value_type( elem, nbShared )); - } - else { - nbShared = ++(*vNbIt).second; - } - if ( nbShared > maxNbShared ) - maxNbShared = nbShared; + if ( elem != myVolume ) { + vNbIt = volNbShared.insert( make_pair( elem, 0 )).first; + (*vNbIt).second++; + if ( vNbIt->second > maxNbShared ) + maxNbShared = vNbIt->second; } } } if ( maxNbShared < 3 ) - return free; // is free + return isFree; // is free // find volumes laying on the opposite side of the face // and sharing all nodes @@ -1327,55 +1498,81 @@ bool SMDS_VolumeTool::IsFreeFace( int faceIndex ) if ( IsFaceExternal( faceIndex )) intNormal = XYZ( -intNormal.x, -intNormal.y, -intNormal.z ); XYZ p0 ( nodes[0] ), baryCenter; - for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) { - int nbShared = (*vNbIt).second; + for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); ) { + const int& nbShared = (*vNbIt).second; if ( nbShared >= 3 ) { SMDS_VolumeTool volume( (*vNbIt).first ); volume.GetBaryCenter( baryCenter.x, baryCenter.y, baryCenter.z ); XYZ intNormal2( baryCenter - p0 ); - if ( intNormal.Dot( intNormal2 ) < 0 ) - continue; // opposite side + if ( intNormal.Dot( intNormal2 ) < 0 ) { + // opposite side + if ( nbShared >= nbFaceNodes ) + { + // a volume shares the whole facet + if ( otherVol ) *otherVol = vNbIt->first; + return !isFree; + } + ++vNbIt; + continue; + } } // remove a volume from volNbShared map - volNbShared.erase( vNbIt-- ); + volNbShared.erase( vNbIt++ ); } - // here volNbShared contains only volumes laying on the - // opposite side of the face - if ( volNbShared.empty() ) { - return free; // is free + // here volNbShared contains only volumes laying on the opposite side of + // the face and sharing 3 or more but not all face nodes with myVolume + if ( volNbShared.size() < 2 ) { + return isFree; // is free } // check if the whole area of a face is shared - bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle - for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) { - SMDS_VolumeTool volume( (*vNbIt).first ); - bool prevLinkShared = false; - int nbSharedLinks = 0; - for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) { - bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] ); - if ( linkShared ) - nbSharedLinks++; - if ( linkShared && prevLinkShared && - volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] )) - isShared[ iNode ] = true; - prevLinkShared = linkShared; - } - if ( nbSharedLinks == nbFaceNodes ) - return !free; // is not free - if ( nbFaceNodes == 4 ) { - // check traingle parts 1 & 3 - if ( isShared[1] && isShared[3] ) - return !free; // is not free - // check triangle parts 0 & 2; - // 0 part could not be checked in the loop; check it here - if ( isShared[2] && prevLinkShared && - volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) && - volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) ) - return !free; // is not free - } + for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) + { + const SMDS_MeshNode* n = nodes[ iNode ]; + // check if n is shared by one of volumes of volNbShared + bool isShared = false; + SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator( SMDSAbs_Volume ); + while ( eIt->more() && !isShared ) + isShared = volNbShared.count( eIt->next() ); + if ( !isShared ) + return isFree; } - return free; + if ( otherVol ) *otherVol = volNbShared.begin()->first; + return !isFree; + +// if ( !myVolume->IsPoly() ) +// { +// bool isShared[] = { false, false, false, false }; // 4 triangle parts of a quadrangle +// for ( vNbIt = volNbShared.begin(); vNbIt != volNbShared.end(); vNbIt++ ) { +// SMDS_VolumeTool volume( (*vNbIt).first ); +// bool prevLinkShared = false; +// int nbSharedLinks = 0; +// for ( int iNode = 0; iNode < nbFaceNodes; iNode++ ) { +// bool linkShared = volume.IsLinked( nodes[ iNode ], nodes[ iNode + 1] ); +// if ( linkShared ) +// nbSharedLinks++; +// if ( linkShared && prevLinkShared && +// volume.IsLinked( nodes[ iNode - 1 ], nodes[ iNode + 1] )) +// isShared[ iNode ] = true; +// prevLinkShared = linkShared; +// } +// if ( nbSharedLinks == nbFaceNodes ) +// return !free; // is not free +// if ( nbFaceNodes == 4 ) { +// // check traingle parts 1 & 3 +// if ( isShared[1] && isShared[3] ) +// return !free; // is not free +// // check triangle parts 0 & 2; +// // 0 part could not be checked in the loop; check it here +// if ( isShared[2] && prevLinkShared && +// volume.IsLinked( nodes[ 0 ], nodes[ 1 ] ) && +// volume.IsLinked( nodes[ 1 ], nodes[ 3 ] ) ) +// return !free; // is not free +// } +// } +// } +// return free; } //======================================================================= @@ -1383,7 +1580,7 @@ bool SMDS_VolumeTool::IsFreeFace( int faceIndex ) //purpose : Return index of a face formed by theFaceNodes //======================================================================= -int SMDS_VolumeTool::GetFaceIndex( const set& theFaceNodes ) +int SMDS_VolumeTool::GetFaceIndex( const set& theFaceNodes ) const { for ( int iFace = 0; iFace < myNbFaces; iFace++ ) { const SMDS_MeshNode** nodes = GetFaceNodes( iFace ); @@ -1421,7 +1618,7 @@ int SMDS_VolumeTool::GetFaceIndex( const set& theFaceNodes //purpose : //======================================================================= -bool SMDS_VolumeTool::setFace( int faceIndex ) +bool SMDS_VolumeTool::setFace( int faceIndex ) const { if ( !myVolume ) return false; @@ -1439,93 +1636,120 @@ bool SMDS_VolumeTool::setFace( int faceIndex ) myFaceNodes = NULL; } - if (myVolume->IsPoly()) { + if (myVolume->IsPoly()) + { if (!myPolyedre) { MESSAGE("Warning: bad volumic element"); return false; } - // check orientation - bool isGoodOri = true; - if (myExternalFaces) - isGoodOri = IsFaceExternal( faceIndex ); - // set face nodes int iNode; myFaceNbNodes = myPolyedre->NbFaceNodes(faceIndex + 1); myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1]; - if (isGoodOri) { - for ( iNode = 0; iNode < myFaceNbNodes; iNode++ ) - myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, iNode + 1); - } else { - for ( iNode = 0; iNode < myFaceNbNodes; iNode++ ) - myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, myFaceNbNodes - iNode); - } + for ( iNode = 0; iNode < myFaceNbNodes; iNode++ ) + myFaceNodes[ iNode ] = myPolyedre->GetFaceNode(faceIndex + 1, iNode + 1); myFaceNodes[ myFaceNbNodes ] = myFaceNodes[ 0 ]; // last = first + // check orientation + if (myExternalFaces) + { + myCurFace = faceIndex; // avoid infinite recursion in IsFaceExternal() + myExternalFaces = false; // force normal computation by IsFaceExternal() + if ( !IsFaceExternal( faceIndex )) + for ( int i = 0, j = myFaceNbNodes; i < j; ++i, --j ) + std::swap( myFaceNodes[i], myFaceNodes[j] ); + myExternalFaces = true; + } } - else { - // choose face node indices - switch ( myVolumeNbNodes ) { - case 4: - myFaceNbNodes = Tetra_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? Tetra_F[ faceIndex ] : Tetra_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? Tetra_F[ faceIndex ] : Tetra_R[ faceIndex ]; - break; - case 5: - myFaceNbNodes = Pyramid_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? Pyramid_F[ faceIndex ] : Pyramid_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? Pyramid_F[ faceIndex ] : Pyramid_R[ faceIndex ]; - break; - case 6: - myFaceNbNodes = Penta_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? Penta_FE[ faceIndex ] : Penta_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? Penta_F[ faceIndex ] : Penta_R[ faceIndex ]; - break; - case 8: - myFaceNbNodes = Hexa_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? Hexa_FE[ faceIndex ] : Hexa_RE[ faceIndex ]; - else - myFaceNodeIndices = Hexa_F[ faceIndex ]; - break; - case 10: - myFaceNbNodes = QuadTetra_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? QuadTetra_F[ faceIndex ] : QuadTetra_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? QuadTetra_F[ faceIndex ] : QuadTetra_R[ faceIndex ]; - break; - case 13: - myFaceNbNodes = QuadPyram_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? QuadPyram_F[ faceIndex ] : QuadPyram_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? QuadPyram_F[ faceIndex ] : QuadPyram_R[ faceIndex ]; - break; - case 15: - myFaceNbNodes = QuadPenta_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? QuadPenta_FE[ faceIndex ] : QuadPenta_RE[ faceIndex ]; - else - myFaceNodeIndices = myVolForward ? QuadPenta_F[ faceIndex ] : QuadPenta_R[ faceIndex ]; - break; - case 20: - myFaceNbNodes = QuadHexa_nbN[ faceIndex ]; - if ( myExternalFaces ) - myFaceNodeIndices = myVolForward ? QuadHexa_FE[ faceIndex ] : QuadHexa_RE[ faceIndex ]; - else - myFaceNodeIndices = QuadHexa_F[ faceIndex ]; - break; - default: - return false; + else + { + if ( !myAllFacesNodeIndices_F ) + { + // choose data for an element type + switch ( myVolumeNbNodes ) { + case 4: + myAllFacesNodeIndices_F = &Tetra_F [0][0]; + //myAllFacesNodeIndices_FE = &Tetra_F [0][0]; + myAllFacesNodeIndices_RE = &Tetra_RE[0][0]; + myAllFacesNbNodes = Tetra_nbN; + myMaxFaceNbNodes = sizeof(Tetra_F[0])/sizeof(Tetra_F[0][0]); + break; + case 5: + myAllFacesNodeIndices_F = &Pyramid_F [0][0]; + //myAllFacesNodeIndices_FE = &Pyramid_F [0][0]; + myAllFacesNodeIndices_RE = &Pyramid_RE[0][0]; + myAllFacesNbNodes = Pyramid_nbN; + myMaxFaceNbNodes = sizeof(Pyramid_F[0])/sizeof(Pyramid_F[0][0]); + break; + case 6: + myAllFacesNodeIndices_F = &Penta_F [0][0]; + //myAllFacesNodeIndices_FE = &Penta_FE[0][0]; + myAllFacesNodeIndices_RE = &Penta_RE[0][0]; + myAllFacesNbNodes = Penta_nbN; + myMaxFaceNbNodes = sizeof(Penta_F[0])/sizeof(Penta_F[0][0]); + break; + case 8: + myAllFacesNodeIndices_F = &Hexa_F [0][0]; + ///myAllFacesNodeIndices_FE = &Hexa_FE[0][0]; + myAllFacesNodeIndices_RE = &Hexa_RE[0][0]; + myAllFacesNbNodes = Hexa_nbN; + myMaxFaceNbNodes = sizeof(Hexa_F[0])/sizeof(Hexa_F[0][0]); + break; + case 10: + myAllFacesNodeIndices_F = &QuadTetra_F [0][0]; + //myAllFacesNodeIndices_FE = &QuadTetra_F [0][0]; + myAllFacesNodeIndices_RE = &QuadTetra_RE[0][0]; + myAllFacesNbNodes = QuadTetra_nbN; + myMaxFaceNbNodes = sizeof(QuadTetra_F[0])/sizeof(QuadTetra_F[0][0]); + break; + case 13: + myAllFacesNodeIndices_F = &QuadPyram_F [0][0]; + //myAllFacesNodeIndices_FE = &QuadPyram_F [0][0]; + myAllFacesNodeIndices_RE = &QuadPyram_RE[0][0]; + myAllFacesNbNodes = QuadPyram_nbN; + myMaxFaceNbNodes = sizeof(QuadPyram_F[0])/sizeof(QuadPyram_F[0][0]); + break; + case 15: + myAllFacesNodeIndices_F = &QuadPenta_F [0][0]; + //myAllFacesNodeIndices_FE = &QuadPenta_FE[0][0]; + myAllFacesNodeIndices_RE = &QuadPenta_RE[0][0]; + myAllFacesNbNodes = QuadPenta_nbN; + myMaxFaceNbNodes = sizeof(QuadPenta_F[0])/sizeof(QuadPenta_F[0][0]); + break; + case 20: + case 27: + myAllFacesNodeIndices_F = &QuadHexa_F [0][0]; + //myAllFacesNodeIndices_FE = &QuadHexa_FE[0][0]; + myAllFacesNodeIndices_RE = &QuadHexa_RE[0][0]; + myAllFacesNbNodes = QuadHexa_nbN; + myMaxFaceNbNodes = sizeof(QuadHexa_F[0])/sizeof(QuadHexa_F[0][0]); + if ( !myIgnoreCentralNodes && myVolumeNbNodes == 27 ) + { + myAllFacesNodeIndices_F = &TriQuadHexa_F [0][0]; + //myAllFacesNodeIndices_FE = &TriQuadHexa_FE[0][0]; + myAllFacesNodeIndices_RE = &TriQuadHexa_RE[0][0]; + myAllFacesNbNodes = TriQuadHexa_nbN; + myMaxFaceNbNodes = sizeof(TriQuadHexa_F[0])/sizeof(TriQuadHexa_F[0][0]); + } + break; + case 12: + myAllFacesNodeIndices_F = &HexPrism_F [0][0]; + //myAllFacesNodeIndices_FE = &HexPrism_FE[0][0]; + myAllFacesNodeIndices_RE = &HexPrism_RE[0][0]; + myAllFacesNbNodes = HexPrism_nbN; + myMaxFaceNbNodes = sizeof(HexPrism_F[0])/sizeof(HexPrism_F[0][0]); + break; + default: + return false; + } } + myFaceNbNodes = myAllFacesNbNodes[ faceIndex ]; + // if ( myExternalFaces ) + // myFaceNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_FE + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes ); + // else + // myFaceNodeIndices = (int*)( myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes ); + myFaceNodeIndices = (int*)( myVolForward ? myAllFacesNodeIndices_F + faceIndex*myMaxFaceNbNodes : myAllFacesNodeIndices_RE + faceIndex*myMaxFaceNbNodes ); // set face nodes myFaceNodes = new const SMDS_MeshNode* [myFaceNbNodes + 1]; @@ -1554,7 +1778,9 @@ SMDS_VolumeTool::VolumeType SMDS_VolumeTool::GetType(int nbNodes) case 10: return QUAD_TETRA; case 13: return QUAD_PYRAM; case 15: return QUAD_PENTA; - case 20: return QUAD_HEXA; + case 20: + case 27: return QUAD_HEXA; + case 12: return HEX_PRISM; default:return UNKNOWN; } } @@ -1575,7 +1801,8 @@ int SMDS_VolumeTool::NbFaces( VolumeType type ) case QUAD_PENTA: return 5; case HEXA : case QUAD_HEXA : return 6; - default: return 0; + case HEX_PRISM : return 8; + default: return 0; } } @@ -1598,7 +1825,8 @@ int SMDS_VolumeTool::NbCornerNodes(VolumeType type) case QUAD_PENTA: return 6; case HEXA : case QUAD_HEXA : return 8; - default: return 0; + case HEX_PRISM : return 12; + default: return 0; } return 0; } @@ -1619,12 +1847,14 @@ const int* SMDS_VolumeTool::GetFaceNodesIndices(VolumeType type, switch ( type ) { case TETRA: return Tetra_F[ faceIndex ]; case PYRAM: return Pyramid_F[ faceIndex ]; - case PENTA: return external ? Penta_FE[ faceIndex ] : Penta_F[ faceIndex ]; - case HEXA: return external ? Hexa_FE[ faceIndex ] : Hexa_F[ faceIndex ]; + case PENTA: return external ? Penta_F[ faceIndex ] : Penta_F[ faceIndex ]; + case HEXA: return external ? Hexa_F[ faceIndex ] : Hexa_F[ faceIndex ]; case QUAD_TETRA: return QuadTetra_F[ faceIndex ]; case QUAD_PYRAM: return QuadPyram_F[ faceIndex ]; - case QUAD_PENTA: return external ? QuadPenta_FE[ faceIndex ] : QuadPenta_F[ faceIndex ]; - case QUAD_HEXA: return external ? QuadHexa_FE[ faceIndex ] : QuadHexa_F[ faceIndex ]; + case QUAD_PENTA: return external ? QuadPenta_F[ faceIndex ] : QuadPenta_F[ faceIndex ]; + // what about SMDSEntity_TriQuad_Hexa? + case QUAD_HEXA: return external ? QuadHexa_F[ faceIndex ] : QuadHexa_F[ faceIndex ]; + case HEX_PRISM: return external ? HexPrism_F[ faceIndex ] : HexPrism_F[ faceIndex ]; default:; } return 0; @@ -1646,18 +1876,20 @@ int SMDS_VolumeTool::NbFaceNodes(VolumeType type, case QUAD_TETRA: return QuadTetra_nbN[ faceIndex ]; case QUAD_PYRAM: return QuadPyram_nbN[ faceIndex ]; case QUAD_PENTA: return QuadPenta_nbN[ faceIndex ]; + // what about SMDSEntity_TriQuad_Hexa? case QUAD_HEXA: return QuadHexa_nbN[ faceIndex ]; + case HEX_PRISM: return HexPrism_nbN[ faceIndex ]; default:; } return 0; } //======================================================================= -//function : Get +//function : Element //purpose : return element //======================================================================= -const SMDS_MeshVolume* SMDS_VolumeTool::Get() const +const SMDS_MeshVolume* SMDS_VolumeTool::Element() const { return static_cast( myVolume ); } diff --git a/src/SMDS/SMDS_VolumeTool.hxx b/src/SMDS/SMDS_VolumeTool.hxx index b95bc8a62..5c22c3eda 100644 --- a/src/SMDS/SMDS_VolumeTool.hxx +++ b/src/SMDS/SMDS_VolumeTool.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMDS_VolumeTool.hxx // Module : SMESH @@ -32,7 +33,7 @@ class SMDS_MeshElement; class SMDS_MeshNode; -class SMDS_PolyhedralVolumeOfNodes; +class SMDS_VtkVolume; class SMDS_MeshVolume; #include @@ -50,18 +51,23 @@ class SMDS_EXPORT SMDS_VolumeTool { public: - enum VolumeType { UNKNOWN = -1, TETRA = 0, PYRAM, PENTA, HEXA, QUAD_TETRA, - QUAD_PYRAM, QUAD_PENTA, QUAD_HEXA, POLYHEDA }; + enum VolumeType { UNKNOWN = -1, TETRA = 0, PYRAM, PENTA, HEXA, + HEX_PRISM, QUAD_TETRA, QUAD_PYRAM, QUAD_PENTA, QUAD_HEXA, + POLYHEDA, NB_VOLUME_TYPES }; // to keep synchronised with GetSize()! SMDS_VolumeTool (); ~SMDS_VolumeTool (); - SMDS_VolumeTool (const SMDS_MeshElement* theVolume); + SMDS_VolumeTool (const SMDS_MeshElement* theVolume, + const bool ignoreCentralNodes=true); - bool Set (const SMDS_MeshElement* theVolume); + bool Set (const SMDS_MeshElement* theVolume, + const bool ignoreCentralNodes=true); // Set volume. - // Return false if theVolume is not of type SMDSAbs_Volume + // Return false if theVolume is not of type SMDSAbs_Volume. + // ignoreCentralNodes makes skip nodes at face centers when returning + // nodes of faces of SMDSEntity_TriQuad_Hexa - const SMDS_MeshVolume* Get() const; + const SMDS_MeshVolume* Element() const; // return element int ID() const; @@ -93,19 +99,25 @@ class SMDS_EXPORT SMDS_VolumeTool bool GetBaryCenter (double & X, double & Y, double & Z) const; + bool IsOut(double X, double Y, double Z, double tol) const; + // Classify a point // ----------------------- // info on node connection // ----------------------- bool IsLinked (const SMDS_MeshNode* theNode1, - const SMDS_MeshNode* theNode2) const; + const SMDS_MeshNode* theNode2, + const bool theIgnoreMediumNodes=false) const; // Return true if theNode1 is linked with theNode2. + // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well bool IsLinked (const int theNode1Index, - const int theNode2Index) const; + const int theNode2Index, + bool theIgnoreMediumNodes=false) const; // Return true if the node with theNode1Index is linked // with the node with theNode2Index + // If theIgnoreMediumNodes then corner nodes of quadratic cell are considered linked as well int GetNodeIndex(const SMDS_MeshNode* theNode) const; // Return an index of theNode @@ -113,9 +125,15 @@ class SMDS_EXPORT SMDS_VolumeTool int GetAllExistingEdges(std::vector & edges) const; // Fill vector with boundary edges existing in the mesh + double MinLinearSize2() const; + // Return minimal square distance between connected corner nodes + // ------------- // info on faces // ------------- + // For all elements, 0-th face is bottom based on the first nodes. + // For prismatic elements (tetra,hexa,prisms), 1-th face is a top one. + // For all elements, side faces follow order of bottom nodes void SetExternalNormal (); // Node order in faces will be so that faces normals are external. @@ -124,44 +142,57 @@ class SMDS_EXPORT SMDS_VolumeTool // Return number of faces of the volume. In the following // methods 0 <= faceIndex < NbFaces() - int NbFaceNodes( int faceIndex ); + int NbFaceNodes( int faceIndex ) const; // Return number of nodes in the array of face nodes - const int* GetFaceNodesIndices( int faceIndex ); + const int* GetFaceNodesIndices( int faceIndex ) const; // Return the array of face nodes indices // To comfort link iteration, the array // length == NbFaceNodes( faceIndex ) + 1 and - // the last node index == the first one. + // the last node index == the first one, except for + // SMDSEntity_TriQuad_Hexa at ignoreCentralNodes==false. + // NOTE: for the quadratic volume, node indices are in the order the nodes encounter + // in face boundary and not the order they are in the mesh face - const SMDS_MeshNode** GetFaceNodes( int faceIndex ); + const SMDS_MeshNode** GetFaceNodes( int faceIndex ) const; // Return the array of face nodes. // To comfort link iteration, the array // length == NbFaceNodes( faceIndex ) + 1 and - // the last node == the first one. + // the last node == the first one, except for + // SMDSEntity_TriQuad_Hexa at ignoreCentralNodes==false. + // NOTE: for the quadratic volume, nodes are in the order they encounter in face boundary + // and not the order they are in the mesh face // WARNING: do not modify the array, some methods // work basing on its contents bool GetFaceNodes (int faceIndex, - std::set& theFaceNodes ); + std::set& theFaceNodes ) const; // Return a set of face nodes. - bool IsFaceExternal( int faceIndex ); + bool IsFaceExternal( int faceIndex ) const; // Check normal orientation of a face. // SetExternalNormal() is taken into account. - bool IsFreeFace( int faceIndex ); + bool IsFreeFace( int faceIndex, const SMDS_MeshElement** otherVol=0 ) const; // Check that all volumes built on the face nodes lays on one side + // otherVol returns another volume sharing the given facet - bool GetFaceNormal (int faceIndex, double & X, double & Y, double & Z); + bool GetFaceNormal (int faceIndex, double & X, double & Y, double & Z) const; // Return a normal to a face - double GetFaceArea( int faceIndex ); + bool GetFaceBaryCenter (int faceIndex, double & X, double & Y, double & Z) const; + // Return barycenter of a face + + double GetFaceArea( int faceIndex ) const; // Return face area int GetOppFaceIndex( int faceIndex ) const; // Return index of the opposite face if it exists, else -1. - int GetFaceIndex( const std::set& theFaceNodes ); + int GetCenterNodeIndex( int faceIndex ) const; + // Return index of the node located at face center of a quadratic element like HEX27 + + int GetFaceIndex( const std::set& theFaceNodes ) const; // Return index of a face formed by theFaceNodes. // Return -1 if a face not found @@ -169,7 +200,7 @@ class SMDS_EXPORT SMDS_VolumeTool // Return index of a face formed by theFaceNodesIndices // Return -1 if a face not found - int GetAllExistingFaces(std::vector & faces); + int GetAllExistingFaces(std::vector & faces) const; // Fill vector with boundary faces existing in the mesh // ------------------------ @@ -189,32 +220,40 @@ class SMDS_EXPORT SMDS_VolumeTool // To comfort link iteration, the array // length == NbFaceNodes( faceIndex ) + 1 and // the last node index == the first one. + // Nodes at face centers of SMDSEntity_TriQuad_Hexa are ignored - static int NbFaceNodes(VolumeType type, - int faceIndex ); + static int NbFaceNodes(VolumeType type, int faceIndex ); // Return number of nodes in the array of face nodes + // Nodes at face centers of SMDSEntity_TriQuad_Hexa are ignored static int NbCornerNodes(VolumeType type); // Useful to know nb of corner nodes of a quadratic volume private: - bool setFace( int faceIndex ); + bool setFace( int faceIndex ) const; const SMDS_MeshElement* myVolume; - const SMDS_PolyhedralVolumeOfNodes* myPolyedre; + const SMDS_VtkVolume* myPolyedre; + bool myIgnoreCentralNodes; bool myVolForward; int myNbFaces; int myVolumeNbNodes; const SMDS_MeshNode** myVolumeNodes; + std::vector< int > myPolyIndices; + + mutable bool myExternalFaces; - bool myExternalFaces; + mutable const int* myAllFacesNodeIndices_F; + mutable const int* myAllFacesNodeIndices_RE; + mutable const int* myAllFacesNbNodes; + mutable int myMaxFaceNbNodes; - int myCurFace; - int myFaceNbNodes; - int* myFaceNodeIndices; - const SMDS_MeshNode** myFaceNodes; + mutable int myCurFace; + mutable int myFaceNbNodes; + mutable int* myFaceNodeIndices; + mutable const SMDS_MeshNode** myFaceNodes; }; #endif diff --git a/src/SMDS/SMDS_VtkCellIterator.cxx b/src/SMDS/SMDS_VtkCellIterator.cxx new file mode 100644 index 000000000..80b421c5d --- /dev/null +++ b/src/SMDS/SMDS_VtkCellIterator.cxx @@ -0,0 +1,205 @@ +// Copyright (C) 2010-2012 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 "SMDS_VtkCellIterator.hxx" +#include "utilities.h" + +SMDS_VtkCellIterator::SMDS_VtkCellIterator(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType) : + _mesh(mesh), _cellId(vtkCellId), _index(0), _type(aType) +{ + vtkUnstructuredGrid* grid = _mesh->getGrid(); + _vtkIdList = vtkIdList::New(); + const std::vector& interlace = SMDS_MeshCell::fromVtkOrder( aType ); + if ( interlace.empty() ) + { + grid->GetCellPoints(_cellId, _vtkIdList); + _nbNodes = _vtkIdList->GetNumberOfIds(); + } + else + { + vtkIdType npts, *pts; + grid->GetCellPoints( _cellId, npts, pts ); + _vtkIdList->SetNumberOfIds( _nbNodes = npts ); + for (int i = 0; i < _nbNodes; i++) + _vtkIdList->SetId(i, pts[interlace[i]]); + } +} + +SMDS_VtkCellIterator::~SMDS_VtkCellIterator() +{ + _vtkIdList->Delete(); +} + +bool SMDS_VtkCellIterator::more() +{ + return (_index < _nbNodes); +} + +const SMDS_MeshElement* SMDS_VtkCellIterator::next() +{ + vtkIdType id = _vtkIdList->GetId(_index++); + return _mesh->FindNodeVtk(id); +} + +SMDS_VtkCellIteratorToUNV::SMDS_VtkCellIteratorToUNV(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType) : + SMDS_VtkCellIterator() +{ + _mesh = mesh; + _cellId = vtkCellId; + _index = 0; + _type = aType; + //MESSAGE("SMDS_VtkCellInterlacedIterator (UNV)" << _type); + + _vtkIdList = vtkIdList::New(); + vtkIdType* pts; + vtkUnstructuredGrid* grid = _mesh->getGrid(); + grid->GetCellPoints((vtkIdType)_cellId, (vtkIdType&)_nbNodes, pts); + _vtkIdList->SetNumberOfIds(_nbNodes); + int *ids = 0; + switch (_type) + { + case SMDSEntity_Quad_Edge: + { + static int id[] = { 0, 2, 1 }; + ids = id; + break; + } + case SMDSEntity_Quad_Triangle: + { + static int id[] = { 0, 3, 1, 4, 2, 5 }; + ids = id; + break; + } + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + { + static int id[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; + ids = id; + _nbNodes = 8; + break; + } + case SMDSEntity_Quad_Tetra: + { + static int id[] = { 0, 4, 1, 5, 2, 6, 7, 8, 9, 3 }; + ids = id; + break; + } + case SMDSEntity_Quad_Pyramid: + { + static int id[] = { 0, 5, 1, 6, 2, 7, 3, 8, 9, 10, 11, 12, 4 }; + ids = id; + break; + } + case SMDSEntity_Penta: + { + static int id[] = { 0, 2, 1, 3, 5, 4 }; + ids = id; + break; + } + case SMDSEntity_Quad_Penta: + { + static int id[] = { 0, 8, 2, 7, 1, 6, 12, 14, 13, 3, 11, 5, 10, 4, 9 }; + ids = id; + break; + } + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + { + static int id[] = { 0, 8, 1, 9, 2, 10, 3, 11, 16, 17, 18, 19, 4, 12, 5, 13, 6, 14, 7, 15 }; + ids = id; + _nbNodes = 20; + break; + } + case SMDSEntity_Polygon: + case SMDSEntity_Quad_Polygon: + case SMDSEntity_Polyhedra: + case SMDSEntity_Quad_Polyhedra: + default: + { + // static int id[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + // 25, 26, 27, 28, 29 }; + // ids = id; + // break; + } + } + if ( ids ) + for (int i = 0; i < _nbNodes; i++) + _vtkIdList->SetId(i, pts[ids[i]]); + else + for (int i = 0; i < _nbNodes; i++) + _vtkIdList->SetId(i, pts[i]); +} + +SMDS_VtkCellIteratorToUNV::~SMDS_VtkCellIteratorToUNV() +{ +} + +SMDS_VtkCellIteratorPolyH::SMDS_VtkCellIteratorPolyH(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType) : + SMDS_VtkCellIterator() +{ + _mesh = mesh; + _cellId = vtkCellId; + _index = 0; + _type = aType; + //MESSAGE("SMDS_VtkCellIteratorPolyH " << _type); + _vtkIdList = vtkIdList::New(); + vtkUnstructuredGrid* grid = _mesh->getGrid(); + grid->GetCellPoints(_cellId, _vtkIdList); + _nbNodes = _vtkIdList->GetNumberOfIds(); + switch (_type) + { + case SMDSEntity_Polyhedra: + { + //MESSAGE("SMDS_VtkCellIterator Polyhedra"); + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(_cellId, nFaces, ptIds); + int id = 0; + _nbNodesInFaces = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; // nodeIds in ptIds[id+1 .. id+nodesInFace] + _nbNodesInFaces += nodesInFace; + id += (nodesInFace + 1); + } + _vtkIdList->SetNumberOfIds(_nbNodesInFaces); + id = 0; + int n = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; // nodeIds in ptIds[id+1 .. id+nodesInFace] + for (int k = 1; k <= nodesInFace; k++) + _vtkIdList->SetId(n++, ptIds[id + k]); + id += (nodesInFace + 1); + } + break; + } + default: + assert(0); + } +} + +SMDS_VtkCellIteratorPolyH::~SMDS_VtkCellIteratorPolyH() +{ +} + +bool SMDS_VtkCellIteratorPolyH::more() +{ + return (_index < _nbNodesInFaces); +} diff --git a/src/SMDS/SMDS_VtkCellIterator.hxx b/src/SMDS/SMDS_VtkCellIterator.hxx new file mode 100644 index 000000000..ce151099d --- /dev/null +++ b/src/SMDS/SMDS_VtkCellIterator.hxx @@ -0,0 +1,72 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _SMDS_VTKCELLITERATOR_HXX_ +#define _SMDS_VTKCELLITERATOR_HXX_ + +#include "SMDS_ElemIterator.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDSAbs_ElementType.hxx" + +#include +#include + +class SMDS_VtkCellIterator: public SMDS_ElemIterator +{ +public: + SMDS_VtkCellIterator(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType); + virtual ~SMDS_VtkCellIterator(); + virtual bool more(); + virtual const SMDS_MeshElement* next(); + inline void exchange(vtkIdType a, vtkIdType b) + { + vtkIdType t = _vtkIdList->GetId(a); + _vtkIdList->SetId(a, _vtkIdList->GetId(b)); + _vtkIdList->SetId(b, t); + } + +protected: + SMDS_VtkCellIterator() {}; + + SMDS_Mesh* _mesh; + int _cellId; + int _index; + int _nbNodes; + SMDSAbs_EntityType _type; + vtkIdList* _vtkIdList; +}; + +class SMDS_VtkCellIteratorToUNV: public SMDS_VtkCellIterator +{ +public: + SMDS_VtkCellIteratorToUNV(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType); + virtual ~SMDS_VtkCellIteratorToUNV(); +}; + +class SMDS_VtkCellIteratorPolyH: public SMDS_VtkCellIterator +{ +public: + SMDS_VtkCellIteratorPolyH(SMDS_Mesh* mesh, int vtkCellId, SMDSAbs_EntityType aType); + virtual ~SMDS_VtkCellIteratorPolyH(); + virtual bool more(); +protected: + int _nbNodesInFaces; +}; + +#endif diff --git a/src/SMDS/SMDS_VtkEdge.cxx b/src/SMDS/SMDS_VtkEdge.cxx new file mode 100644 index 000000000..cb324014e --- /dev/null +++ b/src/SMDS/SMDS_VtkEdge.cxx @@ -0,0 +1,172 @@ +// Copyright (C) 2010-2012 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 "SMDS_VtkEdge.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_VtkCellIterator.hxx" + +#include "utilities.h" + +#include +#include + +using namespace std; + +SMDS_VtkEdge::SMDS_VtkEdge() +{ +} + +SMDS_VtkEdge::SMDS_VtkEdge(std::vector nodeIds, SMDS_Mesh* mesh) +{ + init(nodeIds, mesh); +} + +SMDS_VtkEdge::~SMDS_VtkEdge() +{ +} + +void SMDS_VtkEdge::init(std::vector nodeIds, SMDS_Mesh* mesh) +{ + SMDS_MeshEdge::init(); + vtkUnstructuredGrid* grid = mesh->getGrid(); + myMeshId = mesh->getMeshId(); + vtkIdType aType = VTK_LINE; + if (nodeIds.size() == 3) + aType = VTK_QUADRATIC_EDGE; + myVtkID = grid->InsertNextLinkedCell(aType, nodeIds.size(), &nodeIds[0]); + mesh->setMyModified(); + //MESSAGE("SMDS_VtkEdge::init myVtkID " << myVtkID); +} + +bool SMDS_VtkEdge::ChangeNodes(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2) +{ + const SMDS_MeshNode* nodes[] = { node1, node2 }; + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); + return ChangeNodes(nodes, 2); +} + +bool SMDS_VtkEdge::ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + if (nbNodes != npts) + { + MESSAGE("ChangeNodes problem: not the same number of nodes " << npts << " -> " << nbNodes); + return false; + } + for (int i = 0; i < nbNodes; i++) + { + pts[i] = nodes[i]->getVtkId(); + } + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); + return true; +} + +bool SMDS_VtkEdge::IsMediumNode(const SMDS_MeshNode* node) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + //MESSAGE("IsMediumNode " << npts << " " << (node->getVtkId() == pts[npts-1])); + return ((npts == 3) && (node->getVtkId() == pts[2])); +} + +void SMDS_VtkEdge::Print(std::ostream & OS) const +{ + OS << "edge <" << GetID() << "> : "; +} + +int SMDS_VtkEdge::NbNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + int nbPoints = grid->GetCell(myVtkID)->GetNumberOfPoints(); + assert(nbPoints >= 2); + return nbPoints; +} + +int SMDS_VtkEdge::NbEdges() const +{ + return 1; +} + +SMDSAbs_EntityType SMDS_VtkEdge::GetEntityType() const +{ + if (NbNodes() == 2) + return SMDSEntity_Edge; + else + return SMDSEntity_Quad_Edge; +} + +vtkIdType SMDS_VtkEdge::GetVtkType() const +{ + if (NbNodes() == 2) + return VTK_LINE; + else + return VTK_QUADRATIC_EDGE; + +} + +/*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ +const SMDS_MeshNode* +SMDS_VtkEdge::GetNode(const int ind) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts, *pts; + grid->GetCellPoints( this->myVtkID, npts, pts ); + return SMDS_Mesh::_meshList[myMeshId]->FindNodeVtk( pts[ ind ]); +} + +bool SMDS_VtkEdge::IsQuadratic() const +{ + if (this->NbNodes() > 2) + return true; + else + return false; +} + +SMDS_ElemIteratorPtr SMDS_VtkEdge::elementsIterator(SMDSAbs_ElementType type) const +{ + switch (type) + { + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIterator(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); + default: + MESSAGE("ERROR : Iterator not implemented") + ; + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); + } +} + +SMDS_ElemIteratorPtr SMDS_VtkEdge::nodesIteratorToUNV() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} + +SMDS_ElemIteratorPtr SMDS_VtkEdge::interlacedNodesElemIterator() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} diff --git a/src/SMDS/SMDS_VtkEdge.hxx b/src/SMDS/SMDS_VtkEdge.hxx new file mode 100644 index 000000000..c5f04300b --- /dev/null +++ b/src/SMDS/SMDS_VtkEdge.hxx @@ -0,0 +1,59 @@ +// Copyright (C) 2010-2012 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 +// + +// SMESH SMDS : implementaion of Salome mesh data structure +// File : SMDS_VtkEdge.hxx +// Module : SMESH + +#ifndef _SMDS_VTKEDGE_HXX_ +#define _SMDS_VTKEDGE_HXX_ + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshEdge.hxx" +#include +#include + +class SMDS_EXPORT SMDS_VtkEdge: public SMDS_MeshEdge +{ + +public: + SMDS_VtkEdge(); + SMDS_VtkEdge(std::vector nodeIds, SMDS_Mesh* mesh); + ~SMDS_VtkEdge(); + void init(std::vector nodeIds, SMDS_Mesh* mesh); + bool ChangeNodes(const SMDS_MeshNode * node1, const SMDS_MeshNode * node2); + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes); + virtual bool IsMediumNode(const SMDS_MeshNode* node) const; + + virtual void Print(std::ostream & OS) const; + virtual int NbNodes() const; + virtual int NbEdges() const; + + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual const SMDS_MeshNode* GetNode(const int ind) const; + virtual bool IsQuadratic() const; + + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; + virtual SMDS_ElemIteratorPtr nodesIteratorToUNV() const; + virtual SMDS_ElemIteratorPtr interlacedNodesElemIterator() const; +protected: +}; +#endif diff --git a/src/SMDS/SMDS_VtkFace.cxx b/src/SMDS/SMDS_VtkFace.cxx new file mode 100644 index 000000000..80644c7e2 --- /dev/null +++ b/src/SMDS/SMDS_VtkFace.cxx @@ -0,0 +1,301 @@ +// Copyright (C) 2010-2012 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 "SMDS_VtkFace.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_VtkCellIterator.hxx" + +#include "utilities.h" + +#include + +using namespace std; + +SMDS_VtkFace::SMDS_VtkFace() +{ +} + +SMDS_VtkFace::SMDS_VtkFace(const std::vector& nodeIds, SMDS_Mesh* mesh) +{ + init(nodeIds, mesh); +} + +SMDS_VtkFace::~SMDS_VtkFace() +{ +} + +void SMDS_VtkFace::init(const std::vector& nodeIds, SMDS_Mesh* mesh) +{ + SMDS_MeshFace::init(); + vtkUnstructuredGrid* grid = mesh->getGrid(); + myMeshId = mesh->getMeshId(); + vtkIdType aType = VTK_TRIANGLE; + switch (nodeIds.size()) + { + case 3: + aType = VTK_TRIANGLE; + break; + case 4: + aType = VTK_QUAD; + break; + case 6: + aType = VTK_QUADRATIC_TRIANGLE; + break; + case 8: + aType = VTK_QUADRATIC_QUAD; + break; + case 9: + aType = VTK_BIQUADRATIC_QUAD; + break; + default: + aType = VTK_POLYGON; + break; + } + myVtkID = grid->InsertNextLinkedCell(aType, nodeIds.size(), (vtkIdType*) &nodeIds[0]); + mesh->setMyModified(); + //MESSAGE("SMDS_VtkFace::init myVtkID " << myVtkID); +} + +void SMDS_VtkFace::initPoly(const std::vector& nodeIds, SMDS_Mesh* mesh) +{ + SMDS_MeshFace::init(); + vtkUnstructuredGrid* grid = mesh->getGrid(); + myMeshId = mesh->getMeshId(); + myVtkID = grid->InsertNextLinkedCell(VTK_POLYGON, nodeIds.size(), (vtkIdType*) &nodeIds[0]); + mesh->setMyModified(); +} + +bool SMDS_VtkFace::ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + if (nbNodes != npts) + { + MESSAGE("ChangeNodes problem: not the same number of nodes " << npts << " -> " << nbNodes); + return false; + } + for (int i = 0; i < nbNodes; i++) + { + pts[i] = nodes[i]->getVtkId(); + } + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); + return true; +} + +void SMDS_VtkFace::Print(std::ostream & OS) const +{ + OS << "face <" << GetID() << "> : "; +} + +int SMDS_VtkFace::NbEdges() const +{ + // TODO quadratic polygons ? + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int nbEdges = 3; + switch (aVtkType) + { + case VTK_TRIANGLE: + case VTK_QUADRATIC_TRIANGLE: + nbEdges = 3; + break; + case VTK_QUAD: + case VTK_QUADRATIC_QUAD: + case VTK_BIQUADRATIC_QUAD: + nbEdges = 4; + break; + case VTK_POLYGON: + default: + nbEdges = grid->GetCell(myVtkID)->GetNumberOfPoints(); + break; + } + return nbEdges; +} + +int SMDS_VtkFace::NbFaces() const +{ + return 1; +} + +int SMDS_VtkFace::NbNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + int nbPoints = grid->GetCell(myVtkID)->GetNumberOfPoints(); + return nbPoints; +} + +/*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ +const SMDS_MeshNode* +SMDS_VtkFace::GetNode(const int ind) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts, *pts; + grid->GetCellPoints( this->myVtkID, npts, pts ); + return SMDS_Mesh::_meshList[myMeshId]->FindNodeVtk( pts[ ind ]); +} + +bool SMDS_VtkFace::IsQuadratic() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + // TODO quadratic polygons ? + switch (aVtkType) + { + case VTK_QUADRATIC_TRIANGLE: + case VTK_QUADRATIC_QUAD: + case VTK_BIQUADRATIC_QUAD: + return true; + break; + default: + return false; + } +} + +bool SMDS_VtkFace::IsPoly() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + return (aVtkType == VTK_POLYGON); +} + +bool SMDS_VtkFace::IsMediumNode(const SMDS_MeshNode* node) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int rankFirstMedium = 0; + switch (aVtkType) + { + case VTK_QUADRATIC_TRIANGLE: + rankFirstMedium = 3; // medium nodes are of rank 3,4,5 + break; + case VTK_QUADRATIC_QUAD: + case VTK_BIQUADRATIC_QUAD: + rankFirstMedium = 4; // medium nodes are of rank 4,5,6,7 + break; + default: + //MESSAGE("wrong element type " << aVtkType); + return false; + } + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + vtkIdType nodeId = node->getVtkId(); + for (int rank = 0; rank < npts; rank++) + { + if (pts[rank] == nodeId) + { + //MESSAGE("rank " << rank << " is medium node " << (rank < rankFirstMedium)); + if (rank < rankFirstMedium) + return false; + else + return true; + } + } + //throw SALOME_Exception(LOCALIZED("node does not belong to this element")); + MESSAGE("======================================================"); + MESSAGE("= IsMediumNode: node does not belong to this element ="); + MESSAGE("======================================================"); + return false; +} + +int SMDS_VtkFace::NbCornerNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + int nbPoints = grid->GetCell(myVtkID)->GetNumberOfPoints(); + vtkIdType aVtkType = grid->GetCellType(myVtkID); + if ( aVtkType != VTK_POLYGON ) + return nbPoints <= 4 ? nbPoints : nbPoints / 2; + return nbPoints; +} + +SMDSAbs_EntityType SMDS_VtkFace::GetEntityType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + return SMDS_MeshCell::toSmdsType( VTKCellType( aVtkType )); +} + +SMDSAbs_GeometryType SMDS_VtkFace::GetGeomType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + switch ( aVtkType ) { + case VTK_TRIANGLE: + case VTK_QUADRATIC_TRIANGLE: + return SMDSGeom_TRIANGLE; + case VTK_QUAD: + case VTK_QUADRATIC_QUAD: + case VTK_BIQUADRATIC_QUAD: + return SMDSGeom_QUADRANGLE; + case VTK_POLYGON: + return SMDSGeom_POLYGON; + default:; + } + return SMDSGeom_NONE; +} + +vtkIdType SMDS_VtkFace::GetVtkType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + return aVtkType; +} + +SMDS_ElemIteratorPtr SMDS_VtkFace::elementsIterator(SMDSAbs_ElementType type) const +{ + switch (type) + { + case SMDSAbs_Node: + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIterator(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); + default: + MESSAGE("ERROR : Iterator not implemented") + ; + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); + } +} + +SMDS_ElemIteratorPtr SMDS_VtkFace::nodesIteratorToUNV() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} + +SMDS_ElemIteratorPtr SMDS_VtkFace::interlacedNodesElemIterator() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} + +//! change only the first node, used for temporary triangles in quadrangle to triangle adaptor +void SMDS_VtkFace::ChangeApex(SMDS_MeshNode* node) +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + grid->RemoveReferenceToCell(pts[0], myVtkID); + pts[0] = node->getVtkId(); + node->AddInverseElement(this), + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); +} diff --git a/src/SMDS/SMDS_VtkFace.hxx b/src/SMDS/SMDS_VtkFace.hxx new file mode 100644 index 000000000..f84192f3f --- /dev/null +++ b/src/SMDS/SMDS_VtkFace.hxx @@ -0,0 +1,62 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _SMDS_VTKFACE_HXX_ +#define _SMDS_VTKFACE_HXX_ + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshFace.hxx" +#include +#include + +class SMDS_EXPORT SMDS_VtkFace: public SMDS_MeshFace +{ +public: + SMDS_VtkFace(); + SMDS_VtkFace(const std::vector& nodeIds, SMDS_Mesh* mesh); + ~SMDS_VtkFace(); + void init(const std::vector& nodeIds, SMDS_Mesh* mesh); + void initPoly(const std::vector& nodeIds, SMDS_Mesh* mesh); + + bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes); + void ChangeApex(SMDS_MeshNode* node); // to use only for tmp triangles + + virtual void Print(std::ostream & OS) const; + virtual int NbEdges() const; + virtual int NbFaces() const; + virtual int NbNodes() const; + + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual SMDSAbs_GeometryType GetGeomType() const; + virtual const SMDS_MeshNode* GetNode(const int ind) const; + + virtual bool IsQuadratic() const; + virtual bool IsPoly() const; + virtual bool IsMediumNode(const SMDS_MeshNode* node) const; + virtual int NbCornerNodes() const; + + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; + virtual SMDS_ElemIteratorPtr nodesIteratorToUNV() const; + virtual SMDS_ElemIteratorPtr interlacedNodesElemIterator() const; +protected: +}; + +#endif diff --git a/src/SMDS/SMDS_VtkVolume.cxx b/src/SMDS/SMDS_VtkVolume.cxx new file mode 100644 index 000000000..de9174e0b --- /dev/null +++ b/src/SMDS/SMDS_VtkVolume.cxx @@ -0,0 +1,675 @@ +// Copyright (C) 2010-2012 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 "SMDS_VtkVolume.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_VtkCellIterator.hxx" + +#include "utilities.h" + +#include + +SMDS_VtkVolume::SMDS_VtkVolume() +{ +} + +SMDS_VtkVolume::SMDS_VtkVolume(const std::vector& nodeIds, SMDS_Mesh* mesh) +{ + init(nodeIds, mesh); +} +/*! + * typed used are vtk types (@see vtkCellType.h) + * see GetEntityType() for conversion in SMDS type (@see SMDSAbs_ElementType.hxx) + */ +void SMDS_VtkVolume::init(const std::vector& nodeIds, SMDS_Mesh* mesh) +{ + SMDS_MeshVolume::init(); + vtkUnstructuredGrid* grid = mesh->getGrid(); + myMeshId = mesh->getMeshId(); + vtkIdType aType = VTK_TETRA; + switch (nodeIds.size()) // cases are in order of usage frequency + { + case 4: + aType = VTK_TETRA; + break; + case 8: + aType = VTK_HEXAHEDRON; + break; + case 5: + aType = VTK_PYRAMID; + break; + case 6: + aType = VTK_WEDGE; + break; + case 10: + aType = VTK_QUADRATIC_TETRA; + break; + case 20: + aType = VTK_QUADRATIC_HEXAHEDRON; + break; + case 13: + aType = VTK_QUADRATIC_PYRAMID; + break; + case 15: + aType = VTK_QUADRATIC_WEDGE; + break; + case 12: + aType = VTK_HEXAGONAL_PRISM; + break; + case 27: + aType = VTK_TRIQUADRATIC_HEXAHEDRON; + break; + default: + aType = VTK_HEXAHEDRON; + break; + } + myVtkID = grid->InsertNextLinkedCell(aType, nodeIds.size(), (vtkIdType *) &nodeIds[0]); + mesh->setMyModified(); + //MESSAGE("SMDS_VtkVolume::init myVtkID " << myVtkID); +} + +//#ifdef VTK_HAVE_POLYHEDRON +void SMDS_VtkVolume::initPoly(const std::vector& nodeIds, + const std::vector& nbNodesPerFace, + SMDS_Mesh* mesh) +{ + SMDS_MeshVolume::init(); + //MESSAGE("SMDS_VtkVolume::initPoly"); + SMDS_UnstructuredGrid* grid = mesh->getGrid(); + //double center[3]; + //this->gravityCenter(grid, &nodeIds[0], nodeIds.size(), ¢er[0]); + vector ptIds; + vtkIdType nbFaces = nbNodesPerFace.size(); + int k = 0; + for (int i = 0; i < nbFaces; i++) + { + int nf = nbNodesPerFace[i]; + ptIds.push_back(nf); + // EAP: a right approach is: + // - either the user should care of order of nodes or + // - the user should use a service method arranging nodes if he + // don't want or can't to do it by him-self + // The method below works OK only with planar faces and convex polyhedrones + // + // double a[3]; + // double b[3]; + // double c[3]; + // grid->GetPoints()->GetPoint(nodeIds[k], a); + // grid->GetPoints()->GetPoint(nodeIds[k + 1], b); + // grid->GetPoints()->GetPoint(nodeIds[k + 2], c); + // bool isFaceForward = this->isForward(a, b, c, center); + //MESSAGE("isFaceForward " << i << " " << isFaceForward); + const vtkIdType *facePts = &nodeIds[k]; + //if (isFaceForward) + for (int n = 0; n < nf; n++) + ptIds.push_back(facePts[n]); + // else + // for (int n = nf - 1; n >= 0; n--) + // ptIds.push_back(facePts[n]); + k += nf; + } + myVtkID = grid->InsertNextLinkedCell(VTK_POLYHEDRON, nbFaces, &ptIds[0]); + mesh->setMyModified(); +} +//#endif + +bool SMDS_VtkVolume::ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes) +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + if (nbNodes != npts) + { + MESSAGE("ChangeNodes problem: not the same number of nodes " << npts << " -> " << nbNodes); + return false; + } + for (int i = 0; i < nbNodes; i++) + { + pts[i] = nodes[i]->getVtkId(); + } + SMDS_Mesh::_meshList[myMeshId]->setMyModified(); + return true; +} + +/*! + * Reorder in VTK order a list of nodes given in SMDS order. + * To be used before ChangeNodes: lists are given or computed in SMDS order. + */ +bool SMDS_VtkVolume::vtkOrder(const SMDS_MeshNode* nodes[], const int nbNodes) +{ + if (nbNodes != this->NbNodes()) + { + MESSAGE("vtkOrder, wrong number of nodes " << nbNodes << " instead of "<< this->NbNodes()); + return false; + } + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + const std::vector& interlace = SMDS_MeshCell::toVtkOrder( VTKCellType( aVtkType )); + if ( !interlace.empty() ) + { + ASSERT( interlace.size() == nbNodes ); + std::vector initNodes( nodes, nodes+nbNodes ); + for ( size_t i = 0; i < interlace.size(); ++i ) + nodes[i] = initNodes[ interlace[i] ]; + } + return true; +} + +SMDS_VtkVolume::~SMDS_VtkVolume() +{ +} + +void SMDS_VtkVolume::Print(ostream & OS) const +{ + OS << "volume <" << GetID() << "> : "; +} + +int SMDS_VtkVolume::NbFaces() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int nbFaces = 4; + switch (aVtkType) + { + case VTK_TETRA: + case VTK_QUADRATIC_TETRA: + nbFaces = 4; + break; + case VTK_PYRAMID: + case VTK_WEDGE: + case VTK_QUADRATIC_PYRAMID: + case VTK_QUADRATIC_WEDGE: + nbFaces = 5; + break; + case VTK_HEXAHEDRON: + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: + nbFaces = 6; + break; + case VTK_POLYHEDRON: + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + nbFaces = nFaces; + break; + } + case VTK_HEXAGONAL_PRISM: + nbFaces = 8; + break; + default: + MESSAGE("invalid volume type") + ; + nbFaces = 0; + break; + } + return nbFaces; +} + +int SMDS_VtkVolume::NbNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int nbPoints = 0; + if (aVtkType != VTK_POLYHEDRON) + { + nbPoints = grid->GetCell(myVtkID)->GetNumberOfPoints(); + } + else + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + int id = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; + nbPoints += nodesInFace; + id += (nodesInFace + 1); + } + } + return nbPoints; +} + +int SMDS_VtkVolume::NbEdges() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int nbEdges = 6; + switch (aVtkType) + { + case VTK_TETRA: + case VTK_QUADRATIC_TETRA: + nbEdges = 6; + break; + case VTK_PYRAMID: + case VTK_QUADRATIC_PYRAMID: + nbEdges = 8; + break; + case VTK_WEDGE: + case VTK_QUADRATIC_WEDGE: + nbEdges = 9; + break; + case VTK_HEXAHEDRON: + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: + nbEdges = 12; + break; + case VTK_POLYHEDRON: + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + nbEdges = 0; + int id = 0; + for (int i = 0; i < nFaces; i++) + { + int edgesInFace = ptIds[id]; + id += (edgesInFace + 1); + nbEdges += edgesInFace; + } + nbEdges = nbEdges / 2; + break; + } + case VTK_HEXAGONAL_PRISM: + nbEdges = 18; + break; + default: + MESSAGE("invalid volume type") + ; + nbEdges = 0; + break; + } + return nbEdges; +} + +/*! polyhedron only, + * 1 <= face_ind <= NbFaces() + */ +int SMDS_VtkVolume::NbFaceNodes(const int face_ind) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int nbNodes = 0; + if (aVtkType == VTK_POLYHEDRON) + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + int id = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; + id += (nodesInFace + 1); + if (i == face_ind - 1) + { + nbNodes = nodesInFace; + break; + } + } + } + return nbNodes; +} + +/*! polyhedron only, + * 1 <= face_ind <= NbFaces() + * 1 <= node_ind <= NbFaceNodes() + */ +const SMDS_MeshNode* SMDS_VtkVolume::GetFaceNode(const int face_ind, const int node_ind) const +{ + SMDS_Mesh *mesh = SMDS_Mesh::_meshList[myMeshId]; + vtkUnstructuredGrid* grid = mesh->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + const SMDS_MeshNode* node = 0; + if (aVtkType == VTK_POLYHEDRON) + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + int id = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; // nodeIds in ptIds[id+1 .. id+nodesInFace] + if (i == face_ind - 1) // first face is number 1 + { + if ((node_ind > 0) && (node_ind <= nodesInFace)) + node = mesh->FindNodeVtk(ptIds[id + node_ind]); // ptIds[id+1] : first node + break; + } + id += (nodesInFace + 1); + } + } + return node; +} + +/*! polyhedron only, + * return number of nodes for each face + */ +std::vector SMDS_VtkVolume::GetQuantities() const +{ + vector quantities; + quantities.clear(); + SMDS_Mesh *mesh = SMDS_Mesh::_meshList[myMeshId]; + vtkUnstructuredGrid* grid = mesh->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + if (aVtkType == VTK_POLYHEDRON) + { + vtkIdType nFaces = 0; + vtkIdType* ptIds = 0; + grid->GetFaceStream(this->myVtkID, nFaces, ptIds); + int id = 0; + for (int i = 0; i < nFaces; i++) + { + int nodesInFace = ptIds[id]; // nodeIds in ptIds[id+1 .. id+nodesInFace] + quantities.push_back(nodesInFace); + id += (nodesInFace + 1); + } + } + return quantities; +} + +SMDS_ElemIteratorPtr SMDS_VtkVolume::elementsIterator(SMDSAbs_ElementType type) const +{ + switch (type) + { + case SMDSAbs_Node: + { + SMDSAbs_EntityType aType = this->GetEntityType(); + if (aType == SMDSEntity_Polyhedra) + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorPolyH(SMDS_Mesh::_meshList[myMeshId], myVtkID, aType)); + else + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIterator(SMDS_Mesh::_meshList[myMeshId], myVtkID, aType)); + } + default: + MESSAGE("ERROR : Iterator not implemented"); + return SMDS_ElemIteratorPtr((SMDS_ElemIterator*) NULL); + } +} + +SMDS_ElemIteratorPtr SMDS_VtkVolume::nodesIteratorToUNV() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} + +SMDS_ElemIteratorPtr SMDS_VtkVolume::interlacedNodesElemIterator() const +{ + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIteratorToUNV(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} + +SMDSAbs_ElementType SMDS_VtkVolume::GetType() const +{ + return SMDSAbs_Volume; +} + +/*! + * \brief Return node by its index + * \param ind - node index + * \retval const SMDS_MeshNode* - the node + */ +const SMDS_MeshNode* SMDS_VtkVolume::GetNode(const int ind) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + vtkIdType npts, *pts; + grid->GetCellPoints( this->myVtkID, npts, pts ); + const std::vector& interlace = SMDS_MeshCell::fromVtkOrder( VTKCellType( aVtkType )); + return SMDS_Mesh::_meshList[myMeshId]->FindNodeVtk( pts[ interlace.empty() ? ind : interlace[ind]] ); +} + +bool SMDS_VtkVolume::IsQuadratic() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + // TODO quadratic polyhedrons ? + switch (aVtkType) + { + case VTK_QUADRATIC_TETRA: + case VTK_QUADRATIC_PYRAMID: + case VTK_QUADRATIC_WEDGE: + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: + return true; + break; + default: + return false; + } +} + +bool SMDS_VtkVolume::IsPoly() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + return (aVtkType == VTK_POLYHEDRON); +} + +bool SMDS_VtkVolume::IsMediumNode(const SMDS_MeshNode* node) const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + int rankFirstMedium = 0; + switch (aVtkType) + { + case VTK_QUADRATIC_TETRA: + rankFirstMedium = 4; // medium nodes are of rank 4 to 9 + break; + case VTK_QUADRATIC_PYRAMID: + rankFirstMedium = 5; // medium nodes are of rank 5 to 12 + break; + case VTK_QUADRATIC_WEDGE: + rankFirstMedium = 6; // medium nodes are of rank 6 to 14 + break; + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: + rankFirstMedium = 8; // medium nodes are of rank 8 to 19 + break; + default: + return false; + } + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(myVtkID, npts, pts); + vtkIdType nodeId = node->getVtkId(); + for (int rank = 0; rank < npts; rank++) + { + if (pts[rank] == nodeId) + { + if (rank < rankFirstMedium) + return false; + else + return true; + } + } + //throw SALOME_Exception(LOCALIZED("node does not belong to this element")); + MESSAGE("======================================================"); + MESSAGE("= IsMediumNode: node does not belong to this element ="); + MESSAGE("======================================================"); + return false; +} + +int SMDS_VtkVolume::NbCornerNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + int nbN = grid->GetCell(myVtkID)->GetNumberOfPoints(); + vtkIdType aVtkType = grid->GetCellType(myVtkID); + switch (aVtkType) + { + case VTK_QUADRATIC_TETRA: return 4; + case VTK_QUADRATIC_PYRAMID: return 5; + case VTK_QUADRATIC_WEDGE: return 6; + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: return 8; + default:; + } + return nbN; +} + +SMDSAbs_EntityType SMDS_VtkVolume::GetEntityType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + + SMDSAbs_EntityType aType = SMDSEntity_Tetra; + switch (aVtkType) + { + case VTK_TETRA: + aType = SMDSEntity_Tetra; + break; + case VTK_PYRAMID: + aType = SMDSEntity_Pyramid; + break; + case VTK_WEDGE: + aType = SMDSEntity_Penta; + break; + case VTK_HEXAHEDRON: + aType = SMDSEntity_Hexa; + break; + case VTK_QUADRATIC_TETRA: + aType = SMDSEntity_Quad_Tetra; + break; + case VTK_QUADRATIC_PYRAMID: + aType = SMDSEntity_Quad_Pyramid; + break; + case VTK_QUADRATIC_WEDGE: + aType = SMDSEntity_Quad_Penta; + break; + case VTK_QUADRATIC_HEXAHEDRON: + aType = SMDSEntity_Quad_Hexa; + break; + case VTK_TRIQUADRATIC_HEXAHEDRON: + aType = SMDSEntity_TriQuad_Hexa; + break; + case VTK_HEXAGONAL_PRISM: + aType = SMDSEntity_Hexagonal_Prism; + break; +//#ifdef VTK_HAVE_POLYHEDRON + case VTK_POLYHEDRON: + aType = SMDSEntity_Polyhedra; + break; +//#endif + default: + aType = SMDSEntity_Polyhedra; + break; + } + return aType; +} + +SMDSAbs_GeometryType SMDS_VtkVolume::GetGeomType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aVtkType = grid->GetCellType(this->myVtkID); + + SMDSAbs_GeometryType aType = SMDSGeom_NONE; + switch (aVtkType) + { + case VTK_TETRA: + case VTK_QUADRATIC_TETRA: + aType = SMDSGeom_TETRA; + break; + case VTK_PYRAMID: + case VTK_QUADRATIC_PYRAMID: + aType = SMDSGeom_PYRAMID; + break; + case VTK_WEDGE: + case VTK_QUADRATIC_WEDGE: + aType = SMDSGeom_PENTA; + break; + case VTK_HEXAHEDRON: + case VTK_QUADRATIC_HEXAHEDRON: + case VTK_TRIQUADRATIC_HEXAHEDRON: + aType = SMDSGeom_HEXA; + break; + case VTK_HEXAGONAL_PRISM: + aType = SMDSGeom_HEXAGONAL_PRISM; + break; +//#ifdef VTK_HAVE_POLYHEDRON + case VTK_POLYHEDRON: + aType = SMDSGeom_POLYHEDRA; + break; +//#endif + default: + aType = SMDSGeom_POLYHEDRA; + break; + } + return aType; +} + +vtkIdType SMDS_VtkVolume::GetVtkType() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + vtkIdType aType = grid->GetCellType(myVtkID); + return aType; +} + +void SMDS_VtkVolume::gravityCenter(SMDS_UnstructuredGrid* grid, + const vtkIdType * nodeIds, + int nbNodes, + double* result) +{ + for (int j = 0; j < 3; j++) + result[j] = 0; + if (nbNodes <= 0) + return; + for (int i = 0; i < nbNodes; i++) + { + double *coords = grid->GetPoint(nodeIds[i]); + for (int j = 0; j < 3; j++) + result[j] += coords[j]; + } + for (int j = 0; j < 3; j++) + result[j] = result[j] / nbNodes; + //MESSAGE("center " << result[0] << " " << result[1] << " " << result[2]); + return; +} + +bool SMDS_VtkVolume::isForward(double* a, double* b, double* c, double* d) +{ + double u[3], v[3], w[3]; + for (int j = 0; j < 3; j++) + { + //MESSAGE("a,b,c,d " << a[j] << " " << b[j] << " " << c[j] << " " << d[j]); + u[j] = b[j] - a[j]; + v[j] = c[j] - a[j]; + w[j] = d[j] - a[j]; + //MESSAGE("u,v,w " << u[j] << " " << v[j] << " " << w[j]); + } + double prodmixte = (u[1]*v[2] - u[2]*v[1]) * w[0] + + (u[2]*v[0] - u[0]*v[2]) * w[1] + + (u[0]*v[1] - u[1]*v[0]) * w[2]; + return (prodmixte < 0); +} + +/*! For polyhedron only + * @return actual number of nodes (not the sum of nodes of all faces) + */ +int SMDS_VtkVolume::NbUniqueNodes() const +{ + vtkUnstructuredGrid* grid = SMDS_Mesh::_meshList[myMeshId]->getGrid(); + return grid->GetCell(myVtkID)->GetNumberOfPoints(); +} + +/*! For polyhedron use only + * @return iterator on actual nodes (not through the faces) + */ +SMDS_ElemIteratorPtr SMDS_VtkVolume::uniqueNodesIterator() const +{ + MESSAGE("uniqueNodesIterator"); + return SMDS_ElemIteratorPtr(new SMDS_VtkCellIterator(SMDS_Mesh::_meshList[myMeshId], myVtkID, GetEntityType())); +} diff --git a/src/SMDS/SMDS_VtkVolume.hxx b/src/SMDS/SMDS_VtkVolume.hxx new file mode 100644 index 000000000..5f3f62b31 --- /dev/null +++ b/src/SMDS/SMDS_VtkVolume.hxx @@ -0,0 +1,79 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _SMDS_VTKVOLUME_HXX_ +#define _SMDS_VTKVOLUME_HXX_ + +#include "SMESH_SMDS.hxx" + +#include "SMDS_MeshVolume.hxx" +#include "SMDS_UnstructuredGrid.hxx" +#include + +class SMDS_EXPORT SMDS_VtkVolume: public SMDS_MeshVolume +{ +public: + SMDS_VtkVolume(); + SMDS_VtkVolume(const std::vector& nodeIds, SMDS_Mesh* mesh); + ~SMDS_VtkVolume(); + void init(const std::vector& nodeIds, SMDS_Mesh* mesh); +//#ifdef VTK_HAVE_POLYHEDRON + void initPoly(const std::vector& nodeIds, + const std::vector& nbNodesPerFace, SMDS_Mesh* mesh); +//#endif + virtual bool ChangeNodes(const SMDS_MeshNode* nodes[], const int nbNodes); + virtual bool vtkOrder(const SMDS_MeshNode* nodes[], const int nbNodes); + + virtual void Print(std::ostream & OS) const; + virtual int NbFaces() const; + virtual int NbNodes() const; + virtual int NbEdges() const; + + // 1 <= face_ind <= NbFaces() + int NbFaceNodes (const int face_ind) const; + // 1 <= face_ind <= NbFaces() + // 1 <= node_ind <= NbFaceNodes() + const SMDS_MeshNode* GetFaceNode (const int face_ind, const int node_ind) const; + + virtual SMDSAbs_ElementType GetType() const; + virtual vtkIdType GetVtkType() const; + virtual SMDSAbs_EntityType GetEntityType() const; + virtual SMDSAbs_GeometryType GetGeomType() const; + virtual const SMDS_MeshNode* GetNode(const int ind) const; + virtual bool IsQuadratic() const; + virtual bool IsPoly() const; + virtual bool IsMediumNode(const SMDS_MeshNode* node) const; + virtual int NbCornerNodes() const; + static void gravityCenter(SMDS_UnstructuredGrid* grid, + const vtkIdType *nodeIds, + int nbNodes, + double* result); + static bool isForward(double* a,double* b,double* c,double* d); + int NbUniqueNodes() const; + SMDS_ElemIteratorPtr uniqueNodesIterator() const; + std::vector GetQuantities() const; + + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const; + virtual SMDS_ElemIteratorPtr nodesIteratorToUNV() const; + virtual SMDS_ElemIteratorPtr interlacedNodesElemIterator() const; + +protected: +}; + +#endif diff --git a/src/SMDS/SMESH_SMDS.hxx b/src/SMDS/SMESH_SMDS.hxx index 97a1b63ff..d99e401df 100755 --- a/src/SMDS/SMESH_SMDS.hxx +++ b/src/SMDS/SMESH_SMDS.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_SMDS.hxx // Author : Alexander A. BORODIN // Module : SMESH diff --git a/src/SMDS/chrono.cxx b/src/SMDS/chrono.cxx new file mode 100644 index 000000000..8956ce34a --- /dev/null +++ b/src/SMDS/chrono.cxx @@ -0,0 +1,86 @@ +// Copyright (C) 2006-2012 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 "chrono.hxx" +#include "utilities.h" + +using namespace std; + +cntStruct* counters::_ctrs = 0; +int counters::_nbChrono = 0; + +counters::counters(int nb) +{ + MESSAGE("counters::counters(int nb)"); + _nbChrono = nb; + _ctrs = new cntStruct[_nbChrono]; + + for (int i = 0; i < _nbChrono; i++) + { + _ctrs[i]._ctrNames = 0; + _ctrs[i]._ctrLines = 0; + _ctrs[i]._ctrOccur = 0; + _ctrs[i]._ctrCumul = 0; + } + + MESSAGE("counters::counters()"); +} + +counters::~counters() +{ + stats(); +} + +void counters::stats() +{ + MESSAGE("counters::stats()"); + for (int i = 0; i < _nbChrono; i++) + if (_ctrs[i]._ctrOccur) + { + MESSAGE("Compteur[" << i << "]: "<< _ctrs[i]._ctrNames << "[" << _ctrs[i]._ctrLines << "]"); + MESSAGE(" " << _ctrs[i]._ctrOccur); + MESSAGE(" " << _ctrs[i]._ctrCumul); + } +} + +chrono::chrono(int i) : + _ctr(i), _run(true) +{ + //MESSAGE("chrono::chrono " << _ctr << " " << _run); + _start = clock(); +} + +chrono::~chrono() +{ + if (_run) + stop(); +} + +void chrono::stop() +{ + //MESSAGE("chrono::stop " << _ctr << " " << _run); + if (_run) + { + _run = false; + _end = clock(); + double elapse = double(_end - _start) / double(CLOCKS_PER_SEC); + counters::_ctrs[_ctr]._ctrOccur++; + counters::_ctrs[_ctr]._ctrCumul += elapse; + } +} diff --git a/src/SMDS/chrono.hxx b/src/SMDS/chrono.hxx new file mode 100644 index 000000000..bee39b0b8 --- /dev/null +++ b/src/SMDS/chrono.hxx @@ -0,0 +1,75 @@ +// Copyright (C) 2006-2012 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 +// + +#ifndef _CHRONO_HXX_ +#define _CHRONO_HXX_ + +#include "SMESH_SMDS.hxx" + +#include +#include +#include +#include + +typedef struct acnt +{ + char* _ctrNames; + int _ctrLines; + int _ctrOccur; + double _ctrCumul; +} cntStruct; + +class SMDS_EXPORT counters +{ +public: + static cntStruct *_ctrs; + counters(int nb); + ~counters(); + static void stats(); +protected: + static int _nbChrono; +}; + +class SMDS_EXPORT chrono +{ +public: + chrono(int i); + ~chrono(); + void stop(); +protected: + bool _run; + int _ctr; + clock_t _start, _end; +}; + +#ifdef CHRONODEF +#define CHRONO(i) counters::_ctrs[i]._ctrNames = (char *)__FILE__; \ + counters::_ctrs[i]._ctrLines = __LINE__; \ + chrono aChrono##i(i); + +#define CHRONOSTOP(i) aChrono##i.stop(); + +#else // CHRONODEF + +#define CHRONO(i) +#define CHRONOSTOP(i) + +#endif // CHRONODEF + +#endif // _CHRONO_HXX_ diff --git a/src/SMESH/Makefile.am b/src/SMESH/Makefile.am index 2905cc92b..3008e024f 100644 --- a/src/SMESH/Makefile.am +++ b/src/SMESH/Makefile.am @@ -1,31 +1,28 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESH : implementaion of SMESH idl descriptions # File : Makefile.in # Author : Paul RASCLE, EDF # Modified by : Alexander BORODIN (OCN) - autotools usage # Module : SMESH -# $Header$ -# + include $(top_srcdir)/adm_local/unix/make_common_starter.am # header files @@ -43,17 +40,9 @@ salomeinclude_HEADERS = \ SMESH_3D_Algo.hxx \ SMESH_Group.hxx \ SMESH_MeshEditor.hxx \ - SMESH_Block.hxx \ SMESH_Pattern.hxx \ - SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx \ - SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx \ - SMESH_SequenceOfElemPtr.hxx \ - SMESH_SequenceOfNode.hxx \ SMESH_MesherHelper.hxx \ - SMESH_Octree.hxx \ - SMESH_OctreeNode.hxx \ - SMESH_Comment.hxx \ - SMESH_ComputeError.hxx \ + SMESH_ProxyMesh.hxx \ SMESH_SMESH.hxx # Libraries targets @@ -61,6 +50,7 @@ salomeinclude_HEADERS = \ lib_LTLIBRARIES = libSMESHimpl.la dist_libSMESHimpl_la_SOURCES = \ + memoire.h \ SMESH_Gen.cxx \ SMESH_Mesh.cxx \ SMESH_subMesh.cxx \ @@ -72,12 +62,10 @@ dist_libSMESHimpl_la_SOURCES = \ SMESH_3D_Algo.cxx \ SMESH_Group.cxx \ SMESH_MeshEditor.cxx \ - SMESH_Block.cxx \ SMESH_Pattern.cxx \ SMESH_HypoFilter.cxx \ - SMESH_MesherHelper.cxx \ - SMESH_Octree.cxx \ - SMESH_OctreeNode.cxx + SMESH_ProxyMesh.cxx \ + SMESH_MesherHelper.cxx # additionnal information to compile and link file libSMESHimpl_la_CPPFLAGS = \ @@ -85,6 +73,7 @@ libSMESHimpl_la_CPPFLAGS = \ $(CAS_CPPFLAGS) \ $(MED_CXXFLAGS) \ $(GEOM_CXX_FLAGS) \ + $(VTK_INCLUDES) \ $(BOOST_CPPFLAGS) \ @HDF5_INCLUDES@ \ -I$(srcdir)/../Controls \ @@ -93,10 +82,14 @@ libSMESHimpl_la_CPPFLAGS = \ -I$(srcdir)/../DriverMED \ -I$(srcdir)/../DriverUNV \ -I$(srcdir)/../DriverSTL \ + -I$(srcdir)/../DriverCGNS \ -I$(srcdir)/../SMDS \ -I$(srcdir)/../SMESHDS \ - -I$(top_builddir)/salome_adm/unix + -I$(srcdir)/../SMESHUtils +if WITH_CGNS + DriverCGNS_LIB = ../DriverCGNS/libMeshDriverCGNS.la +endif libSMESHimpl_la_LDFLAGS = \ ../SMESHDS/libSMESHDS.la \ @@ -105,5 +98,8 @@ libSMESHimpl_la_LDFLAGS = \ ../DriverSTL/libMeshDriverSTL.la \ ../DriverMED/libMeshDriverMED.la \ ../DriverUNV/libMeshDriverUNV.la \ + $(DriverCGNS_LIB) \ + ../SMESHUtils/libSMESHUtils.la \ + $(BOOST_LIB_THREAD) \ $(GEOM_LDFLAGS) -lNMTTools \ - $(CAS_LDPATH) -lTKShHealing + $(CAS_LDPATH) -lTKShHealing -lTKPrim -lTKG2d diff --git a/src/SMESH/SMESH_0D_Algo.cxx b/src/SMESH/SMESH_0D_Algo.cxx index f20074cc3..bc3461d10 100644 --- a/src/SMESH/SMESH_0D_Algo.cxx +++ b/src/SMESH/SMESH_0D_Algo.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_0D_Algo.cxx // Module : SMESH diff --git a/src/SMESH/SMESH_0D_Algo.hxx b/src/SMESH/SMESH_0D_Algo.hxx index 71f7476e6..d7ba782d8 100644 --- a/src/SMESH/SMESH_0D_Algo.hxx +++ b/src/SMESH/SMESH_0D_Algo.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_0D_Algo.hxx // Module : SMESH diff --git a/src/SMESH/SMESH_1D_Algo.cxx b/src/SMESH/SMESH_1D_Algo.cxx index ff6b9f7ff..3027f82f4 100644 --- a/src/SMESH/SMESH_1D_Algo.cxx +++ b/src/SMESH/SMESH_1D_Algo.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_1D_Algo.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_1D_Algo.hxx b/src/SMESH/SMESH_1D_Algo.hxx index 9da4fd985..e10aa197d 100644 --- a/src/SMESH/SMESH_1D_Algo.hxx +++ b/src/SMESH/SMESH_1D_Algo.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_1D_Algo.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_2D_Algo.cxx b/src/SMESH/SMESH_2D_Algo.cxx index 00723cbd7..a4feece7b 100644 --- a/src/SMESH/SMESH_2D_Algo.cxx +++ b/src/SMESH/SMESH_2D_Algo.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_2D_Algo.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_2D_Algo.hxx b/src/SMESH/SMESH_2D_Algo.hxx index 41a8bc1af..0a2a127f3 100644 --- a/src/SMESH/SMESH_2D_Algo.hxx +++ b/src/SMESH/SMESH_2D_Algo.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_2D_Algo.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_3D_Algo.cxx b/src/SMESH/SMESH_3D_Algo.cxx index ee547368f..d7a0ed88d 100644 --- a/src/SMESH/SMESH_3D_Algo.cxx +++ b/src/SMESH/SMESH_3D_Algo.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_3D_Algo.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_3D_Algo.hxx b/src/SMESH/SMESH_3D_Algo.hxx index 9c4761408..2b64d9eb0 100644 --- a/src/SMESH/SMESH_3D_Algo.hxx +++ b/src/SMESH/SMESH_3D_Algo.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_3D_Algo.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH/SMESH_Algo.cxx b/src/SMESH/SMESH_Algo.cxx index 7398cf856..9768eb049 100644 --- a/src/SMESH/SMESH_Algo.cxx +++ b/src/SMESH/SMESH_Algo.cxx @@ -1,41 +1,46 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Algo.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// #include "SMESH_Algo.hxx" -#include "SMESH_Comment.hxx" -#include "SMESH_Gen.hxx" -#include "SMESH_Mesh.hxx" -#include "SMESH_HypoFilter.hxx" -#include "SMDS_FacePosition.hxx" + #include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" #include "SMDS_MeshElement.hxx" #include "SMDS_MeshNode.hxx" +#include "SMDS_VolumeTool.hxx" #include "SMESHDS_Mesh.hxx" #include "SMESHDS_SubMesh.hxx" +#include "SMESH_Comment.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_HypoFilter.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_TypeDefs.hxx" + +#include #include #include @@ -61,6 +66,7 @@ #include "utilities.h" #include +#include using namespace std; @@ -75,8 +81,8 @@ SMESH_Algo::SMESH_Algo (int hypId, int studyId, SMESH_Gen * gen) { gen->_mapAlgo[hypId] = this; - _onlyUnaryInput = _requireDescretBoundary = _requireShape = true; - _quadraticMesh = false; + _onlyUnaryInput = _requireDiscreteBoundary = _requireShape = true; + _quadraticMesh = _supportSubmeshes = false; _error = COMPERR_OK; } @@ -170,11 +176,42 @@ double SMESH_Algo::EdgeLength(const TopoDS_Edge & E) return 0; TopLoc_Location L; Handle(Geom_Curve) C = BRep_Tool::Curve(E, L, UMin, UMax); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); //range is important for periodic curves double length = GCPnts_AbscissaPoint::Length(AdaptCurve, UMin, UMax); return length; } +//================================================================================ +/*! + * \brief Calculate normal of a mesh face + */ +//================================================================================ + +bool SMESH_Algo::FaceNormal(const SMDS_MeshElement* F, gp_XYZ& normal, bool normalized) +{ + if ( !F || F->GetType() != SMDSAbs_Face ) + return false; + + normal.SetCoord(0,0,0); + int nbNodes = F->IsQuadratic() ? F->NbNodes()/2 : F->NbNodes(); + for ( int i = 0; i < nbNodes-2; ++i ) + { + gp_XYZ p[3]; + for ( int n = 0; n < 3; ++n ) + { + const SMDS_MeshNode* node = F->GetNode( i + n ); + p[n].SetCoord( node->X(), node->Y(), node->Z() ); + } + normal += ( p[2] - p[1] ) ^ ( p[0] - p[1] ); + } + double size2 = normal.SquareModulus(); + bool ok = ( size2 > numeric_limits::min() * numeric_limits::min()); + if ( normalized && ok ) + normal /= sqrt( size2 ); + + return ok; +} + //================================================================================ /*! * \brief Find out elements orientation on a geometrical face @@ -223,10 +260,10 @@ bool SMESH_Algo::IsReversedSubMesh (const TopoDS_Face& theFace, const SMDS_PositionPtr& pos = node->GetPosition(); if ( !pos ) continue; if ( pos->GetTypeOfPosition() == SMDS_TOP_FACE ) { - fPos = dynamic_cast< const SMDS_FacePosition* >( pos.get() ); + fPos = dynamic_cast< const SMDS_FacePosition* >( pos ); } else if ( pos->GetTypeOfPosition() == SMDS_TOP_VERTEX ) { - vID = pos->GetShapeId(); + vID = node->getshapeId(); } } if ( fPos || ( !normalOK && vID )) { @@ -321,7 +358,7 @@ bool SMESH_Algo::GetNodeParamOnEdge(const SMESHDS_Mesh* theMesh, if ( !eSubMesh || !eSubMesh->GetElements()->more() ) return false; // edge is not meshed - int nbEdgeNodes = 0; + //int nbEdgeNodes = 0; set < double > paramSet; if ( eSubMesh ) { @@ -334,17 +371,19 @@ bool SMESH_Algo::GetNodeParamOnEdge(const SMESHDS_Mesh* theMesh, if ( pos->GetTypeOfPosition() != SMDS_TOP_EDGE ) return false; const SMDS_EdgePosition* epos = - static_cast(node->GetPosition().get()); - paramSet.insert( epos->GetUParameter() ); - ++nbEdgeNodes; + static_cast(node->GetPosition()); + if ( !paramSet.insert( epos->GetUParameter() ).second ) + return false; // equal parameters } } // add vertex nodes params - Standard_Real f, l; - BRep_Tool::Range(theEdge, f, l); - paramSet.insert( f ); - paramSet.insert( l ); - if ( paramSet.size() != nbEdgeNodes + 2 ) + TopoDS_Vertex V1,V2; + TopExp::Vertices( theEdge, V1, V2); + if ( VertexNode( V1, theMesh ) && + !paramSet.insert( BRep_Tool::Parameter(V1,theEdge) ).second ) + return false; // there are equal parameters + if ( VertexNode( V2, theMesh ) && + !paramSet.insert( BRep_Tool::Parameter(V2,theEdge) ).second ) return false; // there are equal parameters // fill the vector @@ -399,8 +438,9 @@ bool SMESH_Algo::GetSortedNodesOnEdge(const SMESHDS_Mesh* theM if ( pos->GetTypeOfPosition() != SMDS_TOP_EDGE ) return false; const SMDS_EdgePosition* epos = - static_cast(node->GetPosition().get()); - theNodes.insert( make_pair( epos->GetUParameter(), node )); + static_cast(node->GetPosition()); + theNodes.insert( theNodes.end(), make_pair( epos->GetUParameter(), node )); + //MESSAGE("U " << epos->GetUParameter() << " ID " << node->GetID()); ++nbNodes; } } @@ -409,6 +449,7 @@ bool SMESH_Algo::GetSortedNodesOnEdge(const SMESHDS_Mesh* theM TopExp::Vertices(theEdge, v1, v2); const SMDS_MeshNode* n1 = VertexNode( v1, (SMESHDS_Mesh*) theMesh ); const SMDS_MeshNode* n2 = VertexNode( v2, (SMESHDS_Mesh*) theMesh ); + //MESSAGE("Vertices ID " << n1->GetID() << " " << n2->GetID()); Standard_Real f, l; BRep_Tool::Range(theEdge, f, l); if ( v1.Orientation() != TopAbs_FORWARD ) @@ -455,20 +496,31 @@ bool SMESH_Algo::InitCompatibleHypoFilter( SMESH_HypoFilter & theFilter, */ //================================================================================ -GeomAbs_Shape SMESH_Algo::Continuity(const TopoDS_Edge & E1, - const TopoDS_Edge & E2) +GeomAbs_Shape SMESH_Algo::Continuity(TopoDS_Edge E1, + TopoDS_Edge E2) { - TopoDS_Vertex V = TopExp::LastVertex (E1, true); - if ( !V.IsSame( TopExp::FirstVertex(E2, true ))) - if ( !TopExp::CommonVertex( E1, E2, V )) - return GeomAbs_C0; + //E1.Orientation(TopAbs_FORWARD), E2.Orientation(TopAbs_FORWARD); // avoid pb with internal edges + if (E1.Orientation() > TopAbs_REVERSED) // INTERNAL + E1.Orientation( TopAbs_FORWARD ); + if (E2.Orientation() > TopAbs_REVERSED) // INTERNAL + E2.Orientation( TopAbs_FORWARD ); + + TopoDS_Vertex V, VV1[2], VV2[2]; + TopExp::Vertices( E1, VV1[0], VV1[1], true ); + TopExp::Vertices( E2, VV2[0], VV2[1], true ); + if ( VV1[1].IsSame( VV2[0] )) { V = VV1[1]; } + else if ( VV1[0].IsSame( VV2[1] )) { V = VV1[0]; } + else if ( VV1[1].IsSame( VV2[1] )) { V = VV1[1]; E1.Reverse(); } + else if ( VV1[0].IsSame( VV2[0] )) { V = VV1[0]; E1.Reverse(); } + else { return GeomAbs_C0; } + Standard_Real u1 = BRep_Tool::Parameter( V, E1 ); Standard_Real u2 = BRep_Tool::Parameter( V, E2 ); BRepAdaptor_Curve C1( E1 ), C2( E2 ); Standard_Real tol = BRep_Tool::Tolerance( V ); Standard_Real angTol = 2e-3; try { -#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 +#if OCC_VERSION_LARGE > 0x06010000 OCC_CATCH_SIGNALS; #endif return BRepLProp::Continuity(C1, C2, u1, u2, tol, angTol); @@ -488,7 +540,7 @@ GeomAbs_Shape SMESH_Algo::Continuity(const TopoDS_Edge & E1, //================================================================================ const SMDS_MeshNode* SMESH_Algo::VertexNode(const TopoDS_Vertex& V, - SMESHDS_Mesh* meshDS) + const SMESHDS_Mesh* meshDS) { if ( SMESHDS_SubMesh* sm = meshDS->MeshElements(V) ) { SMDS_NodeIteratorPtr nIt= sm->GetNodes(); @@ -498,6 +550,103 @@ const SMDS_MeshNode* SMESH_Algo::VertexNode(const TopoDS_Vertex& V, return 0; } +//======================================================================= +//function : GetCommonNodes +//purpose : Return nodes common to two elements +//======================================================================= + +vector< const SMDS_MeshNode*> SMESH_Algo::GetCommonNodes(const SMDS_MeshElement* e1, + const SMDS_MeshElement* e2) +{ + vector< const SMDS_MeshNode*> common; + for ( int i = 0 ; i < e1->NbNodes(); ++i ) + if ( e2->GetNodeIndex( e1->GetNode( i )) >= 0 ) + common.push_back( e1->GetNode( i )); + return common; +} + +//======================================================================= +//function : GetMeshError +//purpose : Finds topological errors of a sub-mesh +//WARNING : 1D check is NOT implemented so far +//======================================================================= + +SMESH_Algo::EMeshError SMESH_Algo::GetMeshError(SMESH_subMesh* subMesh) +{ + EMeshError err = MEr_OK; + + SMESHDS_SubMesh* smDS = subMesh->GetSubMeshDS(); + if ( !smDS ) + return MEr_EMPTY; + + switch ( subMesh->GetSubShape().ShapeType() ) + { + case TopAbs_FACE: { // ====================== 2D ===================== + + SMDS_ElemIteratorPtr fIt = smDS->GetElements(); + if ( !fIt->more() ) + return MEr_EMPTY; + + // We check that olny links on EDGEs encouter once, the rest links, twice + set< SMESH_TLink > links; + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + int nbNodes = f->NbCornerNodes(); // ignore medium nodes + for ( int i = 0; i < nbNodes; ++i ) + { + const SMDS_MeshNode* n1 = f->GetNode( i ); + const SMDS_MeshNode* n2 = f->GetNode(( i+1 ) % nbNodes); + std::pair< set< SMESH_TLink >::iterator, bool > it_added = + links.insert( SMESH_TLink( n1, n2 )); + if ( !it_added.second ) + // As we do NOT(!) check if mesh is manifold, we believe that a link can + // encounter once or twice only (not three times), we erase a link as soon + // as it encounters twice to speed up search in the map. + links.erase( it_added.first ); + } + } + // the links remaining in the should all be on EDGE + set< SMESH_TLink >::iterator linkIt = links.begin(); + for ( ; linkIt != links.end(); ++linkIt ) + { + const SMESH_TLink& link = *linkIt; + if ( link.node1()->GetPosition()->GetTypeOfPosition() > SMDS_TOP_EDGE || + link.node2()->GetPosition()->GetTypeOfPosition() > SMDS_TOP_EDGE ) + return MEr_HOLES; + } + // TODO: to check orientation + break; + } + case TopAbs_SOLID: { // ====================== 3D ===================== + + SMDS_ElemIteratorPtr vIt = smDS->GetElements(); + if ( !vIt->more() ) + return MEr_EMPTY; + + SMDS_VolumeTool vTool; + while ( !vIt->more() ) + { + if (!vTool.Set( vIt->next() )) + continue; // strange + + for ( int iF = 0; iF < vTool.NbFaces(); ++iF ) + if ( vTool.IsFreeFace( iF )) + { + int nbN = vTool.NbFaceNodes( iF ); + const SMDS_MeshNode** nodes = vTool.GetFaceNodes( iF ); + for ( int i = 0; i < nbN; ++i ) + if ( nodes[i]->GetPosition()->GetTypeOfPosition() > SMDS_TOP_FACE ) + return MEr_HOLES; + } + } + break; + } + default:; + } + return err; +} + //================================================================================ /*! * \brief Sets event listener to submeshes if necessary @@ -539,6 +688,18 @@ bool SMESH_Algo::Compute(SMESH_Mesh & /*aMesh*/, SMESH_MesherHelper* /*aHelper*/ return error( COMPERR_BAD_INPUT_MESH, "Mesh built on shape expected"); } +//======================================================================= +//function : CancelCompute +//purpose : Sets _computeCanceled to true. It's usage depends on +// * implementation of a particular mesher. +//======================================================================= + +void SMESH_Algo::CancelCompute() +{ + _computeCanceled = true; + _error = COMPERR_CANCELED; +} + //================================================================================ /*! * \brief store error and comment and then return ( error == COMPERR_OK ) @@ -599,6 +760,8 @@ void SMESH_Algo::InitComputeError() if ( (*elem)->GetID() < 1 ) delete *elem; _badInputElements.clear(); + + _computeCanceled = false; } //================================================================================ diff --git a/src/SMESH/SMESH_Algo.hxx b/src/SMESH/SMESH_Algo.hxx index 4b681dc0a..580445b48 100644 --- a/src/SMESH/SMESH_Algo.hxx +++ b/src/SMESH/SMESH_Algo.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Algo.hxx // Author : Paul RASCLE, EDF // Module : SMESH // - #ifndef _SMESH_ALGO_HXX_ #define _SMESH_ALGO_HXX_ @@ -53,8 +53,20 @@ class SMESHDS_Mesh; class SMDS_MeshNode; class SMESH_subMesh; class SMESH_MesherHelper; - - +class gp_XYZ; + +typedef std::map< SMESH_subMesh*, std::vector > MapShapeNbElems; +typedef std::map< SMESH_subMesh*, std::vector >::iterator MapShapeNbElemsItr; + +/*! + * \brief Root of all algorithms + * + * Methods of the class are grouped into several parts: + * - main lifecycle methods, like Compute() + * - methods describing features of the algorithm, like NeedShape() + * - methods related to dependencies between sub-meshes imposed by the algorith + * - static utilities, like EdgeLength() + */ class SMESH_EXPORT SMESH_Algo:public SMESH_Hypothesis { public: @@ -74,14 +86,14 @@ public: /*! * \brief Saves nothing in a stream * \param save - the stream - * \retval virtual std::ostream & - the stream + * \retval std::ostream & - the stream */ virtual std::ostream & SaveTo(std::ostream & save); /*! * \brief Loads nothing from a stream * \param load - the stream - * \retval virtual std::ostream & - the stream + * \retval std::ostream & - the stream */ virtual std::istream & LoadFrom(std::istream & load); @@ -106,9 +118,9 @@ public: * \param aShape - the shape * \retval bool - is a success * - * Algorithms that !NeedDescretBoundary() || !OnlyUnaryInput() are + * Algorithms that !NeedDiscreteBoundary() || !OnlyUnaryInput() are * to set SMESH_ComputeError returned by SMESH_submesh::GetComputeError() - * to report problematic subshapes + * to report problematic sub-shapes */ virtual bool Compute(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape) = 0; @@ -122,6 +134,22 @@ public: */ virtual bool Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper); + /*! + * \brief Sets _computeCanceled to true. It's usage depends on + * implementation of a particular mesher. + */ + virtual void CancelCompute(); + + /*! + * \brief evaluates size of prospective mesh on a shape + * \param aMesh - the mesh + * \param aShape - the shape + * \param aNbElems - prospective number of elements by types + * \retval bool - is a success + */ + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap) = 0; + /*! * \brief Returns a list of compatible hypotheses used to mesh a shape * \param aMesh - the mesh @@ -193,14 +221,14 @@ public: // an input temporary shape that is neither MainShape nor // its child. - bool NeedDescretBoundary() const { return _requireDescretBoundary; } + bool NeedDiscreteBoundary() const { return _requireDiscreteBoundary; } // 3 - is a Dim-1 mesh prerequisite bool NeedShape() const { return _requireShape; } // 4 - is shape existance required bool SupportSubmeshes() const { return _supportSubmeshes; } - // 5 - whether supports submeshes if !NeedDescretBoundary() + // 5 - whether supports submeshes if !NeedDiscreteBoundary() public: @@ -214,7 +242,7 @@ public: * * This method is called when a submesh gets HYP_OK algo_state. * After being set, event listener is notified on each event of a submesh. - * By default non listener is set + * By default none listener is set */ virtual void SetEventListener(SMESH_subMesh* subMesh); @@ -268,13 +296,18 @@ public: */ static double EdgeLength(const TopoDS_Edge & E); + /*! + * \brief Calculate normal of a mesh face + */ + static bool FaceNormal(const SMDS_MeshElement* F, gp_XYZ& normal, bool normalized=true); + /*! * \brief Return continuity of two edges * \param E1 - the 1st edge * \param E2 - the 2nd edge * \retval GeomAbs_Shape - regularity at the junction between E1 and E2 */ - static GeomAbs_Shape Continuity(const TopoDS_Edge & E1, const TopoDS_Edge & E2); + static GeomAbs_Shape Continuity(TopoDS_Edge E1, TopoDS_Edge E2); /*! * \brief Return true if an edge can be considered as a continuation of another @@ -289,10 +322,22 @@ public: * \param meshDS - mesh * \retval const SMDS_MeshNode* - found node or NULL */ - static const SMDS_MeshNode* VertexNode(const TopoDS_Vertex& V, - SMESHDS_Mesh* meshDS); + static const SMDS_MeshNode* VertexNode(const TopoDS_Vertex& V, const SMESHDS_Mesh* meshDS); -protected: + /*! + * \brief Return nodes common to two elements + */ + static std::vector< const SMDS_MeshNode*> GetCommonNodes(const SMDS_MeshElement* e1, + const SMDS_MeshElement* e2); + + enum EMeshError { MEr_OK = 0, MEr_HOLES, MEr_BAD_ORI, MEr_EMPTY }; + + /*! + * \brief Finds topological errors of a sub-mesh + */ + static EMeshError GetMeshError(SMESH_subMesh* subMesh); + + protected: /*! * \brief store error and comment and then return ( error == COMPERR_OK ) @@ -322,11 +367,11 @@ protected: // Algo features influencing which Compute() and how is called: // in what turn and with what input shape. - // This fields must be redefined if necessary by each descendant at constructor. + // These fields must be redefined if necessary by each descendant at constructor. bool _onlyUnaryInput; // mesh one shape of GetDim() at once. Default TRUE - bool _requireDescretBoundary; // GetDim()-1 mesh must be present. Default TRUE + bool _requireDiscreteBoundary; // GetDim()-1 mesh must be present. Default TRUE bool _requireShape; // work with GetDim()-1 mesh bound to geom only. Default TRUE - bool _supportSubmeshes; // if !_requireDescretBoundary. Default FALSE + bool _supportSubmeshes; // if !_requireDiscreteBoundary. Default FALSE // quadratic mesh creation required, // is usually set trough SMESH_MesherHelper::IsQuadraticSubMesh() @@ -335,6 +380,8 @@ protected: int _error; //!< SMESH_ComputeErrorName or anything algo specific std::string _comment; //!< any text explaining what is wrong in Compute() std::list _badInputElements; //!< to explain COMPERR_BAD_INPUT_MESH + + volatile bool _computeCanceled; //!< is set to True while computing to stop it }; #endif diff --git a/src/SMESH/SMESH_Comment.hxx b/src/SMESH/SMESH_Comment.hxx deleted file mode 100644 index 14d42faf4..000000000 --- a/src/SMESH/SMESH_Comment.hxx +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESH : implementaion of SMESH idl descriptions -// File : SMESH_Comment.hxx -// Created : Wed Mar 14 18:28:45 2007 -// Author : Edward AGAPOV (eap) -// Module : SMESH -// $Header: -// -#ifndef SMESH_Comment_HeaderFile -#define SMESH_Comment_HeaderFile - -# include -# include - -using namespace std; - -/*! - * \brief Class to generate string from any type - */ -class SMESH_Comment : public string -{ - ostringstream _s ; - -public : - - SMESH_Comment():string("") {} - - SMESH_Comment(const SMESH_Comment& c):string() { - _s << c.c_str() ; - this->string::operator=( _s.str() ); - } - - template - SMESH_Comment( const T &anything ) { - _s << anything ; - this->string::operator=( _s.str() ); - } - - template - SMESH_Comment & operator<<( const T &anything ) { - _s << anything ; - this->string::operator=( _s.str() ); - return *this ; - } -}; - - -#endif diff --git a/src/SMESH/SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx b/src/SMESH/SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx deleted file mode 100644 index 0ac60ca29..000000000 --- a/src/SMESH/SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File: SMESH_DataMapOfElemPtrSequenceOfElemPtr.hxx -// Created: 26.09.05 17:41:10 -// Author: Sergey KUUL -// -#ifndef SMESH_DataMapOfElemPtrSequenceOfElemPtr_HeaderFile -#define SMESH_DataMapOfElemPtrSequenceOfElemPtr_HeaderFile - -#include "SMESH_SMESH.hxx" - -#include - -#include - -SMESH_EXPORT -inline Standard_Integer HashCode(SMDS_MeshElementPtr theElem, - const Standard_Integer theUpper) -{ - void* anElem = (void*) theElem; - return HashCode(anElem,theUpper); -} - -SMESH_EXPORT -inline Standard_Boolean IsEqual(SMDS_MeshElementPtr theOne, - SMDS_MeshElementPtr theTwo) -{ - return theOne == theTwo; -} - -DEFINE_BASECOLLECTION (SMESH_BaseCollectionSequenceOfElemPtr, SMESH_SequenceOfElemPtr) -DEFINE_DATAMAP (SMESH_DataMapOfElemPtrSequenceOfElemPtr, - SMESH_BaseCollectionSequenceOfElemPtr, - SMDS_MeshElementPtr, SMESH_SequenceOfElemPtr) -#endif diff --git a/src/SMESH/SMESH_Gen.cxx b/src/SMESH/SMESH_Gen.cxx index 955e1335d..dae945eec 100644 --- a/src/SMESH/SMESH_Gen.cxx +++ b/src/SMESH/SMESH_Gen.cxx @@ -1,104 +1,89 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Gen.cxx // Author : Paul RASCLE, EDF // Module : SMESH +// + +//#define CHRONODEF #include "SMESH_Gen.hxx" -#include "SMESH_subMesh.hxx" -#include "SMESH_HypoFilter.hxx" -#include "SMESHDS_Document.hxx" + +#include "SMDS_Mesh.hxx" #include "SMDS_MeshElement.hxx" #include "SMDS_MeshNode.hxx" +#include "SMESHDS_Document.hxx" +#include "SMESH_HypoFilter.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" #include "utilities.h" #include "OpUtil.hxx" #include "Utils_ExceptHandlers.hxx" -#include -#include -#include -#include +#include + +#include "memoire.h" using namespace std; //============================================================================= /*! - * default constructor: + * Constructor */ //============================================================================= SMESH_Gen::SMESH_Gen() { - MESSAGE("SMESH_Gen::SMESH_Gen"); - _localId = 0; - _hypId = 0; - _segmentation = 10; + MESSAGE("SMESH_Gen::SMESH_Gen"); + _localId = 0; + _hypId = 0; + _segmentation = _nbSegments = 10; + SMDS_Mesh::_meshList.clear(); + MESSAGE(SMDS_Mesh::_meshList.size()); + _counters = new counters(100); +#ifdef WITH_SMESH_CANCEL_COMPUTE + _compute_canceled = false; + _sm_current = NULL; +#endif } //============================================================================= /*! - * + * Destructor */ //============================================================================= SMESH_Gen::~SMESH_Gen() { - MESSAGE("SMESH_Gen::~SMESH_Gen"); + MESSAGE("SMESH_Gen::~SMESH_Gen"); } //============================================================================= /*! - * - */ -//============================================================================= - -/*SMESH_Hypothesis *SMESH_Gen::CreateHypothesis(const char *anHyp, int studyId) - throw(SALOME_Exception) -{ - - MESSAGE("CreateHypothesis("<GetID(); - myStudyContext->mapHypothesis[hypId] = myHypothesis; - SCRUTE(studyId); - SCRUTE(hypId); - - // store hypothesis in SMESHDS document - - myStudyContext->myDocument->AddHypothesis(myHypothesis); - return myHypothesis; -}*/ - -//============================================================================= -/*! - * + * Creates a mesh in a study. + * if (theIsEmbeddedMode) { mesh modification commands are not logged } */ //============================================================================= @@ -113,11 +98,11 @@ SMESH_Mesh* SMESH_Gen::CreateMesh(int theStudyId, bool theIsEmbeddedMode) // create a new SMESH_mesh object SMESH_Mesh *aMesh = new SMESH_Mesh(_localId++, - theStudyId, - this, - theIsEmbeddedMode, - aStudyContext->myDocument); - aStudyContext->mapMesh[_localId] = aMesh; + theStudyId, + this, + theIsEmbeddedMode, + aStudyContext->myDocument); + aStudyContext->mapMesh[_localId-1] = aMesh; return aMesh; } @@ -131,10 +116,11 @@ SMESH_Mesh* SMESH_Gen::CreateMesh(int theStudyId, bool theIsEmbeddedMode) bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, const bool anUpward, - const ::MeshDimension aDim, - TSetOfInt* aShapesId) + const ::MeshDimension aDim, + TSetOfInt* aShapesId) { MESSAGE("SMESH_Gen::Compute"); + MEMOSTAT; bool ret = true; @@ -142,13 +128,14 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, const bool includeSelf = true; const bool complexShapeFirst = true; + const int globalAlgoDim = 100; SMESH_subMeshIteratorPtr smIt; if ( anUpward ) // is called from below code here { // ----------------------------------------------- - // mesh all the subshapes starting from vertices + // mesh all the sub-shapes starting from vertices // ----------------------------------------------- smIt = sm->getDependsOnIterator(includeSelf, !complexShapeFirst); while ( smIt->more() ) @@ -166,28 +153,47 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, // clear compute state to not show previous compute errors // if preview invoked less dimension less than previous smToCompute->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); - continue; + continue; } if (smToCompute->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE) + { +#ifdef WITH_SMESH_CANCEL_COMPUTE + if (_compute_canceled) + return false; + _sm_current = smToCompute; +#endif smToCompute->ComputeStateEngine( SMESH_subMesh::COMPUTE ); +#ifdef WITH_SMESH_CANCEL_COMPUTE + _sm_current = NULL; +#endif + } // we check all the submeshes here and detect if any of them failed to compute if (smToCompute->GetComputeState() == SMESH_subMesh::FAILED_TO_COMPUTE) ret = false; else if ( aShapesId ) - aShapesId->insert( smToCompute->GetId() ); + aShapesId->insert( smToCompute->GetId() ); } + //aMesh.GetMeshDS()->Modified(); return ret; } else { // ----------------------------------------------------------------- - // apply algos that DO NOT require descretized boundaries and DO NOT + // apply algos that DO NOT require Discreteized boundaries and DO NOT // support submeshes, starting from the most complex shapes // and collect submeshes with algos that DO support submeshes // ----------------------------------------------------------------- list< SMESH_subMesh* > smWithAlgoSupportingSubmeshes; + + // map to sort sm with same dim algos according to dim of + // the shape the algo assigned to (issue 0021217) + multimap< int, SMESH_subMesh* > shDim2sm; + multimap< int, SMESH_subMesh* >::reverse_iterator shDim2smIt; + TopoDS_Shape algoShape; + int prevShapeDim = -1; + smIt = sm->getDependsOnIterator(includeSelf, complexShapeFirst); while ( smIt->more() ) { @@ -196,40 +202,87 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, continue; const TopoDS_Shape& aSubShape = smToCompute->GetSubShape(); - const int aShapeDim = GetShapeDim( aSubShape ); + int aShapeDim = GetShapeDim( aSubShape ); if ( aShapeDim < 1 ) break; // check for preview dimension limitations if ( aShapesId && aShapeDim > (int)aDim ) - continue; + continue; - SMESH_Algo* algo = GetAlgo( aMesh, aSubShape ); - if ( algo && !algo->NeedDescretBoundary() ) + SMESH_Algo* algo = GetAlgo( aMesh, aSubShape, &algoShape ); + if ( algo && !algo->NeedDiscreteBoundary() ) { if ( algo->SupportSubmeshes() ) - smWithAlgoSupportingSubmeshes.push_back( smToCompute ); + { + // reload sub-meshes from shDim2sm into smWithAlgoSupportingSubmeshes + // so that more local algos to go first + if ( prevShapeDim != aShapeDim ) + { + prevShapeDim = aShapeDim; + for ( shDim2smIt = shDim2sm.rbegin(); shDim2smIt != shDim2sm.rend(); ++shDim2smIt ) + if ( shDim2smIt->first == globalAlgoDim ) + smWithAlgoSupportingSubmeshes.push_back( shDim2smIt->second ); + else + smWithAlgoSupportingSubmeshes.push_front( shDim2smIt->second ); + shDim2sm.clear(); + } + // add smToCompute to shDim2sm map + if ( algoShape.IsSame( aMesh.GetShapeToMesh() )) + { + aShapeDim = globalAlgoDim; // to compute last + } + else + { + aShapeDim = GetShapeDim( algoShape ); + if ( algoShape.ShapeType() == TopAbs_COMPOUND ) + { + TopoDS_Iterator it( algoShape ); + aShapeDim += GetShapeDim( it.Value() ); + } + } + shDim2sm.insert( make_pair( aShapeDim, smToCompute )); + } else - { + { +#ifdef WITH_SMESH_CANCEL_COMPUTE + if (_compute_canceled) + return false; + _sm_current = smToCompute; +#endif smToCompute->ComputeStateEngine( SMESH_subMesh::COMPUTE ); - if ( aShapesId ) - aShapesId->insert( smToCompute->GetId() ); - } +#ifdef WITH_SMESH_CANCEL_COMPUTE + _sm_current = NULL; +#endif + if ( aShapesId ) + aShapesId->insert( smToCompute->GetId() ); + } } } + // reload sub-meshes from shDim2sm into smWithAlgoSupportingSubmeshes + for ( shDim2smIt = shDim2sm.rbegin(); shDim2smIt != shDim2sm.rend(); ++shDim2smIt ) + if ( shDim2smIt->first == globalAlgoDim ) + smWithAlgoSupportingSubmeshes.push_back( shDim2smIt->second ); + else + smWithAlgoSupportingSubmeshes.push_front( shDim2smIt->second ); + + // ------------------------------------------------------------ + // sort list of submeshes according to mesh order + // ------------------------------------------------------------ + aMesh.SortByMeshOrder( smWithAlgoSupportingSubmeshes ); + // ------------------------------------------------------------ // compute submeshes under shapes with algos that DO NOT require - // descretized boundaries and DO support submeshes + // Discreteized boundaries and DO support submeshes // ------------------------------------------------------------ - list< SMESH_subMesh* >::reverse_iterator subIt, subEnd; - subIt = smWithAlgoSupportingSubmeshes.rbegin(); - subEnd = smWithAlgoSupportingSubmeshes.rend(); + list< SMESH_subMesh* >::iterator subIt, subEnd; + subIt = smWithAlgoSupportingSubmeshes.begin(); + subEnd = smWithAlgoSupportingSubmeshes.end(); // start from lower shapes for ( ; subIt != subEnd; ++subIt ) { sm = *subIt; // get a shape the algo is assigned to - TopoDS_Shape algoShape; if ( !GetAlgo( aMesh, sm->GetSubShape(), & algoShape )) continue; // strange... @@ -240,18 +293,18 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, SMESH_subMesh* smToCompute = smIt->next(); const TopoDS_Shape& aSubShape = smToCompute->GetSubShape(); - const int aShapeDim = GetShapeDim( aSubShape ); + const int aShapeDim = GetShapeDim( aSubShape ); //if ( aSubShape.ShapeType() == TopAbs_VERTEX ) continue; - if ( aShapeDim < 1 ) continue; + if ( aShapeDim < 1 ) continue; - // check for preview dimension limitations - if ( aShapesId && GetShapeDim( aSubShape.ShapeType() ) > (int)aDim ) - continue; - + // check for preview dimension limitations + if ( aShapesId && GetShapeDim( aSubShape.ShapeType() ) > (int)aDim ) + continue; + SMESH_HypoFilter filter( SMESH_HypoFilter::IsAlgo() ); filter .And( SMESH_HypoFilter::IsApplicableTo( aSubShape )) - .And( SMESH_HypoFilter::IsMoreLocalThan( algoShape )); + .And( SMESH_HypoFilter::IsMoreLocalThan( algoShape, aMesh )); if ( SMESH_Algo* subAlgo = (SMESH_Algo*) aMesh.GetHypothesis( aSubShape, filter, true )) { SMESH_Hypothesis::Hypothesis_Status status; @@ -262,31 +315,239 @@ bool SMESH_Gen::Compute(SMESH_Mesh & aMesh, } } // ---------------------------------------------------------- - // apply the algos that do not require descretized boundaries + // apply the algos that do not require Discreteized boundaries // ---------------------------------------------------------- - for ( subIt = smWithAlgoSupportingSubmeshes.rbegin(); subIt != subEnd; ++subIt ) + for ( subIt = smWithAlgoSupportingSubmeshes.begin(); subIt != subEnd; ++subIt ) + { + sm = *subIt; if ( sm->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE) { - const TopAbs_ShapeEnum aShType = sm->GetSubShape().ShapeType(); - // check for preview dimension limitations - if ( aShapesId && GetShapeDim( aShType ) > (int)aDim ) - continue; - + const TopAbs_ShapeEnum aShType = sm->GetSubShape().ShapeType(); + // check for preview dimension limitations + if ( aShapesId && GetShapeDim( aShType ) > (int)aDim ) + continue; + +#ifdef WITH_SMESH_CANCEL_COMPUTE + if (_compute_canceled) + return false; + _sm_current = sm; +#endif sm->ComputeStateEngine( SMESH_subMesh::COMPUTE ); - if ( aShapesId ) - aShapesId->insert( sm->GetId() ); +#ifdef WITH_SMESH_CANCEL_COMPUTE + _sm_current = NULL; +#endif + if ( aShapesId ) + aShapesId->insert( sm->GetId() ); } - + } // ----------------------------------------------- - // mesh the rest subshapes starting from vertices + // mesh the rest sub-shapes starting from vertices // ----------------------------------------------- ret = Compute( aMesh, aShape, /*anUpward=*/true, aDim, aShapesId ); } MESSAGE( "VSR - SMESH_Gen::Compute() finished, OK = " << ret); + MEMOSTAT; + + SMESHDS_Mesh *myMesh = aMesh.GetMeshDS(); + myMesh->adjustStructure(); + MESSAGE("*** compactMesh after compute"); + myMesh->compactMesh(); + //myMesh->adjustStructure(); + list listind = myMesh->SubMeshIndices(); + list::iterator it = listind.begin(); + int total = 0; + for(; it != listind.end(); ++it) + { + ::SMESHDS_SubMesh *subMesh = myMesh->MeshElements(*it); + total += subMesh->getSize(); + } + MESSAGE("total elements and nodes in submesh sets:" << total); + MESSAGE("Number of node objects " << SMDS_MeshNode::nbNodes); + MESSAGE("Number of cell objects " << SMDS_MeshCell::nbCells); + //myMesh->dumpGrid(); + //aMesh.GetMeshDS()->Modified(); + + // fix quadratic mesh by bending iternal links near concave boundary + if ( aShape.IsSame( aMesh.GetShapeToMesh() ) && + !aShapesId ) // not preview + { + SMESH_MesherHelper aHelper( aMesh ); + if ( aHelper.IsQuadraticMesh() != SMESH_MesherHelper::LINEAR ) + aHelper.FixQuadraticElements(); + } + return ret; +} + + +#ifdef WITH_SMESH_CANCEL_COMPUTE +//============================================================================= +/*! + * Prepare Compute a mesh + */ +//============================================================================= +void SMESH_Gen::PrepareCompute(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape) +{ + _compute_canceled = false; + _sm_current = NULL; +} +//============================================================================= +/*! + * Cancel Compute a mesh + */ +//============================================================================= +void SMESH_Gen::CancelCompute(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape) +{ + _compute_canceled = true; + if(_sm_current) + { + _sm_current->ComputeStateEngine( SMESH_subMesh::COMPUTE_CANCELED ); + } +} +#endif + +//============================================================================= +/*! + * Evaluate a mesh + */ +//============================================================================= + +bool SMESH_Gen::Evaluate(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap, + const bool anUpward, + TSetOfInt* aShapesId) +{ + MESSAGE("SMESH_Gen::Evaluate"); + + bool ret = true; + + SMESH_subMesh *sm = aMesh.GetSubMesh(aShape); + + const bool includeSelf = true; + const bool complexShapeFirst = true; + SMESH_subMeshIteratorPtr smIt; + + if ( anUpward ) { // is called from below code here + // ----------------------------------------------- + // mesh all the sub-shapes starting from vertices + // ----------------------------------------------- + smIt = sm->getDependsOnIterator(includeSelf, !complexShapeFirst); + while ( smIt->more() ) { + SMESH_subMesh* smToCompute = smIt->next(); + + // do not mesh vertices of a pseudo shape + const TopAbs_ShapeEnum aShType = smToCompute->GetSubShape().ShapeType(); + //if ( !aMesh.HasShapeToMesh() && aShType == TopAbs_VERTEX ) + // continue; + if ( !aMesh.HasShapeToMesh() ) { + if( aShType == TopAbs_VERTEX || aShType == TopAbs_WIRE || + aShType == TopAbs_SHELL ) + continue; + } + + smToCompute->Evaluate(aResMap); + if( aShapesId ) + aShapesId->insert( smToCompute->GetId() ); + } + return ret; + } + else { + // ----------------------------------------------------------------- + // apply algos that DO NOT require Discreteized boundaries and DO NOT + // support submeshes, starting from the most complex shapes + // and collect submeshes with algos that DO support submeshes + // ----------------------------------------------------------------- + list< SMESH_subMesh* > smWithAlgoSupportingSubmeshes; + smIt = sm->getDependsOnIterator(includeSelf, complexShapeFirst); + while ( smIt->more() ) { + SMESH_subMesh* smToCompute = smIt->next(); + const TopoDS_Shape& aSubShape = smToCompute->GetSubShape(); + const int aShapeDim = GetShapeDim( aSubShape ); + if ( aShapeDim < 1 ) break; + + SMESH_Algo* algo = GetAlgo( aMesh, aSubShape ); + if ( algo && !algo->NeedDiscreteBoundary() ) { + if ( algo->SupportSubmeshes() ) { + smWithAlgoSupportingSubmeshes.push_front( smToCompute ); + } + else { + smToCompute->Evaluate(aResMap); + if ( aShapesId ) + aShapesId->insert( smToCompute->GetId() ); + } + } + } + + // ------------------------------------------------------------ + // sort list of meshes according to mesh order + // ------------------------------------------------------------ + aMesh.SortByMeshOrder( smWithAlgoSupportingSubmeshes ); + + // ------------------------------------------------------------ + // compute submeshes under shapes with algos that DO NOT require + // Discreteized boundaries and DO support submeshes + // ------------------------------------------------------------ + list< SMESH_subMesh* >::iterator subIt, subEnd; + subIt = smWithAlgoSupportingSubmeshes.begin(); + subEnd = smWithAlgoSupportingSubmeshes.end(); + // start from lower shapes + for ( ; subIt != subEnd; ++subIt ) { + sm = *subIt; + + // get a shape the algo is assigned to + TopoDS_Shape algoShape; + if ( !GetAlgo( aMesh, sm->GetSubShape(), & algoShape )) + continue; // strange... + + // look for more local algos + smIt = sm->getDependsOnIterator(!includeSelf, !complexShapeFirst); + while ( smIt->more() ) { + SMESH_subMesh* smToCompute = smIt->next(); + + const TopoDS_Shape& aSubShape = smToCompute->GetSubShape(); + const int aShapeDim = GetShapeDim( aSubShape ); + if ( aShapeDim < 1 ) continue; + + //const TopAbs_ShapeEnum aShType = smToCompute->GetSubShape().ShapeType(); + + SMESH_HypoFilter filter( SMESH_HypoFilter::IsAlgo() ); + filter + .And( SMESH_HypoFilter::IsApplicableTo( aSubShape )) + .And( SMESH_HypoFilter::IsMoreLocalThan( algoShape, aMesh )); + + if ( SMESH_Algo* subAlgo = (SMESH_Algo*) aMesh.GetHypothesis( aSubShape, filter, true )) { + SMESH_Hypothesis::Hypothesis_Status status; + if ( subAlgo->CheckHypothesis( aMesh, aSubShape, status )) + // mesh a lower smToCompute starting from vertices + Evaluate( aMesh, aSubShape, aResMap, /*anUpward=*/true, aShapesId ); + } + } + } + // ---------------------------------------------------------- + // apply the algos that do not require Discreteized boundaries + // ---------------------------------------------------------- + for ( subIt = smWithAlgoSupportingSubmeshes.begin(); subIt != subEnd; ++subIt ) + { + sm = *subIt; + sm->Evaluate(aResMap); + if ( aShapesId ) + aShapesId->insert( sm->GetId() ); + } + + // ----------------------------------------------- + // mesh the rest sub-shapes starting from vertices + // ----------------------------------------------- + ret = Evaluate( aMesh, aShape, aResMap, /*anUpward=*/true, aShapesId ); + } + + MESSAGE( "VSR - SMESH_Gen::Evaluate() finished, OK = " << ret); return ret; } + //======================================================================= //function : checkConformIgnoredAlgos //purpose : @@ -297,7 +558,7 @@ static bool checkConformIgnoredAlgos(SMESH_Mesh& aMesh, const SMESH_Algo* aGlobIgnoAlgo, const SMESH_Algo* aLocIgnoAlgo, bool & checkConform, - map& aCheckedMap, + set& aCheckedMap, list< SMESH_Gen::TAlgoStateError > & theErrors) { ASSERT( aSubMesh ); @@ -337,7 +598,7 @@ static bool checkConformIgnoredAlgos(SMESH_Mesh& aMesh, << " <" << algo->GetName() << "> is hidden by global <" << aGlobIgnoAlgo->GetName() << ">"); } - else if ( !algo->NeedDescretBoundary() && !isGlobal) + else if ( !algo->NeedDiscreteBoundary() && !isGlobal) { // local algo is not hidden and hides algos on sub-shapes if (checkConform && !aSubMesh->IsConform( algo )) @@ -352,19 +613,15 @@ static bool checkConformIgnoredAlgos(SMESH_Mesh& aMesh, } // sub-algos will be hidden by a local - const map& smMap = aSubMesh->DependsOn(); - map::const_reverse_iterator revItSub; + SMESH_subMeshIteratorPtr revItSub = + aSubMesh->getDependsOnIterator( /*includeSelf=*/false, /*complexShapeFirst=*/true); bool checkConform2 = false; - for ( revItSub = smMap.rbegin(); revItSub != smMap.rend(); revItSub++) + while ( revItSub->more() ) { - checkConformIgnoredAlgos (aMesh, (*revItSub).second, aGlobIgnoAlgo, + SMESH_subMesh* sm = revItSub->next(); + checkConformIgnoredAlgos (aMesh, sm, aGlobIgnoAlgo, algo, checkConform2, aCheckedMap, theErrors); - int key = (*revItSub).first; - SMESH_subMesh* sm = (*revItSub).second; - if ( aCheckedMap.find( key ) == aCheckedMap.end() ) - { - aCheckedMap[ key ] = sm; - } + aCheckedMap.insert( sm ); } } } @@ -385,7 +642,7 @@ static bool checkMissing(SMESH_Gen* aGen, const int aTopAlgoDim, bool* globalChecked, const bool checkNoAlgo, - map& aCheckedMap, + set& aCheckedMap, list< SMESH_Gen::TAlgoStateError > & theErrors) { if ( aSubMesh->GetSubShape().ShapeType() == TopAbs_VERTEX) @@ -455,18 +712,16 @@ static bool checkMissing(SMESH_Gen* aGen, ASSERT (algo); bool isTopLocalAlgo = ( aTopAlgoDim <= algo->GetDim() && !aGen->IsGlobalHypothesis( algo, aMesh )); - if (!algo->NeedDescretBoundary() || isTopLocalAlgo) + if (!algo->NeedDiscreteBoundary() || isTopLocalAlgo) { - bool checkNoAlgo2 = ( algo->NeedDescretBoundary() ); - const map& subMeshes = aSubMesh->DependsOn(); - map::const_iterator itsub; - for (itsub = subMeshes.begin(); itsub != subMeshes.end(); itsub++) + bool checkNoAlgo2 = ( algo->NeedDiscreteBoundary() ); + SMESH_subMeshIteratorPtr itsub = aSubMesh->getDependsOnIterator( /*includeSelf=*/false, + /*complexShapeFirst=*/false); + while ( itsub->more() ) { // sub-meshes should not be checked further more - int key = (*itsub).first; - SMESH_subMesh* sm = (*itsub).second; - if ( aCheckedMap.find( key ) == aCheckedMap.end() ) - aCheckedMap[ key ] = sm; + SMESH_subMesh* sm = itsub->next(); + aCheckedMap.insert( sm ); if (isTopLocalAlgo) { @@ -542,7 +797,7 @@ bool SMESH_Gen::GetAlgoState(SMESH_Mesh& theMesh, // -------------------------------------------------------- // info on algos that will be ignored because of ones that - // don't NeedDescretBoundary() attached to super-shapes, + // don't NeedDiscreteBoundary() attached to super-shapes, // check that a conform mesh will be produced // -------------------------------------------------------- @@ -553,46 +808,32 @@ bool SMESH_Gen::GetAlgoState(SMESH_Mesh& theMesh, for (dim = 3; dim > 0; dim--) { if (aGlobAlgoArr[ dim ] && - !aGlobAlgoArr[ dim ]->NeedDescretBoundary()) + !aGlobAlgoArr[ dim ]->NeedDiscreteBoundary()) { aGlobIgnoAlgo = aGlobAlgoArr[ dim ]; break; } } - const map& smMap = sm->DependsOn(); - map::const_reverse_iterator revItSub = smMap.rbegin(); - map aCheckedMap; + set aCheckedSubs; bool checkConform = ( !theMesh.IsNotConformAllowed() ); - int aKey = 1; - SMESH_subMesh* smToCheck = sm; // loop on theShape and its sub-shapes - while ( smToCheck ) + SMESH_subMeshIteratorPtr revItSub = sm->getDependsOnIterator( /*includeSelf=*/true, + /*complexShapeFirst=*/true); + while ( revItSub->more() ) { + SMESH_subMesh* smToCheck = revItSub->next(); if ( smToCheck->GetSubShape().ShapeType() == TopAbs_VERTEX) break; - if ( aCheckedMap.find( aKey ) == aCheckedMap.end() ) + if ( aCheckedSubs.insert( smToCheck ).second ) // not yet checked if (!checkConformIgnoredAlgos (theMesh, smToCheck, aGlobIgnoAlgo, - 0, checkConform, aCheckedMap, theErrors)) + 0, checkConform, aCheckedSubs, theErrors)) ret = false; if ( smToCheck->GetAlgoState() != SMESH_subMesh::NO_ALGO ) hasAlgo = true; - - // next subMesh - if (revItSub != smMap.rend()) - { - aKey = (*revItSub).first; - smToCheck = (*revItSub).second; - revItSub++; - } - else - { - smToCheck = 0; - } - } // ---------------------------------------------------------------- @@ -612,36 +853,26 @@ bool SMESH_Gen::GetAlgoState(SMESH_Mesh& theMesh, break; } } - aCheckedMap.clear(); - smToCheck = sm; - revItSub = smMap.rbegin(); bool checkNoAlgo = theMesh.HasShapeToMesh() ? bool( aTopAlgoDim ) : false; bool globalChecked[] = { false, false, false, false }; // loop on theShape and its sub-shapes - while ( smToCheck ) + aCheckedSubs.clear(); + revItSub = sm->getDependsOnIterator( /*includeSelf=*/true, /*complexShapeFirst=*/true); + while ( revItSub->more() ) { + SMESH_subMesh* smToCheck = revItSub->next(); if ( smToCheck->GetSubShape().ShapeType() == TopAbs_VERTEX) break; - if ( aCheckedMap.find( aKey ) == aCheckedMap.end() ) + if ( aCheckedSubs.insert( smToCheck ).second ) // not yet checked if (!checkMissing (this, theMesh, smToCheck, aTopAlgoDim, - globalChecked, checkNoAlgo, aCheckedMap, theErrors)) + globalChecked, checkNoAlgo, aCheckedSubs, theErrors)) { ret = false; if (smToCheck->GetAlgoState() == SMESH_subMesh::NO_ALGO ) checkNoAlgo = false; } - - // next subMesh - if (revItSub != smMap.rend()) - { - aKey = (*revItSub).first; - smToCheck = (*revItSub).second; - revItSub++; - } - else - smToCheck = 0; } if ( !hasAlgo ) { @@ -667,7 +898,7 @@ bool SMESH_Gen::IsGlobalHypothesis(const SMESH_Hypothesis* theHyp, SMESH_Mesh& a //============================================================================= /*! - * + * Finds algo to mesh a shape. Optionally returns a shape the found algo is bound to */ //============================================================================= @@ -675,7 +906,6 @@ SMESH_Algo *SMESH_Gen::GetAlgo(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, TopoDS_Shape* assignedTo) { - SMESH_HypoFilter filter( SMESH_HypoFilter::IsAlgo() ); filter.And( filter.IsApplicableTo( aShape )); @@ -684,59 +914,28 @@ SMESH_Algo *SMESH_Gen::GetAlgo(SMESH_Mesh & aMesh, //============================================================================= /*! - * + * Returns StudyContextStruct for a study */ //============================================================================= StudyContextStruct *SMESH_Gen::GetStudyContext(int studyId) { - // Get studyContext, create it if it does'nt exist, with a SMESHDS_Document - - if (_mapStudyContext.find(studyId) == _mapStudyContext.end()) - { - _mapStudyContext[studyId] = new StudyContextStruct; - _mapStudyContext[studyId]->myDocument = new SMESHDS_Document(studyId); - } - StudyContextStruct *myStudyContext = _mapStudyContext[studyId]; -// ASSERT(_mapStudyContext.find(studyId) != _mapStudyContext.end()); - return myStudyContext; -} - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= - -// void SMESH_Gen::Save(int studyId, const char *aUrlOfFile) -// { -// } - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= - -// void SMESH_Gen::Load(int studyId, const char *aUrlOfFile) -// { -// } - -// //============================================================================= -// /*! -// * -// */ -// //============================================================================= + // Get studyContext, create it if it does'nt exist, with a SMESHDS_Document -// void SMESH_Gen::Close(int studyId) -// { -// } + if (_mapStudyContext.find(studyId) == _mapStudyContext.end()) + { + _mapStudyContext[studyId] = new StudyContextStruct; + _mapStudyContext[studyId]->myDocument = new SMESHDS_Document(studyId); + } + StudyContextStruct *myStudyContext = _mapStudyContext[studyId]; + return myStudyContext; +} -//============================================================================= +//================================================================================ /*! - * + * \brief Return shape dimension by TopAbs_ShapeEnum */ -//============================================================================= +//================================================================================ int SMESH_Gen::GetShapeDim(const TopAbs_ShapeEnum & aShapeType) { @@ -747,7 +946,7 @@ int SMESH_Gen::GetShapeDim(const TopAbs_ShapeEnum & aShapeType) dim[ TopAbs_COMPOUND ] = MeshDim_3D; dim[ TopAbs_COMPSOLID ] = MeshDim_3D; dim[ TopAbs_SOLID ] = MeshDim_3D; - dim[ TopAbs_SHELL ] = MeshDim_3D; + dim[ TopAbs_SHELL ] = MeshDim_2D; dim[ TopAbs_FACE ] = MeshDim_2D; dim[ TopAbs_WIRE ] = MeshDim_1D; dim[ TopAbs_EDGE ] = MeshDim_1D; @@ -758,12 +957,11 @@ int SMESH_Gen::GetShapeDim(const TopAbs_ShapeEnum & aShapeType) //============================================================================= /*! - * + * Genarate a new id unique withing this Gen */ //============================================================================= int SMESH_Gen::GetANewId() { - //MESSAGE("SMESH_Gen::GetANewId"); - return _hypId++; + return _hypId++; } diff --git a/src/SMESH/SMESH_Gen.hxx b/src/SMESH/SMESH_Gen.hxx index 46c00dc2c..353cba6df 100644 --- a/src/SMESH/SMESH_Gen.hxx +++ b/src/SMESH/SMESH_Gen.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Gen.hxx // Author : Paul RASCLE, EDF // Module : SMESH // - #ifndef _SMESH_GEN_HXX_ #define _SMESH_GEN_HXX_ @@ -41,6 +41,8 @@ #include "SMESH_3D_Algo.hxx" #include "SMESH_Mesh.hxx" +#include "chrono.hxx" + #include #include @@ -61,7 +63,7 @@ typedef std::set TSetOfInt; class SMESH_EXPORT SMESH_Gen { - public: +public: SMESH_Gen(); ~SMESH_Gen(); @@ -78,8 +80,28 @@ class SMESH_EXPORT SMESH_Gen bool Compute(::SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, const bool anUpward=false, - const ::MeshDimension aDim=::MeshDim_3D, - TSetOfInt* aShapesId=0); + const ::MeshDimension aDim=::MeshDim_3D, + TSetOfInt* aShapesId=0); + +#ifdef WITH_SMESH_CANCEL_COMPUTE + void PrepareCompute(::SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape); + void CancelCompute(::SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape); +#endif + + /*! + * \brief evaluates size of prospective mesh on a shape + * \param aMesh - the mesh + * \param aShape - the shape + * \param aResMap - map for prospective numbers of elements + * \retval bool - is a success + */ + bool Evaluate(::SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap, + const bool anUpward=false, + TSetOfInt* aShapesId=0); bool CheckAlgoState(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); // notify on bad state of attached algos, return false @@ -96,7 +118,7 @@ class SMESH_EXPORT SMESH_Gen */ void SetDefaultNbSegments(int nb) { _nbSegments = nb; } int GetDefaultNbSegments() const { return _nbSegments; } - + struct TAlgoStateError { TAlgoStateErrorName _name; @@ -125,16 +147,6 @@ class SMESH_EXPORT SMESH_Gen SMESH_Algo* GetAlgo(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, TopoDS_Shape* assignedTo=0); static bool IsGlobalHypothesis(const SMESH_Hypothesis* theHyp, SMESH_Mesh& aMesh); - // inherited methods from SALOMEDS::Driver - -// void Save(int studyId, const char *aUrlOfFile); -// void Load(int studyId, const char *aUrlOfFile); -// void Close(int studyId); -// const char *ComponentDataType(); - -// const char *IORToLocalPersistentID(const char *IORString, bool & IsAFile); -// const char *LocalPersistentIDToIOR(const char *aLocalPersistentID); - int GetANewId(); std::map < int, SMESH_Algo * >_mapAlgo; @@ -143,9 +155,9 @@ class SMESH_EXPORT SMESH_Gen std::map < int, SMESH_2D_Algo * >_map2D_Algo; std::map < int, SMESH_3D_Algo * >_map3D_Algo; - private: +private: - int _localId; // unique Id of created objects, within SMESH_Gen entity + int _localId; // unique Id of created objects, within SMESH_Gen entity std::map < int, StudyContextStruct * >_mapStudyContext; // hypotheses managing @@ -156,6 +168,12 @@ class SMESH_EXPORT SMESH_Gen int _segmentation; // default of segments int _nbSegments; + counters *_counters; + +#ifdef WITH_SMESH_CANCEL_COMPUTE + volatile bool _compute_canceled; + SMESH_subMesh* _sm_current; +#endif }; #endif diff --git a/src/SMESH/SMESH_Group.cxx b/src/SMESH/SMESH_Group.cxx index a05204d31..7ef568593 100644 --- a/src/SMESH/SMESH_Group.cxx +++ b/src/SMESH/SMESH_Group.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Group.cxx // Author : Michael Sazonov (OCC) @@ -29,6 +30,7 @@ #include "SMESH_Mesh.hxx" #include "SMESHDS_Group.hxx" #include "SMESHDS_GroupOnGeom.hxx" +#include "SMESHDS_GroupOnFilter.hxx" //============================================================================= /*! @@ -40,18 +42,36 @@ SMESH_Group::SMESH_Group (int theID, const SMESH_Mesh* theMesh, const SMDSAbs_ElementType theType, const char* theName, - const TopoDS_Shape& theShape) + const TopoDS_Shape& theShape, + const SMESH_PredicatePtr& thePredicate) : myName(theName) { - if ( theShape.IsNull() ) - myGroupDS = new SMESHDS_Group (theID, - const_cast(theMesh)->GetMeshDS(), - theType); - else + if ( !theShape.IsNull() ) myGroupDS = new SMESHDS_GroupOnGeom (theID, const_cast(theMesh)->GetMeshDS(), theType, theShape); + else if ( thePredicate ) + myGroupDS = new SMESHDS_GroupOnFilter (theID, + const_cast(theMesh)->GetMeshDS(), + theType, + thePredicate); + else + myGroupDS = new SMESHDS_Group (theID, + const_cast(theMesh)->GetMeshDS(), + theType); +} + +//================================================================================ +/*! + * \brief Constructor accesible to SMESH_Mesh only + */ +//================================================================================ + +SMESH_Group::SMESH_Group (SMESHDS_GroupBase* groupDS): myGroupDS( groupDS ) +{ + if ( myGroupDS ) + myName = myGroupDS->GetStoreName(); } //============================================================================= @@ -62,5 +82,5 @@ SMESH_Group::SMESH_Group (int theID, SMESH_Group::~SMESH_Group () { - delete myGroupDS; + delete myGroupDS; myGroupDS=0; } diff --git a/src/SMESH/SMESH_Group.hxx b/src/SMESH/SMESH_Group.hxx index ab3530827..f378be296 100644 --- a/src/SMESH/SMESH_Group.hxx +++ b/src/SMESH/SMESH_Group.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Group.hxx // Author : Michael Sazonov (OCC) // Module : SMESH -// $Header$ // #ifndef _SMESH_Group_HeaderFile #define _SMESH_Group_HeaderFile @@ -31,6 +31,7 @@ #include "SMESH_SMESH.hxx" #include "SMDSAbs_ElementType.hxx" +#include "SMESH_Controls.hxx" #include #include @@ -46,7 +47,9 @@ class SMESH_EXPORT SMESH_Group const SMESH_Mesh* theMesh, const SMDSAbs_ElementType theType, const char* theName, - const TopoDS_Shape& theShape = TopoDS_Shape()); + const TopoDS_Shape& theShape = TopoDS_Shape(), + const SMESH_PredicatePtr& thePredicate = SMESH_PredicatePtr()); + SMESH_Group (SMESHDS_GroupBase* groupDS); ~SMESH_Group (); void SetName (const char* theName) { myName = theName; } diff --git a/src/SMESH/SMESH_HypoFilter.cxx b/src/SMESH/SMESH_HypoFilter.cxx index d23f7cbe0..5552964bb 100644 --- a/src/SMESH/SMESH_HypoFilter.cxx +++ b/src/SMESH/SMESH_HypoFilter.cxx @@ -1,34 +1,38 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_HypoFilter.cxx // Module : SMESH -// $Header$ // #include "SMESH_HypoFilter.hxx" +#include "SMESH_Gen.hxx" #include "SMESH_Hypothesis.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" +#include + using namespace std; @@ -112,11 +116,39 @@ bool SMESH_HypoFilter::InstancePredicate::IsOk(const SMESH_Hypothesis* aHyp, //======================================================================= bool SMESH_HypoFilter::IsAssignedToPredicate::IsOk(const SMESH_Hypothesis* aHyp, - const TopoDS_Shape& aShape) const + const TopoDS_Shape& aShape) const { return ( !_mainShape.IsNull() && !aShape.IsNull() && _mainShape.IsSame( aShape )); } +//================================================================================ +/*! + * \brief Finds shapes preferable over _shape due to sub-mesh order + */ +//================================================================================ + +void SMESH_HypoFilter::IsMoreLocalThanPredicate::findPreferable() +{ + const int shapeID = _mesh.GetMeshDS()->ShapeToIndex( _shape ); + const TListOfListOfInt& listOfShapeIDList = _mesh.GetMeshOrder(); + TListOfListOfInt::const_iterator listsIt = listOfShapeIDList.begin(); + for ( ; listsIt != listOfShapeIDList.end(); ++listsIt ) + { + const TListOfInt& idList = *listsIt; + TListOfInt::const_iterator idIt = + std::find( idList.begin(), idList.end(), shapeID ); + if ( idIt != idList.end() && *idIt != idList.front() ) + { + for ( ; idIt != idList.end(); --idIt ) + { + const TopoDS_Shape& shape = _mesh.GetMeshDS()->IndexToShape( *idIt ); + if ( !shape.IsNull()) + _preferableShapes.Add( shape ); + } + } + } +} + //======================================================================= //function : IsMoreLocalThanPredicate::IsOk //purpose : @@ -125,7 +157,26 @@ bool SMESH_HypoFilter::IsAssignedToPredicate::IsOk(const SMESH_Hypothesis* aHyp, bool SMESH_HypoFilter::IsMoreLocalThanPredicate::IsOk(const SMESH_Hypothesis* aHyp, const TopoDS_Shape& aShape) const { - return ( aShape.ShapeType() > _shapeType ); + if ( aShape.IsSame( _mesh.GetShapeToMesh() )) + return false; // aHyp is global + + if ( SMESH_MesherHelper::IsSubShape( aShape, /*mainShape=*/_shape )) + return true; + + if ( aShape.ShapeType() == TopAbs_COMPOUND && + !SMESH_MesherHelper::IsSubShape( _shape, /*mainShape=*/aShape)) // issue 0020963 + { + for ( int type = TopAbs_SOLID; type < TopAbs_SHAPE; ++type ) + if ( aHyp->GetDim() == SMESH_Gen::GetShapeDim( TopAbs_ShapeEnum( type ))) + for ( TopExp_Explorer exp( aShape, TopAbs_ShapeEnum( type )); exp.More(); exp.Next()) + if ( SMESH_MesherHelper::IsSubShape( exp.Current(), /*mainShape=*/_shape )) + return true; + } + + if ( _preferableShapes.Contains( aShape )) + return true; // issue 21559, Mesh_6 + + return false; } //======================================================================= @@ -277,9 +328,10 @@ SMESH_HypoPredicate* SMESH_HypoFilter::IsApplicableTo(const TopoDS_Shape& theSha //purpose : //======================================================================= -SMESH_HypoPredicate* SMESH_HypoFilter::IsMoreLocalThan(const TopoDS_Shape& theShape) +SMESH_HypoPredicate* SMESH_HypoFilter::IsMoreLocalThan(const TopoDS_Shape& theShape, + const SMESH_Mesh& theMesh) { - return new IsMoreLocalThanPredicate( theShape ); + return new IsMoreLocalThanPredicate( theShape, theMesh ); } //======================================================================= @@ -346,3 +398,5 @@ SMESH_HypoFilter::~SMESH_HypoFilter() Init(0); } + + diff --git a/src/SMESH/SMESH_HypoFilter.hxx b/src/SMESH/SMESH_HypoFilter.hxx index c7aa1ed82..b4ace7d2a 100644 --- a/src/SMESH/SMESH_HypoFilter.hxx +++ b/src/SMESH/SMESH_HypoFilter.hxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_HypoFilter.hxx // Module : SMESH -// $Header$ // #ifndef SMESH_HypoFilter_HeaderFile #define SMESH_HypoFilter_HeaderFile @@ -36,9 +36,11 @@ #include #include #include +#include class SMESH_HypoFilter; class SMESH_Hypothesis; +class SMESH_Mesh; class SMESH_EXPORT SMESH_HypoPredicate { public: @@ -72,11 +74,14 @@ class SMESH_EXPORT SMESH_HypoFilter: public SMESH_HypoPredicate static SMESH_HypoPredicate* IsAssignedTo(const TopoDS_Shape& theShape); static SMESH_HypoPredicate* Is(const SMESH_Hypothesis* theHypo); static SMESH_HypoPredicate* IsGlobal(const TopoDS_Shape& theMainShape); - static SMESH_HypoPredicate* IsMoreLocalThan(const TopoDS_Shape& theShape); + static SMESH_HypoPredicate* IsMoreLocalThan(const TopoDS_Shape& theShape, + const SMESH_Mesh& theMesh); static SMESH_HypoPredicate* HasName(const std::string & theName); static SMESH_HypoPredicate* HasDim(const int theDim); static SMESH_HypoPredicate* HasType(const int theHypType); + bool IsEmpty() const { return myPredicates.empty(); } + /*! * \brief check aHyp or/and aShape it is assigned to */ @@ -167,10 +172,15 @@ class SMESH_EXPORT SMESH_HypoFilter: public SMESH_HypoPredicate }; struct IsMoreLocalThanPredicate : public SMESH_HypoPredicate { - TopAbs_ShapeEnum _shapeType; - IsMoreLocalThanPredicate( const TopoDS_Shape& shape ):_shapeType(shape.ShapeType()){} + TopoDS_Shape _shape; + const SMESH_Mesh& _mesh; + TopTools_MapOfShape _preferableShapes; + IsMoreLocalThanPredicate( const TopoDS_Shape& shape, + const SMESH_Mesh& mesh ) + :_shape(shape),_mesh(mesh) { findPreferable(); } bool IsOk(const SMESH_Hypothesis* aHyp, const TopoDS_Shape& aShape) const; + void findPreferable(); }; struct IsAuxiliaryPredicate : public SMESH_HypoPredicate { diff --git a/src/SMESH/SMESH_Hypothesis.cxx b/src/SMESH/SMESH_Hypothesis.cxx index 4b288a0d9..fee310f27 100644 --- a/src/SMESH/SMESH_Hypothesis.cxx +++ b/src/SMESH/SMESH_Hypothesis.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Hypothesis.cxx // Author : Paul RASCLE, EDF @@ -39,8 +40,8 @@ using namespace std; //============================================================================= SMESH_Hypothesis::SMESH_Hypothesis(int hypId, - int studyId, - SMESH_Gen* gen) : SMESHDS_Hypothesis(hypId) + int studyId, + SMESH_Gen* gen) : SMESHDS_Hypothesis(hypId) { //MESSAGE("SMESH_Hypothesis::SMESH_Hypothesis"); _gen = gen; @@ -51,6 +52,8 @@ SMESH_Hypothesis::SMESH_Hypothesis(int hypId, _shapeType = 0; // to be set by algo with TopAbs_Enum _param_algo_dim = -1; // to be set by algo parameter _parameters = string(); + _lastParameters = string(); + _libName = string(); } //============================================================================= @@ -151,6 +154,24 @@ void SMESH_Hypothesis::SetLibName(const char* theLibName) _libName = string(theLibName); } +//======================================================================= +//function : GetMeshByPersistentID +//purpose : Find a mesh with given persistent ID +//======================================================================= + +SMESH_Mesh* SMESH_Hypothesis::GetMeshByPersistentID(int id) +{ + StudyContextStruct* myStudyContext = _gen->GetStudyContext(_studyId); + map::iterator itm = itm = myStudyContext->mapMesh.begin(); + for ( ; itm != myStudyContext->mapMesh.end(); itm++) + { + SMESH_Mesh* mesh = (*itm).second; + if ( mesh->GetMeshDS()->GetPersistentId() == id ) + return mesh; + } + return 0; +} + //============================================================================= /*! * diff --git a/src/SMESH/SMESH_Hypothesis.hxx b/src/SMESH/SMESH_Hypothesis.hxx index 55ed9dd5f..0c81caac7 100644 --- a/src/SMESH/SMESH_Hypothesis.hxx +++ b/src/SMESH/SMESH_Hypothesis.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_Hypothesis.hxx // Author : Paul RASCLE, EDF // Module : SMESH // - #ifndef _SMESH_HYPOTHESIS_HXX_ #define _SMESH_HYPOTHESIS_HXX_ @@ -61,7 +61,7 @@ public: HYP_NOTCONFORM, // not conform mesh is produced appling a hypothesis HYP_ALREADY_EXIST,// such hypothesis already exist HYP_BAD_DIM, // bad dimension - HYP_BAD_SUBSHAPE, // shape is neither the main one, nor its subshape, nor a group + HYP_BAD_SUBSHAPE, // shape is neither the main one, nor its sub-shape, nor a group HYP_BAD_GEOMETRY, // shape geometry mismatches algorithm's expectation HYP_NEED_SHAPE // algorithm can work on shape only }; @@ -94,8 +94,9 @@ public: struct TDefaults { - double _elemLength; - int _nbSegments; + double _elemLength; + int _nbSegments; + TopoDS_Shape* _shape; // future shape of the mesh being created }; /*! * \brief Initialize my parameter values by default parameters. @@ -114,11 +115,16 @@ public: virtual bool IsAuxiliary() const { return GetType() == PARAM_ALGO && _param_algo_dim < 0; } + /*! + * \brief Find a mesh with given persistent ID + */ + SMESH_Mesh* GetMeshByPersistentID(int id); + protected: SMESH_Gen* _gen; int _studyId; int _shapeType; - int _param_algo_dim; + int _param_algo_dim; // to be set at descendant hypothesis constructor private: std::string _libName; diff --git a/src/SMESH/SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx b/src/SMESH/SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx deleted file mode 100644 index 7daa8b4bc..000000000 --- a/src/SMESH/SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File: SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx -// Created: 20.09.05 09:51:12 -// Author: Sergey KUUL -// -#ifndef SMESH_IndexedMapOfShape_HeaderFile -#define SMESH_IndexedMapOfShape_HeaderFile - -#include "SMESH_SMESH.hxx" - -#include "SMESHDS_DataMapOfShape.hxx" - -#include - -#include - -/// Class SMESH_IndexedMapOfShape - -DEFINE_BASECOLLECTION (SMESH_BaseCollectionShape, TopoDS_Shape) -DEFINE_INDEXEDMAP (SMESH_IndexedMapOfShape, SMESH_BaseCollectionShape, TopoDS_Shape) - -#endif - -#ifndef SMESH_IndexedDataMapOfShapeIndexedMapOfShape_HeaderFile -#define SMESH_IndexedDataMapOfShapeIndexedMapOfShape_HeaderFile - -#include - -/// Class SMESH_IndexedDataMapOfShapeIndexedMapOfShape - -DEFINE_BASECOLLECTION (SMESH_BaseCollectionIndexedMapOfShape, SMESH_IndexedMapOfShape) -DEFINE_INDEXEDDATAMAP (SMESH_IndexedDataMapOfShapeIndexedMapOfShape, - SMESH_BaseCollectionIndexedMapOfShape, TopoDS_Shape, - SMESH_IndexedMapOfShape) -#endif diff --git a/src/SMESH/SMESH_Mesh.cxx b/src/SMESH/SMESH_Mesh.cxx index 866fda739..1057d3dee 100644 --- a/src/SMESH/SMESH_Mesh.cxx +++ b/src/SMESH/SMESH_Mesh.cxx @@ -1,30 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : SMESH_Mesh.cxx // Author : Paul RASCLE, EDF // Module : SMESH // #include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Hypothesis.hxx" @@ -47,6 +48,10 @@ #include "DriverMED_R_SMESHDS_Mesh.h" #include "DriverUNV_R_SMDS_Mesh.h" #include "DriverSTL_R_SMDS_Mesh.h" +#ifdef WITH_CGNS +#include "DriverCGNS_Read.hxx" +#include "DriverCGNS_Write.hxx" +#endif #undef _Precision_HeaderFile #include @@ -61,6 +66,11 @@ #include "Utils_ExceptHandlers.hxx" +#include +#include + +using namespace std; + // maximum stored group name length in MED file #define MAX_MED_GROUP_NAME_LENGTH 80 @@ -81,11 +91,11 @@ typedef SMESH_HypoFilter THypType; //============================================================================= SMESH_Mesh::SMESH_Mesh(int theLocalId, - int theStudyId, - SMESH_Gen* theGen, - bool theIsEmbeddedMode, - SMESHDS_Document* theDocument): - _groupId( 0 ) + int theStudyId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument): + _groupId( 0 ), _nbSubShapes( 0 ) { MESSAGE("SMESH_Mesh::SMESH_Mesh(int localId)"); _id = theLocalId; @@ -96,10 +106,44 @@ SMESH_Mesh::SMESH_Mesh(int theLocalId, _myMeshDS = theDocument->GetMesh(_idDoc); _isShapeToMesh = false; _isAutoColor = false; + _isModified = false; _shapeDiagonal = 0.0; + _callUp = 0; _myMeshDS->ShapeToMesh( PseudoShape() ); } +//================================================================================ +/*! + * \brief Constructor of SMESH_Mesh being a base of some descendant class + */ +//================================================================================ + +SMESH_Mesh::SMESH_Mesh(): + _id(-1), + _studyId(-1), + _idDoc(-1), + _groupId( 0 ), + _nbSubShapes( 0 ), + _isShapeToMesh( false ), + _myDocument( 0 ), + _myMeshDS( 0 ), + _gen( 0 ), + _isAutoColor( false ), + _isModified( false ), + _shapeDiagonal( 0.0 ), + _callUp( 0 ) +{ +} + +namespace +{ + void deleteMeshDS(SMESHDS_Mesh* meshDS) + { + //cout << "deleteMeshDS( " << meshDS << endl; + delete meshDS; + } +} + //============================================================================= /*! * @@ -108,15 +152,57 @@ SMESH_Mesh::SMESH_Mesh(int theLocalId, SMESH_Mesh::~SMESH_Mesh() { - INFOS("SMESH_Mesh::~SMESH_Mesh"); + MESSAGE("SMESH_Mesh::~SMESH_Mesh"); + + // issue 0020340: EDF 1022 SMESH : Crash with FindNodeClosestTo in a second new study + // Notify event listeners at least that something happens + if ( SMESH_subMesh * sm = GetSubMeshContaining(1)) + sm->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED ); // delete groups - std::map < int, SMESH_Group * >::iterator itg; + map < int, SMESH_Group * >::iterator itg; for (itg = _mapGroup.begin(); itg != _mapGroup.end(); itg++) { SMESH_Group *aGroup = (*itg).second; delete aGroup; } _mapGroup.clear(); + + // delete sub-meshes + map ::iterator sm = _mapSubMesh.begin(); + for ( ; sm != _mapSubMesh.end(); ++sm ) + { + delete sm->second; + sm->second = 0; + } + _mapSubMesh.clear(); + + if ( _callUp) delete _callUp; + _callUp = 0; + + // remove self from studyContext + if ( _gen ) + { + StudyContextStruct * studyContext = _gen->GetStudyContext( _studyId ); + studyContext->mapMesh.erase( _id ); + } + if ( _myDocument ) + _myDocument->RemoveMesh( _id ); + _myDocument = 0; + + if ( _myMeshDS ) + // delete _myMeshDS, in a thread in order not to block closing a study with large meshes + boost::thread aThread(boost::bind( & deleteMeshDS, _myMeshDS )); +} + +//================================================================================ +/*! + * \brief Return true if a mesh with given id exists + */ +//================================================================================ + +bool SMESH_Mesh::MeshExists( int meshId ) const +{ + return _myDocument ? _myDocument->GetMesh( meshId ) : false; } //============================================================================= @@ -129,20 +215,22 @@ void SMESH_Mesh::ShapeToMesh(const TopoDS_Shape & aShape) { if(MYDEBUG) MESSAGE("SMESH_Mesh::ShapeToMesh"); - if ( !aShape.IsNull() && _isShapeToMesh ) - throw SALOME_Exception(LOCALIZED ("a shape to mesh has already been defined")); - + if ( !aShape.IsNull() && _isShapeToMesh ) { + if ( aShape.ShapeType() != TopAbs_COMPOUND && // group contents is allowed to change + _myMeshDS->ShapeToMesh().ShapeType() != TopAbs_COMPOUND ) + throw SALOME_Exception(LOCALIZED ("a shape to mesh has already been defined")); + } // clear current data if ( !_myMeshDS->ShapeToMesh().IsNull() ) { // removal of a shape to mesh, delete objects referring to sub-shapes: // - sub-meshes - std::map ::iterator i_sm = _mapSubMesh.begin(); + map ::iterator i_sm = _mapSubMesh.begin(); for ( ; i_sm != _mapSubMesh.end(); ++i_sm ) delete i_sm->second; _mapSubMesh.clear(); // - groups on geometry - std::map ::iterator i_gr = _mapGroup.begin(); + map ::iterator i_gr = _mapGroup.begin(); while ( i_gr != _mapGroup.end() ) { if ( dynamic_cast( i_gr->second->GetGroupDS() )) { _myMeshDS->RemoveGroup( i_gr->second->GetGroupDS() ); @@ -166,16 +254,18 @@ void SMESH_Mesh::ShapeToMesh(const TopoDS_Shape & aShape) { _myMeshDS->ShapeToMesh(aShape); _isShapeToMesh = true; + _nbSubShapes = _myMeshDS->MaxShapeIndex(); - // fill _mapAncestors - int desType, ancType; - for ( desType = TopAbs_VERTEX; desType > TopAbs_COMPOUND; desType-- ) - for ( ancType = desType - 1; ancType >= TopAbs_COMPOUND; ancType-- ) - TopExp::MapShapesAndAncestors ( aShape, - (TopAbs_ShapeEnum) desType, - (TopAbs_ShapeEnum) ancType, - _mapAncestors ); + // fill map of ancestors + fillAncestorsMap(aShape); } + else + { + _isShapeToMesh = false; + _shapeDiagonal = 0.0; + _myMeshDS->ShapeToMesh( PseudoShape() ); + } + _isModified = false; } //======================================================================= @@ -236,6 +326,18 @@ double SMESH_Mesh::GetShapeDiagonalSize() const return _shapeDiagonal; } +//================================================================================ +/*! + * \brief Load mesh from study file + */ +//================================================================================ + +void SMESH_Mesh::Load() +{ + if (_callUp) + _callUp->Load(); +} + //======================================================================= /*! * \brief Remove all nodes and elements @@ -248,55 +350,14 @@ void SMESH_Mesh::Clear() _myMeshDS->ClearMesh(); // update compute state of submeshes - if ( SMESH_subMesh *sm = GetSubMeshContaining( GetShapeToMesh() ) ) { - SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true, - /*complexShapeFirst=*/false); - while ( smIt->more() ) { - sm = smIt->next(); - sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); - } + if ( SMESH_subMesh *sm = GetSubMeshContaining( GetShapeToMesh() ) ) + { + sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); + sm->ComputeSubMeshStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); + sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); // for event listeners (issue 0020918) + sm->ComputeSubMeshStateEngine( SMESH_subMesh::CLEAN ); } - -// // clear sub-meshes; get ready to re-compute as a side-effect - -// if ( SMESH_subMesh *sm = GetSubMeshContaining( GetShapeToMesh() ) ) -// { -// SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true, -// /*complexShapeFirst=*/false); -// while ( smIt->more() ) -// { -// sm = smIt->next(); -// TopAbs_ShapeEnum shapeType = sm->GetSubShape().ShapeType(); -// if ( shapeType == TopAbs_VERTEX || shapeType < TopAbs_SOLID ) -// // all other shapes depends on vertices so they are already cleaned -// sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); -// // to recompute even if failed -// sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); -// } -// } - -// // clear entities not on sub-meshes - -// SMDS_VolumeIteratorPtr vIt = _myMeshDS->volumesIterator(); -// while ( vIt->more() ) -// _myMeshDS->RemoveFreeElement( vIt->next(), 0 ); - -// SMDS_FaceIteratorPtr fIt = _myMeshDS->facesIterator(); -// while ( fIt->more() ) -// _myMeshDS->RemoveFreeElement( fIt->next(), 0 ); - -// SMDS_EdgeIteratorPtr eIt = _myMeshDS->edgesIterator(); -// while ( eIt->more() ) -// _myMeshDS->RemoveFreeElement( eIt->next(), 0 ); - -// SMDS_NodeIteratorPtr nIt = _myMeshDS->nodesIterator(); -// while ( nIt->more() ) { -// const SMDS_MeshNode * node = nIt->next(); -// if ( node->NbInverseElements() == 0 ) -// _myMeshDS->RemoveFreeNode( node, 0 ); -// else -// _myMeshDS->RemoveNode(node); -// } + _isModified = false; } //======================================================================= @@ -311,14 +372,14 @@ void SMESH_Mesh::ClearSubMesh(const int theShapeId) if ( SMESH_subMesh *sm = GetSubMeshContaining( theShapeId ) ) { SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true, - /*complexShapeFirst=*/false); + /*complexShapeFirst=*/false); while ( smIt->more() ) { sm = smIt->next(); TopAbs_ShapeEnum shapeType = sm->GetSubShape().ShapeType(); if ( shapeType == TopAbs_VERTEX || shapeType < TopAbs_SOLID ) - // all other shapes depends on vertices so they are already cleaned - sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); + // all other shapes depends on vertices so they are already cleaned + sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); // to recompute even if failed sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); } @@ -354,26 +415,26 @@ int SMESH_Mesh::UNVToMesh(const char* theFileName) aGroup->InitSubGroupsIterator(); while (aGroup->MoreSubGroups()) { SMDS_MeshGroup* aSubGroup = (SMDS_MeshGroup*) aGroup->NextSubGroup(); - std::string aName = aGroupNames[aSubGroup]; + string aName = aGroupNames[aSubGroup]; int aId; SMESH_Group* aSMESHGroup = AddGroup( aSubGroup->GetType(), aName.c_str(), aId ); if ( aSMESHGroup ) { - if(MYDEBUG) MESSAGE("UNVToMesh - group added: "<( aSMESHGroup->GetGroupDS() ); - if ( aGroupDS ) { - aGroupDS->SetStoreName(aName.c_str()); - aSubGroup->InitIterator(); - const SMDS_MeshElement* aElement = 0; - while (aSubGroup->More()) { - aElement = aSubGroup->Next(); - if (aElement) { - aGroupDS->SMDSGroup().Add(aElement); - } - } - if (aElement) - aGroupDS->SetType(aElement->GetType()); - } + if(MYDEBUG) MESSAGE("UNVToMesh - group added: "<( aSMESHGroup->GetGroupDS() ); + if ( aGroupDS ) { + aGroupDS->SetStoreName(aName.c_str()); + aSubGroup->InitIterator(); + const SMDS_MeshElement* aElement = 0; + while (aSubGroup->More()) { + aElement = aSubGroup->Next(); + if (aElement) { + aGroupDS->SMDSGroup().Add(aElement); + } + } + if (aElement) + aGroupDS->SetType(aElement->GetType()); + } } } } @@ -405,10 +466,10 @@ int SMESH_Mesh::MEDToMesh(const char* theFileName, const char* theMeshName) } // Reading groups (sub-meshes are out of scope of MED import functionality) - std::list aGroupNames = myReader.GetGroupNamesAndTypes(); + list aGroupNames = myReader.GetGroupNamesAndTypes(); if(MYDEBUG) MESSAGE("MEDToMesh - Nb groups = "<::iterator name_type = aGroupNames.begin(); + list::iterator name_type = aGroupNames.begin(); for ( ; name_type != aGroupNames.end(); name_type++ ) { SMESH_Group* aGroup = AddGroup( name_type->second, name_type->first.c_str(), anId ); if ( aGroup ) { @@ -448,6 +509,35 @@ int SMESH_Mesh::STLToMesh(const char* theFileName) return 1; } +//================================================================================ +/*! + * \brief Reads the given mesh from the CGNS file + * \param theFileName - name of the file + * \retval int - Driver_Mesh::Status + */ +//================================================================================ + +int SMESH_Mesh::CGNSToMesh(const char* theFileName, + const int theMeshIndex, + std::string& theMeshName) +{ + int res = Driver_Mesh::DRS_FAIL; +#ifdef WITH_CGNS + + DriverCGNS_Read myReader; + myReader.SetMesh(_myMeshDS); + myReader.SetFile(theFileName); + myReader.SetMeshId(theMeshIndex); + res = myReader.Perform(); + theMeshName = myReader.GetMeshName(); + + // create groups + SynchronizeGroups(); + +#endif + return res; +} + //============================================================================= /*! * @@ -465,33 +555,6 @@ SMESH_Hypothesis::Hypothesis_Status if ( !subMesh || !subMesh->GetId()) return SMESH_Hypothesis::HYP_BAD_SUBSHAPE; - SMESHDS_SubMesh *subMeshDS = subMesh->GetSubMeshDS(); - if ( subMeshDS && subMeshDS->IsComplexSubmesh() ) // group of sub-shapes and maybe of not sub- - { - MESSAGE("AddHypothesis() to complex submesh"); - // return the worst but not fatal state of all group memebers - SMESH_Hypothesis::Hypothesis_Status aBestRet, aWorstNotFatal, ret; - aBestRet = SMESH_Hypothesis::HYP_BAD_DIM; - aWorstNotFatal = SMESH_Hypothesis::HYP_OK; - for ( TopoDS_Iterator itS ( aSubShape ); itS.More(); itS.Next()) - { - if ( !GetMeshDS()->ShapeToIndex( itS.Value() )) - continue; // not sub-shape - ret = AddHypothesis( itS.Value(), anHypId ); - if ( !SMESH_Hypothesis::IsStatusFatal( ret ) && ret > aWorstNotFatal ) - aWorstNotFatal = ret; - if ( ret < aBestRet ) - aBestRet = ret; - } - // bind hypotheses to a group just to know - SMESH_Hypothesis *anHyp = _gen->GetStudyContext(_studyId)->mapHypothesis[anHypId]; - GetMeshDS()->AddHypothesis( aSubShape, anHyp ); - - if ( SMESH_Hypothesis::IsStatusFatal( aBestRet )) - return aBestRet; - return aWorstNotFatal; - } - StudyContextStruct *sc = _gen->GetStudyContext(_studyId); if (sc->mapHypothesis.find(anHypId) == sc->mapHypothesis.end()) { @@ -511,6 +574,9 @@ SMESH_Hypothesis::Hypothesis_Status // NotConformAllowed can be only global if ( !isGlobalHyp ) { + // NOTE: this is not a correct way to check a name of hypothesis, + // there should be an attribute of hypothesis saying that it can/can't + // be global/local string hypName = anHyp->GetName(); if ( hypName == "NotConformAllowed" ) { @@ -526,7 +592,7 @@ SMESH_Hypothesis::Hypothesis_Status SMESH_Hypothesis::Hypothesis_Status ret = subMesh->AlgoStateEngine(event, anHyp); - // subShapes + // sub-shapes if (!SMESH_Hypothesis::IsStatusFatal(ret) && anHyp->GetDim() <= SMESH_Gen::GetShapeDim(aSubShape)) // is added on father { @@ -537,7 +603,7 @@ SMESH_Hypothesis::Hypothesis_Status if (ret2 > ret) ret = ret2; - // check concurent hypotheses on ansestors + // check concurent hypotheses on ancestors if (ret < SMESH_Hypothesis::HYP_CONCURENT && !isGlobalHyp ) { SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false,false); @@ -553,9 +619,12 @@ SMESH_Hypothesis::Hypothesis_Status } } } + HasModificationsToDiscard(); // to reset _isModified flag if a mesh becomes empty + + GetMeshDS()->Modified(); if(MYDEBUG) subMesh->DumpAlgoState(true); - SCRUTE(ret); + if(MYDEBUG) SCRUTE(ret); return ret; } @@ -572,45 +641,23 @@ SMESH_Hypothesis::Hypothesis_Status Unexpect aCatch(SalomeException); if(MYDEBUG) MESSAGE("SMESH_Mesh::RemoveHypothesis"); - SMESH_subMesh *subMesh = GetSubMesh(aSubShape); - SMESHDS_SubMesh *subMeshDS = subMesh->GetSubMeshDS(); - if ( subMeshDS && subMeshDS->IsComplexSubmesh() ) - { - // return the worst but not fatal state of all group memebers - SMESH_Hypothesis::Hypothesis_Status aBestRet, aWorstNotFatal, ret; - aBestRet = SMESH_Hypothesis::HYP_BAD_DIM; - aWorstNotFatal = SMESH_Hypothesis::HYP_OK; - for ( TopoDS_Iterator itS ( aSubShape ); itS.More(); itS.Next()) - { - if ( !GetMeshDS()->ShapeToIndex( itS.Value() )) - continue; // not sub-shape - ret = RemoveHypothesis( itS.Value(), anHypId ); - if ( !SMESH_Hypothesis::IsStatusFatal( ret ) && ret > aWorstNotFatal ) - aWorstNotFatal = ret; - if ( ret < aBestRet ) - aBestRet = ret; - } - SMESH_Hypothesis *anHyp = _gen->GetStudyContext(_studyId)->mapHypothesis[anHypId]; - GetMeshDS()->RemoveHypothesis( aSubShape, anHyp ); - - if ( SMESH_Hypothesis::IsStatusFatal( aBestRet )) - return aBestRet; - return aWorstNotFatal; - } - StudyContextStruct *sc = _gen->GetStudyContext(_studyId); if (sc->mapHypothesis.find(anHypId) == sc->mapHypothesis.end()) throw SALOME_Exception(LOCALIZED("hypothesis does not exist")); SMESH_Hypothesis *anHyp = sc->mapHypothesis[anHypId]; - int hypType = anHyp->GetType(); - if(MYDEBUG) SCRUTE(hypType); + if(MYDEBUG) { + int hypType = anHyp->GetType(); + SCRUTE(hypType); + } // shape bool isAlgo = ( !anHyp->GetType() == SMESHDS_Hypothesis::PARAM_ALGO ); int event = isAlgo ? SMESH_subMesh::REMOVE_ALGO : SMESH_subMesh::REMOVE_HYP; + SMESH_subMesh *subMesh = GetSubMesh(aSubShape); + SMESH_Hypothesis::Hypothesis_Status ret = subMesh->AlgoStateEngine(event, anHyp); // there may appear concurrent hyps that were covered by the removed hyp @@ -619,7 +666,7 @@ SMESH_Hypothesis::Hypothesis_Status subMesh->CheckConcurentHypothesis( anHyp->GetType() ) != SMESH_Hypothesis::HYP_OK) ret = SMESH_Hypothesis::HYP_CONCURENT; - // subShapes + // sub-shapes if (!SMESH_Hypothesis::IsStatusFatal(ret) && anHyp->GetDim() <= SMESH_Gen::GetShapeDim(aSubShape)) // is removed from father { @@ -630,7 +677,7 @@ SMESH_Hypothesis::Hypothesis_Status if (ret2 > ret) // more severe ret = ret2; - // check concurent hypotheses on ansestors + // check concurent hypotheses on ancestors if (ret < SMESH_Hypothesis::HYP_CONCURENT && !IsMainShape( aSubShape ) ) { SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false,false); @@ -646,7 +693,11 @@ SMESH_Hypothesis::Hypothesis_Status } } } - + + HasModificationsToDiscard(); // to reset _isModified flag if mesh become empty + + GetMeshDS()->Modified(); + if(MYDEBUG) subMesh->DumpAlgoState(true); if(MYDEBUG) SCRUTE(ret); return ret; @@ -658,7 +709,7 @@ SMESH_Hypothesis::Hypothesis_Status */ //============================================================================= -const std::list& +const list& SMESH_Mesh::GetHypothesisList(const TopoDS_Shape & aSubShape) const throw(SALOME_Exception) { @@ -683,8 +734,8 @@ const SMESH_Hypothesis * SMESH_Mesh::GetHypothesis(const TopoDS_Shape & aSubS TopoDS_Shape* assignedTo) const { { - const std::list& hypList = _myMeshDS->GetHypothesis(aSubShape); - std::list::const_iterator hyp = hypList.begin(); + const list& hypList = _myMeshDS->GetHypothesis(aSubShape); + list::const_iterator hyp = hypList.begin(); for ( ; hyp != hypList.end(); hyp++ ) { const SMESH_Hypothesis * h = cSMESH_Hyp( *hyp ); if ( aFilter.IsOk( h, aSubShape)) { @@ -695,15 +746,18 @@ const SMESH_Hypothesis * SMESH_Mesh::GetHypothesis(const TopoDS_Shape & aSubS } if ( andAncestors ) { - TopTools_ListIteratorOfListOfShape it( GetAncestors( aSubShape )); - for (; it.More(); it.Next() ) + // user sorted submeshes of ancestors, according to stored submesh priority + const list smList = getAncestorsSubMeshes( aSubShape ); + list::const_iterator smIt = smList.begin(); + for ( ; smIt != smList.end(); smIt++ ) { - const std::list& hypList = _myMeshDS->GetHypothesis(it.Value()); - std::list::const_iterator hyp = hypList.begin(); + const TopoDS_Shape& curSh = (*smIt)->GetSubShape(); + const list& hypList = _myMeshDS->GetHypothesis(curSh); + list::const_iterator hyp = hypList.begin(); for ( ; hyp != hypList.end(); hyp++ ) { const SMESH_Hypothesis * h = cSMESH_Hyp( *hyp ); - if (aFilter.IsOk( h, it.Value() )) { - if ( assignedTo ) *assignedTo = it.Value(); + if (aFilter.IsOk( h, curSh )) { + if ( assignedTo ) *assignedTo = curSh; return h; } } @@ -735,7 +789,7 @@ int SMESH_Mesh::GetHypotheses(const TopoDS_Shape & aSubShape, bool mainHypFound = false; // fill in hypTypes - std::list::const_iterator hyp; + list::const_iterator hyp; for ( hyp = aHypList.begin(); hyp != aHypList.end(); hyp++ ) { if ( hypTypes.insert( (*hyp)->GetName() ).second ) nbHyps++; @@ -745,7 +799,7 @@ int SMESH_Mesh::GetHypotheses(const TopoDS_Shape & aSubShape, // get hypos from aSubShape { - const std::list& hypList = _myMeshDS->GetHypothesis(aSubShape); + const list& hypList = _myMeshDS->GetHypothesis(aSubShape); for ( hyp = hypList.begin(); hyp != hypList.end(); hyp++ ) if ( aFilter.IsOk (cSMESH_Hyp( *hyp ), aSubShape) && ( cSMESH_Hyp(*hyp)->IsAuxiliary() || !mainHypFound ) && @@ -762,14 +816,18 @@ int SMESH_Mesh::GetHypotheses(const TopoDS_Shape & aSubShape, if ( andAncestors ) { TopTools_MapOfShape map; - TopTools_ListIteratorOfListOfShape it( GetAncestors( aSubShape )); - for (; it.More(); it.Next() ) + + // user sorted submeshes of ancestors, according to stored submesh priority + const list smList = getAncestorsSubMeshes( aSubShape ); + list::const_iterator smIt = smList.begin(); + for ( ; smIt != smList.end(); smIt++ ) { - if ( !map.Add( it.Value() )) + const TopoDS_Shape& curSh = (*smIt)->GetSubShape(); + if ( !map.Add( curSh )) continue; - const std::list& hypList = _myMeshDS->GetHypothesis(it.Value()); + const list& hypList = _myMeshDS->GetHypothesis(curSh); for ( hyp = hypList.begin(); hyp != hypList.end(); hyp++ ) - if (aFilter.IsOk( cSMESH_Hyp( *hyp ), it.Value() ) && + if (aFilter.IsOk( cSMESH_Hyp( *hyp ), curSh ) && ( cSMESH_Hyp(*hyp)->IsAuxiliary() || !mainHypFound ) && hypTypes.insert( (*hyp)->GetName() ).second ) { @@ -789,7 +847,7 @@ int SMESH_Mesh::GetHypotheses(const TopoDS_Shape & aSubShape, */ //============================================================================= -const std::list & SMESH_Mesh::GetLog() throw(SALOME_Exception) +const list & SMESH_Mesh::GetLog() throw(SALOME_Exception) { Unexpect aCatch(SalomeException); if(MYDEBUG) MESSAGE("SMESH_Mesh::GetLog"); @@ -822,10 +880,16 @@ SMESH_subMesh *SMESH_Mesh::GetSubMesh(const TopoDS_Shape & aSubShape) int index = _myMeshDS->ShapeToIndex(aSubShape); // for submeshes on GEOM Group - if ( !index && aSubShape.ShapeType() == TopAbs_COMPOUND ) { + if (( !index || index > _nbSubShapes ) && aSubShape.ShapeType() == TopAbs_COMPOUND ) { TopoDS_Iterator it( aSubShape ); if ( it.More() ) + { index = _myMeshDS->AddCompoundSubmesh( aSubShape, it.Value().ShapeType() ); + if ( index > _nbSubShapes ) _nbSubShapes = index; // not to create sm for this group again + + // fill map of Ancestors + fillAncestorsMap(aSubShape); + } } // if ( !index ) // return NULL; // neither sub-shape nor a group @@ -883,37 +947,47 @@ throw(SALOME_Exception) } //================================================================================ /*! - * \brief Return submeshes of groups containing the given subshape + * \brief Return submeshes of groups containing the given sub-shape */ //================================================================================ -std::list +list SMESH_Mesh::GetGroupSubMeshesContaining(const TopoDS_Shape & aSubShape) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); - std::list found; + list found; SMESH_subMesh * subMesh = GetSubMeshContaining(aSubShape); if ( !subMesh ) return found; // submeshes of groups have max IDs, so search from the map end - std::map::const_reverse_iterator i_sm; + map::const_reverse_iterator i_sm; for ( i_sm = _mapSubMesh.rbegin(); i_sm != _mapSubMesh.rend(); ++i_sm) { SMESHDS_SubMesh * ds = i_sm->second->GetSubMeshDS(); if ( ds && ds->IsComplexSubmesh() ) { - TopExp_Explorer exp( i_sm->second->GetSubShape(), aSubShape.ShapeType() ); - for ( ; exp.More(); exp.Next() ) { - if ( aSubShape.IsSame( exp.Current() )) { - found.push_back( i_sm->second ); - break; - } + if ( SMESH_MesherHelper::IsSubShape( aSubShape, i_sm->second->GetSubShape() )) + { + found.push_back( i_sm->second ); + //break; } } else { - break; + break; // the rest sub-meshes are not those of groups } } + + if ( found.empty() ) // maybe the main shape is a COMPOUND (issue 0021530) + { + if ( SMESH_subMesh * mainSM = GetSubMeshContaining(1)) + if ( mainSM->GetSubShape().ShapeType() == TopAbs_COMPOUND ) + { + TopoDS_Iterator it( mainSM->GetSubShape() ); + if ( it.Value().ShapeType() == aSubShape.ShapeType() && + SMESH_MesherHelper::IsSubShape( aSubShape, mainSM->GetSubShape() )) + found.push_back( mainSM ); + } + } return found; } //======================================================================= @@ -987,6 +1061,12 @@ void SMESH_Mesh::NotifySubMeshesHypothesisModification(const SMESH_Hypothesis* h { Unexpect aCatch(SalomeException); + if ( !GetMeshDS()->IsUsedHypothesis( hyp )) + return; + + if (_callUp) + _callUp->HypothesisModified(); + const SMESH_Algo *foundAlgo = 0; SMESH_HypoFilter algoKind, compatibleHypoKind; list usedHyps; @@ -1029,6 +1109,8 @@ void SMESH_Mesh::NotifySubMeshesHypothesisModification(const SMESH_Hypothesis* h } } } + HasModificationsToDiscard(); // to reset _isModified flag if mesh becomes empty + GetMeshDS()->Modified(); } //============================================================================= @@ -1048,37 +1130,98 @@ bool SMESH_Mesh::GetAutoColor() throw(SALOME_Exception) return _isAutoColor; } -//============================================================================= -/*! Export* methods. - * To store mesh contents on disk in different formats. +//======================================================================= +//function : SetIsModified +//purpose : Set the flag meaning that the mesh has been edited "manually" +//======================================================================= + +void SMESH_Mesh::SetIsModified(bool isModified) +{ + _isModified = isModified; + + if ( _isModified ) + // check if mesh becomes empty as result of modification + HasModificationsToDiscard(); +} + +//======================================================================= +//function : HasModificationsToDiscard +//purpose : Return true if the mesh has been edited since a total re-compute +// and those modifications may prevent successful partial re-compute. +// As a side effect reset _isModified flag if mesh is empty +//issue : 0020693 +//======================================================================= + +bool SMESH_Mesh::HasModificationsToDiscard() const +{ + if ( ! _isModified ) + return false; + + // return true if the next Compute() will be partial and + // existing but changed elements may prevent successful re-compute + bool hasComputed = false, hasNotComputed = false; + map ::const_iterator i_sm = _mapSubMesh.begin(); + for ( ; i_sm != _mapSubMesh.end() ; ++i_sm ) + switch ( i_sm->second->GetSubShape().ShapeType() ) + { + case TopAbs_EDGE: + case TopAbs_FACE: + case TopAbs_SOLID: + if ( i_sm->second->IsMeshComputed() ) + hasComputed = true; + else + hasNotComputed = true; + if ( hasComputed && hasNotComputed) + return true; + } + + if ( NbNodes() < 1 ) + const_cast(this)->_isModified = false; + + return false; +} + +//================================================================================ +/*! + * \brief Check if any groups of the same type have equal names */ -//============================================================================= +//================================================================================ bool SMESH_Mesh::HasDuplicatedGroupNamesMED() { - set aGroupNames; - for ( std::map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) { + //set aGroupNames; // Corrected for Mantis issue 0020028 + map< SMDSAbs_ElementType, set > aGroupNames; + for ( map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) + { SMESH_Group* aGroup = it->second; - std::string aGroupName = aGroup->GetName(); + SMDSAbs_ElementType aType = aGroup->GetGroupDS()->GetType(); + string aGroupName = aGroup->GetName(); aGroupName.resize(MAX_MED_GROUP_NAME_LENGTH); - if (!aGroupNames.insert(aGroupName).second) + if (!aGroupNames[aType].insert(aGroupName).second) return true; } return false; } -void SMESH_Mesh::ExportMED(const char *file, - const char* theMeshName, - bool theAutoGroups, - int theVersion) +//================================================================================ +/*! + * \brief Export the mesh to a med file + */ +//================================================================================ + +void SMESH_Mesh::ExportMED(const char * file, + const char* theMeshName, + bool theAutoGroups, + int theVersion, + const SMESHDS_Mesh* meshPart) throw(SALOME_Exception) { Unexpect aCatch(SalomeException); DriverMED_W_SMESHDS_Mesh myWriter; myWriter.SetFile ( file, MED::EVersion(theVersion) ); - myWriter.SetMesh ( _myMeshDS ); + myWriter.SetMesh ( meshPart ? (SMESHDS_Mesh*) meshPart : _myMeshDS ); if ( !theMeshName ) myWriter.SetMeshId ( _idDoc ); else { @@ -1094,79 +1237,167 @@ void SMESH_Mesh::ExportMED(const char *file, } // Pass groups to writer. Provide unique group names. - set aGroupNames; - char aString [256]; - int maxNbIter = 10000; // to guarantee cycle finish - for ( std::map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) { - SMESH_Group* aGroup = it->second; - SMESHDS_GroupBase* aGroupDS = aGroup->GetGroupDS(); - if ( aGroupDS ) { - string aGroupName0 = aGroup->GetName(); - aGroupName0.resize(MAX_MED_GROUP_NAME_LENGTH); - string aGroupName = aGroupName0; - for (int i = 1; !aGroupNames.insert(aGroupName).second && i < maxNbIter; i++) { - sprintf(&aString[0], "GR_%d_%s", i, aGroupName0.c_str()); - aGroupName = aString; - aGroupName.resize(MAX_MED_GROUP_NAME_LENGTH); + //set aGroupNames; // Corrected for Mantis issue 0020028 + if ( !meshPart ) + { + map< SMDSAbs_ElementType, set > aGroupNames; + char aString [256]; + int maxNbIter = 10000; // to guarantee cycle finish + for ( map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) { + SMESH_Group* aGroup = it->second; + SMESHDS_GroupBase* aGroupDS = aGroup->GetGroupDS(); + if ( aGroupDS ) { + SMDSAbs_ElementType aType = aGroupDS->GetType(); + string aGroupName0 = aGroup->GetName(); + aGroupName0.resize(MAX_MED_GROUP_NAME_LENGTH); + string aGroupName = aGroupName0; + for (int i = 1; !aGroupNames[aType].insert(aGroupName).second && i < maxNbIter; i++) { + sprintf(&aString[0], "GR_%d_%s", i, aGroupName0.c_str()); + aGroupName = aString; + aGroupName.resize(MAX_MED_GROUP_NAME_LENGTH); + } + aGroupDS->SetStoreName( aGroupName.c_str() ); + myWriter.AddGroup( aGroupDS ); } - aGroupDS->SetStoreName( aGroupName.c_str() ); - myWriter.AddGroup( aGroupDS ); } } - // Perform export myWriter.Perform(); } -void SMESH_Mesh::ExportDAT(const char *file) throw(SALOME_Exception) +void SMESH_Mesh::ExportSAUV(const char *file, + const char* theMeshName, + bool theAutoGroups) + throw(SALOME_Exception) +{ + std::string medfilename(file); + medfilename += ".med"; + std::string cmd; +#ifdef WNT + cmd = "%PYTHONBIN% "; +#else + cmd = "python "; +#endif + cmd += "-c \""; + cmd += "from medutilities import my_remove ; my_remove(r'" + medfilename + "')"; + cmd += "\""; + system(cmd.c_str()); + ExportMED(medfilename.c_str(), theMeshName, theAutoGroups, 1); +#ifdef WNT + cmd = "%PYTHONBIN% "; +#else + cmd = "python "; +#endif + cmd += "-c \""; + cmd += "from medutilities import convert ; convert(r'" + medfilename + "', 'MED', 'GIBI', 1, r'" + file + "')"; + cmd += "\""; + system(cmd.c_str()); +#ifdef WNT + cmd = "%PYTHONBIN% "; +#else + cmd = "python "; +#endif + cmd += "-c \""; + cmd += "from medutilities import my_remove ; my_remove(r'" + medfilename + "')"; + cmd += "\""; + system(cmd.c_str()); +} + +//================================================================================ +/*! + * \brief Export the mesh to a DAT file + */ +//================================================================================ + +void SMESH_Mesh::ExportDAT(const char * file, + const SMESHDS_Mesh* meshPart) throw(SALOME_Exception) { Unexpect aCatch(SalomeException); DriverDAT_W_SMDS_Mesh myWriter; - myWriter.SetFile(string(file)); - myWriter.SetMesh(_myMeshDS); + myWriter.SetFile( file ); + myWriter.SetMesh( meshPart ? (SMESHDS_Mesh*) meshPart : _myMeshDS ); myWriter.SetMeshId(_idDoc); myWriter.Perform(); } -void SMESH_Mesh::ExportUNV(const char *file) throw(SALOME_Exception) +//================================================================================ +/*! + * \brief Export the mesh to an UNV file + */ +//================================================================================ + +void SMESH_Mesh::ExportUNV(const char * file, + const SMESHDS_Mesh* meshPart) throw(SALOME_Exception) { Unexpect aCatch(SalomeException); DriverUNV_W_SMDS_Mesh myWriter; - myWriter.SetFile(string(file)); - myWriter.SetMesh(_myMeshDS); + myWriter.SetFile( file ); + myWriter.SetMesh( meshPart ? (SMESHDS_Mesh*) meshPart : _myMeshDS ); myWriter.SetMeshId(_idDoc); // myWriter.SetGroups(_mapGroup); - for ( std::map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) { - SMESH_Group* aGroup = it->second; - SMESHDS_GroupBase* aGroupDS = aGroup->GetGroupDS(); - if ( aGroupDS ) { - std::string aGroupName = aGroup->GetName(); - aGroupDS->SetStoreName( aGroupName.c_str() ); - myWriter.AddGroup( aGroupDS ); + if ( !meshPart ) + { + for ( map::iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) { + SMESH_Group* aGroup = it->second; + SMESHDS_GroupBase* aGroupDS = aGroup->GetGroupDS(); + if ( aGroupDS ) { + string aGroupName = aGroup->GetName(); + aGroupDS->SetStoreName( aGroupName.c_str() ); + myWriter.AddGroup( aGroupDS ); + } } } myWriter.Perform(); } -void SMESH_Mesh::ExportSTL(const char *file, const bool isascii) throw(SALOME_Exception) +//================================================================================ +/*! + * \brief Export the mesh to an STL file + */ +//================================================================================ + +void SMESH_Mesh::ExportSTL(const char * file, + const bool isascii, + const SMESHDS_Mesh* meshPart) throw(SALOME_Exception) { Unexpect aCatch(SalomeException); DriverSTL_W_SMDS_Mesh myWriter; - myWriter.SetFile(string(file)); + myWriter.SetFile( file ); myWriter.SetIsAscii( isascii ); - myWriter.SetMesh(_myMeshDS); + myWriter.SetMesh( meshPart ? (SMESHDS_Mesh*) meshPart : _myMeshDS); myWriter.SetMeshId(_idDoc); myWriter.Perform(); } +//================================================================================ +/*! + * \brief Export the mesh to the CGNS file + */ +//================================================================================ + +void SMESH_Mesh::ExportCGNS(const char * file, + const SMESHDS_Mesh* meshDS) +{ + int res = Driver_Mesh::DRS_FAIL; +#ifdef WITH_CGNS + DriverCGNS_Write myWriter; + myWriter.SetFile( file ); + myWriter.SetMesh( const_cast( meshDS )); + myWriter.SetMeshName( SMESH_Comment("Mesh_") << meshDS->GetPersistentId()); + res = myWriter.Perform(); +#endif + if ( res != Driver_Mesh::DRS_OK ) + throw SALOME_Exception("Export failed"); +} + //================================================================================ /*! * \brief Return number of nodes in the mesh */ //================================================================================ -int SMESH_Mesh::NbNodes() throw(SALOME_Exception) +int SMESH_Mesh::NbNodes() const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->NbNodes(); @@ -1178,7 +1409,19 @@ int SMESH_Mesh::NbNodes() throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbEdges(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::Nb0DElements() const throw(SALOME_Exception) +{ + Unexpect aCatch(SalomeException); + return _myMeshDS->GetMeshInfo().Nb0DElements(); +} + +//================================================================================ +/*! + * \brief Return number of edges of given order in the mesh + */ +//================================================================================ + +int SMESH_Mesh::NbEdges(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbEdges(order); @@ -1190,7 +1433,7 @@ int SMESH_Mesh::NbEdges(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbFaces(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbFaces(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbFaces(order); @@ -1202,7 +1445,7 @@ int SMESH_Mesh::NbFaces(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbTriangles(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbTriangles(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbTriangles(order); @@ -1214,19 +1457,31 @@ int SMESH_Mesh::NbTriangles(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbQuadrangles(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbQuadrangles(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbQuadrangles(order); } +//================================================================================ +/*! + * \brief Return number of biquadratic quadrangles in the mesh + */ +//================================================================================ + +int SMESH_Mesh::NbBiQuadQuadrangles() const throw(SALOME_Exception) +{ + Unexpect aCatch(SalomeException); + return _myMeshDS->GetMeshInfo().NbBiQuadQuadrangles(); +} + //================================================================================ /*! * \brief Return the number of polygonal faces in the mesh */ //================================================================================ -int SMESH_Mesh::NbPolygons() throw(SALOME_Exception) +int SMESH_Mesh::NbPolygons() const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbPolygons(); @@ -1238,7 +1493,7 @@ int SMESH_Mesh::NbPolygons() throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbVolumes(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbVolumes(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbVolumes(order); @@ -1250,7 +1505,7 @@ int SMESH_Mesh::NbVolumes(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbTetras(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbTetras(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbTetras(order); @@ -1262,19 +1517,31 @@ int SMESH_Mesh::NbTetras(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbHexas(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbHexas(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbHexas(order); } +//================================================================================ +/*! + * \brief Return number of triquadratic hexahedrons in the mesh + */ +//================================================================================ + +int SMESH_Mesh::NbTriQuadraticHexas() const throw(SALOME_Exception) +{ + Unexpect aCatch(SalomeException); + return _myMeshDS->GetMeshInfo().NbTriQuadHexas(); +} + //================================================================================ /*! * \brief Return number of pyramids of given order in the mesh */ //================================================================================ -int SMESH_Mesh::NbPyramids(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbPyramids(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbPyramids(order); @@ -1286,31 +1553,55 @@ int SMESH_Mesh::NbPyramids(SMDSAbs_ElementOrder order) throw(SALOME_Exception) */ //================================================================================ -int SMESH_Mesh::NbPrisms(SMDSAbs_ElementOrder order) throw(SALOME_Exception) +int SMESH_Mesh::NbPrisms(SMDSAbs_ElementOrder order) const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbPrisms(order); } +//================================================================================ +/*! + * \brief Return number of hexagonal prisms in the mesh + */ +//================================================================================ + +int SMESH_Mesh::NbHexagonalPrisms() const throw(SALOME_Exception) +{ + Unexpect aCatch(SalomeException); + return _myMeshDS->GetMeshInfo().NbHexPrisms(); +} + //================================================================================ /*! * \brief Return number of polyhedrons in the mesh */ //================================================================================ -int SMESH_Mesh::NbPolyhedrons() throw(SALOME_Exception) +int SMESH_Mesh::NbPolyhedrons() const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->GetMeshInfo().NbPolyhedrons(); } +//================================================================================ +/*! + * \brief Return number of ball elements in the mesh + */ +//================================================================================ + +int SMESH_Mesh::NbBalls() const throw(SALOME_Exception) +{ + Unexpect aCatch(SalomeException); + return _myMeshDS->GetMeshInfo().NbBalls(); +} + //================================================================================ /*! * \brief Return number of submeshes in the mesh */ //================================================================================ -int SMESH_Mesh::NbSubMesh() throw(SALOME_Exception) +int SMESH_Mesh::NbSubMesh() const throw(SALOME_Exception) { Unexpect aCatch(SalomeException); return _myMeshDS->NbSubMesh(); @@ -1347,18 +1638,45 @@ bool SMESH_Mesh::IsMainShape(const TopoDS_Shape& theShape) const SMESH_Group* SMESH_Mesh::AddGroup (const SMDSAbs_ElementType theType, const char* theName, - int& theId, - const TopoDS_Shape& theShape) + int& theId, + const TopoDS_Shape& theShape, + const SMESH_PredicatePtr& thePredicate) { - if (_mapGroup.find(_groupId) != _mapGroup.end()) + if (_mapGroup.count(_groupId)) return NULL; theId = _groupId; - SMESH_Group* aGroup = new SMESH_Group (theId, this, theType, theName, theShape); + SMESH_Group* aGroup = new SMESH_Group (theId, this, theType, theName, theShape, thePredicate); GetMeshDS()->AddGroup( aGroup->GetGroupDS() ); _mapGroup[_groupId++] = aGroup; return aGroup; } +//================================================================================ +/*! + * \brief Creates SMESH_Groups for not wrapped SMESHDS_Groups + * \retval bool - true if new SMESH_Groups have been created + * + */ +//================================================================================ + +bool SMESH_Mesh::SynchronizeGroups() +{ + int nbGroups = _mapGroup.size(); + const set& groups = _myMeshDS->GetGroups(); + set::const_iterator gIt = groups.begin(); + for ( ; gIt != groups.end(); ++gIt ) + { + SMESHDS_GroupBase* groupDS = (SMESHDS_GroupBase*) *gIt; + _groupId = groupDS->GetID(); + if ( !_mapGroup.count( _groupId )) + _mapGroup[_groupId] = new SMESH_Group( groupDS ); + } + if ( !_mapGroup.empty() ) + _groupId = _mapGroup.rbegin()->first + 1; + + return nbGroups < _mapGroup.size(); +} + //================================================================================ /*! * \brief Return iterator on all existing groups @@ -1391,15 +1709,27 @@ SMESH_Group* SMESH_Mesh::GetGroup (const int theGroupID) */ //============================================================================= -std::list SMESH_Mesh::GetGroupIds() const +list SMESH_Mesh::GetGroupIds() const { - std::list anIds; - for ( std::map::const_iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) + list anIds; + for ( map::const_iterator it = _mapGroup.begin(); it != _mapGroup.end(); it++ ) anIds.push_back( it->first ); return anIds; } +//================================================================================ +/*! + * \brief Set a caller of methods at level of CORBA API implementation. + * The set upCaller will be deleted by SMESH_Mesh + */ +//================================================================================ + +void SMESH_Mesh::SetCallUp( TCallUp* upCaller ) +{ + if ( _callUp ) delete _callUp; + _callUp = upCaller; +} //============================================================================= /*! @@ -1407,13 +1737,16 @@ std::list SMESH_Mesh::GetGroupIds() const */ //============================================================================= -void SMESH_Mesh::RemoveGroup (const int theGroupID) +bool SMESH_Mesh::RemoveGroup (const int theGroupID) { if (_mapGroup.find(theGroupID) == _mapGroup.end()) - return; + return false; GetMeshDS()->RemoveGroup( _mapGroup[theGroupID]->GetGroupDS() ); delete _mapGroup[theGroupID]; _mapGroup.erase (theGroupID); + if (_callUp) + _callUp->RemoveGroup( theGroupID ); + return true; } //======================================================================= @@ -1459,7 +1792,7 @@ ostream& SMESH_Mesh::Dump(ostream& save) save << clause << ".1) Number of " << orderStr << " triangles: \t" << nb3 << endl; save << clause << ".2) Number of " << orderStr << " quadrangles:\t" << nb4 << endl; if ( nb3 + nb4 != NbFaces(order) ) { - std::map myFaceMap; + map myFaceMap; SMDS_FaceIteratorPtr itFaces=_myMeshDS->facesIterator(); while( itFaces->more( ) ) { int nbNodes = itFaces->next()->NbNodes(); @@ -1484,7 +1817,7 @@ ostream& SMESH_Mesh::Dump(ostream& save) save << clause << ".3) Number of " << orderStr << " prisms: \t" << nb6 << endl; save << clause << ".4) Number of " << orderStr << " pyramids:\t" << nb5 << endl; if ( nb8 + nb4 + nb5 + nb6 != NbVolumes(order) ) { - std::map myVolumesMap; + map myVolumesMap; SMDS_VolumeIteratorPtr itVolumes=_myMeshDS->volumesIterator(); while( itVolumes->more( ) ) { int nbNodes = itVolumes->next()->NbNodes(); @@ -1523,7 +1856,7 @@ SMDSAbs_ElementType SMESH_Mesh::GetElementType( const int id, const bool iselem SMESH_Group* SMESH_Mesh::ConvertToStandalone ( int theGroupID ) { SMESH_Group* aGroup = 0; - std::map < int, SMESH_Group * >::iterator itg = _mapGroup.find( theGroupID ); + map < int, SMESH_Group * >::iterator itg = _mapGroup.find( theGroupID ); if ( itg == _mapGroup.end() ) return aGroup; @@ -1545,9 +1878,153 @@ SMESH_Group* SMESH_Mesh::ConvertToStandalone ( int theGroupID ) while ( anItr->more() ) aNewGrpDS->Add( (anItr->next())->GetID() ); + // set color + aNewGrpDS->SetColor( anOldGrpDS->GetColor() ); + // remove old group delete anOldGrp; return aGroup; } +//============================================================================= +/*! + * \brief remove submesh order from Mesh + */ +//============================================================================= + +void SMESH_Mesh::ClearMeshOrder() +{ + _mySubMeshOrder.clear(); +} + +//============================================================================= +/*! + * \brief remove submesh order from Mesh + */ +//============================================================================= + +void SMESH_Mesh::SetMeshOrder(const TListOfListOfInt& theOrder ) +{ + _mySubMeshOrder = theOrder; +} + +//============================================================================= +/*! + * \brief return submesh order if any + */ +//============================================================================= + +const TListOfListOfInt& SMESH_Mesh::GetMeshOrder() const +{ + return _mySubMeshOrder; +} + +//============================================================================= +/*! + * \brief fill _mapAncestors + */ +//============================================================================= + +void SMESH_Mesh::fillAncestorsMap(const TopoDS_Shape& theShape) +{ + + int desType, ancType; + if ( !theShape.IsSame( GetShapeToMesh()) && theShape.ShapeType() == TopAbs_COMPOUND ) + { + // a geom group is added. Insert it into lists of ancestors before + // the first ancestor more complex than group members + int memberType = TopoDS_Iterator( theShape ).Value().ShapeType(); + for ( desType = TopAbs_VERTEX; desType >= memberType; desType-- ) + for (TopExp_Explorer des( theShape, TopAbs_ShapeEnum( desType )); des.More(); des.Next()) + { + if ( !_mapAncestors.Contains( des.Current() )) continue;// issue 0020982 + TopTools_ListOfShape& ancList = _mapAncestors.ChangeFromKey( des.Current() ); + TopTools_ListIteratorOfListOfShape ancIt (ancList); + while ( ancIt.More() && ancIt.Value().ShapeType() >= memberType ) + ancIt.Next(); + if ( ancIt.More() ) + ancList.InsertBefore( theShape, ancIt ); + } + } + { + for ( desType = TopAbs_VERTEX; desType > TopAbs_COMPOUND; desType-- ) + for ( ancType = desType - 1; ancType >= TopAbs_COMPOUND; ancType-- ) + TopExp::MapShapesAndAncestors ( theShape, + (TopAbs_ShapeEnum) desType, + (TopAbs_ShapeEnum) ancType, + _mapAncestors ); + } +} + +//============================================================================= +/*! + * \brief sort submeshes according to stored mesh order + * \param theListToSort in out list to be sorted + * \return FALSE if nothing sorted + */ +//============================================================================= + +bool SMESH_Mesh::SortByMeshOrder(list& theListToSort) const +{ + if ( !_mySubMeshOrder.size() || theListToSort.size() < 2) + return true; + + bool res = false; + list onlyOrderedList; + // collect all ordered submeshes in one list as pointers + // and get their positions within theListToSort + typedef list::iterator TPosInList; + map< int, TPosInList > sortedPos; + TPosInList smBeg = theListToSort.begin(), smEnd = theListToSort.end(); + TListOfListOfInt::const_iterator listIddIt = _mySubMeshOrder.begin(); + for( ; listIddIt != _mySubMeshOrder.end(); listIddIt++) { + const TListOfInt& listOfId = *listIddIt; + TListOfInt::const_iterator idIt = listOfId.begin(); + for ( ; idIt != listOfId.end(); idIt++ ) { + if ( SMESH_subMesh * sm = GetSubMeshContaining( *idIt )) { + TPosInList smPos = find( smBeg, smEnd, sm ); + if ( smPos != smEnd ) { + onlyOrderedList.push_back( sm ); + sortedPos[ distance( smBeg, smPos )] = smPos; + } + } + } + } + if (onlyOrderedList.size() < 2) + return res; + res = true; + + list::iterator onlyBIt = onlyOrderedList.begin(); + list::iterator onlyEIt = onlyOrderedList.end(); + + // iterate on ordered submeshes and insert them in detected positions + map< int, TPosInList >::iterator i_pos = sortedPos.begin(); + for ( ; onlyBIt != onlyEIt; ++onlyBIt, ++i_pos ) + *(i_pos->second) = *onlyBIt; + + return res; +} + +//============================================================================= +/*! + * \brief sort submeshes according to stored mesh order + * \param theListToSort in out list to be sorted + * \return FALSE if nothing sorted + */ +//============================================================================= + +list SMESH_Mesh::getAncestorsSubMeshes + (const TopoDS_Shape& theSubShape) const +{ + list listOfSubMesh; + TopTools_ListIteratorOfListOfShape it( GetAncestors( theSubShape )); + for (; it.More(); it.Next() ) + if ( SMESH_subMesh* sm = GetSubMeshContaining( it.Value() )) + listOfSubMesh.push_back(sm); + + // sort submeshes according to stored mesh order + SortByMeshOrder( listOfSubMesh ); + + return listOfSubMesh; +} diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 91ae67eda..5ae3cc61c 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -1,25 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : SMESH_Mesh.hxx // Author : Paul RASCLE, EDF // Module : SMESH @@ -30,6 +30,7 @@ #include "SMESH_SMESH.hxx" #include "SMESH_Hypothesis.hxx" +#include "SMESH_Controls.hxx" #include "SMESHDS_Mesh.hxx" #include "SMESHDS_Command.hxx" @@ -40,8 +41,13 @@ #include #include -#include #include +#include + +#ifdef WNT +#pragma warning(disable:4251) // Warning DLL Interface ... +#pragma warning(disable:4290) // Warning Exception ... +#endif class SMESH_Gen; class SMESHDS_Document; @@ -51,14 +57,17 @@ class SMESH_subMesh; class SMESH_HypoFilter; class TopoDS_Solid; +typedef std::list TListOfInt; +typedef std::list TListOfListOfInt; + class SMESH_EXPORT SMESH_Mesh { public: SMESH_Mesh(int theLocalId, - int theStudyId, - SMESH_Gen* theGen, - bool theIsEmbeddedMode, - SMESHDS_Document* theDocument); + int theStudyId, + SMESH_Gen* theGen, + bool theIsEmbeddedMode, + SMESHDS_Document* theDocument); virtual ~SMESH_Mesh(); @@ -88,24 +97,30 @@ public: */ static const TopoDS_Solid& PseudoShape(); + /*! + * \brief Load mesh from study file + */ + void Load(); /*! * \brief Remove all nodes and elements */ void Clear(); - /*! * \brief Remove all nodes and elements of indicated shape */ void ClearSubMesh(const int theShapeId); - int UNVToMesh(const char* theFileName); /*! * consult DriverMED_R_SMESHDS_Mesh::ReadStatus for returned value */ + int UNVToMesh(const char* theFileName); + int MEDToMesh(const char* theFileName, const char* theMeshName); int STLToMesh(const char* theFileName); + int CGNSToMesh(const char* theFileName, const int theMeshIndex, std::string& theMeshName); + SMESH_Hypothesis::Hypothesis_Status AddHypothesis(const TopoDS_Shape & aSubShape, int anHypId) throw(SALOME_Exception); @@ -132,12 +147,16 @@ public: void ClearLog() throw(SALOME_Exception); - int GetId() { return _id; } + int GetId() const { return _id; } + + bool MeshExists( int meshId ) const; SMESHDS_Mesh * GetMeshDS() { return _myMeshDS; } - SMESH_Gen *GetGen() { return _gen; } + const SMESHDS_Mesh * GetMeshDS() const { return _myMeshDS; } + SMESH_Gen *GetGen() { return _gen; } + SMESH_subMesh *GetSubMesh(const TopoDS_Shape & aSubShape) throw(SALOME_Exception); @@ -162,7 +181,7 @@ public: * \brief Return True if anHyp is used to mesh aSubShape */ bool IsUsedHypothesis(SMESHDS_Hypothesis * anHyp, - const SMESH_subMesh * aSubMesh); + const SMESH_subMesh * aSubMesh); /*! * \brief check if a hypothesis alowing notconform mesh is present */ @@ -179,6 +198,21 @@ public: bool GetAutoColor() throw(SALOME_Exception); + /*! + * \brief Set the flag meaning that the mesh has been edited "manually". + * It is to set to false after Clear() and to set to true by MeshEditor + */ + void SetIsModified(bool isModified); + + bool GetIsModified() const { return _isModified; } + + /*! + * \brief Return true if the mesh has been edited since a total re-compute + * and those modifications may prevent successful partial re-compute. + * As a side effect reset _isModified flag if mesh is empty + */ + bool HasModificationsToDiscard() const; + /*! * \brief Return data map of descendant to ancestor shapes */ @@ -191,47 +225,68 @@ public: bool HasDuplicatedGroupNamesMED(); void ExportMED(const char *file, - const char* theMeshName = NULL, - bool theAutoGroups = true, - int theVersion = 0) + const char* theMeshName = NULL, + bool theAutoGroups = true, + int theVersion = 0, + const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception); - void ExportDAT(const char *file) throw(SALOME_Exception); - void ExportUNV(const char *file) throw(SALOME_Exception); - void ExportSTL(const char *file, const bool isascii) throw(SALOME_Exception); + void ExportDAT(const char * file, + const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception); + void ExportUNV(const char * file, + const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception); + void ExportSTL(const char * file, + const bool isascii, + const SMESHDS_Mesh* meshPart = 0) throw(SALOME_Exception); + void ExportCGNS(const char * file, + const SMESHDS_Mesh* mesh); + void ExportSAUV(const char *file, + const char* theMeshName = NULL, + bool theAutoGroups = true) throw(SALOME_Exception); + + int NbNodes() const throw(SALOME_Exception); - int NbNodes() throw(SALOME_Exception); + int Nb0DElements() const throw(SALOME_Exception); - int NbEdges(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbEdges(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbFaces(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbFaces(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbTriangles(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbTriangles(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbQuadrangles(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbQuadrangles(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbPolygons() throw(SALOME_Exception); + int NbBiQuadQuadrangles() const throw(SALOME_Exception); + + int NbPolygons() const throw(SALOME_Exception); - int NbVolumes(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbVolumes(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbTetras(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbTetras(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbHexas(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbHexas(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbPyramids(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbTriQuadraticHexas() const throw(SALOME_Exception); + + int NbPyramids(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbPrisms(SMDSAbs_ElementOrder order = ORDER_ANY) throw(SALOME_Exception); + int NbPrisms(SMDSAbs_ElementOrder order = ORDER_ANY) const throw(SALOME_Exception); - int NbPolyhedrons() throw(SALOME_Exception); + int NbHexagonalPrisms() const throw(SALOME_Exception); - int NbSubMesh() throw(SALOME_Exception); + int NbPolyhedrons() const throw(SALOME_Exception); - int NbGroup() const { return _mapGroup.size(); } + int NbBalls() const throw(SALOME_Exception); + + int NbSubMesh() const throw(SALOME_Exception); + int NbGroup() const { return _mapGroup.size(); } + SMESH_Group* AddGroup (const SMDSAbs_ElementType theType, - const char* theName, - int& theId, - const TopoDS_Shape& theShape=TopoDS_Shape()); + const char* theName, + int& theId, + const TopoDS_Shape& theShape=TopoDS_Shape(), + const SMESH_PredicatePtr& thePredicate=SMESH_PredicatePtr()); typedef boost::shared_ptr< SMDS_Iterator > GroupIteratorPtr; GroupIteratorPtr GetGroups() const; @@ -240,39 +295,76 @@ public: SMESH_Group* GetGroup (const int theGroupID); - void RemoveGroup (const int theGroupID); + bool RemoveGroup (const int theGroupID); SMESH_Group* ConvertToStandalone ( int theGroupID ); + struct TCallUp // callback from SMESH to SMESH_I level + { + virtual void RemoveGroup (const int theGroupID)=0; + virtual void HypothesisModified ()=0; + virtual void Load ()=0; + virtual ~TCallUp() {} + }; + void SetCallUp( TCallUp * upCaller ); + + bool SynchronizeGroups(); + + SMDSAbs_ElementType GetElementType( const int id, const bool iselem ); + void ClearMeshOrder(); + void SetMeshOrder(const TListOfListOfInt& theOrder ); + const TListOfListOfInt& GetMeshOrder() const; + + /*! + * \brief sort submeshes according to stored mesh order + * \param theListToSort in out list to be sorted + * \return FALSE if nothing sorted + */ + bool SortByMeshOrder(std::list& theListToSort) const; + // ostream& Dump(ostream & save); private: + + void fillAncestorsMap(const TopoDS_Shape& theShape); + std::list getAncestorsSubMeshes + (const TopoDS_Shape& theSubShape) const; protected: int _id; // id given by creator (unique within the creator instance) int _studyId; int _idDoc; // id given by SMESHDS_Document int _groupId; // id generator for group objects + int _nbSubShapes; // initial nb of subshapes in the shape to mesh bool _isShapeToMesh;// set to true when a shape is given (only once) std::list _subMeshesUsingHypothesisList; SMESHDS_Document * _myDocument; SMESHDS_Mesh * _myMeshDS; + SMESH_Gen * _gen; std::map _mapSubMesh; std::map _mapGroup; - SMESH_Gen * _gen; - + bool _isAutoColor; + bool _isModified; //!< modified since last total re-compute, issue 0020693 double _shapeDiagonal; //!< diagonal size of bounding box of shape to mesh TopTools_IndexedDataMapOfShapeListOfShape _mapAncestors; + TListOfListOfInt _mySubMeshOrder; + + // Struct calling methods at CORBA API implementation level, used to + // 1) make an upper level be consistent with a lower one when group removal + // is invoked by hyp modification (issue 0020918) + // 2) to forget not loaded mesh data at hyp modification + TCallUp* _callUp; + protected: - SMESH_Mesh() {}; + SMESH_Mesh(); SMESH_Mesh(const SMESH_Mesh&) {}; }; diff --git a/src/SMESH/SMESH_MeshEditor.cxx b/src/SMESH/SMESH_MeshEditor.cxx index 063f68819..324b6b81d 100644 --- a/src/SMESH/SMESH_MeshEditor.cxx +++ b/src/SMESH/SMESH_MeshEditor.cxx @@ -1,66 +1,83 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : idl implementation based on 'SMESH' unit's classes + // File : SMESH_MeshEditor.cxx // Created : Mon Apr 12 16:10:22 2004 // Author : Edward AGAPOV (eap) -// + #include "SMESH_MeshEditor.hxx" #include "SMDS_FaceOfNodes.hxx" #include "SMDS_VolumeTool.hxx" #include "SMDS_EdgePosition.hxx" -#include "SMDS_PolyhedralVolumeOfNodes.hxx" #include "SMDS_FacePosition.hxx" #include "SMDS_SpacePosition.hxx" -#include "SMDS_QuadraticFaceOfNodes.hxx" #include "SMDS_MeshGroup.hxx" +#include "SMDS_LinearEdge.hxx" +#include "SMDS_Downward.hxx" +#include "SMDS_SetIterator.hxx" #include "SMESHDS_Group.hxx" #include "SMESHDS_Mesh.hxx" -#include "SMESH_subMesh.hxx" +#include "SMESH_Algo.hxx" #include "SMESH_ControlsDef.hxx" +#include "SMESH_Group.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_OctreeNode.hxx" -#include "SMESH_Group.hxx" +#include "SMESH_subMesh.hxx" + +#include #include "utilities.h" +#include +#include +#include #include #include #include +#include #include +#include #include +#include #include #include +#include #include +#include +#include +#include #include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -70,10 +87,18 @@ #include #include #include -#include + +#include #include #include +#include +#include +#include +#include + +#include +#include #define cast2Node(elem) static_cast( elem ) @@ -82,13 +107,8 @@ using namespace SMESH::Controls; typedef map > TElemOfNodeListMap; typedef map > TElemOfElemListMap; -//typedef map > TNodeOfNodeVecMap; -//typedef TNodeOfNodeVecMap::iterator TNodeOfNodeVecMapItr; -//typedef map > TElemOfVecOfMapNodesMap; -struct TNodeXYZ : public gp_XYZ { - TNodeXYZ( const SMDS_MeshNode* n ):gp_XYZ( n->X(), n->Y(), n->Z() ) {} -}; +typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator> TSetIterator; //======================================================================= //function : SMESH_MeshEditor @@ -110,101 +130,170 @@ SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector & node, const SMDSAbs_ElementType type, const bool isPoly, - const int ID) + const int ID, + const double ballDiameter) { + //MESSAGE("AddElement " <AddEdgeWithID(node[0], node[1], ID); - else e = mesh->AddEdge (node[0], node[1] ); - else if ( nbnode == 3 ) - if ( ID ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID); - else e = mesh->AddEdge (node[0], node[1], node[2] ); - break; case SMDSAbs_Face: if ( !isPoly ) { - if (nbnode == 3) - if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID); - else e = mesh->AddFace (node[0], node[1], node[2] ); - else if (nbnode == 4) - if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID); - else e = mesh->AddFace (node[0], node[1], node[2], node[3] ); - else if (nbnode == 6) - if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], - node[4], node[5], ID); - else e = mesh->AddFace (node[0], node[1], node[2], node[3], - node[4], node[5] ); - else if (nbnode == 8) - if ( ID ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], ID); - else e = mesh->AddFace (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7] ); + if (nbnode == 3) { + if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], ID); + else e = mesh->AddFace (node[0], node[1], node[2] ); + } + else if (nbnode == 4) { + if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3] ); + } + else if (nbnode == 6) { + if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], + node[4], node[5], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3], + node[4], node[5] ); + } + else if (nbnode == 8) { + if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7] ); + } + else if (nbnode == 9) { + if ( ID >= 1 ) e = mesh->AddFaceWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], node[8], ID); + else e = mesh->AddFace (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], node[8] ); + } } else { - if ( ID ) e = mesh->AddPolygonalFaceWithID(node, ID); - else e = mesh->AddPolygonalFace (node ); + if ( ID >= 1 ) e = mesh->AddPolygonalFaceWithID(node, ID); + else e = mesh->AddPolygonalFace (node ); } break; + case SMDSAbs_Volume: if ( !isPoly ) { - if (nbnode == 4) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3] ); - else if (nbnode == 5) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4] ); - else if (nbnode == 6) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5] ); - else if (nbnode == 8) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7] ); - else if (nbnode == 10) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9] ); - else if (nbnode == 13) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12],ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12] ); - else if (nbnode == 15) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12],node[13],node[14],ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12],node[13],node[14] ); - else if (nbnode == 20) - if ( ID ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12],node[13],node[14],node[15], - node[16],node[17],node[18],node[19],ID); - else e = mesh->AddVolume (node[0], node[1], node[2], node[3], - node[4], node[5], node[6], node[7], - node[8], node[9], node[10],node[11], - node[12],node[13],node[14],node[15], - node[16],node[17],node[18],node[19] ); + if (nbnode == 4) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3] ); + } + else if (nbnode == 5) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4] ); + } + else if (nbnode == 6) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5] ); + } + else if (nbnode == 8) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7] ); + } + else if (nbnode == 10) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9] ); + } + else if (nbnode == 12) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10], node[11], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10], node[11] ); + } + else if (nbnode == 13) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12] ); + } + else if (nbnode == 15) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14] ); + } + else if (nbnode == 20) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19],ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19] ); + } + else if (nbnode == 27) { + if ( ID >= 1 ) e = mesh->AddVolumeWithID(node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19], + node[20],node[21],node[22],node[23], + node[24],node[25],node[26], ID); + else e = mesh->AddVolume (node[0], node[1], node[2], node[3], + node[4], node[5], node[6], node[7], + node[8], node[9], node[10],node[11], + node[12],node[13],node[14],node[15], + node[16],node[17],node[18],node[19], + node[20],node[21],node[22],node[23], + node[24],node[25],node[26] ); + } } + break; + + case SMDSAbs_Edge: + if ( nbnode == 2 ) { + if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], ID); + else e = mesh->AddEdge (node[0], node[1] ); + } + else if ( nbnode == 3 ) { + if ( ID >= 1 ) e = mesh->AddEdgeWithID(node[0], node[1], node[2], ID); + else e = mesh->AddEdge (node[0], node[1], node[2] ); + } + break; + + case SMDSAbs_0DElement: + if ( nbnode == 1 ) { + if ( ID >= 1 ) e = mesh->Add0DElementWithID(node[0], ID); + else e = mesh->Add0DElement (node[0] ); + } + break; + + case SMDSAbs_Node: + if ( ID >= 1 ) e = mesh->AddNodeWithID(node[0]->X(), node[0]->Y(), node[0]->Z(), ID); + else e = mesh->AddNode (node[0]->X(), node[0]->Y(), node[0]->Z()); + break; + + case SMDSAbs_Ball: + if ( ID >= 1 ) e = mesh->AddBallWithID(node[0], ballDiameter, ID); + else e = mesh->AddBall (node[0], ballDiameter); + break; + + default:; } + if ( e ) myLastCreatedElems.Append( e ); return e; } @@ -237,8 +326,8 @@ SMDS_MeshElement* SMESH_MeshEditor::AddElement(const vector & nodeIDs // Modify a compute state of sub-meshes which become empty //======================================================================= -bool SMESH_MeshEditor::Remove (const list< int >& theIDs, - const bool isNodes ) +int SMESH_MeshEditor::Remove (const list< int >& theIDs, + const bool isNodes ) { myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); @@ -246,6 +335,7 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, SMESHDS_Mesh* aMesh = GetMeshDS(); set< SMESH_subMesh *> smmap; + int removed = 0; list::const_iterator it = theIDs.begin(); for ( ; it != theIDs.end(); it++ ) { const SMDS_MeshElement * elem; @@ -260,28 +350,29 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, if ( isNodes ) { const SMDS_MeshNode* node = cast2Node( elem ); if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) - if ( int aShapeID = node->GetPosition()->GetShapeId() ) + if ( int aShapeID = node->getshapeId() ) if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) ) smmap.insert( sm ); } // Find sub-meshes to notify about modification -// SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); -// while ( nodeIt->more() ) { -// const SMDS_MeshNode* node = static_cast( nodeIt->next() ); -// const SMDS_PositionPtr& aPosition = node->GetPosition(); -// if ( aPosition.get() ) { -// if ( int aShapeID = aPosition->GetShapeId() ) { -// if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) ) -// smmap.insert( sm ); -// } -// } -// } + // SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); + // while ( nodeIt->more() ) { + // const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + // const SMDS_PositionPtr& aPosition = node->GetPosition(); + // if ( aPosition.get() ) { + // if ( int aShapeID = aPosition->GetShapeId() ) { + // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( aShapeID ) ) + // smmap.insert( sm ); + // } + // } + // } // Do remove if ( isNodes ) aMesh->RemoveNode( static_cast< const SMDS_MeshNode* >( elem )); else aMesh->RemoveElement( elem ); + removed++; } // Notify sub-meshes about modification @@ -291,11 +382,11 @@ bool SMESH_MeshEditor::Remove (const list< int >& theIDs, (*smIt)->ComputeStateEngine( SMESH_subMesh::MESH_ENTITY_REMOVED ); } -// // Check if the whole mesh becomes empty -// if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) ) -// sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); + // // Check if the whole mesh becomes empty + // if ( SMESH_subMesh * sm = GetMesh()->GetSubMeshContaining( 1 ) ) + // sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); - return true; + return removed; } //======================================================================= @@ -313,46 +404,55 @@ int SMESH_MeshEditor::FindShape (const SMDS_MeshElement * theElem) if ( aMesh->ShapeToMesh().IsNull() ) return 0; + int aShapeID = theElem->getshapeId(); + if ( aShapeID < 1 ) + return 0; + + if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID )) + if ( sm->Contains( theElem )) + return aShapeID; + if ( theElem->GetType() == SMDSAbs_Node ) { - const SMDS_PositionPtr& aPosition = - static_cast( theElem )->GetPosition(); - if ( aPosition.get() ) - return aPosition->GetShapeId(); - else - return 0; + MESSAGE( ":( Error: invalid myShapeId of node " << theElem->GetID() ); + } + else { + MESSAGE( ":( Error: invalid myShapeId of element " << theElem->GetID() ); } - TopoDS_Shape aShape; // the shape a node is on - SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); - while ( nodeIt->more() ) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - const SMDS_PositionPtr& aPosition = node->GetPosition(); - if ( aPosition.get() ) { - int aShapeID = aPosition->GetShapeId(); - SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ); - if ( sm ) { - if ( sm->Contains( theElem )) - return aShapeID; - if ( aShape.IsNull() ) - aShape = aMesh->IndexToShape( aShapeID ); - } - else { - //MESSAGE ( "::FindShape() No SubShape for aShapeID " << aShapeID ); + TopoDS_Shape aShape; // the shape a node of theElem is on + if ( theElem->GetType() != SMDSAbs_Node ) + { + SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); + while ( nodeIt->more() ) { + const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + if ((aShapeID = node->getshapeId()) > 0) { + if ( SMESHDS_SubMesh * sm = aMesh->MeshElements( aShapeID ) ) { + if ( sm->Contains( theElem )) + return aShapeID; + if ( aShape.IsNull() ) + aShape = aMesh->IndexToShape( aShapeID ); + } } } } // None of nodes is on a proper shape, // find the shape among ancestors of aShape on which a node is - if ( aShape.IsNull() ) { - //MESSAGE ("::FindShape() - NONE node is on shape") - return 0; + if ( !aShape.IsNull() ) { + TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape )); + for ( ; ancIt.More(); ancIt.Next() ) { + SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() ); + if ( sm && sm->Contains( theElem )) + return aMesh->ShapeToIndex( ancIt.Value() ); + } } - TopTools_ListIteratorOfListOfShape ancIt( GetMesh()->GetAncestors( aShape )); - for ( ; ancIt.More(); ancIt.Next() ) { - SMESHDS_SubMesh * sm = aMesh->MeshElements( ancIt.Value() ); - if ( sm && sm->Contains( theElem )) - return aMesh->ShapeToIndex( ancIt.Value() ); + else + { + const map& id2sm = GetMeshDS()->SubMeshes(); + map::const_iterator id_sm = id2sm.begin(); + for ( ; id_sm != id2sm.end(); ++id_sm ) + if ( id_sm->second->Contains( theElem )) + return id_sm->first; } //MESSAGE ("::FindShape() - SHAPE NOT FOUND") @@ -394,6 +494,25 @@ static void ShiftNodesQuadTria(const SMDS_MeshNode* aNodes[]) aNodes[5] = nd2; } +//======================================================================= +//function : edgeConnectivity +//purpose : auxilary +// return number of the edges connected with the theNode. +// if theEdges has connections with the other type of the +// elements, return -1 +//======================================================================= +static int nbEdgeConnectivity(const SMDS_MeshNode* theNode) +{ + SMDS_ElemIteratorPtr elemIt = theNode->GetInverseElementIterator(); + int nb=0; + while(elemIt->more()) { + elemIt->next(); + nb++; + } + return nb; +} + + //======================================================================= //function : GetNodesFromTwoTria //purpose : auxilary @@ -459,15 +578,19 @@ static bool GetNodesFromTwoTria(const SMDS_MeshElement * theTria1, bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1, const SMDS_MeshElement * theTria2 ) { + MESSAGE("InverseDiag"); myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); if (!theTria1 || !theTria2) return false; - const SMDS_FaceOfNodes* F1 = dynamic_cast( theTria1 ); - const SMDS_FaceOfNodes* F2 = dynamic_cast( theTria2 ); - if (F1 && F2) { + const SMDS_VtkFace* F1 = dynamic_cast( theTria1 ); + if (!F1) return false; + const SMDS_VtkFace* F2 = dynamic_cast( theTria2 ); + if (!F2) return false; + if ((theTria1->GetEntityType() == SMDSEntity_Triangle) && + (theTria2->GetEntityType() == SMDSEntity_Triangle)) { // 1 +--+ A theTria1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A // | /| theTria2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | @@ -504,12 +627,14 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1, // find indices of 1,2 and of A,B in theTria1 int iA = 0, iB = 0, i1 = 0, i2 = 0; for ( i = 0; i < 6; i++ ) { - if ( sameInd [ i ] == 0 ) + if ( sameInd [ i ] == 0 ) { if ( i < 3 ) i1 = i; else i2 = i; - else if (i < 3) + } + else if (i < 3) { if ( iA ) iB = i; else iA = i; + } } // nodes 1 and 2 should not be the same if ( aNodes[ i1 ] == aNodes[ i2 ] ) @@ -520,24 +645,18 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshElement * theTria1, // theTria2: B->1 aNodes[ sameInd[ iB ]] = aNodes[ i1 ]; - //MESSAGE( theTria1 << theTria2 ); - GetMeshDS()->ChangeElementNodes( theTria1, aNodes, 3 ); GetMeshDS()->ChangeElementNodes( theTria2, &aNodes[ 3 ], 3 ); - //MESSAGE( theTria1 << theTria2 ); - return true; } // end if(F1 && F2) // check case of quadratic faces - const SMDS_QuadraticFaceOfNodes* QF1 = - dynamic_cast (theTria1); - if(!QF1) return false; - const SMDS_QuadraticFaceOfNodes* QF2 = - dynamic_cast (theTria2); - if(!QF2) return false; + if (theTria1->GetEntityType() != SMDSEntity_Quad_Triangle) + return false; + if (theTria2->GetEntityType() != SMDSEntity_Quad_Triangle) + return false; // 5 // 1 +--+--+ 2 theTria1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9) @@ -602,7 +721,7 @@ static bool findTriangles(const SMDS_MeshNode * theNode1, it = theNode2->GetInverseElementIterator(SMDSAbs_Face); while (it->more()) { const SMDS_MeshElement* elem = it->next(); - if ( emap.find( elem ) != emap.end() ) + if ( emap.find( elem ) != emap.end() ) { if ( theTria1 ) { // theTria1 must be element with minimum ID if( theTria1->GetID() < elem->GetID() ) { @@ -617,6 +736,7 @@ static bool findTriangles(const SMDS_MeshNode * theNode1, else { theTria1 = elem; } + } } return ( theTria1 && theTria2 ); } @@ -640,11 +760,12 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1, if ( !findTriangles( theNode1, theNode2, tr1, tr2 )) return false; - const SMDS_FaceOfNodes* F1 = dynamic_cast( tr1 ); - //if (!F1) return false; - const SMDS_FaceOfNodes* F2 = dynamic_cast( tr2 ); - //if (!F2) return false; - if (F1 && F2) { + const SMDS_VtkFace* F1 = dynamic_cast( tr1 ); + if (!F1) return false; + const SMDS_VtkFace* F2 = dynamic_cast( tr2 ); + if (!F2) return false; + if ((tr1->GetEntityType() == SMDSEntity_Triangle) && + (tr2->GetEntityType() == SMDSEntity_Triangle)) { // 1 +--+ A tr1: ( 1 A B ) A->2 ( 1 2 B ) 1 +--+ A // | /| tr2: ( B A 2 ) B->1 ( 1 A 2 ) |\ | @@ -682,23 +803,13 @@ bool SMESH_MeshEditor::InverseDiag (const SMDS_MeshNode * theNode1, // tr2: B->1 aNodes2[ iB2 ] = aNodes1[ i1 ]; - //MESSAGE( tr1 << tr2 ); - GetMeshDS()->ChangeElementNodes( tr1, aNodes1, 3 ); GetMeshDS()->ChangeElementNodes( tr2, aNodes2, 3 ); - //MESSAGE( tr1 << tr2 ); - return true; } // check case of quadratic faces - const SMDS_QuadraticFaceOfNodes* QF1 = - dynamic_cast (tr1); - if(!QF1) return false; - const SMDS_QuadraticFaceOfNodes* QF2 = - dynamic_cast (tr2); - if(!QF2) return false; return InverseDiag(tr1,tr2); } @@ -772,34 +883,39 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, if ( !findTriangles( theNode1, theNode2, tr1, tr2 )) return false; - const SMDS_FaceOfNodes* F1 = dynamic_cast( tr1 ); - //if (!F1) return false; - const SMDS_FaceOfNodes* F2 = dynamic_cast( tr2 ); - //if (!F2) return false; - if (F1 && F2) { + const SMDS_VtkFace* F1 = dynamic_cast( tr1 ); + if (!F1) return false; + const SMDS_VtkFace* F2 = dynamic_cast( tr2 ); + if (!F2) return false; + SMESHDS_Mesh * aMesh = GetMeshDS(); + + if ((tr1->GetEntityType() == SMDSEntity_Triangle) && + (tr2->GetEntityType() == SMDSEntity_Triangle)) { const SMDS_MeshNode* aNodes [ 4 ]; if ( ! getQuadrangleNodes( aNodes, theNode1, theNode2, tr1, tr2 )) return false; - //MESSAGE( endl << tr1 << tr2 ); - - GetMeshDS()->ChangeElementNodes( tr1, aNodes, 4 ); - myLastCreatedElems.Append(tr1); - GetMeshDS()->RemoveElement( tr2 ); - - //MESSAGE( endl << tr1 ); + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3] ); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr2 ); return true; } // check case of quadratic faces - const SMDS_QuadraticFaceOfNodes* QF1 = - dynamic_cast (tr1); - if(!QF1) return false; - const SMDS_QuadraticFaceOfNodes* QF2 = - dynamic_cast (tr2); - if(!QF2) return false; + if (tr1->GetEntityType() != SMDSEntity_Quad_Triangle) + return false; + if (tr2->GetEntityType() != SMDSEntity_Quad_Triangle) + return false; // 5 // 1 +--+--+ 2 tr1: (1 2 4 5 9 7) or (2 4 1 9 7 5) or (4 1 2 7 5 9) @@ -829,9 +945,18 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, aNodes[6] = N2[3]; aNodes[7] = N1[5]; - GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); - myLastCreatedElems.Append(tr1); - GetMeshDS()->RemoveElement( tr2 ); + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace( aNodes[0], aNodes[1], aNodes[2], aNodes[3], + aNodes[4], aNodes[5], aNodes[6], aNodes[7]); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr2 ); // remove middle node (9) GetMeshDS()->RemoveNode( N1[4] ); @@ -846,6 +971,7 @@ bool SMESH_MeshEditor::DeleteDiag (const SMDS_MeshNode * theNode1, bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) { + MESSAGE("Reorient"); myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); @@ -892,8 +1018,10 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) } case SMDSAbs_Volume: { if (theElem->IsPoly()) { - const SMDS_PolyhedralVolumeOfNodes* aPolyedre = - static_cast( theElem ); + // TODO reorient vtk polyhedron + MESSAGE("reorient vtk polyhedron ?"); + const SMDS_VtkVolume* aPolyedre = + dynamic_cast( theElem ); if (!aPolyedre) { MESSAGE("Warning: bad volumic element"); return false; @@ -922,6 +1050,7 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) if ( !vTool.Set( theElem )) return false; vTool.Inverse(); + MESSAGE("ChangeElementNodes reorient: check vTool.Inverse"); return GetMeshDS()->ChangeElementNodes( theElem, vTool.GetNodes(), vTool.NbNodes() ); } } @@ -931,6 +1060,97 @@ bool SMESH_MeshEditor::Reorient (const SMDS_MeshElement * theElem) return false; } +//================================================================================ +/*! + * \brief Reorient faces. + * \param theFaces - the faces to reorient. If empty the whole mesh is meant + * \param theDirection - desired direction of normal of \a theFace + * \param theFace - one of \a theFaces that sould be orientated according to + * \a theDirection and whose orientation defines orientation of other faces + * \return number of reoriented faces. + */ +//================================================================================ + +int SMESH_MeshEditor::Reorient2D (TIDSortedElemSet & theFaces, + const gp_Dir& theDirection, + const SMDS_MeshElement * theFace) +{ + int nbReori = 0; + if ( !theFace || theFace->GetType() != SMDSAbs_Face ) return nbReori; + + if ( theFaces.empty() ) + { + SMDS_FaceIteratorPtr fIt = GetMeshDS()->facesIterator(/*idInceasingOrder=*/true); + while ( fIt->more() ) + theFaces.insert( theFaces.end(), fIt->next() ); + } + + // orient theFace according to theDirection + gp_XYZ normal; + SMESH_Algo::FaceNormal( theFace, normal, /*normalized=*/false ); + if ( normal * theDirection.XYZ() < 0 ) + nbReori += Reorient( theFace ); + + // Orient other faces + + set< const SMDS_MeshElement* > startFaces; + TIDSortedElemSet avoidSet; + set< SMESH_TLink > checkedLinks; + pair< set< SMESH_TLink >::iterator, bool > linkIt_isNew; + + if ( theFaces.size() > 1 )// leave 1 face to prevent finding not selected faces + theFaces.erase( theFace ); + startFaces.insert( theFace ); + + set< const SMDS_MeshElement* >::iterator startFace = startFaces.begin(); + while ( startFace != startFaces.end() ) + { + theFace = *startFace; + const int nbNodes = theFace->NbCornerNodes(); + + avoidSet.clear(); + avoidSet.insert(theFace); + + NLink link( theFace->GetNode( 0 ), 0 ); + for ( int i = 0; i < nbNodes; ++i ) // loop on links of theFace + { + link.second = theFace->GetNode(( i+1 ) % nbNodes ); + linkIt_isNew = checkedLinks.insert( link ); + if ( !linkIt_isNew.second ) + { + // link has already been checked and won't be encountered more + // if the group (theFaces) is manifold + checkedLinks.erase( linkIt_isNew.first ); + } + else + { + int nodeInd1, nodeInd2; + const SMDS_MeshElement* otherFace = FindFaceInSet( link.first, link.second, + theFaces, avoidSet, + & nodeInd1, & nodeInd2); + if ( otherFace && otherFace != theFace) + { + // link must be reversed in otherFace if orientation ot otherFace + // is same as that of theFace + if ( abs(nodeInd2-nodeInd1) == 1 ? nodeInd2 > nodeInd1 : nodeInd1 > nodeInd2 ) + { + // cout << "Reorient " << otherFace->GetID() << " near theFace=" <GetID() + // << " \tlink( " << link.first->GetID() << " " << link.second->GetID() << endl; + nbReori += Reorient( otherFace ); + } + startFaces.insert( otherFace ); + if ( theFaces.size() > 1 ) // leave 1 face to prevent finding not selected faces + theFaces.erase( otherFace ); + } + } + std::swap( link.first, link.second ); + } + startFaces.erase( startFace ); + startFace = startFaces.begin(); + } + return nbReori; +} + //======================================================================= //function : getBadRate //purpose : @@ -973,15 +1193,11 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, const SMDS_MeshElement* elem = *itElem; if ( !elem || elem->GetType() != SMDSAbs_Face ) continue; - if ( elem->NbNodes() != ( elem->IsQuadratic() ? 8 : 4 )) + if ( elem->NbCornerNodes() != 4 ) continue; // retrieve element nodes - const SMDS_MeshNode* aNodes [8]; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - int i = 0; - while ( itN->more() ) - aNodes[ i++ ] = static_cast( itN->next() ); + vector< const SMDS_MeshNode* > aNodes( elem->begin_nodes(), elem->end_nodes() ); // compare two sets of possible triangles double aBadRate1, aBadRate2; // to what extent a set is bad @@ -994,7 +1210,8 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, aBadRate2 = getBadRate( &tr3, theCrit ) + getBadRate( &tr4, theCrit ); int aShapeId = FindShape( elem ); - const SMDS_MeshElement* newElem = 0; + const SMDS_MeshElement* newElem1 = 0; + const SMDS_MeshElement* newElem2 = 0; if( !elem->IsQuadratic() ) { @@ -1002,13 +1219,13 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, if ( aBadRate1 <= aBadRate2 ) { // tr1 + tr2 is better - aMesh->ChangeElementNodes( elem, aNodes, 3 ); - newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + newElem1 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + newElem2 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] ); } else { // tr3 + tr4 is better - aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); - newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] ); } } else { @@ -1028,72 +1245,70 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, helper.SetSubShape( shape ); } } - // get elem nodes - const SMDS_MeshNode* aNodes [8]; - const SMDS_MeshNode* inFaceNode = 0; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - int i = 0; - while ( itN->more() ) { - aNodes[ i++ ] = static_cast( itN->next() ); - if ( !inFaceNode && helper.GetNodeUVneedInFaceNode() && - aNodes[ i-1 ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) - { - inFaceNode = aNodes[ i-1 ]; - } - } // find middle point for (0,1,2,3) // and create a node in this point; - gp_XYZ p( 0,0,0 ); - if ( surface.IsNull() ) { - for(i=0; i<4; i++) - p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() ); - p /= 4; + const SMDS_MeshNode* newN = 0; + if ( aNodes.size() == 9 ) + { + // SMDSEntity_BiQuad_Quadrangle + newN = aNodes.back(); } - else { - TopoDS_Face face = TopoDS::Face( helper.GetSubShape() ); - gp_XY uv( 0,0 ); - for(i=0; i<4; i++) - uv += helper.GetNodeUV( face, aNodes[i], inFaceNode ); - uv /= 4.; - p = surface->Value( uv.X(), uv.Y() ).XYZ(); + else + { + gp_XYZ p( 0,0,0 ); + if ( surface.IsNull() ) + { + for ( int i = 0; i < 4; i++ ) + p += gp_XYZ(aNodes[i]->X(), aNodes[i]->Y(), aNodes[i]->Z() ); + p /= 4; + } + else + { + const SMDS_MeshNode* inFaceNode = 0; + if ( helper.GetNodeUVneedInFaceNode() ) + for ( size_t i = 0; i < aNodes.size() && !inFaceNode; ++i ) + if ( aNodes[ i ]->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) + inFaceNode = aNodes[ i ]; + + TopoDS_Face face = TopoDS::Face( helper.GetSubShape() ); + gp_XY uv( 0,0 ); + for ( int i = 0; i < 4; i++ ) + uv += helper.GetNodeUV( face, aNodes[i], inFaceNode ); + uv /= 4.; + p = surface->Value( uv.X(), uv.Y() ).XYZ(); + } + newN = aMesh->AddNode( p.X(), p.Y(), p.Z() ); + myLastCreatedNodes.Append(newN); } - const SMDS_MeshNode* newN = aMesh->AddNode( p.X(), p.Y(), p.Z() ); - myLastCreatedNodes.Append(newN); - // create a new element - const SMDS_MeshNode* N[6]; if ( aBadRate1 <= aBadRate2 ) { - N[0] = aNodes[0]; - N[1] = aNodes[1]; - N[2] = aNodes[2]; - N[3] = aNodes[4]; - N[4] = aNodes[5]; - N[5] = newN; - newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], - aNodes[6], aNodes[7], newN ); + newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], + aNodes[6], aNodes[7], newN ); + newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1], + newN, aNodes[4], aNodes[5] ); } else { - N[0] = aNodes[1]; - N[1] = aNodes[2]; - N[2] = aNodes[3]; - N[3] = aNodes[5]; - N[4] = aNodes[6]; - N[5] = newN; - newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], - aNodes[7], aNodes[4], newN ); + newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], + aNodes[7], aNodes[4], newN ); + newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2], + newN, aNodes[5], aNodes[6] ); } - aMesh->ChangeElementNodes( elem, N, 6 ); - } // quadratic case // care of a new element - myLastCreatedElems.Append(newElem); - AddToSameGroups( newElem, elem, aMesh ); + myLastCreatedElems.Append(newElem1); + myLastCreatedElems.Append(newElem2); + AddToSameGroups( newElem1, elem, aMesh ); + AddToSameGroups( newElem2, elem, aMesh ); // put a new triangle on the same shape if ( aShapeId ) - aMesh->SetMeshElementOnShape( newElem, aShapeId ); + { + aMesh->SetMeshElementOnShape( newElem1, aShapeId ); + aMesh->SetMeshElementOnShape( newElem2, aShapeId ); + } + aMesh->RemoveElement( elem ); } return true; } @@ -1102,6 +1317,7 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, //function : BestSplit //purpose : Find better diagonal for cutting. //======================================================================= + int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad, SMESH::Controls::NumericalFunctorPtr theCrit) { @@ -1143,85 +1359,681 @@ int SMESH_MeshEditor::BestSplit (const SMDS_MeshElement* theQuad, return -1; } -//======================================================================= -//function : AddToSameGroups -//purpose : add elemToAdd to the groups the elemInGroups belongs to -//======================================================================= - -void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd, - const SMDS_MeshElement* elemInGroups, - SMESHDS_Mesh * aMesh) +namespace { - const set& groups = aMesh->GetGroups(); - if (!groups.empty()) { - set::const_iterator grIt = groups.begin(); - for ( ; grIt != groups.end(); grIt++ ) { - SMESHDS_Group* group = dynamic_cast( *grIt ); - if ( group && group->Contains( elemInGroups )) - group->SMDSGroup().Add( elemToAdd ); - } - } -} + // Methods of splitting volumes into tetra + const int theHexTo5_1[5*4+1] = + { + 0, 1, 2, 5, 0, 4, 5, 7, 0, 2, 3, 7, 2, 5, 6, 7, 0, 5, 2, 7, -1 + }; + const int theHexTo5_2[5*4+1] = + { + 1, 2, 3, 6, 1, 4, 5, 6, 0, 1, 3, 4, 3, 4, 6, 7, 1, 3, 4, 6, -1 + }; + const int* theHexTo5[2] = { theHexTo5_1, theHexTo5_2 }; -//======================================================================= -//function : RemoveElemFromGroups -//purpose : Remove removeelem to the groups the elemInGroups belongs to -//======================================================================= -void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem, - SMESHDS_Mesh * aMesh) -{ - const set& groups = aMesh->GetGroups(); - if (!groups.empty()) - { - set::const_iterator GrIt = groups.begin(); - for (; GrIt != groups.end(); GrIt++) + const int theHexTo6_1[6*4+1] = { - SMESHDS_Group* grp = dynamic_cast(*GrIt); - if (!grp || grp->IsEmpty()) continue; - grp->SMDSGroup().Remove(removeelem); - } - } -} + 1, 5, 6, 0, 0, 1, 2, 6, 0, 4, 5, 6, 0, 4, 6, 7, 0, 2, 3, 6, 0, 3, 7, 6, -1 + }; + const int theHexTo6_2[6*4+1] = + { + 2, 6, 7, 1, 1, 2, 3, 7, 1, 5, 6, 7, 1, 5, 7, 4, 1, 3, 0, 7, 1, 0, 4, 7, -1 + }; + const int theHexTo6_3[6*4+1] = + { + 3, 7, 4, 2, 2, 3, 0, 4, 2, 6, 7, 4, 2, 6, 4, 5, 2, 0, 1, 4, 2, 1, 5, 4, -1 + }; + const int theHexTo6_4[6*4+1] = + { + 0, 4, 5, 3, 3, 0, 1, 5, 3, 7, 4, 5, 3, 7, 5, 6, 3, 1, 2, 5, 3, 2, 6, 5, -1 + }; + const int* theHexTo6[4] = { theHexTo6_1, theHexTo6_2, theHexTo6_3, theHexTo6_4 }; -//======================================================================= -//function : ReplaceElemInGroups -//purpose : replace elemToRm by elemToAdd in the all groups -//======================================================================= + const int thePyraTo2_1[2*4+1] = + { + 0, 1, 2, 4, 0, 2, 3, 4, -1 + }; + const int thePyraTo2_2[2*4+1] = + { + 1, 2, 3, 4, 1, 3, 0, 4, -1 + }; + const int* thePyraTo2[2] = { thePyraTo2_1, thePyraTo2_2 }; -void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, - const SMDS_MeshElement* elemToAdd, - SMESHDS_Mesh * aMesh) -{ - const set& groups = aMesh->GetGroups(); - if (!groups.empty()) { - set::const_iterator grIt = groups.begin(); - for ( ; grIt != groups.end(); grIt++ ) { - SMESHDS_Group* group = dynamic_cast( *grIt ); - if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd ) - group->SMDSGroup().Add( elemToAdd ); + const int thePentaTo3_1[3*4+1] = + { + 0, 1, 2, 3, 1, 3, 4, 2, 2, 3, 4, 5, -1 + }; + const int thePentaTo3_2[3*4+1] = + { + 1, 2, 0, 4, 2, 4, 5, 0, 0, 4, 5, 3, -1 + }; + const int thePentaTo3_3[3*4+1] = + { + 2, 0, 1, 5, 0, 5, 3, 1, 1, 5, 3, 4, -1 + }; + const int thePentaTo3_4[3*4+1] = + { + 0, 1, 2, 3, 1, 3, 4, 5, 2, 3, 1, 5, -1 + }; + const int thePentaTo3_5[3*4+1] = + { + 1, 2, 0, 4, 2, 4, 5, 3, 0, 4, 2, 3, -1 + }; + const int thePentaTo3_6[3*4+1] = + { + 2, 0, 1, 5, 0, 5, 3, 4, 1, 5, 0, 4, -1 + }; + const int* thePentaTo3[6] = { thePentaTo3_1, thePentaTo3_2, thePentaTo3_3, + thePentaTo3_4, thePentaTo3_5, thePentaTo3_6 }; + + struct TTriangleFacet //!< stores indices of three nodes of tetra facet + { + int _n1, _n2, _n3; + TTriangleFacet(int n1, int n2, int n3): _n1(n1), _n2(n2), _n3(n3) {} + bool contains(int n) const { return ( n == _n1 || n == _n2 || n == _n3 ); } + bool hasAdjacentTetra( const SMDS_MeshElement* elem ) const; + }; + struct TSplitMethod + { + int _nbTetra; + const int* _connectivity; //!< foursomes of tetra connectivy finished by -1 + bool _baryNode; //!< additional node is to be created at cell barycenter + bool _ownConn; //!< to delete _connectivity in destructor + map _faceBaryNode; //!< map face index to node at BC of face + + TSplitMethod( int nbTet=0, const int* conn=0, bool addNode=false) + : _nbTetra(nbTet), _connectivity(conn), _baryNode(addNode), _ownConn(false) {} + ~TSplitMethod() { if ( _ownConn ) delete [] _connectivity; _connectivity = 0; } + bool hasFacet( const TTriangleFacet& facet ) const + { + const int* tetConn = _connectivity; + for ( ; tetConn[0] >= 0; tetConn += 4 ) + if (( facet.contains( tetConn[0] ) + + facet.contains( tetConn[1] ) + + facet.contains( tetConn[2] ) + + facet.contains( tetConn[3] )) == 3 ) + return true; + return false; } - } -} + }; -//======================================================================= -//function : QuadToTri -//purpose : Cut quadrangles into triangles. -// theCrit is used to select a diagonal to cut -//======================================================================= + //======================================================================= + /*! + * \brief return TSplitMethod for the given element + */ + //======================================================================= -bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, - const bool the13Diag) -{ - myLastCreatedElems.Clear(); - myLastCreatedNodes.Clear(); + TSplitMethod getSplitMethod( SMDS_VolumeTool& vol, const int theMethodFlags) + { + const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; - MESSAGE( "::QuadToTri()" ); + // at HEXA_TO_24 method, each face of volume is split into triangles each based on + // an edge and a face barycenter; tertaherdons are based on triangles and + // a volume barycenter + const bool is24TetMode = ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_24 ); - SMESHDS_Mesh * aMesh = GetMeshDS(); + // Find out how adjacent volumes are split - Handle(Geom_Surface) surface; - SMESH_MesherHelper helper( *GetMesh() ); + vector < list< TTriangleFacet > > triaSplitsByFace( vol.NbFaces() ); // splits of each side + int hasAdjacentSplits = 0, maxTetConnSize = 0; + for ( int iF = 0; iF < vol.NbFaces(); ++iF ) + { + int nbNodes = vol.NbFaceNodes( iF ) / iQ; + maxTetConnSize += 4 * ( nbNodes - (is24TetMode ? 0 : 2)); + if ( nbNodes < 4 ) continue; + + list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ]; + const int* nInd = vol.GetFaceNodesIndices( iF ); + if ( nbNodes == 4 ) + { + TTriangleFacet t012( nInd[0*iQ], nInd[1*iQ], nInd[2*iQ] ); + TTriangleFacet t123( nInd[1*iQ], nInd[2*iQ], nInd[3*iQ] ); + if ( t012.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t012 ); + else if ( t123.hasAdjacentTetra( vol.Element() )) triaSplits.push_back( t123 ); + } + else + { + int iCom = 0; // common node of triangle faces to split into + for ( int iVar = 0; iVar < nbNodes; ++iVar, ++iCom ) + { + TTriangleFacet t012( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+1)%nbNodes )], + nInd[ iQ * ( (iCom+2)%nbNodes )]); + TTriangleFacet t023( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+2)%nbNodes )], + nInd[ iQ * ( (iCom+3)%nbNodes )]); + if ( t012.hasAdjacentTetra( vol.Element() ) && t023.hasAdjacentTetra( vol.Element() )) + { + triaSplits.push_back( t012 ); + triaSplits.push_back( t023 ); + break; + } + } + } + if ( !triaSplits.empty() ) + hasAdjacentSplits = true; + } + + // Among variants of split method select one compliant with adjacent volumes + + TSplitMethod method; + if ( !vol.Element()->IsPoly() && !is24TetMode ) + { + int nbVariants = 2, nbTet = 0; + const int** connVariants = 0; + switch ( vol.Element()->GetEntityType() ) + { + case SMDSEntity_Hexa: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + if ( theMethodFlags == SMESH_MeshEditor::HEXA_TO_5 ) + connVariants = theHexTo5, nbTet = 5; + else + connVariants = theHexTo6, nbTet = 6, nbVariants = 4; + break; + case SMDSEntity_Pyramid: + case SMDSEntity_Quad_Pyramid: + connVariants = thePyraTo2; nbTet = 2; + break; + case SMDSEntity_Penta: + case SMDSEntity_Quad_Penta: + connVariants = thePentaTo3; nbTet = 3; nbVariants = 6; + break; + default: + nbVariants = 0; + } + for ( int variant = 0; variant < nbVariants && method._nbTetra == 0; ++variant ) + { + // check method compliancy with adjacent tetras, + // all found splits must be among facets of tetras described by this method + method = TSplitMethod( nbTet, connVariants[variant] ); + if ( hasAdjacentSplits && method._nbTetra > 0 ) + { + bool facetCreated = true; + for ( int iF = 0; facetCreated && iF < triaSplitsByFace.size(); ++iF ) + { + list< TTriangleFacet >::const_iterator facet = triaSplitsByFace[iF].begin(); + for ( ; facetCreated && facet != triaSplitsByFace[iF].end(); ++facet ) + facetCreated = method.hasFacet( *facet ); + } + if ( !facetCreated ) + method = TSplitMethod(0); // incompatible method + } + } + } + if ( method._nbTetra < 1 ) + { + // No standard method is applicable, use a generic solution: + // each facet of a volume is split into triangles and + // each of triangles and a volume barycenter form a tetrahedron. + + const bool isHex27 = ( vol.Element()->GetEntityType() == SMDSEntity_TriQuad_Hexa ); + + int* connectivity = new int[ maxTetConnSize + 1 ]; + method._connectivity = connectivity; + method._ownConn = true; + method._baryNode = !isHex27; // to create central node or not + + int connSize = 0; + int baryCenInd = vol.NbNodes() - int( isHex27 ); + for ( int iF = 0; iF < vol.NbFaces(); ++iF ) + { + const int nbNodes = vol.NbFaceNodes( iF ) / iQ; + const int* nInd = vol.GetFaceNodesIndices( iF ); + // find common node of triangle facets of tetra to create + int iCommon = 0; // index in linear numeration + const list< TTriangleFacet >& triaSplits = triaSplitsByFace[ iF ]; + if ( !triaSplits.empty() ) + { + // by found facets + const TTriangleFacet* facet = &triaSplits.front(); + for ( ; iCommon < nbNodes-1 ; ++iCommon ) + if ( facet->contains( nInd[ iQ * iCommon ]) && + facet->contains( nInd[ iQ * ((iCommon+2)%nbNodes) ])) + break; + } + else if ( nbNodes > 3 && !is24TetMode ) + { + // find the best method of splitting into triangles by aspect ratio + SMESH::Controls::NumericalFunctorPtr aspectRatio( new SMESH::Controls::AspectRatio); + map< double, int > badness2iCommon; + const SMDS_MeshNode** nodes = vol.GetFaceNodes( iF ); + int nbVariants = ( nbNodes == 4 ? 2 : nbNodes ); + for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCommon ) + { + double badness = 0; + for ( int iLast = iCommon+2; iLast < iCommon+nbNodes; ++iLast ) + { + SMDS_FaceOfNodes tria ( nodes[ iQ*( iCommon )], + nodes[ iQ*((iLast-1)%nbNodes)], + nodes[ iQ*((iLast )%nbNodes)]); + badness += getBadRate( &tria, aspectRatio ); + } + badness2iCommon.insert( make_pair( badness, iCommon )); + } + // use iCommon with lowest badness + iCommon = badness2iCommon.begin()->second; + } + if ( iCommon >= nbNodes ) + iCommon = 0; // something wrong + + // fill connectivity of tetrahedra based on a current face + int nbTet = nbNodes - 2; + if ( is24TetMode && nbNodes > 3 && triaSplits.empty()) + { + int faceBaryCenInd; + if ( isHex27 ) + { + faceBaryCenInd = vol.GetCenterNodeIndex( iF ); + method._faceBaryNode[ iF ] = vol.GetNodes()[ faceBaryCenInd ]; + } + else + { + method._faceBaryNode[ iF ] = 0; + faceBaryCenInd = baryCenInd + method._faceBaryNode.size(); + } + nbTet = nbNodes; + for ( int i = 0; i < nbTet; ++i ) + { + int i1 = i, i2 = (i+1) % nbNodes; + if ( !vol.IsFaceExternal( iF )) swap( i1, i2 ); + connectivity[ connSize++ ] = nInd[ iQ * i1 ]; + connectivity[ connSize++ ] = nInd[ iQ * i2 ]; + connectivity[ connSize++ ] = faceBaryCenInd; + connectivity[ connSize++ ] = baryCenInd; + } + } + else + { + for ( int i = 0; i < nbTet; ++i ) + { + int i1 = (iCommon+1+i) % nbNodes, i2 = (iCommon+2+i) % nbNodes; + if ( !vol.IsFaceExternal( iF )) swap( i1, i2 ); + connectivity[ connSize++ ] = nInd[ iQ * iCommon ]; + connectivity[ connSize++ ] = nInd[ iQ * i1 ]; + connectivity[ connSize++ ] = nInd[ iQ * i2 ]; + connectivity[ connSize++ ] = baryCenInd; + } + } + method._nbTetra += nbTet; + + } // loop on volume faces + + connectivity[ connSize++ ] = -1; + + } // end of generic solution + + return method; + } + //================================================================================ + /*! + * \brief Check if there is a tetraherdon adjacent to the given element via this facet + */ + //================================================================================ + + bool TTriangleFacet::hasAdjacentTetra( const SMDS_MeshElement* elem ) const + { + // find the tetrahedron including the three nodes of facet + const SMDS_MeshNode* n1 = elem->GetNode(_n1); + const SMDS_MeshNode* n2 = elem->GetNode(_n2); + const SMDS_MeshNode* n3 = elem->GetNode(_n3); + SMDS_ElemIteratorPtr volIt1 = n1->GetInverseElementIterator(SMDSAbs_Volume); + while ( volIt1->more() ) + { + const SMDS_MeshElement* v = volIt1->next(); + SMDSAbs_EntityType type = v->GetEntityType(); + if ( type != SMDSEntity_Tetra && type != SMDSEntity_Quad_Tetra ) + continue; + if ( type == SMDSEntity_Quad_Tetra && v->GetNodeIndex( n1 ) > 3 ) + continue; // medium node not allowed + const int ind2 = v->GetNodeIndex( n2 ); + if ( ind2 < 0 || 3 < ind2 ) + continue; + const int ind3 = v->GetNodeIndex( n3 ); + if ( ind3 < 0 || 3 < ind3 ) + continue; + return true; + } + return false; + } + + //======================================================================= + /*! + * \brief A key of a face of volume + */ + //======================================================================= + + struct TVolumeFaceKey: pair< pair< int, int>, pair< int, int> > + { + TVolumeFaceKey( SMDS_VolumeTool& vol, int iF ) + { + TIDSortedNodeSet sortedNodes; + const int iQ = vol.Element()->IsQuadratic() ? 2 : 1; + int nbNodes = vol.NbFaceNodes( iF ); + const SMDS_MeshNode** fNodes = vol.GetFaceNodes( iF ); + for ( int i = 0; i < nbNodes; i += iQ ) + sortedNodes.insert( fNodes[i] ); + TIDSortedNodeSet::iterator n = sortedNodes.begin(); + first.first = (*(n++))->GetID(); + first.second = (*(n++))->GetID(); + second.first = (*(n++))->GetID(); + second.second = ( sortedNodes.size() > 3 ) ? (*(n++))->GetID() : 0; + } + }; +} // namespace + +//======================================================================= +//function : SplitVolumesIntoTetra +//purpose : Split volume elements into tetrahedra. +//======================================================================= + +void SMESH_MeshEditor::SplitVolumesIntoTetra (const TIDSortedElemSet & theElems, + const int theMethodFlags) +{ + // std-like iterator on coordinates of nodes of mesh element + typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > NXyzIterator; + NXyzIterator xyzEnd; + + SMDS_VolumeTool volTool; + SMESH_MesherHelper helper( *GetMesh()); + + SMESHDS_SubMesh* subMesh = 0;//GetMeshDS()->MeshElements(1); + SMESHDS_SubMesh* fSubMesh = 0;//subMesh; + + SMESH_SequenceOfElemPtr newNodes, newElems; + + // map face of volume to it's baricenrtic node + map< TVolumeFaceKey, const SMDS_MeshNode* > volFace2BaryNode; + double bc[3]; + + TIDSortedElemSet::const_iterator elem = theElems.begin(); + for ( ; elem != theElems.end(); ++elem ) + { + if ( (*elem)->GetType() != SMDSAbs_Volume ) + continue; + SMDSAbs_EntityType geomType = (*elem)->GetEntityType(); + if ( geomType == SMDSEntity_Tetra || geomType == SMDSEntity_Quad_Tetra ) + continue; + + if ( !volTool.Set( *elem, /*ignoreCentralNodes=*/false )) continue; // strange... + + TSplitMethod splitMethod = getSplitMethod( volTool, theMethodFlags ); + if ( splitMethod._nbTetra < 1 ) continue; + + // find submesh to add new tetras to + if ( !subMesh || !subMesh->Contains( *elem )) + { + int shapeID = FindShape( *elem ); + helper.SetSubShape( shapeID ); // helper will add tetras to the found submesh + subMesh = GetMeshDS()->MeshElements( shapeID ); + } + int iQ; + if ( (*elem)->IsQuadratic() ) + { + iQ = 2; + // add quadratic links to the helper + for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) + { + const SMDS_MeshNode** fNodes = volTool.GetFaceNodes( iF ); + int nbN = volTool.NbFaceNodes( iF ) - bool( volTool.GetCenterNodeIndex(iF) > 0 ); + for ( int iN = 0; iN < nbN; iN += iQ ) + helper.AddTLinkNode( fNodes[iN], fNodes[iN+2], fNodes[iN+1] ); + } + helper.SetIsQuadratic( true ); + } + else + { + iQ = 1; + helper.SetIsQuadratic( false ); + } + vector nodes( (*elem)->begin_nodes(), (*elem)->end_nodes() ); + helper.SetElementsOnShape( true ); + if ( splitMethod._baryNode ) + { + // make a node at barycenter + volTool.GetBaryCenter( bc[0], bc[1], bc[2] ); + SMDS_MeshNode* gcNode = helper.AddNode( bc[0], bc[1], bc[2] ); + nodes.push_back( gcNode ); + newNodes.Append( gcNode ); + } + if ( !splitMethod._faceBaryNode.empty() ) + { + // make or find baricentric nodes of faces + map::iterator iF_n = splitMethod._faceBaryNode.begin(); + for ( ; iF_n != splitMethod._faceBaryNode.end(); ++iF_n ) + { + map< TVolumeFaceKey, const SMDS_MeshNode* >::iterator f_n = + volFace2BaryNode.insert + ( make_pair( TVolumeFaceKey( volTool,iF_n->first ), iF_n->second )).first; + if ( !f_n->second ) + { + volTool.GetFaceBaryCenter( iF_n->first, bc[0], bc[1], bc[2] ); + newNodes.Append( f_n->second = helper.AddNode( bc[0], bc[1], bc[2] )); + } + nodes.push_back( iF_n->second = f_n->second ); + } + } + + // make tetras + vector tetras( splitMethod._nbTetra ); // splits of a volume + const int* tetConn = splitMethod._connectivity; + for ( int i = 0; i < splitMethod._nbTetra; ++i, tetConn += 4 ) + newElems.Append( tetras[ i ] = helper.AddVolume( nodes[ tetConn[0] ], + nodes[ tetConn[1] ], + nodes[ tetConn[2] ], + nodes[ tetConn[3] ])); + + ReplaceElemInGroups( *elem, tetras, GetMeshDS() ); + + // Split faces on sides of the split volume + + const SMDS_MeshNode** volNodes = volTool.GetNodes(); + for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) + { + const int nbNodes = volTool.NbFaceNodes( iF ) / iQ; + if ( nbNodes < 4 ) continue; + + // find an existing face + vector fNodes( volTool.GetFaceNodes( iF ), + volTool.GetFaceNodes( iF ) + volTool.NbFaceNodes( iF )); + while ( const SMDS_MeshElement* face = GetMeshDS()->FindElement( fNodes, SMDSAbs_Face, + /*noMedium=*/false)) + { + // make triangles + helper.SetElementsOnShape( false ); + vector< const SMDS_MeshElement* > triangles; + + // find submesh to add new triangles in + if ( !fSubMesh || !fSubMesh->Contains( face )) + { + int shapeID = FindShape( face ); + fSubMesh = GetMeshDS()->MeshElements( shapeID ); + } + map::iterator iF_n = splitMethod._faceBaryNode.find(iF); + if ( iF_n != splitMethod._faceBaryNode.end() ) + { + for ( int iN = 0; iN < nbNodes*iQ; iN += iQ ) + { + const SMDS_MeshNode* n1 = fNodes[iN]; + const SMDS_MeshNode *n2 = fNodes[(iN+iQ)%(nbNodes*iQ)]; + const SMDS_MeshNode *n3 = iF_n->second; + if ( !volTool.IsFaceExternal( iF )) + swap( n2, n3 ); + triangles.push_back( helper.AddFace( n1,n2,n3 )); + + if ( fSubMesh && n3->getshapeId() < 1 ) + fSubMesh->AddNode( n3 ); + } + } + else + { + // among possible triangles create ones discribed by split method + const int* nInd = volTool.GetFaceNodesIndices( iF ); + int nbVariants = ( nbNodes == 4 ? 2 : nbNodes ); + int iCom = 0; // common node of triangle faces to split into + list< TTriangleFacet > facets; + for ( int iVar = 0; iVar < nbVariants; ++iVar, ++iCom ) + { + TTriangleFacet t012( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+1)%nbNodes )], + nInd[ iQ * ( (iCom+2)%nbNodes )]); + TTriangleFacet t023( nInd[ iQ * ( iCom )], + nInd[ iQ * ( (iCom+2)%nbNodes )], + nInd[ iQ * ( (iCom+3)%nbNodes )]); + if ( splitMethod.hasFacet( t012 ) && splitMethod.hasFacet( t023 )) + { + facets.push_back( t012 ); + facets.push_back( t023 ); + for ( int iLast = iCom+4; iLast < iCom+nbNodes; ++iLast ) + facets.push_back( TTriangleFacet( nInd[ iQ * ( iCom )], + nInd[ iQ * ((iLast-1)%nbNodes )], + nInd[ iQ * ((iLast )%nbNodes )])); + break; + } + } + list< TTriangleFacet >::iterator facet = facets.begin(); + for ( ; facet != facets.end(); ++facet ) + { + if ( !volTool.IsFaceExternal( iF )) + swap( facet->_n2, facet->_n3 ); + triangles.push_back( helper.AddFace( volNodes[ facet->_n1 ], + volNodes[ facet->_n2 ], + volNodes[ facet->_n3 ])); + } + } + for ( int i = 0; i < triangles.size(); ++i ) + { + if ( !triangles[i] ) continue; + if ( fSubMesh ) + fSubMesh->AddElement( triangles[i]); + newElems.Append( triangles[i] ); + } + ReplaceElemInGroups( face, triangles, GetMeshDS() ); + GetMeshDS()->RemoveFreeElement( face, fSubMesh, /*fromGroups=*/false ); + } + + } // loop on volume faces to split them into triangles + + GetMeshDS()->RemoveFreeElement( *elem, subMesh, /*fromGroups=*/false ); + + if ( geomType == SMDSEntity_TriQuad_Hexa ) + { + // remove medium nodes that could become free + for ( int i = 20; i < volTool.NbNodes(); ++i ) + if ( volNodes[i]->NbInverseElements() == 0 ) + GetMeshDS()->RemoveNode( volNodes[i] ); + } + } // loop on volumes to split + + myLastCreatedNodes = newNodes; + myLastCreatedElems = newElems; +} + +//======================================================================= +//function : AddToSameGroups +//purpose : add elemToAdd to the groups the elemInGroups belongs to +//======================================================================= + +void SMESH_MeshEditor::AddToSameGroups (const SMDS_MeshElement* elemToAdd, + const SMDS_MeshElement* elemInGroups, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->Contains( elemInGroups )) + group->SMDSGroup().Add( elemToAdd ); + } + } +} + + +//======================================================================= +//function : RemoveElemFromGroups +//purpose : Remove removeelem to the groups the elemInGroups belongs to +//======================================================================= +void SMESH_MeshEditor::RemoveElemFromGroups (const SMDS_MeshElement* removeelem, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) + { + set::const_iterator GrIt = groups.begin(); + for (; GrIt != groups.end(); GrIt++) + { + SMESHDS_Group* grp = dynamic_cast(*GrIt); + if (!grp || grp->IsEmpty()) continue; + grp->SMDSGroup().Remove(removeelem); + } + } +} + +//================================================================================ +/*! + * \brief Replace elemToRm by elemToAdd in the all groups + */ +//================================================================================ + +void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, + const SMDS_MeshElement* elemToAdd, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->SMDSGroup().Remove( elemToRm ) && elemToAdd ) + group->SMDSGroup().Add( elemToAdd ); + } + } +} + +//================================================================================ +/*! + * \brief Replace elemToRm by elemToAdd in the all groups + */ +//================================================================================ + +void SMESH_MeshEditor::ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, + const vector& elemToAdd, + SMESHDS_Mesh * aMesh) +{ + const set& groups = aMesh->GetGroups(); + if (!groups.empty()) + { + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); grIt++ ) { + SMESHDS_Group* group = dynamic_cast( *grIt ); + if ( group && group->SMDSGroup().Remove( elemToRm ) ) + for ( int i = 0; i < elemToAdd.size(); ++i ) + group->SMDSGroup().Add( elemToAdd[ i ] ); + } + } +} + +//======================================================================= +//function : QuadToTri +//purpose : Cut quadrangles into triangles. +// theCrit is used to select a diagonal to cut +//======================================================================= + +bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, + const bool the13Diag) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + MESSAGE( "::QuadToTri()" ); + + SMESHDS_Mesh * aMesh = GetMeshDS(); + + Handle(Geom_Surface) surface; + SMESH_MesherHelper helper( *GetMesh() ); TIDSortedElemSet::iterator itElem; for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { @@ -1240,20 +2052,28 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, aNodes[ i++ ] = static_cast( itN->next() ); int aShapeId = FindShape( elem ); - const SMDS_MeshElement* newElem = 0; + const SMDS_MeshElement* newElem1 = 0; + const SMDS_MeshElement* newElem2 = 0; if ( the13Diag ) { - aMesh->ChangeElementNodes( elem, aNodes, 3 ); - newElem = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); + newElem1 = aMesh->AddFace( aNodes[2], aNodes[0], aNodes[1] ); + newElem2 = aMesh->AddFace( aNodes[2], aNodes[3], aNodes[0] ); } else { - aMesh->ChangeElementNodes( elem, &aNodes[1], 3 ); - newElem = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + newElem1 = aMesh->AddFace( aNodes[3], aNodes[0], aNodes[1] ); + newElem2 = aMesh->AddFace( aNodes[3], aNodes[1], aNodes[2] ); } - myLastCreatedElems.Append(newElem); + myLastCreatedElems.Append(newElem1); + myLastCreatedElems.Append(newElem2); // put a new triangle on the same shape and add to the same groups if ( aShapeId ) - aMesh->SetMeshElementOnShape( newElem, aShapeId ); - AddToSameGroups( newElem, elem, aMesh ); + { + aMesh->SetMeshElementOnShape( newElem1, aShapeId ); + aMesh->SetMeshElementOnShape( newElem2, aShapeId ); + } + AddToSameGroups( newElem1, elem, aMesh ); + AddToSameGroups( newElem2, elem, aMesh ); + //aMesh->RemoveFreeElement(elem, aMesh->MeshElements(aShapeId), true); + aMesh->RemoveElement( elem ); } // Quadratic quadrangle @@ -1308,34 +2128,31 @@ bool SMESH_MeshEditor::QuadToTri (TIDSortedElemSet & theElems, myLastCreatedNodes.Append(newN); // create a new element - const SMDS_MeshElement* newElem = 0; - const SMDS_MeshNode* N[6]; + const SMDS_MeshElement* newElem1 = 0; + const SMDS_MeshElement* newElem2 = 0; if ( the13Diag ) { - N[0] = aNodes[0]; - N[1] = aNodes[1]; - N[2] = aNodes[2]; - N[3] = aNodes[4]; - N[4] = aNodes[5]; - N[5] = newN; - newElem = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], - aNodes[6], aNodes[7], newN ); + newElem1 = aMesh->AddFace(aNodes[2], aNodes[3], aNodes[0], + aNodes[6], aNodes[7], newN ); + newElem2 = aMesh->AddFace(aNodes[2], aNodes[0], aNodes[1], + newN, aNodes[4], aNodes[5] ); } else { - N[0] = aNodes[1]; - N[1] = aNodes[2]; - N[2] = aNodes[3]; - N[3] = aNodes[5]; - N[4] = aNodes[6]; - N[5] = newN; - newElem = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], - aNodes[7], aNodes[4], newN ); + newElem1 = aMesh->AddFace(aNodes[3], aNodes[0], aNodes[1], + aNodes[7], aNodes[4], newN ); + newElem2 = aMesh->AddFace(aNodes[3], aNodes[1], aNodes[2], + newN, aNodes[5], aNodes[6] ); } - myLastCreatedElems.Append(newElem); - aMesh->ChangeElementNodes( elem, N, 6 ); + myLastCreatedElems.Append(newElem1); + myLastCreatedElems.Append(newElem2); // put a new triangle on the same shape and add to the same groups if ( aShapeId ) - aMesh->SetMeshElementOnShape( newElem, aShapeId ); - AddToSameGroups( newElem, elem, aMesh ); + { + aMesh->SetMeshElementOnShape( newElem1, aShapeId ); + aMesh->SetMeshElementOnShape( newElem2, aShapeId ); + } + AddToSameGroups( newElem1, elem, aMesh ); + AddToSameGroups( newElem2, elem, aMesh ); + aMesh->RemoveElement( elem ); } } @@ -1352,7 +2169,7 @@ double getAngle(const SMDS_MeshElement * tr1, const SMDS_MeshNode * n1, const SMDS_MeshNode * n2) { - double angle = 2*PI; // bad angle + double angle = 2. * M_PI; // bad angle // get normals SMESH::Controls::TSequenceOfXYZ P1, P2; @@ -1381,7 +2198,7 @@ double getAngle(const SMDS_MeshElement * tr1, int i = 0, iDiag = -1; while ( it->more()) { const SMDS_MeshElement *n = it->next(); - if ( n == n1 || n == n2 ) + if ( n == n1 || n == n2 ) { if ( iDiag < 0) iDiag = i; else { @@ -1391,6 +2208,7 @@ double getAngle(const SMDS_MeshElement * tr1, nFirst[ t ] = n; break; } + } i++; } } @@ -1407,7 +2225,7 @@ double getAngle(const SMDS_MeshElement * tr1, // and able to return nodes by that ID // ================================================= class LinkID_Gen { - public: +public: LinkID_Gen( const SMESHDS_Mesh* theMesh ) :myMesh( theMesh ), myMaxID( theMesh->MaxNodeID() + 1) @@ -1430,7 +2248,7 @@ class LinkID_Gen { return true; } - private: +private: LinkID_Gen(); const SMESHDS_Mesh* myMesh; long myMaxID; @@ -1631,16 +2449,17 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, mapEl_setLi.erase( tr2 ); mapLi_listEl.erase( *link12 ); if(tr1->NbNodes()==3) { - if( tr1->GetID() < tr2->GetID() ) { - aMesh->ChangeElementNodes( tr1, n12, 4 ); - myLastCreatedElems.Append(tr1); - aMesh->RemoveElement( tr2 ); - } - else { - aMesh->ChangeElementNodes( tr2, n12, 4 ); - myLastCreatedElems.Append(tr2); - aMesh->RemoveElement( tr1); - } + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace(n12[0], n12[1], n12[2], n12[3] ); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr2 ); } else { const SMDS_MeshNode* N1 [6]; @@ -1658,16 +2477,18 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, aNodes[5] = N2[5]; aNodes[6] = N2[3]; aNodes[7] = N1[5]; - if( tr1->GetID() < tr2->GetID() ) { - GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); - myLastCreatedElems.Append(tr1); - GetMeshDS()->RemoveElement( tr2 ); - } - else { - GetMeshDS()->ChangeElementNodes( tr2, aNodes, 8 ); - myLastCreatedElems.Append(tr2); - GetMeshDS()->RemoveElement( tr1 ); - } + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3], + aNodes[4], aNodes[5], aNodes[6], aNodes[7]); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr2 ); // remove middle node (9) GetMeshDS()->RemoveNode( N1[4] ); } @@ -1676,16 +2497,17 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, mapEl_setLi.erase( tr3 ); mapLi_listEl.erase( *link13 ); if(tr1->NbNodes()==3) { - if( tr1->GetID() < tr2->GetID() ) { - aMesh->ChangeElementNodes( tr1, n13, 4 ); - myLastCreatedElems.Append(tr1); - aMesh->RemoveElement( tr3 ); - } - else { - aMesh->ChangeElementNodes( tr3, n13, 4 ); - myLastCreatedElems.Append(tr3); - aMesh->RemoveElement( tr1 ); - } + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace(n13[0], n13[1], n13[2], n13[3] ); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr3 ); } else { const SMDS_MeshNode* N1 [6]; @@ -1703,16 +2525,18 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, aNodes[5] = N2[5]; aNodes[6] = N2[3]; aNodes[7] = N1[5]; - if( tr1->GetID() < tr2->GetID() ) { - GetMeshDS()->ChangeElementNodes( tr1, aNodes, 8 ); - myLastCreatedElems.Append(tr1); - GetMeshDS()->RemoveElement( tr3 ); - } - else { - GetMeshDS()->ChangeElementNodes( tr3, aNodes, 8 ); - myLastCreatedElems.Append(tr3); - GetMeshDS()->RemoveElement( tr1 ); - } + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddFace(aNodes[0], aNodes[1], aNodes[2], aNodes[3], + aNodes[4], aNodes[5], aNodes[6], aNodes[7]); + myLastCreatedElems.Append(newElem); + AddToSameGroups( newElem, tr1, aMesh ); + int aShapeId = tr1->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + aMesh->RemoveElement( tr1 ); + aMesh->RemoveElement( tr3 ); // remove middle node (9) GetMeshDS()->RemoveNode( N1[4] ); } @@ -1739,15 +2563,15 @@ bool SMESH_MeshEditor::TriToQuad (TIDSortedElemSet & theElems, //============================================================================= static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] ) { - if ( i1 == i2 ) - return; - int tmp = idNodes[ i1 ]; - idNodes[ i1 ] = idNodes[ i2 ]; - idNodes[ i2 ] = tmp; - gp_Pnt Ptmp = P[ i1 ]; - P[ i1 ] = P[ i2 ]; - P[ i2 ] = Ptmp; - DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")"); +if ( i1 == i2 ) +return; +int tmp = idNodes[ i1 ]; +idNodes[ i1 ] = idNodes[ i2 ]; +idNodes[ i2 ] = tmp; +gp_Pnt Ptmp = P[ i1 ]; +P[ i1 ] = P[ i2 ]; +P[ i2 ] = Ptmp; +DUMPSO( i1 << "(" << idNodes[ i2 ] << ") <-> " << i2 << "(" << idNodes[ i1 ] << ")"); } //======================================================================= @@ -1758,7 +2582,7 @@ static void swap( int i1, int i2, int idNodes[], gp_Pnt P[] ) //======================================================================= int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh, - int idNodes[] ) +int idNodes[] ) { gp_Pnt P[4]; int i; @@ -1787,10 +2611,10 @@ int SMESH_MeshEditor::SortQuadNodes (const SMDS_Mesh * theMesh, i = 1; swap ( i, i + 1, idNodes, P ); -// for ( int ii = 0; ii < 4; ii++ ) { -// const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); -// DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); -// } + // for ( int ii = 0; ii < 4; ii++ ) { + // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); + // DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); + // } } return i; } @@ -1906,7 +2730,7 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, faceNodes.insert( idNodes[ 2 ] ); faceNodes.insert( idNodes[ iMin ] ); DUMPSO( "loop " << iLoop2 << " id2 " << idNodes[ 1 ] << " id3 " << idNodes[ 2 ] - << " leastDist = " << leastDist); + << " leastDist = " << leastDist); if ( leastDist <= DBL_MIN ) break; } @@ -1944,11 +2768,11 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, P[ i ] = P[ i+1 ]; P[ i+1 ] = Ptmp; } -// else -// for ( int ii = 0; ii < 4; ii++ ) { -// const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); -// DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); -// } + // else + // for ( int ii = 0; ii < 4; ii++ ) { + // const SMDS_MeshNode *n = theMesh->FindNode( idNodes[ii] ); + // DUMPSO( ii << "(" << idNodes[ii] <<") : "<X()<<" "<Y()<<" "<Z()); + // } // Gravity center of the top and bottom faces gp_Pnt aGCb = ( P[0].XYZ() + P[1].XYZ() + P[2].XYZ() + P[3].XYZ() ) / 4.; @@ -2000,11 +2824,11 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, swap( 5, 7, idNodes, P ); } -// DUMPSO( "OUTPUT: ========================================"); -// for ( i = 0; i < 8; i++ ) { -// float *p = ugrid->GetPoint(idNodes[i]); -// DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]); -// } + // DUMPSO( "OUTPUT: ========================================"); + // for ( i = 0; i < 8; i++ ) { + // float *p = ugrid->GetPoint(idNodes[i]); + // DUMPSO( i << "(" << idNodes[i] << ") : " << p[0] << " " << p[1] << " " << p[2]); + // } return true; }*/ @@ -2012,11 +2836,11 @@ bool SMESH_MeshEditor::SortHexaNodes (const SMDS_Mesh * theMesh, //================================================================================ /*! * \brief Return nodes linked to the given one - * \param theNode - the node - * \param linkedNodes - the found nodes - * \param type - the type of elements to check - * - * Medium nodes are ignored + * \param theNode - the node + * \param linkedNodes - the found nodes + * \param type - the type of elements to check + * + * Medium nodes are ignored */ //================================================================================ @@ -2028,6 +2852,9 @@ void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode, while ( elemIt->more() ) { const SMDS_MeshElement* elem = elemIt->next(); + if(elem->GetType() == SMDSAbs_0DElement) + continue; + SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); if ( elem->GetType() == SMDSAbs_Volume ) { @@ -2050,8 +2877,8 @@ void SMESH_MeshEditor::GetLinkedNodes( const SMDS_MeshNode* theNode, iAfter = SMESH_MesherHelper::WrapIndex( iAfter, nb ); iBefore = SMESH_MesherHelper::WrapIndex( iBefore, nb ); } - linkedNodes.insert( elem->GetNode( iAfter )); - linkedNodes.insert( elem->GetNode( iBefore )); + linkedNodes.insert( elem->GetNodeWrap( iAfter )); + linkedNodes.insert( elem->GetNodeWrap( iBefore )); } } } @@ -2184,8 +3011,13 @@ static bool getClosestUV (Extrema_GenExtPS& projector, if ( projector.IsDone() ) { double u, v, minVal = DBL_MAX; for ( int i = projector.NbExt(); i > 0; i-- ) +#if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1 + if ( projector.SquareDistance( i ) < minVal ) { + minVal = projector.SquareDistance( i ); +#else if ( projector.Value( i ) < minVal ) { minVal = projector.Value( i ); +#endif projector.Point( i ).Parameter( u, v ); } result.SetCoord( u, v ); @@ -2260,7 +3092,7 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, Handle(Geom_Surface) surface; SMESHDS_SubMesh* faceSubMesh = 0; TopoDS_Face face; - double fToler2 = 0, vPeriod = 0., uPeriod = 0., f,l; + double fToler2 = 0, f,l; double u1 = 0, u2 = 0, v1 = 0, v2 = 0; bool isUPeriodic = false, isVPeriodic = false; if ( *fId ) { @@ -2271,10 +3103,10 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, fToler2 *= fToler2 * 10.; isUPeriodic = surface->IsUPeriodic(); if ( isUPeriodic ) - vPeriod = surface->UPeriod(); + surface->UPeriod(); isVPeriodic = surface->IsVPeriodic(); if ( isVPeriodic ) - uPeriod = surface->VPeriod(); + surface->VPeriod(); surface->Bounds( u1, u2, v1, v2 ); } // --------------------------------------------------------- @@ -2296,14 +3128,14 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, } int nbElemOnFace = 0; itElem = theElems.begin(); - // loop on not yet smoothed elements: look for elems on a face + // loop on not yet smoothed elements: look for elems on a face while ( itElem != theElems.end() ) { if ( faceSubMesh && nbElemOnFace == faceSubMesh->NbElements() ) break; // all elements found const SMDS_MeshElement* elem = *itElem; if ( !elem || elem->GetType() != SMDSAbs_Face || elem->NbNodes() < 3 || - ( faceSubMesh && !faceSubMesh->Contains( elem ))) { + ( faceSubMesh && !faceSubMesh->Contains( elem ))) { ++itElem; continue; } @@ -2324,7 +3156,7 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, while ( nn++ < nbn ) { node = static_cast( itN->next() ); const SMDS_PositionPtr& pos = node->GetPosition(); - posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE; + posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE; if (posType != SMDS_TOP_EDGE && posType != SMDS_TOP_VERTEX && theFixedNodes.find( node ) == theFixedNodes.end()) @@ -2362,19 +3194,19 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, if ( uvMap.find( node ) == uvMap.end() ) uvCheckNodes.push_back( node ); // add nodes of elems sharing node -// SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face); -// while ( eIt->more() ) { -// const SMDS_MeshElement* e = eIt->next(); -// if ( e != elem ) { -// SMDS_ElemIteratorPtr nIt = e->nodesIterator(); -// while ( nIt->more() ) { -// const SMDS_MeshNode* n = -// static_cast( nIt->next() ); -// if ( uvMap.find( n ) == uvMap.end() ) -// uvCheckNodes.push_back( n ); -// } -// } -// } + // SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Face); + // while ( eIt->more() ) { + // const SMDS_MeshElement* e = eIt->next(); + // if ( e != elem ) { + // SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + // while ( nIt->more() ) { + // const SMDS_MeshNode* n = + // static_cast( nIt->next() ); + // if ( uvMap.find( n ) == uvMap.end() ) + // uvCheckNodes.push_back( n ); + // } + // } + // } } // check UV on face list< const SMDS_MeshNode* >::iterator n = uvCheckNodes.begin(); @@ -2382,27 +3214,27 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, node = *n; gp_XY uv( 0, 0 ); const SMDS_PositionPtr& pos = node->GetPosition(); - posType = pos.get() ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE; + posType = pos ? pos->GetTypeOfPosition() : SMDS_TOP_3DSPACE; // get existing UV switch ( posType ) { case SMDS_TOP_FACE: { - SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos.get(); + SMDS_FacePosition* fPos = ( SMDS_FacePosition* ) pos; uv.SetCoord( fPos->GetUParameter(), fPos->GetVParameter() ); break; } case SMDS_TOP_EDGE: { - TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() ); + TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() ); Handle(Geom2d_Curve) pcurve; if ( !S.IsNull() && S.ShapeType() == TopAbs_EDGE ) pcurve = BRep_Tool::CurveOnSurface( TopoDS::Edge( S ), face, f,l ); if ( !pcurve.IsNull() ) { - double u = (( SMDS_EdgePosition* ) pos.get() )->GetUParameter(); + double u = (( SMDS_EdgePosition* ) pos )->GetUParameter(); uv = pcurve->Value( u ).XY(); } break; } case SMDS_TOP_VERTEX: { - TopoDS_Shape S = aMesh->IndexToShape( pos->GetShapeId() ); + TopoDS_Shape S = aMesh->IndexToShape( node->getshapeId() ); if ( !S.IsNull() && S.ShapeType() == TopAbs_VERTEX ) uv = BRep_Tool::Parameters( TopoDS::Vertex( S ), face ).XY(); break; @@ -2446,35 +3278,27 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, // fix nodes on mesh boundary if ( checkBoundaryNodes ) { - map< NLink, int > linkNbMap; // how many times a link encounters in elemsOnFace - map< NLink, int >::iterator link_nb; + map< SMESH_TLink, int > linkNbMap; // how many times a link encounters in elemsOnFace + map< SMESH_TLink, int >::iterator link_nb; // put all elements links to linkNbMap list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin(); for ( ; elemIt != elemsOnFace.end(); ++elemIt ) { const SMDS_MeshElement* elem = (*elemIt); - int nbn = elem->NbNodes(); - if(elem->IsQuadratic()) - nbn = nbn/2; + int nbn = elem->NbCornerNodes(); // loop on elem links: insert them in linkNbMap - const SMDS_MeshNode* curNode, *prevNode = elem->GetNode( nbn ); for ( int iN = 0; iN < nbn; ++iN ) { - curNode = elem->GetNode( iN ); - NLink link; - if ( curNode < prevNode ) link = make_pair( curNode , prevNode ); - else link = make_pair( prevNode , curNode ); - prevNode = curNode; - link_nb = linkNbMap.find( link ); - if ( link_nb == linkNbMap.end() ) - linkNbMap.insert( make_pair ( link, 1 )); - else - link_nb->second++; + const SMDS_MeshNode* n1 = elem->GetNode( iN ); + const SMDS_MeshNode* n2 = elem->GetNode(( iN+1 ) % nbn); + SMESH_TLink link( n1, n2 ); + link_nb = linkNbMap.insert( make_pair( link, 0 )).first; + link_nb->second++; } } // remove nodes that are in links encountered only once from setMovableNodes for ( link_nb = linkNbMap.begin(); link_nb != linkNbMap.end(); ++link_nb ) { if ( link_nb->second == 1 ) { - setMovableNodes.erase( link_nb->first.first ); - setMovableNodes.erase( link_nb->first.second ); + setMovableNodes.erase( link_nb->first.node1() ); + setMovableNodes.erase( link_nb->first.node2() ); } } } @@ -2665,7 +3489,7 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, if ( node_uv != uvMap.end() ) { gp_XY* uv = node_uv->second; node->SetPosition - ( SMDS_PositionPtr( new SMDS_FacePosition( *fId, uv->X(), uv->Y() ))); + ( SMDS_PositionPtr( new SMDS_FacePosition( uv->X(), uv->Y() ))); } } @@ -2677,14 +3501,14 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, helper.SetSubShape( face ); list< const SMDS_MeshElement* >::iterator elemIt = elemsOnFace.begin(); for ( ; elemIt != elemsOnFace.end(); ++elemIt ) { - const SMDS_QuadraticFaceOfNodes* QF = - dynamic_cast (*elemIt); - if(QF) { + const SMDS_VtkFace* QF = + dynamic_cast (*elemIt); + if(QF && QF->IsQuadratic()) { vector Ns; Ns.reserve(QF->NbNodes()+1); - SMDS_NodeIteratorPtr anIter = QF->interlacedNodesIterator(); + SMDS_ElemIteratorPtr anIter = QF->interlacedNodesElemIterator(); while ( anIter->more() ) - Ns.push_back( anIter->next() ); + Ns.push_back( cast2Node(anIter->next()) ); Ns.push_back( Ns[0] ); double x, y, z; for(int i=0; iNbNodes(); i=i+2) { @@ -2719,30 +3543,22 @@ void SMESH_MeshEditor::Smooth (TIDSortedElemSet & theElems, //function : isReverse //purpose : Return true if normal of prevNodes is not co-directied with // gp_Vec(prevNodes[iNotSame],nextNodes[iNotSame]). -// iNotSame is where prevNodes and nextNodes are different +// iNotSame is where prevNodes and nextNodes are different. +// If result is true then future volume orientation is OK //======================================================================= -static bool isReverse(vector prevNodes, - vector nextNodes, - const int nbNodes, - const int iNotSame) +static bool isReverse(const SMDS_MeshElement* face, + const vector& prevNodes, + const vector& nextNodes, + const int iNotSame) { - int iBeforeNotSame = ( iNotSame == 0 ? nbNodes - 1 : iNotSame - 1 ); - int iAfterNotSame = ( iNotSame + 1 == nbNodes ? 0 : iNotSame + 1 ); - - const SMDS_MeshNode* nB = prevNodes[ iBeforeNotSame ]; - const SMDS_MeshNode* nA = prevNodes[ iAfterNotSame ]; - const SMDS_MeshNode* nP = prevNodes[ iNotSame ]; - const SMDS_MeshNode* nN = nextNodes[ iNotSame ]; - gp_Pnt pB ( nB->X(), nB->Y(), nB->Z() ); - gp_Pnt pA ( nA->X(), nA->Y(), nA->Z() ); - gp_Pnt pP ( nP->X(), nP->Y(), nP->Z() ); - gp_Pnt pN ( nN->X(), nN->Y(), nN->Z() ); + SMESH_TNodeXYZ pP = prevNodes[ iNotSame ]; + SMESH_TNodeXYZ pN = nextNodes[ iNotSame ]; + gp_XYZ extrDir( pN - pP ), faceNorm; + SMESH_Algo::FaceNormal( face, faceNorm, /*normalized=*/false ); - gp_Vec vB ( pP, pB ), vA ( pP, pA ), vN ( pP, pN ); - - return (vA ^ vB) * vN < 0.0; + return faceNorm * extrDir < 0.0; } //======================================================================= @@ -2762,296 +3578,413 @@ void SMESH_MeshEditor::sweepElement(const SMDS_MeshElement* elem, const int nbSteps, SMESH_SequenceOfElemPtr& srcElements) { + //MESSAGE("sweepElement " << nbSteps); SMESHDS_Mesh* aMesh = GetMeshDS(); + const int nbNodes = elem->NbNodes(); + const int nbCorners = elem->NbCornerNodes(); + SMDSAbs_EntityType baseType = elem->GetEntityType(); /* it can change in case of + polyhedron creation !!! */ // Loop on elem nodes: // find new nodes and detect same nodes indices - int nbNodes = elem->NbNodes(); vector < list< const SMDS_MeshNode* >::const_iterator > itNN( nbNodes ); vector prevNod( nbNodes ); vector nextNod( nbNodes ); vector midlNod( nbNodes ); - int iNode, nbSame = 0, iNotSameNode = 0, iSameNode = 0; + int iNode, nbSame = 0, nbDouble = 0, iNotSameNode = 0; vector sames(nbNodes); - - //bool issimple[nbNodes]; - vector issimple(nbNodes); + vector isSingleNode(nbNodes); for ( iNode = 0; iNode < nbNodes; iNode++ ) { - TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ]; - const SMDS_MeshNode* node = nnIt->first; + TNodeOfNodeListMapItr nnIt = newNodesItVec[ iNode ]; + const SMDS_MeshNode* node = nnIt->first; const list< const SMDS_MeshNode* > & listNewNodes = nnIt->second; if ( listNewNodes.empty() ) return; - if(listNewNodes.size()==nbSteps) { - issimple[iNode] = true; - } - else { - issimple[iNode] = false; - } - - itNN[ iNode ] = listNewNodes.begin(); + itNN [ iNode ] = listNewNodes.begin(); prevNod[ iNode ] = node; nextNod[ iNode ] = listNewNodes.front(); -//cout<<"iNode="<GetID() ); return; } -// if( elem->IsQuadratic() && nbSame>0 ) { -// MESSAGE( "Can not rotate quadratic element " << elem->GetID() ); -// return; -// } - - int iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0; - if ( nbSame > 0 ) { - iBeforeSame = ( iSameNode == 0 ? nbNodes - 1 : iSameNode - 1 ); - iAfterSame = ( iSameNode + 1 == nbNodes ? 0 : iSameNode + 1 ); - iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 ); + if ( elem->GetType() == SMDSAbs_Face && !isReverse( elem, prevNod, nextNod, iNotSameNode )) + { + // fix nodes order to have bottom normal external + if ( baseType == SMDSEntity_Polygon ) + { + std::reverse( itNN.begin(), itNN.end() ); + std::reverse( prevNod.begin(), prevNod.end() ); + std::reverse( midlNod.begin(), midlNod.end() ); + std::reverse( nextNod.begin(), nextNod.end() ); + std::reverse( isSingleNode.begin(), isSingleNode.end() ); + } + else + { + const vector& ind = SMDS_MeshCell::reverseSmdsOrder( baseType ); + SMDS_MeshCell::applyInterlace( ind, itNN ); + SMDS_MeshCell::applyInterlace( ind, prevNod ); + SMDS_MeshCell::applyInterlace( ind, nextNod ); + SMDS_MeshCell::applyInterlace( ind, midlNod ); + SMDS_MeshCell::applyInterlace( ind, isSingleNode ); + if ( nbSame > 0 ) + { + sames[nbSame] = iNotSameNode; + for ( int j = 0; j <= nbSame; ++j ) + for ( size_t i = 0; i < ind.size(); ++i ) + if ( ind[i] == sames[j] ) + { + sames[j] = i; + break; + } + iNotSameNode = sames[nbSame]; + } + } } -//if(nbNodes==8) -//cout<<" prevNod[0]="<< prevNod[0]<<" prevNod[1]="<< prevNod[1] -// <<" prevNod[2]="<< prevNod[2]<<" prevNod[3]="<< prevNod[4] -// <<" prevNod[4]="<< prevNod[4]<<" prevNod[5]="<< prevNod[5] -// <<" prevNod[6]="<< prevNod[6]<<" prevNod[7]="<< prevNod[7]< 2 && !isReverse( prevNod, nextNod, nbNodes, iNotSameNode )) { - //MESSAGE("Reversed elem " << elem ); - i0 = 2; - i2 = 0; - if ( nbSame > 0 ) { - int iAB = iAfterSame + iBeforeSame; - iBeforeSame = iAB - iBeforeSame; - iAfterSame = iAB - iAfterSame; - } + int iSameNode = 0, iBeforeSame = 0, iAfterSame = 0, iOpposSame = 0; + if ( nbSame > 0 ) { + iSameNode = sames[ nbSame-1 ]; + iBeforeSame = ( iSameNode + nbCorners - 1 ) % nbCorners; + iAfterSame = ( iSameNode + 1 ) % nbCorners; + iOpposSame = ( iSameNode - 2 < 0 ? iSameNode + 2 : iSameNode - 2 ); } // make new elements - for (int iStep = 0; iStep < nbSteps; iStep++ ) { + for (int iStep = 0; iStep < nbSteps; iStep++ ) + { // get next nodes - for ( iNode = 0; iNode < nbNodes; iNode++ ) { - if(issimple[iNode]) { - nextNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - } - else { - if( elem->GetType()==SMDSAbs_Node ) { - // we have to use two nodes - midlNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - nextNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - } - else if(!elem->IsQuadratic() || - elem->IsQuadratic() && elem->IsMediumNode(prevNod[iNode]) ) { - // we have to use each second node - itNN[ iNode ]++; - nextNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - } - else { - // we have to use two nodes - midlNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - nextNod[ iNode ] = *itNN[ iNode ]; - itNN[ iNode ]++; - } - } + for ( iNode = 0; iNode < nbNodes; iNode++ ) + { + midlNod[ iNode ] = isSingleNode[iNode] ? 0 : *itNN[ iNode ]++; + nextNod[ iNode ] = *itNN[ iNode ]++; } + SMDS_MeshElement* aNewElem = 0; - if(!elem->IsPoly()) { - switch ( nbNodes ) { - case 0: - return; - case 1: { // NODE + /*if(!elem->IsPoly())*/ { + switch ( baseType ) { + case SMDSEntity_0D: + case SMDSEntity_Node: { // sweep NODE if ( nbSame == 0 ) { - if(issimple[0]) + if ( isSingleNode[0] ) aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ] ); else aNewElem = aMesh->AddEdge( prevNod[ 0 ], nextNod[ 0 ], midlNod[ 0 ] ); } + else + return; break; } - case 2: { // EDGE - if ( nbSame == 0 ) - aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], - nextNod[ 1 ], nextNod[ 0 ] ); - else - aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], - nextNod[ iNotSameNode ] ); + case SMDSEntity_Edge: { // sweep EDGE + if ( nbDouble == 0 ) + { + if ( nbSame == 0 ) // ---> quadrangle + aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], + nextNod[ 1 ], nextNod[ 0 ] ); + else // ---> triangle + aNewElem = aMesh->AddFace(prevNod[ 0 ], prevNod[ 1 ], + nextNod[ iNotSameNode ] ); + } + else // ---> polygon + { + vector poly_nodes; + poly_nodes.push_back( prevNod[0] ); + poly_nodes.push_back( prevNod[1] ); + if ( prevNod[1] != nextNod[1] ) + { + if ( midlNod[1]) poly_nodes.push_back( midlNod[1]); + poly_nodes.push_back( nextNod[1] ); + } + if ( prevNod[0] != nextNod[0] ) + { + poly_nodes.push_back( nextNod[0] ); + if ( midlNod[0]) poly_nodes.push_back( midlNod[0]); + } + switch ( poly_nodes.size() ) { + case 3: + aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], poly_nodes[ 2 ]); + break; + case 4: + aNewElem = aMesh->AddFace( poly_nodes[ 0 ], poly_nodes[ 1 ], + poly_nodes[ 2 ], poly_nodes[ 3 ]); + break; + default: + aNewElem = aMesh->AddPolygonalFace (poly_nodes); + } + } break; } - - case 3: { // TRIANGLE or quadratic edge - if(elem->GetType() == SMDSAbs_Face) { // TRIANGLE - - if ( nbSame == 0 ) // --- pentahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], - nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ] ); - - else if ( nbSame == 1 ) // --- pyramid - aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], - nextNod[ iAfterSame ], nextNod[ iBeforeSame ], + case SMDSEntity_Triangle: // TRIANGLE ---> + { + if ( nbDouble > 0 ) break; + if ( nbSame == 0 ) // ---> pentahedron + aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], + nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ] ); + + else if ( nbSame == 1 ) // ---> pyramid + aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], + nextNod[ iAfterSame ], nextNod[ iBeforeSame ], nextNod[ iSameNode ]); - else // 2 same nodes: --- tetrahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], + else // 2 same nodes: ---> tetrahedron + aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], nextNod[ iNotSameNode ]); + break; } - else { // quadratic edge - if(nbSame==0) { // quadratic quadrangle - aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], nextNod[1], prevNod[1], - midlNod[0], nextNod[2], midlNod[1], prevNod[2]); - } - else if(nbSame==1) { // quadratic triangle - if(sames[0]==2) - return; // medium node on axis - else if(sames[0]==0) { - aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1], - nextNod[2], midlNod[1], prevNod[2]); + case SMDSEntity_Quad_Edge: // sweep quadratic EDGE ---> + { + if ( nbSame == 2 ) + return; + if ( nbDouble+nbSame == 2 ) + { + if(nbSame==0) { // ---> quadratic quadrangle + aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0], + prevNod[2], midlNod[1], nextNod[2], midlNod[0]); + } + else { //(nbSame==1) // ---> quadratic triangle + if(sames[0]==2) { + return; // medium node on axis + } + else if(sames[0]==0) + aNewElem = aMesh->AddFace(prevNod[0], nextNod[1], prevNod[1], + nextNod[2], midlNod[1], prevNod[2]); + else // sames[0]==1 + aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1], + midlNod[0], nextNod[2], prevNod[2]); } - else { // sames[0]==1 - aNewElem = aMesh->AddFace(prevNod[0], nextNod[0], prevNod[1], - midlNod[0], nextNod[2], prevNod[2]); + } + else if ( nbDouble == 3 ) + { + if ( nbSame == 0 ) { // ---> bi-quadratic quadrangle + aNewElem = aMesh->AddFace(prevNod[0], prevNod[1], nextNod[1], nextNod[0], + prevNod[2], midlNod[1], nextNod[2], midlNod[0], midlNod[2]); } } else return; + break; } - break; - } - case 4: { // QUADRANGLE + case SMDSEntity_Quadrangle: { // sweep QUADRANGLE ---> + if ( nbDouble > 0 ) break; - if ( nbSame == 0 ) // --- hexahedron - aNewElem = aMesh->AddVolume (prevNod[ i0 ], prevNod[ 1 ], prevNod[ i2 ], prevNod[ 3 ], - nextNod[ i0 ], nextNod[ 1 ], nextNod[ i2 ], nextNod[ 3 ]); + if ( nbSame == 0 ) // ---> hexahedron + aNewElem = aMesh->AddVolume (prevNod[ 0 ], prevNod[ 1 ], prevNod[ 2 ], prevNod[ 3 ], + nextNod[ 0 ], nextNod[ 1 ], nextNod[ 2 ], nextNod[ 3 ]); - else if ( nbSame == 1 ) { // --- pyramid + pentahedron - aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], - nextNod[ iAfterSame ], nextNod[ iBeforeSame ], + else if ( nbSame == 1 ) { // ---> pyramid + pentahedron + aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iAfterSame ], + nextNod[ iAfterSame ], nextNod[ iBeforeSame ], nextNod[ iSameNode ]); newElems.push_back( aNewElem ); - aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ], - prevNod[ iBeforeSame ], nextNod[ iAfterSame ], + aNewElem = aMesh->AddVolume (prevNod[ iAfterSame ], prevNod[ iOpposSame ], + prevNod[ iBeforeSame ], nextNod[ iAfterSame ], nextNod[ iOpposSame ], nextNod[ iBeforeSame ] ); } - else if ( nbSame == 2 ) { // pentahedron + else if ( nbSame == 2 ) { // ---> pentahedron if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) // iBeforeSame is same too aNewElem = aMesh->AddVolume (prevNod[ iBeforeSame ], prevNod[ iOpposSame ], - nextNod[ iOpposSame ], prevNod[ iSameNode ], + nextNod[ iOpposSame ], prevNod[ iSameNode ], prevNod[ iAfterSame ], nextNod[ iAfterSame ]); else // iAfterSame is same too - aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ], + aNewElem = aMesh->AddVolume (prevNod[ iSameNode ], prevNod[ iBeforeSame ], nextNod[ iBeforeSame ], prevNod[ iAfterSame ], prevNod[ iOpposSame ], nextNod[ iOpposSame ]); } break; } - case 6: { // quadratic triangle - // create pentahedron with 15 nodes - if(i0>0) { // reversed case - aNewElem = aMesh->AddVolume (prevNod[0], prevNod[2], prevNod[1], - nextNod[0], nextNod[2], nextNod[1], - prevNod[5], prevNod[4], prevNod[3], - nextNod[5], nextNod[4], nextNod[3], - midlNod[0], midlNod[2], midlNod[1]); - } - else { // not reversed case + case SMDSEntity_Quad_Triangle: { // sweep Quadratic TRIANGLE ---> + if ( nbDouble+nbSame != 3 ) break; + if(nbSame==0) { + // ---> pentahedron with 15 nodes aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], nextNod[0], nextNod[1], nextNod[2], prevNod[3], prevNod[4], prevNod[5], nextNod[3], nextNod[4], nextNod[5], midlNod[0], midlNod[1], midlNod[2]); } + else if(nbSame==1) { + // ---> 2d order pyramid of 13 nodes + int apex = iSameNode; + int i0 = ( apex + 1 ) % nbCorners; + int i1 = ( apex - 1 + nbCorners ) % nbCorners; + int i0a = apex + 3; + int i1a = i1 + 3; + int i01 = i0 + 3; + aNewElem = aMesh->AddVolume(prevNod[i1], prevNod[i0], + nextNod[i0], nextNod[i1], prevNod[apex], + prevNod[i01], midlNod[i0], + nextNod[i01], midlNod[i1], + prevNod[i1a], prevNod[i0a], + nextNod[i0a], nextNod[i1a]); + } + else if(nbSame==2) { + // ---> 2d order tetrahedron of 10 nodes + int n1 = iNotSameNode; + int n2 = ( n1 + 1 ) % nbCorners; + int n3 = ( n1 + nbCorners - 1 ) % nbCorners; + int n12 = n1 + 3; + int n23 = n2 + 3; + int n31 = n3 + 3; + aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], prevNod[n3], nextNod[n1], + prevNod[n12], prevNod[n23], prevNod[n31], + midlNod[n1], nextNod[n12], nextNod[n31]); + } break; } - case 8: { // quadratic quadrangle - // create hexahedron with 20 nodes - if(i0>0) { // reversed case - aNewElem = aMesh->AddVolume (prevNod[0], prevNod[3], prevNod[2], prevNod[1], - nextNod[0], nextNod[3], nextNod[2], nextNod[1], - prevNod[7], prevNod[6], prevNod[5], prevNod[4], - nextNod[7], nextNod[6], nextNod[5], nextNod[4], - midlNod[0], midlNod[3], midlNod[2], midlNod[1]); - } - else { // not reversed case + case SMDSEntity_Quad_Quadrangle: { // sweep Quadratic QUADRANGLE ---> + if( nbSame == 0 ) { + if ( nbDouble != 4 ) break; + // ---> hexahedron with 20 nodes aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3], nextNod[0], nextNod[1], nextNod[2], nextNod[3], prevNod[4], prevNod[5], prevNod[6], prevNod[7], nextNod[4], nextNod[5], nextNod[6], nextNod[7], midlNod[0], midlNod[1], midlNod[2], midlNod[3]); } + else if(nbSame==1) { + // ---> pyramid + pentahedron - can not be created since it is needed + // additional middle node at the center of face + INFOS( " Sweep for face " << elem->GetID() << " can not be created" ); + return; + } + else if( nbSame == 2 ) { + if ( nbDouble != 2 ) break; + // ---> 2d order Pentahedron with 15 nodes + int n1,n2,n4,n5; + if ( prevNod[ iBeforeSame ] == nextNod[ iBeforeSame ] ) { + // iBeforeSame is same too + n1 = iBeforeSame; + n2 = iOpposSame; + n4 = iSameNode; + n5 = iAfterSame; + } + else { + // iAfterSame is same too + n1 = iSameNode; + n2 = iBeforeSame; + n4 = iAfterSame; + n5 = iOpposSame; + } + int n12 = n2 + 4; + int n45 = n4 + 4; + int n14 = n1 + 4; + int n25 = n5 + 4; + aNewElem = aMesh->AddVolume (prevNod[n1], prevNod[n2], nextNod[n2], + prevNod[n4], prevNod[n5], nextNod[n5], + prevNod[n12], midlNod[n2], nextNod[n12], + prevNod[n45], midlNod[n5], nextNod[n45], + prevNod[n14], prevNod[n25], nextNod[n25]); + } break; } - default: { - // realized for extrusion only - //vector polyedre_nodes (nbNodes*2 + 4*nbNodes); - //vector quantities (nbNodes + 2); - - //quantities[0] = nbNodes; // bottom of prism - //for (int inode = 0; inode < nbNodes; inode++) { - // polyedre_nodes[inode] = prevNod[inode]; - //} - - //quantities[1] = nbNodes; // top of prism - //for (int inode = 0; inode < nbNodes; inode++) { - // polyedre_nodes[nbNodes + inode] = nextNod[inode]; - //} - - //for (int iface = 0; iface < nbNodes; iface++) { - // quantities[iface + 2] = 4; - // int inextface = (iface == nbNodes - 1) ? 0 : iface + 1; - // polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface]; - // polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface]; - // polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface]; - // polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface]; - //} - //aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities); + case SMDSEntity_BiQuad_Quadrangle: { // sweep BiQuadratic QUADRANGLE ---> + + if( nbSame == 0 && nbDouble == 9 ) { + // ---> tri-quadratic hexahedron with 27 nodes + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], prevNod[3], + nextNod[0], nextNod[1], nextNod[2], nextNod[3], + prevNod[4], prevNod[5], prevNod[6], prevNod[7], + nextNod[4], nextNod[5], nextNod[6], nextNod[7], + midlNod[0], midlNod[1], midlNod[2], midlNod[3], + prevNod[8], // bottom center + midlNod[4], midlNod[5], midlNod[6], midlNod[7], + nextNod[8], // top center + midlNod[8]);// elem center + } + else + { + return; + } break; } - } - } - - if(!aNewElem) { - // realized for extrusion only - vector polyedre_nodes (nbNodes*2 + 4*nbNodes); - vector quantities (nbNodes + 2); + case SMDSEntity_Polygon: { // sweep POLYGON - quantities[0] = nbNodes; // bottom of prism - for (int inode = 0; inode < nbNodes; inode++) { - polyedre_nodes[inode] = prevNod[inode]; + if ( nbNodes == 6 && nbSame == 0 && nbDouble == 0 ) { + // ---> hexagonal prism + aNewElem = aMesh->AddVolume (prevNod[0], prevNod[1], prevNod[2], + prevNod[3], prevNod[4], prevNod[5], + nextNod[0], nextNod[1], nextNod[2], + nextNod[3], nextNod[4], nextNod[5]); + } + break; } + case SMDSEntity_Ball: + return; - quantities[1] = nbNodes; // top of prism - for (int inode = 0; inode < nbNodes; inode++) { - polyedre_nodes[nbNodes + inode] = nextNod[inode]; + default: + break; } + } - for (int iface = 0; iface < nbNodes; iface++) { - quantities[iface + 2] = 4; - int inextface = (iface == nbNodes - 1) ? 0 : iface + 1; - polyedre_nodes[2*nbNodes + 4*iface + 0] = prevNod[iface]; - polyedre_nodes[2*nbNodes + 4*iface + 1] = prevNod[inextface]; - polyedre_nodes[2*nbNodes + 4*iface + 2] = nextNod[inextface]; - polyedre_nodes[2*nbNodes + 4*iface + 3] = nextNod[iface]; + if ( !aNewElem && elem->GetType() == SMDSAbs_Face ) // try to create a polyherdal prism + { + if ( baseType != SMDSEntity_Polygon ) + { + const std::vector& ind = SMDS_MeshCell::interlacedSmdsOrder(baseType); + SMDS_MeshCell::applyInterlace( ind, prevNod ); + SMDS_MeshCell::applyInterlace( ind, nextNod ); + SMDS_MeshCell::applyInterlace( ind, midlNod ); + SMDS_MeshCell::applyInterlace( ind, itNN ); + SMDS_MeshCell::applyInterlace( ind, isSingleNode ); + baseType = SMDSEntity_Polygon; // WARNING: change baseType !!!! + } + vector polyedre_nodes (nbNodes*2 + 4*nbNodes); + vector quantities (nbNodes + 2); + polyedre_nodes.clear(); + quantities.clear(); + + // bottom of prism + for (int inode = 0; inode < nbNodes; inode++) + polyedre_nodes.push_back( prevNod[inode] ); + quantities.push_back( nbNodes ); + + // top of prism + polyedre_nodes.push_back( nextNod[0] ); + for (int inode = nbNodes; inode-1; --inode ) + polyedre_nodes.push_back( nextNod[inode-1] ); + quantities.push_back( nbNodes ); + + // side faces + for (int iface = 0; iface < nbNodes; iface++) + { + const int prevNbNodes = polyedre_nodes.size(); + int inextface = (iface+1) % nbNodes; + polyedre_nodes.push_back( prevNod[inextface] ); + polyedre_nodes.push_back( prevNod[iface] ); + if ( prevNod[iface] != nextNod[iface] ) + { + if ( midlNod[ iface ]) polyedre_nodes.push_back( midlNod[ iface ]); + polyedre_nodes.push_back( nextNod[iface] ); + } + if ( prevNod[inextface] != nextNod[inextface] ) + { + polyedre_nodes.push_back( nextNod[inextface] ); + if ( midlNod[ inextface ]) polyedre_nodes.push_back( midlNod[ inextface ]); + } + const int nbFaceNodes = polyedre_nodes.size() - prevNbNodes; + if ( nbFaceNodes > 2 ) + quantities.push_back( nbFaceNodes ); + else // degenerated face + polyedre_nodes.resize( prevNbNodes ); } aNewElem = aMesh->AddPolyhedralVolume (polyedre_nodes, quantities); } @@ -3094,7 +4027,8 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, // Find nodes belonging to only one initial element - sweep them to get edges. TNodeOfNodeListMapItr nList = mapNewNodes.begin(); - for ( ; nList != mapNewNodes.end(); nList++ ) { + for ( ; nList != mapNewNodes.end(); nList++ ) + { const SMDS_MeshNode* node = static_cast( nList->first ); SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(); @@ -3109,11 +4043,10 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, nbInitElems = 0; highType = type; } - if ( elemSet.find(el) != elemSet.end() ) - nbInitElems++; + nbInitElems += elemSet.count(el); } if ( nbInitElems < 2 ) { - bool NotCreateEdge = el && el->IsQuadratic() && el->IsMediumNode(node); + bool NotCreateEdge = el && el->IsMediumNode(node); if(!NotCreateEdge) { vector newNodesItVec( 1, nList ); list newEdges; @@ -3127,13 +4060,18 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, TElemOfElemListMap::iterator itElem = newElemsMap.begin(); TElemOfVecOfNnlmiMap::iterator itElemNodes = elemNewNodesMap.begin(); - for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) { + for ( ; itElem != newElemsMap.end(); itElem++, itElemNodes++ ) + { const SMDS_MeshElement* elem = itElem->first; vector& vecNewNodes = itElemNodes->second; + if(itElem->second.size()==0) continue; + + const bool isQuadratic = elem->IsQuadratic(); + if ( elem->GetType() == SMDSAbs_Edge ) { // create a ceiling edge - if (!elem->IsQuadratic()) { + if ( !isQuadratic ) { if ( !aMesh->FindEdge( vecNewNodes[ 0 ]->second.back(), vecNewNodes[ 1 ]->second.back())) { myLastCreatedElems.Append(aMesh->AddEdge(vecNewNodes[ 0 ]->second.back(), @@ -3155,8 +4093,6 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, if ( elem->GetType() != SMDSAbs_Face ) continue; - if(itElem->second.size()==0) continue; - bool hasFreeLinks = false; TIDSortedElemSet avoidSet; @@ -3164,7 +4100,7 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, set aFaceLastNodes; int iNode, nbNodes = vecNewNodes.size(); - if(!elem->IsQuadratic()) { + if ( !isQuadratic ) { // loop on the face nodes for ( iNode = 0; iNode < nbNodes; iNode++ ) { aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); @@ -3196,12 +4132,14 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, int iNext = ( iNode + 1 == nbn ) ? 0 : iNode + 1; const SMDS_MeshNode* n1 = vecNewNodes[ iNode ]->first; const SMDS_MeshNode* n2 = vecNewNodes[ iNext ]->first; + const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first; // check if a link is free - if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet )) { + if ( ! SMESH_MeshEditor::FindFaceInSet ( n1, n2, elemSet, avoidSet ) && + ! SMESH_MeshEditor::FindFaceInSet ( n1, n3, elemSet, avoidSet ) && + ! SMESH_MeshEditor::FindFaceInSet ( n3, n2, elemSet, avoidSet ) ) { hasFreeLinks = true; // make an edge and a ceiling for a new edge // find medium node - const SMDS_MeshNode* n3 = vecNewNodes[ iNode+nbn ]->first; if ( !aMesh->FindEdge( n1, n2, n3 )) { myLastCreatedElems.Append(aMesh->AddEdge( n1, n2, n3 )); // free link edge srcElements.Append( myLastCreatedElems.Last() ); @@ -3215,7 +4153,7 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, } } } - for ( iNode = nbn; iNode < 2*nbn; iNode++ ) { + for ( iNode = nbn; iNode < nbNodes; iNode++ ) { aFaceLastNodes.insert( vecNewNodes[ iNode ]->second.back() ); } } @@ -3233,12 +4171,11 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, } for ( volNb = 0; volNb < nbVolumesByStep; volNb++ ) { list::iterator v = newVolumes.begin(); - iVol = 0; - while ( iVol++ < volNb ) v++; + std::advance( v, volNb ); // find indices of free faces of a volume and their source edges list< int > freeInd; list< const SMDS_MeshElement* > srcEdges; // source edges of free faces - SMDS_VolumeTool vTool( *v ); + SMDS_VolumeTool vTool( *v, /*ignoreCentralNodes=*/false ); int iF, nbF = vTool.NbFaces(); for ( iF = 0; iF < nbF; iF ++ ) { if (vTool.IsFreeFace( iF ) && @@ -3273,62 +4210,137 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, // create faces for all steps; // if such a face has been already created by sweep of edge, // assure that its orientation is OK - for ( int iStep = 0; iStep < nbSteps; iStep++ ) { - vTool.Set( *v ); + for ( int iStep = 0; iStep < nbSteps; iStep++ ) { + vTool.Set( *v, /*ignoreCentralNodes=*/false ); vTool.SetExternalNormal(); + const int nextShift = vTool.IsForward() ? +1 : -1; list< int >::iterator ind = freeInd.begin(); list< const SMDS_MeshElement* >::iterator srcEdge = srcEdges.begin(); for ( ; ind != freeInd.end(); ++ind, ++srcEdge ) // loop on free faces { const SMDS_MeshNode** nodes = vTool.GetFaceNodes( *ind ); int nbn = vTool.NbFaceNodes( *ind ); - switch ( nbn ) { - case 3: { ///// triangle - const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]); - if ( !f ) - myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] )); - else if ( nodes[ 1 ] != f->GetNode( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) - aMesh->ChangeElementNodes( f, nodes, nbn ); - break; + const SMDS_MeshElement * f = 0; + if ( nbn == 3 ) ///// triangle + { + f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ]); + if ( !f || + nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift )) + { + const SMDS_MeshNode* newOrder[3] = { nodes[ 1 - nextShift ], + nodes[ 1 ], + nodes[ 1 + nextShift ] }; + if ( f ) + aMesh->ChangeElementNodes( f, &newOrder[0], nbn ); + else + myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ], + newOrder[ 2 ] )); + } } - case 4: { ///// quadrangle - const SMDS_MeshFace * f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]); - if ( !f ) - myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] )); - else if ( nodes[ 1 ] != f->GetNode( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) - aMesh->ChangeElementNodes( f, nodes, nbn ); - break; + else if ( nbn == 4 ) ///// quadrangle + { + f = aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ]); + if ( !f || + nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ]) + nextShift )) + { + const SMDS_MeshNode* newOrder[4] = { nodes[ 0 ], nodes[ 2-nextShift ], + nodes[ 2 ], nodes[ 2+nextShift ] }; + if ( f ) + aMesh->ChangeElementNodes( f, &newOrder[0], nbn ); + else + myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], newOrder[ 1 ], + newOrder[ 2 ], newOrder[ 3 ])); + } } - default: - if( (*v)->IsQuadratic() ) { - if(nbn==6) { /////// quadratic triangle - const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], - nodes[1], nodes[3], nodes[5] ); - if ( !f ) - myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], - nodes[1], nodes[3], nodes[5])); - else if ( nodes[ 2 ] != f->GetNode( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) - aMesh->ChangeElementNodes( f, nodes, nbn ); - } - else { /////// quadratic quadrangle - const SMDS_MeshFace * f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6], - nodes[1], nodes[3], nodes[5], nodes[7] ); - if ( !f ) - myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], - nodes[1], nodes[3], nodes[5], nodes[7])); - else if ( nodes[ 2 ] != f->GetNode( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) - aMesh->ChangeElementNodes( f, nodes, nbn ); - } + else if ( nbn == 6 && isQuadratic ) /////// quadratic triangle + { + f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5] ); + if ( !f || + nodes[2] != f->GetNodeWrap( f->GetNodeIndex( nodes[0] ) + 2*nextShift )) + { + const SMDS_MeshNode* newOrder[6] = { nodes[2 - 2*nextShift], + nodes[2], + nodes[2 + 2*nextShift], + nodes[3 - 2*nextShift], + nodes[3], + nodes[3 + 2*nextShift]}; + if ( f ) + aMesh->ChangeElementNodes( f, &newOrder[0], nbn ); + else + myLastCreatedElems.Append(aMesh->AddFace( newOrder[ 0 ], + newOrder[ 1 ], + newOrder[ 2 ], + newOrder[ 3 ], + newOrder[ 4 ], + newOrder[ 5 ] )); + } + } + else if ( nbn == 8 && isQuadratic ) /////// quadratic quadrangle + { + f = aMesh->FindFace( nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7] ); + if ( !f || + nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift )) + { + const SMDS_MeshNode* newOrder[8] = { nodes[0], + nodes[4 - 2*nextShift], + nodes[4], + nodes[4 + 2*nextShift], + nodes[1], + nodes[5 - 2*nextShift], + nodes[5], + nodes[5 + 2*nextShift] }; + if ( f ) + aMesh->ChangeElementNodes( f, &newOrder[0], nbn ); + else + myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ], + newOrder[ 2 ], newOrder[ 3 ], + newOrder[ 4 ], newOrder[ 5 ], + newOrder[ 6 ], newOrder[ 7 ])); + } + } + else if ( nbn == 9 && isQuadratic ) /////// bi-quadratic quadrangle + { + f = aMesh->FindElement( vector( nodes, nodes+nbn ), + SMDSAbs_Face, /*noMedium=*/false); + if ( !f || + nodes[ 2 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + 2*nextShift )) + { + const SMDS_MeshNode* newOrder[9] = { nodes[0], + nodes[4 - 2*nextShift], + nodes[4], + nodes[4 + 2*nextShift], + nodes[1], + nodes[5 - 2*nextShift], + nodes[5], + nodes[5 + 2*nextShift], + nodes[8] }; + if ( f ) + aMesh->ChangeElementNodes( f, &newOrder[0], nbn ); + else + myLastCreatedElems.Append(aMesh->AddFace(newOrder[ 0 ], newOrder[ 1 ], + newOrder[ 2 ], newOrder[ 3 ], + newOrder[ 4 ], newOrder[ 5 ], + newOrder[ 6 ], newOrder[ 7 ], + newOrder[ 8 ])); } - else { //////// polygon - vector polygon_nodes ( nodes, &nodes[nbn] ); - const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes ); - if ( !f ) - myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes)); - else if ( nodes[ 1 ] != f->GetNode( f->GetNodeIndex( nodes[ 0 ] ) + 1 )) - aMesh->ChangeElementNodes( f, nodes, nbn ); + } + else //////// polygon + { + vector polygon_nodes ( nodes, nodes+nbn ); + const SMDS_MeshFace * f = aMesh->FindFace( polygon_nodes ); + if ( !f || + nodes[ 1 ] != f->GetNodeWrap( f->GetNodeIndex( nodes[ 0 ] ) + nextShift )) + { + if ( !vTool.IsForward() ) + std::reverse( polygon_nodes.begin(), polygon_nodes.end()); + if ( f ) + aMesh->ChangeElementNodes( f, &polygon_nodes[0], nbn ); + else + AddElement(polygon_nodes, SMDSAbs_Face, polygon_nodes.size()>4); } } + while ( srcElements.Length() < myLastCreatedElems.Length() ) srcElements.Append( *srcEdge ); @@ -3337,54 +4349,60 @@ void SMESH_MeshEditor::makeWalls (TNodeOfNodeListMap & mapNewNodes, // go to the next volume iVol = 0; while ( iVol++ < nbVolumesByStep ) v++; - } - } + + } // loop on steps + } // loop on volumes of one step } // sweep free links into faces // Make a ceiling face with a normal external to a volume - SMDS_VolumeTool lastVol( itElem->second.back() ); + SMDS_VolumeTool lastVol( itElem->second.back(), /*ignoreCentralNodes=*/false ); int iF = lastVol.GetFaceIndex( aFaceLastNodes ); if ( iF >= 0 ) { lastVol.SetExternalNormal(); const SMDS_MeshNode** nodes = lastVol.GetFaceNodes( iF ); int nbn = lastVol.NbFaceNodes( iF ); - switch ( nbn ) { - case 3: + if ( nbn == 3 ) { if (!hasFreeLinks || !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ])) myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ] )); - break; - case 4: + } + else if ( nbn == 4 ) + { if (!hasFreeLinks || !aMesh->FindFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ])) - myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ] )); - break; - default: - if(itElem->second.back()->IsQuadratic()) { - if(nbn==6) { - if (!hasFreeLinks || - !aMesh->FindFace(nodes[0], nodes[2], nodes[4], - nodes[1], nodes[3], nodes[5]) ) { - myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], - nodes[1], nodes[3], nodes[5])); - } - } - else { // nbn==8 - if (!hasFreeLinks || - !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6], - nodes[1], nodes[3], nodes[5], nodes[7]) ) - myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], - nodes[1], nodes[3], nodes[5], nodes[7])); - } - } - else { - vector polygon_nodes ( nodes, &nodes[nbn] ); - if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes)) - myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes)); - } - } // switch + myLastCreatedElems.Append(aMesh->AddFace( nodes[ 0 ], nodes[ 1 ], nodes[ 2 ], nodes[ 3 ])); + } + else if ( nbn == 6 && isQuadratic ) + { + if (!hasFreeLinks || + !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[1], nodes[3], nodes[5]) ) + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], + nodes[1], nodes[3], nodes[5])); + } + else if ( nbn == 8 && isQuadratic ) + { + if (!hasFreeLinks || + !aMesh->FindFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7]) ) + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7])); + } + else if ( nbn == 9 && isQuadratic ) + { + if (!hasFreeLinks || + !aMesh->FindElement(vector( nodes, nodes+nbn ), + SMDSAbs_Face, /*noMedium=*/false) ) + myLastCreatedElems.Append(aMesh->AddFace(nodes[0], nodes[2], nodes[4], nodes[6], + nodes[1], nodes[3], nodes[5], nodes[7], + nodes[8])); + } + else { + vector polygon_nodes ( nodes, nodes + nbn ); + if (!hasFreeLinks || !aMesh->FindFace(polygon_nodes)) + myLastCreatedElems.Append(aMesh->AddPolygonalFace(polygon_nodes)); + } while ( srcElements.Length() < myLastCreatedElems.Length() ) srcElements.Append( myLastCreatedElems.Last() ); @@ -3427,6 +4445,9 @@ SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems, TElemOfVecOfNnlmiMap mapElemNewNodes; TElemOfElemListMap newElemsMap; + const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) + + myMesh->NbFaces(ORDER_QUADRATIC) + + myMesh->NbVolumes(ORDER_QUADRATIC) ); // loop on theElems TIDSortedElemSet::iterator itElem; for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { @@ -3442,64 +4463,58 @@ SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems, { // check if a node has been already sweeped const SMDS_MeshNode* node = cast2Node( itN->next() ); - TNodeOfNodeListMapItr nIt = mapNewNodes.find( node ); - if ( nIt == mapNewNodes.end() ) { - nIt = mapNewNodes.insert( make_pair( node, list() )).first; - list& listNewNodes = nIt->second; + + gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); + double coord[3]; + aXYZ.Coord( coord[0], coord[1], coord[2] ); + bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol ); + + TNodeOfNodeListMapItr nIt = + mapNewNodes.insert( make_pair( node, list() )).first; + list& listNewNodes = nIt->second; + if ( listNewNodes.empty() ) + { + // check if we are to create medium nodes between corner ones + bool needMediumNodes = false; + if ( isQuadraticMesh ) + { + SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(); + while (it->more() && !needMediumNodes ) + { + const SMDS_MeshElement* invElem = it->next(); + if ( invElem != elem && !theElems.count( invElem )) continue; + needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) ); + if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle ) + needMediumNodes = true; + } + } // make new nodes - gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); - double coord[3]; - aXYZ.Coord( coord[0], coord[1], coord[2] ); - bool isOnAxis = ( aLine.SquareDistance( aXYZ ) <= aSqTol ); const SMDS_MeshNode * newNode = node; for ( int i = 0; i < theNbSteps; i++ ) { if ( !isOnAxis ) { - if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { - // create two nodes + if ( needMediumNodes ) // create a medium node + { aTrsf2.Transforms( coord[0], coord[1], coord[2] ); - //aTrsf.Transforms( coord[0], coord[1], coord[2] ); newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); myLastCreatedNodes.Append(newNode); srcNodes.Append( node ); listNewNodes.push_back( newNode ); aTrsf2.Transforms( coord[0], coord[1], coord[2] ); - //aTrsf.Transforms( coord[0], coord[1], coord[2] ); } else { aTrsf.Transforms( coord[0], coord[1], coord[2] ); } + // create a corner node newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); myLastCreatedNodes.Append(newNode); srcNodes.Append( node ); + listNewNodes.push_back( newNode ); } - listNewNodes.push_back( newNode ); - } - } - else { - // if current elem is quadratic and current node is not medium - // we have to check - may be it is needed to insert additional nodes - if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { - list< const SMDS_MeshNode* > & listNewNodes = nIt->second; - if(listNewNodes.size()==theNbSteps) { - listNewNodes.clear(); - // make new nodes - gp_XYZ aXYZ( node->X(), node->Y(), node->Z() ); - double coord[3]; - aXYZ.Coord( coord[0], coord[1], coord[2] ); - const SMDS_MeshNode * newNode = node; - for(int i = 0; iAddNode( coord[0], coord[1], coord[2] ); - myLastCreatedNodes.Append(newNode); - listNewNodes.push_back( newNode ); - srcNodes.Append( node ); - aTrsf2.Transforms( coord[0], coord[1], coord[2] ); - newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); - myLastCreatedNodes.Append(newNode); - srcNodes.Append( node ); - listNewNodes.push_back( newNode ); - } + else { + listNewNodes.push_back( newNode ); + // if ( needMediumNodes ) + // listNewNodes.push_back( newNode ); } } } @@ -3511,7 +4526,7 @@ SMESH_MeshEditor::RotationSweep(TIDSortedElemSet & theElems, if ( theMakeWalls ) makeWalls( mapNewNodes, newElemsMap, mapElemNewNodes, theElems, theNbSteps, srcElems ); - + PGroupIDs newGroupIDs; if ( theMakeGroups ) newGroupIDs = generateGroups( srcNodes, srcElems, "rotated"); @@ -3530,8 +4545,8 @@ const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x, const double tolnode, SMESH_SequenceOfNode& aNodes) { - myLastCreatedElems.Clear(); - myLastCreatedNodes.Clear(); + // myLastCreatedElems.Clear(); + // myLastCreatedNodes.Clear(); gp_Pnt P1(x,y,z); SMESHDS_Mesh * aMesh = myMesh->GetMeshDS(); @@ -3559,7 +4574,7 @@ const SMDS_MeshNode* SMESH_MeshEditor::CreateNode(const double x, // create new node and return it const SMDS_MeshNode* NewNode = aMesh->AddNode(x,y,z); - myLastCreatedNodes.Append(NewNode); + //myLastCreatedNodes.Append(NewNode); return NewNode; } @@ -3619,6 +4634,9 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, TElemOfVecOfNnlmiMap mapElemNewNodes; //TElemOfVecOfMapNodesMap mapElemNewNodes; + const bool isQuadraticMesh = bool( myMesh->NbEdges(ORDER_QUADRATIC) + + myMesh->NbFaces(ORDER_QUADRATIC) + + myMesh->NbVolumes(ORDER_QUADRATIC) ); // loop on theElems TIDSortedElemSet::iterator itElem; for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { @@ -3628,7 +4646,6 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, continue; vector & newNodesItVec = mapElemNewNodes[ elem ]; - //vector & newNodesItVec = mapElemNewNodes[ elem ]; newNodesItVec.reserve( elem->NbNodes() ); // loop on elem nodes @@ -3637,21 +4654,33 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, { // check if a node has been already sweeped const SMDS_MeshNode* node = cast2Node( itN->next() ); - TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); - //TNodeOfNodeVecMap::iterator nIt = mapNewNodes.find( node ); - if ( nIt == mapNewNodes.end() ) { - nIt = mapNewNodes.insert( make_pair( node, list() )).first; - //nIt = mapNewNodes.insert( make_pair( node, vector() )).first; - list& listNewNodes = nIt->second; - //vector& vecNewNodes = nIt->second; - //vecNewNodes.reserve(nbsteps); - + TNodeOfNodeListMap::iterator nIt = + mapNewNodes.insert( make_pair( node, list() )).first; + list& listNewNodes = nIt->second; + if ( listNewNodes.empty() ) + { // make new nodes - double coord[] = { node->X(), node->Y(), node->Z() }; - //int nbsteps = theParams.mySteps->Length(); - for ( int i = 0; i < nbsteps; i++ ) { - if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { - // create additional node + + // check if we are to create medium nodes between corner ones + bool needMediumNodes = false; + if ( isQuadraticMesh ) + { + SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(); + while (it->more() && !needMediumNodes ) + { + const SMDS_MeshElement* invElem = it->next(); + if ( invElem != elem && !theElems.count( invElem )) continue; + needMediumNodes = ( invElem->IsQuadratic() && !invElem->IsMediumNode(node) ); + if ( !needMediumNodes && invElem->GetEntityType() == SMDSEntity_BiQuad_Quadrangle ) + needMediumNodes = true; + } + } + + double coord[] = { node->X(), node->Y(), node->Z() }; + for ( int i = 0; i < nbsteps; i++ ) + { + if ( needMediumNodes ) // create a medium node + { double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1)/2.; double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1)/2.; double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1)/2.; @@ -3667,7 +4696,7 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, listNewNodes.push_back( newNode ); } } - //aTrsf.Transforms( coord[0], coord[1], coord[2] ); + // create a corner node coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); @@ -3675,55 +4704,12 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2], theTolerance, theParams.myNodes); listNewNodes.push_back( newNode ); - //vecNewNodes[i]=newNode; } else { const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); myLastCreatedNodes.Append(newNode); srcNodes.Append( node ); listNewNodes.push_back( newNode ); - //vecNewNodes[i]=newNode; - } - } - } - else { - // if current elem is quadratic and current node is not medium - // we have to check - may be it is needed to insert additional nodes - if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { - list< const SMDS_MeshNode* > & listNewNodes = nIt->second; - if(listNewNodes.size()==nbsteps) { - listNewNodes.clear(); - double coord[] = { node->X(), node->Y(), node->Z() }; - for ( int i = 0; i < nbsteps; i++ ) { - double x = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); - double y = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); - double z = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); - if( theFlags & EXTRUSION_FLAG_SEW ) { - const SMDS_MeshNode * newNode = CreateNode(x, y, z, - theTolerance, theParams.myNodes); - listNewNodes.push_back( newNode ); - } - else { - const SMDS_MeshNode * newNode = aMesh->AddNode(x, y, z); - myLastCreatedNodes.Append(newNode); - srcNodes.Append( node ); - listNewNodes.push_back( newNode ); - } - coord[0] = coord[0] + theParams.myDir.X()*theParams.mySteps->Value(i+1); - coord[1] = coord[1] + theParams.myDir.Y()*theParams.mySteps->Value(i+1); - coord[2] = coord[2] + theParams.myDir.Z()*theParams.mySteps->Value(i+1); - if( theFlags & EXTRUSION_FLAG_SEW ) { - const SMDS_MeshNode * newNode = CreateNode(coord[0], coord[1], coord[2], - theTolerance, theParams.myNodes); - listNewNodes.push_back( newNode ); - } - else { - const SMDS_MeshNode * newNode = aMesh->AddNode( coord[0], coord[1], coord[2] ); - myLastCreatedNodes.Append(newNode); - srcNodes.Append( node ); - listNewNodes.push_back( newNode ); - } - } } } } @@ -3743,82 +4729,30 @@ SMESH_MeshEditor::ExtrusionSweep (TIDSortedElemSet & theElems, return newGroupIDs; } - -//======================================================================= -//class : SMESH_MeshEditor_PathPoint -//purpose : auxiliary class -//======================================================================= -class SMESH_MeshEditor_PathPoint { -public: - SMESH_MeshEditor_PathPoint() { - myPnt.SetCoord(99., 99., 99.); - myTgt.SetCoord(1.,0.,0.); - myAngle=0.; - myPrm=0.; - } - void SetPnt(const gp_Pnt& aP3D){ - myPnt=aP3D; - } - void SetTangent(const gp_Dir& aTgt){ - myTgt=aTgt; - } - void SetAngle(const double& aBeta){ - myAngle=aBeta; - } - void SetParameter(const double& aPrm){ - myPrm=aPrm; - } - const gp_Pnt& Pnt()const{ - return myPnt; - } - const gp_Dir& Tangent()const{ - return myTgt; - } - double Angle()const{ - return myAngle; - } - double Parameter()const{ - return myPrm; - } - -protected: - gp_Pnt myPnt; - gp_Dir myTgt; - double myAngle; - double myPrm; -}; - //======================================================================= //function : ExtrusionAlongTrack //purpose : //======================================================================= SMESH_MeshEditor::Extrusion_Error - SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements, - SMESH_subMesh* theTrack, - const SMDS_MeshNode* theN1, - const bool theHasAngles, - list& theAngles, - const bool theHasRefPoint, - const gp_Pnt& theRefPoint, - const bool theMakeGroups) +SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements, + SMESH_subMesh* theTrack, + const SMDS_MeshNode* theN1, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) { + MESSAGE("ExtrusionAlongTrack"); myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); - // source elements for each generated one - SMESH_SequenceOfElemPtr srcElems, srcNodes; - - int j, aNbTP, aNbE, aNb; - double aT1, aT2, aT, aAngle, aX, aY, aZ; + int aNbE; std::list aPrms; - std::list::iterator aItD; TIDSortedElemSet::iterator itElem; - Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2; - gp_Pnt aP3D, aV0; - gp_Vec aVec; gp_XYZ aGC; - Handle(Geom_Curve) aC3D; TopoDS_Edge aTrackEdge; TopoDS_Vertex aV1, aV2; @@ -3827,11 +4761,6 @@ SMESH_MeshEditor::Extrusion_Error SMDSAbs_ElementType aTypeE; TNodeOfNodeListMap mapNewNodes; - TElemOfVecOfNnlmiMap mapElemNewNodes; - TElemOfElemListMap newElemsMap; - - aTolVec=1.e-7; - aTolVec2=aTolVec*aTolVec; // 1. Check data aNbE = theElements.size(); @@ -3842,7 +4771,7 @@ SMESH_MeshEditor::Extrusion_Error // 1.1 Track Pattern ASSERT( theTrack ); - SMESHDS_SubMesh* pSubMeshDS=theTrack->GetSubMeshDS(); + SMESHDS_SubMesh* pSubMeshDS = theTrack->GetSubMeshDS(); aItE = pSubMeshDS->GetElements(); while ( aItE->more() ) { @@ -3853,63 +4782,441 @@ SMESH_MeshEditor::Extrusion_Error return EXTR_PATH_NOT_EDGE; } + list fullList; + const TopoDS_Shape& aS = theTrack->GetSubShape(); - // Sub shape for the Pattern must be an Edge - if ( aS.ShapeType() != TopAbs_EDGE ) + // Sub-shape for the Pattern must be an Edge or Wire + if( aS.ShapeType() == TopAbs_EDGE ) { + aTrackEdge = TopoDS::Edge( aS ); + // the Edge must not be degenerated + if ( BRep_Tool::Degenerated( aTrackEdge ) ) + return EXTR_BAD_PATH_SHAPE; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1 == theN1 || aN2 == theN1 ) ) + return EXTR_BAD_STARTING_NODE; + aItN = pSubMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList); + } else if( aS.ShapeType() == TopAbs_WIRE ) { + list< SMESH_subMesh* > LSM; + TopTools_SequenceOfShape Edges; + SMESH_subMeshIteratorPtr itSM = theTrack->getDependsOnIterator(false,true); + while(itSM->more()) { + SMESH_subMesh* SM = itSM->next(); + LSM.push_back(SM); + const TopoDS_Shape& aS = SM->GetSubShape(); + Edges.Append(aS); + } + list< list > LLPPs; + int startNid = theN1->GetID(); + TColStd_MapOfInteger UsedNums; + + int NbEdges = Edges.Length(); + int i = 1; + for(; i<=NbEdges; i++) { + int k = 0; + list< SMESH_subMesh* >::iterator itLSM = LSM.begin(); + for(; itLSM!=LSM.end(); itLSM++) { + k++; + if(UsedNums.Contains(k)) continue; + aTrackEdge = TopoDS::Edge( Edges.Value(k) ); + SMESH_subMesh* locTrack = *itLSM; + SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS(); + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue; + // 2. Collect parameters on the track edge + aPrms.clear(); + aItN = locMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + list LPP; + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP); + LLPPs.push_back(LPP); + UsedNums.Add(k); + // update startN for search following egde + if( aN1->GetID() == startNid ) startNid = aN2->GetID(); + else startNid = aN1->GetID(); + break; + } + } + list< list >::iterator itLLPP = LLPPs.begin(); + list firstList = *itLLPP; + list::iterator itPP = firstList.begin(); + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + SMESH_MeshEditor_PathPoint PP1 = fullList.back(); + fullList.pop_back(); + itLLPP++; + for(; itLLPP!=LLPPs.end(); itLLPP++) { + list currList = *itLLPP; + itPP = currList.begin(); + SMESH_MeshEditor_PathPoint PP2 = currList.front(); + gp_Dir D1 = PP1.Tangent(); + gp_Dir D2 = PP2.Tangent(); + gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2, + (D1.Z()+D2.Z())/2 ) ); + PP1.SetTangent(Dnew); + fullList.push_back(PP1); + itPP++; + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + PP1 = fullList.back(); + fullList.pop_back(); + } + // if wire not closed + fullList.push_back(PP1); + // else ??? + } + else { return EXTR_BAD_PATH_SHAPE; + } - aTrackEdge = TopoDS::Edge( aS ); - // the Edge must not be degenerated - if ( BRep_Tool::Degenerated( aTrackEdge ) ) - return EXTR_BAD_PATH_SHAPE; + return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation, + theHasRefPoint, theRefPoint, theMakeGroups); +} - TopExp::Vertices( aTrackEdge, aV1, aV2 ); - aT1=BRep_Tool::Parameter( aV1, aTrackEdge ); - aT2=BRep_Tool::Parameter( aV2, aTrackEdge ); - aItN = theTrack->GetFather()->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); - const SMDS_MeshNode* aN1 = aItN->next(); +//======================================================================= +//function : ExtrusionAlongTrack +//purpose : +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::ExtrusionAlongTrack (TIDSortedElemSet & theElements, + SMESH_Mesh* theTrack, + const SMDS_MeshNode* theN1, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + int aNbE; + std::list aPrms; + TIDSortedElemSet::iterator itElem; + + gp_XYZ aGC; + TopoDS_Edge aTrackEdge; + TopoDS_Vertex aV1, aV2; + + SMDS_ElemIteratorPtr aItE; + SMDS_NodeIteratorPtr aItN; + SMDSAbs_ElementType aTypeE; - aItN = theTrack->GetFather()->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); - const SMDS_MeshNode* aN2 = aItN->next(); + TNodeOfNodeListMap mapNewNodes; - // starting node must be aN1 or aN2 - if ( !( aN1 == theN1 || aN2 == theN1 ) ) - return EXTR_BAD_STARTING_NODE; + // 1. Check data + aNbE = theElements.size(); + // nothing to do + if ( !aNbE ) + return EXTR_NO_ELEMENTS; - aNbTP = pSubMeshDS->NbNodes() + 2; + // 1.1 Track Pattern + ASSERT( theTrack ); - // 1.2. Angles - vector aAngles( aNbTP ); + SMESHDS_Mesh* pMeshDS = theTrack->GetMeshDS(); - for ( j=0; j < aNbTP; ++j ) { - aAngles[j] = 0.; + aItE = pMeshDS->elementsIterator(); + while ( aItE->more() ) { + const SMDS_MeshElement* pE = aItE->next(); + aTypeE = pE->GetType(); + // Pattern must contain links only + if ( aTypeE != SMDSAbs_Edge ) + return EXTR_PATH_NOT_EDGE; } - if ( theHasAngles ) { - aItD = theAngles.begin(); - for ( j=1; (aItD != theAngles.end()) && (j fullList; + + const TopoDS_Shape& aS = theTrack->GetShapeToMesh(); + + if( aS == SMESH_Mesh::PseudoShape() ) { + //Mesh without shape + const SMDS_MeshNode* currentNode = NULL; + const SMDS_MeshNode* prevNode = theN1; + std::vector aNodesList; + aNodesList.push_back(theN1); + int nbEdges = 0, conn=0; + const SMDS_MeshElement* prevElem = NULL; + const SMDS_MeshElement* currentElem = NULL; + int totalNbEdges = theTrack->NbEdges(); + SMDS_ElemIteratorPtr nIt; + + //check start node + if( !theTrack->GetMeshDS()->Contains(theN1) ) { + return EXTR_BAD_STARTING_NODE; + } + + conn = nbEdgeConnectivity(theN1); + if(conn > 2) + return EXTR_PATH_NOT_EDGE; + + aItE = theN1->GetInverseElementIterator(); + prevElem = aItE->next(); + currentElem = prevElem; + //Get all nodes + if(totalNbEdges == 1 ) { + nIt = currentElem->nodesIterator(); + currentNode = static_cast(nIt->next()); + if(currentNode == prevNode) + currentNode = static_cast(nIt->next()); + aNodesList.push_back(currentNode); + } else { + nIt = currentElem->nodesIterator(); + while( nIt->more() ) { + currentNode = static_cast(nIt->next()); + if(currentNode == prevNode) + currentNode = static_cast(nIt->next()); + aNodesList.push_back(currentNode); + + //case of the closed mesh + if(currentNode == theN1) { + nbEdges++; + break; + } + + conn = nbEdgeConnectivity(currentNode); + if(conn > 2) { + return EXTR_PATH_NOT_EDGE; + }else if( conn == 1 && nbEdges > 0 ) { + //End of the path + nbEdges++; + break; + }else { + prevNode = currentNode; + aItE = currentNode->GetInverseElementIterator(); + currentElem = aItE->next(); + if( currentElem == prevElem) + currentElem = aItE->next(); + nIt = currentElem->nodesIterator(); + prevElem = currentElem; + nbEdges++; + } + } + } + + if(nbEdges != totalNbEdges) + return EXTR_PATH_NOT_EDGE; + + TopTools_SequenceOfShape Edges; + double x1,x2,y1,y2,z1,z2; + list< list > LLPPs; + int startNid = theN1->GetID(); + for(int i = 1; i < aNodesList.size(); i++) { + x1 = aNodesList[i-1]->X();x2 = aNodesList[i]->X(); + y1 = aNodesList[i-1]->Y();y2 = aNodesList[i]->Y(); + z1 = aNodesList[i-1]->Z();z2 = aNodesList[i]->Z(); + TopoDS_Edge e = BRepBuilderAPI_MakeEdge(gp_Pnt(x1,y1,z1),gp_Pnt(x2,y2,z2)); + list LPP; + aPrms.clear(); + MakeEdgePathPoints(aPrms, e, (aNodesList[i-1]->GetID()==startNid), LPP); + LLPPs.push_back(LPP); + if( aNodesList[i-1]->GetID() == startNid ) startNid = aNodesList[i]->GetID(); + else startNid = aNodesList[i-1]->GetID(); + + } + + list< list >::iterator itLLPP = LLPPs.begin(); + list firstList = *itLLPP; + list::iterator itPP = firstList.begin(); + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + + SMESH_MeshEditor_PathPoint PP1 = fullList.back(); + SMESH_MeshEditor_PathPoint PP2; + fullList.pop_back(); + itLLPP++; + for(; itLLPP!=LLPPs.end(); itLLPP++) { + list currList = *itLLPP; + itPP = currList.begin(); + PP2 = currList.front(); + gp_Dir D1 = PP1.Tangent(); + gp_Dir D2 = PP2.Tangent(); + gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2, + (D1.Z()+D2.Z())/2 ) ); + PP1.SetTangent(Dnew); + fullList.push_back(PP1); + itPP++; + for(; itPP!=currList.end(); itPP++) { + fullList.push_back( *itPP ); + } + PP1 = fullList.back(); + fullList.pop_back(); + } + fullList.push_back(PP1); + + } // Sub-shape for the Pattern must be an Edge or Wire + else if( aS.ShapeType() == TopAbs_EDGE ) { + aTrackEdge = TopoDS::Edge( aS ); + // the Edge must not be degenerated + if ( BRep_Tool::Degenerated( aTrackEdge ) ) + return EXTR_BAD_PATH_SHAPE; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = theTrack->GetSubMesh( aV1 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = theTrack->GetSubMesh( aV2 )->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1 == theN1 || aN2 == theN1 ) ) + return EXTR_BAD_STARTING_NODE; + aItN = pMeshDS->nodesIterator(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + if( pNode==aN1 || pNode==aN2 ) continue; + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge, (aN1==theN1), fullList); + } + else if( aS.ShapeType() == TopAbs_WIRE ) { + list< SMESH_subMesh* > LSM; + TopTools_SequenceOfShape Edges; + TopExp_Explorer eExp(aS, TopAbs_EDGE); + for(; eExp.More(); eExp.Next()) { + TopoDS_Edge E = TopoDS::Edge( eExp.Current() ); + if( BRep_Tool::Degenerated(E) ) continue; + SMESH_subMesh* SM = theTrack->GetSubMesh(E); + if(SM) { + LSM.push_back(SM); + Edges.Append(E); + } + } + list< list > LLPPs; + int startNid = theN1->GetID(); + TColStd_MapOfInteger UsedNums; + int NbEdges = Edges.Length(); + int i = 1; + for(; i<=NbEdges; i++) { + int k = 0; + list< SMESH_subMesh* >::iterator itLSM = LSM.begin(); + for(; itLSM!=LSM.end(); itLSM++) { + k++; + if(UsedNums.Contains(k)) continue; + aTrackEdge = TopoDS::Edge( Edges.Value(k) ); + SMESH_subMesh* locTrack = *itLSM; + SMESHDS_SubMesh* locMeshDS = locTrack->GetSubMeshDS(); + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aItN = locTrack->GetFather()->GetSubMesh(aV1)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN1 = aItN->next(); + aItN = locTrack->GetFather()->GetSubMesh(aV2)->GetSubMeshDS()->GetNodes(); + const SMDS_MeshNode* aN2 = aItN->next(); + // starting node must be aN1 or aN2 + if ( !( aN1->GetID() == startNid || aN2->GetID() == startNid ) ) continue; + // 2. Collect parameters on the track edge + aPrms.clear(); + aItN = locMeshDS->GetNodes(); + while ( aItN->more() ) { + const SMDS_MeshNode* pNode = aItN->next(); + const SMDS_EdgePosition* pEPos = + static_cast( pNode->GetPosition() ); + double aT = pEPos->GetUParameter(); + aPrms.push_back( aT ); + } + list LPP; + //Extrusion_Error err = + MakeEdgePathPoints(aPrms, aTrackEdge,(aN1->GetID()==startNid), LPP); + LLPPs.push_back(LPP); + UsedNums.Add(k); + // update startN for search following egde + if( aN1->GetID() == startNid ) startNid = aN2->GetID(); + else startNid = aN1->GetID(); + break; + } } + list< list >::iterator itLLPP = LLPPs.begin(); + list firstList = *itLLPP; + list::iterator itPP = firstList.begin(); + for(; itPP!=firstList.end(); itPP++) { + fullList.push_back( *itPP ); + } + SMESH_MeshEditor_PathPoint PP1 = fullList.back(); + fullList.pop_back(); + itLLPP++; + for(; itLLPP!=LLPPs.end(); itLLPP++) { + list currList = *itLLPP; + itPP = currList.begin(); + SMESH_MeshEditor_PathPoint PP2 = currList.front(); + gp_Dir D1 = PP1.Tangent(); + gp_Dir D2 = PP2.Tangent(); + gp_Dir Dnew( gp_Vec( (D1.X()+D2.X())/2, (D1.Y()+D2.Y())/2, + (D1.Z()+D2.Z())/2 ) ); + PP1.SetTangent(Dnew); + fullList.push_back(PP1); + itPP++; + for(; itPP!=currList.end(); itPP++) { + fullList.push_back( *itPP ); + } + PP1 = fullList.back(); + fullList.pop_back(); + } + // if wire not closed + fullList.push_back(PP1); + // else ??? + } + else { + return EXTR_BAD_PATH_SHAPE; } - // 2. Collect parameters on the track edge - aPrms.push_back( aT1 ); - aPrms.push_back( aT2 ); + return MakeExtrElements(theElements, fullList, theHasAngles, theAngles, theLinearVariation, + theHasRefPoint, theRefPoint, theMakeGroups); +} - aItN = pSubMeshDS->GetNodes(); - while ( aItN->more() ) { - const SMDS_MeshNode* pNode = aItN->next(); - const SMDS_EdgePosition* pEPos = - static_cast( pNode->GetPosition().get() ); - aT = pEPos->GetUParameter(); - aPrms.push_back( aT ); - } +//======================================================================= +//function : MakeEdgePathPoints +//purpose : auxilary for ExtrusionAlongTrack +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::MakeEdgePathPoints(std::list& aPrms, + const TopoDS_Edge& aTrackEdge, + bool FirstIsStart, + list& LPP) +{ + Standard_Real aTx1, aTx2, aL2, aTolVec, aTolVec2; + aTolVec=1.e-7; + aTolVec2=aTolVec*aTolVec; + double aT1, aT2; + TopoDS_Vertex aV1, aV2; + TopExp::Vertices( aTrackEdge, aV1, aV2 ); + aT1=BRep_Tool::Parameter( aV1, aTrackEdge ); + aT2=BRep_Tool::Parameter( aV2, aTrackEdge ); + // 2. Collect parameters on the track edge + aPrms.push_front( aT1 ); + aPrms.push_back( aT2 ); // sort parameters aPrms.sort(); - if ( aN1 == theN1 ) { + if( FirstIsStart ) { if ( aT1 > aT2 ) { aPrms.reverse(); } @@ -3919,33 +5226,87 @@ SMESH_MeshEditor::Extrusion_Error aPrms.reverse(); } } - // 3. Path Points SMESH_MeshEditor_PathPoint aPP; - vector aPPs( aNbTP ); - // - aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 ); - // - aItD = aPrms.begin(); - for ( j=0; aItD != aPrms.end(); ++aItD, ++j ) { - aT = *aItD; + Handle(Geom_Curve) aC3D = BRep_Tool::Curve( aTrackEdge, aTx1, aTx2 ); + std::list::iterator aItD = aPrms.begin(); + for(; aItD != aPrms.end(); ++aItD) { + double aT = *aItD; + gp_Pnt aP3D; + gp_Vec aVec; aC3D->D1( aT, aP3D, aVec ); aL2 = aVec.SquareMagnitude(); if ( aL2 < aTolVec2 ) return EXTR_CANT_GET_TANGENT; - gp_Dir aTgt( aVec ); - aAngle = aAngles[j]; - aPP.SetPnt( aP3D ); aPP.SetTangent( aTgt ); - aPP.SetAngle( aAngle ); aPP.SetParameter( aT ); - aPPs[j]=aPP; + LPP.push_back(aPP); + } + return EXTR_OK; +} + + +//======================================================================= +//function : MakeExtrElements +//purpose : auxilary for ExtrusionAlongTrack +//======================================================================= +SMESH_MeshEditor::Extrusion_Error +SMESH_MeshEditor::MakeExtrElements(TIDSortedElemSet& theElements, + list& fullList, + const bool theHasAngles, + list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups) +{ + MESSAGE("MakeExtrElements"); + //cout<<"MakeExtrElements fullList.size() = "< aPPs(aNbTP); + // Angles + if( theHasAngles && theAngles.size()>0 && theLinearVariation ) { + LinearAngleVariation(aNbTP-1, theAngles); + } + vector aAngles( aNbTP ); + int j = 0; + for(; j::iterator aItD = theAngles.begin(); + for ( j=1; (aItD != theAngles.end()) && (j::iterator itPP = fullList.begin(); + for(; itPP!=fullList.end(); itPP++) { + j++; + SMESH_MeshEditor_PathPoint PP = *itPP; + PP.SetAngle(aAngles[j]); + aPPs[j] = PP; } + TNodeOfNodeListMap mapNewNodes; + TElemOfVecOfNnlmiMap mapElemNewNodes; + TElemOfElemListMap newElemsMap; + TIDSortedElemSet::iterator itElem; + double aX, aY, aZ; + int aNb; + SMDSAbs_ElementType aTypeE; + // source elements for each generated one + SMESH_SequenceOfElemPtr srcElems, srcNodes; + // 3. Center of rotation aV0 - aV0 = theRefPoint; + gp_Pnt aV0 = theRefPoint; + gp_XYZ aGC; if ( !theHasRefPoint ) { aNb = 0; aGC.SetCoord( 0.,0.,0. ); @@ -3956,19 +5317,19 @@ SMESH_MeshEditor::Extrusion_Error SMDS_ElemIteratorPtr itN = elem->nodesIterator(); while ( itN->more() ) { - const SMDS_MeshNode* node = static_cast( itN->next() ); - aX = node->X(); - aY = node->Y(); - aZ = node->Z(); - - if ( mapNewNodes.find( node ) == mapNewNodes.end() ) { - list aLNx; - mapNewNodes[node] = aLNx; - // - gp_XYZ aXYZ( aX, aY, aZ ); - aGC += aXYZ; - ++aNb; - } + const SMDS_MeshNode* node = static_cast( itN->next() ); + aX = node->X(); + aY = node->Y(); + aZ = node->Z(); + + if ( mapNewNodes.find( node ) == mapNewNodes.end() ) { + list aLNx; + mapNewNodes[node] = aLNx; + // + gp_XYZ aXYZ( aX, aY, aZ ); + aGC += aXYZ; + ++aNb; + } } } aGC /= aNb; @@ -3997,65 +5358,67 @@ SMESH_MeshEditor::Extrusion_Error ++nodeIndex; // check if a node has been already processed const SMDS_MeshNode* node = - static_cast( itN->next() ); + static_cast( itN->next() ); TNodeOfNodeListMap::iterator nIt = mapNewNodes.find( node ); if ( nIt == mapNewNodes.end() ) { nIt = mapNewNodes.insert( make_pair( node, list() )).first; list& listNewNodes = nIt->second; - // make new nodes - aX = node->X(); aY = node->Y(); aZ = node->Z(); - - Standard_Real aAngle1x, aAngleT1T0, aTolAng; - gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x; - gp_Ax1 anAx1, anAxT1T0; - gp_Dir aDT1x, aDT0x, aDT1T0; - - aTolAng=1.e-4; - - aV0x = aV0; - aPN0.SetCoord(aX, aY, aZ); - - const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0]; - aP0x = aPP0.Pnt(); - aDT0x= aPP0.Tangent(); - - for ( j = 1; j < aNbTP; ++j ) { - const SMESH_MeshEditor_PathPoint& aPP1 = aPPs[j]; - aP1x = aPP1.Pnt(); - aDT1x = aPP1.Tangent(); - aAngle1x = aPP1.Angle(); - - gp_Trsf aTrsf, aTrsfRot, aTrsfRotT1T0; - // Translation - gp_Vec aV01x( aP0x, aP1x ); - aTrsf.SetTranslation( aV01x ); - - // traslated point - aV1x = aV0x.Transformed( aTrsf ); - aPN1 = aPN0.Transformed( aTrsf ); - - // rotation 1 [ T1,T0 ] - aAngleT1T0=-aDT1x.Angle( aDT0x ); - if (fabs(aAngleT1T0) > aTolAng) { - aDT1T0=aDT1x^aDT0x; - anAxT1T0.SetLocation( aV1x ); - anAxT1T0.SetDirection( aDT1T0 ); - aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 ); - - aPN1 = aPN1.Transformed( aTrsfRotT1T0 ); - } + // make new nodes + aX = node->X(); aY = node->Y(); aZ = node->Z(); + + Standard_Real aAngle1x, aAngleT1T0, aTolAng; + gp_Pnt aP0x, aP1x, aPN0, aPN1, aV0x, aV1x; + gp_Ax1 anAx1, anAxT1T0; + gp_Dir aDT1x, aDT0x, aDT1T0; + + aTolAng=1.e-4; + + aV0x = aV0; + aPN0.SetCoord(aX, aY, aZ); + + const SMESH_MeshEditor_PathPoint& aPP0 = aPPs[0]; + aP0x = aPP0.Pnt(); + aDT0x= aPP0.Tangent(); + //cout<<"j = 0 PP: Pnt("< aTolAng) { + aDT1T0=aDT1x^aDT0x; + anAxT1T0.SetLocation( aV1x ); + anAxT1T0.SetDirection( aDT1T0 ); + aTrsfRotT1T0.SetRotation( anAxT1T0, aAngleT1T0 ); + + aPN1 = aPN1.Transformed( aTrsfRotT1T0 ); + } - // rotation 2 - if ( theHasAngles ) { - anAx1.SetLocation( aV1x ); - anAx1.SetDirection( aDT1x ); - aTrsfRot.SetRotation( anAx1, aAngle1x ); + // rotation 2 + if ( theHasAngles ) { + anAx1.SetLocation( aV1x ); + anAx1.SetDirection( aDT1x ); + aTrsfRot.SetRotation( anAx1, aAngle1x ); - aPN1 = aPN1.Transformed( aTrsfRot ); - } + aPN1 = aPN1.Transformed( aTrsfRot ); + } - // make new node + // make new node + //MESSAGE("elem->IsQuadratic " << elem->IsQuadratic() << " " << elem->IsMediumNode(node)); if( elem->IsQuadratic() && !elem->IsMediumNode(node) ) { // create additional node double x = ( aPN1.X() + aPN0.X() )/2.; @@ -4066,19 +5429,19 @@ SMESH_MeshEditor::Extrusion_Error srcNodes.Append( node ); listNewNodes.push_back( newNode ); } - aX = aPN1.X(); - aY = aPN1.Y(); - aZ = aPN1.Z(); - const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ ); + aX = aPN1.X(); + aY = aPN1.Y(); + aZ = aPN1.Z(); + const SMDS_MeshNode* newNode = aMesh->AddNode( aX, aY, aZ ); myLastCreatedNodes.Append(newNode); srcNodes.Append( node ); - listNewNodes.push_back( newNode ); + listNewNodes.push_back( newNode ); - aPN0 = aPN1; - aP0x = aP1x; - aV0x = aV1x; - aDT0x = aDT1x; - } + aPN0 = aPN1; + aP0x = aP1x; + aV0x = aV1x; + aDT0x = aDT1x; + } } else { @@ -4127,10 +5490,67 @@ SMESH_MeshEditor::Extrusion_Error return EXTR_OK; } + //======================================================================= -//function : Transform -//purpose : +//function : LinearAngleVariation +//purpose : auxilary for ExtrusionAlongTrack //======================================================================= +void SMESH_MeshEditor::LinearAngleVariation(const int nbSteps, + list& Angles) +{ + int nbAngles = Angles.size(); + if( nbSteps > nbAngles ) { + vector theAngles(nbAngles); + list::iterator it = Angles.begin(); + int i = -1; + for(; it!=Angles.end(); it++) { + i++; + theAngles[i] = (*it); + } + list res; + double rAn2St = double( nbAngles ) / double( nbSteps ); + double angPrev = 0, angle; + for ( int iSt = 0; iSt < nbSteps; ++iSt ) { + double angCur = rAn2St * ( iSt+1 ); + double angCurFloor = floor( angCur ); + double angPrevFloor = floor( angPrev ); + if ( angPrevFloor == angCurFloor ) + angle = rAn2St * theAngles[ int( angCurFloor ) ]; + else { + int iP = int( angPrevFloor ); + double angPrevCeil = ceil(angPrev); + angle = ( angPrevCeil - angPrev ) * theAngles[ iP ]; + + int iC = int( angCurFloor ); + if ( iC < nbAngles ) + angle += ( angCur - angCurFloor ) * theAngles[ iC ]; + + iP = int( angPrevCeil ); + while ( iC-- > iP ) + angle += theAngles[ iC ]; + } + res.push_back(angle); + angPrev = angCur; + } + Angles.clear(); + it = res.begin(); + for(; it!=res.end(); it++) + Angles.push_back( *it ); + } +} + + +//================================================================================ +/*! + * \brief Move or copy theElements applying theTrsf to their nodes + * \param theElems - elements to transform, if theElems is empty then apply to all mesh nodes + * \param theTrsf - transformation to apply + * \param theCopy - if true, create translated copies of theElems + * \param theMakeGroups - if true and theCopy, create translated groups + * \param theTargetMesh - mesh to copy translated elements into + * \return SMESH_MeshEditor::PGroupIDs - list of ids of created groups + */ +//================================================================================ SMESH_MeshEditor::PGroupIDs SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, @@ -4146,21 +5566,37 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, string groupPostfix; switch ( theTrsf.Form() ) { case gp_PntMirror: + MESSAGE("gp_PntMirror"); + needReverse = true; + groupPostfix = "mirrored"; + break; case gp_Ax1Mirror: + MESSAGE("gp_Ax1Mirror"); + groupPostfix = "mirrored"; + break; case gp_Ax2Mirror: + MESSAGE("gp_Ax2Mirror"); needReverse = true; groupPostfix = "mirrored"; break; case gp_Rotation: + MESSAGE("gp_Rotation"); groupPostfix = "rotated"; break; case gp_Translation: + MESSAGE("gp_Translation"); groupPostfix = "translated"; break; case gp_Scale: + MESSAGE("gp_Scale"); + groupPostfix = "scaled"; + break; + case gp_CompoundTrsf: // different scale by axis + MESSAGE("gp_CompoundTrsf"); groupPostfix = "scaled"; break; default: + MESSAGE("default"); needReverse = false; groupPostfix = "transformed"; } @@ -4168,7 +5604,7 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, SMESH_MeshEditor targetMeshEditor( theTargetMesh ); SMESHDS_Mesh* aTgtMesh = theTargetMesh ? theTargetMesh->GetMeshDS() : 0; SMESHDS_Mesh* aMesh = GetMeshDS(); - + // map old node to new one TNodeNodeMap nodeMap; @@ -4180,9 +5616,29 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, // source elements for each generated one SMESH_SequenceOfElemPtr srcElems, srcNodes; - // loop on theElems + // issue 021015: EDF 1578 SMESH: Free nodes are removed when translating a mesh + TIDSortedElemSet orphanNode; + + if ( theElems.empty() ) // transform the whole mesh + { + // add all elements + SMDS_ElemIteratorPtr eIt = aMesh->elementsIterator(); + while ( eIt->more() ) theElems.insert( eIt->next() ); + // add orphan nodes + SMDS_NodeIteratorPtr nIt = aMesh->nodesIterator(); + while ( nIt->more() ) + { + const SMDS_MeshNode* node = nIt->next(); + if ( node->NbInverseElements() == 0) + orphanNode.insert( node ); + } + } + + // loop on elements to transform nodes : first orphan nodes then elems TIDSortedElemSet::iterator itElem; - for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { + TIDSortedElemSet *elements[] = {&orphanNode, &theElems }; + for (int i=0; i<2; i++) + for ( itElem = elements[i]->begin(); itElem != elements[i]->end(); itElem++ ) { const SMDS_MeshElement* elem = *itElem; if ( !elem ) continue; @@ -4191,8 +5647,8 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, SMDS_ElemIteratorPtr itN = elem->nodesIterator(); while ( itN->more() ) { - // check if a node has been already transformed const SMDS_MeshNode* node = cast2Node( itN->next() ); + // check if a node has been already transformed pair n2n_isnew = nodeMap.insert( make_pair ( node, node )); if ( !n2n_isnew.second ) @@ -4241,203 +5697,167 @@ SMESH_MeshEditor::Transform (TIDSortedElemSet & theElems, for ( ; invElemIt != inverseElemSet.end(); invElemIt++ ) theElems.insert( *invElemIt ); - // replicate or reverse elements - - enum { - REV_TETRA = 0, // = nbNodes - 4 - REV_PYRAMID = 1, // = nbNodes - 4 - REV_PENTA = 2, // = nbNodes - 4 - REV_FACE = 3, - REV_HEXA = 4, // = nbNodes - 4 - FORWARD = 5 - }; - int index[][8] = { - { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_TETRA - { 2, 1, 0, 3, 4, 0, 0, 0 }, // REV_PYRAMID - { 2, 1, 0, 5, 4, 3, 0, 0 }, // REV_PENTA - { 2, 1, 0, 3, 0, 0, 0, 0 }, // REV_FACE - { 2, 1, 0, 3, 6, 5, 4, 7 }, // REV_HEXA - { 0, 1, 2, 3, 4, 5, 6, 7 } // FORWARD - }; + // Replicate or reverse elements + std::vector iForw; for ( itElem = theElems.begin(); itElem != theElems.end(); itElem++ ) { const SMDS_MeshElement* elem = *itElem; - if ( !elem || elem->GetType() == SMDSAbs_Node ) - continue; + if ( !elem ) continue; - int nbNodes = elem->NbNodes(); - int elemType = elem->GetType(); + SMDSAbs_GeometryType geomType = elem->GetGeomType(); + int nbNodes = elem->NbNodes(); + if ( geomType == SMDSGeom_POINT ) continue; // node - if (elem->IsPoly()) { - // Polygon or Polyhedral Volume - switch ( elemType ) { - case SMDSAbs_Face: - { - vector poly_nodes (nbNodes); - int iNode = 0; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while (itN->more()) { - const SMDS_MeshNode* node = - static_cast(itN->next()); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); - if (nodeMapIt == nodeMap.end()) - break; // not all nodes transformed - if (needReverse) { - // reverse mirrored faces and volumes - poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second; - } else { - poly_nodes[iNode] = (*nodeMapIt).second; - } - iNode++; - } - if ( iNode != nbNodes ) - continue; // not all nodes transformed + switch ( geomType ) { - if ( theTargetMesh ) { - myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes)); - srcElems.Append( elem ); - } - else if ( theCopy ) { - myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes)); - srcElems.Append( elem ); - } - else { - aMesh->ChangePolygonNodes(elem, poly_nodes); + case SMDSGeom_POLYGON: // ---------------------- polygon + { + vector poly_nodes (nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while (itN->more()) { + const SMDS_MeshNode* node = + static_cast(itN->next()); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) + break; // not all nodes transformed + if (needReverse) { + // reverse mirrored faces and volumes + poly_nodes[nbNodes - iNode - 1] = (*nodeMapIt).second; + } else { + poly_nodes[iNode] = (*nodeMapIt).second; } + iNode++; } - break; - case SMDSAbs_Volume: - { - // ATTENTION: Reversing is not yet done!!! - const SMDS_PolyhedralVolumeOfNodes* aPolyedre = - dynamic_cast( elem ); - if (!aPolyedre) { - MESSAGE("Warning: bad volumic element"); - continue; - } + if ( iNode != nbNodes ) + continue; // not all nodes transformed - vector poly_nodes; - vector quantities; + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolygonalFace(poly_nodes)); + srcElems.Append( elem ); + } + else { + aMesh->ChangePolygonNodes(elem, poly_nodes); + } + } + break; - bool allTransformed = true; - int nbFaces = aPolyedre->NbFaces(); - for (int iface = 1; iface <= nbFaces && allTransformed; iface++) { - int nbFaceNodes = aPolyedre->NbFaceNodes(iface); - for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) { - const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); - if (nodeMapIt == nodeMap.end()) { - allTransformed = false; // not all nodes transformed - } else { - poly_nodes.push_back((*nodeMapIt).second); - } + case SMDSGeom_POLYHEDRA: // ------------------ polyhedral volume + { + const SMDS_VtkVolume* aPolyedre = + dynamic_cast( elem ); + if (!aPolyedre) { + MESSAGE("Warning: bad volumic element"); + continue; + } + + vector poly_nodes; poly_nodes.reserve( nbNodes ); + vector quantities; quantities.reserve( nbNodes ); + + bool allTransformed = true; + int nbFaces = aPolyedre->NbFaces(); + for (int iface = 1; iface <= nbFaces && allTransformed; iface++) { + int nbFaceNodes = aPolyedre->NbFaceNodes(iface); + for (int inode = 1; inode <= nbFaceNodes && allTransformed; inode++) { + const SMDS_MeshNode* node = aPolyedre->GetFaceNode(iface, inode); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find(node); + if (nodeMapIt == nodeMap.end()) { + allTransformed = false; // not all nodes transformed + } else { + poly_nodes.push_back((*nodeMapIt).second); } - quantities.push_back(nbFaceNodes); + if ( needReverse && allTransformed ) + std::reverse( poly_nodes.end() - nbFaceNodes, poly_nodes.end() ); } - if ( !allTransformed ) - continue; // not all nodes transformed + quantities.push_back(nbFaceNodes); + } + if ( !allTransformed ) + continue; // not all nodes transformed - if ( theTargetMesh ) { - myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities)); - srcElems.Append( elem ); - } - else if ( theCopy ) { - myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities)); - srcElems.Append( elem ); - } - else { - aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); - } - } - break; - default:; - } - continue; - } - - // Regular elements - int* i = index[ FORWARD ]; - if ( needReverse && nbNodes > 2) // reverse mirrored faces and volumes - if ( elemType == SMDSAbs_Face ) - i = index[ REV_FACE ]; - else - i = index[ nbNodes - 4 ]; - - if(elem->IsQuadratic()) { - static int anIds[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}; - i = anIds; - if(needReverse) { - if(nbNodes==3) { // quadratic edge - static int anIds[] = {1,0,2}; - i = anIds; + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); } - else if(nbNodes==6) { // quadratic triangle - static int anIds[] = {0,2,1,5,4,3}; - i = anIds; + else if ( theCopy ) { + myLastCreatedElems.Append(aMesh->AddPolyhedralVolume(poly_nodes, quantities)); + srcElems.Append( elem ); } - else if(nbNodes==8) { // quadratic quadrangle - static int anIds[] = {0,3,2,1,7,6,5,4}; - i = anIds; - } - else if(nbNodes==10) { // quadratic tetrahedron of 10 nodes - static int anIds[] = {0,2,1,3,6,5,4,7,9,8}; - i = anIds; - } - else if(nbNodes==13) { // quadratic pyramid of 13 nodes - static int anIds[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; - i = anIds; + else { + aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); } - else if(nbNodes==15) { // quadratic pentahedron with 15 nodes - static int anIds[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13}; - i = anIds; + } + break; + + case SMDSGeom_BALL: // -------------------- Ball + { + if ( !theCopy && !theTargetMesh ) continue; + + TNodeNodeMap::iterator nodeMapIt = nodeMap.find( elem->GetNode(0) ); + if (nodeMapIt == nodeMap.end()) + continue; // not all nodes transformed + + double diameter = static_cast(elem)->GetDiameter(); + if ( theTargetMesh ) { + myLastCreatedElems.Append(aTgtMesh->AddBall( nodeMapIt->second, diameter )); + srcElems.Append( elem ); } - else { // nbNodes==20 - quadratic hexahedron with 20 nodes - static int anIds[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; - i = anIds; + else { + myLastCreatedElems.Append(aMesh->AddBall( nodeMapIt->second, diameter )); + srcElems.Append( elem ); } } - } + break; - // find transformed nodes - vector nodes(nbNodes); - int iNode = 0; - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { - const SMDS_MeshNode* node = - static_cast( itN->next() ); - TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node ); - if ( nodeMapIt == nodeMap.end() ) - break; // not all nodes transformed - nodes[ i [ iNode++ ]] = (*nodeMapIt).second; - } - if ( iNode != nbNodes ) - continue; // not all nodes transformed + default: // ----------------------- Regular elements + + while ( iForw.size() < nbNodes ) iForw.push_back( iForw.size() ); + const std::vector& iRev = SMDS_MeshCell::reverseSmdsOrder( elem->GetEntityType() ); + const std::vector& i = needReverse ? iRev : iForw; + + // find transformed nodes + vector nodes(nbNodes); + int iNode = 0; + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + const SMDS_MeshNode* node = + static_cast( itN->next() ); + TNodeNodeMap::iterator nodeMapIt = nodeMap.find( node ); + if ( nodeMapIt == nodeMap.end() ) + break; // not all nodes transformed + nodes[ i [ iNode++ ]] = (*nodeMapIt).second; + } + if ( iNode != nbNodes ) + continue; // not all nodes transformed - if ( theTargetMesh ) { - if ( SMDS_MeshElement* copy = - targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) { - myLastCreatedElems.Append( copy ); - srcElems.Append( elem ); + if ( theTargetMesh ) { + if ( SMDS_MeshElement* copy = + targetMeshEditor.AddElement( nodes, elem->GetType(), elem->IsPoly() )) { + myLastCreatedElems.Append( copy ); + srcElems.Append( elem ); + } } - } - else if ( theCopy ) { - if ( SMDS_MeshElement* copy = AddElement( nodes, elem->GetType(), elem->IsPoly() )) { - myLastCreatedElems.Append( copy ); - srcElems.Append( elem ); + else if ( theCopy ) { + if ( AddElement( nodes, elem->GetType(), elem->IsPoly() )) + srcElems.Append( elem ); } - } - else { - // reverse element as it was reversed by transformation - if ( nbNodes > 2 ) - aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes ); - } - } + else { + // reverse element as it was reversed by transformation + if ( nbNodes > 2 ) + aMesh->ChangeElementNodes( elem, &nodes[0], nbNodes ); + } + } // switch ( geomType ) + + } // loop on elements PGroupIDs newGroupIDs; - if ( theMakeGroups && theCopy || - theMakeGroups && theTargetMesh ) + if ( ( theMakeGroups && theCopy ) || + ( theMakeGroups && theTargetMesh ) ) newGroupIDs = generateGroups( srcNodes, srcElems, groupPostfix, theTargetMesh ); return newGroupIDs; @@ -4515,9 +5935,9 @@ SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens, if ( resElem != sourceElem ) resultElems.push_back( resElem ); // do not generate element groups from node ones - if ( sourceElem->GetType() == SMDSAbs_Node && - elems( iElem )->GetType() != SMDSAbs_Node ) - continue; +// if ( sourceElem->GetType() == SMDSAbs_Node && +// elems( iElem )->GetType() != SMDSAbs_Node ) +// continue; // add resultElems to groups made by ones the sourceElem belongs to list< TOldNewGroup >::iterator gOldNew, gLast = groupsOldNew.end(); @@ -4569,33 +5989,32 @@ SMESH_MeshEditor::generateGroups(const SMESH_SequenceOfElemPtr& nodeGens, return newGroupIDs; } -//======================================================================= -//function : FindCoincidentNodes -//purpose : Return list of group of nodes close to each other within theTolerance -// Search among theNodes or in the whole mesh if theNodes is empty using -// an Octree algorithm -//======================================================================= +//================================================================================ +/*! + * \brief Return list of group of nodes close to each other within theTolerance + * Search among theNodes or in the whole mesh if theNodes is empty using + * an Octree algorithm + */ +//================================================================================ -void SMESH_MeshEditor::FindCoincidentNodes (set & theNodes, - const double theTolerance, - TListOfListOfNodes & theGroupsOfNodes) +void SMESH_MeshEditor::FindCoincidentNodes (TIDSortedNodeSet & theNodes, + const double theTolerance, + TListOfListOfNodes & theGroupsOfNodes) { myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); - set nodes; if ( theNodes.empty() ) { // get all nodes in the mesh - SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(); + SMDS_NodeIteratorPtr nIt = GetMeshDS()->nodesIterator(/*idInceasingOrder=*/true); while ( nIt->more() ) - nodes.insert( nodes.end(),nIt->next()); + theNodes.insert( theNodes.end(),nIt->next()); } - else - nodes=theNodes; - SMESH_OctreeNode::FindCoincidentNodes ( nodes, &theGroupsOfNodes, theTolerance); + SMESH_OctreeNode::FindCoincidentNodes ( theNodes, &theGroupsOfNodes, theTolerance); } + //======================================================================= /*! * \brief Implementation of search for the node closest to point @@ -4604,31 +6023,57 @@ void SMESH_MeshEditor::FindCoincidentNodes (set & theNodes struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher { + //--------------------------------------------------------------------- /*! * \brief Constructor */ SMESH_NodeSearcherImpl( const SMESHDS_Mesh* theMesh ) { - set nodes; + myMesh = ( SMESHDS_Mesh* ) theMesh; + + TIDSortedNodeSet nodes; if ( theMesh ) { - SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(); + SMDS_NodeIteratorPtr nIt = theMesh->nodesIterator(/*idInceasingOrder=*/true); while ( nIt->more() ) nodes.insert( nodes.end(), nIt->next() ); } myOctreeNode = new SMESH_OctreeNode(nodes) ; + + // get max size of a leaf box + SMESH_OctreeNode* tree = myOctreeNode; + while ( !tree->isLeaf() ) + { + SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator(); + if ( cIt->more() ) + tree = cIt->next(); + } + myHalfLeafSize = tree->maxSize() / 2.; + } + + //--------------------------------------------------------------------- + /*! + * \brief Move node and update myOctreeNode accordingly + */ + void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt ) + { + myOctreeNode->UpdateByMoveNode( node, toPnt ); + myMesh->MoveNode( node, toPnt.X(), toPnt.Y(), toPnt.Z() ); } + + //--------------------------------------------------------------------- /*! * \brief Do it's job */ const SMDS_MeshNode* FindClosestTo( const gp_Pnt& thePnt ) { - SMDS_MeshNode tgtNode( thePnt.X(), thePnt.Y(), thePnt.Z() ); + map dist2Nodes; + myOctreeNode->NodesAround( thePnt.Coord(), dist2Nodes, myHalfLeafSize ); + if ( !dist2Nodes.empty() ) + return dist2Nodes.begin()->second; list nodes; - const double precision = 1e-6; - myOctreeNode->NodesAround( &tgtNode, &nodes, precision ); + //myOctreeNode->NodesAround( &tgtNode, &nodes, myHalfLeafSize ); double minSqDist = DBL_MAX; - Bnd_B3d box; if ( nodes.empty() ) // get all nodes of OctreeNode's closest to thePnt { // sort leafs by their distance from thePnt @@ -4637,20 +6082,26 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher list< SMESH_OctreeNode* > treeList; list< SMESH_OctreeNode* >::iterator trIt; treeList.push_back( myOctreeNode ); + + gp_XYZ pointNode( thePnt.X(), thePnt.Y(), thePnt.Z() ); + bool pointInside = myOctreeNode->isInside( pointNode, myHalfLeafSize ); for ( trIt = treeList.begin(); trIt != treeList.end(); ++trIt) { SMESH_OctreeNode* tree = *trIt; - if ( !tree->isLeaf() ) { // put children to the queue + if ( !tree->isLeaf() ) // put children to the queue + { + if ( pointInside && !tree->isInside( pointNode, myHalfLeafSize )) continue; SMESH_OctreeNodeIteratorPtr cIt = tree->GetChildrenIterator(); while ( cIt->more() ) treeList.push_back( cIt->next() ); } - else if ( tree->NbNodes() ) { // put tree to treeMap - tree->getBox( box ); + else if ( tree->NbNodes() ) // put a tree to the treeMap + { + const Bnd_B3d& box = tree->getBox(); double sqDist = thePnt.SquareDistance( 0.5 * ( box.CornerMin() + box.CornerMax() )); pair it_in = treeMap.insert( make_pair( sqDist, tree )); if ( !it_in.second ) // not unique distance to box center - treeMap.insert( it_in.first, make_pair( sqDist - 1e-13*treeMap.size(), tree )); + treeMap.insert( it_in.first, make_pair( sqDist + 1e-13*treeMap.size(), tree )); } } // find distance after which there is no sense to check tree's @@ -4658,7 +6109,7 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher TDistTreeMap::iterator sqDist_tree = treeMap.begin(); if ( treeMap.size() > 5 ) { SMESH_OctreeNode* closestTree = sqDist_tree->second; - closestTree->getBox( box ); + const Bnd_B3d& box = closestTree->getBox(); double limit = sqrt( sqDist_tree->first ) + sqrt ( box.SquareExtent() ); sqLimit = limit * limit; } @@ -4675,7 +6126,7 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher const SMDS_MeshNode* closestNode = 0; list::iterator nIt = nodes.begin(); for ( ; nIt != nodes.end(); ++nIt ) { - double sqDist = thePnt.SquareDistance( TNodeXYZ( *nIt ) ); + double sqDist = thePnt.SquareDistance( SMESH_TNodeXYZ( *nIt ) ); if ( minSqDist > sqDist ) { closestNode = *nIt; minSqDist = sqDist; @@ -4683,12 +6134,23 @@ struct SMESH_NodeSearcherImpl: public SMESH_NodeSearcher } return closestNode; } + + //--------------------------------------------------------------------- /*! * \brief Destructor */ ~SMESH_NodeSearcherImpl() { delete myOctreeNode; } + + //--------------------------------------------------------------------- + /*! + * \brief Return the node tree + */ + const SMESH_OctreeNode* getTree() const { return myOctreeNode; } + private: SMESH_OctreeNode* myOctreeNode; + SMESHDS_Mesh* myMesh; + double myHalfLeafSize; // max size of a leaf box }; //======================================================================= @@ -4702,156 +6164,1400 @@ SMESH_NodeSearcher* SMESH_MeshEditor::GetNodeSearcher() return new SMESH_NodeSearcherImpl( GetMeshDS() ); } -//======================================================================= -//function : SimplifyFace -//purpose : -//======================================================================= -int SMESH_MeshEditor::SimplifyFace (const vector faceNodes, - vector& poly_nodes, - vector& quantities) const +// ======================================================================== +namespace // Utils used in SMESH_ElementSearcherImpl::FindElementsByPoint() { - int nbNodes = faceNodes.size(); + const int MaxNbElemsInLeaf = 10; // maximal number of elements in a leaf of tree + const int MaxLevel = 7; // maximal tree height -> nb terminal boxes: 8^7 = 2097152 + const double NodeRadius = 1e-9; // to enlarge bnd box of element - if (nbNodes < 3) - return 0; + //======================================================================= + /*! + * \brief Octal tree of bounding boxes of elements + */ + //======================================================================= - set nodeSet; + class ElementBndBoxTree : public SMESH_Octree + { + public: + + ElementBndBoxTree(const SMDS_Mesh& mesh, + SMDSAbs_ElementType elemType, + SMDS_ElemIteratorPtr theElemIt = SMDS_ElemIteratorPtr(), + double tolerance = NodeRadius ); + void getElementsNearPoint( const gp_Pnt& point, TIDSortedElemSet& foundElems ); + void getElementsNearLine ( const gp_Ax1& line, TIDSortedElemSet& foundElems); + void getElementsInSphere ( const gp_XYZ& center, + const double radius, TIDSortedElemSet& foundElems); + size_t getSize() { return std::max( _size, _elements.size() ); } + ~ElementBndBoxTree(); + + protected: + ElementBndBoxTree():_size(0) {} + SMESH_Octree* allocateOctreeChild() const { return new ElementBndBoxTree; } + void buildChildrenData(); + Bnd_B3d* buildRootBox(); + private: + //!< Bounding box of element + struct ElementBox : public Bnd_B3d + { + const SMDS_MeshElement* _element; + int _refCount; // an ElementBox can be included in several tree branches + ElementBox(const SMDS_MeshElement* elem, double tolerance); + }; + vector< ElementBox* > _elements; + size_t _size; + }; - // get simple seq of nodes - //const SMDS_MeshNode* simpleNodes[ nbNodes ]; - vector simpleNodes( nbNodes ); - int iSimple = 0, nbUnique = 0; + //================================================================================ + /*! + * \brief ElementBndBoxTree creation + */ + //================================================================================ - simpleNodes[iSimple++] = faceNodes[0]; - nbUnique++; - for (int iCur = 1; iCur < nbNodes; iCur++) { - if (faceNodes[iCur] != simpleNodes[iSimple - 1]) { - simpleNodes[iSimple++] = faceNodes[iCur]; - if (nodeSet.insert( faceNodes[iCur] ).second) - nbUnique++; - } + ElementBndBoxTree::ElementBndBoxTree(const SMDS_Mesh& mesh, SMDSAbs_ElementType elemType, SMDS_ElemIteratorPtr theElemIt, double tolerance) + :SMESH_Octree( new SMESH_Octree::Limit( MaxLevel, /*minSize=*/0. )) + { + int nbElems = mesh.GetMeshInfo().NbElements( elemType ); + _elements.reserve( nbElems ); + + SMDS_ElemIteratorPtr elemIt = theElemIt ? theElemIt : mesh.elementsIterator( elemType ); + while ( elemIt->more() ) + _elements.push_back( new ElementBox( elemIt->next(),tolerance )); + + compute(); } - int nbSimple = iSimple; - if (simpleNodes[nbSimple - 1] == simpleNodes[0]) { - nbSimple--; - iSimple--; + + //================================================================================ + /*! + * \brief Destructor + */ + //================================================================================ + + ElementBndBoxTree::~ElementBndBoxTree() + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( --_elements[i]->_refCount <= 0 ) + delete _elements[i]; } - if (nbUnique < 3) - return 0; + //================================================================================ + /*! + * \brief Return the maximal box + */ + //================================================================================ - // separate loops - int nbNew = 0; - bool foundLoop = (nbSimple > nbUnique); - while (foundLoop) { - foundLoop = false; - set loopSet; - for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) { - const SMDS_MeshNode* n = simpleNodes[iSimple]; - if (!loopSet.insert( n ).second) { - foundLoop = true; + Bnd_B3d* ElementBndBoxTree::buildRootBox() + { + Bnd_B3d* box = new Bnd_B3d; + for ( int i = 0; i < _elements.size(); ++i ) + box->Add( *_elements[i] ); + return box; + } - // separate loop - int iC = 0, curLast = iSimple; - for (; iC < curLast; iC++) { - if (simpleNodes[iC] == n) break; - } - int loopLen = curLast - iC; - if (loopLen > 2) { - // create sub-element - nbNew++; - quantities.push_back(loopLen); - for (; iC < curLast; iC++) { - poly_nodes.push_back(simpleNodes[iC]); - } - } - // shift the rest nodes (place from the first loop position) - for (iC = curLast + 1; iC < nbSimple; iC++) { - simpleNodes[iC - loopLen] = simpleNodes[iC]; + //================================================================================ + /*! + * \brief Redistrubute element boxes among children + */ + //================================================================================ + + void ElementBndBoxTree::buildChildrenData() + { + for ( int i = 0; i < _elements.size(); ++i ) + { + for (int j = 0; j < 8; j++) + { + if ( !_elements[i]->IsOut( myChildren[j]->getBox() )) + { + _elements[i]->_refCount++; + ((ElementBndBoxTree*)myChildren[j])->_elements.push_back( _elements[i]); } - nbSimple -= loopLen; - iSimple -= loopLen; } - } // for (iSimple = 0; iSimple < nbSimple; iSimple++) - } // while (foundLoop) + _elements[i]->_refCount--; + } + _size = _elements.size(); + SMESHUtils::FreeVector( _elements ); // = _elements.clear() + free memory - if (iSimple > 2) { - nbNew++; - quantities.push_back(iSimple); - for (int i = 0; i < iSimple; i++) - poly_nodes.push_back(simpleNodes[i]); + for (int j = 0; j < 8; j++) + { + ElementBndBoxTree* child = static_cast( myChildren[j]); + if ( child->_elements.size() <= MaxNbElemsInLeaf ) + child->myIsLeaf = true; + + if ( child->_elements.capacity() - child->_elements.size() > 1000 ) + SMESHUtils::CompactVector( child->_elements ); + } } - return nbNew; -} + //================================================================================ + /*! + * \brief Return elements which can include the point + */ + //================================================================================ -//======================================================================= -//function : MergeNodes -//purpose : In each group, the cdr of nodes are substituted by the first one -// in all elements. -//======================================================================= + void ElementBndBoxTree::getElementsNearPoint( const gp_Pnt& point, + TIDSortedElemSet& foundElems) + { + if ( getBox().IsOut( point.XYZ() )) + return; -void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) -{ - myLastCreatedElems.Clear(); - myLastCreatedNodes.Clear(); + if ( isLeaf() ) + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( !_elements[i]->IsOut( point.XYZ() )) + foundElems.insert( _elements[i]->_element ); + } + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsNearPoint( point, foundElems ); + } + } - SMESHDS_Mesh* aMesh = GetMeshDS(); + //================================================================================ + /*! + * \brief Return elements which can be intersected by the line + */ + //================================================================================ - TNodeNodeMap nodeNodeMap; // node to replace - new node - set elems; // all elements with changed nodes - list< int > rmElemIds, rmNodeIds; + void ElementBndBoxTree::getElementsNearLine( const gp_Ax1& line, + TIDSortedElemSet& foundElems) + { + if ( getBox().IsOut( line )) + return; - // Fill nodeNodeMap and elems + if ( isLeaf() ) + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( !_elements[i]->IsOut( line )) + foundElems.insert( _elements[i]->_element ); + } + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsNearLine( line, foundElems ); + } + } - TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin(); - for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) { - list& nodes = *grIt; - list::iterator nIt = nodes.begin(); - const SMDS_MeshNode* nToKeep = *nIt; - for ( ++nIt; nIt != nodes.end(); nIt++ ) { - const SMDS_MeshNode* nToRemove = *nIt; - nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep )); - if ( nToRemove != nToKeep ) { - rmNodeIds.push_back( nToRemove->GetID() ); - AddToSameGroups( nToKeep, nToRemove, aMesh ); - } + //================================================================================ + /*! + * \brief Return elements from leaves intersecting the sphere + */ + //================================================================================ - SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator(); - while ( invElemIt->more() ) { - const SMDS_MeshElement* elem = invElemIt->next(); - elems.insert(elem); - } + void ElementBndBoxTree::getElementsInSphere ( const gp_XYZ& center, + const double radius, + TIDSortedElemSet& foundElems) + { + if ( getBox().IsOut( center, radius )) + return; + + if ( isLeaf() ) + { + for ( int i = 0; i < _elements.size(); ++i ) + if ( !_elements[i]->IsOut( center, radius )) + foundElems.insert( _elements[i]->_element ); + } + else + { + for (int i = 0; i < 8; i++) + ((ElementBndBoxTree*) myChildren[i])->getElementsInSphere( center, radius, foundElems ); } } - // Change element nodes or remove an element - set::iterator eIt = elems.begin(); - for ( ; eIt != elems.end(); eIt++ ) { - const SMDS_MeshElement* elem = *eIt; - int nbNodes = elem->NbNodes(); - int aShapeId = FindShape( elem ); + //================================================================================ + /*! + * \brief Construct the element box + */ + //================================================================================ - set nodeSet; - vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes ); - int iUnique = 0, iCur = 0, nbRepl = 0; - vector iRepl( nbNodes ); + ElementBndBoxTree::ElementBox::ElementBox(const SMDS_MeshElement* elem, double tolerance) + { + _element = elem; + _refCount = 1; + SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); + while ( nIt->more() ) + Add( SMESH_TNodeXYZ( nIt->next() )); + Enlarge( tolerance ); + } - // get new seq of nodes - SMDS_ElemIteratorPtr itN = elem->nodesIterator(); - while ( itN->more() ) { - const SMDS_MeshNode* n = - static_cast( itN->next() ); +} // namespace - TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n ); - if ( nnIt != nodeNodeMap.end() ) { // n sticks - n = (*nnIt).second; - iRepl[ nbRepl++ ] = iCur; +//======================================================================= +/*! + * \brief Implementation of search for the elements by point and + * of classification of point in 2D mesh + */ +//======================================================================= + +struct SMESH_ElementSearcherImpl: public SMESH_ElementSearcher +{ + SMESHDS_Mesh* _mesh; + SMDS_ElemIteratorPtr _meshPartIt; + ElementBndBoxTree* _ebbTree; + SMESH_NodeSearcherImpl* _nodeSearcher; + SMDSAbs_ElementType _elementType; + double _tolerance; + bool _outerFacesFound; + set _outerFaces; // empty means "no internal faces at all" + + SMESH_ElementSearcherImpl( SMESHDS_Mesh& mesh, SMDS_ElemIteratorPtr elemIt=SMDS_ElemIteratorPtr()) + : _mesh(&mesh),_meshPartIt(elemIt),_ebbTree(0),_nodeSearcher(0),_tolerance(-1),_outerFacesFound(false) {} + ~SMESH_ElementSearcherImpl() + { + if ( _ebbTree ) delete _ebbTree; _ebbTree = 0; + if ( _nodeSearcher ) delete _nodeSearcher; _nodeSearcher = 0; + } + virtual int FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements); + virtual TopAbs_State GetPointState(const gp_Pnt& point); + virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point, + SMDSAbs_ElementType type ); + + void GetElementsNearLine( const gp_Ax1& line, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElems); + double getTolerance(); + bool getIntersParamOnLine(const gp_Lin& line, const SMDS_MeshElement* face, + const double tolerance, double & param); + void findOuterBoundary(const SMDS_MeshElement* anyOuterFace); + bool isOuterBoundary(const SMDS_MeshElement* face) const + { + return _outerFaces.empty() || _outerFaces.count(face); + } + struct TInters //!< data of intersection of the line and the mesh face (used in GetPointState()) + { + const SMDS_MeshElement* _face; + gp_Vec _faceNorm; + bool _coincides; //!< the line lays in face plane + TInters(const SMDS_MeshElement* face, const gp_Vec& faceNorm, bool coinc=false) + : _face(face), _faceNorm( faceNorm ), _coincides( coinc ) {} + }; + struct TFaceLink //!< link and faces sharing it (used in findOuterBoundary()) + { + SMESH_TLink _link; + TIDSortedElemSet _faces; + TFaceLink( const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshElement* face) + : _link( n1, n2 ), _faces( &face, &face + 1) {} + }; +}; + +ostream& operator<< (ostream& out, const SMESH_ElementSearcherImpl::TInters& i) +{ + return out << "TInters(face=" << ( i._face ? i._face->GetID() : 0) + << ", _coincides="<GetMeshInfo(); + + _tolerance = 0; + if ( _nodeSearcher && meshInfo.NbNodes() > 1 ) + { + double boxSize = _nodeSearcher->getTree()->maxSize(); + _tolerance = 1e-8 * boxSize/* / meshInfo.NbNodes()*/; + } + else if ( _ebbTree && meshInfo.NbElements() > 0 ) + { + double boxSize = _ebbTree->maxSize(); + _tolerance = 1e-8 * boxSize/* / meshInfo.NbElements()*/; + } + if ( _tolerance == 0 ) + { + // define tolerance by size of a most complex element + int complexType = SMDSAbs_Volume; + while ( complexType > SMDSAbs_All && + meshInfo.NbElements( SMDSAbs_ElementType( complexType )) < 1 ) + --complexType; + if ( complexType == SMDSAbs_All ) return 0; // empty mesh + double elemSize; + if ( complexType == int( SMDSAbs_Node )) + { + SMDS_NodeIteratorPtr nodeIt = _mesh->nodesIterator(); + elemSize = 1; + if ( meshInfo.NbNodes() > 2 ) + elemSize = SMESH_TNodeXYZ( nodeIt->next() ).Distance( nodeIt->next() ); + } + else + { + SMDS_ElemIteratorPtr elemIt = + _mesh->elementsIterator( SMDSAbs_ElementType( complexType )); + const SMDS_MeshElement* elem = elemIt->next(); + SMDS_ElemIteratorPtr nodeIt = elem->nodesIterator(); + SMESH_TNodeXYZ n1( cast2Node( nodeIt->next() )); + elemSize = 0; + while ( nodeIt->more() ) + { + double dist = n1.Distance( cast2Node( nodeIt->next() )); + elemSize = max( dist, elemSize ); + } + } + _tolerance = 1e-4 * elemSize; + } + } + return _tolerance; +} + +//================================================================================ +/*! + * \brief Find intersection of the line and an edge of face and return parameter on line + */ +//================================================================================ + +bool SMESH_ElementSearcherImpl::getIntersParamOnLine(const gp_Lin& line, + const SMDS_MeshElement* face, + const double tol, + double & param) +{ + int nbInts = 0; + param = 0; + + GeomAPI_ExtremaCurveCurve anExtCC; + Handle(Geom_Curve) lineCurve = new Geom_Line( line ); + + int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes(); + for ( int i = 0; i < nbNodes && nbInts < 2; ++i ) + { + GC_MakeSegment edge( SMESH_TNodeXYZ( face->GetNode( i )), + SMESH_TNodeXYZ( face->GetNode( (i+1)%nbNodes) )); + anExtCC.Init( lineCurve, edge); + if ( anExtCC.NbExtrema() > 0 && anExtCC.LowerDistance() <= tol) + { + Quantity_Parameter pl, pe; + anExtCC.LowerDistanceParameters( pl, pe ); + param += pl; + if ( ++nbInts == 2 ) + break; + } + } + if ( nbInts > 0 ) param /= nbInts; + return nbInts > 0; +} +//================================================================================ +/*! + * \brief Find all faces belonging to the outer boundary of mesh + */ +//================================================================================ + +void SMESH_ElementSearcherImpl::findOuterBoundary(const SMDS_MeshElement* outerFace) +{ + if ( _outerFacesFound ) return; + + // Collect all outer faces by passing from one outer face to another via their links + // and BTW find out if there are internal faces at all. + + // checked links and links where outer boundary meets internal one + set< SMESH_TLink > visitedLinks, seamLinks; + + // links to treat with already visited faces sharing them + list < TFaceLink > startLinks; + + // load startLinks with the first outerFace + startLinks.push_back( TFaceLink( outerFace->GetNode(0), outerFace->GetNode(1), outerFace)); + _outerFaces.insert( outerFace ); + + TIDSortedElemSet emptySet; + while ( !startLinks.empty() ) + { + const SMESH_TLink& link = startLinks.front()._link; + TIDSortedElemSet& faces = startLinks.front()._faces; + + outerFace = *faces.begin(); + // find other faces sharing the link + const SMDS_MeshElement* f; + while (( f = SMESH_MeshEditor::FindFaceInSet(link.node1(), link.node2(), emptySet, faces ))) + faces.insert( f ); + + // select another outer face among the found + const SMDS_MeshElement* outerFace2 = 0; + if ( faces.size() == 2 ) + { + outerFace2 = (outerFace == *faces.begin() ? *faces.rbegin() : *faces.begin()); + } + else if ( faces.size() > 2 ) + { + seamLinks.insert( link ); + + // link direction within the outerFace + gp_Vec n1n2( SMESH_TNodeXYZ( link.node1()), + SMESH_TNodeXYZ( link.node2())); + int i1 = outerFace->GetNodeIndex( link.node1() ); + int i2 = outerFace->GetNodeIndex( link.node2() ); + bool rev = ( abs(i2-i1) == 1 ? i1 > i2 : i2 > i1 ); + if ( rev ) n1n2.Reverse(); + // outerFace normal + gp_XYZ ofNorm, fNorm; + if ( SMESH_Algo::FaceNormal( outerFace, ofNorm, /*normalized=*/false )) + { + // direction from the link inside outerFace + gp_Vec dirInOF = gp_Vec( ofNorm ) ^ n1n2; + // sort all other faces by angle with the dirInOF + map< double, const SMDS_MeshElement* > angle2Face; + set< const SMDS_MeshElement*, TIDCompare >::const_iterator face = faces.begin(); + for ( ; face != faces.end(); ++face ) + { + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false )) + continue; + gp_Vec dirInF = gp_Vec( fNorm ) ^ n1n2; + double angle = dirInOF.AngleWithRef( dirInF, n1n2 ); + if ( angle < 0 ) angle += 2. * M_PI; + angle2Face.insert( make_pair( angle, *face )); + } + if ( !angle2Face.empty() ) + outerFace2 = angle2Face.begin()->second; + } + } + // store the found outer face and add its links to continue seaching from + if ( outerFace2 ) + { + _outerFaces.insert( outerFace ); + int nbNodes = outerFace2->NbNodes()/( outerFace2->IsQuadratic() ? 2 : 1 ); + for ( int i = 0; i < nbNodes; ++i ) + { + SMESH_TLink link2( outerFace2->GetNode(i), outerFace2->GetNode((i+1)%nbNodes)); + if ( visitedLinks.insert( link2 ).second ) + startLinks.push_back( TFaceLink( link2.node1(), link2.node2(), outerFace2 )); + } + } + startLinks.pop_front(); + } + _outerFacesFound = true; + + if ( !seamLinks.empty() ) + { + // There are internal boundaries touching the outher one, + // find all faces of internal boundaries in order to find + // faces of boundaries of holes, if any. + + } + else + { + _outerFaces.clear(); + } +} + +//======================================================================= +/*! + * \brief Find elements of given type where the given point is IN or ON. + * Returns nb of found elements and elements them-selves. + * + * 'ALL' type means elements of any type excluding nodes, balls and 0D elements + */ +//======================================================================= + +int SMESH_ElementSearcherImpl:: +FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElements) +{ + foundElements.clear(); + + double tolerance = getTolerance(); + + // ================================================================================= + if ( type == SMDSAbs_Node || type == SMDSAbs_0DElement || type == SMDSAbs_Ball) + { + if ( !_nodeSearcher ) + _nodeSearcher = new SMESH_NodeSearcherImpl( _mesh ); + + const SMDS_MeshNode* closeNode = _nodeSearcher->FindClosestTo( point ); + if ( !closeNode ) return foundElements.size(); + + if ( point.Distance( SMESH_TNodeXYZ( closeNode )) > tolerance ) + return foundElements.size(); // to far from any node + + if ( type == SMDSAbs_Node ) + { + foundElements.push_back( closeNode ); + } + else + { + SMDS_ElemIteratorPtr elemIt = closeNode->GetInverseElementIterator( type ); + while ( elemIt->more() ) + foundElements.push_back( elemIt->next() ); + } + } + // ================================================================================= + else // elements more complex than 0D + { + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt, tolerance ); + } + TIDSortedElemSet suspectElems; + _ebbTree->getElementsNearPoint( point, suspectElems ); + TIDSortedElemSet::iterator elem = suspectElems.begin(); + for ( ; elem != suspectElems.end(); ++elem ) + if ( !SMESH_MeshEditor::IsOut( *elem, point, tolerance )) + foundElements.push_back( *elem ); + } + return foundElements.size(); +} + +//======================================================================= +/*! + * \brief Find an element of given type most close to the given point + * + * WARNING: Only face search is implemeneted so far + */ +//======================================================================= + +const SMDS_MeshElement* +SMESH_ElementSearcherImpl::FindClosestTo( const gp_Pnt& point, + SMDSAbs_ElementType type ) +{ + const SMDS_MeshElement* closestElem = 0; + + if ( type == SMDSAbs_Face ) + { + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt ); + } + TIDSortedElemSet suspectElems; + _ebbTree->getElementsNearPoint( point, suspectElems ); + + if ( suspectElems.empty() && _ebbTree->maxSize() > 0 ) + { + gp_Pnt boxCenter = 0.5 * ( _ebbTree->getBox().CornerMin() + + _ebbTree->getBox().CornerMax() ); + double radius; + if ( _ebbTree->getBox().IsOut( point.XYZ() )) + radius = point.Distance( boxCenter ) - 0.5 * _ebbTree->maxSize(); + else + radius = _ebbTree->maxSize() / pow( 2., _ebbTree->getHeight()) / 2; + while ( suspectElems.empty() ) + { + _ebbTree->getElementsInSphere( point.XYZ(), radius, suspectElems ); + radius *= 1.1; + } + } + double minDist = std::numeric_limits::max(); + multimap< double, const SMDS_MeshElement* > dist2face; + TIDSortedElemSet::iterator elem = suspectElems.begin(); + for ( ; elem != suspectElems.end(); ++elem ) + { + double dist = SMESH_MeshEditor::GetDistance( dynamic_cast(*elem), + point ); + if ( dist < minDist + 1e-10) + { + minDist = dist; + dist2face.insert( dist2face.begin(), make_pair( dist, *elem )); + } + } + if ( !dist2face.empty() ) + { + multimap< double, const SMDS_MeshElement* >::iterator d2f = dist2face.begin(); + closestElem = d2f->second; + // if there are several elements at the same distance, select one + // with GC closest to the point + typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator; + double minDistToGC = 0; + for ( ++d2f; d2f != dist2face.end() && fabs( d2f->first - minDist ) < 1e-10; ++d2f ) + { + if ( minDistToGC == 0 ) + { + gp_XYZ gc(0,0,0); + gc = accumulate( TXyzIterator(closestElem->nodesIterator()), + TXyzIterator(), gc ) / closestElem->NbNodes(); + minDistToGC = point.SquareDistance( gc ); + } + gp_XYZ gc(0,0,0); + gc = accumulate( TXyzIterator( d2f->second->nodesIterator()), + TXyzIterator(), gc ) / d2f->second->NbNodes(); + double d = point.SquareDistance( gc ); + if ( d < minDistToGC ) + { + minDistToGC = d; + closestElem = d2f->second; + } + } + // cout << "FindClosestTo( " <GetID() << " DIST " << minDist << endl; + } + } + else + { + // NOT IMPLEMENTED SO FAR + } + return closestElem; +} + + +//================================================================================ +/*! + * \brief Classify the given point in the closed 2D mesh + */ +//================================================================================ + +TopAbs_State SMESH_ElementSearcherImpl::GetPointState(const gp_Pnt& point) +{ + double tolerance = getTolerance(); + if ( !_ebbTree || _elementType != SMDSAbs_Face ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = SMDSAbs_Face, _meshPartIt ); + } + // Algo: analyse transition of a line starting at the point through mesh boundary; + // try three lines parallel to axis of the coordinate system and perform rough + // analysis. If solution is not clear perform thorough analysis. + + const int nbAxes = 3; + gp_Dir axisDir[ nbAxes ] = { gp::DX(), gp::DY(), gp::DZ() }; + map< double, TInters > paramOnLine2TInters[ nbAxes ]; + list< TInters > tangentInters[ nbAxes ]; // of faces whose plane includes the line + multimap< int, int > nbInt2Axis; // to find the simplest case + for ( int axis = 0; axis < nbAxes; ++axis ) + { + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); + + TIDSortedElemSet suspectFaces; // faces possibly intersecting the line + _ebbTree->getElementsNearLine( lineAxis, suspectFaces ); + + // Intersect faces with the line + + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + TIDSortedElemSet::iterator face = suspectFaces.begin(); + for ( ; face != suspectFaces.end(); ++face ) + { + // get face plane + gp_XYZ fNorm; + if ( !SMESH_Algo::FaceNormal( *face, fNorm, /*normalized=*/false)) continue; + gp_Pln facePlane( SMESH_TNodeXYZ( (*face)->GetNode(0)), fNorm ); + + // perform intersection + IntAna_IntConicQuad intersection( line, IntAna_Quadric( facePlane )); + if ( !intersection.IsDone() ) + continue; + if ( intersection.IsInQuadric() ) + { + tangentInters[ axis ].push_back( TInters( *face, fNorm, true )); + } + else if ( ! intersection.IsParallel() && intersection.NbPoints() > 0 ) + { + gp_Pnt intersectionPoint = intersection.Point(1); + if ( !SMESH_MeshEditor::IsOut( *face, intersectionPoint, tolerance )) + u2inters.insert(make_pair( intersection.ParamOnConic(1), TInters( *face, fNorm ))); + } + } + // Analyse intersections roughly + + int nbInter = u2inters.size(); + if ( nbInter == 0 ) + return TopAbs_OUT; + + double f = u2inters.begin()->first, l = u2inters.rbegin()->first; + if ( nbInter == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; + + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; + + if ( (f<0) == (l<0) ) + return TopAbs_OUT; + + int nbIntBeforePoint = std::distance( u2inters.begin(), u2inters.lower_bound(0)); + int nbIntAfterPoint = nbInter - nbIntBeforePoint; + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; + + nbInt2Axis.insert( make_pair( min( nbIntBeforePoint, nbIntAfterPoint ), axis )); + + if ( _outerFacesFound ) break; // pass to thorough analysis + + } // three attempts - loop on CS axes + + // Analyse intersections thoroughly. + // We make two loops maximum, on the first one we only exclude touching intersections, + // on the second, if situation is still unclear, we gather and use information on + // position of faces (internal or outer). If faces position is already gathered, + // we make the second loop right away. + + for ( int hasPositionInfo = _outerFacesFound; hasPositionInfo < 2; ++hasPositionInfo ) + { + multimap< int, int >::const_iterator nb_axis = nbInt2Axis.begin(); + for ( ; nb_axis != nbInt2Axis.end(); ++nb_axis ) + { + int axis = nb_axis->second; + map< double, TInters > & u2inters = paramOnLine2TInters[ axis ]; + + gp_Ax1 lineAxis( point, axisDir[axis]); + gp_Lin line ( lineAxis ); + + // add tangent intersections to u2inters + double param; + list< TInters >::const_iterator tgtInt = tangentInters[ axis ].begin(); + for ( ; tgtInt != tangentInters[ axis ].end(); ++tgtInt ) + if ( getIntersParamOnLine( line, tgtInt->_face, tolerance, param )) + u2inters.insert(make_pair( param, *tgtInt )); + tangentInters[ axis ].clear(); + + // Count intersections before and after the point excluding touching ones. + // If hasPositionInfo we count intersections of outer boundary only + + int nbIntBeforePoint = 0, nbIntAfterPoint = 0; + double f = numeric_limits::max(), l = -numeric_limits::max(); + map< double, TInters >::iterator u_int1 = u2inters.begin(), u_int2 = u_int1; + bool ok = ! u_int1->second._coincides; + while ( ok && u_int1 != u2inters.end() ) + { + double u = u_int1->first; + bool touchingInt = false; + if ( ++u_int2 != u2inters.end() ) + { + // skip intersections at the same point (if the line passes through edge or node) + int nbSamePnt = 0; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + + // skip tangent intersections + int nbTgt = 0; + const SMDS_MeshElement* prevFace = u_int1->second._face; + while ( ok && u_int2->second._coincides ) + { + if ( SMESH_Algo::GetCommonNodes(prevFace , u_int2->second._face).empty() ) + ok = false; + else + { + nbTgt++; + u_int2++; + ok = ( u_int2 != u2inters.end() ); + } + } + if ( !ok ) break; + + // skip intersections at the same point after tangent intersections + if ( nbTgt > 0 ) + { + double u2 = u_int2->first; + ++u_int2; + while ( u_int2 != u2inters.end() && fabs( u_int2->first - u2 ) < tolerance ) + { + ++nbSamePnt; + ++u_int2; + } + } + // decide if we skipped a touching intersection + if ( nbSamePnt + nbTgt > 0 ) + { + double minDot = numeric_limits::max(), maxDot = -numeric_limits::max(); + map< double, TInters >::iterator u_int = u_int1; + for ( ; u_int != u_int2; ++u_int ) + { + if ( u_int->second._coincides ) continue; + double dot = u_int->second._faceNorm * line.Direction(); + if ( dot > maxDot ) maxDot = dot; + if ( dot < minDot ) minDot = dot; + } + touchingInt = ( minDot*maxDot < 0 ); + } + } + if ( !touchingInt ) + { + if ( !hasPositionInfo || isOuterBoundary( u_int1->second._face )) + { + if ( u < 0 ) + ++nbIntBeforePoint; + else + ++nbIntAfterPoint; + } + if ( u < f ) f = u; + if ( u > l ) l = u; + } + + u_int1 = u_int2; // to next intersection + + } // loop on intersections with one line + + if ( ok ) + { + if ( fabs( f ) < tolerance || fabs( l ) < tolerance ) + return TopAbs_ON; + + if ( nbIntBeforePoint == 0 || nbIntAfterPoint == 0) + return TopAbs_OUT; + + if ( nbIntBeforePoint + nbIntAfterPoint == 1 ) // not closed mesh + return fabs( f ) < tolerance ? TopAbs_ON : TopAbs_UNKNOWN; + + if ( nbIntBeforePoint == 1 || nbIntAfterPoint == 1 ) + return TopAbs_IN; + + if ( (f<0) == (l<0) ) + return TopAbs_OUT; + + if ( hasPositionInfo ) + return nbIntBeforePoint % 2 ? TopAbs_IN : TopAbs_OUT; + } + } // loop on intersections of the tree lines - thorough analysis + + if ( !hasPositionInfo ) + { + // gather info on faces position - is face in the outer boundary or not + map< double, TInters > & u2inters = paramOnLine2TInters[ 0 ]; + findOuterBoundary( u2inters.begin()->second._face ); + } + + } // two attempts - with and w/o faces position info in the mesh + + return TopAbs_UNKNOWN; +} + +//======================================================================= +/*! + * \brief Return elements possibly intersecting the line + */ +//======================================================================= + +void SMESH_ElementSearcherImpl::GetElementsNearLine( const gp_Ax1& line, + SMDSAbs_ElementType type, + vector< const SMDS_MeshElement* >& foundElems) +{ + if ( !_ebbTree || _elementType != type ) + { + if ( _ebbTree ) delete _ebbTree; + _ebbTree = new ElementBndBoxTree( *_mesh, _elementType = type, _meshPartIt ); + } + TIDSortedElemSet suspectFaces; // elements possibly intersecting the line + _ebbTree->getElementsNearLine( line, suspectFaces ); + foundElems.assign( suspectFaces.begin(), suspectFaces.end()); +} + +//======================================================================= +/*! + * \brief Return SMESH_ElementSearcher + */ +//======================================================================= + +SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher() +{ + return new SMESH_ElementSearcherImpl( *GetMeshDS() ); +} + +//======================================================================= +/*! + * \brief Return SMESH_ElementSearcher acting on a sub-set of elements + */ +//======================================================================= + +SMESH_ElementSearcher* SMESH_MeshEditor::GetElementSearcher(SMDS_ElemIteratorPtr elemIt) +{ + return new SMESH_ElementSearcherImpl( *GetMeshDS(), elemIt ); +} + +//======================================================================= +/*! + * \brief Return true if the point is IN or ON of the element + */ +//======================================================================= + +bool SMESH_MeshEditor::IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol ) +{ + if ( element->GetType() == SMDSAbs_Volume) + { + return SMDS_VolumeTool( element ).IsOut( point.X(), point.Y(), point.Z(), tol ); + } + + // get ordered nodes + + vector< gp_XYZ > xyz; + vector nodeList; + + SMDS_ElemIteratorPtr nodeIt = element->nodesIterator(); + if ( element->IsQuadratic() ) { + if (const SMDS_VtkFace* f=dynamic_cast(element)) + nodeIt = f->interlacedNodesElemIterator(); + else if (const SMDS_VtkEdge* e =dynamic_cast(element)) + nodeIt = e->interlacedNodesElemIterator(); + } + while ( nodeIt->more() ) + { + const SMDS_MeshNode* node = cast2Node( nodeIt->next() ); + xyz.push_back( SMESH_TNodeXYZ(node) ); + nodeList.push_back(node); + } + + int i, nbNodes = element->NbNodes(); + + if ( element->GetType() == SMDSAbs_Face ) // -------------------------------------------------- + { + // compute face normal + gp_Vec faceNorm(0,0,0); + xyz.push_back( xyz.front() ); + nodeList.push_back( nodeList.front() ); + for ( i = 0; i < nbNodes; ++i ) + { + gp_Vec edge1( xyz[i+1], xyz[i]); + gp_Vec edge2( xyz[i+1], xyz[(i+2)%nbNodes] ); + faceNorm += edge1 ^ edge2; + } + double normSize = faceNorm.Magnitude(); + if ( normSize <= tol ) + { + // degenerated face: point is out if it is out of all face edges + for ( i = 0; i < nbNodes; ++i ) + { + SMDS_LinearEdge edge( nodeList[i], nodeList[i+1] ); + if ( !IsOut( &edge, point, tol )) + return false; + } + return true; + } + faceNorm /= normSize; + + // check if the point lays on face plane + gp_Vec n2p( xyz[0], point ); + if ( fabs( n2p * faceNorm ) > tol ) + return true; // not on face plane + + // check if point is out of face boundary: + // define it by closest transition of a ray point->infinity through face boundary + // on the face plane. + // First, find normal of a plane perpendicular to face plane, to be used as a cutting tool + // to find intersections of the ray with the boundary. + gp_Vec ray = n2p; + gp_Vec plnNorm = ray ^ faceNorm; + normSize = plnNorm.Magnitude(); + if ( normSize <= tol ) return false; // point coincides with the first node + plnNorm /= normSize; + // for each node of the face, compute its signed distance to the plane + vector dist( nbNodes + 1); + for ( i = 0; i < nbNodes; ++i ) + { + gp_Vec n2p( xyz[i], point ); + dist[i] = n2p * plnNorm; + } + dist.back() = dist.front(); + // find the closest intersection + int iClosest = -1; + double rClosest, distClosest = 1e100;; + gp_Pnt pClosest; + for ( i = 0; i < nbNodes; ++i ) + { + double r; + if ( fabs( dist[i]) < tol ) + r = 0.; + else if ( fabs( dist[i+1]) < tol ) + r = 1.; + else if ( dist[i] * dist[i+1] < 0 ) + r = dist[i] / ( dist[i] - dist[i+1] ); + else + continue; // no intersection + gp_Pnt pInt = xyz[i] * (1.-r) + xyz[i+1] * r; + gp_Vec p2int ( point, pInt); + if ( p2int * ray > -tol ) // right half-space + { + double intDist = p2int.SquareMagnitude(); + if ( intDist < distClosest ) + { + iClosest = i; + rClosest = r; + pClosest = pInt; + distClosest = intDist; + } + } + } + if ( iClosest < 0 ) + return true; // no intesections - out + + // analyse transition + gp_Vec edge( xyz[iClosest], xyz[iClosest+1] ); + gp_Vec edgeNorm = -( edge ^ faceNorm ); // normal to intersected edge pointing out of face + gp_Vec p2int ( point, pClosest ); + bool out = (edgeNorm * p2int) < -tol; + if ( rClosest > 0. && rClosest < 1. ) // not node intersection + return out; + + // ray pass through a face node; analyze transition through an adjacent edge + gp_Pnt p1 = xyz[ (rClosest == 0.) ? ((iClosest+nbNodes-1) % nbNodes) : (iClosest+1) ]; + gp_Pnt p2 = xyz[ (rClosest == 0.) ? iClosest : ((iClosest+2) % nbNodes) ]; + gp_Vec edgeAdjacent( p1, p2 ); + gp_Vec edgeNorm2 = -( edgeAdjacent ^ faceNorm ); + bool out2 = (edgeNorm2 * p2int) < -tol; + + bool covexCorner = ( edgeNorm * edgeAdjacent * (rClosest==1. ? 1. : -1.)) < 0; + return covexCorner ? (out || out2) : (out && out2); + } + if ( element->GetType() == SMDSAbs_Edge ) // -------------------------------------------------- + { + // point is out of edge if it is NOT ON any straight part of edge + // (we consider quadratic edge as being composed of two straight parts) + for ( i = 1; i < nbNodes; ++i ) + { + gp_Vec edge( xyz[i-1], xyz[i]); + gp_Vec n1p ( xyz[i-1], point); + double dist = ( edge ^ n1p ).Magnitude() / edge.Magnitude(); + if ( dist > tol ) + continue; + gp_Vec n2p( xyz[i], point ); + if ( fabs( edge.Magnitude() - n1p.Magnitude() - n2p.Magnitude()) > tol ) + continue; + return false; // point is ON this part + } + return true; + } + // Node or 0D element ------------------------------------------------------------------------- + { + gp_Vec n2p ( xyz[0], point ); + return n2p.Magnitude() <= tol; + } + return true; +} + +//======================================================================= + +namespace +{ + // Position of a point relative to a segment + // . . + // . LEFT . + // . . + // VERTEX 1 o----ON-----> VERTEX 2 + // . . + // . RIGHT . + // . . + enum PositionName { POS_LEFT = 1, POS_VERTEX = 2, POS_RIGHT = 4, //POS_ON = 8, + POS_ALL = POS_LEFT | POS_RIGHT | POS_VERTEX }; + struct PointPos + { + PositionName _name; + int _index; // index of vertex or segment + + PointPos( PositionName n, int i=-1 ): _name(n), _index(i) {} + bool operator < (const PointPos& other ) const + { + if ( _name == other._name ) + return ( _index < 0 || other._index < 0 ) ? false : _index < other._index; + return _name < other._name; + } + }; + + //================================================================================ + /*! + * \brief Return of a point relative to a segment + * \param point2D - the point to analyze position of + * \param xyVec - end points of segments + * \param index0 - 0-based index of the first point of segment + * \param posToFindOut - flags of positions to detect + * \retval PointPos - point position + */ + //================================================================================ + + PointPos getPointPosition( const gp_XY& point2D, + const gp_XY* segEnds, + const int index0 = 0, + const int posToFindOut = POS_ALL) + { + const gp_XY& p1 = segEnds[ index0 ]; + const gp_XY& p2 = segEnds[ index0+1 ]; + const gp_XY grad = p2 - p1; + + if ( posToFindOut & POS_VERTEX ) + { + // check if the point2D is at "vertex 1" zone + gp_XY pp1[2] = { p1, gp_XY( p1.X() - grad.Y(), + p1.Y() + grad.X() ) }; + if ( getPointPosition( point2D, pp1, 0, POS_LEFT|POS_RIGHT )._name == POS_LEFT ) + return PointPos( POS_VERTEX, index0 ); + + // check if the point2D is at "vertex 2" zone + gp_XY pp2[2] = { p2, gp_XY( p2.X() - grad.Y(), + p2.Y() + grad.X() ) }; + if ( getPointPosition( point2D, pp2, 0, POS_LEFT|POS_RIGHT )._name == POS_RIGHT ) + return PointPos( POS_VERTEX, index0 + 1); + } + double edgeEquation = + ( point2D.X() - p1.X() ) * grad.Y() - ( point2D.Y() - p1.Y() ) * grad.X(); + return PointPos( edgeEquation < 0 ? POS_LEFT : POS_RIGHT, index0 ); + } +} + +//======================================================================= +/*! + * \brief Return minimal distance from a point to a face + * + * Currently we ignore non-planarity and 2nd order of face + */ +//======================================================================= + +double SMESH_MeshEditor::GetDistance( const SMDS_MeshFace* face, + const gp_Pnt& point ) +{ + double badDistance = -1; + if ( !face ) return badDistance; + + // coordinates of nodes (medium nodes, if any, ignored) + typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator; + vector xyz( TXyzIterator( face->nodesIterator()), TXyzIterator() ); + xyz.resize( face->NbCornerNodes()+1 ); + + // transformation to get xyz[0] lies on the origin, xyz[1] lies on the Z axis, + // and xyz[2] lies in the XZ plane. This is to pass to 2D space on XZ plane. + gp_Trsf trsf; + gp_Vec OZ ( xyz[0], xyz[1] ); + gp_Vec OX ( xyz[0], xyz[2] ); + if ( OZ.Magnitude() < std::numeric_limits::min() ) + { + if ( xyz.size() < 4 ) return badDistance; + OZ = gp_Vec ( xyz[0], xyz[2] ); + OX = gp_Vec ( xyz[0], xyz[3] ); + } + gp_Ax3 tgtCS; + try { + tgtCS = gp_Ax3( xyz[0], OZ, OX ); + } + catch ( Standard_Failure ) { + return badDistance; + } + trsf.SetTransformation( tgtCS ); + + // move all the nodes to 2D + vector xy( xyz.size() ); + for ( size_t i = 0;i < xyz.size()-1; ++i ) + { + gp_XYZ p3d = xyz[i]; + trsf.Transforms( p3d ); + xy[i].SetCoord( p3d.X(), p3d.Z() ); + } + xyz.back() = xyz.front(); + xy.back() = xy.front(); + + // // move the point in 2D + gp_XYZ tmpPnt = point.XYZ(); + trsf.Transforms( tmpPnt ); + gp_XY point2D( tmpPnt.X(), tmpPnt.Z() ); + + // loop on segments of the face to analyze point position ralative to the face + set< PointPos > pntPosSet; + for ( size_t i = 1; i < xy.size(); ++i ) + { + PointPos pos = getPointPosition( point2D, &xy[0], i-1 ); + pntPosSet.insert( pos ); + } + + // compute distance + PointPos pos = *pntPosSet.begin(); + // cout << "Face " << face->GetID() << " DIST: "; + switch ( pos._name ) + { + case POS_LEFT: { + // point is most close to a segment + gp_Vec p0p1( point, xyz[ pos._index ] ); + gp_Vec p1p2( xyz[ pos._index ], xyz[ pos._index+1 ]); // segment vector + p1p2.Normalize(); + double projDist = p0p1 * p1p2; // distance projected to the segment + gp_Vec projVec = p1p2 * projDist; + gp_Vec distVec = p0p1 - projVec; + // cout << distVec.Magnitude() << ", SEG " << face->GetNode(pos._index)->GetID() + // << " - " << face->GetNodeWrap(pos._index+1)->GetID() << endl; + return distVec.Magnitude(); + } + case POS_RIGHT: { + // point is inside the face + double distToFacePlane = tmpPnt.Y(); + // cout << distToFacePlane << ", INSIDE " << endl; + return Abs( distToFacePlane ); + } + case POS_VERTEX: { + // point is most close to a node + gp_Vec distVec( point, xyz[ pos._index ]); + // cout << distVec.Magnitude() << " VERTEX " << face->GetNode(pos._index)->GetID() << endl; + return distVec.Magnitude(); + } + } + return badDistance; +} + +//======================================================================= +//function : SimplifyFace +//purpose : +//======================================================================= +int SMESH_MeshEditor::SimplifyFace (const vector faceNodes, + vector& poly_nodes, + vector& quantities) const +{ + int nbNodes = faceNodes.size(); + + if (nbNodes < 3) + return 0; + + set nodeSet; + + // get simple seq of nodes + //const SMDS_MeshNode* simpleNodes[ nbNodes ]; + vector simpleNodes( nbNodes ); + int iSimple = 0, nbUnique = 0; + + simpleNodes[iSimple++] = faceNodes[0]; + nbUnique++; + for (int iCur = 1; iCur < nbNodes; iCur++) { + if (faceNodes[iCur] != simpleNodes[iSimple - 1]) { + simpleNodes[iSimple++] = faceNodes[iCur]; + if (nodeSet.insert( faceNodes[iCur] ).second) + nbUnique++; + } + } + int nbSimple = iSimple; + if (simpleNodes[nbSimple - 1] == simpleNodes[0]) { + nbSimple--; + iSimple--; + } + + if (nbUnique < 3) + return 0; + + // separate loops + int nbNew = 0; + bool foundLoop = (nbSimple > nbUnique); + while (foundLoop) { + foundLoop = false; + set loopSet; + for (iSimple = 0; iSimple < nbSimple && !foundLoop; iSimple++) { + const SMDS_MeshNode* n = simpleNodes[iSimple]; + if (!loopSet.insert( n ).second) { + foundLoop = true; + + // separate loop + int iC = 0, curLast = iSimple; + for (; iC < curLast; iC++) { + if (simpleNodes[iC] == n) break; + } + int loopLen = curLast - iC; + if (loopLen > 2) { + // create sub-element + nbNew++; + quantities.push_back(loopLen); + for (; iC < curLast; iC++) { + poly_nodes.push_back(simpleNodes[iC]); + } + } + // shift the rest nodes (place from the first loop position) + for (iC = curLast + 1; iC < nbSimple; iC++) { + simpleNodes[iC - loopLen] = simpleNodes[iC]; + } + nbSimple -= loopLen; + iSimple -= loopLen; + } + } // for (iSimple = 0; iSimple < nbSimple; iSimple++) + } // while (foundLoop) + + if (iSimple > 2) { + nbNew++; + quantities.push_back(iSimple); + for (int i = 0; i < iSimple; i++) + poly_nodes.push_back(simpleNodes[i]); + } + + return nbNew; +} + +//======================================================================= +//function : MergeNodes +//purpose : In each group, the cdr of nodes are substituted by the first one +// in all elements. +//======================================================================= + +void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) +{ + MESSAGE("MergeNodes"); + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + SMESHDS_Mesh* aMesh = GetMeshDS(); + + TNodeNodeMap nodeNodeMap; // node to replace - new node + set elems; // all elements with changed nodes + list< int > rmElemIds, rmNodeIds; + + // Fill nodeNodeMap and elems + + TListOfListOfNodes::iterator grIt = theGroupsOfNodes.begin(); + for ( ; grIt != theGroupsOfNodes.end(); grIt++ ) { + list& nodes = *grIt; + list::iterator nIt = nodes.begin(); + const SMDS_MeshNode* nToKeep = *nIt; + //MESSAGE("node to keep " << nToKeep->GetID()); + for ( ++nIt; nIt != nodes.end(); nIt++ ) { + const SMDS_MeshNode* nToRemove = *nIt; + nodeNodeMap.insert( TNodeNodeMap::value_type( nToRemove, nToKeep )); + if ( nToRemove != nToKeep ) { + //MESSAGE(" node to remove " << nToRemove->GetID()); + rmNodeIds.push_back( nToRemove->GetID() ); + AddToSameGroups( nToKeep, nToRemove, aMesh ); + } + + SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator(); + while ( invElemIt->more() ) { + const SMDS_MeshElement* elem = invElemIt->next(); + elems.insert(elem); + } + } + } + // Change element nodes or remove an element + + set::iterator eIt = elems.begin(); + for ( ; eIt != elems.end(); eIt++ ) { + const SMDS_MeshElement* elem = *eIt; + //MESSAGE(" ---- inverse elem on node to remove " << elem->GetID()); + int nbNodes = elem->NbNodes(); + int aShapeId = FindShape( elem ); + + set nodeSet; + vector< const SMDS_MeshNode*> curNodes( nbNodes ), uniqueNodes( nbNodes ); + int iUnique = 0, iCur = 0, nbRepl = 0; + vector iRepl( nbNodes ); + + // get new seq of nodes + SMDS_ElemIteratorPtr itN = elem->nodesIterator(); + while ( itN->more() ) { + const SMDS_MeshNode* n = + static_cast( itN->next() ); + + TNodeNodeMap::iterator nnIt = nodeNodeMap.find( n ); + if ( nnIt != nodeNodeMap.end() ) { // n sticks + n = (*nnIt).second; + // BUG 0020185: begin + { + bool stopRecur = false; + set nodesRecur; + nodesRecur.insert(n); + while (!stopRecur) { + TNodeNodeMap::iterator nnIt_i = nodeNodeMap.find( n ); + if ( nnIt_i != nodeNodeMap.end() ) { // n sticks + n = (*nnIt_i).second; + if (!nodesRecur.insert(n).second) { + // error: recursive dependancy + stopRecur = true; + } + } + else + stopRecur = true; + } + } + // BUG 0020185: end } curNodes[ iCur ] = n; bool isUnique = nodeSet.insert( n ).second; if ( isUnique ) uniqueNodes[ iUnique++ ] = n; + else + iRepl[ nbRepl++ ] = iCur; iCur++; } @@ -4859,6 +7565,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) bool isOk = true; int nbUniqueNodes = nodeSet.size(); + //MESSAGE("nbNodes nbUniqueNodes " << nbNodes << " " << nbUniqueNodes); if ( nbNodes != nbUniqueNodes ) { // some nodes stick // Polygons and Polyhedral volumes if (elem->IsPoly()) { @@ -4874,10 +7581,9 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) vector polygons_nodes; vector quantities; int nbNew = SimplifyFace(face_nodes, polygons_nodes, quantities); - if (nbNew > 0) { inode = 0; - for (int iface = 0; iface < nbNew - 1; iface++) { + for (int iface = 0; iface < nbNew; iface++) { int nbNodes = quantities[iface]; vector poly_nodes (nbNodes); for (int ii = 0; ii < nbNodes; ii++, inode++) { @@ -4888,7 +7594,19 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) if (aShapeId) aMesh->SetMeshElementOnShape(newElem, aShapeId); } - aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]); + + MESSAGE("ChangeElementNodes MergeNodes Polygon"); + //aMesh->ChangeElementNodes(elem, &polygons_nodes[inode], quantities[nbNew - 1]); + vector polynodes(polygons_nodes.begin()+inode,polygons_nodes.end()); + int quid =0; + if (nbNew > 0) quid = nbNew - 1; + vector newquant(quantities.begin()+quid, quantities.end()); + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddPolyhedralVolume(polynodes, newquant); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + rmElemIds.push_back(elem->GetID()); } else { rmElemIds.push_back(elem->GetID()); @@ -4901,9 +7619,9 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) rmElemIds.push_back(elem->GetID()); } else { - // each face has to be analized in order to check volume validity - const SMDS_PolyhedralVolumeOfNodes* aPolyedre = - static_cast( elem ); + // each face has to be analyzed in order to check volume validity + const SMDS_VtkVolume* aPolyedre = + dynamic_cast( elem ); if (aPolyedre) { int nbFaces = aPolyedre->NbFaces(); @@ -4931,10 +7649,16 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } if (quantities.size() > 3) - aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); - else - rmElemIds.push_back(elem->GetID()); - + { + MESSAGE("ChangeElementNodes MergeNodes Polyhedron"); + //aMesh->ChangePolyhedronNodes(elem, poly_nodes, quantities); + const SMDS_MeshElement* newElem = 0; + newElem = aMesh->AddPolyhedralVolume(poly_nodes, quantities); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + rmElemIds.push_back(elem->GetID()); + } } else { rmElemIds.push_back(elem->GetID()); @@ -4945,9 +7669,10 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } continue; - } + } // poly element // Regular elements + // TODO not all the possible cases are solved. Find something more generic? switch ( nbNodes ) { case 2: ///////////////////////////////////// EDGE isOk = false; break; @@ -4961,6 +7686,7 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) isOk = false; else if ( nbRepl == 2 && iRepl[ 1 ] - iRepl[ 0 ] == 2 ) isOk = false; // opposite nodes stick + //MESSAGE("isOk " << isOk); } break; case 6: ///////////////////////////////////// PENTAHEDRON @@ -5025,7 +7751,11 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) // +---+---+ // 0 7 3 isOk = false; + if(nbRepl==2) { + MESSAGE("nbRepl=2: " << iRepl[0] << " " << iRepl[1]); + } if(nbRepl==3) { + MESSAGE("nbRepl=3: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2]); nbUniqueNodes = 6; if( iRepl[0]==0 && iRepl[1]==1 && iRepl[2]==4 ) { uniqueNodes[0] = curNodes[0]; @@ -5100,14 +7830,20 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) isOk = true; } } + if(nbRepl==4) { + MESSAGE("nbRepl=4: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3]); + } + if(nbRepl==5) { + MESSAGE("nbRepl=5: " << iRepl[0] << " " << iRepl[1] << " " << iRepl[2] << " " << iRepl[3] << " " << iRepl[4]); + } break; } //////////////////////////////////// HEXAHEDRON isOk = false; SMDS_VolumeTool hexa (elem); hexa.SetExternalNormal(); - if ( nbUniqueNodes == 4 && nbRepl == 6 ) { - //////////////////////// ---> tetrahedron + if ( nbUniqueNodes == 4 && nbRepl == 4 ) { + //////////////////////// HEX ---> 1 tetrahedron for ( int iFace = 0; iFace < 6; iFace++ ) { const int *ind = hexa.GetFaceNodesIndices( iFace ); // indices of face nodes if (curNodes[ind[ 0 ]] == curNodes[ind[ 1 ]] && @@ -5117,12 +7853,9 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) int iOppFace = hexa.GetOppFaceIndex( iFace ); ind = hexa.GetFaceNodesIndices( iOppFace ); int nbStick = 0; - iUnique = 2; // reverse a tetrahedron bottom for ( iCur = 0; iCur < 4 && nbStick < 2; iCur++ ) { if ( curNodes[ind[ iCur ]] == curNodes[ind[ iCur + 1 ]] ) nbStick++; - else if ( iUnique >= 0 ) - uniqueNodes[ iUnique-- ] = curNodes[ind[ iCur ]]; } if ( nbStick == 1 ) { // ... and the opposite one - into a triangle. @@ -5135,6 +7868,45 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } } } + else if ( nbUniqueNodes == 6 && nbRepl == 2 ) { + //////////////////////// HEX ---> 1 prism + int nbTria = 0, iTria[3]; + const int *ind; // indices of face nodes + // look for triangular faces + for ( int iFace = 0; iFace < 6 && nbTria < 3; iFace++ ) { + ind = hexa.GetFaceNodesIndices( iFace ); + TIDSortedNodeSet faceNodes; + for ( iCur = 0; iCur < 4; iCur++ ) + faceNodes.insert( curNodes[ind[iCur]] ); + if ( faceNodes.size() == 3 ) + iTria[ nbTria++ ] = iFace; + } + // check if triangles are opposite + if ( nbTria == 2 && iTria[0] == hexa.GetOppFaceIndex( iTria[1] )) + { + isOk = true; + // set nodes of the bottom triangle + ind = hexa.GetFaceNodesIndices( iTria[ 0 ]); + vector indB; + for ( iCur = 0; iCur < 4; iCur++ ) + if ( ind[iCur] != iRepl[0] && ind[iCur] != iRepl[1]) + indB.push_back( ind[iCur] ); + if ( !hexa.IsForward() ) + std::swap( indB[0], indB[2] ); + for ( iCur = 0; iCur < 3; iCur++ ) + uniqueNodes[ iCur ] = curNodes[indB[iCur]]; + // set nodes of the top triangle + const int *indT = hexa.GetFaceNodesIndices( iTria[ 1 ]); + for ( iCur = 0; iCur < 3; ++iCur ) + for ( int j = 0; j < 4; ++j ) + if ( hexa.IsLinked( indB[ iCur ], indT[ j ] )) + { + uniqueNodes[ iCur + 3 ] = curNodes[ indT[ j ]]; + break; + } + } + break; + } else if (nbUniqueNodes == 5 && nbRepl == 4 ) { //////////////////// HEXAHEDRON ---> 2 tetrahedrons for ( int iFace = 0; iFace < 6; iFace++ ) { @@ -5272,6 +8044,10 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } } } // if ( nbUniqueNodes == 6 && nbRepl == 4 ) + else + { + MESSAGE("MergeNodes() removes hexahedron "<< elem); + } break; } // HEXAHEDRON @@ -5281,11 +8057,12 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } // if ( nbNodes != nbUniqueNodes ) // some nodes stick - if ( isOk ) { - if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume) { + if ( isOk ) { // the elem remains valid after sticking nodes + if (elem->IsPoly() && elem->GetType() == SMDSAbs_Volume) + { // Change nodes of polyedre - const SMDS_PolyhedralVolumeOfNodes* aPolyedre = - static_cast( elem ); + const SMDS_VtkVolume* aPolyedre = + dynamic_cast( elem ); if (aPolyedre) { int nbFaces = aPolyedre->NbFaces(); @@ -5309,9 +8086,21 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) aMesh->ChangePolyhedronNodes( elem, poly_nodes, quantities ); } } - else { - // Change regular element or polygon - aMesh->ChangeElementNodes( elem, & uniqueNodes[0], nbUniqueNodes ); + else // replace non-polyhedron elements + { + const SMDSAbs_ElementType etyp = elem->GetType(); + const int elemId = elem->GetID(); + const bool isPoly = (elem->GetEntityType() == SMDSEntity_Polygon); + uniqueNodes.resize(nbUniqueNodes); + + SMESHDS_SubMesh * sm = aShapeId > 0 ? aMesh->MeshElements(aShapeId) : 0; + + aMesh->RemoveFreeElement(elem, sm, /*fromGroups=*/false); + SMDS_MeshElement* newElem = this->AddElement(uniqueNodes, etyp, isPoly, elemId); + if ( sm && newElem ) + sm->AddElement( newElem ); + if ( elem != newElem ) + ReplaceElemInGroups( elem, newElem, aMesh ); } } else { @@ -5321,10 +8110,10 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) } // loop on elements - // Remove equal nodes and bad elements + // Remove bad elements, then equal nodes (order important) - Remove( rmNodeIds, true ); Remove( rmElemIds, false ); + Remove( rmNodeIds, true ); } @@ -5335,24 +8124,24 @@ void SMESH_MeshEditor::MergeNodes (TListOfListOfNodes & theGroupsOfNodes) // ======================================================== class SortableElement : public set { - public: +public: SortableElement( const SMDS_MeshElement* theElem ) - { - myElem = theElem; - SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); - while ( nodeIt->more() ) - this->insert( nodeIt->next() ); - } + { + myElem = theElem; + SMDS_ElemIteratorPtr nodeIt = theElem->nodesIterator(); + while ( nodeIt->more() ) + this->insert( nodeIt->next() ); + } const SMDS_MeshElement* Get() const - { return myElem; } + { return myElem; } void Set(const SMDS_MeshElement* e) const - { myElem = e; } + { myElem = e; } - private: +private: mutable const SMDS_MeshElement* myElem; }; @@ -5362,7 +8151,7 @@ class SortableElement : public set // Search among theElements or in the whole mesh if theElements is empty //======================================================================= void SMESH_MeshEditor::FindEqualElements(set & theElements, - TListOfListOfElementsID & theGroupsOfElementsID) + TListOfListOfElementsID & theGroupsOfElementsID) { myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); @@ -5460,7 +8249,7 @@ void SMESH_MeshEditor::MergeElements(TListOfListOfElementsID & theGroupsOfElemen void SMESH_MeshEditor::MergeEqualElements() { set aMeshElements; /* empty input - - to merge equal elements in the whole mesh */ + to merge equal elements in the whole mesh */ TListOfListOfElementsID aGroupsOfElementsID; FindEqualElements(aMeshElements, aGroupsOfElementsID); MergeElements(aGroupsOfElementsID); @@ -5471,83 +8260,68 @@ void SMESH_MeshEditor::MergeEqualElements() //purpose : Return a face having linked nodes n1 and n2 and which is // - not in avoidSet, // - in elemSet provided that !elemSet.empty() +// i1 and i2 optionally returns indices of n1 and n2 //======================================================================= const SMDS_MeshElement* - SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const TIDSortedElemSet& elemSet, - const TIDSortedElemSet& avoidSet) +SMESH_MeshEditor::FindFaceInSet(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const TIDSortedElemSet& elemSet, + const TIDSortedElemSet& avoidSet, + int* n1ind, + int* n2ind) { + int i1, i2; + const SMDS_MeshElement* face = 0; + SMDS_ElemIteratorPtr invElemIt = n1->GetInverseElementIterator(SMDSAbs_Face); - while ( invElemIt->more() ) { // loop on inverse elements of n1 + //MESSAGE("n1->GetInverseElementIterator(SMDSAbs_Face) " << invElemIt); + while ( invElemIt->more() && !face ) // loop on inverse faces of n1 + { + //MESSAGE("in while ( invElemIt->more() && !face )"); const SMDS_MeshElement* elem = invElemIt->next(); - if (avoidSet.find( elem ) != avoidSet.end() ) + if (avoidSet.count( elem )) continue; - if ( !elemSet.empty() && elemSet.find( elem ) == elemSet.end()) + if ( !elemSet.empty() && !elemSet.count( elem )) continue; - // get face nodes and find index of n1 - int i1, nbN = elem->NbNodes(), iNode = 0; - //const SMDS_MeshNode* faceNodes[ nbN ], *n; - vector faceNodes( nbN ); - const SMDS_MeshNode* n; - SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); - while ( nIt->more() ) { - faceNodes[ iNode ] = static_cast( nIt->next() ); - if ( faceNodes[ iNode++ ] == n1 ) - i1 = iNode - 1; - } + // index of n1 + i1 = elem->GetNodeIndex( n1 ); // find a n2 linked to n1 - if(!elem->IsQuadratic()) { - for ( iNode = 0; iNode < 2; iNode++ ) { - if ( iNode ) // node before n1 - n = faceNodes[ i1 == 0 ? nbN - 1 : i1 - 1 ]; - else // node after n1 - n = faceNodes[ i1 + 1 == nbN ? 0 : i1 + 1 ]; - if ( n == n2 ) - return elem; - } - } - else { // analysis for quadratic elements - bool IsFind = false; - // check using only corner nodes - for ( iNode = 0; iNode < 2; iNode++ ) { - if ( iNode ) // node before n1 - n = faceNodes[ i1 == 0 ? nbN/2 - 1 : i1 - 1 ]; - else // node after n1 - n = faceNodes[ i1 + 1 == nbN/2 ? 0 : i1 + 1 ]; - if ( n == n2 ) - IsFind = true; - } - if(IsFind) { - return elem; - } - else { - // check using all nodes - const SMDS_QuadraticFaceOfNodes* F = - static_cast(elem); - // use special nodes iterator - iNode = 0; - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); - while ( anIter->more() ) { - faceNodes[iNode] = static_cast(anIter->next()); - if ( faceNodes[ iNode++ ] == n1 ) - i1 = iNode - 1; - } - for ( iNode = 0; iNode < 2; iNode++ ) { - if ( iNode ) // node before n1 - n = faceNodes[ i1 == 0 ? nbN - 1 : i1 - 1 ]; - else // node after n1 - n = faceNodes[ i1 + 1 == nbN ? 0 : i1 + 1 ]; - if ( n == n2 ) { - return elem; - } + int nbN = elem->IsQuadratic() ? elem->NbNodes()/2 : elem->NbNodes(); + for ( int di = -1; di < 2 && !face; di += 2 ) + { + i2 = (i1+di+nbN) % nbN; + if ( elem->GetNode( i2 ) == n2 ) + face = elem; + } + if ( !face && elem->IsQuadratic()) + { + // analysis for quadratic elements using all nodes + const SMDS_VtkFace* F = + dynamic_cast(elem); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); + // use special nodes iterator + SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator(); + const SMDS_MeshNode* prevN = cast2Node( anIter->next() ); + for ( i1 = -1, i2 = 0; anIter->more() && !face; i1++, i2++ ) + { + const SMDS_MeshNode* n = cast2Node( anIter->next() ); + if ( n1 == prevN && n2 == n ) + { + face = elem; + } + else if ( n2 == prevN && n1 == n ) + { + face = elem; swap( i1, i2 ); } + prevN = n; } - } // end analysis for quadratic elements + } } - return 0; + if ( n1ind ) *n1ind = i1; + if ( n2ind ) *n2ind = i2; + return face; } //======================================================================= @@ -5591,7 +8365,7 @@ bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirst //vector nodes; const SMDS_MeshNode *nIgnore = theFirstNode, *nStart = theSecondNode; - set < const SMDS_MeshElement* > foundElems; + TIDSortedElemSet foundElems; bool needTheLast = ( theLastNode != 0 ); while ( nStart != theLastNode ) { @@ -5610,14 +8384,15 @@ bool SMESH_MeshEditor::FindFreeBorder (const SMDS_MeshNode* theFirst int iNode = 0, nbNodes = e->NbNodes(); //const SMDS_MeshNode* nodes[nbNodes+1]; vector nodes(nbNodes+1); - + if(e->IsQuadratic()) { - const SMDS_QuadraticFaceOfNodes* F = - static_cast(e); + const SMDS_VtkFace* F = + dynamic_cast(e); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); // use special nodes iterator - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator(); while( anIter->more() ) { - nodes[ iNode++ ] = anIter->next(); + nodes[ iNode++ ] = cast2Node(anIter->next()); } } else { @@ -5730,15 +8505,15 @@ bool SMESH_MeshEditor::CheckFreeBorderNodes(const SMDS_MeshNode* theNode1, //======================================================================= SMESH_MeshEditor::Sew_Error - SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode, - const SMDS_MeshNode* theBordSecondNode, - const SMDS_MeshNode* theBordLastNode, - const SMDS_MeshNode* theSideFirstNode, - const SMDS_MeshNode* theSideSecondNode, - const SMDS_MeshNode* theSideThirdNode, - const bool theSideIsFreeBorder, - const bool toCreatePolygons, - const bool toCreatePolyedrs) +SMESH_MeshEditor::SewFreeBorder (const SMDS_MeshNode* theBordFirstNode, + const SMDS_MeshNode* theBordSecondNode, + const SMDS_MeshNode* theBordLastNode, + const SMDS_MeshNode* theSideFirstNode, + const SMDS_MeshNode* theSideSecondNode, + const SMDS_MeshNode* theSideThirdNode, + const bool theSideIsFreeBorder, + const bool toCreatePolygons, + const bool toCreatePolyedrs) { myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); @@ -5787,7 +8562,7 @@ SMESH_MeshEditor::Sew_Error // links of the free border // ------------------------------------------------------------------------- - // 1. Since sewing may brake if there are volumes to split on the side 2, + // 1. Since sewing may break if there are volumes to split on the side 2, // we wont move nodes but just compute new coordinates for them typedef map TNodeXYZMap; TNodeXYZMap nBordXYZ; @@ -5885,12 +8660,13 @@ SMESH_MeshEditor::Sew_Error else if ( elem->GetType()==SMDSAbs_Face ) { // --face // retrieve all face nodes and find iPrevNode - an index of the prevSideNode if(elem->IsQuadratic()) { - const SMDS_QuadraticFaceOfNodes* F = - static_cast(elem); + const SMDS_VtkFace* F = + dynamic_cast(elem); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); // use special nodes iterator - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator(); while( anIter->more() ) { - nodes[ iNode ] = anIter->next(); + nodes[ iNode ] = cast2Node(anIter->next()); if ( nodes[ iNode++ ] == prevSideNode ) iPrevNode = iNode - 1; } @@ -5992,7 +8768,7 @@ SMESH_MeshEditor::Sew_Error TListOfListOfNodes nodeGroupsToMerge; if ( nbNodes[0] == nbNodes[1] || - ( theSideIsFreeBorder && !theSideThirdNode)) { + ( theSideIsFreeBorder && !theSideThirdNode)) { // all nodes are to be merged @@ -6204,12 +8980,13 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, vector nodes( theFace->NbNodes() ); if(theFace->IsQuadratic()) { - const SMDS_QuadraticFaceOfNodes* F = - static_cast(theFace); + const SMDS_VtkFace* F = + dynamic_cast(theFace); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); // use special nodes iterator - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator(); while( anIter->more() ) { - const SMDS_MeshNode* n = anIter->next(); + const SMDS_MeshNode* n = cast2Node(anIter->next()); if ( n == theBetweenNode1 ) il1 = iNode; else if ( n == theBetweenNode2 ) @@ -6265,12 +9042,13 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, bool isFLN = false; if(theFace->IsQuadratic()) { - const SMDS_QuadraticFaceOfNodes* F = - static_cast(theFace); + const SMDS_VtkFace* F = + dynamic_cast(theFace); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); // use special nodes iterator - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); + SMDS_ElemIteratorPtr anIter = F->interlacedNodesElemIterator(); while( anIter->more() && !isFLN ) { - const SMDS_MeshNode* n = anIter->next(); + const SMDS_MeshNode* n = cast2Node(anIter->next()); poly_nodes[iNode++] = n; if (n == nodes[il1]) { isFLN = true; @@ -6283,7 +9061,7 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, } // add nodes of face starting from last node of link while ( anIter->more() ) { - poly_nodes[iNode++] = anIter->next(); + poly_nodes[iNode++] = cast2Node(anIter->next()); } } else { @@ -6326,6 +9104,7 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, return; } + SMESHDS_Mesh *aMesh = GetMeshDS(); if( !theFace->IsQuadratic() ) { // put aNodesToInsert between theBetweenNode1 and theBetweenNode2 @@ -6376,7 +9155,6 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, } // create new elements - SMESHDS_Mesh *aMesh = GetMeshDS(); int aShapeId = FindShape( theFace ); i1 = 0; i2 = 1; @@ -6402,8 +9180,16 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, newNodes[ 1 ] = linkNodes[ i2 ]; newNodes[ 2 ] = nodes[ iSplit >= iBestQuad ? i3 : i4 ]; newNodes[ 3 ] = nodes[ i4 ]; - aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 ); - } // end if(!theFace->IsQuadratic()) + //aMesh->ChangeElementNodes( theFace, newNodes, iSplit == iBestQuad ? 4 : 3 ); + const SMDS_MeshElement* newElem = 0; + if (iSplit == iBestQuad) + newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ); + else + newElem = aMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ); + myLastCreatedElems.Append(newElem); + if ( aShapeId && newElem ) + aMesh->SetMeshElementOnShape( newElem, aShapeId ); +} // end if(!theFace->IsQuadratic()) else { // theFace is quadratic // we have to split theFace on simple triangles and one simple quadrangle int tmp = il1/2; @@ -6430,7 +9216,6 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, // n4 n6 n5 n4 // create new elements - SMESHDS_Mesh *aMesh = GetMeshDS(); int aShapeId = FindShape( theFace ); int n1,n2,n3; @@ -6513,9 +9298,9 @@ void SMESH_MeshEditor::InsertNodesIntoLink(const SMDS_MeshElement* theFace, if ( aShapeId && newElem ) aMesh->SetMeshElementOnShape( newElem, aShapeId ); } - // remove old quadratic face - aMesh->RemoveElement(theFace); } + // remove old face + aMesh->RemoveElement(theFace); } //======================================================================= @@ -6604,10 +9389,34 @@ void SMESH_MeshEditor::UpdateVolumes (const SMDS_MeshNode* theBetweenNode } } +namespace +{ + //================================================================================ + /*! + * \brief Transform any volume into data of SMDSEntity_Polyhedra + */ + //================================================================================ + + void volumeToPolyhedron( const SMDS_MeshElement* elem, + vector & nodes, + vector & nbNodeInFaces ) + { + nodes.clear(); + nbNodeInFaces.clear(); + SMDS_VolumeTool vTool ( elem ); + for ( int iF = 0; iF < vTool.NbFaces(); ++iF ) + { + const SMDS_MeshNode** fNodes = vTool.GetFaceNodes( iF ); + nodes.insert( nodes.end(), fNodes, fNodes + vTool.NbFaceNodes( iF )); + nbNodeInFaces.push_back( vTool.NbFaceNodes( iF )); + } + } +} + //======================================================================= /*! * \brief Convert elements contained in a submesh to quadratic - * \retval int - nb of checked elements + * \return int - nb of checked elements */ //======================================================================= @@ -6618,7 +9427,8 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm, int nbElem = 0; if( !theSm ) return nbElem; - const bool notFromGroups = false; + vector nbNodeInFaces; + vector nodes; SMDS_ElemIteratorPtr ElemItr = theSm->GetElements(); while(ElemItr->more()) { @@ -6626,61 +9436,69 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm, const SMDS_MeshElement* elem = ElemItr->next(); if( !elem || elem->IsQuadratic() ) continue; - int id = elem->GetID(); - int nbNodes = elem->NbNodes(); - vector aNds (nbNodes); - - for(int i = 0; i < nbNodes; i++) - { - aNds[i] = elem->GetNode(i); - } - SMDSAbs_ElementType aType = elem->GetType(); - - GetMeshDS()->RemoveFreeElement(elem, theSm, notFromGroups); + // get elem data needed to re-create it + // + const int id = elem->GetID(); + const int nbNodes = elem->NbNodes(); + const SMDSAbs_ElementType aType = elem->GetType(); + const SMDSAbs_EntityType aGeomType = elem->GetEntityType(); + nodes.assign(elem->begin_nodes(), elem->end_nodes()); + if ( aGeomType == SMDSEntity_Polyhedra ) + nbNodeInFaces = static_cast( elem )->GetQuantities(); + else if ( aGeomType == SMDSEntity_Hexagonal_Prism ) + volumeToPolyhedron( elem, nodes, nbNodeInFaces ); + + // remove a linear element + GetMeshDS()->RemoveFreeElement(elem, theSm, /*fromGroups=*/false); const SMDS_MeshElement* NewElem = 0; switch( aType ) { case SMDSAbs_Edge : - { - NewElem = theHelper.AddEdge(aNds[0], aNds[1], id, theForce3d); - break; - } + { + NewElem = theHelper.AddEdge(nodes[0], nodes[1], id, theForce3d); + break; + } case SMDSAbs_Face : - { - switch(nbNodes) { - case 3: - NewElem = theHelper.AddFace(aNds[0], aNds[1], aNds[2], id, theForce3d); - break; - case 4: - NewElem = theHelper.AddFace(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d); - break; - default: - continue; + switch(nbNodes) + { + case 3: + NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d); + break; + case 4: + NewElem = theHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + default: + NewElem = theHelper.AddPolygonalFace(nodes, id, theForce3d); + continue; + } + break; } - break; - } case SMDSAbs_Volume : - { - switch(nbNodes) { - case 4: - NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d); - break; - case 6: - NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], aNds[4], aNds[5], id, theForce3d); - break; - case 8: - NewElem = theHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], - aNds[4], aNds[5], aNds[6], aNds[7], id, theForce3d); - break; - default: - continue; + switch( aGeomType ) + { + case SMDSEntity_Tetra: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + case SMDSEntity_Pyramid: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], id, theForce3d); + break; + case SMDSEntity_Penta: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], id, theForce3d); + break; + case SMDSEntity_Hexa: + NewElem = theHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d); + break; + case SMDSEntity_Hexagonal_Prism: + default: + NewElem = theHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d); + } + break; } - break; - } default : continue; } @@ -6695,13 +9513,13 @@ int SMESH_MeshEditor::convertElemToQuadratic(SMESHDS_SubMesh * theSm, //function : ConvertToQuadratic //purpose : //======================================================================= + void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d) { SMESHDS_Mesh* meshDS = GetMeshDS(); SMESH_MesherHelper aHelper(*myMesh); aHelper.SetIsQuadratic( true ); - const bool notFromGroups = false; int nbCheckedElems = 0; if ( myMesh->HasShapeToMesh() ) @@ -6728,11 +9546,12 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d) const SMDS_MeshEdge* edge = aEdgeItr->next(); if(edge && !edge->IsQuadratic()) { - int id = edge->GetID(); - const SMDS_MeshNode* n1 = edge->GetNode(0); - const SMDS_MeshNode* n2 = edge->GetNode(1); + int id = edge->GetID(); + //MESSAGE("edge->GetID() " << id); + const SMDS_MeshNode* n1 = edge->GetNode(0); + const SMDS_MeshNode* n2 = edge->GetNode(1); - meshDS->RemoveFreeElement(edge, smDS, notFromGroups); + meshDS->RemoveFreeElement(edge, smDS, /*fromGroups=*/false); const SMDS_MeshEdge* NewEdge = aHelper.AddEdge(n1, n2, id, theForce3d); ReplaceElemInGroups( edge, NewEdge, GetMeshDS()); @@ -6744,75 +9563,220 @@ void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d) const SMDS_MeshFace* face = aFaceItr->next(); if(!face || face->IsQuadratic() ) continue; - int id = face->GetID(); - int nbNodes = face->NbNodes(); - vector aNds (nbNodes); + const int id = face->GetID(); + const SMDSAbs_EntityType type = face->GetEntityType(); + vector nodes ( face->begin_nodes(), face->end_nodes()); + + meshDS->RemoveFreeElement(face, smDS, /*fromGroups=*/false); + + SMDS_MeshFace * NewFace = 0; + switch( type ) + { + case SMDSEntity_Triangle: + NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], id, theForce3d); + break; + case SMDSEntity_Quadrangle: + NewFace = aHelper.AddFace(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + default: + NewFace = aHelper.AddPolygonalFace(nodes, id, theForce3d); + } + ReplaceElemInGroups( face, NewFace, GetMeshDS()); + } + vector nbNodeInFaces; + SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator(); + while(aVolumeItr->more()) + { + const SMDS_MeshVolume* volume = aVolumeItr->next(); + if(!volume || volume->IsQuadratic() ) continue; + + const int id = volume->GetID(); + const SMDSAbs_EntityType type = volume->GetEntityType(); + vector nodes (volume->begin_nodes(), volume->end_nodes()); + if ( type == SMDSEntity_Polyhedra ) + nbNodeInFaces = static_cast(volume)->GetQuantities(); + else if ( type == SMDSEntity_Hexagonal_Prism ) + volumeToPolyhedron( volume, nodes, nbNodeInFaces ); + + meshDS->RemoveFreeElement(volume, smDS, /*fromGroups=*/false); + + SMDS_MeshVolume * NewVolume = 0; + switch ( type ) + { + case SMDSEntity_Tetra: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d ); + break; + case SMDSEntity_Hexa: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d); + break; + case SMDSEntity_Pyramid: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], + nodes[3], nodes[4], id, theForce3d); + break; + case SMDSEntity_Penta: + NewVolume = aHelper.AddVolume(nodes[0], nodes[1], nodes[2], + nodes[3], nodes[4], nodes[5], id, theForce3d); + break; + case SMDSEntity_Hexagonal_Prism: + default: + NewVolume = aHelper.AddPolyhedralVolume(nodes, nbNodeInFaces, id, theForce3d); + } + ReplaceElemInGroups(volume, NewVolume, meshDS); + } + } + + if ( !theForce3d ) + { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion + aHelper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh + aHelper.FixQuadraticElements(); + } +} + +//================================================================================ +/*! + * \brief Makes given elements quadratic + * \param theForce3d - if true, the medium nodes will be placed in the middle of link + * \param theElements - elements to make quadratic + */ +//================================================================================ - for(int i = 0; i < nbNodes; i++) +void SMESH_MeshEditor::ConvertToQuadratic(const bool theForce3d, + TIDSortedElemSet& theElements) +{ + if ( theElements.empty() ) return; + + // we believe that all theElements are of the same type + const SMDSAbs_ElementType elemType = (*theElements.begin())->GetType(); + + // get all nodes shared by theElements + TIDSortedNodeSet allNodes; + TIDSortedElemSet::iterator eIt = theElements.begin(); + for ( ; eIt != theElements.end(); ++eIt ) + allNodes.insert( (*eIt)->begin_nodes(), (*eIt)->end_nodes() ); + + // complete theElements with elements of lower dim whose all nodes are in allNodes + + TIDSortedElemSet quadAdjacentElems [ SMDSAbs_NbElementTypes ]; // quadratic adjacent elements + TIDSortedElemSet checkedAdjacentElems [ SMDSAbs_NbElementTypes ]; + TIDSortedNodeSet::iterator nIt = allNodes.begin(); + for ( ; nIt != allNodes.end(); ++nIt ) + { + const SMDS_MeshNode* n = *nIt; + SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator(); + while ( invIt->more() ) + { + const SMDS_MeshElement* e = invIt->next(); + if ( e->IsQuadratic() ) { - aNds[i] = face->GetNode(i); + quadAdjacentElems[ e->GetType() ].insert( e ); + continue; } - - meshDS->RemoveFreeElement(face, smDS, notFromGroups); - - SMDS_MeshFace * NewFace = 0; - switch(nbNodes) + if ( e->GetType() >= elemType ) { - case 3: - NewFace = aHelper.AddFace(aNds[0], aNds[1], aNds[2], id, theForce3d); - break; - case 4: - NewFace = aHelper.AddFace(aNds[0], aNds[1], aNds[2], aNds[3], id, theForce3d); - break; - default: - continue; + continue; // same type of more complex linear element } - ReplaceElemInGroups( face, NewFace, GetMeshDS()); + + if ( !checkedAdjacentElems[ e->GetType() ].insert( e ).second ) + continue; // e is already checked + + // check nodes + bool allIn = true; + SMDS_ElemIteratorPtr nodeIt = e->nodesIterator(); + while ( nodeIt->more() && allIn ) + allIn = allNodes.count( cast2Node( nodeIt->next() )); + if ( allIn ) + theElements.insert(e ); } - SMDS_VolumeIteratorPtr aVolumeItr = meshDS->volumesIterator(); - while(aVolumeItr->more()) + } + + SMESH_MesherHelper helper(*myMesh); + helper.SetIsQuadratic( true ); + + // add links of quadratic adjacent elements to the helper + + if ( !quadAdjacentElems[SMDSAbs_Edge].empty() ) + for ( eIt = quadAdjacentElems[SMDSAbs_Edge].begin(); + eIt != quadAdjacentElems[SMDSAbs_Edge].end(); ++eIt ) { - const SMDS_MeshVolume* volume = aVolumeItr->next(); - if(!volume || volume->IsQuadratic() ) continue; + helper.AddTLinks( static_cast< const SMDS_MeshEdge*> (*eIt) ); + } + if ( !quadAdjacentElems[SMDSAbs_Face].empty() ) + for ( eIt = quadAdjacentElems[SMDSAbs_Face].begin(); + eIt != quadAdjacentElems[SMDSAbs_Face].end(); ++eIt ) + { + helper.AddTLinks( static_cast< const SMDS_MeshFace*> (*eIt) ); + } + if ( !quadAdjacentElems[SMDSAbs_Volume].empty() ) + for ( eIt = quadAdjacentElems[SMDSAbs_Volume].begin(); + eIt != quadAdjacentElems[SMDSAbs_Volume].end(); ++eIt ) + { + helper.AddTLinks( static_cast< const SMDS_MeshVolume*> (*eIt) ); + } - int id = volume->GetID(); - int nbNodes = volume->NbNodes(); - vector aNds (nbNodes); + // make quadratic elements instead of linear ones - for(int i = 0; i < nbNodes; i++) - { - aNds[i] = volume->GetNode(i); - } + SMESHDS_Mesh* meshDS = GetMeshDS(); + SMESHDS_SubMesh* smDS = 0; + for ( eIt = theElements.begin(); eIt != theElements.end(); ++eIt ) + { + const SMDS_MeshElement* elem = *eIt; + if( elem->IsQuadratic() || elem->NbNodes() < 2 || elem->IsPoly() ) + continue; - meshDS->RemoveFreeElement(volume, smDS, notFromGroups); + const int id = elem->GetID(); + const SMDSAbs_ElementType type = elem->GetType(); + vector nodes ( elem->begin_nodes(), elem->end_nodes()); - SMDS_MeshVolume * NewVolume = 0; - switch(nbNodes) - { - case 4: - NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2], - aNds[3], id, theForce3d ); - break; - case 6: - NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2], - aNds[3], aNds[4], aNds[5], id, theForce3d); - break; - case 8: - NewVolume = aHelper.AddVolume(aNds[0], aNds[1], aNds[2], aNds[3], - aNds[4], aNds[5], aNds[6], aNds[7], id, theForce3d); - break; - default: - continue; - } - ReplaceElemInGroups(volume, NewVolume, meshDS); + if ( !smDS || !smDS->Contains( elem )) + smDS = meshDS->MeshElements( elem->getshapeId() ); + meshDS->RemoveFreeElement(elem, smDS, /*fromGroups=*/false); + + SMDS_MeshElement * newElem = 0; + switch( nodes.size() ) + { + case 4: // cases for most frequently used element types go first (for optimization) + if ( type == SMDSAbs_Volume ) + newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + else + newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], nodes[3], id, theForce3d); + break; + case 8: + newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], id, theForce3d); + break; + case 3: + newElem = helper.AddFace (nodes[0], nodes[1], nodes[2], id, theForce3d); + break; + case 2: + newElem = helper.AddEdge(nodes[0], nodes[1], id, theForce3d); + break; + case 5: + newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], id, theForce3d); + break; + case 6: + newElem = helper.AddVolume(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], id, theForce3d); + break; + default:; } + ReplaceElemInGroups( elem, newElem, meshDS); + if( newElem && smDS ) + smDS->AddElement( newElem ); + } + + if ( !theForce3d && !getenv("NO_FixQuadraticElements")) + { // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion + helper.SetSubShape(0); // apply FixQuadraticElements() to the whole mesh + helper.FixQuadraticElements(); } } //======================================================================= /*! * \brief Convert quadratic elements to linear ones and remove quadratic nodes - * \retval int - nb of checked elements + * \return int - nb of checked elements */ //======================================================================= @@ -6822,7 +9786,6 @@ int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm, { int nbElem = 0; SMESHDS_Mesh* meshDS = GetMeshDS(); - const bool notFromGroups = false; while( theItr->more() ) { @@ -6830,44 +9793,28 @@ int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm, nbElem++; if( elem && elem->IsQuadratic()) { - int id = elem->GetID(); - int nbNodes = elem->NbNodes(); - vector aNds, mediumNodes; - aNds.reserve( nbNodes ); - mediumNodes.reserve( nbNodes ); - - for(int i = 0; i < nbNodes; i++) - { - const SMDS_MeshNode* n = elem->GetNode(i); - - if( elem->IsMediumNode( n ) ) - mediumNodes.push_back( n ); - else - aNds.push_back( n ); - } - if( aNds.empty() ) continue; + int id = elem->GetID(); + int nbCornerNodes = elem->NbCornerNodes(); SMDSAbs_ElementType aType = elem->GetType(); - //remove old quadratic element - meshDS->RemoveFreeElement( elem, theSm, notFromGroups ); + vector nodes( elem->begin_nodes(), elem->end_nodes() ); - SMDS_MeshElement * NewElem = AddElement( aNds, aType, false, id ); - ReplaceElemInGroups(elem, NewElem, meshDS); - if( theSm && NewElem ) - theSm->AddElement( NewElem ); + //remove a quadratic element + if ( !theSm || !theSm->Contains( elem )) + theSm = meshDS->MeshElements( elem->getshapeId() ); + meshDS->RemoveFreeElement( elem, theSm, /*fromGroups=*/false ); // remove medium nodes - vector::iterator nIt = mediumNodes.begin(); - for ( ; nIt != mediumNodes.end(); ++nIt ) { - const SMDS_MeshNode* n = *nIt; - if ( n->NbInverseElements() == 0 ) { - if ( n->GetPosition()->GetShapeId() != theShapeID ) - meshDS->RemoveFreeNode( n, meshDS->MeshElements - ( n->GetPosition()->GetShapeId() )); - else - meshDS->RemoveFreeNode( n, theSm ); - } - } + for ( unsigned i = nbCornerNodes; i < nodes.size(); ++i ) + if ( nodes[i]->NbInverseElements() == 0 ) + meshDS->RemoveFreeNode( nodes[i], theSm ); + + // add a linear element + nodes.resize( nbCornerNodes ); + SMDS_MeshElement * newElem = AddElement( nodes, aType, false, id ); + ReplaceElemInGroups(elem, newElem, meshDS); + if( theSm && newElem ) + theSm->AddElement( newElem ); } } return nbElem; @@ -6877,7 +9824,8 @@ int SMESH_MeshEditor::removeQuadElem(SMESHDS_SubMesh * theSm, //function : ConvertFromQuadratic //purpose : //======================================================================= -bool SMESH_MeshEditor::ConvertFromQuadratic() + +bool SMESH_MeshEditor::ConvertFromQuadratic() { int nbCheckedElems = 0; if ( myMesh->HasShapeToMesh() ) @@ -6892,7 +9840,7 @@ bool SMESH_MeshEditor::ConvertFromQuadratic() } } } - + int totalNbElems = GetMeshDS()->NbEdges() + GetMeshDS()->NbFaces() + GetMeshDS()->NbVolumes(); if ( nbCheckedElems < totalNbElems ) // not all elements are in submeshes @@ -6904,18 +9852,114 @@ bool SMESH_MeshEditor::ConvertFromQuadratic() return true; } +namespace +{ + //================================================================================ + /*! + * \brief Return true if all medium nodes of the element are in the node set + */ + //================================================================================ + + bool allMediumNodesIn(const SMDS_MeshElement* elem, TIDSortedNodeSet& nodeSet ) + { + for ( int i = elem->NbCornerNodes(); i < elem->NbNodes(); ++i ) + if ( !nodeSet.count( elem->GetNode(i) )) + return false; + return true; + } +} + +//================================================================================ +/*! + * \brief Makes given elements linear + */ +//================================================================================ + +void SMESH_MeshEditor::ConvertFromQuadratic(TIDSortedElemSet& theElements) +{ + if ( theElements.empty() ) return; + + // collect IDs of medium nodes of theElements; some of these nodes will be removed + set mediumNodeIDs; + TIDSortedElemSet::iterator eIt = theElements.begin(); + for ( ; eIt != theElements.end(); ++eIt ) + { + const SMDS_MeshElement* e = *eIt; + for ( int i = e->NbCornerNodes(); i < e->NbNodes(); ++i ) + mediumNodeIDs.insert( e->GetNode(i)->GetID() ); + } + + // replace given elements by linear ones + typedef SMDS_SetIterator TSetIterator; + SMDS_ElemIteratorPtr elemIt( new TSetIterator( theElements.begin(), theElements.end() )); + removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 ); + + // we need to convert remaining elements whose all medium nodes are in mediumNodeIDs + // except those elements sharing medium nodes of quadratic element whose medium nodes + // are not all in mediumNodeIDs + + // get remaining medium nodes + TIDSortedNodeSet mediumNodes; + set::iterator nIdsIt = mediumNodeIDs.begin(); + for ( ; nIdsIt != mediumNodeIDs.end(); ++nIdsIt ) + if ( const SMDS_MeshNode* n = GetMeshDS()->FindNode( *nIdsIt )) + mediumNodes.insert( mediumNodes.end(), n ); + + // find more quadratic elements to convert + TIDSortedElemSet moreElemsToConvert; + TIDSortedNodeSet::iterator nIt = mediumNodes.begin(); + for ( ; nIt != mediumNodes.end(); ++nIt ) + { + SMDS_ElemIteratorPtr invIt = (*nIt)->GetInverseElementIterator(); + while ( invIt->more() ) + { + const SMDS_MeshElement* e = invIt->next(); + if ( e->IsQuadratic() && allMediumNodesIn( e, mediumNodes )) + { + // find a more complex element including e and + // whose medium nodes are not in mediumNodes + bool complexFound = false; + for ( int type = e->GetType() + 1; type < SMDSAbs_0DElement; ++type ) + { + SMDS_ElemIteratorPtr invIt2 = + (*nIt)->GetInverseElementIterator( SMDSAbs_ElementType( type )); + while ( invIt2->more() ) + { + const SMDS_MeshElement* eComplex = invIt2->next(); + if ( eComplex->IsQuadratic() && !allMediumNodesIn( eComplex, mediumNodes)) + { + int nbCommonNodes = SMESH_Algo::GetCommonNodes( e, eComplex ).size(); + if ( nbCommonNodes == e->NbNodes()) + { + complexFound = true; + type = SMDSAbs_NbElementTypes; // to quit from the outer loop + break; + } + } + } + } + if ( !complexFound ) + moreElemsToConvert.insert( e ); + } + } + } + elemIt = SMDS_ElemIteratorPtr + (new TSetIterator( moreElemsToConvert.begin(), moreElemsToConvert.end() )); + removeQuadElem( /*theSm=*/0, elemIt, /*theShapeID=*/0 ); +} + //======================================================================= //function : SewSideElements //purpose : //======================================================================= SMESH_MeshEditor::Sew_Error - SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1, - TIDSortedElemSet& theSide2, - const SMDS_MeshNode* theFirstNode1, - const SMDS_MeshNode* theFirstNode2, - const SMDS_MeshNode* theSecondNode1, - const SMDS_MeshNode* theSecondNode2) +SMESH_MeshEditor::SewSideElements (TIDSortedElemSet& theSide1, + TIDSortedElemSet& theSide2, + const SMDS_MeshNode* theFirstNode1, + const SMDS_MeshNode* theFirstNode2, + const SMDS_MeshNode* theSecondNode1, + const SMDS_MeshNode* theSecondNode2) { myLastCreatedElems.Clear(); myLastCreatedNodes.Clear(); @@ -6934,25 +9978,27 @@ SMESH_MeshEditor::Sew_Error // 1. Build set of faces representing each side: // ======================================================================= // a. build set of nodes belonging to faces - // b. complete set of faces: find missing fices whose nodes are in set of nodes + // b. complete set of faces: find missing faces whose nodes are in set of nodes // c. create temporary faces representing side of volumes if correspondent // face does not exist SMESHDS_Mesh* aMesh = GetMeshDS(); - SMDS_Mesh aTmpFacesMesh; - set faceSet1, faceSet2; + // TODO algoritm not OK with vtkUnstructuredGrid: 2 meshes can't share nodes + //SMDS_Mesh aTmpFacesMesh; // try to use the same mesh + TIDSortedElemSet faceSet1, faceSet2; set volSet1, volSet2; set nodeSet1, nodeSet2; - set * faceSetPtr[] = { &faceSet1, &faceSet2 }; - set * volSetPtr[] = { &volSet1, &volSet2 }; + TIDSortedElemSet * faceSetPtr[] = { &faceSet1, &faceSet2 }; + set * volSetPtr[] = { &volSet1, &volSet2 }; set * nodeSetPtr[] = { &nodeSet1, &nodeSet2 }; - TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 }; + TIDSortedElemSet * elemSetPtr[] = { &theSide1, &theSide2 }; int iSide, iFace, iNode; + list tempFaceList; for ( iSide = 0; iSide < 2; iSide++ ) { set * nodeSet = nodeSetPtr[ iSide ]; - TIDSortedElemSet * elemSet = elemSetPtr[ iSide ]; - set * faceSet = faceSetPtr[ iSide ]; + TIDSortedElemSet * elemSet = elemSetPtr[ iSide ]; + TIDSortedElemSet * faceSet = faceSetPtr[ iSide ]; set * volSet = volSetPtr [ iSide ]; set::iterator vIt; TIDSortedElemSet::iterator eIt; @@ -6976,7 +10022,7 @@ SMESH_MeshEditor::Sew_Error // ----------------------------------------------------------- // 1a. Collect nodes of existing faces // and build set of face nodes in order to detect missing - // faces corresponing to sides of volumes + // faces corresponding to sides of volumes // ----------------------------------------------------------- set< set > setOfFaceNodeSet; @@ -7000,7 +10046,7 @@ SMESH_MeshEditor::Sew_Error volSet->insert( elem ); } // ------------------------------------------------------------------------------ - // 1b. Complete set of faces: find missing fices whose nodes are in set of nodes + // 1b. Complete set of faces: find missing faces whose nodes are in set of nodes // ------------------------------------------------------------------------------ for ( nIt = nodeSet->begin(); nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide @@ -7054,40 +10100,38 @@ SMESH_MeshEditor::Sew_Error bool isNewFace = setOfFaceNodeSet.insert( faceNodeSet ).second; if ( isNewFace ) { // no such a face is given but it still can exist, check it - if ( nbNodes == 3 ) { - aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2] ); - } - else if ( nbNodes == 4 ) { - aFreeFace = aMesh->FindFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); - } - else { - vector poly_nodes ( fNodes, & fNodes[nbNodes]); - aFreeFace = aMesh->FindFace(poly_nodes); - } + vector nodes ( fNodes, fNodes + nbNodes); + aFreeFace = aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false ); } if ( !aFreeFace ) { // create a temporary face if ( nbNodes == 3 ) { - aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] ); + //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2] ); + aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2] ); } else if ( nbNodes == 4 ) { - aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); + //aFreeFace = aTmpFacesMesh.AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); + aFreeFace = aMesh->AddFace( fNodes[0],fNodes[1],fNodes[2],fNodes[3] ); } else { vector poly_nodes ( fNodes, & fNodes[nbNodes]); - aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes); + //aFreeFace = aTmpFacesMesh.AddPolygonalFace(poly_nodes); + aFreeFace = aMesh->AddPolygonalFace(poly_nodes); } + if ( aFreeFace ) + tempFaceList.push_back( aFreeFace ); } + if ( aFreeFace ) freeFaceList.push_back( aFreeFace ); } // loop on faces of a volume - // choose one of several free faces - // -------------------------------------- + // choose one of several free faces of a volume + // -------------------------------------------- if ( freeFaceList.size() > 1 ) { // choose a face having max nb of nodes shared by other elems of a side - int maxNbNodes = -1/*, nbExcludedFaces = 0*/; + int maxNbNodes = -1; list::iterator fIt = freeFaceList.begin(); while ( fIt != freeFaceList.end() ) { // loop on free faces int nbSharedNodes = 0; @@ -7098,18 +10142,20 @@ SMESH_MeshEditor::Sew_Error SMDS_ElemIteratorPtr invElemIt = n->GetInverseElementIterator(); while ( invElemIt->more() ) { const SMDS_MeshElement* e = invElemIt->next(); - if ( faceSet->find( e ) != faceSet->end() ) - nbSharedNodes++; - if ( elemSet->find( e ) != elemSet->end() ) - nbSharedNodes++; + nbSharedNodes += faceSet->count( e ); + nbSharedNodes += elemSet->count( e ); } } - if ( nbSharedNodes >= maxNbNodes ) { + if ( nbSharedNodes > maxNbNodes ) { maxNbNodes = nbSharedNodes; + freeFaceList.erase( freeFaceList.begin(), fIt++ ); + } + else if ( nbSharedNodes == maxNbNodes ) { fIt++; } - else - freeFaceList.erase( fIt++ ); // here fIt++ occures before erase + else { + freeFaceList.erase( fIt++ ); // here fIt++ occurs before erase + } } if ( freeFaceList.size() > 1 ) { @@ -7154,48 +10200,51 @@ SMESH_MeshEditor::Sew_Error const SMDS_MeshElement* aFreeFace = freeFaceList.front(); faceSet->insert( aFreeFace ); // complete a node set with nodes of a found free face -// for ( iNode = 0; iNode < ; iNode++ ) -// nodeSet->insert( fNodes[ iNode ] ); + // for ( iNode = 0; iNode < ; iNode++ ) + // nodeSet->insert( fNodes[ iNode ] ); } } // loop on volumes of a side -// // complete a set of faces if new nodes in a nodeSet appeared -// // ---------------------------------------------------------- -// if ( nodeSetSize != nodeSet->size() ) { -// for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide -// SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face); -// while ( fIt->more() ) { // loop on faces sharing a node -// const SMDS_MeshElement* f = fIt->next(); -// if ( faceSet->find( f ) == faceSet->end() ) { -// // check if all nodes are in nodeSet and -// // complete setOfFaceNodeSet if they are -// set faceNodeSet; -// SMDS_ElemIteratorPtr nodeIt = f->nodesIterator(); -// bool allInSet = true; -// while ( nodeIt->more() && allInSet ) { // loop on nodes of a face -// const SMDS_MeshNode* n = static_cast( nodeIt->next() ); -// if ( nodeSet->find( n ) == nodeSet->end() ) -// allInSet = false; -// else -// faceNodeSet.insert( n ); -// } -// if ( allInSet ) { -// faceSet->insert( f ); -// setOfFaceNodeSet.insert( faceNodeSet ); -// } -// } -// } -// } -// } + // // complete a set of faces if new nodes in a nodeSet appeared + // // ---------------------------------------------------------- + // if ( nodeSetSize != nodeSet->size() ) { + // for ( ; nIt != nodeSet->end(); nIt++ ) { // loop on nodes of iSide + // SMDS_ElemIteratorPtr fIt = (*nIt)->GetInverseElementIterator(SMDSAbs_Face); + // while ( fIt->more() ) { // loop on faces sharing a node + // const SMDS_MeshElement* f = fIt->next(); + // if ( faceSet->find( f ) == faceSet->end() ) { + // // check if all nodes are in nodeSet and + // // complete setOfFaceNodeSet if they are + // set faceNodeSet; + // SMDS_ElemIteratorPtr nodeIt = f->nodesIterator(); + // bool allInSet = true; + // while ( nodeIt->more() && allInSet ) { // loop on nodes of a face + // const SMDS_MeshNode* n = static_cast( nodeIt->next() ); + // if ( nodeSet->find( n ) == nodeSet->end() ) + // allInSet = false; + // else + // faceNodeSet.insert( n ); + // } + // if ( allInSet ) { + // faceSet->insert( f ); + // setOfFaceNodeSet.insert( faceNodeSet ); + // } + // } + // } + // } + // } } // Create temporary faces, if there are volumes given } // loop on sides if ( faceSet1.size() != faceSet2.size() ) { // delete temporary faces: they are in reverseElements of actual nodes - SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator(); - while ( tmpFaceIt->more() ) - aTmpFacesMesh.RemoveElement( tmpFaceIt->next() ); +// SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator(); +// while ( tmpFaceIt->more() ) +// aTmpFacesMesh.RemoveElement( tmpFaceIt->next() ); +// list::iterator tmpFaceIt = tempFaceList.begin(); +// for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt) +// aMesh->RemoveElement(*tmpFaceIt); MESSAGE("Diff nb of faces"); return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; } @@ -7205,27 +10254,254 @@ SMESH_MeshEditor::Sew_Error // bind a node to remove to a node to put instead // ============================================================ - TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead + TNodeNodeMap nReplaceMap; // bind a node to remove to a node to put instead + if ( theFirstNode1 != theFirstNode2 ) + nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 )); + if ( theSecondNode1 != theSecondNode2 ) + nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 )); + + LinkID_Gen aLinkID_Gen( GetMeshDS() ); + set< long > linkIdSet; // links to process + linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 )); + + typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink; + list< NLink > linkList[2]; + linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 )); + linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 )); + // loop on links in linkList; find faces by links and append links + // of the found faces to linkList + list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; + for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) + { + NLink link[] = { *linkIt[0], *linkIt[1] }; + long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second ); + if ( !linkIdSet.count( linkID ) ) + continue; + + // by links, find faces in the face sets, + // and find indices of link nodes in the found faces; + // in a face set, there is only one or no face sharing a link + // --------------------------------------------------------------- + + const SMDS_MeshElement* face[] = { 0, 0 }; + vector fnodes[2]; + int iLinkNode[2][2]; + TIDSortedElemSet avoidSet; + for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides + const SMDS_MeshNode* n1 = link[iSide].first; + const SMDS_MeshNode* n2 = link[iSide].second; + //cout << "Side " << iSide << " "; + //cout << "L( " << n1->GetID() << ", " << n2->GetID() << " ) " << endl; + // find a face by two link nodes + face[ iSide ] = FindFaceInSet( n1, n2, *faceSetPtr[ iSide ], avoidSet, + &iLinkNode[iSide][0], &iLinkNode[iSide][1] ); + if ( face[ iSide ]) + { + //cout << " F " << face[ iSide]->GetID() <erase( face[ iSide ]); + // put face nodes to fnodes + if ( face[ iSide ]->IsQuadratic() ) + { + // use interlaced nodes iterator + const SMDS_VtkFace* F = dynamic_cast( face[ iSide ]); + if (!F) throw SALOME_Exception(LOCALIZED("not an SMDS_VtkFace")); + SMDS_ElemIteratorPtr nIter = F->interlacedNodesElemIterator(); + while ( nIter->more() ) + fnodes[ iSide ].push_back( cast2Node( nIter->next() )); + } + else + { + fnodes[ iSide ].assign( face[ iSide ]->begin_nodes(), + face[ iSide ]->end_nodes() ); + } + fnodes[ iSide ].push_back( fnodes[ iSide ].front()); + } + } + + // check similarity of elements of the sides + if (aResult == SEW_OK && (( face[0] && !face[1] ) || ( !face[0] && face[1] ))) { + MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); + if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found + aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); + } + else { + aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } + break; // do not return because it's necessary to remove tmp faces + } + + // set nodes to merge + // ------------------- + + if ( face[0] && face[1] ) { + const int nbNodes = face[0]->NbNodes(); + if ( nbNodes != face[1]->NbNodes() ) { + MESSAGE("Diff nb of face nodes"); + aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + break; // do not return because it s necessary to remove tmp faces + } + bool reverse[] = { false, false }; // order of nodes in the link + for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides + // analyse link orientation in faces + int i1 = iLinkNode[ iSide ][ 0 ]; + int i2 = iLinkNode[ iSide ][ 1 ]; + reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1; + } + int di1 = reverse[0] ? -1 : +1, i1 = iLinkNode[0][1] + di1; + int di2 = reverse[1] ? -1 : +1, i2 = iLinkNode[1][1] + di2; + for ( int i = nbNodes - 2; i > 0; --i, i1 += di1, i2 += di2 ) + { + nReplaceMap.insert ( make_pair ( fnodes[0][ ( i1 + nbNodes ) % nbNodes ], + fnodes[1][ ( i2 + nbNodes ) % nbNodes ])); + } + + // add other links of the faces to linkList + // ----------------------------------------- + + for ( iNode = 0; iNode < nbNodes; iNode++ ) { + linkID = aLinkID_Gen.GetLinkID( fnodes[0][iNode], fnodes[0][iNode+1] ); + pair< set::iterator, bool > iter_isnew = linkIdSet.insert( linkID ); + if ( !iter_isnew.second ) { // already in a set: no need to process + linkIdSet.erase( iter_isnew.first ); + } + else // new in set == encountered for the first time: add + { + const SMDS_MeshNode* n1 = fnodes[0][ iNode ]; + const SMDS_MeshNode* n2 = fnodes[0][ iNode + 1]; + linkList[0].push_back ( NLink( n1, n2 )); + linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); + } + } + } // 2 faces found + + if ( faceSetPtr[0]->empty() || faceSetPtr[1]->empty() ) + break; + + } // loop on link lists + + if ( aResult == SEW_OK && + ( //linkIt[0] != linkList[0].end() || + !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) { + MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) << + " " << (faceSetPtr[1]->empty())); + aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } + + // ==================================================================== + // 3. Replace nodes in elements of the side 1 and remove replaced nodes + // ==================================================================== + + // delete temporary faces +// SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator(); +// while ( tmpFaceIt->more() ) +// aTmpFacesMesh.RemoveElement( tmpFaceIt->next() ); + list::iterator tmpFaceIt = tempFaceList.begin(); + for (; tmpFaceIt !=tempFaceList.end(); ++tmpFaceIt) + aMesh->RemoveElement(*tmpFaceIt); + + if ( aResult != SEW_OK) + return aResult; + + list< int > nodeIDsToRemove/*, elemIDsToRemove*/; + // loop on nodes replacement map + TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt; + for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ ) + if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) { + const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first; + nodeIDsToRemove.push_back( nToRemove->GetID() ); + // loop on elements sharing nToRemove + SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator(); + while ( invElemIt->more() ) { + const SMDS_MeshElement* e = invElemIt->next(); + // get a new suite of nodes: make replacement + int nbReplaced = 0, i = 0, nbNodes = e->NbNodes(); + vector< const SMDS_MeshNode*> nodes( nbNodes ); + SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + while ( nIt->more() ) { + const SMDS_MeshNode* n = + static_cast( nIt->next() ); + nnIt = nReplaceMap.find( n ); + if ( nnIt != nReplaceMap.end() ) { + nbReplaced++; + n = (*nnIt).second; + } + nodes[ i++ ] = n; + } + // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face ) + // elemIDsToRemove.push_back( e->GetID() ); + // else + if ( nbReplaced ) + { + SMDSAbs_ElementType etyp = e->GetType(); + SMDS_MeshElement* newElem = this->AddElement(nodes, etyp, false); + if (newElem) + { + myLastCreatedElems.Append(newElem); + AddToSameGroups(newElem, e, aMesh); + int aShapeId = e->getshapeId(); + if ( aShapeId ) + { + aMesh->SetMeshElementOnShape( newElem, aShapeId ); + } + } + aMesh->RemoveElement(e); + } + } + } + + Remove( nodeIDsToRemove, true ); + + return aResult; +} + +//================================================================================ +/*! + * \brief Find corresponding nodes in two sets of faces + * \param theSide1 - first face set + * \param theSide2 - second first face + * \param theFirstNode1 - a boundary node of set 1 + * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1 + * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1 + * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1 + * \param nReplaceMap - output map of corresponding nodes + * \return bool - is a success or not + */ +//================================================================================ + +#ifdef _DEBUG_ +//#define DEBUG_MATCHING_NODES +#endif + +SMESH_MeshEditor::Sew_Error +SMESH_MeshEditor::FindMatchingNodes(set& theSide1, + set& theSide2, + const SMDS_MeshNode* theFirstNode1, + const SMDS_MeshNode* theFirstNode2, + const SMDS_MeshNode* theSecondNode1, + const SMDS_MeshNode* theSecondNode2, + TNodeNodeMap & nReplaceMap) +{ + set * faceSetPtr[] = { &theSide1, &theSide2 }; + + nReplaceMap.clear(); if ( theFirstNode1 != theFirstNode2 ) - nReplaceMap.insert( TNodeNodeMap::value_type( theFirstNode1, theFirstNode2 )); + nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 )); if ( theSecondNode1 != theSecondNode2 ) - nReplaceMap.insert( TNodeNodeMap::value_type( theSecondNode1, theSecondNode2 )); + nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 )); - LinkID_Gen aLinkID_Gen( GetMeshDS() ); - set< long > linkIdSet; // links to process - linkIdSet.insert( aLinkID_Gen.GetLinkID( theFirstNode1, theSecondNode1 )); + set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored + linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 )); - typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink; list< NLink > linkList[2]; linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 )); linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 )); + // loop on links in linkList; find faces by links and append links // of the found faces to linkList list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) { NLink link[] = { *linkIt[0], *linkIt[1] }; - long linkID = aLinkID_Gen.GetLinkID( link[0].first, link[0].second ); - if ( linkIdSet.find( linkID ) == linkIdSet.end() ) + if ( linkSet.find( link[0] ) == linkSet.end() ) continue; // by links, find faces in the face sets, @@ -7234,525 +10510,1467 @@ SMESH_MeshEditor::Sew_Error // --------------------------------------------------------------- const SMDS_MeshElement* face[] = { 0, 0 }; - //const SMDS_MeshNode* faceNodes[ 2 ][ 5 ]; - vector fnodes1(9); - vector fnodes2(9); - //const SMDS_MeshNode* notLinkNodes[ 2 ][ 2 ] = {{ 0, 0 },{ 0, 0 }} ; - vector notLinkNodes1(6); - vector notLinkNodes2(6); - int iLinkNode[2][2]; - for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides + list notLinkNodes[2]; + //bool reverse[] = { false, false }; // order of notLinkNodes + int nbNodes[2]; + for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides + { const SMDS_MeshNode* n1 = link[iSide].first; const SMDS_MeshNode* n2 = link[iSide].second; set * faceSet = faceSetPtr[ iSide ]; - set< const SMDS_MeshElement* > fMap; - for ( int i = 0; i < 2; i++ ) { // loop on 2 nodes of a link - const SMDS_MeshNode* n = i ? n1 : n2; // a node of a link + set< const SMDS_MeshElement* > facesOfNode1; + for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link + { + // during a loop of the first node, we find all faces around n1, + // during a loop of the second node, we find one face sharing both n1 and n2 + const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face); while ( fIt->more() ) { // loop on faces sharing a node const SMDS_MeshElement* f = fIt->next(); if (faceSet->find( f ) != faceSet->end() && // f is in face set - ! fMap.insert( f ).second ) // f encounters twice + ! facesOfNode1.insert( f ).second ) // f encounters twice { if ( face[ iSide ] ) { MESSAGE( "2 faces per link " ); - aResult = iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES; - break; + return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); } face[ iSide ] = f; faceSet->erase( f ); - // get face nodes and find ones of a link - iNode = 0; - int nbl = -1; - if(f->IsPoly()) { - if(iSide==0) { - fnodes1.resize(f->NbNodes()+1); - notLinkNodes1.resize(f->NbNodes()-2); - } - else { - fnodes2.resize(f->NbNodes()+1); - notLinkNodes2.resize(f->NbNodes()-2); - } - } - if(!f->IsQuadratic()) { - SMDS_ElemIteratorPtr nIt = f->nodesIterator(); - while ( nIt->more() ) { - const SMDS_MeshNode* n = - static_cast( nIt->next() ); - if ( n == n1 ) { - iLinkNode[ iSide ][ 0 ] = iNode; - } - else if ( n == n2 ) { - iLinkNode[ iSide ][ 1 ] = iNode; - } - //else if ( notLinkNodes[ iSide ][ 0 ] ) - // notLinkNodes[ iSide ][ 1 ] = n; - //else - // notLinkNodes[ iSide ][ 0 ] = n; - else { - nbl++; - if(iSide==0) - notLinkNodes1[nbl] = n; - //notLinkNodes1.push_back(n); - else - notLinkNodes2[nbl] = n; - //notLinkNodes2.push_back(n); - } - //faceNodes[ iSide ][ iNode++ ] = n; - if(iSide==0) { - fnodes1[iNode++] = n; - } - else { - fnodes2[iNode++] = n; - } - } - } - else { // f->IsQuadratic() - const SMDS_QuadraticFaceOfNodes* F = - static_cast(f); - // use special nodes iterator - SMDS_NodeIteratorPtr anIter = F->interlacedNodesIterator(); - while ( anIter->more() ) { - const SMDS_MeshNode* n = - static_cast( anIter->next() ); - if ( n == n1 ) { - iLinkNode[ iSide ][ 0 ] = iNode; - } - else if ( n == n2 ) { - iLinkNode[ iSide ][ 1 ] = iNode; - } - else { - nbl++; - if(iSide==0) { - notLinkNodes1[nbl] = n; - } - else { - notLinkNodes2[nbl] = n; - } - } - if(iSide==0) { - fnodes1[iNode++] = n; - } - else { - fnodes2[iNode++] = n; - } - } - } - //faceNodes[ iSide ][ iNode ] = faceNodes[ iSide ][ 0 ]; - if(iSide==0) { - fnodes1[iNode] = fnodes1[0]; + + // get not link nodes + int nbN = f->NbNodes(); + if ( f->IsQuadratic() ) + nbN /= 2; + nbNodes[ iSide ] = nbN; + list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ]; + int i1 = f->GetNodeIndex( n1 ); + int i2 = f->GetNodeIndex( n2 ); + int iEnd = nbN, iBeg = -1, iDelta = 1; + bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 ); + if ( reverse ) { + std::swap( iEnd, iBeg ); iDelta = -1; } - else { - fnodes2[iNode] = fnodes1[0]; + int i = i2; + while ( true ) { + i += iDelta; + if ( i == iEnd ) i = iBeg + iDelta; + if ( i == i1 ) break; + nodes.push_back ( f->GetNode( i ) ); } } } } } + // check similarity of elements of the sides + if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) { + MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); + if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found + return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); + } + else { + return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } + } + + // set nodes to merge + // ------------------- + + if ( face[0] && face[1] ) { + if ( nbNodes[0] != nbNodes[1] ) { + MESSAGE("Diff nb of face nodes"); + return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + } +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID() + << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" " + << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ; +#endif + int nbN = nbNodes[0]; + { + list::iterator n1 = notLinkNodes[0].begin(); + list::iterator n2 = notLinkNodes[1].begin(); + for ( int i = 0 ; i < nbN - 2; ++i ) { +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() ); +#endif + nReplaceMap.insert( make_pair( *(n1++), *(n2++) )); + } + } + + // add other links of the face 1 to linkList + // ----------------------------------------- + + const SMDS_MeshElement* f0 = face[0]; + const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 ); + for ( int i = 0; i < nbN; i++ ) + { + const SMDS_MeshNode* n2 = f0->GetNode( i ); + pair< set< SMESH_TLink >::iterator, bool > iter_isnew = + linkSet.insert( SMESH_TLink( n1, n2 )); + if ( !iter_isnew.second ) { // already in a set: no need to process + linkSet.erase( iter_isnew.first ); + } + else // new in set == encountered for the first time: add + { +#ifdef DEBUG_MATCHING_NODES + MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " " + << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " ); +#endif + linkList[0].push_back ( NLink( n1, n2 )); + linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); + } + n1 = n2; + } + } // 2 faces found + } // loop on link lists + + return SEW_OK; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - the list of elements (edges or faces) to be replicated + The nodes for duplication could be found from these elements + \param theNodesNot - list of nodes to NOT replicate + \param theAffectedElems - the list of elements (cells and edges) to which the + replicated nodes should be associated to. + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::DoubleNodes( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TIDSortedElemSet& theAffectedElems ) +{ + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + if ( theElems.size() == 0 ) + return false; + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + if ( !aMeshDS ) + return false; + + bool res = false; + std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode; + // duplicate elements and nodes + res = doubleNodes( aMeshDS, theElems, theNodesNot, anOldNodeToNewNode, true ); + // replce nodes by duplications + res = doubleNodes( aMeshDS, theAffectedElems, theNodesNot, anOldNodeToNewNode, false ); + return res; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theMeshDS - mesh instance + \param theElems - the elements replicated or modified (nodes should be changed) + \param theNodesNot - nodes to NOT replicate + \param theNodeNodeMap - relation of old node to new created node + \param theIsDoubleElem - flag os to replicate element or modify + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::doubleNodes( SMESHDS_Mesh* theMeshDS, + const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + std::map< const SMDS_MeshNode*, + const SMDS_MeshNode* >& theNodeNodeMap, + const bool theIsDoubleElem ) +{ + MESSAGE("doubleNodes"); + // iterate on through element and duplicate them (by nodes duplication) + bool res = false; + TIDSortedElemSet::const_iterator elemItr = theElems.begin(); + for ( ; elemItr != theElems.end(); ++elemItr ) + { + const SMDS_MeshElement* anElem = *elemItr; + if (!anElem) + continue; + + bool isDuplicate = false; + // duplicate nodes to duplicate element + std::vector newNodes( anElem->NbNodes() ); + SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); + int ind = 0; + while ( anIter->more() ) + { + + SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next(); + SMDS_MeshNode* aNewNode = aCurrNode; + if ( theNodeNodeMap.find( aCurrNode ) != theNodeNodeMap.end() ) + aNewNode = (SMDS_MeshNode*)theNodeNodeMap[ aCurrNode ]; + else if ( theIsDoubleElem && theNodesNot.find( aCurrNode ) == theNodesNot.end() ) + { + // duplicate node + aNewNode = theMeshDS->AddNode( aCurrNode->X(), aCurrNode->Y(), aCurrNode->Z() ); + theNodeNodeMap[ aCurrNode ] = aNewNode; + myLastCreatedNodes.Append( aNewNode ); + } + isDuplicate |= (aCurrNode != aNewNode); + newNodes[ ind++ ] = aNewNode; + } + if ( !isDuplicate ) + continue; + + if ( theIsDoubleElem ) + AddElement(newNodes, anElem->GetType(), anElem->IsPoly()); + else + { + MESSAGE("ChangeElementNodes"); + theMeshDS->ChangeElementNodes( anElem, &newNodes[ 0 ], anElem->NbNodes() ); + } + res = true; + } + return res; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theNodes - identifiers of nodes to be doubled + \param theModifiedElems - identifiers of elements to be updated by the new (doubled) + nodes. If list of element identifiers is empty then nodes are doubled but + they not assigned to elements + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, + const std::list< int >& theListOfModifiedElems ) +{ + MESSAGE("DoubleNodes"); + myLastCreatedElems.Clear(); + myLastCreatedNodes.Clear(); + + if ( theListOfNodes.size() == 0 ) + return false; + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + if ( !aMeshDS ) + return false; + + // iterate through nodes and duplicate them + + std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode; + + std::list< int >::const_iterator aNodeIter; + for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter ) + { + int aCurr = *aNodeIter; + SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr ); + if ( !aNode ) + continue; + + // duplicate node + + const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() ); + if ( aNewNode ) + { + anOldNodeToNewNode[ aNode ] = aNewNode; + myLastCreatedNodes.Append( aNewNode ); + } + } + + // Create map of new nodes for modified elements + + std::map< SMDS_MeshElement*, vector > anElemToNodes; + + std::list< int >::const_iterator anElemIter; + for ( anElemIter = theListOfModifiedElems.begin(); + anElemIter != theListOfModifiedElems.end(); ++anElemIter ) + { + int aCurr = *anElemIter; + SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr ); + if ( !anElem ) + continue; - // check similarity of elements of the sides - if (aResult == SEW_OK && ( face[0] && !face[1] ) || ( !face[0] && face[1] )) { - MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); - if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found - aResult = ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); - } - else { - aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + vector aNodeArr( anElem->NbNodes() ); + + SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); + int ind = 0; + while ( anIter->more() ) + { + SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next(); + if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() ) + { + const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ]; + aNodeArr[ ind++ ] = aNewNode; } - break; // do not return because it s necessary to remove tmp faces + else + aNodeArr[ ind++ ] = aCurrNode; } + anElemToNodes[ anElem ] = aNodeArr; + } - // set nodes to merge - // ------------------- + // Change nodes of elements - if ( face[0] && face[1] ) { - int nbNodes = face[0]->NbNodes(); - if ( nbNodes != face[1]->NbNodes() ) { - MESSAGE("Diff nb of face nodes"); - aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; - break; // do not return because it s necessary to remove tmp faces - } - bool reverse[] = { false, false }; // order of notLinkNodes of quadrangle - if ( nbNodes == 3 ) { - //nReplaceMap.insert( TNodeNodeMap::value_type - // ( notLinkNodes[0][0], notLinkNodes[1][0] )); - nReplaceMap.insert( TNodeNodeMap::value_type - ( notLinkNodes1[0], notLinkNodes2[0] )); - } - else { - for ( iSide = 0; iSide < 2; iSide++ ) { // loop on 2 sides - // analyse link orientation in faces - int i1 = iLinkNode[ iSide ][ 0 ]; - int i2 = iLinkNode[ iSide ][ 1 ]; - reverse[ iSide ] = Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1; - // if notLinkNodes are the first and the last ones, then - // their order does not correspond to the link orientation - if (( i1 == 1 && i2 == 2 ) || - ( i1 == 2 && i2 == 1 )) - reverse[ iSide ] = !reverse[ iSide ]; - } - if ( reverse[0] == reverse[1] ) { - //nReplaceMap.insert( TNodeNodeMap::value_type - // ( notLinkNodes[0][0], notLinkNodes[1][0] )); - //nReplaceMap.insert( TNodeNodeMap::value_type - // ( notLinkNodes[0][1], notLinkNodes[1][1] )); - for(int nn=0; nn >::iterator + anElemToNodesIter = anElemToNodes.begin(); + for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter ) + { + const SMDS_MeshElement* anElem = anElemToNodesIter->first; + vector aNodeArr = anElemToNodesIter->second; + if ( anElem ) + { + MESSAGE("ChangeElementNodes"); + aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() ); } + } - // add other links of the faces to linkList - // ----------------------------------------- + return true; +} - //const SMDS_MeshNode** nodes = faceNodes[ 0 ]; - for ( iNode = 0; iNode < nbNodes; iNode++ ) { - //linkID = aLinkID_Gen.GetLinkID( nodes[iNode], nodes[iNode+1] ); - linkID = aLinkID_Gen.GetLinkID( fnodes1[iNode], fnodes1[iNode+1] ); - pair< set::iterator, bool > iter_isnew = linkIdSet.insert( linkID ); - if ( !iter_isnew.second ) { // already in a set: no need to process - linkIdSet.erase( iter_isnew.first ); - } - else // new in set == encountered for the first time: add - { - //const SMDS_MeshNode* n1 = nodes[ iNode ]; - //const SMDS_MeshNode* n2 = nodes[ iNode + 1]; - const SMDS_MeshNode* n1 = fnodes1[ iNode ]; - const SMDS_MeshNode* n2 = fnodes1[ iNode + 1]; - linkList[0].push_back ( NLink( n1, n2 )); - linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); - } - } - } // 2 faces found - } // loop on link lists +namespace { - if ( aResult == SEW_OK && - ( linkIt[0] != linkList[0].end() || - !faceSetPtr[0]->empty() || !faceSetPtr[1]->empty() )) { - MESSAGE( (linkIt[0] != linkList[0].end()) <<" "<< (faceSetPtr[0]->empty()) << - " " << (faceSetPtr[1]->empty())); - aResult = SEW_TOPO_DIFF_SETS_OF_ELEMENTS; + //================================================================================ + /*! + \brief Check if element located inside shape + \return TRUE if IN or ON shape, FALSE otherwise + */ + //================================================================================ + + template + bool isInside(const SMDS_MeshElement* theElem, + Classifier& theClassifier, + const double theTol) + { + gp_XYZ centerXYZ (0, 0, 0); + SMDS_ElemIteratorPtr aNodeItr = theElem->nodesIterator(); + while (aNodeItr->more()) + centerXYZ += SMESH_TNodeXYZ(cast2Node( aNodeItr->next())); + + gp_Pnt aPnt = centerXYZ / theElem->NbNodes(); + theClassifier.Perform(aPnt, theTol); + TopAbs_State aState = theClassifier.State(); + return (aState == TopAbs_IN || aState == TopAbs_ON ); } - // ==================================================================== - // 3. Replace nodes in elements of the side 1 and remove replaced nodes - // ==================================================================== - - // delete temporary faces: they are in reverseElements of actual nodes - SMDS_FaceIteratorPtr tmpFaceIt = aTmpFacesMesh.facesIterator(); - while ( tmpFaceIt->more() ) - aTmpFacesMesh.RemoveElement( tmpFaceIt->next() ); + //================================================================================ + /*! + * \brief Classifier of the 3D point on the TopoDS_Face + * with interaface suitable for isInside() + */ + //================================================================================ - if ( aResult != SEW_OK) - return aResult; + struct _FaceClassifier + { + Extrema_ExtPS _extremum; + BRepAdaptor_Surface _surface; + TopAbs_State _state; - list< int > nodeIDsToRemove/*, elemIDsToRemove*/; - // loop on nodes replacement map - TNodeNodeMap::iterator nReplaceMapIt = nReplaceMap.begin(), nnIt; - for ( ; nReplaceMapIt != nReplaceMap.end(); nReplaceMapIt++ ) - if ( (*nReplaceMapIt).first != (*nReplaceMapIt).second ) { - const SMDS_MeshNode* nToRemove = (*nReplaceMapIt).first; - nodeIDsToRemove.push_back( nToRemove->GetID() ); - // loop on elements sharing nToRemove - SMDS_ElemIteratorPtr invElemIt = nToRemove->GetInverseElementIterator(); - while ( invElemIt->more() ) { - const SMDS_MeshElement* e = invElemIt->next(); - // get a new suite of nodes: make replacement - int nbReplaced = 0, i = 0, nbNodes = e->NbNodes(); - vector< const SMDS_MeshNode*> nodes( nbNodes ); - SMDS_ElemIteratorPtr nIt = e->nodesIterator(); - while ( nIt->more() ) { - const SMDS_MeshNode* n = - static_cast( nIt->next() ); - nnIt = nReplaceMap.find( n ); - if ( nnIt != nReplaceMap.end() ) { - nbReplaced++; - n = (*nnIt).second; - } - nodes[ i++ ] = n; - } - // if ( nbReplaced == nbNodes && e->GetType() == SMDSAbs_Face ) - // elemIDsToRemove.push_back( e->GetID() ); - // else - if ( nbReplaced ) - aMesh->ChangeElementNodes( e, & nodes[0], nbNodes ); - } + _FaceClassifier(const TopoDS_Face& face):_extremum(),_surface(face),_state(TopAbs_OUT) + { + _extremum.Initialize( _surface, + _surface.FirstUParameter(), _surface.LastUParameter(), + _surface.FirstVParameter(), _surface.LastVParameter(), + _surface.Tolerance(), _surface.Tolerance() ); } - - Remove( nodeIDsToRemove, true ); - - return aResult; + void Perform(const gp_Pnt& aPnt, double theTol) + { + _state = TopAbs_OUT; + _extremum.Perform(aPnt); + if ( _extremum.IsDone() ) + for ( int iSol = 1; iSol <= _extremum.NbExt() && _state == TopAbs_OUT; ++iSol) +#if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1 + _state = ( _extremum.SquareDistance(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT ); +#else + _state = ( _extremum.Value(iSol) <= theTol ? TopAbs_IN : TopAbs_OUT ); +#endif + } + TopAbs_State State() const + { + return _state; + } + }; } //================================================================================ - /*! - * \brief Find corresponding nodes in two sets of faces - * \param theSide1 - first face set - * \param theSide2 - second first face - * \param theFirstNode1 - a boundary node of set 1 - * \param theFirstNode2 - a node of set 2 corresponding to theFirstNode1 - * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1 - * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1 - * \param nReplaceMap - output map of corresponding nodes - * \retval bool - is a success or not - */ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - group of of elements (edges or faces) to be replicated + \param theNodesNot - group of nodes not to replicate + \param theShape - shape to detect affected elements (element which geometric center + located on or inside shape). + The replicated nodes should be associated to affected elements. + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ //================================================================================ -#ifdef _DEBUG_ -//#define DEBUG_MATCHING_NODES -#endif +bool SMESH_MeshEditor::DoubleNodesInRegion( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TopoDS_Shape& theShape ) +{ + if ( theShape.IsNull() ) + return false; -SMESH_MeshEditor::Sew_Error -SMESH_MeshEditor::FindMatchingNodes(set& theSide1, - set& theSide2, - const SMDS_MeshNode* theFirstNode1, - const SMDS_MeshNode* theFirstNode2, - const SMDS_MeshNode* theSecondNode1, - const SMDS_MeshNode* theSecondNode2, - TNodeNodeMap & nReplaceMap) + const double aTol = Precision::Confusion(); + auto_ptr< BRepClass3d_SolidClassifier> bsc3d; + auto_ptr<_FaceClassifier> aFaceClassifier; + if ( theShape.ShapeType() == TopAbs_SOLID ) + { + bsc3d.reset( new BRepClass3d_SolidClassifier(theShape));; + bsc3d->PerformInfinitePoint(aTol); + } + else if (theShape.ShapeType() == TopAbs_FACE ) + { + aFaceClassifier.reset( new _FaceClassifier(TopoDS::Face(theShape))); + } + + // iterates on indicated elements and get elements by back references from their nodes + TIDSortedElemSet anAffected; + TIDSortedElemSet::const_iterator elemItr = theElems.begin(); + for ( ; elemItr != theElems.end(); ++elemItr ) + { + SMDS_MeshElement* anElem = (SMDS_MeshElement*)*elemItr; + if (!anElem) + continue; + + SMDS_ElemIteratorPtr nodeItr = anElem->nodesIterator(); + while ( nodeItr->more() ) + { + const SMDS_MeshNode* aNode = cast2Node(nodeItr->next()); + if ( !aNode || theNodesNot.find(aNode) != theNodesNot.end() ) + continue; + SMDS_ElemIteratorPtr backElemItr = aNode->GetInverseElementIterator(); + while ( backElemItr->more() ) + { + const SMDS_MeshElement* curElem = backElemItr->next(); + if ( curElem && theElems.find(curElem) == theElems.end() && + ( bsc3d.get() ? + isInside( curElem, *bsc3d, aTol ) : + isInside( curElem, *aFaceClassifier, aTol ))) + anAffected.insert( curElem ); + } + } + } + return DoubleNodes( theElems, theNodesNot, anAffected ); +} + +/*! + * \brief compute an oriented angle between two planes defined by four points. + * The vector (p0,p1) defines the intersection of the 2 planes (p0,p1,g1) and (p0,p1,g2) + * @param p0 base of the rotation axe + * @param p1 extremity of the rotation axe + * @param g1 belongs to the first plane + * @param g2 belongs to the second plane + */ +double SMESH_MeshEditor::OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2) { - set * faceSetPtr[] = { &theSide1, &theSide2 }; +// MESSAGE(" p0: " << p0.X() << " " << p0.Y() << " " << p0.Z()); +// MESSAGE(" p1: " << p1.X() << " " << p1.Y() << " " << p1.Z()); +// MESSAGE(" g1: " << g1.X() << " " << g1.Y() << " " << g1.Z()); +// MESSAGE(" g2: " << g2.X() << " " << g2.Y() << " " << g2.Z()); + gp_Vec vref(p0, p1); + gp_Vec v1(p0, g1); + gp_Vec v2(p0, g2); + gp_Vec n1 = vref.Crossed(v1); + gp_Vec n2 = vref.Crossed(v2); + return n2.AngleWithRef(n1, vref); +} - nReplaceMap.clear(); - if ( theFirstNode1 != theFirstNode2 ) - nReplaceMap.insert( make_pair( theFirstNode1, theFirstNode2 )); - if ( theSecondNode1 != theSecondNode2 ) - nReplaceMap.insert( make_pair( theSecondNode1, theSecondNode2 )); +/*! + * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand. + * The list of groups must describe a partition of the mesh volumes. + * The nodes of the internal faces at the boundaries of the groups are doubled. + * In option, the internal faces are replaced by flat elements. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * The flat elements are stored in groups of volumes. + * @param theElems - list of groups of volumes, where a group of volume is a set of + * SMDS_MeshElements sorted by Id. + * @param createJointElems - if TRUE, create the elements + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ +bool SMESH_MeshEditor::DoubleNodesOnGroupBoundaries( const std::vector& theElems, + bool createJointElems) +{ + MESSAGE("----------------------------------------------"); + MESSAGE("SMESH_MeshEditor::doubleNodesOnGroupBoundaries"); + MESSAGE("----------------------------------------------"); + + SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS(); + meshDS->BuildDownWardConnectivity(true); + CHRONO(50); + SMDS_UnstructuredGrid *grid = meshDS->getGrid(); + + // --- build the list of faces shared by 2 domains (group of elements), with their domain and volume indexes + // build the list of cells with only a node or an edge on the border, with their domain and volume indexes + // build the list of nodes shared by 2 or more domains, with their domain indexes + + std::map, DownIdCompare> faceDomains; // face --> (id domain --> id volume) + std::mapcelldom; // cell vtkId --> domain + std::map, DownIdCompare> cellDomains; // oldNode --> (id domain --> id cell) + std::map > nodeDomains; // oldId --> (domainId --> newId) + faceDomains.clear(); + celldom.clear(); + cellDomains.clear(); + nodeDomains.clear(); + std::map emptyMap; + std::set emptySet; + emptyMap.clear(); + + for (int idom = 0; idom < theElems.size(); idom++) + { - set< SMESH_TLink > linkSet; // set of nodes where order of nodes is ignored - linkSet.insert( SMESH_TLink( theFirstNode1, theSecondNode1 )); + // --- build a map (face to duplicate --> volume to modify) + // with all the faces shared by 2 domains (group of elements) + // and corresponding volume of this domain, for each shared face. + // a volume has a face shared by 2 domains if it has a neighbor which is not in is domain. - list< NLink > linkList[2]; - linkList[0].push_back( NLink( theFirstNode1, theSecondNode1 )); - linkList[1].push_back( NLink( theFirstNode2, theSecondNode2 )); + //MESSAGE("Domain " << idom); + const TIDSortedElemSet& domain = theElems[idom]; + TIDSortedElemSet::const_iterator elemItr = domain.begin(); + for (; elemItr != domain.end(); ++elemItr) + { + SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr; + if (!anElem) + continue; + int vtkId = anElem->getVtkId(); + //MESSAGE(" vtkId " << vtkId << " smdsId " << anElem->GetID()); + int neighborsVtkIds[NBMAXNEIGHBORS]; + int downIds[NBMAXNEIGHBORS]; + unsigned char downTypes[NBMAXNEIGHBORS]; + int nbNeighbors = grid->GetNeighbors(neighborsVtkIds, downIds, downTypes, vtkId); + for (int n = 0; n < nbNeighbors; n++) + { + int smdsId = meshDS->fromVtkToSmds(neighborsVtkIds[n]); + const SMDS_MeshElement* elem = meshDS->FindElement(smdsId); + if (! domain.count(elem)) // neighbor is in another domain : face is shared + { + DownIdType face(downIds[n], downTypes[n]); + if (!faceDomains.count(face)) + faceDomains[face] = emptyMap; // create an empty entry for face + if (!faceDomains[face].count(idom)) + { + faceDomains[face][idom] = vtkId; // volume associated to face in this domain + celldom[vtkId] = idom; + //MESSAGE(" cell with a border " << vtkId << " domain " << idom); + } + } + } + } + } - // loop on links in linkList; find faces by links and append links - // of the found faces to linkList - list< NLink >::iterator linkIt[] = { linkList[0].begin(), linkList[1].begin() } ; - for ( ; linkIt[0] != linkList[0].end(); linkIt[0]++, linkIt[1]++ ) { - NLink link[] = { *linkIt[0], *linkIt[1] }; - if ( linkSet.find( link[0] ) == linkSet.end() ) - continue; + //MESSAGE("Number of shared faces " << faceDomains.size()); + std::map, DownIdCompare>::iterator itface; - // by links, find faces in the face sets, - // and find indices of link nodes in the found faces; - // in a face set, there is only one or no face sharing a link - // --------------------------------------------------------------- + // --- explore the shared faces domain by domain, + // explore the nodes of the face and see if they belong to a cell in the domain, + // which has only a node or an edge on the border (not a shared face) - const SMDS_MeshElement* face[] = { 0, 0 }; - list notLinkNodes[2]; - //bool reverse[] = { false, false }; // order of notLinkNodes - int nbNodes[2]; - for ( int iSide = 0; iSide < 2; iSide++ ) // loop on 2 sides + for (int idomain = 0; idomain < theElems.size(); idomain++) { - const SMDS_MeshNode* n1 = link[iSide].first; - const SMDS_MeshNode* n2 = link[iSide].second; - set * faceSet = faceSetPtr[ iSide ]; - set< const SMDS_MeshElement* > facesOfNode1; - for ( int iNode = 0; iNode < 2; iNode++ ) // loop on 2 nodes of a link - { - // during a loop of the first node, we find all faces around n1, - // during a loop of the second node, we find one face sharing both n1 and n2 - const SMDS_MeshNode* n = iNode ? n1 : n2; // a node of a link - SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face); - while ( fIt->more() ) { // loop on faces sharing a node - const SMDS_MeshElement* f = fIt->next(); - if (faceSet->find( f ) != faceSet->end() && // f is in face set - ! facesOfNode1.insert( f ).second ) // f encounters twice - { - if ( face[ iSide ] ) { - MESSAGE( "2 faces per link " ); - return ( iSide ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); + //MESSAGE("Domain " << idomain); + const TIDSortedElemSet& domain = theElems[idomain]; + itface = faceDomains.begin(); + for (; itface != faceDomains.end(); ++itface) + { + std::map domvol = itface->second; + if (!domvol.count(idomain)) + continue; + DownIdType face = itface->first; + //MESSAGE(" --- face " << face.cellId); + std::set oldNodes; + oldNodes.clear(); + grid->GetNodeIds(oldNodes, face.cellId, face.cellType); + std::set::iterator itn = oldNodes.begin(); + for (; itn != oldNodes.end(); ++itn) + { + int oldId = *itn; + //MESSAGE(" node " << oldId); + std::set cells; + cells.clear(); + vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId); + for (int i=0; iFindElement(GetMeshDS()->fromVtkToSmds(vtkId)); + if (!domain.count(anElem)) + continue; + int vtkType = grid->GetCellType(vtkId); + int downId = grid->CellIdToDownId(vtkId); + if (downId < 0) + { + MESSAGE("doubleNodesOnGroupBoundaries: internal algorithm problem"); + continue; // not OK at this stage of the algorithm: + //no cells created after BuildDownWardConnectivity + } + DownIdType aCell(downId, vtkType); + if (celldom.count(vtkId)) + continue; + cellDomains[aCell][idomain] = vtkId; + celldom[vtkId] = idomain; + //MESSAGE(" cell " << vtkId << " domain " << idomain); + } } - face[ iSide ] = f; - faceSet->erase( f ); + } + } - // get not link nodes - int nbN = f->NbNodes(); - if ( f->IsQuadratic() ) - nbN /= 2; - nbNodes[ iSide ] = nbN; - list< const SMDS_MeshNode* > & nodes = notLinkNodes[ iSide ]; - int i1 = f->GetNodeIndex( n1 ); - int i2 = f->GetNodeIndex( n2 ); - int iEnd = nbN, iBeg = -1, iDelta = 1; - bool reverse = ( Abs( i1 - i2 ) == 1 ? i1 > i2 : i2 > i1 ); - if ( reverse ) { - std::swap( iEnd, iBeg ); iDelta = -1; + // --- explore the shared faces domain by domain, to duplicate the nodes in a coherent way + // for each shared face, get the nodes + // for each node, for each domain of the face, create a clone of the node + + // --- edges at the intersection of 3 or 4 domains, with the order of domains to build + // junction elements of type prism or hexa. the key is the pair of nodesId (lower first) + // the value is the ordered domain ids. (more than 4 domains not taken into account) + + std::map, std::vector > edgesMultiDomains; // nodes of edge --> ordered domains + std::map > mutipleNodes; // nodes multi domains with domain order + std::map > mutipleNodesToFace; // nodes multi domains with domain order to transform in Face (junction between 3 or more 2D domains) + + for (int idomain = 0; idomain < theElems.size(); idomain++) + { + itface = faceDomains.begin(); + for (; itface != faceDomains.end(); ++itface) + { + std::map domvol = itface->second; + if (!domvol.count(idomain)) + continue; + DownIdType face = itface->first; + //MESSAGE(" --- face " << face.cellId); + std::set oldNodes; + oldNodes.clear(); + grid->GetNodeIds(oldNodes, face.cellId, face.cellType); + bool isMultipleDetected = false; + std::set::iterator itn = oldNodes.begin(); + for (; itn != oldNodes.end(); ++itn) + { + int oldId = *itn; + //MESSAGE(" node " << oldId); + if (!nodeDomains.count(oldId)) + nodeDomains[oldId] = emptyMap; // create an empty entry for node + if (nodeDomains[oldId].empty()) + nodeDomains[oldId][idomain] = oldId; // keep the old node in the first domain + std::map::iterator itdom = domvol.begin(); + for (; itdom != domvol.end(); ++itdom) + { + int idom = itdom->first; + //MESSAGE(" domain " << idom); + if (!nodeDomains[oldId].count(idom)) // --- node to clone + { + if (nodeDomains[oldId].size() >= 2) // a multiple node + { + vector orderedDoms; + //MESSAGE("multiple node " << oldId); + isMultipleDetected =true; + if (mutipleNodes.count(oldId)) + orderedDoms = mutipleNodes[oldId]; + else + { + map::iterator it = nodeDomains[oldId].begin(); + for (; it != nodeDomains[oldId].end(); ++it) + orderedDoms.push_back(it->first); + } + orderedDoms.push_back(idom); // TODO order ==> push_front or back + //stringstream txt; + //for (int i=0; iGetPoint(oldId); + SMDS_MeshNode *newNode = meshDS->AddNode(coords[0], coords[1], coords[2]); + int newId = newNode->getVtkId(); + nodeDomains[oldId][idom] = newId; // cloned node for other domains + //MESSAGE(" newNode " << newId << " oldNode " << oldId << " size=" <= 3) + { + //MESSAGE("confirm multiple node " << oldId); + isMultipleDetected =true; + } + } } - int i = i2; - while ( true ) { - i += iDelta; - if ( i == iEnd ) i = iBeg + iDelta; - if ( i == i1 ) break; - nodes.push_back ( f->GetNode( i ) ); + if (isMultipleDetected) // check if an edge of the face is shared between 3 or more domains + { + //MESSAGE("multiple Nodes detected on a shared face"); + int downId = itface->first.cellId; + unsigned char cellType = itface->first.cellType; + // --- shared edge or shared face ? + if ((cellType == VTK_LINE) || (cellType == VTK_QUADRATIC_EDGE)) // shared edge (between two faces) + { + int nodes[3]; + int nbNodes = grid->getDownArray(cellType)->getNodes(downId, nodes); + for (int i=0; i< nbNodes; i=i+nbNodes-1) // i=0 , i=nbNodes-1 + if (mutipleNodes.count(nodes[i])) + if (!mutipleNodesToFace.count(nodes[i])) + mutipleNodesToFace[nodes[i]] = mutipleNodes[nodes[i]]; + } + else // shared face (between two volumes) + { + int nbEdges = grid->getDownArray(cellType)->getNumberOfDownCells(downId); + const int* downEdgeIds = grid->getDownArray(cellType)->getDownCells(downId); + const unsigned char* edgeType = grid->getDownArray(cellType)->getDownTypes(downId); + for (int ie =0; ie < nbEdges; ie++) + { + int nodes[3]; + int nbNodes = grid->getDownArray(edgeType[ie])->getNodes(downEdgeIds[ie], nodes); + if (mutipleNodes.count(nodes[0]) && mutipleNodes.count(nodes[nbNodes-1])) + { + vector vn0 = mutipleNodes[nodes[0]]; + vector vn1 = mutipleNodes[nodes[nbNodes - 1]]; + sort( vn0.begin(), vn0.end() ); + sort( vn1.begin(), vn1.end() ); + if (vn0 == vn1) + { + //MESSAGE(" detect edgesMultiDomains " << nodes[0] << " " << nodes[nbNodes - 1]); + double *coords = grid->GetPoint(nodes[0]); + gp_Pnt p0(coords[0], coords[1], coords[2]); + coords = grid->GetPoint(nodes[nbNodes - 1]); + gp_Pnt p1(coords[0], coords[1], coords[2]); + gp_Pnt gref; + int vtkVolIds[1000]; // an edge can belong to a lot of volumes + map domvol; // domain --> a volume with the edge + map angleDom; // oriented angles between planes defined by edge and volume centers + int nbvol = grid->GetParentVolumes(vtkVolIds, downEdgeIds[ie], edgeType[ie]); + for (int id=0; id < vn0.size(); id++) + { + int idom = vn0[id]; + for (int ivol=0; ivolfromVtkToSmds(vtkVolIds[ivol]); + SMDS_MeshElement* elem = (SMDS_MeshElement*)meshDS->FindElement(smdsId); + if (theElems[idom].count(elem)) + { + SMDS_VtkVolume* svol = dynamic_cast(elem); + domvol[idom] = svol; + //MESSAGE(" domain " << idom << " volume " << elem->GetID()); + double values[3]; + vtkIdType npts = 0; + vtkIdType* pts = 0; + grid->GetCellPoints(vtkVolIds[ivol], npts, pts); + SMDS_VtkVolume::gravityCenter(grid, pts, npts, values); + if (id ==0) + { + gref.SetXYZ(gp_XYZ(values[0], values[1], values[2])); + angleDom[idom] = 0; + } + else + { + gp_Pnt g(values[0], values[1], values[2]); + angleDom[idom] = OrientedAngle(p0, p1, gref, g); // -pisecond << " angle " << ib->first); + } + for (int ino = 0; ino < nbNodes; ino++) + vnodes.push_back(nodes[ino]); + edgesMultiDomains[vnodes] = vdom; // nodes vector --> ordered domains + } + } + } + } } - } } - } } - // check similarity of elements of the sides - if (( face[0] && !face[1] ) || ( !face[0] && face[1] )) { - MESSAGE("Correspondent face not found on side " << ( face[0] ? 1 : 0 )); - if ( nReplaceMap.size() == 2 ) { // faces on input nodes not found - return ( face[0] ? SEW_BAD_SIDE2_NODES : SEW_BAD_SIDE1_NODES ); - } - else { - return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; - } + + // --- iterate on shared faces (volumes to modify, face to extrude) + // get node id's of the face (id SMDS = id VTK) + // create flat element with old and new nodes if requested + + // --- new quad nodes on flat quad elements: oldId --> ((domain1 X domain2) --> newId) + // (domain1 X domain2) = domain1 + MAXINT*domain2 + + std::map > nodeQuadDomains; + std::map mapOfJunctionGroups; + + if (createJointElems) + { + itface = faceDomains.begin(); + for (; itface != faceDomains.end(); ++itface) + { + DownIdType face = itface->first; + std::set oldNodes; + std::set::iterator itn; + oldNodes.clear(); + grid->GetNodeIds(oldNodes, face.cellId, face.cellType); + + std::map domvol = itface->second; + std::map::iterator itdom = domvol.begin(); + int dom1 = itdom->first; + int vtkVolId = itdom->second; + itdom++; + int dom2 = itdom->first; + SMDS_MeshCell *vol = grid->extrudeVolumeFromFace(vtkVolId, dom1, dom2, oldNodes, nodeDomains, + nodeQuadDomains); + stringstream grpname; + grpname << "j_"; + if (dom1 < dom2) + grpname << dom1 << "_" << dom2; + else + grpname << dom2 << "_" << dom1; + int idg; + string namegrp = grpname.str(); + if (!mapOfJunctionGroups.count(namegrp)) + mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(vol->GetType(), namegrp.c_str(), idg); + SMESHDS_Group *sgrp = dynamic_cast(mapOfJunctionGroups[namegrp]->GetGroupDS()); + if (sgrp) + sgrp->Add(vol->GetID()); + } } - // set nodes to merge - // ------------------- + // --- create volumes on multiple domain intersection if requested + // iterate on mutipleNodesToFace + // iterate on edgesMultiDomains - if ( face[0] && face[1] ) { - if ( nbNodes[0] != nbNodes[1] ) { - MESSAGE("Diff nb of face nodes"); - return SEW_TOPO_DIFF_SETS_OF_ELEMENTS; - } -#ifdef DEBUG_MATCHING_NODES - MESSAGE ( " Link 1: " << link[0].first->GetID() <<" "<< link[0].second->GetID() - << " F 1: " << face[0] << "| Link 2: " << link[1].first->GetID() <<" " - << link[1].second->GetID() << " F 2: " << face[1] << " | Bind: " ) ; -#endif - int nbN = nbNodes[0]; - { - list::iterator n1 = notLinkNodes[0].begin(); - list::iterator n2 = notLinkNodes[1].begin(); - for ( int i = 0 ; i < nbN - 2; ++i ) { -#ifdef DEBUG_MATCHING_NODES - MESSAGE ( (*n1)->GetID() << " to " << (*n2)->GetID() ); -#endif - nReplaceMap.insert( make_pair( *(n1++), *(n2++) )); + if (createJointElems) + { + // --- iterate on mutipleNodesToFace + + std::map >::iterator itn = mutipleNodesToFace.begin(); + for (; itn != mutipleNodesToFace.end(); ++itn) + { + int node = itn->first; + vector orderDom = itn->second; + vector orderedNodes; + for (int idom = 0; idom GetMeshDS()->AddFaceFromVtkIds(orderedNodes); + + stringstream grpname; + grpname << "m2j_"; + grpname << 0 << "_" << 0; + int idg; + string namegrp = grpname.str(); + if (!mapOfJunctionGroups.count(namegrp)) + mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Face, namegrp.c_str(), idg); + SMESHDS_Group *sgrp = dynamic_cast(mapOfJunctionGroups[namegrp]->GetGroupDS()); + if (sgrp) + sgrp->Add(face->GetID()); } - } - // add other links of the face 1 to linkList - // ----------------------------------------- + // --- iterate on edgesMultiDomains - const SMDS_MeshElement* f0 = face[0]; - const SMDS_MeshNode* n1 = f0->GetNode( nbN - 1 ); - for ( int i = 0; i < nbN; i++ ) - { - const SMDS_MeshNode* n2 = f0->GetNode( i ); - pair< set< SMESH_TLink >::iterator, bool > iter_isnew = - linkSet.insert( SMESH_TLink( n1, n2 )); - if ( !iter_isnew.second ) { // already in a set: no need to process - linkSet.erase( iter_isnew.first ); + std::map, std::vector >::iterator ite = edgesMultiDomains.begin(); + for (; ite != edgesMultiDomains.end(); ++ite) + { + vector nodes = ite->first; + vector orderDom = ite->second; + vector orderedNodes; + if (nodes.size() == 2) + { + //MESSAGE(" use edgesMultiDomains " << nodes[0] << " " << nodes[1]); + for (int ino=0; ino < nodes.size(); ino++) + if (orderDom.size() == 3) + for (int idom = 0; idom =0; idom--) + orderedNodes.push_back( nodeDomains[nodes[ino]][orderDom[idom]] ); + SMDS_MeshVolume* vol = this->GetMeshDS()->AddVolumeFromVtkIds(orderedNodes); + + stringstream grpname; + grpname << "mj_"; + grpname << 0 << "_" << 0; + int idg; + string namegrp = grpname.str(); + if (!mapOfJunctionGroups.count(namegrp)) + mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg); + SMESHDS_Group *sgrp = dynamic_cast(mapOfJunctionGroups[namegrp]->GetGroupDS()); + if (sgrp) + sgrp->Add(vol->GetID()); + } + else + { + MESSAGE("Quadratic multiple joints not implemented"); + // TODO quadratic nodes + } } - else // new in set == encountered for the first time: add + } + + // --- list the explicit faces and edges of the mesh that need to be modified, + // i.e. faces and edges built with one or more duplicated nodes. + // associate these faces or edges to their corresponding domain. + // only the first domain found is kept when a face or edge is shared + + std::map, DownIdCompare> faceOrEdgeDom; // cellToModify --> (id domain --> id cell) + std::map feDom; // vtk id of cell to modify --> id domain + faceOrEdgeDom.clear(); + feDom.clear(); + + for (int idomain = 0; idomain < theElems.size(); idomain++) + { + std::map >::const_iterator itnod = nodeDomains.begin(); + for (; itnod != nodeDomains.end(); ++itnod) { -#ifdef DEBUG_MATCHING_NODES - MESSAGE ( "Add link 1: " << n1->GetID() << " " << n2->GetID() << " " - << " | link 2: " << nReplaceMap[n1]->GetID() << " " << nReplaceMap[n2]->GetID() << " " ); -#endif - linkList[0].push_back ( NLink( n1, n2 )); - linkList[1].push_back ( NLink( nReplaceMap[n1], nReplaceMap[n2] )); + int oldId = itnod->first; + //MESSAGE(" node " << oldId); + vtkCellLinks::Link l = grid->GetCellLinks()->GetLink(oldId); + for (int i = 0; i < l.ncells; i++) + { + int vtkId = l.cells[i]; + int vtkType = grid->GetCellType(vtkId); + int downId = grid->CellIdToDownId(vtkId); + if (downId < 0) + continue; // new cells: not to be modified + DownIdType aCell(downId, vtkType); + int volParents[1000]; + int nbvol = grid->GetParentVolumes(volParents, vtkId); + for (int j = 0; j < nbvol; j++) + if (celldom.count(volParents[j]) && (celldom[volParents[j]] == idomain)) + if (!feDom.count(vtkId)) + { + feDom[vtkId] = idomain; + faceOrEdgeDom[aCell] = emptyMap; + faceOrEdgeDom[aCell][idomain] = vtkId; // affect face or edge to the first domain only + //MESSAGE("affect cell " << this->GetMeshDS()->fromVtkToSmds(vtkId) << " domain " << idomain + // << " type " << vtkType << " downId " << downId); + } + } } - n1 = n2; - } - } // 2 faces found - } // loop on link lists + } - return SEW_OK; + // --- iterate on shared faces (volumes to modify, face to extrude) + // get node id's of the face + // replace old nodes by new nodes in volumes, and update inverse connectivity + + std::map, DownIdCompare>* maps[3] = {&faceDomains, &cellDomains, &faceOrEdgeDom}; + for (int m=0; m<3; m++) + { + std::map, DownIdCompare>* amap = maps[m]; + itface = (*amap).begin(); + for (; itface != (*amap).end(); ++itface) + { + DownIdType face = itface->first; + std::set oldNodes; + std::set::iterator itn; + oldNodes.clear(); + grid->GetNodeIds(oldNodes, face.cellId, face.cellType); + //MESSAGE("examine cell, downId " << face.cellId << " type " << int(face.cellType)); + std::map localClonedNodeIds; + + std::map domvol = itface->second; + std::map::iterator itdom = domvol.begin(); + for (; itdom != domvol.end(); ++itdom) + { + int idom = itdom->first; + int vtkVolId = itdom->second; + //MESSAGE("modify nodes of cell " << this->GetMeshDS()->fromVtkToSmds(vtkVolId) << " domain " << idom); + localClonedNodeIds.clear(); + for (itn = oldNodes.begin(); itn != oldNodes.end(); ++itn) + { + int oldId = *itn; + if (nodeDomains[oldId].count(idom)) + { + localClonedNodeIds[oldId] = nodeDomains[oldId][idom]; + //MESSAGE(" node " << oldId << " --> " << localClonedNodeIds[oldId]); + } + } + meshDS->ModifyCellNodes(vtkVolId, localClonedNodeIds); + } + } + } + + meshDS->CleanDownWardConnectivity(); // Mesh has been modified, downward connectivity is no more usable, free memory + grid->BuildLinks(); + + CHRONOSTOP(50); + counters::stats(); + return true; } /*! - \brief Creates a hole in a mesh by doubling the nodes of some particular elements - \param theNodes - identifiers of nodes to be doubled - \param theModifiedElems - identifiers of elements to be updated by the new (doubled) - nodes. If list of element identifiers is empty then nodes are doubled but - they not assigned to elements - \return TRUE if operation has been completed successfully, FALSE otherwise -*/ -bool SMESH_MeshEditor::DoubleNodes( const std::list< int >& theListOfNodes, - const std::list< int >& theListOfModifiedElems ) + * \brief Double nodes on some external faces and create flat elements. + * Flat elements are mainly used by some types of mechanic calculations. + * + * Each group of the list must be constituted of faces. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * @param theElems - list of groups of faces, where a group of faces is a set of + * SMDS_MeshElements sorted by Id. + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ +bool SMESH_MeshEditor::CreateFlatElementsOnFacesGroups(const std::vector& theElems) { - myLastCreatedElems.Clear(); - myLastCreatedNodes.Clear(); + MESSAGE("-------------------------------------------------"); + MESSAGE("SMESH_MeshEditor::CreateFlatElementsOnFacesGroups"); + MESSAGE("-------------------------------------------------"); - if ( theListOfNodes.size() == 0 ) - return false; + SMESHDS_Mesh *meshDS = this->myMesh->GetMeshDS(); - SMESHDS_Mesh* aMeshDS = GetMeshDS(); - if ( !aMeshDS ) - return false; + // --- For each group of faces + // duplicate the nodes, create a flat element based on the face + // replace the nodes of the faces by their clones - // iterate through nodes and duplicate them + std::map clonedNodes; + std::map intermediateNodes; + clonedNodes.clear(); + intermediateNodes.clear(); + std::map mapOfJunctionGroups; + mapOfJunctionGroups.clear(); - std::map< const SMDS_MeshNode*, const SMDS_MeshNode* > anOldNodeToNewNode; + for (int idom = 0; idom < theElems.size(); idom++) + { + const TIDSortedElemSet& domain = theElems[idom]; + TIDSortedElemSet::const_iterator elemItr = domain.begin(); + for (; elemItr != domain.end(); ++elemItr) + { + SMDS_MeshElement* anElem = (SMDS_MeshElement*) *elemItr; + SMDS_MeshFace* aFace = dynamic_cast (anElem); + if (!aFace) + continue; + // MESSAGE("aFace=" << aFace->GetID()); + bool isQuad = aFace->IsQuadratic(); + vector ln0, ln1, ln2, ln3, ln4; - std::list< int >::const_iterator aNodeIter; - for ( aNodeIter = theListOfNodes.begin(); aNodeIter != theListOfNodes.end(); ++aNodeIter ) - { - int aCurr = *aNodeIter; - SMDS_MeshNode* aNode = (SMDS_MeshNode*)aMeshDS->FindNode( aCurr ); - if ( !aNode ) - continue; + // --- clone the nodes, create intermediate nodes for non medium nodes of a quad face - // duplicate node + SMDS_ElemIteratorPtr nodeIt = aFace->nodesIterator(); + while (nodeIt->more()) + { + const SMDS_MeshNode* node = static_cast (nodeIt->next()); + bool isMedium = isQuad && (aFace->IsMediumNode(node)); + if (isMedium) + ln2.push_back(node); + else + ln0.push_back(node); - const SMDS_MeshNode* aNewNode = aMeshDS->AddNode( aNode->X(), aNode->Y(), aNode->Z() ); - if ( aNewNode ) + const SMDS_MeshNode* clone = 0; + if (!clonedNodes.count(node)) + { + clone = meshDS->AddNode(node->X(), node->Y(), node->Z()); + clonedNodes[node] = clone; + } + else + clone = clonedNodes[node]; + + if (isMedium) + ln3.push_back(clone); + else + ln1.push_back(clone); + + const SMDS_MeshNode* inter = 0; + if (isQuad && (!isMedium)) + { + if (!intermediateNodes.count(node)) + { + inter = meshDS->AddNode(node->X(), node->Y(), node->Z()); + intermediateNodes[node] = inter; + } + else + inter = intermediateNodes[node]; + ln4.push_back(inter); + } + } + + // --- extrude the face + + vector ln; + SMDS_MeshVolume* vol = 0; + vtkIdType aType = aFace->GetVtkType(); + switch (aType) + { + case VTK_TRIANGLE: + vol = meshDS->AddVolume(ln0[2], ln0[1], ln0[0], ln1[2], ln1[1], ln1[0]); + // MESSAGE("vol prism " << vol->GetID()); + ln.push_back(ln1[0]); + ln.push_back(ln1[1]); + ln.push_back(ln1[2]); + break; + case VTK_QUAD: + vol = meshDS->AddVolume(ln0[3], ln0[2], ln0[1], ln0[0], ln1[3], ln1[2], ln1[1], ln1[0]); + // MESSAGE("vol hexa " << vol->GetID()); + ln.push_back(ln1[0]); + ln.push_back(ln1[1]); + ln.push_back(ln1[2]); + ln.push_back(ln1[3]); + break; + case VTK_QUADRATIC_TRIANGLE: + vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln0[0], ln0[1], ln0[2], ln3[0], ln3[1], ln3[2], + ln2[0], ln2[1], ln2[2], ln4[0], ln4[1], ln4[2]); + // MESSAGE("vol quad prism " << vol->GetID()); + ln.push_back(ln1[0]); + ln.push_back(ln1[1]); + ln.push_back(ln1[2]); + ln.push_back(ln3[0]); + ln.push_back(ln3[1]); + ln.push_back(ln3[2]); + break; + case VTK_QUADRATIC_QUAD: +// vol = meshDS->AddVolume(ln0[0], ln0[1], ln0[2], ln0[3], ln1[0], ln1[1], ln1[2], ln1[3], +// ln2[0], ln2[1], ln2[2], ln2[3], ln3[0], ln3[1], ln3[2], ln3[3], +// ln4[0], ln4[1], ln4[2], ln4[3]); + vol = meshDS->AddVolume(ln1[0], ln1[1], ln1[2], ln1[3], ln0[0], ln0[1], ln0[2], ln0[3], + ln3[0], ln3[1], ln3[2], ln3[3], ln2[0], ln2[1], ln2[2], ln2[3], + ln4[0], ln4[1], ln4[2], ln4[3]); + // MESSAGE("vol quad hexa " << vol->GetID()); + ln.push_back(ln1[0]); + ln.push_back(ln1[1]); + ln.push_back(ln1[2]); + ln.push_back(ln1[3]); + ln.push_back(ln3[0]); + ln.push_back(ln3[1]); + ln.push_back(ln3[2]); + ln.push_back(ln3[3]); + break; + case VTK_POLYGON: + break; + default: + break; + } + + if (vol) + { + stringstream grpname; + grpname << "jf_"; + grpname << idom; + int idg; + string namegrp = grpname.str(); + if (!mapOfJunctionGroups.count(namegrp)) + mapOfJunctionGroups[namegrp] = this->myMesh->AddGroup(SMDSAbs_Volume, namegrp.c_str(), idg); + SMESHDS_Group *sgrp = dynamic_cast(mapOfJunctionGroups[namegrp]->GetGroupDS()); + if (sgrp) + sgrp->Add(vol->GetID()); + } + + // --- modify the face + + aFace->ChangeNodes(&ln[0], ln.size()); + } + } + return true; +} + +//================================================================================ +/*! + * \brief Generates skin mesh (containing 2D cells) from 3D mesh + * The created 2D mesh elements based on nodes of free faces of boundary volumes + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ +//================================================================================ + +bool SMESH_MeshEditor::Make2DMeshFrom3D() +{ + // iterates on volume elements and detect all free faces on them + SMESHDS_Mesh* aMesh = GetMeshDS(); + if (!aMesh) + return false; + //bool res = false; + int nbFree = 0, nbExisted = 0, nbCreated = 0; + SMDS_VolumeIteratorPtr vIt = aMesh->volumesIterator(); + while(vIt->more()) + { + const SMDS_MeshVolume* volume = vIt->next(); + SMDS_VolumeTool vTool( volume, /*ignoreCentralNodes=*/false ); + vTool.SetExternalNormal(); + //const bool isPoly = volume->IsPoly(); + const int iQuad = volume->IsQuadratic(); + for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ ) { - anOldNodeToNewNode[ aNode ] = aNewNode; - myLastCreatedNodes.Append( aNewNode ); + if (!vTool.IsFreeFace(iface)) + continue; + nbFree++; + vector nodes; + int nbFaceNodes = vTool.NbFaceNodes(iface); + const SMDS_MeshNode** faceNodes = vTool.GetFaceNodes(iface); + int inode = 0; + for ( ; inode < nbFaceNodes; inode += iQuad+1) + nodes.push_back(faceNodes[inode]); + if (iQuad) { // add medium nodes + for ( inode = 1; inode < nbFaceNodes; inode += 2) + nodes.push_back(faceNodes[inode]); + if ( nbFaceNodes == 9 ) // bi-quadratic quad + nodes.push_back(faceNodes[8]); + } + // add new face based on volume nodes + if (aMesh->FindElement( nodes, SMDSAbs_Face, /*noMedium=*/false) ) { + nbExisted++; + continue; // face already exsist + } + AddElement(nodes, SMDSAbs_Face, ( !iQuad && nbFaceNodes/(iQuad+1) > 4 )); + nbCreated++; } } + return ( nbFree==(nbExisted+nbCreated) ); +} - // Create map of new nodes for modified elements +namespace +{ + inline const SMDS_MeshNode* getNodeWithSameID(SMESHDS_Mesh* mesh, const SMDS_MeshNode* node) + { + if ( const SMDS_MeshNode* n = mesh->FindNode( node->GetID() )) + return n; + return mesh->AddNodeWithID( node->X(),node->Y(),node->Z(), node->GetID() ); + } +} +//================================================================================ +/*! + * \brief Creates missing boundary elements + * \param elements - elements whose boundary is to be checked + * \param dimension - defines type of boundary elements to create + * \param group - a group to store created boundary elements in + * \param targetMesh - a mesh to store created boundary elements in + * \param toCopyElements - if true, the checked elements will be copied into the targetMesh + * \param toCopyExistingBoundary - if true, not only new but also pre-existing + * boundary elements will be copied into the targetMesh + * \param toAddExistingBondary - if true, not only new but also pre-existing + * boundary elements will be added into the new group + * \param aroundElements - if true, elements will be created on boundary of given + * elements else, on boundary of the whole mesh. + * \return nb of added boundary elements + */ +//================================================================================ - std::map< SMDS_MeshElement*, vector > anElemToNodes; +int SMESH_MeshEditor::MakeBoundaryMesh(const TIDSortedElemSet& elements, + Bnd_Dimension dimension, + SMESH_Group* group/*=0*/, + SMESH_Mesh* targetMesh/*=0*/, + bool toCopyElements/*=false*/, + bool toCopyExistingBoundary/*=false*/, + bool toAddExistingBondary/*= false*/, + bool aroundElements/*= false*/) +{ + SMDSAbs_ElementType missType = (dimension == BND_2DFROM3D) ? SMDSAbs_Face : SMDSAbs_Edge; + SMDSAbs_ElementType elemType = (dimension == BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume; + // hope that all elements are of the same type, do not check them all + if ( !elements.empty() && (*elements.begin())->GetType() != elemType ) + throw SALOME_Exception(LOCALIZED("wrong element type")); + + if ( !targetMesh ) + toCopyElements = toCopyExistingBoundary = false; + + SMESH_MeshEditor tgtEditor( targetMesh ? targetMesh : myMesh ); + SMESHDS_Mesh* aMesh = GetMeshDS(), *tgtMeshDS = tgtEditor.GetMeshDS(); + int nbAddedBnd = 0; + + // editor adding present bnd elements and optionally holding elements to add to the group + SMESH_MeshEditor* presentEditor; + SMESH_MeshEditor tgtEditor2( tgtEditor.GetMesh() ); + presentEditor = toAddExistingBondary ? &tgtEditor : &tgtEditor2; + + SMESH_MesherHelper helper( *myMesh ); + const TopAbs_ShapeEnum missShapeType = ( missType==SMDSAbs_Face ? TopAbs_FACE : TopAbs_EDGE ); + SMDS_VolumeTool vTool; + TIDSortedElemSet avoidSet; + const TIDSortedElemSet emptySet, *elemSet = aroundElements ? &elements : &emptySet; + int inode; + + typedef vector TConnectivity; + + SMDS_ElemIteratorPtr eIt; + if (elements.empty()) + eIt = aMesh->elementsIterator(elemType); + else + eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() )); - std::list< int >::const_iterator anElemIter; - for ( anElemIter = theListOfModifiedElems.begin(); - anElemIter != theListOfModifiedElems.end(); ++anElemIter ) + while (eIt->more()) { - int aCurr = *anElemIter; - SMDS_MeshElement* anElem = (SMDS_MeshElement*)aMeshDS->FindElement( aCurr ); - if ( !anElem ) - continue; - - vector aNodeArr( anElem->NbNodes() ); + const SMDS_MeshElement* elem = eIt->next(); + const int iQuad = elem->IsQuadratic(); + + // ------------------------------------------------------------------------------------ + // 1. For an elem, get present bnd elements and connectivities of missing bnd elements + // ------------------------------------------------------------------------------------ + vector presentBndElems; + vector missingBndElems; + TConnectivity nodes; + if ( vTool.Set(elem, /*ignoreCentralNodes=*/true) ) // elem is a volume -------------- + { + vTool.SetExternalNormal(); + const SMDS_MeshElement* otherVol = 0; + for ( int iface = 0, n = vTool.NbFaces(); iface < n; iface++ ) + { + if ( !vTool.IsFreeFace(iface, &otherVol) && + ( !aroundElements || elements.count( otherVol ))) + continue; + const int nbFaceNodes = vTool.NbFaceNodes(iface); + const SMDS_MeshNode** nn = vTool.GetFaceNodes(iface); + if ( missType == SMDSAbs_Edge ) // boundary edges + { + nodes.resize( 2+iQuad ); + for ( int i = 0; i < nbFaceNodes; i += 1+iQuad) + { + for ( int j = 0; j < nodes.size(); ++j ) + nodes[j] =nn[i+j]; + if ( const SMDS_MeshElement* edge = + aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/false)) + presentBndElems.push_back( edge ); + else + missingBndElems.push_back( nodes ); + } + } + else // boundary face + { + nodes.clear(); + for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad) + nodes.push_back( nn[inode] ); + if (iQuad) // add medium nodes + for ( inode = 1; inode < nbFaceNodes; inode += 2) + nodes.push_back( nn[inode] ); + int iCenter = vTool.GetCenterNodeIndex(iface); // for HEX27 + if ( iCenter > 0 ) + nodes.push_back( vTool.GetNodes()[ iCenter ] ); + + if (const SMDS_MeshElement * f = aMesh->FindElement( nodes, + SMDSAbs_Face, /*noMedium=*/false )) + presentBndElems.push_back( f ); + else + missingBndElems.push_back( nodes ); - SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); - int ind = 0; - while ( anIter->more() ) - { - SMDS_MeshNode* aCurrNode = (SMDS_MeshNode*)anIter->next(); - if ( aCurr && anOldNodeToNewNode.find( aCurrNode ) != anOldNodeToNewNode.end() ) + if ( targetMesh != myMesh ) + { + // add 1D elements on face boundary to be added to a new mesh + const SMDS_MeshElement* edge; + for ( inode = 0; inode < nbFaceNodes; inode += 1+iQuad) + { + if ( iQuad ) + edge = aMesh->FindEdge( nn[inode], nn[inode+1], nn[inode+2]); + else + edge = aMesh->FindEdge( nn[inode], nn[inode+1]); + if ( edge && avoidSet.insert( edge ).second ) + presentBndElems.push_back( edge ); + } + } + } + } + } + else // elem is a face ------------------------------------------ + { + avoidSet.clear(), avoidSet.insert( elem ); + int nbNodes = elem->NbCornerNodes(); + nodes.resize( 2 /*+ iQuad*/); + for ( int i = 0; i < nbNodes; i++ ) { - const SMDS_MeshNode* aNewNode = anOldNodeToNewNode[ aCurrNode ]; - aNodeArr[ ind++ ] = aNewNode; + nodes[0] = elem->GetNode(i); + nodes[1] = elem->GetNode((i+1)%nbNodes); + if ( FindFaceInSet( nodes[0], nodes[1], *elemSet, avoidSet)) + continue; // not free link + + //if ( iQuad ) + //nodes[2] = elem->GetNode( i + nbNodes ); + if ( const SMDS_MeshElement* edge = + aMesh->FindElement(nodes,SMDSAbs_Edge,/*noMedium=*/true)) + presentBndElems.push_back( edge ); + else + missingBndElems.push_back( nodes ); } - else - aNodeArr[ ind++ ] = aCurrNode; } - anElemToNodes[ anElem ] = aNodeArr; - } - // Change nodes of elements + // --------------------------------- + // 2. Add missing boundary elements + // --------------------------------- + if ( targetMesh != myMesh ) + // instead of making a map of nodes in this mesh and targetMesh, + // we create nodes with same IDs. + for ( int i = 0; i < missingBndElems.size(); ++i ) + { + TConnectivity& srcNodes = missingBndElems[i]; + TConnectivity nodes( srcNodes.size() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, srcNodes[inode] ); + if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes, + missType, + /*noMedium=*/false)) + continue; + tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4); + ++nbAddedBnd; + } + else + for ( int i = 0; i < missingBndElems.size(); ++i ) + { + TConnectivity& nodes = missingBndElems[i]; + if ( aroundElements && tgtEditor.GetMeshDS()->FindElement( nodes, + missType, + /*noMedium=*/false)) + continue; + SMDS_MeshElement* elem = + tgtEditor.AddElement(nodes, missType, !iQuad && nodes.size()/(iQuad+1)>4); + ++nbAddedBnd; - std::map< SMDS_MeshElement*, vector >::iterator - anElemToNodesIter = anElemToNodes.begin(); - for ( ; anElemToNodesIter != anElemToNodes.end(); ++anElemToNodesIter ) + // try to set a new element to a shape + if ( myMesh->HasShapeToMesh() ) + { + bool ok = true; + set< pair > mediumShapes; + const int nbN = nodes.size() / (iQuad+1 ); + for ( inode = 0; inode < nbN && ok; ++inode ) + { + pair i_stype = + helper.GetMediumPos( nodes[inode], nodes[(inode+1)%nbN]); + if (( ok = ( i_stype.first > 0 && i_stype.second >= TopAbs_FACE ))) + mediumShapes.insert( make_pair ( i_stype.second, i_stype.first )); + } + if ( ok && mediumShapes.size() > 1 ) + { + set< pair >::iterator stype_i = mediumShapes.begin(); + pair stype_i_0 = *stype_i; + for ( ++stype_i; stype_i != mediumShapes.end() && ok; ++stype_i ) + { + if (( ok = ( stype_i->first != stype_i_0.first ))) + ok = helper.IsSubShape( aMesh->IndexToShape( stype_i->second ), + aMesh->IndexToShape( stype_i_0.second )); + } + } + if ( ok && mediumShapes.begin()->first == missShapeType ) + aMesh->SetMeshElementOnShape( elem, mediumShapes.begin()->second ); + } + } + + // ---------------------------------- + // 3. Copy present boundary elements + // ---------------------------------- + if ( toCopyExistingBoundary ) + for ( int i = 0 ; i < presentBndElems.size(); ++i ) + { + const SMDS_MeshElement* e = presentBndElems[i]; + TConnectivity nodes( e->NbNodes() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, e->GetNode(inode) ); + presentEditor->AddElement(nodes, e->GetType(), e->IsPoly()); + } + else // store present elements to add them to a group + for ( int i = 0 ; i < presentBndElems.size(); ++i ) + { + presentEditor->myLastCreatedElems.Append(presentBndElems[i]); + } + + } // loop on given elements + + // --------------------------------------------- + // 4. Fill group with boundary elements + // --------------------------------------------- + if ( group ) { - const SMDS_MeshElement* anElem = anElemToNodesIter->first; - vector aNodeArr = anElemToNodesIter->second; - if ( anElem ) - aMeshDS->ChangeElementNodes( anElem, &aNodeArr[ 0 ], anElem->NbNodes() ); + if ( SMESHDS_Group* g = dynamic_cast( group->GetGroupDS() )) + for ( int i = 0; i < tgtEditor.myLastCreatedElems.Size(); ++i ) + g->SMDSGroup().Add( tgtEditor.myLastCreatedElems( i+1 )); } + tgtEditor.myLastCreatedElems.Clear(); + tgtEditor2.myLastCreatedElems.Clear(); - return true; + // ----------------------- + // 5. Copy given elements + // ----------------------- + if ( toCopyElements && targetMesh != myMesh ) + { + if (elements.empty()) + eIt = aMesh->elementsIterator(elemType); + else + eIt = SMDS_ElemIteratorPtr( new TSetIterator( elements.begin(), elements.end() )); + while (eIt->more()) + { + const SMDS_MeshElement* elem = eIt->next(); + TConnectivity nodes( elem->NbNodes() ); + for ( inode = 0; inode < nodes.size(); ++inode ) + nodes[inode] = getNodeWithSameID( tgtMeshDS, elem->GetNode(inode) ); + tgtEditor.AddElement(nodes, elemType, elem->IsPoly()); + + tgtEditor.myLastCreatedElems.Clear(); + } + } + return nbAddedBnd; } diff --git a/src/SMESH/SMESH_MeshEditor.hxx b/src/SMESH/SMESH_MeshEditor.hxx index ccab3fbb6..4bd455825 100644 --- a/src/SMESH/SMESH_MeshEditor.hxx +++ b/src/SMESH/SMESH_MeshEditor.hxx @@ -1,25 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses + // File : SMESH_MeshEditor.hxx // Created : Mon Apr 12 14:56:19 2004 // Author : Edward AGAPOV (eap) @@ -30,70 +30,72 @@ #include "SMESH_SMESH.hxx" -#include "SMESH_Mesh.hxx" -#include "SMESH_Controls.hxx" -#include "SMESH_SequenceOfNode.hxx" -#include "SMESH_SequenceOfElemPtr.hxx" -#include "TColStd_HSequenceOfReal.hxx" -#include "SMESH_MesherHelper.hxx" #include "SMDS_MeshElement.hxx" +#include "SMESH_Controls.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_TypeDefs.hxx" + +#include +#include #include #include #include - -typedef std::map > TElemOfElemListMap; -typedef std::map TNodeNodeMap; - - -typedef pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink; - -//======================================================================= -/*! - * \brief A sorted pair of nodes - */ -//======================================================================= - -struct SMESH_TLink: public NLink -{ - SMESH_TLink(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 ):NLink( n1, n2 ) - { if ( n1->GetID() < n2->GetID() ) std::swap( first, second ); } - SMESH_TLink(const NLink& link ):NLink( link ) - { if ( first->GetID() < second->GetID() ) std::swap( first, second ); } -}; - +#include class SMDS_MeshFace; class SMDS_MeshNode; class gp_Ax1; class gp_Vec; class gp_Pnt; +class SMESH_MesherHelper; -// ============================================================ + +//======================================================================= /*! - * \brief Set of elements sorted by ID, to be used to assure - * predictability of edition + * \brief Searcher for the node closest to point */ -// ============================================================ - -template < class TMeshElem = SMDS_MeshElement> -struct TIDCompare { - bool operator () (const TMeshElem* e1, const TMeshElem* e2) const - { return e1->GetID() < e2->GetID(); } +//======================================================================= +struct SMESH_NodeSearcher +{ + virtual const SMDS_MeshNode* FindClosestTo( const gp_Pnt& pnt ) = 0; + virtual void MoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt ) = 0; }; -typedef std::set< const SMDS_MeshElement*, TIDCompare< SMDS_MeshElement> > TIDSortedElemSet; -// ============================================================ +//======================================================================= /*! - * \brief Searcher for the node closest to point + * \brief Searcher for elements */ -// ============================================================ +//======================================================================= -struct SMESH_NodeSearcher +struct SMESH_ElementSearcher { - virtual const SMDS_MeshNode* FindClosestTo( const gp_Pnt& pnt ) = 0; + /*! + * \brief Find elements of given type where the given point is IN or ON. + * Returns nb of found elements and elements them-selves. + * + * 'ALL' type means elements of any type excluding nodes and 0D elements + */ + virtual int FindElementsByPoint(const gp_Pnt& point, + SMDSAbs_ElementType type, + std::vector< const SMDS_MeshElement* >& foundElems)=0; + /*! + * \brief Return an element most close to the given point + */ + virtual const SMDS_MeshElement* FindClosestTo( const gp_Pnt& point, + SMDSAbs_ElementType type) = 0; + /*! + * \brief Return elements possibly intersecting the line + */ + virtual void GetElementsNearLine( const gp_Ax1& line, + SMDSAbs_ElementType type, + std::vector< const SMDS_MeshElement* >& foundElems)=0; + /*! + * \brief Find out if the given point is out of closed 2D mesh. + */ + virtual TopAbs_State GetPointState(const gp_Pnt& point) = 0; + }; // ============================================================ @@ -102,8 +104,8 @@ struct SMESH_NodeSearcher */ // ============================================================ -class SMESH_EXPORT SMESH_MeshEditor { - +class SMESH_EXPORT SMESH_MeshEditor +{ public: SMESH_MeshEditor( SMESH_Mesh* theMesh ); @@ -114,16 +116,17 @@ public: SMDS_MeshElement* AddElement(const std::vector & nodes, const SMDSAbs_ElementType type, const bool isPoly, - const int ID = 0); + const int ID = -1, + const double ballDiameter=0.); /*! * \brief Add element */ SMDS_MeshElement* AddElement(const std::vector & nodeIDs, const SMDSAbs_ElementType type, const bool isPoly, - const int ID = 0); + const int ID = -1); - bool Remove (const std::list< int >& theElemIDs, const bool isNodes); + int Remove (const std::list< int >& theElemIDs, const bool isNodes); // Remove a node or an element. // Modify a compute state of sub-meshes which become empty @@ -148,6 +151,11 @@ public: bool Reorient (const SMDS_MeshElement * theElement); // Reverse theElement orientation + int Reorient2D (TIDSortedElemSet & theFaces, + const gp_Dir& theDirection, + const SMDS_MeshElement * theFace); + // Reverse theFaces whose orientation to be same as that of theFace + // oriented according to theDirection. Return nb of reoriented faces /*! * \brief Fuse neighbour triangles into quadrangles. @@ -155,7 +163,7 @@ public: * \param theCriterion - Is used to choose a neighbour to fuse with. * \param theMaxAngle - Is a max angle between element normals at which fusion * is still performed; theMaxAngle is mesured in radians. - * \retval bool - Success or not. + * \return bool - Success or not. */ bool TriToQuad (TIDSortedElemSet & theElems, SMESH::Controls::NumericalFunctorPtr theCriterion, @@ -165,7 +173,7 @@ public: * \brief Split quadrangles into triangles. * \param theElems - The faces to be splitted. * \param theCriterion - Is used to choose a diagonal for splitting. - * \retval bool - Success or not. + * \return bool - Success or not. */ bool QuadToTri (TIDSortedElemSet & theElems, SMESH::Controls::NumericalFunctorPtr theCriterion); @@ -174,7 +182,7 @@ public: * \brief Split quadrangles into triangles. * \param theElems - The faces to be splitted. * \param the13Diag - Is used to choose a diagonal for splitting. - * \retval bool - Success or not. + * \return bool - Success or not. */ bool QuadToTri (TIDSortedElemSet & theElems, const bool the13Diag); @@ -183,12 +191,19 @@ public: * \brief Find better diagonal for splitting. * \param theQuad - The face to find better splitting of. * \param theCriterion - Is used to choose a diagonal for splitting. - * \retval int - 1 for 1-3 diagonal, 2 for 2-4, -1 - for errors. + * \return int - 1 for 1-3 diagonal, 2 for 2-4, -1 - for errors. */ int BestSplit (const SMDS_MeshElement* theQuad, SMESH::Controls::NumericalFunctorPtr theCriterion); + enum SplitVolumToTetraFlags { HEXA_TO_5 = 1, HEXA_TO_6 = 2, HEXA_TO_24 = 3 };//!& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups); + Extrusion_Error ExtrusionAlongTrack (TIDSortedElemSet & theElements, + SMESH_Mesh* theTrackPattern, + const SMDS_MeshNode* theNodeStart, + const bool theHasAngles, + std::list& theAngles, + const bool theLinearVariation, const bool theHasRefPoint, const gp_Pnt& theRefPoint, const bool theMakeGroups); @@ -316,19 +341,32 @@ public: SMESH_Mesh* theTargetMesh=0); // Move or copy theElements applying theTrsf to their nodes + typedef std::list< std::list< const SMDS_MeshNode* > > TListOfListOfNodes; - void FindCoincidentNodes (std::set & theNodes, - const double theTolerance, - TListOfListOfNodes & theGroupsOfNodes); + void FindCoincidentNodes (TIDSortedNodeSet & theNodes, + const double theTolerance, + TListOfListOfNodes & theGroupsOfNodes); // Return list of group of nodes close to each other within theTolerance. // Search among theNodes or in the whole mesh if theNodes is empty. /*! - * \brief Return SMESH_NodeSearcher + * \brief Return SMESH_NodeSearcher. The caller is responsible for deleteing it */ SMESH_NodeSearcher* GetNodeSearcher(); + /*! + * \brief Return SMESH_ElementSearcher. The caller is responsible for deleting it + */ + SMESH_ElementSearcher* GetElementSearcher(); + SMESH_ElementSearcher* GetElementSearcher( SMDS_ElemIteratorPtr elemIt ); + /*! + * \brief Return true if the point is IN or ON of the element + */ + static bool IsOut( const SMDS_MeshElement* element, const gp_Pnt& point, double tol ); + + static double GetDistance( const SMDS_MeshFace* face, const gp_Pnt& point ); + int SimplifyFace (const std::vector faceNodes, std::vector& poly_nodes, std::vector& quantities) const; @@ -342,7 +380,7 @@ public: typedef std::list< std::list< int > > TListOfListOfElementsID; void FindEqualElements(std::set & theElements, - TListOfListOfElementsID & theGroupsOfElementsID); + TListOfListOfElementsID & theGroupsOfElementsID); // Return list of group of elements build on the same nodes. // Search among theElements or in the whole mesh if theElements is empty. @@ -439,24 +477,20 @@ public: // theBetweenNode1 - theBetweenNode2, between theBetweenNode1 and theBetweenNode2. void ConvertToQuadratic(const bool theForce3d); - //converts all mesh to quadratic one, deletes old elements, replacing - //them with quadratic ones with the same id. + void ConvertToQuadratic(const bool theForce3d, TIDSortedElemSet& theElements); + // Converts all mesh to quadratic one, deletes old elements, replacing + // them with quadratic ones with the same id. + // If theForce3d = 1; this results in the medium node lying at the + // middle of the line segments connecting start and end node of a mesh + // element + // If theForce3d = 0; this results in the medium node lying at the + // geometrical edge from which the mesh element is built bool ConvertFromQuadratic(); - //converts all mesh from quadratic to ordinary ones, deletes old quadratic elements, replacing - //them with ordinary mesh elements with the same id. - - -// static int SortQuadNodes (const SMDS_Mesh * theMesh, -// int theNodeIds[] ); -// // Set 4 nodes of a quadrangle face in a good order. -// // Swap 1<->2 or 2<->3 nodes and correspondingly return -// // 1 or 2 else 0. -// -// static bool SortHexaNodes (const SMDS_Mesh * theMesh, -// int theNodeIds[] ); -// // Set 8 nodes of a hexahedron in a good order. -// // Return success status + void ConvertFromQuadratic(TIDSortedElemSet& theElements); + // Converts all mesh from quadratic to ordinary ones, deletes old quadratic elements, replacing + // them with ordinary mesh elements with the same id. + // Returns true in case of success, false otherwise. static void AddToSameGroups (const SMDS_MeshElement* elemToAdd, const SMDS_MeshElement* elemInGroups, @@ -472,6 +506,11 @@ public: SMESHDS_Mesh * aMesh); // replace elemToRm by elemToAdd in the all groups + static void ReplaceElemInGroups (const SMDS_MeshElement* elemToRm, + const std::vector& elemToAdd, + SMESHDS_Mesh * aMesh); + // replace elemToRm by elemToAdd in the all groups + /*! * \brief Return nodes linked to the given one in elements of the type */ @@ -479,14 +518,16 @@ public: TIDSortedElemSet & linkedNodes, SMDSAbs_ElementType type = SMDSAbs_All ); - static const SMDS_MeshElement* - FindFaceInSet(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const TIDSortedElemSet& elemSet, - const TIDSortedElemSet& avoidSet); + static const SMDS_MeshElement* FindFaceInSet(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const TIDSortedElemSet& elemSet, + const TIDSortedElemSet& avoidSet, + int* i1=0, + int* i2=0); // Return a face having linked nodes n1 and n2 and which is // - not in avoidSet, // - in elemSet provided that !elemSet.empty() + // i1 and i2 optionally returns indices of n1 and n2 /*! * \brief Find corresponding nodes in two sets of faces @@ -497,21 +538,21 @@ public: * \param theSecondNode1 - a boundary node of set 1 linked with theFirstNode1 * \param theSecondNode2 - a node of set 2 corresponding to theSecondNode1 * \param nReplaceMap - output map of corresponding nodes - * \retval Sew_Error - is a success or not + * \return Sew_Error - is a success or not */ static Sew_Error FindMatchingNodes(std::set& theSide1, std::set& theSide2, - const SMDS_MeshNode* theFirstNode1, - const SMDS_MeshNode* theFirstNode2, - const SMDS_MeshNode* theSecondNode1, - const SMDS_MeshNode* theSecondNode2, - TNodeNodeMap & nReplaceMap); + const SMDS_MeshNode* theFirstNode1, + const SMDS_MeshNode* theFirstNode2, + const SMDS_MeshNode* theSecondNode1, + const SMDS_MeshNode* theSecondNode2, + TNodeNodeMap & theNodeReplaceMap); /*! * \brief Returns true if given node is medium * \param n - node to check * \param typeToCheck - type of elements containing the node to ask about node status - * \retval bool - check result + * \return bool - check result */ static bool IsMedium(const SMDS_MeshNode* node, const SMDSAbs_ElementType typeToCheck = SMDSAbs_All); @@ -527,15 +568,49 @@ public: const SMESH_SequenceOfElemPtr& GetLastCreatedNodes() const { return myLastCreatedNodes; } const SMESH_SequenceOfElemPtr& GetLastCreatedElems() const { return myLastCreatedElems; } - + bool DoubleNodes( const std::list< int >& theListOfNodes, const std::list< int >& theListOfModifiedElems ); + + bool DoubleNodes( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TIDSortedElemSet& theAffectedElems ); -private: + bool DoubleNodesInRegion( const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + const TopoDS_Shape& theShape ); + + double OrientedAngle(const gp_Pnt& p0, const gp_Pnt& p1, const gp_Pnt& g1, const gp_Pnt& g2); + + bool DoubleNodesOnGroupBoundaries( const std::vector& theElems, + bool createJointElems); + + bool CreateFlatElementsOnFacesGroups( const std::vector& theElems ); + + /*! + * \brief Generated skin mesh (containing 2D cells) from 3D mesh + * The created 2D mesh elements based on nodes of free faces of boundary volumes + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ + bool Make2DMeshFrom3D(); + + enum Bnd_Dimension { BND_2DFROM3D, BND_1DFROM3D, BND_1DFROM2D }; + + int MakeBoundaryMesh(const TIDSortedElemSet& elements, + Bnd_Dimension dimension, + SMESH_Group* group = 0, + SMESH_Mesh* targetMesh = 0, + bool toCopyElements = false, + bool toCopyExistingBondary = false, + bool toAddExistingBondary = false, + bool aroundElements = false); + + + private: /*! * \brief Convert elements contained in a submesh to quadratic - * \retval int - nb of checked elements + * \return int - nb of checked elements */ int convertElemToQuadratic(SMESHDS_SubMesh * theSm, SMESH_MesherHelper& theHelper, @@ -543,7 +618,7 @@ private: /*! * \brief Convert quadratic elements to linear ones and remove quadratic nodes - * \retval int - nb of checked elements + * \return nb of checked elements */ int removeQuadElem( SMESHDS_SubMesh * theSm, SMDS_ElemIteratorPtr theItr, @@ -567,11 +642,11 @@ private: /*! * \brief Create elements by sweeping an element - * \param elem - element to sweep - * \param newNodesItVec - nodes generated from each node of the element - * \param newElems - generated elements - * \param nbSteps - number of sweeping steps - * \param srcElements - to append elem for each generated element + * \param elem - element to sweep + * \param newNodesItVec - nodes generated from each node of the element + * \param newElems - generated elements + * \param nbSteps - number of sweeping steps + * \param srcElements - to append elem for each generated element */ void sweepElement(const SMDS_MeshElement* elem, const std::vector & newNodesItVec, @@ -581,12 +656,12 @@ private: /*! * \brief Create 1D and 2D elements around swept elements - * \param mapNewNodes - source nodes and ones generated from them - * \param newElemsMap - source elements and ones generated from them - * \param elemNewNodesMap - nodes generated from each node of each element - * \param elemSet - all swept elements - * \param nbSteps - number of sweeping steps - * \param srcElements - to append elem for each generated element + * \param mapNewNodes - source nodes and ones generated from them + * \param newElemsMap - source elements and ones generated from them + * \param elemNewNodesMap - nodes generated from each node of each element + * \param elemSet - all swept elements + * \param nbSteps - number of sweeping steps + * \param srcElements - to append elem for each generated element */ void makeWalls (TNodeOfNodeListMap & mapNewNodes, TElemOfElemListMap & newElemsMap, @@ -594,6 +669,44 @@ private: TIDSortedElemSet& elemSet, const int nbSteps, SMESH_SequenceOfElemPtr& srcElements); + + struct SMESH_MeshEditor_PathPoint + { + gp_Pnt myPnt; + gp_Dir myTgt; + double myAngle, myPrm; + + SMESH_MeshEditor_PathPoint(): myPnt(99., 99., 99.), myTgt(1.,0.,0.), myAngle(0), myPrm(0) {} + void SetPnt (const gp_Pnt& aP3D) { myPnt =aP3D; } + void SetTangent (const gp_Dir& aTgt) { myTgt =aTgt; } + void SetAngle (const double& aBeta) { myAngle=aBeta; } + void SetParameter(const double& aPrm) { myPrm =aPrm; } + const gp_Pnt& Pnt ()const { return myPnt; } + const gp_Dir& Tangent ()const { return myTgt; } + double Angle ()const { return myAngle; } + double Parameter ()const { return myPrm; } + }; + Extrusion_Error MakeEdgePathPoints(std::list& aPrms, + const TopoDS_Edge& aTrackEdge, + bool aFirstIsStart, + std::list& aLPP); + Extrusion_Error MakeExtrElements(TIDSortedElemSet& theElements, + std::list& theFullList, + const bool theHasAngles, + std::list& theAngles, + const bool theLinearVariation, + const bool theHasRefPoint, + const gp_Pnt& theRefPoint, + const bool theMakeGroups); + void LinearAngleVariation(const int NbSteps, + list& theAngles); + + bool doubleNodes( SMESHDS_Mesh* theMeshDS, + const TIDSortedElemSet& theElems, + const TIDSortedElemSet& theNodesNot, + std::map< const SMDS_MeshNode*, const SMDS_MeshNode* >& theNodeNodeMap, + const bool theIsDoubleElem ); + private: SMESH_Mesh * myMesh; diff --git a/src/SMESH/SMESH_MesherHelper.cxx b/src/SMESH/SMESH_MesherHelper.cxx index 292c29ecb..efaf7a853 100644 --- a/src/SMESH/SMESH_MesherHelper.cxx +++ b/src/SMESH/SMESH_MesherHelper.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File: SMESH_MesherHelper.cxx // Created: 15.02.06 15:22:41 // Author: Sergey KUUL @@ -27,30 +28,49 @@ #include "SMDS_FacePosition.hxx" #include "SMDS_EdgePosition.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMDS_VolumeTool.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_ProxyMesh.hxx" #include #include -#include #include +#include #include +#include +#include #include +#include #include #include #include #include #include +#include #include #include +#include #include +#include #include #include #include +#include + +using namespace std; + #define RETURN_BAD_RESULT(msg) { MESSAGE(msg); return false; } +namespace { + + gp_XYZ XYZ(const SMDS_MeshNode* n) { return gp_XYZ(n->X(), n->Y(), n->Z()); } + + enum { U_periodic = 1, V_periodic = 2 }; +} + //================================================================================ /*! * \brief Constructor @@ -58,94 +78,140 @@ //================================================================================ SMESH_MesherHelper::SMESH_MesherHelper(SMESH_Mesh& theMesh) - : myMesh(&theMesh), myShapeID(-1), myCreateQuadratic(false) + : myParIndex(0), myMesh(&theMesh), myShapeID(0), myCreateQuadratic(false), + myFixNodeParameters(false) { + myPar1[0] = myPar2[0] = myPar1[1] = myPar2[1] = 0; mySetElemOnShape = ( ! myMesh->HasShapeToMesh() ); } //======================================================================= -//function : CheckShape +//function : ~SMESH_MesherHelper //purpose : //======================================================================= +SMESH_MesherHelper::~SMESH_MesherHelper() +{ + { + TID2ProjectorOnSurf::iterator i_proj = myFace2Projector.begin(); + for ( ; i_proj != myFace2Projector.end(); ++i_proj ) + delete i_proj->second; + } + { + TID2ProjectorOnCurve::iterator i_proj = myEdge2Projector.begin(); + for ( ; i_proj != myEdge2Projector.end(); ++i_proj ) + delete i_proj->second; + } +} + +//======================================================================= +//function : IsQuadraticSubMesh +//purpose : Check submesh for given shape: if all elements on this shape +// are quadratic, quadratic elements will be created. +// Also fill myTLinkNodeMap +//======================================================================= + bool SMESH_MesherHelper::IsQuadraticSubMesh(const TopoDS_Shape& aSh) { SMESHDS_Mesh* meshDS = GetMeshDS(); // we can create quadratic elements only if all elements - // created on subshapes of given shape are quadratic - // also we have to fill myNLinkNodeMap + // created on sub-shapes of given shape are quadratic + // also we have to fill myTLinkNodeMap myCreateQuadratic = true; mySeamShapeIds.clear(); myDegenShapeIds.clear(); TopAbs_ShapeEnum subType( aSh.ShapeType()==TopAbs_FACE ? TopAbs_EDGE : TopAbs_FACE ); + if ( aSh.ShapeType()==TopAbs_COMPOUND ) + { + TopoDS_Iterator subIt( aSh ); + if ( subIt.More() ) + subType = ( subIt.Value().ShapeType()==TopAbs_FACE ) ? TopAbs_EDGE : TopAbs_FACE; + } SMDSAbs_ElementType elemType( subType==TopAbs_FACE ? SMDSAbs_Face : SMDSAbs_Edge ); - TopExp_Explorer exp( aSh, subType ); - for (; exp.More() && myCreateQuadratic; exp.Next()) { - if ( SMESHDS_SubMesh * subMesh = meshDS->MeshElements( exp.Current() )) { - if ( SMDS_ElemIteratorPtr it = subMesh->GetElements() ) { - while(it->more()) { - const SMDS_MeshElement* e = it->next(); - if ( e->GetType() != elemType || !e->IsQuadratic() ) { - myCreateQuadratic = false; - break; - } - else { - // fill NLinkNodeMap - switch ( e->NbNodes() ) { - case 3: - AddNLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break; - case 6: - AddNLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3)); - AddNLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4)); - AddNLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break; - case 8: - AddNLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4)); - AddNLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5)); - AddNLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6)); - AddNLinkNode(e->GetNode(3),e->GetNode(0),e->GetNode(7)); - break; - default: + + int nbOldLinks = myTLinkNodeMap.size(); + + if ( !myMesh->HasShapeToMesh() ) + { + if (( myCreateQuadratic = myMesh->NbFaces( ORDER_QUADRATIC ))) + { + SMDS_FaceIteratorPtr fIt = meshDS->facesIterator(); + while ( fIt->more() ) + AddTLinks( static_cast< const SMDS_MeshFace* >( fIt->next() )); + } + } + else + { + TopExp_Explorer exp( aSh, subType ); + TopTools_MapOfShape checkedSubShapes; + for (; exp.More() && myCreateQuadratic; exp.Next()) { + if ( !checkedSubShapes.Add( exp.Current() )) + continue; // needed if aSh is compound of solids + if ( SMESHDS_SubMesh * subMesh = meshDS->MeshElements( exp.Current() )) { + if ( SMDS_ElemIteratorPtr it = subMesh->GetElements() ) { + while(it->more()) { + const SMDS_MeshElement* e = it->next(); + if ( e->GetType() != elemType || !e->IsQuadratic() ) { myCreateQuadratic = false; break; } + else { + // fill TLinkNodeMap + switch ( e->NbNodes() ) { + case 3: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(2)); break; + case 6: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(3)); + AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(4)); + AddTLinkNode(e->GetNode(2),e->GetNode(0),e->GetNode(5)); break; + case 8: + AddTLinkNode(e->GetNode(0),e->GetNode(1),e->GetNode(4)); + AddTLinkNode(e->GetNode(1),e->GetNode(2),e->GetNode(5)); + AddTLinkNode(e->GetNode(2),e->GetNode(3),e->GetNode(6)); + AddTLinkNode(e->GetNode(3),e->GetNode(0),e->GetNode(7)); + break; + default: + myCreateQuadratic = false; + break; + } + } } } } } } + if ( nbOldLinks == myTLinkNodeMap.size() ) + myCreateQuadratic = false; + if(!myCreateQuadratic) { - myNLinkNodeMap.clear(); + myTLinkNodeMap.clear(); } SetSubShape( aSh ); return myCreateQuadratic; } -//================================================================================ -/*! - * \brief Set geomerty to make elements on - * \param aSh - geomertic shape - */ -//================================================================================ +//======================================================================= +//function : SetSubShape +//purpose : Set geomerty to make elements on +//======================================================================= void SMESH_MesherHelper::SetSubShape(const int aShID) { if ( aShID == myShapeID ) return; - if ( aShID > 1 ) + if ( aShID > 0 ) SetSubShape( GetMeshDS()->IndexToShape( aShID )); else SetSubShape( TopoDS_Shape() ); } -//================================================================================ -/*! - * \brief Set geomerty to make elements on - * \param aSh - geomertic shape - */ -//================================================================================ +//======================================================================= +//function : SetSubShape +//purpose : Set geomerty to create elements on +//======================================================================= void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) { @@ -157,39 +223,45 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) myDegenShapeIds.clear(); if ( myShape.IsNull() ) { - myShapeID = -1; + myShapeID = 0; return; } SMESHDS_Mesh* meshDS = GetMeshDS(); myShapeID = meshDS->ShapeToIndex(aSh); + myParIndex = 0; // treatment of periodic faces for ( TopExp_Explorer eF( aSh, TopAbs_FACE ); eF.More(); eF.Next() ) { const TopoDS_Face& face = TopoDS::Face( eF.Current() ); - BRepAdaptor_Surface surface( face ); - if ( surface.IsUPeriodic() || surface.IsVPeriodic() ) + TopLoc_Location loc; + Handle(Geom_Surface) surface = BRep_Tool::Surface( face, loc ); + + if ( surface->IsUPeriodic() || surface->IsVPeriodic() || + surface->IsUClosed() || surface->IsVClosed() ) { + //while ( surface->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface ))) + //surface = Handle(Geom_RectangularTrimmedSurface)::DownCast( surface )->BasisSurface(); + GeomAdaptor_Surface surf( surface ); + for (TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next()) { // look for a seam edge const TopoDS_Edge& edge = TopoDS::Edge( exp.Current() ); if ( BRep_Tool::IsClosed( edge, face )) { // initialize myPar1, myPar2 and myParIndex - if ( mySeamShapeIds.empty() ) { - gp_Pnt2d uv1, uv2; - BRep_Tool::UVPoints( edge, face, uv1, uv2 ); - if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) )) - { - myParIndex = 1; // U periodic - myPar1 = surface.FirstUParameter(); - myPar2 = surface.LastUParameter(); - } - else { - myParIndex = 2; // V periodic - myPar1 = surface.FirstVParameter(); - myPar2 = surface.LastVParameter(); - } + gp_Pnt2d uv1, uv2; + BRep_Tool::UVPoints( edge, face, uv1, uv2 ); + if ( Abs( uv1.Coord(1) - uv2.Coord(1) ) < Abs( uv1.Coord(2) - uv2.Coord(2) )) + { + myParIndex |= U_periodic; + myPar1[0] = surf.FirstUParameter(); + myPar2[0] = surf.LastUParameter(); + } + else { + myParIndex |= V_periodic; + myPar1[1] = surf.FirstVParameter(); + myPar2[1] = surf.LastVParameter(); } // store seam shape indices, negative if shape encounters twice int edgeID = meshDS->ShapeToIndex( edge ); @@ -211,13 +283,13 @@ void SMESH_MesherHelper::SetSubShape(const TopoDS_Shape& aSh) } } -//================================================================================ - /*! - * \brief Check if inFaceNode argument is necessary for call GetNodeUV(F,..) - * \param F - the face - * \retval bool - return true if the face is periodic - */ -//================================================================================ +//======================================================================= +//function : GetNodeUVneedInFaceNode +//purpose : Check if inFaceNode argument is necessary for call GetNodeUV(F,..) +// Return true if the face is periodic. +// If F is Null, answer about sub-shape set through IsQuadraticSubMesh() or +// * SetSubShape() +//======================================================================= bool SMESH_MesherHelper::GetNodeUVneedInFaceNode(const TopoDS_Face& F) const { @@ -226,7 +298,8 @@ bool SMESH_MesherHelper::GetNodeUVneedInFaceNode(const TopoDS_Face& F) const if ( !F.IsNull() && !myShape.IsNull() && myShape.IsSame( F )) return !mySeamShapeIds.empty(); - Handle(Geom_Surface) aSurface = BRep_Tool::Surface( F ); + TopLoc_Location loc; + Handle(Geom_Surface) aSurface = BRep_Tool::Surface( F,loc ); if ( !aSurface.IsNull() ) return ( aSurface->IsUPeriodic() || aSurface->IsVPeriodic() ); @@ -239,125 +312,273 @@ bool SMESH_MesherHelper::GetNodeUVneedInFaceNode(const TopoDS_Face& F) const //======================================================================= bool SMESH_MesherHelper::IsMedium(const SMDS_MeshNode* node, - const SMDSAbs_ElementType typeToCheck) + const SMDSAbs_ElementType typeToCheck) { return SMESH_MeshEditor::IsMedium( node, typeToCheck ); } //======================================================================= -//function : AddNLinkNode -//purpose : +//function : GetSubShapeByNode +//purpose : Return support shape of a node +//======================================================================= + +TopoDS_Shape SMESH_MesherHelper::GetSubShapeByNode(const SMDS_MeshNode* node, + const SMESHDS_Mesh* meshDS) +{ + int shapeID = node->getshapeId(); + if ( 0 < shapeID && shapeID <= meshDS->MaxShapeIndex() ) + return meshDS->IndexToShape( shapeID ); + else + return TopoDS_Shape(); +} + + +//======================================================================= +//function : AddTLinkNode +//purpose : add a link in my data structure //======================================================================= + +void SMESH_MesherHelper::AddTLinkNode(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* n12) +{ + // add new record to map + SMESH_TLink link( n1, n2 ); + myTLinkNodeMap.insert( make_pair(link,n12)); +} + +//================================================================================ /*! - * Auxilary function for filling myNLinkNodeMap + * \brief Add quadratic links of edge to own data structure */ -void SMESH_MesherHelper::AddNLinkNode(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const SMDS_MeshNode* n12) +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshEdge* edge) { - NLink link( n1, n2 ); - if ( n1 > n2 ) link = NLink( n2, n1 ); - // add new record to map - myNLinkNodeMap.insert( make_pair(link,n12)); + if ( edge->IsQuadratic() ) + AddTLinkNode(edge->GetNode(0), edge->GetNode(1), edge->GetNode(2)); } -//======================================================================= +//================================================================================ +/*! + * \brief Add quadratic links of face to own data structure + */ +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshFace* f) +{ + if ( !f->IsPoly() ) + switch ( f->NbNodes() ) { + case 6: + AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(3)); + AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(4)); + AddTLinkNode(f->GetNode(2),f->GetNode(0),f->GetNode(5)); break; + case 8: + AddTLinkNode(f->GetNode(0),f->GetNode(1),f->GetNode(4)); + AddTLinkNode(f->GetNode(1),f->GetNode(2),f->GetNode(5)); + AddTLinkNode(f->GetNode(2),f->GetNode(3),f->GetNode(6)); + AddTLinkNode(f->GetNode(3),f->GetNode(0),f->GetNode(7)); + default:; + } +} + +//================================================================================ +/*! + * \brief Add quadratic links of volume to own data structure + */ +//================================================================================ + +void SMESH_MesherHelper::AddTLinks(const SMDS_MeshVolume* volume) +{ + if ( volume->IsQuadratic() ) + { + SMDS_VolumeTool vTool( volume ); + const SMDS_MeshNode** nodes = vTool.GetNodes(); + set addedLinks; + for ( int iF = 1; iF < vTool.NbFaces(); ++iF ) + { + const int nbN = vTool.NbFaceNodes( iF ); + const int* iNodes = vTool.GetFaceNodesIndices( iF ); + for ( int i = 0; i < nbN; ) + { + int iN1 = iNodes[i++]; + int iN12 = iNodes[i++]; + int iN2 = iNodes[i++]; + if ( iN1 > iN2 ) std::swap( iN1, iN2 ); + int linkID = iN1 * vTool.NbNodes() + iN2; + pair< set::iterator, bool > it_isNew = addedLinks.insert( linkID ); + if ( it_isNew.second ) + AddTLinkNode( nodes[iN1], nodes[iN2], nodes[iN12] ); + else + addedLinks.erase( it_isNew.first ); // each link encounters only twice + } + } + } +} + +//================================================================================ +/*! + * \brief Return true if position of nodes on the shape hasn't yet been checked or + * the positions proved to be invalid + */ +//================================================================================ + +bool SMESH_MesherHelper::toCheckPosOnShape(int shapeID ) const +{ + map< int,bool >::const_iterator id_ok = myNodePosShapesValidity.find( shapeID ); + return ( id_ok == myNodePosShapesValidity.end() || !id_ok->second ); +} + +//================================================================================ /*! - * \brief Select UV on either of 2 pcurves of a seam edge, closest to the given UV - * \param uv1 - UV on the seam - * \param uv2 - UV within a face - * \retval gp_Pnt2d - selected UV + * \brief Set validity of positions of nodes on the shape. + * Once set, validity is not changed */ +//================================================================================ + +void SMESH_MesherHelper::setPosOnShapeValidity(int shapeID, bool ok ) const +{ + ((SMESH_MesherHelper*)this)->myNodePosShapesValidity.insert( make_pair( shapeID, ok)); +} + +//======================================================================= +//function : ToFixNodeParameters +//purpose : Enables fixing node parameters on EDGEs and FACEs in +// GetNodeU(...,check=true), GetNodeUV(...,check=true), CheckNodeUV() and +// CheckNodeU() in case if a node lies on a shape set via SetSubShape(). +// Default is False +//======================================================================= + +void SMESH_MesherHelper::ToFixNodeParameters(bool toFix) +{ + myFixNodeParameters = toFix; +} + + +//======================================================================= +//function : GetUVOnSeam +//purpose : Select UV on either of 2 pcurves of a seam edge, closest to the given UV //======================================================================= gp_Pnt2d SMESH_MesherHelper::GetUVOnSeam( const gp_Pnt2d& uv1, const gp_Pnt2d& uv2 ) const { - double p1 = uv1.Coord( myParIndex ); - double p2 = uv2.Coord( myParIndex ); - double p3 = ( Abs( p1 - myPar1 ) < Abs( p1 - myPar2 )) ? myPar2 : myPar1; - if ( Abs( p2 - p1 ) > Abs( p2 - p3 )) - p1 = p3; gp_Pnt2d result = uv1; - result.SetCoord( myParIndex, p1 ); + for ( int i = U_periodic; i <= V_periodic ; ++i ) + { + if ( myParIndex & i ) + { + double p1 = uv1.Coord( i ); + double dp1 = Abs( p1-myPar1[i-1]), dp2 = Abs( p1-myPar2[i-1]); + if ( myParIndex == i || + dp1 < ( myPar2[i-1] - myPar2[i-1] ) / 100. || + dp2 < ( myPar2[i-1] - myPar2[i-1] ) / 100. ) + { + double p2 = uv2.Coord( i ); + double p1Alt = ( dp1 < dp2 ) ? myPar2[i-1] : myPar1[i-1]; + if ( Abs( p2 - p1 ) > Abs( p2 - p1Alt )) + result.SetCoord( i, p1Alt ); + } + } + } return result; } //======================================================================= -/*! - * \brief Return node UV on face - * \param F - the face - * \param n - the node - * \param n2 - a node of element being created located inside a face - * \retval gp_XY - resulting UV - * - * Auxilary function called form GetMediumNode() - */ +//function : GetNodeUV +//purpose : Return node UV on face //======================================================================= gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, const SMDS_MeshNode* n, - const SMDS_MeshNode* n2) const + const SMDS_MeshNode* n2, + bool* check) const { - gp_Pnt2d uv( 1e100, 1e100 ); + gp_Pnt2d uv( Precision::Infinite(), Precision::Infinite() ); const SMDS_PositionPtr Pos = n->GetPosition(); + bool uvOK = false; if(Pos->GetTypeOfPosition()==SMDS_TOP_FACE) { // node has position on face const SMDS_FacePosition* fpos = - static_cast(n->GetPosition().get()); - uv = gp_Pnt2d(fpos->GetUParameter(),fpos->GetVParameter()); + static_cast(n->GetPosition()); + uv.SetCoord(fpos->GetUParameter(),fpos->GetVParameter()); + if ( check ) + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F )); } else if(Pos->GetTypeOfPosition()==SMDS_TOP_EDGE) { // node has position on edge => it is needed to find // corresponding edge from face, get pcurve for this - // edge and recieve value from this pcurve + // edge and retrieve value from this pcurve const SMDS_EdgePosition* epos = - static_cast(n->GetPosition().get()); - SMESHDS_Mesh* meshDS = GetMeshDS(); - int edgeID = Pos->GetShapeId(); - TopoDS_Edge E = TopoDS::Edge(meshDS->IndexToShape(edgeID)); - double f, l; + static_cast(n->GetPosition()); + int edgeID = n->getshapeId(); + TopoDS_Edge E = TopoDS::Edge(GetMeshDS()->IndexToShape(edgeID)); + double f, l, u = epos->GetUParameter(); Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l); - uv = C2d->Value( epos->GetUParameter() ); + bool validU = ( f < u && u < l ); + if ( validU ) + uv = C2d->Value( u ); + else + uv.SetCoord( Precision::Infinite(),0.); + if ( check || !validU ) + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F ),/*force=*/ !validU ); + // for a node on a seam edge select one of UVs on 2 pcurves if ( n2 && IsSeamShape( edgeID ) ) - uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0 )); + { + uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0, check )); + } + else + { // adjust uv to period + TopLoc_Location loc; + Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc); + Standard_Boolean isUPeriodic = S->IsUPeriodic(); + Standard_Boolean isVPeriodic = S->IsVPeriodic(); + if ( isUPeriodic || isVPeriodic ) { + Standard_Real UF,UL,VF,VL; + S->Bounds(UF,UL,VF,VL); + if(isUPeriodic) + uv.SetX( uv.X() + ShapeAnalysis::AdjustToPeriod(uv.X(),UF,UL)); + if(isVPeriodic) + uv.SetY( uv.Y() + ShapeAnalysis::AdjustToPeriod(uv.Y(),VF,VL)); + } + } } else if(Pos->GetTypeOfPosition()==SMDS_TOP_VERTEX) { - if ( int vertexID = n->GetPosition()->GetShapeId() ) { - bool ok = true; + if ( int vertexID = n->getshapeId() ) { const TopoDS_Vertex& V = TopoDS::Vertex(GetMeshDS()->IndexToShape(vertexID)); try { uv = BRep_Tool::Parameters( V, F ); + uvOK = true; } catch (Standard_Failure& exc) { - ok = false; } - if ( !ok ) { - for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !ok && vert.More(); vert.Next() ) - ok = ( V == vert.Current() ); - if ( !ok ) { + if ( !uvOK ) { + for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !uvOK && vert.More(); vert.Next() ) + uvOK = ( V == vert.Current() ); + if ( !uvOK ) { #ifdef _DEBUG_ MESSAGE ( "SMESH_MesherHelper::GetNodeUV(); Vertex " << vertexID << " not in face " << GetMeshDS()->ShapeToIndex( F ) ); #endif // get UV of a vertex closest to the node double dist = 1e100; - gp_Pnt pn ( n->X(),n->Y(),n->Z() ); - for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !ok && vert.More(); vert.Next() ) { + gp_Pnt pn = XYZ( n ); + for ( TopExp_Explorer vert(F,TopAbs_VERTEX); !uvOK && vert.More(); vert.Next() ) { TopoDS_Vertex curV = TopoDS::Vertex( vert.Current() ); gp_Pnt p = BRep_Tool::Pnt( curV ); double curDist = p.SquareDistance( pn ); if ( curDist < dist ) { dist = curDist; uv = BRep_Tool::Parameters( curV, F ); - if ( dist < DBL_MIN ) break; + uvOK = ( dist < DBL_MIN ); } } } else { + uvOK = false; TopTools_ListIteratorOfListOfShape it( myMesh->GetAncestors( V )); for ( ; it.More(); it.Next() ) { if ( it.Value().ShapeType() == TopAbs_EDGE ) { @@ -367,6 +588,7 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, if ( !C2d.IsNull() ) { double u = ( V == TopExp::FirstVertex( edge ) ) ? f : l; uv = C2d->Value( u ); + uvOK = true; break; } } @@ -377,269 +599,694 @@ gp_XY SMESH_MesherHelper::GetNodeUV(const TopoDS_Face& F, uv = GetUVOnSeam( uv, GetNodeUV( F, n2, 0 )); } } - return uv.XY(); -} + else + { + uvOK = CheckNodeUV( F, n, uv.ChangeCoord(), 10*MaxTolerance( F )); + } -//======================================================================= -/*! - * \brief Return node U on edge - * \param E - the Edge - * \param n - the node - * \retval double - resulting U - * - * Auxilary function called form GetMediumNode() - */ -//======================================================================= + if ( check ) + *check = uvOK; -double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& E, - const SMDS_MeshNode* n) -{ - double param = 0; - const SMDS_PositionPtr Pos = n->GetPosition(); - if(Pos->GetTypeOfPosition()==SMDS_TOP_EDGE) { - const SMDS_EdgePosition* epos = - static_cast(n->GetPosition().get()); - param = epos->GetUParameter(); - } - else if(Pos->GetTypeOfPosition()==SMDS_TOP_VERTEX) { - SMESHDS_Mesh * meshDS = GetMeshDS(); - int vertexID = n->GetPosition()->GetShapeId(); - const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID)); - param = BRep_Tool::Parameter( V, E ); - } - return param; + return uv.XY(); } //======================================================================= -//function : GetMediumNode -//purpose : +//function : CheckNodeUV +//purpose : Check and fix node UV on a face //======================================================================= -/*! - * Special function for search or creation medium node - */ -const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - bool force3d) -{ - TopAbs_ShapeEnum shapeType = myShape.IsNull() ? TopAbs_SHAPE : myShape.ShapeType(); - NLink link(( n1 < n2 ? n1 : n2 ), ( n1 < n2 ? n2 : n1 )); - ItNLinkNode itLN = myNLinkNodeMap.find( link ); - if ( itLN != myNLinkNodeMap.end() ) { - return (*itLN).second; - } - else { - // create medium node - SMDS_MeshNode* n12; - SMESHDS_Mesh* meshDS = GetMeshDS(); - int faceID = -1, edgeID = -1; - const SMDS_PositionPtr Pos1 = n1->GetPosition(); - const SMDS_PositionPtr Pos2 = n2->GetPosition(); - - if( myShape.IsNull() ) +bool SMESH_MesherHelper::CheckNodeUV(const TopoDS_Face& F, + const SMDS_MeshNode* n, + gp_XY& uv, + const double tol, + const bool force, + double distXYZ[4]) const +{ + int shapeID = n->getshapeId(); + bool infinit = ( Precision::IsInfinite( uv.X() ) || Precision::IsInfinite( uv.Y() )); + if ( force || toCheckPosOnShape( shapeID ) || infinit ) + { + // check that uv is correct + TopLoc_Location loc; + Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc ); + gp_Pnt nodePnt = XYZ( n ), surfPnt(0,0,0); + double dist = 0; + if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() ); + if ( infinit || + (dist = nodePnt.Distance( surfPnt = surface->Value( uv.X(), uv.Y() ))) > tol ) { - if( Pos1->GetTypeOfPosition()==SMDS_TOP_FACE ) { - faceID = Pos1->GetShapeId(); + setPosOnShapeValidity( shapeID, false ); + if ( !infinit && distXYZ ) { + surfPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z(); } - else if( Pos2->GetTypeOfPosition()==SMDS_TOP_FACE ) { - faceID = Pos2->GetShapeId(); + // uv incorrect, project the node to surface + GeomAPI_ProjectPointOnSurf& projector = GetProjector( F, loc, tol ); + projector.Perform( nodePnt ); + if ( !projector.IsDone() || projector.NbPoints() < 1 ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeUV() failed to project" ); + return false; } - - if( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE ) { - edgeID = Pos1->GetShapeId(); + Quantity_Parameter U,V; + projector.LowerDistanceParameters(U,V); + uv.SetCoord( U,V ); + surfPnt = surface->Value( U, V ); + dist = nodePnt.Distance( surfPnt ); + if ( distXYZ ) { + surfPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = surfPnt.X(); distXYZ[2] = surfPnt.Y(); distXYZ[3]=surfPnt.Z(); } - if( Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE ) { - edgeID = Pos2->GetShapeId(); + if ( dist > tol ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeUV(), invalid projection" ); + return false; } + // store the fixed UV on the face + if ( myShape.IsSame(F) && shapeID == myShapeID && myFixNodeParameters ) + const_cast(n)->SetPosition + ( SMDS_PositionPtr( new SMDS_FacePosition( U, V ))); } - - if(!force3d) { - // we try to create medium node using UV parameters of - // nodes, else - medium between corresponding 3d points - if(faceID>-1 || shapeType == TopAbs_FACE) { - // obtaining a face and 2d points for nodes - TopoDS_Face F; - if( myShape.IsNull() ) - F = TopoDS::Face(meshDS->IndexToShape(faceID)); - else { - F = TopoDS::Face(myShape); - faceID = myShapeID; - } - - gp_XY p1 = GetNodeUV(F,n1,n2); - gp_XY p2 = GetNodeUV(F,n2,n1); - - if ( IsDegenShape( Pos1->GetShapeId() )) - p1.SetCoord( myParIndex, p2.Coord( myParIndex )); - else if ( IsDegenShape( Pos2->GetShapeId() )) - p2.SetCoord( myParIndex, p1.Coord( myParIndex )); - - //checking if surface is periodic - Handle(Geom_Surface) S = BRep_Tool::Surface(F); - Standard_Real UF,UL,VF,VL; - S->Bounds(UF,UL,VF,VL); - - Standard_Real u,v; - Standard_Boolean isUPeriodic = S->IsUPeriodic(); - if(isUPeriodic) { - Standard_Real UPeriod = S->UPeriod(); - Standard_Real p2x = p2.X()+ShapeAnalysis::AdjustByPeriod(p2.X(),p1.X(),UPeriod); - Standard_Real pmid = (p1.X()+p2x)/2.; - u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,UF,UL); - } - else - u= (p1.X()+p2.X())/2.; - - Standard_Boolean isVPeriodic = S->IsVPeriodic(); - if(isVPeriodic) { - Standard_Real VPeriod = S->VPeriod(); - Standard_Real p2y = p2.Y()+ShapeAnalysis::AdjustByPeriod(p2.Y(),p1.Y(),VPeriod); - Standard_Real pmid = (p1.Y()+p2y)/2.; - v = pmid+ShapeAnalysis::AdjustToPeriod(pmid,VF,VL); - } - else - v = (p1.Y()+p2.Y())/2.; - - gp_Pnt P = S->Value(u, v); - n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnFace(n12, faceID, u, v); - myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12)); - return n12; - } - if (edgeID>-1 || shapeType == TopAbs_EDGE) { - - TopoDS_Edge E; - if( myShape.IsNull() ) - E = TopoDS::Edge(meshDS->IndexToShape(edgeID)); - else { - E = TopoDS::Edge(myShape); - edgeID = myShapeID; - } - - double p1 = GetNodeU(E,n1); - double p2 = GetNodeU(E,n2); - - double f,l; - Handle(Geom_Curve) C = BRep_Tool::Curve(E, f, l); - if(!C.IsNull()) { - - Standard_Boolean isPeriodic = C->IsPeriodic(); - double u; - if(isPeriodic) { - Standard_Real Period = C->Period(); - Standard_Real p = p2+ShapeAnalysis::AdjustByPeriod(p2,p1,Period); - Standard_Real pmid = (p1+p)/2.; - u = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter()); - } - else - u = (p1+p2)/2.; - - gp_Pnt P = C->Value( u ); - n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnEdge(n12, edgeID, u); - myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12)); - return n12; - } - } + else if ( uv.Modulus() > numeric_limits::min() ) + { + setPosOnShapeValidity( shapeID, true ); } - // 3d variant - double x = ( n1->X() + n2->X() )/2.; - double y = ( n1->Y() + n2->Y() )/2.; - double z = ( n1->Z() + n2->Z() )/2.; - n12 = meshDS->AddNode(x,y,z); - if(edgeID>-1) - meshDS->SetNodeOnEdge(n12, edgeID); - else if(faceID>-1) - meshDS->SetNodeOnFace(n12, faceID); - else - meshDS->SetNodeInVolume(n12, myShapeID); - myNLinkNodeMap.insert(NLinkNodeMap::value_type(link,n12)); - return n12; } + return true; } //======================================================================= -/*! - * Creates a node - */ +//function : GetProjector +//purpose : Return projector intitialized by given face without location, which is returned //======================================================================= -SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID) +GeomAPI_ProjectPointOnSurf& SMESH_MesherHelper::GetProjector(const TopoDS_Face& F, + TopLoc_Location& loc, + double tol ) const { - SMESHDS_Mesh * meshDS = GetMeshDS(); - SMDS_MeshNode* node = 0; - if ( ID ) - node = meshDS->AddNodeWithID( x, y, z, ID ); - else - node = meshDS->AddNode( x, y, z ); - if ( mySetElemOnShape && myShapeID > 0 ) { - switch ( myShape.ShapeType() ) { - case TopAbs_SOLID: meshDS->SetNodeInVolume( node, myShapeID); break; - case TopAbs_SHELL: meshDS->SetNodeInVolume( node, myShapeID); break; - case TopAbs_FACE: meshDS->SetNodeOnFace( node, myShapeID); break; - case TopAbs_EDGE: meshDS->SetNodeOnEdge( node, myShapeID); break; - case TopAbs_VERTEX: meshDS->SetNodeOnVertex( node, myShapeID); break; - default: ; - } + Handle(Geom_Surface) surface = BRep_Tool::Surface( F,loc ); + int faceID = GetMeshDS()->ShapeToIndex( F ); + TID2ProjectorOnSurf& i2proj = const_cast< TID2ProjectorOnSurf&>( myFace2Projector ); + TID2ProjectorOnSurf::iterator i_proj = i2proj.find( faceID ); + if ( i_proj == i2proj.end() ) + { + if ( tol == 0 ) tol = BRep_Tool::Tolerance( F ); + double U1, U2, V1, V2; + surface->Bounds(U1, U2, V1, V2); + GeomAPI_ProjectPointOnSurf* proj = new GeomAPI_ProjectPointOnSurf(); + proj->Init( surface, U1, U2, V1, V2, tol ); + i_proj = i2proj.insert( make_pair( faceID, proj )).first; } - return node; + return *( i_proj->second ); +} + +namespace +{ + gp_XY AverageUV(const gp_XY& uv1, const gp_XY& uv2) { return ( uv1 + uv2 ) / 2.; } + gp_XY_FunPtr(Added); // define gp_XY_Added pointer to function calling gp_XY::Added(gp_XY) + gp_XY_FunPtr(Subtracted); } //======================================================================= -/*! - * Creates quadratic or linear edge - */ +//function : applyIn2D +//purpose : Perform given operation on two 2d points in parameric space of given surface. +// It takes into account period of the surface. Use gp_XY_FunPtr macro +// to easily define pointer to function of gp_XY class. //======================================================================= -SMDS_MeshEdge* SMESH_MesherHelper::AddEdge(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const int id, - const bool force3d) +gp_XY SMESH_MesherHelper::applyIn2D(const Handle(Geom_Surface)& surface, + const gp_XY& uv1, + const gp_XY& uv2, + xyFunPtr fun, + const bool resultInPeriod) { - SMESHDS_Mesh * meshDS = GetMeshDS(); - - SMDS_MeshEdge* edge = 0; - if (myCreateQuadratic) { - const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); - if(id) - edge = meshDS->AddEdgeWithID(n1, n2, n12, id); - else - edge = meshDS->AddEdge(n1, n2, n12); - } - else { - if(id) - edge = meshDS->AddEdgeWithID(n1, n2, id); - else - edge = meshDS->AddEdge(n1, n2); + Standard_Boolean isUPeriodic = surface.IsNull() ? false : surface->IsUPeriodic(); + Standard_Boolean isVPeriodic = surface.IsNull() ? false : surface->IsVPeriodic(); + if ( !isUPeriodic && !isVPeriodic ) + return fun(uv1,uv2); + + // move uv2 not far than half-period from uv1 + double u2 = + uv2.X()+(isUPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.X(),uv1.X(),surface->UPeriod()) :0); + double v2 = + uv2.Y()+(isVPeriodic ? ShapeAnalysis::AdjustByPeriod(uv2.Y(),uv1.Y(),surface->VPeriod()) :0); + + // execute operation + gp_XY res = fun( uv1, gp_XY(u2,v2) ); + + // move result within period + if ( resultInPeriod ) + { + Standard_Real UF,UL,VF,VL; + surface->Bounds(UF,UL,VF,VL); + if ( isUPeriodic ) + res.SetX( res.X() + ShapeAnalysis::AdjustToPeriod(res.X(),UF,UL)); + if ( isVPeriodic ) + res.SetY( res.Y() + ShapeAnalysis::AdjustToPeriod(res.Y(),VF,VL)); } - if ( mySetElemOnShape && myShapeID > 0 ) - meshDS->SetMeshElementOnShape( edge, myShapeID ); + return res; +} +//======================================================================= +//function : GetMiddleUV +//purpose : Return middle UV taking in account surface period +//======================================================================= - return edge; +gp_XY SMESH_MesherHelper::GetMiddleUV(const Handle(Geom_Surface)& surface, + const gp_XY& p1, + const gp_XY& p2) +{ + // NOTE: + // the proper place of getting basic surface seems to be in applyIn2D() + // but we put it here to decrease a risk of regressions just before releasing a version + Handle(Geom_Surface) surf = surface; + while ( !surf.IsNull() && surf->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface ))) + surf = Handle(Geom_RectangularTrimmedSurface)::DownCast( surf )->BasisSurface(); + + return applyIn2D( surf, p1, p2, & AverageUV ); } //======================================================================= -/*! - * Creates quadratic or linear triangle - */ +//function : GetNodeU +//purpose : Return node U on edge //======================================================================= -SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const SMDS_MeshNode* n3, - const int id, - const bool force3d) +double SMESH_MesherHelper::GetNodeU(const TopoDS_Edge& E, + const SMDS_MeshNode* n, + const SMDS_MeshNode* inEdgeNode, + bool* check) { - SMESHDS_Mesh * meshDS = GetMeshDS(); - SMDS_MeshFace* elem = 0; - if(!myCreateQuadratic) { - if(id) - elem = meshDS->AddFaceWithID(n1, n2, n3, id); - else - elem = meshDS->AddFace(n1, n2, n3); - } - else { + double param = 0; + const SMDS_PositionPtr pos = n->GetPosition(); + if ( pos->GetTypeOfPosition()==SMDS_TOP_EDGE ) + { + const SMDS_EdgePosition* epos = static_cast( pos ); + param = epos->GetUParameter(); + } + else if( pos->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + { + if ( inEdgeNode && TopExp::FirstVertex( E ).IsSame( TopExp::LastVertex( E ))) // issue 0020128 + { + Standard_Real f,l; + BRep_Tool::Range( E, f,l ); + double uInEdge = GetNodeU( E, inEdgeNode ); + param = ( fabs( uInEdge - f ) < fabs( l - uInEdge )) ? f : l; + } + else + { + SMESHDS_Mesh * meshDS = GetMeshDS(); + int vertexID = n->getshapeId(); + const TopoDS_Vertex& V = TopoDS::Vertex(meshDS->IndexToShape(vertexID)); + param = BRep_Tool::Parameter( V, E ); + } + } + if ( check ) + { + double tol = BRep_Tool::Tolerance( E ); + double f,l; BRep_Tool::Range( E, f,l ); + bool force = ( param < f-tol || param > l+tol ); + if ( !force && pos->GetTypeOfPosition()==SMDS_TOP_EDGE ) + force = ( GetMeshDS()->ShapeToIndex( E ) != n->getshapeId() ); + + *check = CheckNodeU( E, n, param, 2*tol, force ); + } + return param; +} + +//======================================================================= +//function : CheckNodeU +//purpose : Check and fix node U on an edge +// Return false if U is bad and could not be fixed +//======================================================================= + +bool SMESH_MesherHelper::CheckNodeU(const TopoDS_Edge& E, + const SMDS_MeshNode* n, + double& u, + const double tol, + const bool force, + double distXYZ[4]) const +{ + int shapeID = n->getshapeId(); + if ( force || toCheckPosOnShape( shapeID )) + { + TopLoc_Location loc; double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( E,loc,f,l ); + if ( curve.IsNull() ) // degenerated edge + { + if ( u+tol < f || u-tol > l ) + { + double r = Max( 0.5, 1 - tol*n->GetID()); // to get a unique u on edge + u = f*r + l*(1-r); + } + } + else + { + gp_Pnt nodePnt = SMESH_TNodeXYZ( n ); + if ( !loc.IsIdentity() ) nodePnt.Transform( loc.Transformation().Inverted() ); + gp_Pnt curvPnt = curve->Value( u ); + double dist = nodePnt.Distance( curvPnt ); + if ( distXYZ ) { + curvPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z(); + } + if ( dist > tol ) + { + setPosOnShapeValidity( shapeID, false ); + // u incorrect, project the node to the curve + int edgeID = GetMeshDS()->ShapeToIndex( E ); + TID2ProjectorOnCurve& i2proj = const_cast< TID2ProjectorOnCurve&>( myEdge2Projector ); + TID2ProjectorOnCurve::iterator i_proj = + i2proj.insert( make_pair( edgeID, (GeomAPI_ProjectPointOnCurve*) 0 )).first; + if ( !i_proj->second ) + { + i_proj->second = new GeomAPI_ProjectPointOnCurve(); + i_proj->second->Init( curve, f, l ); + } + GeomAPI_ProjectPointOnCurve* projector = i_proj->second; + projector->Perform( nodePnt ); + if ( projector->NbPoints() < 1 ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeU() failed to project" ); + return false; + } + Quantity_Parameter U = projector->LowerDistanceParameter(); + u = double( U ); + curvPnt = curve->Value( u ); + dist = nodePnt.Distance( curvPnt ); + if ( distXYZ ) { + curvPnt.Transform( loc ); + distXYZ[0] = dist; + distXYZ[1] = curvPnt.X(); distXYZ[2] = curvPnt.Y(); distXYZ[3]=curvPnt.Z(); + } + if ( dist > tol ) + { + MESSAGE( "SMESH_MesherHelper::CheckNodeU(), invalid projection" ); + MESSAGE("distance " << dist << " " << tol ); + return false; + } + // store the fixed U on the edge + if ( myShape.IsSame(E) && shapeID == myShapeID && myFixNodeParameters ) + const_cast(n)->SetPosition + ( SMDS_PositionPtr( new SMDS_EdgePosition( U ))); + } + else if ( fabs( u ) > numeric_limits::min() ) + { + setPosOnShapeValidity( shapeID, true ); + } + if (( u < f-tol || u > l+tol ) && force ) + { + // node is on vertex but is set on periodic but trimmed edge (issue 0020890) + try + { + // do not use IsPeriodic() as Geom_TrimmedCurve::IsPeriodic () returns false + double period = curve->Period(); + u = ( u < f ) ? u + period : u - period; + } + catch (Standard_Failure& exc) + { + return false; + } + } + } + } + return true; +} + +//======================================================================= +//function : GetMediumPos +//purpose : Return index and type of the shape (EDGE or FACE only) to +// set a medium node on +//======================================================================= + +std::pair SMESH_MesherHelper::GetMediumPos(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2) +{ + TopAbs_ShapeEnum shapeType = TopAbs_SHAPE; + int shapeID = -1; + TopoDS_Shape shape; + + if (( myShapeID == n1->getshapeId() || myShapeID == n2->getshapeId() ) && myShapeID > 0 ) + { + shapeType = myShape.ShapeType(); + shapeID = myShapeID; + } + else if ( n1->getshapeId() == n2->getshapeId() ) + { + shapeID = n2->getshapeId(); + shape = GetSubShapeByNode( n1, GetMeshDS() ); + } + else + { + const SMDS_TypeOfPosition Pos1 = n1->GetPosition()->GetTypeOfPosition(); + const SMDS_TypeOfPosition Pos2 = n2->GetPosition()->GetTypeOfPosition(); + + if ( Pos1 == SMDS_TOP_3DSPACE || Pos2 == SMDS_TOP_3DSPACE ) + { + } + else if ( Pos1 == SMDS_TOP_FACE || Pos2 == SMDS_TOP_FACE ) + { + if ( Pos1 != SMDS_TOP_FACE || Pos2 != SMDS_TOP_FACE ) + { + if ( Pos1 != SMDS_TOP_FACE ) std::swap( n1,n2 ); + TopoDS_Shape F = GetSubShapeByNode( n1, GetMeshDS() ); + TopoDS_Shape S = GetSubShapeByNode( n2, GetMeshDS() ); + if ( IsSubShape( S, F )) + { + shapeType = TopAbs_FACE; + shapeID = n1->getshapeId(); + } + } + } + else if ( Pos1 == SMDS_TOP_EDGE && Pos2 == SMDS_TOP_EDGE ) + { + TopoDS_Shape E1 = GetSubShapeByNode( n1, GetMeshDS() ); + TopoDS_Shape E2 = GetSubShapeByNode( n2, GetMeshDS() ); + shape = GetCommonAncestor( E1, E2, *myMesh, TopAbs_FACE ); + } + else if ( Pos1 == SMDS_TOP_VERTEX && Pos2 == SMDS_TOP_VERTEX ) + { + TopoDS_Shape V1 = GetSubShapeByNode( n1, GetMeshDS() ); + TopoDS_Shape V2 = GetSubShapeByNode( n2, GetMeshDS() ); + shape = GetCommonAncestor( V1, V2, *myMesh, TopAbs_EDGE ); + if ( shape.IsNull() ) shape = GetCommonAncestor( V1, V2, *myMesh, TopAbs_FACE ); + } + else // VERTEX and EDGE + { + if ( Pos1 != SMDS_TOP_VERTEX ) std::swap( n1,n2 ); + TopoDS_Shape V = GetSubShapeByNode( n1, GetMeshDS() ); + TopoDS_Shape E = GetSubShapeByNode( n2, GetMeshDS() ); + if ( IsSubShape( V, E )) + shape = E; + else + shape = GetCommonAncestor( V, E, *myMesh, TopAbs_FACE ); + } + } + + if ( !shape.IsNull() ) + { + if ( shapeID < 1 ) + shapeID = GetMeshDS()->ShapeToIndex( shape ); + shapeType = shape.ShapeType(); + } + return make_pair( shapeID, shapeType ); +} + +//======================================================================= +//function : GetMediumNode +//purpose : Return existing or create new medium nodes between given ones +//======================================================================= + +const SMDS_MeshNode* SMESH_MesherHelper::GetMediumNode(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + bool force3d) +{ + // Find existing node + + SMESH_TLink link(n1,n2); + ItTLinkNode itLN = myTLinkNodeMap.find( link ); + if ( itLN != myTLinkNodeMap.end() ) { + return (*itLN).second; + } + + // Create medium node + + SMDS_MeshNode* n12; + SMESHDS_Mesh* meshDS = GetMeshDS(); + + if ( IsSeamShape( n1->getshapeId() )) + // to get a correct UV of a node on seam, the second node must have checked UV + std::swap( n1, n2 ); + + // get type of shape for the new medium node + int faceID = -1, edgeID = -1; + TopoDS_Edge E; double u [2]; + TopoDS_Face F; gp_XY uv[2]; + bool uvOK[2] = { false, false }; + + pair pos = GetMediumPos( n1, n2 ); + + // get positions of the given nodes on shapes + if ( pos.second == TopAbs_FACE ) + { + F = TopoDS::Face(meshDS->IndexToShape( faceID = pos.first )); + uv[0] = GetNodeUV(F,n1,n2, force3d ? 0 : &uvOK[0]); + uv[1] = GetNodeUV(F,n2,n1, force3d ? 0 : &uvOK[1]); + } + else if ( pos.second == TopAbs_EDGE ) + { + const SMDS_PositionPtr Pos1 = n1->GetPosition(); + const SMDS_PositionPtr Pos2 = n2->GetPosition(); + if ( Pos1->GetTypeOfPosition()==SMDS_TOP_EDGE && + Pos2->GetTypeOfPosition()==SMDS_TOP_EDGE && + n1->getshapeId() != n2->getshapeId() ) + { + // issue 0021006 + return getMediumNodeOnComposedWire(n1,n2,force3d); + } + E = TopoDS::Edge(meshDS->IndexToShape( edgeID = pos.first )); + u[0] = GetNodeU(E,n1,n2, force3d ? 0 : &uvOK[0]); + u[1] = GetNodeU(E,n2,n1, force3d ? 0 : &uvOK[1]); + } + + if ( !force3d & uvOK[0] && uvOK[1] ) + { + // we try to create medium node using UV parameters of + // nodes, else - medium between corresponding 3d points + if( ! F.IsNull() ) + { + //if ( uvOK[0] && uvOK[1] ) + { + if ( IsDegenShape( n1->getshapeId() )) { + if ( myParIndex & U_periodic ) uv[0].SetCoord( 1, uv[1].Coord( 1 )); + else uv[0].SetCoord( 2, uv[1].Coord( 2 )); + } + else if ( IsDegenShape( n2->getshapeId() )) { + if ( myParIndex & U_periodic ) uv[1].SetCoord( 1, uv[0].Coord( 1 )); + else uv[1].SetCoord( 2, uv[0].Coord( 2 )); + } + + TopLoc_Location loc; + Handle(Geom_Surface) S = BRep_Tool::Surface(F,loc); + gp_XY UV = GetMiddleUV( S, uv[0], uv[1] ); + gp_Pnt P = S->Value( UV.X(), UV.Y() ).Transformed(loc); + n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y()); + myTLinkNodeMap.insert(make_pair(link,n12)); + return n12; + } + } + else if ( !E.IsNull() ) + { + double f,l; + Handle(Geom_Curve) C = BRep_Tool::Curve(E, f, l); + if(!C.IsNull()) + { + Standard_Boolean isPeriodic = C->IsPeriodic(); + double U; + if(isPeriodic) { + Standard_Real Period = C->Period(); + Standard_Real p = u[1]+ShapeAnalysis::AdjustByPeriod(u[1],u[0],Period); + Standard_Real pmid = (u[0]+p)/2.; + U = pmid+ShapeAnalysis::AdjustToPeriod(pmid,C->FirstParameter(),C->LastParameter()); + } + else + U = (u[0]+u[1])/2.; + + gp_Pnt P = C->Value( U ); + n12 = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnEdge(n12, edgeID, U); + myTLinkNodeMap.insert(make_pair(link,n12)); + return n12; + } + } + } + + // 3d variant + double x = ( n1->X() + n2->X() )/2.; + double y = ( n1->Y() + n2->Y() )/2.; + double z = ( n1->Z() + n2->Z() )/2.; + n12 = meshDS->AddNode(x,y,z); + + if ( !F.IsNull() ) + { + gp_XY UV = ( uv[0] + uv[1] ) / 2.; + CheckNodeUV( F, n12, UV, 2*BRep_Tool::Tolerance( F ), /*force=*/true); + meshDS->SetNodeOnFace(n12, faceID, UV.X(), UV.Y() ); + } + else if ( !E.IsNull() ) + { + double U = ( u[0] + u[1] ) / 2.; + CheckNodeU( E, n12, U, 2*BRep_Tool::Tolerance( E ), /*force=*/true); + meshDS->SetNodeOnEdge(n12, edgeID, U); + } + else if ( myShapeID > 0 ) + { + meshDS->SetNodeInVolume(n12, myShapeID); + } + + myTLinkNodeMap.insert( make_pair( link, n12 )); + return n12; +} + +//================================================================================ +/*! + * \brief Makes a medium node if nodes reside different edges + */ +//================================================================================ + +const SMDS_MeshNode* SMESH_MesherHelper::getMediumNodeOnComposedWire(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + bool force3d) +{ + gp_Pnt middle = 0.5 * XYZ(n1) + 0.5 * XYZ(n2); + SMDS_MeshNode* n12 = AddNode( middle.X(), middle.Y(), middle.Z() ); + + // To find position on edge and 3D position for n12, + // project to 2 edges and select projection most close to + + double u = 0, distMiddleProj = Precision::Infinite(), distXYZ[4]; + int iOkEdge = 0; + TopoDS_Edge edges[2]; + for ( int is2nd = 0; is2nd < 2; ++is2nd ) + { + // get an edge + const SMDS_MeshNode* n = is2nd ? n2 : n1; + TopoDS_Shape shape = GetSubShapeByNode( n, GetMeshDS() ); + if ( shape.IsNull() || shape.ShapeType() != TopAbs_EDGE ) + continue; + + // project to get U of projection and distance from middle to projection + TopoDS_Edge edge = edges[ is2nd ] = TopoDS::Edge( shape ); + double node2MiddleDist = middle.Distance( XYZ(n) ); + double foundU = GetNodeU( edge, n ); + CheckNodeU( edge, n12, foundU, 2*BRep_Tool::Tolerance(edge), /*force=*/true, distXYZ ); + if ( distXYZ[0] < node2MiddleDist ) + { + distMiddleProj = distXYZ[0]; + u = foundU; + iOkEdge = is2nd; + } + } + if ( Precision::IsInfinite( distMiddleProj )) + { + // both projections failed; set n12 on the edge of n1 with U of a common vertex + TopoDS_Vertex vCommon; + if ( TopExp::CommonVertex( edges[0], edges[1], vCommon )) + u = BRep_Tool::Parameter( vCommon, edges[0] ); + else + { + double f,l, u0 = GetNodeU( edges[0], n1 ); + BRep_Tool::Range( edges[0],f,l ); + u = ( fabs(u0-f) < fabs(u0-l) ) ? f : l; + } + iOkEdge = 0; + distMiddleProj = 0; + } + + // move n12 to position of a successfull projection + double tol = BRep_Tool::Tolerance(edges[ iOkEdge ]); + if ( !force3d && distMiddleProj > 2*tol ) + { + TopLoc_Location loc; double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( edges[iOkEdge],loc,f,l ); + gp_Pnt p = curve->Value( u ); + GetMeshDS()->MoveNode( n12, p.X(), p.Y(), p.Z() ); + } + + GetMeshDS()->SetNodeOnEdge(n12, edges[iOkEdge], u); + + myTLinkNodeMap.insert( make_pair( SMESH_TLink(n1,n2), n12 )); + + return n12; +} + +//======================================================================= +//function : AddNode +//purpose : Creates a node +//======================================================================= + +SMDS_MeshNode* SMESH_MesherHelper::AddNode(double x, double y, double z, int ID, + double u, double v) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshNode* node = 0; + if ( ID ) + node = meshDS->AddNodeWithID( x, y, z, ID ); + else + node = meshDS->AddNode( x, y, z ); + if ( mySetElemOnShape && myShapeID > 0 ) { + switch ( myShape.ShapeType() ) { + case TopAbs_SOLID: meshDS->SetNodeInVolume( node, myShapeID); break; + case TopAbs_SHELL: meshDS->SetNodeInVolume( node, myShapeID); break; + case TopAbs_FACE: meshDS->SetNodeOnFace( node, myShapeID, u, v); break; + case TopAbs_EDGE: meshDS->SetNodeOnEdge( node, myShapeID, u); break; + case TopAbs_VERTEX: meshDS->SetNodeOnVertex( node, myShapeID); break; + default: ; + } + } + return node; +} + +//======================================================================= +//function : AddEdge +//purpose : Creates quadratic or linear edge +//======================================================================= + +SMDS_MeshEdge* SMESH_MesherHelper::AddEdge(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const int id, + const bool force3d) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + + SMDS_MeshEdge* edge = 0; + if (myCreateQuadratic) { + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + if(id) + edge = meshDS->AddEdgeWithID(n1, n2, n12, id); + else + edge = meshDS->AddEdge(n1, n2, n12); + } + else { + if(id) + edge = meshDS->AddEdgeWithID(n1, n2, id); + else + edge = meshDS->AddEdge(n1, n2); + } + + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( edge, myShapeID ); + + return edge; +} + +//======================================================================= +//function : AddFace +//purpose : Creates quadratic or linear triangle +//======================================================================= + +SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* n3, + const int id, + const bool force3d) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshFace* elem = 0; + + if( n1==n2 || n2==n3 || n3==n1 ) + return elem; + + if(!myCreateQuadratic) { + if(id) + elem = meshDS->AddFaceWithID(n1, n2, n3, id); + else + elem = meshDS->AddFace(n1, n2, n3); + } + else { const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d); const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d); @@ -656,20 +1303,39 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear quadrangle - */ +//function : AddFace +//purpose : Creates quadratic or linear quadrangle //======================================================================= SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* n3, const SMDS_MeshNode* n4, - const int id, - const bool force3d) + const int id, + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); SMDS_MeshFace* elem = 0; + + if( n1==n2 ) { + return AddFace(n1,n3,n4,id,force3d); + } + if( n1==n3 ) { + return AddFace(n1,n2,n4,id,force3d); + } + if( n1==n4 ) { + return AddFace(n1,n2,n3,id,force3d); + } + if( n2==n3 ) { + return AddFace(n1,n2,n4,id,force3d); + } + if( n2==n4 ) { + return AddFace(n1,n2,n3,id,force3d); + } + if( n3==n4 ) { + return AddFace(n1,n2,n3,id,force3d); + } + if(!myCreateQuadratic) { if(id) elem = meshDS->AddFaceWithID(n1, n2, n3, n4, id); @@ -694,32 +1360,70 @@ SMDS_MeshFace* SMESH_MesherHelper::AddFace(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear volume - */ +//function : AddPolygonalFace +//purpose : Creates polygon, with additional nodes in quadratic mesh //======================================================================= -SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, - const SMDS_MeshNode* n2, - const SMDS_MeshNode* n3, - const SMDS_MeshNode* n4, - const SMDS_MeshNode* n5, - const SMDS_MeshNode* n6, - const int id, - const bool force3d) +SMDS_MeshFace* SMESH_MesherHelper::AddPolygonalFace (const vector& nodes, + const int id, + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); - SMDS_MeshVolume* elem = 0; + SMDS_MeshFace* elem = 0; + if(!myCreateQuadratic) { if(id) - elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, id); + elem = meshDS->AddPolygonalFaceWithID(nodes, id); else - elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6); + elem = meshDS->AddPolygonalFace(nodes); } else { - const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); - const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d); - const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d); + vector newNodes; + for ( int i = 0; i < nodes.size(); ++i ) + { + const SMDS_MeshNode* n1 = nodes[i]; + const SMDS_MeshNode* n2 = nodes[(i+1)%nodes.size()]; + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + newNodes.push_back( n1 ); + newNodes.push_back( n12 ); + } + if(id) + elem = meshDS->AddPolygonalFaceWithID(newNodes, id); + else + elem = meshDS->AddPolygonalFace(newNodes); + } + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( elem, myShapeID ); + + return elem; +} + +//======================================================================= +//function : AddVolume +//purpose : Creates quadratic or linear prism +//======================================================================= + +SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* n3, + const SMDS_MeshNode* n4, + const SMDS_MeshNode* n5, + const SMDS_MeshNode* n6, + const int id, + const bool force3d) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshVolume* elem = 0; + if(!myCreateQuadratic) { + if(id) + elem = meshDS->AddVolumeWithID(n1, n2, n3, n4, n5, n6, id); + else + elem = meshDS->AddVolume(n1, n2, n3, n4, n5, n6); + } + else { + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + const SMDS_MeshNode* n23 = GetMediumNode(n2,n3,force3d); + const SMDS_MeshNode* n31 = GetMediumNode(n3,n1,force3d); const SMDS_MeshNode* n45 = GetMediumNode(n4,n5,force3d); const SMDS_MeshNode* n56 = GetMediumNode(n5,n6,force3d); @@ -743,9 +1447,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear volume - */ +//function : AddVolume +//purpose : Creates quadratic or linear tetrahedron //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -753,7 +1456,7 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, const SMDS_MeshNode* n3, const SMDS_MeshNode* n4, const int id, - const bool force3d) + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); SMDS_MeshVolume* elem = 0; @@ -784,9 +1487,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear pyramid - */ +//function : AddVolume +//purpose : Creates quadratic or linear pyramid //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -795,7 +1497,7 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, const SMDS_MeshNode* n4, const SMDS_MeshNode* n5, const int id, - const bool force3d) + const bool force3d) { SMDS_MeshVolume* elem = 0; if(!myCreateQuadratic) { @@ -832,9 +1534,8 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * Creates quadratic or linear hexahedron - */ +//function : AddVolume +//purpose : Creates quadratic or linear hexahedron //======================================================================= SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, @@ -846,7 +1547,7 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, const SMDS_MeshNode* n7, const SMDS_MeshNode* n8, const int id, - const bool force3d) + const bool force3d) { SMESHDS_Mesh * meshDS = GetMeshDS(); SMDS_MeshVolume* elem = 0; @@ -888,273 +1589,369 @@ SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, } //======================================================================= -/*! - * \brief Load nodes bound to face into a map of node columns - * \param theParam2ColumnMap - map of node columns to fill - * \param theFace - the face on which nodes are searched for - * \param theBaseEdge - the edge nodes of which are columns' bases - * \param theMesh - the mesh containing nodes - * \retval bool - false if something is wrong - * - * The key of the map is a normalized parameter of each - * base node on theBaseEdge. - * This method works in supposition that nodes on the face - * forms a rectangular grid and elements can be quardrangles or triangles - */ +//function : AddVolume +//purpose : Creates LINEAR!!!!!!!!! octahedron //======================================================================= -bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, - const TopoDS_Face& theFace, - const TopoDS_Edge& theBaseEdge, - SMESHDS_Mesh* theMesh) +SMDS_MeshVolume* SMESH_MesherHelper::AddVolume(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* n3, + const SMDS_MeshNode* n4, + const SMDS_MeshNode* n5, + const SMDS_MeshNode* n6, + const SMDS_MeshNode* n7, + const SMDS_MeshNode* n8, + const SMDS_MeshNode* n9, + const SMDS_MeshNode* n10, + const SMDS_MeshNode* n11, + const SMDS_MeshNode* n12, + const int id, + bool force3d) { - // get vertices of theBaseEdge - TopoDS_Vertex vfb, vlb, vft; // first and last, bottom and top vertices - TopoDS_Edge eFrw = TopoDS::Edge( theBaseEdge.Oriented( TopAbs_FORWARD )); - TopExp::Vertices( eFrw, vfb, vlb ); - - // find the other edges of theFace and orientation of e1 - TopoDS_Edge e1, e2, eTop; - bool rev1, CumOri = false; - TopExp_Explorer exp( theFace, TopAbs_EDGE ); - int nbEdges = 0; - for ( ; exp.More(); exp.Next() ) { - if ( ++nbEdges > 4 ) { - return false; // more than 4 edges in theFace - } - TopoDS_Edge e = TopoDS::Edge( exp.Current() ); - if ( theBaseEdge.IsSame( e )) - continue; - TopoDS_Vertex vCommon; - if ( !TopExp::CommonVertex( theBaseEdge, e, vCommon )) - eTop = e; - else if ( vCommon.IsSame( vfb )) { - e1 = e; - vft = TopExp::LastVertex( e1, CumOri ); - rev1 = vfb.IsSame( vft ); - if ( rev1 ) - vft = TopExp::FirstVertex( e1, CumOri ); - } + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshVolume* elem = 0; + if(id) + elem = meshDS->AddVolumeWithID(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12,id); + else + elem = meshDS->AddVolume(n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,n11,n12); + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( elem, myShapeID ); + return elem; +} + +//======================================================================= +//function : AddPolyhedralVolume +//purpose : Creates polyhedron. In quadratic mesh, adds medium nodes +//======================================================================= + +SMDS_MeshVolume* +SMESH_MesherHelper::AddPolyhedralVolume (const std::vector& nodes, + const std::vector& quantities, + const int id, + const bool force3d) +{ + SMESHDS_Mesh * meshDS = GetMeshDS(); + SMDS_MeshVolume* elem = 0; + if(!myCreateQuadratic) + { + if(id) + elem = meshDS->AddPolyhedralVolumeWithID(nodes, quantities, id); else - e2 = e; - } - if ( nbEdges < 4 ) { - return false; // less than 4 edges in theFace - } - if ( e2.IsNull() && vfb.IsSame( vlb )) - e2 = e1; - - // submeshes corresponding to shapes - SMESHDS_SubMesh* smFace = theMesh->MeshElements( theFace ); - SMESHDS_SubMesh* smb = theMesh->MeshElements( theBaseEdge ); - SMESHDS_SubMesh* smt = theMesh->MeshElements( eTop ); - SMESHDS_SubMesh* sm1 = theMesh->MeshElements( e1 ); - SMESHDS_SubMesh* sm2 = theMesh->MeshElements( e2 ); - SMESHDS_SubMesh* smVfb = theMesh->MeshElements( vfb ); - SMESHDS_SubMesh* smVlb = theMesh->MeshElements( vlb ); - SMESHDS_SubMesh* smVft = theMesh->MeshElements( vft ); - if (!smFace || !smb || !smt || !sm1 || !sm2 || !smVfb || !smVlb || !smVft ) { - RETURN_BAD_RESULT( "NULL submesh " <NbNodes() != smt->NbNodes() || sm1->NbNodes() != sm2->NbNodes() ) { - RETURN_BAD_RESULT(" Diff nb of nodes on opposite edges" ); - } - if (smVfb->NbNodes() != 1 || smVlb->NbNodes() != 1 || smVft->NbNodes() != 1) { - RETURN_BAD_RESULT("Empty submesh of vertex"); - } - // define whether mesh is quadratic - bool isQuadraticMesh = false; - SMDS_ElemIteratorPtr eIt = smFace->GetElements(); - if ( !eIt->more() ) { - RETURN_BAD_RESULT("No elements on the face"); - } - const SMDS_MeshElement* e = eIt->next(); - isQuadraticMesh = e->IsQuadratic(); - - if ( sm1->NbNodes() * smb->NbNodes() != smFace->NbNodes() ) { - // check quadratic case - if ( isQuadraticMesh ) { - // what if there are quadrangles and triangles mixed? -// int n1 = sm1->NbNodes()/2; -// int n2 = smb->NbNodes()/2; -// int n3 = sm1->NbNodes() - n1; -// int n4 = smb->NbNodes() - n2; -// int nf = sm1->NbNodes()*smb->NbNodes() - n3*n4; -// if( nf != smFace->NbNodes() ) { -// MESSAGE( "Wrong nb face nodes: " << -// sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); -// return false; -// } - } - else { - RETURN_BAD_RESULT( "Wrong nb face nodes: " << - sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); - } + elem = meshDS->AddPolyhedralVolume(nodes, quantities); } - // IJ size - int vsize = sm1->NbNodes() + 2; - int hsize = smb->NbNodes() + 2; - if(isQuadraticMesh) { - vsize = vsize - sm1->NbNodes()/2 -1; - hsize = hsize - smb->NbNodes()/2 -1; + else + { + vector newNodes; + vector newQuantities; + for ( int iFace=0, iN=0; iFace < quantities.size(); ++iFace) + { + int nbNodesInFace = quantities[iFace]; + newQuantities.push_back(0); + for ( int i = 0; i < nbNodesInFace; ++i ) + { + const SMDS_MeshNode* n1 = nodes[ iN + i ]; + newNodes.push_back( n1 ); + newQuantities.back()++; + + const SMDS_MeshNode* n2 = nodes[ iN + ( i+1==nbNodesInFace ? 0 : i+1 )]; +// if ( n1->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE && +// n2->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE ) + { + const SMDS_MeshNode* n12 = GetMediumNode(n1,n2,force3d); + newNodes.push_back( n12 ); + newQuantities.back()++; + } + } + iN += nbNodesInFace; + } + if(id) + elem = meshDS->AddPolyhedralVolumeWithID( newNodes, newQuantities, id ); + else + elem = meshDS->AddPolyhedralVolume( newNodes, newQuantities ); } + if ( mySetElemOnShape && myShapeID > 0 ) + meshDS->SetMeshElementOnShape( elem, myShapeID ); + + return elem; +} - // load nodes from theBaseEdge +//======================================================================= +//function : LoadNodeColumns +//purpose : Load nodes bound to face into a map of node columns +//======================================================================= - std::set loadedNodes; - const SMDS_MeshNode* nullNode = 0; +bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, + const TopoDS_Face& theFace, + const TopoDS_Edge& theBaseEdge, + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh) +{ + return LoadNodeColumns(theParam2ColumnMap, + theFace, + std::list(1,theBaseEdge), + theMesh, + theProxyMesh); +} - std::vector & nVecf = theParam2ColumnMap[ 0.]; - nVecf.resize( vsize, nullNode ); - loadedNodes.insert( nVecf[ 0 ] = smVfb->GetNodes()->next() ); +//======================================================================= +//function : LoadNodeColumns +//purpose : Load nodes bound to face into a map of node columns +//======================================================================= - std::vector & nVecl = theParam2ColumnMap[ 1.]; - nVecl.resize( vsize, nullNode ); - loadedNodes.insert( nVecl[ 0 ] = smVlb->GetNodes()->next() ); +bool SMESH_MesherHelper::LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, + const TopoDS_Face& theFace, + const std::list& theBaseSide, + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh) +{ + // get a right submesh of theFace - double f, l; - BRep_Tool::Range( eFrw, f, l ); - double range = l - f; - SMDS_NodeIteratorPtr nIt = smb->GetNodes(); - const SMDS_MeshNode* node; - while ( nIt->more() ) { - node = nIt->next(); - if(IsMedium(node, SMDSAbs_Edge)) - continue; - const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); - if ( !pos ) { - return false; + const SMESHDS_SubMesh* faceSubMesh = 0; + if ( theProxyMesh ) + { + faceSubMesh = theProxyMesh->GetSubMesh( theFace ); + if ( !faceSubMesh || + faceSubMesh->NbElements() == 0 || + theProxyMesh->IsTemporary( faceSubMesh->GetElements()->next() )) + { + // can use a proxy sub-mesh with not temporary elements only + faceSubMesh = 0; + theProxyMesh = 0; } - double u = ( pos->GetUParameter() - f ) / range; - std::vector & nVec = theParam2ColumnMap[ u ]; - nVec.resize( vsize, nullNode ); - loadedNodes.insert( nVec[ 0 ] = node ); } - if ( theParam2ColumnMap.size() != hsize ) { - RETURN_BAD_RESULT( "Wrong node positions on theBaseEdge" ); + if ( !faceSubMesh ) + faceSubMesh = theMesh->MeshElements( theFace ); + if ( !faceSubMesh || faceSubMesh->NbElements() == 0 ) + return false; + + // get data of edges for normalization of params + + vector< double > length; + double fullLen = 0; + list::const_iterator edge; + { + for ( edge = theBaseSide.begin(); edge != theBaseSide.end(); ++edge ) + { + double len = std::max( 1e-10, SMESH_Algo::EdgeLength( *edge )); + fullLen += len; + length.push_back( len ); + } } - // load nodes from e1 + // get nodes on theBaseEdge sorted by param on edge and initialize theParam2ColumnMap with them + edge = theBaseSide.begin(); + for ( int iE = 0; edge != theBaseSide.end(); ++edge, ++iE ) + { + map< double, const SMDS_MeshNode*> sortedBaseNodes; + SMESH_Algo::GetSortedNodesOnEdge( theMesh, *edge,/*noMedium=*/true, sortedBaseNodes); + if ( sortedBaseNodes.empty() ) continue; - std::map< double, const SMDS_MeshNode*> sortedNodes; // sort by param on edge - nIt = sm1->GetNodes(); - while ( nIt->more() ) { - node = nIt->next(); - if(IsMedium(node)) - continue; - const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); - if ( !pos ) { - return false; + double f, l; + BRep_Tool::Range( *edge, f, l ); + if ( edge->Orientation() == TopAbs_REVERSED ) std::swap( f, l ); + const double coeff = 1. / ( l - f ) * length[iE] / fullLen; + const double prevPar = theParam2ColumnMap.empty() ? 0 : theParam2ColumnMap.rbegin()->first; + map< double, const SMDS_MeshNode*>::iterator u_n = sortedBaseNodes.begin(); + for ( ; u_n != sortedBaseNodes.end(); u_n++ ) + { + double par = prevPar + coeff * ( u_n->first - f ); + TParam2ColumnMap::iterator u2nn = + theParam2ColumnMap.insert( theParam2ColumnMap.end(), make_pair( par, TNodeColumn())); + u2nn->second.push_back( u_n->second ); } - sortedNodes.insert( std::make_pair( pos->GetUParameter(), node )); - } - loadedNodes.insert( nVecf[ vsize - 1 ] = smVft->GetNodes()->next() ); - std::map< double, const SMDS_MeshNode*>::iterator u_n = sortedNodes.begin(); - int row = rev1 ? vsize - 1 : 0; - int dRow = rev1 ? -1 : +1; - for ( ; u_n != sortedNodes.end(); u_n++ ) { - row += dRow; - loadedNodes.insert( nVecf[ row ] = u_n->second ); - } - - // try to load the rest nodes - - // get all faces from theFace - TIDSortedElemSet allFaces, foundFaces; - eIt = smFace->GetElements(); - while ( eIt->more() ) { - const SMDS_MeshElement* e = eIt->next(); - if ( e->GetType() == SMDSAbs_Face ) - allFaces.insert( e ); - } - // Starting from 2 neighbour nodes on theBaseEdge, look for a face - // the nodes belong to, and between the nodes of the found face, - // look for a not loaded node considering this node to be the next - // in a column of the starting second node. Repeat, starting - // from nodes next to the previous starting nodes in their columns, - // and so on while a face can be found. Then go the the next pair - // of nodes on theBaseEdge. - TParam2ColumnMap::iterator par_nVec_1 = theParam2ColumnMap.begin(); - TParam2ColumnMap::iterator par_nVec_2 = par_nVec_1; - // loop on columns - int col = 0; - for ( par_nVec_2++; par_nVec_2 != theParam2ColumnMap.end(); par_nVec_1++, par_nVec_2++ ) { - col++; - row = 0; - const SMDS_MeshNode* n1 = par_nVec_1->second[ row ]; - const SMDS_MeshNode* n2 = par_nVec_2->second[ row ]; - const SMDS_MeshElement* face = 0; - bool lastColOnClosedFace = ( nVecf[ row ] == n2 ); - do { - // look for a face by 2 nodes - face = SMESH_MeshEditor::FindFaceInSet( n1, n2, allFaces, foundFaces ); - if ( face ) { - int nbFaceNodes = face->NbNodes(); - if ( face->IsQuadratic() ) - nbFaceNodes /= 2; - if ( nbFaceNodes>4 ) { - RETURN_BAD_RESULT(" Too many nodes in a face: " << nbFaceNodes ); - } - // look for a not loaded node of the - bool found = false; - const SMDS_MeshNode* n3 = 0; // a node defferent from n1 and n2 - for ( int i = 0; i < nbFaceNodes && !found; ++i ) { - node = face->GetNode( i ); - found = loadedNodes.insert( node ).second; - if ( !found && node != n1 && node != n2 ) - n3 = node; - } - if ( lastColOnClosedFace && row + 1 < vsize ) { - node = nVecf[ row + 1 ]; - found = ( face->GetNodeIndex( node ) >= 0 ); - } - if ( found ) { - if ( ++row > vsize - 1 ) { - RETURN_BAD_RESULT( "Too many nodes in column "<< col <<": "<< row+1); - } - par_nVec_2->second[ row ] = node; - foundFaces.insert( face ); - n2 = node; - if ( nbFaceNodes==4 ) { - n1 = par_nVec_1->second[ row ]; - } - } - else if ( nbFaceNodes==3 && n3 == par_nVec_1->second[ row + 1 ] ) { - n1 = n3; - } - else { - RETURN_BAD_RESULT( "Not quad mesh, column "<< col ); - } - } + } + TParam2ColumnMap::iterator par_nVec_2, par_nVec_1 = theParam2ColumnMap.begin(); + if ( theProxyMesh ) + { + for ( ; par_nVec_1 != theParam2ColumnMap.end(); ++par_nVec_1 ) + { + const SMDS_MeshNode* & n = par_nVec_1->second[0]; + n = theProxyMesh->GetProxyNode( n ); } - while ( face && n1 && n2 ); - - if ( row < vsize - 1 ) { - MESSAGE( "Too few nodes in column "<< col <<": "<< row+1); - MESSAGE( "Base node 1: "<< par_nVec_1->second[0]); - MESSAGE( "Base node 2: "<< par_nVec_2->second[0]); - if ( n1 ) { MESSAGE( "Current node 1: "<< n1); } - else { MESSAGE( "Current node 1: NULL"); } - if ( n2 ) { MESSAGE( "Current node 2: "<< n2); } - else { MESSAGE( "Current node 2: NULL"); } - MESSAGE( "first base node: "<< theParam2ColumnMap.begin()->second[0]); - MESSAGE( "last base node: "<< theParam2ColumnMap.rbegin()->second[0]); - return false; + } + + int nbRows = 1 + faceSubMesh->NbElements() / ( theParam2ColumnMap.size()-1 ); + + // fill theParam2ColumnMap column by column by passing from nodes on + // theBaseEdge up via mesh faces on theFace + + par_nVec_2 = theParam2ColumnMap.begin(); + par_nVec_1 = par_nVec_2++; + TIDSortedElemSet emptySet, avoidSet; + for ( ; par_nVec_2 != theParam2ColumnMap.end(); ++par_nVec_1, ++par_nVec_2 ) + { + vector& nCol1 = par_nVec_1->second; + vector& nCol2 = par_nVec_2->second; + nCol1.resize( nbRows ); + nCol2.resize( nbRows ); + + int i1, i2, iRow = 0; + const SMDS_MeshNode *n1 = nCol1[0], *n2 = nCol2[0]; + // find face sharing node n1 and n2 and belonging to faceSubMesh + while ( const SMDS_MeshElement* face = + SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2)) + { + if ( faceSubMesh->Contains( face )) + { + int nbNodes = face->IsQuadratic() ? face->NbNodes()/2 : face->NbNodes(); + if ( nbNodes != 4 ) + return false; + n1 = face->GetNode( (i2+2) % 4 ); // opposite corner of quadrangle face + n2 = face->GetNode( (i1+2) % 4 ); + if ( ++iRow >= nbRows ) + return false; + nCol1[ iRow ] = n1; + nCol2[ iRow ] = n2; + avoidSet.clear(); + } + avoidSet.insert( face ); } - } // loop on columns + // set a real height + nCol1.resize( iRow + 1 ); + nCol2.resize( iRow + 1 ); + } + return theParam2ColumnMap.size() > 1 && theParam2ColumnMap.begin()->second.size() > 1; +} - return true; +//======================================================================= +//function : NbAncestors +//purpose : Return number of unique ancestors of the shape +//======================================================================= + +int SMESH_MesherHelper::NbAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType/*=TopAbs_SHAPE*/) +{ + TopTools_MapOfShape ancestors; + TopTools_ListIteratorOfListOfShape ansIt( mesh.GetAncestors(shape) ); + for ( ; ansIt.More(); ansIt.Next() ) { + if ( ancestorType == TopAbs_SHAPE || ansIt.Value().ShapeType() == ancestorType ) + ancestors.Add( ansIt.Value() ); + } + return ancestors.Extent(); +} + +//======================================================================= +//function : GetSubShapeOri +//purpose : Return orientation of sub-shape in the main shape +//======================================================================= + +TopAbs_Orientation SMESH_MesherHelper::GetSubShapeOri(const TopoDS_Shape& shape, + const TopoDS_Shape& subShape) +{ + TopAbs_Orientation ori = TopAbs_Orientation(-1); + if ( !shape.IsNull() && !subShape.IsNull() ) + { + TopExp_Explorer e( shape, subShape.ShapeType() ); + if ( shape.Orientation() >= TopAbs_INTERNAL ) // TopAbs_INTERNAL or TopAbs_EXTERNAL + e.Init( shape.Oriented(TopAbs_FORWARD), subShape.ShapeType() ); + for ( ; e.More(); e.Next()) + if ( subShape.IsSame( e.Current() )) + break; + if ( e.More() ) + ori = e.Current().Orientation(); + } + return ori; +} + +//======================================================================= +//function : IsSubShape +//purpose : +//======================================================================= + +bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, + const TopoDS_Shape& mainShape ) +{ + if ( !shape.IsNull() && !mainShape.IsNull() ) + { + for ( TopExp_Explorer exp( mainShape, shape.ShapeType()); + exp.More(); + exp.Next() ) + if ( shape.IsSame( exp.Current() )) + return true; + } + SCRUTE((shape.IsNull())); + SCRUTE((mainShape.IsNull())); + return false; } //======================================================================= -/** - * Check mesh without geometry for: if all elements on this shape are quadratic, - * quadratic elements will be created. - * Used then generated 3D mesh without geometry. +//function : IsSubShape +//purpose : +//======================================================================= + +bool SMESH_MesherHelper::IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMesh ) +{ + if ( shape.IsNull() || !aMesh ) + return false; + return + aMesh->GetMeshDS()->ShapeToIndex( shape ) || + // PAL16202 + (shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape )); +} + +//================================================================================ +/*! + * \brief Return maximal tolerance of shape + */ +//================================================================================ + +double SMESH_MesherHelper::MaxTolerance( const TopoDS_Shape& shape ) +{ + double tol = Precision::Confusion(); + TopExp_Explorer exp; + for ( exp.Init( shape, TopAbs_FACE ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Face( exp.Current()))); + for ( exp.Init( shape, TopAbs_EDGE ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Edge( exp.Current()))); + for ( exp.Init( shape, TopAbs_VERTEX ); exp.More(); exp.Next() ) + tol = Max( tol, BRep_Tool::Tolerance( TopoDS::Vertex( exp.Current()))); + + return tol; +} + +//================================================================================ +/*! + * \brief Check if the first and last vertices of an edge are the same + * \param anEdge - the edge to check + * \retval bool - true if same + */ +//================================================================================ + +bool SMESH_MesherHelper::IsClosedEdge( const TopoDS_Edge& anEdge ) +{ + if ( anEdge.Orientation() >= TopAbs_INTERNAL ) + return IsClosedEdge( TopoDS::Edge( anEdge.Oriented( TopAbs_FORWARD ))); + return TopExp::FirstVertex( anEdge ).IsSame( TopExp::LastVertex( anEdge )); +} + +//================================================================================ +/*! + * \brief Wrapper over TopExp::FirstVertex() and TopExp::LastVertex() fixing them + * in the case of INTERNAL edge */ +//================================================================================ + +TopoDS_Vertex SMESH_MesherHelper::IthVertex( const bool is2nd, + TopoDS_Edge anEdge, + const bool CumOri ) +{ + if ( anEdge.Orientation() >= TopAbs_INTERNAL ) + anEdge.Orientation( TopAbs_FORWARD ); + + const TopAbs_Orientation tgtOri = is2nd ? TopAbs_REVERSED : TopAbs_FORWARD; + TopoDS_Iterator vIt( anEdge, CumOri ); + while ( vIt.More() && vIt.Value().Orientation() != tgtOri ) + vIt.Next(); + + return ( vIt.More() ? TopoDS::Vertex(vIt.Value()) : TopoDS_Vertex() ); +} + +//======================================================================= +//function : IsQuadraticMesh +//purpose : Check mesh without geometry for: if all elements on this shape are quadratic, +// quadratic elements will be created. +// Used then generated 3D mesh without geometry. //======================================================================= SMESH_MesherHelper:: MType SMESH_MesherHelper::IsQuadraticMesh() @@ -1184,13 +1981,1520 @@ SMESH_MesherHelper:: MType SMESH_MesherHelper::IsQuadraticMesh() return SMESH_MesherHelper::COMP; } +//======================================================================= +//function : GetOtherParam +//purpose : Return an alternative parameter for a node on seam +//======================================================================= + +double SMESH_MesherHelper::GetOtherParam(const double param) const +{ + int i = myParIndex & U_periodic ? 0 : 1; + return fabs(param-myPar1[i]) < fabs(param-myPar2[i]) ? myPar2[i] : myPar1[i]; +} + +namespace { + + //======================================================================= + /*! + * \brief Iterator on ancestors of the given type + */ + //======================================================================= + + struct TAncestorsIterator : public SMDS_Iterator + { + TopTools_ListIteratorOfListOfShape _ancIter; + TopAbs_ShapeEnum _type; + TopTools_MapOfShape _encountered; + TAncestorsIterator( const TopTools_ListOfShape& ancestors, TopAbs_ShapeEnum type) + : _ancIter( ancestors ), _type( type ) + { + if ( _ancIter.More() ) { + if ( _ancIter.Value().ShapeType() != _type ) next(); + else _encountered.Add( _ancIter.Value() ); + } + } + virtual bool more() + { + return _ancIter.More(); + } + virtual const TopoDS_Shape* next() + { + const TopoDS_Shape* s = _ancIter.More() ? & _ancIter.Value() : 0; + if ( _ancIter.More() ) + for ( _ancIter.Next(); _ancIter.More(); _ancIter.Next()) + if ( _ancIter.Value().ShapeType() == _type && _encountered.Add( _ancIter.Value() )) + break; + return s; + } + }; + +} // namespace + //======================================================================= /*! - * \brief Return an alternative parameter for a node on seam + * \brief Return iterator on ancestors of the given type */ //======================================================================= -double SMESH_MesherHelper::GetOtherParam(const double param) const +PShapeIteratorPtr SMESH_MesherHelper::GetAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType) +{ + return PShapeIteratorPtr( new TAncestorsIterator( mesh.GetAncestors(shape), ancestorType)); +} + +//======================================================================= +//function : GetCommonAncestor +//purpose : Find a common ancestors of two shapes of the given type +//======================================================================= + +TopoDS_Shape SMESH_MesherHelper::GetCommonAncestor(const TopoDS_Shape& shape1, + const TopoDS_Shape& shape2, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType) { - return fabs(param-myPar1) < fabs(param-myPar2) ? myPar2 : myPar1; + TopoDS_Shape commonAnc; + if ( !shape1.IsNull() && !shape2.IsNull() ) + { + PShapeIteratorPtr ancIt = GetAncestors( shape1, mesh, ancestorType ); + while ( const TopoDS_Shape* anc = ancIt->next() ) + if ( IsSubShape( shape2, *anc )) + { + commonAnc = *anc; + break; + } + } + return commonAnc; } + +//#include + +//======================================================================= +namespace { // Structures used by FixQuadraticElements() +//======================================================================= + +#define __DMP__(txt) \ + //cout << txt +#define MSG(txt) __DMP__(txt< < 1/15 * + return middleNodeMove2 < 1/15./15. * linkLen2; + } + + struct QFace; + // --------------------------------------- + /*! + * \brief Quadratic link knowing its faces + */ + struct QLink: public SMESH_TLink + { + const SMDS_MeshNode* _mediumNode; + mutable vector _faces; + mutable gp_Vec _nodeMove; + mutable int _nbMoves; + + QLink(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* nm): + SMESH_TLink( n1,n2 ), _mediumNode(nm), _nodeMove(0,0,0), _nbMoves(0) { + _faces.reserve(4); + //if ( MediumPos() != SMDS_TOP_3DSPACE ) + _nodeMove = MediumPnt() - MiddlePnt(); + } + void SetContinuesFaces() const; + const QFace* GetContinuesFace( const QFace* face ) const; + bool OnBoundary() const; + gp_XYZ MiddlePnt() const { return ( XYZ( node1() ) + XYZ( node2() )) / 2.; } + gp_XYZ MediumPnt() const { return XYZ( _mediumNode ); } + + SMDS_TypeOfPosition MediumPos() const + { return _mediumNode->GetPosition()->GetTypeOfPosition(); } + SMDS_TypeOfPosition EndPos(bool isSecond) const + { return (isSecond ? node2() : node1())->GetPosition()->GetTypeOfPosition(); } + const SMDS_MeshNode* EndPosNode(SMDS_TypeOfPosition pos) const + { return EndPos(0) == pos ? node1() : EndPos(1) == pos ? node2() : 0; } + + void Move(const gp_Vec& move, bool sum=false) const + { _nodeMove += move; _nbMoves += sum ? (_nbMoves==0) : 1; } + gp_XYZ Move() const { return _nodeMove.XYZ() / _nbMoves; } + bool IsMoved() const { return (_nbMoves > 0 /*&& !IsStraight()*/); } + bool IsStraight() const + { return isStraightLink( (XYZ(node1())-XYZ(node2())).SquareModulus(), + _nodeMove.SquareMagnitude()); + } + bool operator<(const QLink& other) const { + return (node1()->GetID() == other.node1()->GetID() ? + node2()->GetID() < other.node2()->GetID() : + node1()->GetID() < other.node1()->GetID()); + } +// struct PtrComparator { +// bool operator() (const QLink* l1, const QLink* l2 ) const { return *l1 < *l2; } +// }; + }; + // --------------------------------------------------------- + /*! + * \brief Link in the chain of links; it connects two faces + */ + struct TChainLink + { + const QLink* _qlink; + mutable const QFace* _qfaces[2]; + + TChainLink(const QLink* qlink=0):_qlink(qlink) { + _qfaces[0] = _qfaces[1] = 0; + } + void SetFace(const QFace* face) const { int iF = _qfaces[0] ? 1 : 0; _qfaces[iF]=face; } + + bool IsBoundary() const { return !_qfaces[1]; } + + void RemoveFace( const QFace* face ) const + { _qfaces[(face == _qfaces[1])] = 0; if (!_qfaces[0]) std::swap(_qfaces[0],_qfaces[1]); } + + const QFace* NextFace( const QFace* f ) const + { return _qfaces[0]==f ? _qfaces[1] : _qfaces[0]; } + + const SMDS_MeshNode* NextNode( const SMDS_MeshNode* n ) const + { return n == _qlink->node1() ? _qlink->node2() : _qlink->node1(); } + + bool operator<(const TChainLink& other) const { return *_qlink < *other._qlink; } + + operator bool() const { return (_qlink); } + + const QLink* operator->() const { return _qlink; } + + gp_Vec Normal() const; + + bool IsStraight() const; + }; + // -------------------------------------------------------------------- + typedef list< TChainLink > TChain; + typedef set < TChainLink > TLinkSet; + typedef TLinkSet::const_iterator TLinkInSet; + + const int theFirstStep = 5; + + enum { ERR_OK, ERR_TRI, ERR_PRISM, ERR_UNKNOWN }; // errors of QFace::GetLinkChain() + // -------------------------------------------------------------------- + /*! + * \brief Face shared by two volumes and bound by QLinks + */ + struct QFace: public TIDSortedNodeSet + { + mutable const SMDS_MeshElement* _volumes[2]; + mutable vector< const QLink* > _sides; + mutable bool _sideIsAdded[4]; // added in chain of links + gp_Vec _normal; +#ifdef _DEBUG_ + mutable const SMDS_MeshElement* _face; +#endif + + QFace( const vector< const QLink*>& links, const SMDS_MeshElement* face=0 ); + + void SetVolume(const SMDS_MeshElement* v) const { _volumes[ _volumes[0] ? 1 : 0 ] = v; } + + int NbVolumes() const { return !_volumes[0] ? 0 : !_volumes[1] ? 1 : 2; } + + void AddSelfToLinks() const { + for ( int i = 0; i < _sides.size(); ++i ) + _sides[i]->_faces.push_back( this ); + } + int LinkIndex( const QLink* side ) const { + for (int i=0; i<_sides.size(); ++i ) if ( _sides[i] == side ) return i; + return -1; + } + bool GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& err) const; + + bool GetLinkChain( TChainLink& link, TChain& chain, SMDS_TypeOfPosition pos, int& err) const + { + int i = LinkIndex( link._qlink ); + if ( i < 0 ) return true; + _sideIsAdded[i] = true; + link.SetFace( this ); + // continue from opposite link + return GetLinkChain( (i+2)%_sides.size(), chain, pos, err ); + } + bool IsBoundary() const { return !_volumes[1]; } + + bool Contains( const SMDS_MeshNode* node ) const { return count(node); } + + bool IsSpoiled(const QLink* bentLink ) const; + + TLinkInSet GetBoundaryLink( const TLinkSet& links, + const TChainLink& avoidLink, + TLinkInSet * notBoundaryLink = 0, + const SMDS_MeshNode* nodeToContain = 0, + bool * isAdjacentUsed = 0, + int nbRecursionsLeft = -1) const; + + TLinkInSet GetLinkByNode( const TLinkSet& links, + const TChainLink& avoidLink, + const SMDS_MeshNode* nodeToContain) const; + + const SMDS_MeshNode* GetNodeInFace() const { + for ( int iL = 0; iL < _sides.size(); ++iL ) + if ( _sides[iL]->MediumPos() == SMDS_TOP_FACE ) return _sides[iL]->_mediumNode; + return 0; + } + + gp_Vec LinkNorm(const int i, SMESH_MesherHelper* theFaceHelper=0) const; + + double MoveByBoundary( const TChainLink& theLink, + const gp_Vec& theRefVec, + const TLinkSet& theLinks, + SMESH_MesherHelper* theFaceHelper=0, + const double thePrevLen=0, + const int theStep=theFirstStep, + gp_Vec* theLinkNorm=0, + double theSign=1.0) const; + }; + + //================================================================================ + /*! + * \brief Dump QLink and QFace + */ + ostream& operator << (ostream& out, const QLink& l) + { + out <<"QLink nodes: " + << l.node1()->GetID() << " - " + << l._mediumNode->GetID() << " - " + << l.node2()->GetID() << endl; + return out; + } + ostream& operator << (ostream& out, const QFace& f) + { + out <<"QFace nodes: "/*<< &f << " "*/; + for ( TIDSortedNodeSet::const_iterator n = f.begin(); n != f.end(); ++n ) + out << (*n)->GetID() << " "; + out << " \tvolumes: " + << (f._volumes[0] ? f._volumes[0]->GetID() : 0) << " " + << (f._volumes[1] ? f._volumes[1]->GetID() : 0); + out << " \tNormal: "<< f._normal.X() <<", "<& links, const SMDS_MeshElement* face ) + { + _volumes[0] = _volumes[1] = 0; + _sides = links; + _sideIsAdded[0]=_sideIsAdded[1]=_sideIsAdded[2]=_sideIsAdded[3]=false; + _normal.SetCoord(0,0,0); + for ( int i = 1; i < _sides.size(); ++i ) { + const QLink *l1 = _sides[i-1], *l2 = _sides[i]; + insert( l1->node1() ); insert( l1->node2() ); + // compute normal + gp_Vec v1( XYZ( l1->node2()), XYZ( l1->node1())); + gp_Vec v2( XYZ( l2->node1()), XYZ( l2->node2())); + if ( l1->node1() != l2->node1() && l1->node2() != l2->node2() ) + v1.Reverse(); + _normal += v1 ^ v2; + } + double normSqSize = _normal.SquareMagnitude(); + if ( normSqSize > numeric_limits::min() ) + _normal /= sqrt( normSqSize ); + else + _normal.SetCoord(1e-33,0,0); + +#ifdef _DEBUG_ + _face = face; +#endif + } + //================================================================================ + /*! + * \brief Make up a chain of links + * \param iSide - link to add first + * \param chain - chain to fill in + * \param pos - postion of medium nodes the links should have + * \param error - out, specifies what is wrong + * \retval bool - false if valid chain can't be built; "valid" means that links + * of the chain belongs to rectangles bounding hexahedrons + */ + //================================================================================ + + bool QFace::GetLinkChain( int iSide, TChain& chain, SMDS_TypeOfPosition pos, int& error) const + { + if ( iSide >= _sides.size() ) // wrong argument iSide + return false; + if ( _sideIsAdded[ iSide ]) // already in chain + return true; + + if ( _sides.size() != 4 ) { // triangle - visit all my continous faces + MSGBEG( *this ); + TLinkSet links; + list< const QFace* > faces( 1, this ); + while ( !faces.empty() ) { + const QFace* face = faces.front(); + for ( int i = 0; i < face->_sides.size(); ++i ) { + if ( !face->_sideIsAdded[i] && face->_sides[i] ) { + face->_sideIsAdded[i] = true; + // find a face side in the chain + TLinkInSet chLink = links.insert( TChainLink(face->_sides[i])).first; +// TChain::iterator chLink = chain.begin(); +// for ( ; chLink != chain.end(); ++chLink ) +// if ( chLink->_qlink == face->_sides[i] ) +// break; +// if ( chLink == chain.end() ) +// chLink = chain.insert( chain.begin(), TChainLink(face->_sides[i])); + // add a face to a chained link and put a continues face in the queue + chLink->SetFace( face ); + if ( face->_sides[i]->MediumPos() == pos ) + if ( const QFace* contFace = face->_sides[i]->GetContinuesFace( face )) + if ( contFace->_sides.size() == 3 ) + faces.push_back( contFace ); + } + } + faces.pop_front(); + } + if ( error < ERR_TRI ) + error = ERR_TRI; + chain.insert( chain.end(), links.begin(),links.end() ); + return false; + } + _sideIsAdded[iSide] = true; // not to add this link to chain again + const QLink* link = _sides[iSide]; + if ( !link) + return true; + + // add link into chain + TChain::iterator chLink = chain.insert( chain.begin(), TChainLink(link)); + chLink->SetFace( this ); + MSGBEG( *this ); + + // propagate from quadrangle to neighbour faces + if ( link->MediumPos() >= pos ) { + int nbLinkFaces = link->_faces.size(); + if ( nbLinkFaces == 4 || (/*nbLinkFaces < 4 && */link->OnBoundary())) { + // hexahedral mesh or boundary quadrangles - goto a continous face + if ( const QFace* f = link->GetContinuesFace( this )) + if ( f->_sides.size() == 4 ) + return f->GetLinkChain( *chLink, chain, pos, error ); + } + else { + TChainLink chLink(link); // side face of prismatic mesh - visit all faces of iSide + for ( int i = 0; i < nbLinkFaces; ++i ) + if ( link->_faces[i] ) + link->_faces[i]->GetLinkChain( chLink, chain, pos, error ); + if ( error < ERR_PRISM ) + error = ERR_PRISM; + return false; + } + } + return true; + } + + //================================================================================ + /*! + * \brief Return a boundary link of the triangle face + * \param links - set of all links + * \param avoidLink - link not to return + * \param notBoundaryLink - out, neither the returned link nor avoidLink + * \param nodeToContain - node the returned link must contain; if provided, search + * also performed on adjacent faces + * \param isAdjacentUsed - returns true if link is found in adjacent faces + * \param nbRecursionsLeft - to limit recursion + */ + //================================================================================ + + TLinkInSet QFace::GetBoundaryLink( const TLinkSet& links, + const TChainLink& avoidLink, + TLinkInSet * notBoundaryLink, + const SMDS_MeshNode* nodeToContain, + bool * isAdjacentUsed, + int nbRecursionsLeft) const + { + TLinkInSet linksEnd = links.end(), boundaryLink = linksEnd; + + typedef list< pair< const QFace*, TLinkInSet > > TFaceLinkList; + TFaceLinkList adjacentFaces; + + for ( int iL = 0; iL < _sides.size(); ++iL ) + { + if ( avoidLink._qlink == _sides[iL] ) + continue; + TLinkInSet link = links.find( _sides[iL] ); + if ( link == linksEnd ) continue; + if ( (*link)->MediumPos() > SMDS_TOP_FACE ) + continue; // We work on faces here, don't go inside a solid + + // check link + if ( link->IsBoundary() ) { + if ( !nodeToContain || + (*link)->node1() == nodeToContain || + (*link)->node2() == nodeToContain ) + { + boundaryLink = link; + if ( !notBoundaryLink ) break; + } + } + else if ( notBoundaryLink ) { + *notBoundaryLink = link; + if ( boundaryLink != linksEnd ) break; + } + + if ( boundaryLink == linksEnd && nodeToContain ) // collect adjacent faces + if ( const QFace* adj = link->NextFace( this )) + if ( adj->Contains( nodeToContain )) + adjacentFaces.push_back( make_pair( adj, link )); + } + + if ( isAdjacentUsed ) *isAdjacentUsed = false; + if ( boundaryLink == linksEnd && nodeToContain && nbRecursionsLeft) // check adjacent faces + { + if ( nbRecursionsLeft < 0 ) + nbRecursionsLeft = nodeToContain->NbInverseElements(); + TFaceLinkList::iterator adj = adjacentFaces.begin(); + for ( ; boundaryLink == linksEnd && adj != adjacentFaces.end(); ++adj ) + boundaryLink = adj->first->GetBoundaryLink( links, *(adj->second), 0, nodeToContain, + isAdjacentUsed, nbRecursionsLeft-1); + if ( isAdjacentUsed ) *isAdjacentUsed = true; + } + return boundaryLink; + } + //================================================================================ + /*! + * \brief Return a link ending at the given node but not avoidLink + */ + //================================================================================ + + TLinkInSet QFace::GetLinkByNode( const TLinkSet& links, + const TChainLink& avoidLink, + const SMDS_MeshNode* nodeToContain) const + { + for ( int i = 0; i < _sides.size(); ++i ) + if ( avoidLink._qlink != _sides[i] && + (_sides[i]->node1() == nodeToContain || _sides[i]->node2() == nodeToContain )) + return links.find( _sides[ i ]); + return links.end(); + } + + //================================================================================ + /*! + * \brief Return normal to the i-th side pointing outside the face + */ + //================================================================================ + + gp_Vec QFace::LinkNorm(const int i, SMESH_MesherHelper* /*uvHelper*/) const + { + gp_Vec norm, vecOut; +// if ( uvHelper ) { +// TopoDS_Face face = TopoDS::Face( uvHelper->GetSubShape()); +// const SMDS_MeshNode* inFaceNode = uvHelper->GetNodeUVneedInFaceNode() ? GetNodeInFace() : 0; +// gp_XY uv1 = uvHelper->GetNodeUV( face, _sides[i]->node1(), inFaceNode ); +// gp_XY uv2 = uvHelper->GetNodeUV( face, _sides[i]->node2(), inFaceNode ); +// norm.SetCoord( uv1.Y() - uv2.Y(), uv2.X() - uv1.X(), 0 ); + +// const QLink* otherLink = _sides[(i + 1) % _sides.size()]; +// const SMDS_MeshNode* otherNode = +// otherLink->node1() == _sides[i]->node1() ? otherLink->node2() : otherLink->node1(); +// gp_XY pIn = uvHelper->GetNodeUV( face, otherNode, inFaceNode ); +// vecOut.SetCoord( uv1.X() - pIn.X(), uv1.Y() - pIn.Y(), 0 ); +// } +// else { + norm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2())); + gp_XYZ pIn = ( XYZ( _sides[0]->node1() ) + + XYZ( _sides[0]->node2() ) + + XYZ( _sides[1]->node1() )) / 3.; + vecOut.SetXYZ( _sides[i]->MiddlePnt() - pIn ); + //} + if ( norm * vecOut < 0 ) + norm.Reverse(); + double mag2 = norm.SquareMagnitude(); + if ( mag2 > numeric_limits::min() ) + norm /= sqrt( mag2 ); + return norm; + } + //================================================================================ + /*! + * \brief Move medium node of theLink according to its distance from boundary + * \param theLink - link to fix + * \param theRefVec - movement of boundary + * \param theLinks - all adjacent links of continous triangles + * \param theFaceHelper - helper is not used so far + * \param thePrevLen - distance from the boundary + * \param theStep - number of steps till movement propagation limit + * \param theLinkNorm - out normal to theLink + * \param theSign - 1 or -1 depending on movement of boundary + * \retval double - distance from boundary to propagation limit or other boundary + */ + //================================================================================ + + double QFace::MoveByBoundary( const TChainLink& theLink, + const gp_Vec& theRefVec, + const TLinkSet& theLinks, + SMESH_MesherHelper* theFaceHelper, + const double thePrevLen, + const int theStep, + gp_Vec* theLinkNorm, + double theSign) const + { + if ( !theStep ) + return thePrevLen; // propagation limit reached + + int iL; // index of theLink + for ( iL = 0; iL < _sides.size(); ++iL ) + if ( theLink._qlink == _sides[ iL ]) + break; + + MSG(string(theStep,'.')<<" Ref( "<NextFace( this ); // adjacent faces + const QFace* f2 = link2->NextFace( this ); + + // propagate to adjacent faces till limit step or boundary + double len1 = thePrevLen + (theLink->MiddlePnt() - _sides[iL1]->MiddlePnt()).Modulus(); + double len2 = thePrevLen + (theLink->MiddlePnt() - _sides[iL2]->MiddlePnt()).Modulus(); + gp_Vec linkDir1(0,0,0); // initialize to avoid valgrind error ("Conditional jump...") + gp_Vec linkDir2(0,0,0); + try { + OCC_CATCH_SIGNALS; + if ( f1 && theLink->MediumPos() <= (*link1)->MediumPos() ) + len1 = f1->MoveByBoundary + ( *link1, theRefVec, theLinks, theFaceHelper, len1, theStep-1, &linkDir1, theSign); + else + linkDir1 = LinkNorm( iL1/*, theFaceHelper*/ ); + } catch (...) { + MSG( " --------------- EXCEPTION"); + return thePrevLen; + } + try { + OCC_CATCH_SIGNALS; + if ( f2 && theLink->MediumPos() <= (*link2)->MediumPos() ) + len2 = f2->MoveByBoundary + ( *link2, theRefVec, theLinks, theFaceHelper, len2, theStep-1, &linkDir2, theSign); + else + linkDir2 = LinkNorm( iL2/*, theFaceHelper*/ ); + } catch (...) { + MSG( " --------------- EXCEPTION"); + return thePrevLen; + } + + double fullLen = 0; + if ( theStep != theFirstStep ) + { + // choose chain length by direction of propagation most codirected with theRefVec + bool choose1 = ( theRefVec * linkDir1 * theSign > theRefVec * linkDir2 * theSign ); + fullLen = choose1 ? len1 : len2; + double r = thePrevLen / fullLen; + + gp_Vec move = linkNorm * refProj * ( 1 - r ); + theLink->Move( move, true ); + + MSG(string(theStep,'.')<<" Move "<< theLink->_mediumNode->GetID()<< + " by " << refProj * ( 1 - r ) << " following " << + (choose1 ? *link1->_qlink : *link2->_qlink)); + + if ( theLinkNorm ) *theLinkNorm = linkNorm; + } + return fullLen; + } + + //================================================================================ + /*! + * \brief Checks if the face is distorted due to bentLink + */ + //================================================================================ + + bool QFace::IsSpoiled(const QLink* bentLink ) const + { + // code is valid for convex faces only + gp_XYZ gc(0,0,0); + for ( TIDSortedNodeSet::const_iterator n = begin(); n!=end(); ++n) + gc += XYZ( *n ) / size(); + for (unsigned i = 0; i < _sides.size(); ++i ) + { + if ( _sides[i] == bentLink ) continue; + gp_Vec linkNorm = _normal ^ gp_Vec( XYZ(_sides[i]->node1()), XYZ(_sides[i]->node2())); + gp_Vec vecOut( gc, _sides[i]->MiddlePnt() ); + if ( linkNorm * vecOut < 0 ) + linkNorm.Reverse(); + double mag2 = linkNorm.SquareMagnitude(); + if ( mag2 > numeric_limits::min() ) + linkNorm /= sqrt( mag2 ); + gp_Vec vecBent ( _sides[i]->MiddlePnt(), bentLink->MediumPnt()); + gp_Vec vecStraight( _sides[i]->MiddlePnt(), bentLink->MiddlePnt()); + if ( vecBent * linkNorm > -0.1*vecStraight.Magnitude() ) + return true; + } + return false; + + } + + //================================================================================ + /*! + * \brief Find pairs of continues faces + */ + //================================================================================ + + void QLink::SetContinuesFaces() const + { + // x0 x - QLink, [-|] - QFace, v - volume + // v0 | v1 + // | Between _faces of link x2 two vertical faces are continues + // x1----x2-----x3 and two horizontal faces are continues. We set vertical faces + // | to _faces[0] and _faces[1] and horizontal faces to + // v2 | v3 _faces[2] and _faces[3] (or vise versa). + // x4 + + if ( _faces.empty() ) + return; + int iFaceCont = -1, nbBoundary = 0, iBoundary[2]={-1,-1}; + if ( _faces[0]->IsBoundary() ) + iBoundary[ nbBoundary++ ] = 0; + for ( int iF = 1; iFaceCont < 0 && iF < _faces.size(); ++iF ) + { + // look for a face bounding none of volumes bound by _faces[0] + bool sameVol = false; + int nbVol = _faces[iF]->NbVolumes(); + for ( int iV = 0; !sameVol && iV < nbVol; ++iV ) + sameVol = ( _faces[iF]->_volumes[iV] == _faces[0]->_volumes[0] || + _faces[iF]->_volumes[iV] == _faces[0]->_volumes[1]); + if ( !sameVol ) + iFaceCont = iF; + if ( _faces[iF]->IsBoundary() ) + iBoundary[ nbBoundary++ ] = iF; + } + // Set continues faces: arrange _faces to have + // _faces[0] continues to _faces[1] + // _faces[2] continues to _faces[3] + if ( nbBoundary == 2 ) // bnd faces are continues + { + if (( iBoundary[0] < 2 ) != ( iBoundary[1] < 2 )) + { + int iNear0 = iBoundary[0] < 2 ? 1-iBoundary[0] : 5-iBoundary[0]; + std::swap( _faces[ iBoundary[1] ], _faces[iNear0] ); + } + } + else if ( iFaceCont > 0 ) // continues faces found + { + if ( iFaceCont != 1 ) + std::swap( _faces[1], _faces[iFaceCont] ); + } + else if ( _faces.size() > 1 ) // not found, set NULL by the first face + { + _faces.insert( ++_faces.begin(), 0 ); + } + } + //================================================================================ + /*! + * \brief Return a face continues to the given one + */ + //================================================================================ + + const QFace* QLink::GetContinuesFace( const QFace* face ) const + { + for ( int i = 0; i < _faces.size(); ++i ) { + if ( _faces[i] == face ) { + int iF = i < 2 ? 1-i : 5-i; + return iF < _faces.size() ? _faces[iF] : 0; + } + } + return 0; + } + //================================================================================ + /*! + * \brief True if link is on mesh boundary + */ + //================================================================================ + + bool QLink::OnBoundary() const + { + for ( int i = 0; i < _faces.size(); ++i ) + if (_faces[i] && _faces[i]->IsBoundary()) return true; + return false; + } + //================================================================================ + /*! + * \brief Return normal of link of the chain + */ + //================================================================================ + + gp_Vec TChainLink::Normal() const { + gp_Vec norm; + if (_qfaces[0]) norm = _qfaces[0]->_normal; + if (_qfaces[1]) norm += _qfaces[1]->_normal; + return norm; + } + //================================================================================ + /*! + * \brief Test link curvature taking into account size of faces + */ + //================================================================================ + + bool TChainLink::IsStraight() const + { + bool isStraight = _qlink->IsStraight(); + if ( isStraight && _qfaces[0] && !_qfaces[1] ) + { + int i = _qfaces[0]->LinkIndex( _qlink ); + int iOpp = ( i + 2 ) % _qfaces[0]->_sides.size(); + gp_XYZ mid1 = _qlink->MiddlePnt(); + gp_XYZ mid2 = _qfaces[0]->_sides[ iOpp ]->MiddlePnt(); + double faceSize2 = (mid1-mid2).SquareModulus(); + isStraight = _qlink->_nodeMove.SquareMagnitude() < 1/10./10. * faceSize2; + } + return isStraight; + } + + //================================================================================ + /*! + * \brief Move medium nodes of vertical links of pentahedrons adjacent by side faces + */ + //================================================================================ + + void fixPrism( TChain& allLinks ) + { + // separate boundary links from internal ones + typedef set QLinkSet; + QLinkSet interLinks, bndLinks1, bndLink2; + + bool isCurved = false; + for ( TChain::iterator lnk = allLinks.begin(); lnk != allLinks.end(); ++lnk ) { + if ( (*lnk)->OnBoundary() ) + bndLinks1.insert( lnk->_qlink ); + else + interLinks.insert( lnk->_qlink ); + isCurved = isCurved || !lnk->IsStraight(); + } + if ( !isCurved ) + return; // no need to move + + QLinkSet *curBndLinks = &bndLinks1, *newBndLinks = &bndLink2; + + while ( !interLinks.empty() && !curBndLinks->empty() ) + { + // propagate movement from boundary links to connected internal links + QLinkSet::iterator bnd = curBndLinks->begin(), bndEnd = curBndLinks->end(); + for ( ; bnd != bndEnd; ++bnd ) + { + const QLink* bndLink = *bnd; + for ( int i = 0; i < bndLink->_faces.size(); ++i ) // loop on faces of bndLink + { + const QFace* face = bndLink->_faces[i]; // quadrange lateral face of a prism + if ( !face ) continue; + // find and move internal link opposite to bndLink within the face + int interInd = ( face->LinkIndex( bndLink ) + 2 ) % face->_sides.size(); + const QLink* interLink = face->_sides[ interInd ]; + QLinkSet::iterator pInterLink = interLinks.find( interLink ); + if ( pInterLink == interLinks.end() ) continue; // not internal link + interLink->Move( bndLink->_nodeMove ); + // treated internal links become new boundary ones + interLinks. erase( pInterLink ); + newBndLinks->insert( interLink ); + } + } + curBndLinks->clear(); + std::swap( curBndLinks, newBndLinks ); + } + } + + //================================================================================ + /*! + * \brief Fix links of continues triangles near curved boundary + */ + //================================================================================ + + void fixTriaNearBoundary( TChain & allLinks, SMESH_MesherHelper& /*helper*/) + { + if ( allLinks.empty() ) return; + + TLinkSet linkSet( allLinks.begin(), allLinks.end()); + TLinkInSet linkIt = linkSet.begin(), linksEnd = linkSet.end(); + + for ( linkIt = linkSet.begin(); linkIt != linksEnd; ++linkIt) + { + if ( linkIt->IsBoundary() && !linkIt->IsStraight() && linkIt->_qfaces[0]) + { + // move iff a boundary link is bent towards inside of a face (issue 0021084) + const QFace* face = linkIt->_qfaces[0]; + gp_XYZ pIn = ( face->_sides[0]->MiddlePnt() + + face->_sides[1]->MiddlePnt() + + face->_sides[2]->MiddlePnt() ) / 3.; + gp_XYZ insideDir( pIn - (*linkIt)->MiddlePnt()); + bool linkBentInside = ((*linkIt)->_nodeMove.Dot( insideDir ) > 0 ); + //if ( face->IsSpoiled( linkIt->_qlink )) + if ( linkBentInside ) + face->MoveByBoundary( *linkIt, (*linkIt)->_nodeMove, linkSet ); + } + } + } + + //================================================================================ + /*! + * \brief Detect rectangular structure of links and build chains from them + */ + //================================================================================ + + enum TSplitTriaResult { + _OK, _NO_CORNERS, _FEW_ROWS, _MANY_ROWS, _NO_SIDELINK, _BAD_MIDQUAD, _NOT_RECT, + _NO_MIDQUAD, _NO_UPTRIA, _BAD_SET_SIZE, _BAD_CORNER, _BAD_START, _NO_BOTLINK, _TWISTED_CHAIN }; + + TSplitTriaResult splitTrianglesIntoChains( TChain & allLinks, + vector< TChain> & resultChains, + SMDS_TypeOfPosition pos ) + { + // put links in the set and evalute number of result chains by number of boundary links + TLinkSet linkSet; + int nbBndLinks = 0; + for ( TChain::iterator lnk = allLinks.begin(); lnk != allLinks.end(); ++lnk ) { + linkSet.insert( *lnk ); + nbBndLinks += lnk->IsBoundary(); + } + resultChains.clear(); + resultChains.reserve( nbBndLinks / 2 ); + + TLinkInSet linkIt, linksEnd = linkSet.end(); + + // find a boundary link with corner node; corner node has position pos-2 + // i.e. SMDS_TOP_VERTEX for links on faces and SMDS_TOP_EDGE for + // links in volume + SMDS_TypeOfPosition cornerPos = SMDS_TypeOfPosition(pos-2); + const SMDS_MeshNode* corner = 0; + for ( linkIt = linkSet.begin(); linkIt != linksEnd; ++linkIt ) + if ( linkIt->IsBoundary() && (corner = (*linkIt)->EndPosNode(cornerPos))) + break; + if ( !corner) + return _NO_CORNERS; + + TLinkInSet startLink = linkIt; + const SMDS_MeshNode* startCorner = corner; + vector< TChain* > rowChains; + int iCol = 0; + + while ( startLink != linksEnd) // loop on columns + { + // We suppose we have a rectangular structure like shown here. We have found a + // corner of the rectangle (startCorner) and a boundary link sharing + // |/ |/ | the startCorner (startLink). We are going to loop on rows of the + // --o---o---o structure making several chains at once. One chain (columnChain) + // |\ | /| starts at startLink and continues upward (we look at the structure + // \ | \ | / | from such point that startLink is on the bottom of the structure). + // \| \|/ | While going upward we also fill horizontal chains (rowChains) we + // --o---o---o encounter. + // /|\ |\ | + // / | \ | \ | startCorner + // | \| \|,' + // --o---o---o + // `.startLink + + if ( resultChains.size() == nbBndLinks / 2 ) + return _NOT_RECT; + resultChains.push_back( TChain() ); + TChain& columnChain = resultChains.back(); + + TLinkInSet botLink = startLink; // current horizontal link to go up from + corner = startCorner; // current corner the botLink ends at + int iRow = 0; + while ( botLink != linksEnd ) // loop on rows + { + // add botLink to the columnChain + columnChain.push_back( *botLink ); + + const QFace* botTria = botLink->_qfaces[0]; // bottom triangle bound by botLink + if ( !botTria ) + { // the column ends + if ( botLink == startLink ) + return _TWISTED_CHAIN; // issue 0020951 + linkSet.erase( botLink ); + if ( iRow != rowChains.size() ) + return _FEW_ROWS; // different nb of rows in columns + break; + } + // find the link dividing the quadrangle (midQuadLink) and vertical boundary + // link ending at (sideLink); there are two cases: + // 1) midQuadLink does not end at , then we easily find it by botTria, + // since midQuadLink is not at boundary while sideLink is. + // 2) midQuadLink ends at + bool isCase2; + TLinkInSet midQuadLink = linksEnd; + TLinkInSet sideLink = botTria->GetBoundaryLink( linkSet, *botLink, &midQuadLink, + corner, &isCase2 ); + if ( isCase2 ) { // find midQuadLink among links of botTria + midQuadLink = botTria->GetLinkByNode( linkSet, *botLink, corner ); + if ( midQuadLink->IsBoundary() ) + return _BAD_MIDQUAD; + } + if ( sideLink == linksEnd || midQuadLink == linksEnd || sideLink == midQuadLink ) + return sideLink == linksEnd ? _NO_SIDELINK : _NO_MIDQUAD; + + // fill chains + columnChain.push_back( *midQuadLink ); + if ( iRow >= rowChains.size() ) { + if ( iCol > 0 ) + return _MANY_ROWS; // different nb of rows in columns + if ( resultChains.size() == nbBndLinks / 2 ) + return _NOT_RECT; + resultChains.push_back( TChain() ); + rowChains.push_back( & resultChains.back() ); + } + rowChains[iRow]->push_back( *sideLink ); + rowChains[iRow]->push_back( *midQuadLink ); + + const QFace* upTria = midQuadLink->NextFace( botTria ); // upper tria of the rectangle + if ( !upTria) + return _NO_UPTRIA; + if ( iRow == 0 ) { + // prepare startCorner and startLink for the next column + startCorner = startLink->NextNode( startCorner ); + if (isCase2) + startLink = botTria->GetBoundaryLink( linkSet, *botLink, 0, startCorner ); + else + startLink = upTria->GetBoundaryLink( linkSet, *midQuadLink, 0, startCorner ); + // check if no more columns remains + if ( startLink != linksEnd ) { + const SMDS_MeshNode* botNode = startLink->NextNode( startCorner ); + if ( (isCase2 ? botTria : upTria)->Contains( botNode )) + startLink = linksEnd; // startLink bounds upTria or botTria + else if ( startLink == botLink || startLink == midQuadLink || startLink == sideLink ) + return _BAD_START; + } + } + // find bottom link and corner for the next row + corner = sideLink->NextNode( corner ); + // next bottom link ends at the new corner + linkSet.erase( botLink ); + botLink = upTria->GetLinkByNode( linkSet, (isCase2 ? *sideLink : *midQuadLink), corner ); + if ( botLink == linksEnd || botLink == midQuadLink || botLink == sideLink) + return _NO_BOTLINK; + if ( midQuadLink == startLink || sideLink == startLink ) + return _TWISTED_CHAIN; // issue 0020951 + linkSet.erase( midQuadLink ); + linkSet.erase( sideLink ); + + // make faces neighboring the found ones be boundary + if ( startLink != linksEnd ) { + const QFace* tria = isCase2 ? botTria : upTria; + for ( int iL = 0; iL < 3; ++iL ) { + linkIt = linkSet.find( tria->_sides[iL] ); + if ( linkIt != linksEnd ) + linkIt->RemoveFace( tria ); + } + } + if ( botLink->_qfaces[0] == upTria || botLink->_qfaces[1] == upTria ) + botLink->RemoveFace( upTria ); // make next botTria first in vector + + iRow++; + } // loop on rows + + iCol++; + } + // In the linkSet, there must remain the last links of rowChains; add them + if ( linkSet.size() != rowChains.size() ) + return _BAD_SET_SIZE; + for ( int iRow = 0; iRow < rowChains.size(); ++iRow ) { + // find the link (startLink) ending at startCorner + corner = 0; + for ( startLink = linkSet.begin(); startLink != linksEnd; ++startLink ) { + if ( (*startLink)->node1() == startCorner ) { + corner = (*startLink)->node2(); break; + } + else if ( (*startLink)->node2() == startCorner) { + corner = (*startLink)->node1(); break; + } + } + if ( startLink == linksEnd ) + return _BAD_CORNER; + rowChains[ iRow ]->push_back( *startLink ); + linkSet.erase( startLink ); + startCorner = corner; + } + + return _OK; + } +} //namespace + +//======================================================================= +/*! + * \brief Move medium nodes of faces and volumes to fix distorted elements + * \param volumeOnly - to fix nodes on faces or not, if the shape is solid + * + * Issue 0020307: EDF 992 SMESH : Linea/Quadratic with Medium Node on Geometry + */ +//======================================================================= + +void SMESH_MesherHelper::FixQuadraticElements(bool volumeOnly) +{ + // setenv NO_FixQuadraticElements to know if FixQuadraticElements() is guilty of bad conversion + if ( getenv("NO_FixQuadraticElements") ) + return; + + // 0. Apply algorithm to SOLIDs or FACEs + // ---------------------------------------------- + if ( myShape.IsNull() ) { + if ( !myMesh->HasShapeToMesh() ) return; + SetSubShape( myMesh->GetShapeToMesh() ); + +#ifdef _DEBUG_ + int nbSolids = 0; + TopTools_IndexedMapOfShape solids; + TopExp::MapShapes(myShape,TopAbs_SOLID,solids); + nbSolids = solids.Extent(); +#endif + TopTools_MapOfShape faces; // faces not in solid or in not meshed solid + for ( TopExp_Explorer f(myShape,TopAbs_FACE,TopAbs_SOLID); f.More(); f.Next() ) { + faces.Add( f.Current() ); // not in solid + } + for ( TopExp_Explorer s(myShape,TopAbs_SOLID); s.More(); s.Next() ) { + if ( myMesh->GetSubMesh( s.Current() )->IsEmpty() ) { // get faces of solid + for ( TopExp_Explorer f( s.Current(), TopAbs_FACE); f.More(); f.Next() ) + faces.Add( f.Current() ); // in not meshed solid + } + else { // fix nodes in the solid and its faces +#ifdef _DEBUG_ + MSG("FIX SOLID " << nbSolids-- << " #" << GetMeshDS()->ShapeToIndex(s.Current())); +#endif + SMESH_MesherHelper h(*myMesh); + h.SetSubShape( s.Current() ); + h.FixQuadraticElements(false); + } + } + // fix nodes on geom faces +#ifdef _DEBUG_ + int nbfaces = faces.Extent(); /*avoid "unused varianbles": */ nbfaces++, nbfaces--; +#endif + for ( TopTools_MapIteratorOfMapOfShape fIt( faces ); fIt.More(); fIt.Next() ) { + MSG("FIX FACE " << nbfaces-- << " #" << GetMeshDS()->ShapeToIndex(fIt.Key())); + SMESH_MesherHelper h(*myMesh); + h.SetSubShape( fIt.Key() ); + h.FixQuadraticElements(true); + h.ToFixNodeParameters(true); + } + //perf_print_all_meters(1); + return; + } + + // 1. Find out type of elements and get iterator on them + // --------------------------------------------------- + + SMDS_ElemIteratorPtr elemIt; + SMDSAbs_ElementType elemType = SMDSAbs_All; + + SMESH_subMesh* submesh = myMesh->GetSubMeshContaining( myShapeID ); + if ( !submesh ) + return; + if ( SMESHDS_SubMesh* smDS = submesh->GetSubMeshDS() ) { + elemIt = smDS->GetElements(); + if ( elemIt->more() ) { + elemType = elemIt->next()->GetType(); + elemIt = smDS->GetElements(); + } + } + if ( !elemIt || !elemIt->more() || elemType < SMDSAbs_Face ) + return; + + // 2. Fill in auxiliary data structures + // ---------------------------------- + + set< QLink > links; + set< QFace > faces; + set< QLink >::iterator pLink; + set< QFace >::iterator pFace; + + bool isCurved = false; + //bool hasRectFaces = false; + //set nbElemNodeSet; + SMDS_VolumeTool volTool; + + TIDSortedNodeSet apexOfPyramid; + const int apexIndex = 4; + + if ( elemType == SMDSAbs_Volume ) + { + while ( elemIt->more() ) // loop on volumes + { + const SMDS_MeshElement* vol = elemIt->next(); + if ( !vol->IsQuadratic() || !volTool.Set( vol )) + return; + double volMinSize2 = -1.; + for ( int iF = 0; iF < volTool.NbFaces(); ++iF ) // loop on faces of volume + { + int nbN = volTool.NbFaceNodes( iF ); + //nbElemNodeSet.insert( nbN ); + const SMDS_MeshNode** faceNodes = volTool.GetFaceNodes( iF ); + vector< const QLink* > faceLinks( nbN/2 ); + for ( int iN = 0; iN < nbN; iN += 2 ) // loop on links of a face + { + // store QLink + QLink link( faceNodes[iN], faceNodes[iN+2], faceNodes[iN+1] ); + pLink = links.insert( link ).first; + faceLinks[ iN/2 ] = & *pLink; + + if ( link.MediumPos() == SMDS_TOP_3DSPACE ) + { + if ( !link.IsStraight() ) + return; // already fixed + } + else if ( !isCurved ) + { + if ( volMinSize2 < 0 ) volMinSize2 = volTool.MinLinearSize2(); + isCurved = !isStraightLink( volMinSize2, link._nodeMove.SquareMagnitude() ); + } + } + // store QFace + pFace = faces.insert( QFace( faceLinks )).first; + if ( pFace->NbVolumes() == 0 ) + pFace->AddSelfToLinks(); + pFace->SetVolume( vol ); +// hasRectFaces = hasRectFaces || +// ( volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_HEXA || +// volTool.GetVolumeType() == SMDS_VolumeTool::QUAD_PENTA ); +#ifdef _DEBUG_ + if ( nbN == 6 ) + pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2],faceNodes[4]); + else + pFace->_face = GetMeshDS()->FindFace(faceNodes[0],faceNodes[2], + faceNodes[4],faceNodes[6] ); +#endif + } + // collect pyramid apexes for further correction + if ( vol->NbCornerNodes() == 5 ) + apexOfPyramid.insert( vol->GetNode( apexIndex )); + } + set< QLink >::iterator pLink = links.begin(); + for ( ; pLink != links.end(); ++pLink ) + pLink->SetContinuesFaces(); + } + else + { + while ( elemIt->more() ) // loop on faces + { + const SMDS_MeshElement* face = elemIt->next(); + if ( !face->IsQuadratic() ) + continue; + //nbElemNodeSet.insert( face->NbNodes() ); + int nbN = face->NbNodes()/2; + vector< const QLink* > faceLinks( nbN ); + for ( int iN = 0; iN < nbN; ++iN ) // loop on links of a face + { + // store QLink + QLink link( face->GetNode(iN), face->GetNode((iN+1)%nbN), face->GetNode(iN+nbN) ); + pLink = links.insert( link ).first; + faceLinks[ iN ] = & *pLink; + if ( !isCurved ) + isCurved = !link.IsStraight(); + } + // store QFace + pFace = faces.insert( QFace( faceLinks )).first; + pFace->AddSelfToLinks(); + //hasRectFaces = ( hasRectFaces || nbN == 4 ); + } + } + if ( !isCurved ) + return; // no curved edges of faces + + // 3. Compute displacement of medium nodes + // --------------------------------------- + + // two loops on QFaces: the first is to treat boundary links, the second is for internal ones + TopLoc_Location loc; + // not treat boundary of volumic submesh + int isInside = ( elemType == SMDSAbs_Volume && volumeOnly ) ? 1 : 0; + for ( ; isInside < 2; ++isInside ) { + MSG( "--------------- LOOP (inside=" << isInside << ") ------------------"); + SMDS_TypeOfPosition pos = isInside ? SMDS_TOP_3DSPACE : SMDS_TOP_FACE; + SMDS_TypeOfPosition bndPos = isInside ? SMDS_TOP_FACE : SMDS_TOP_EDGE; + + for ( pFace = faces.begin(); pFace != faces.end(); ++pFace ) { + if ( bool(isInside) == pFace->IsBoundary() ) + continue; + for ( int dir = 0; dir < 2; ++dir ) // 2 directions of propagation from the quadrangle + { + MSG( "CHAIN"); + // make chain of links connected via continues faces + int error = ERR_OK; + TChain rawChain; + if ( !pFace->GetLinkChain( dir, rawChain, pos, error) && error ==ERR_UNKNOWN ) continue; + rawChain.reverse(); + if ( !pFace->GetLinkChain( dir+2, rawChain, pos, error ) && error ==ERR_UNKNOWN ) continue; + + vector< TChain > chains; + if ( error == ERR_OK ) { // chain contains continues rectangles + chains.resize(1); + chains[0].splice( chains[0].begin(), rawChain ); + } + else if ( error == ERR_TRI ) { // chain contains continues triangles + TSplitTriaResult res = splitTrianglesIntoChains( rawChain, chains, pos ); + if ( res != _OK ) { // not quadrangles split into triangles + fixTriaNearBoundary( rawChain, *this ); + break; + } + } + else if ( error == ERR_PRISM ) { // quadrangle side faces of prisms + fixPrism( rawChain ); + break; + } + else { + continue; + } + for ( int iC = 0; iC < chains.size(); ++iC ) + { + TChain& chain = chains[iC]; + if ( chain.empty() ) continue; + if ( chain.front().IsStraight() && chain.back().IsStraight() ) { + MSG("3D straight - ignore"); + continue; + } + if ( chain.front()->MediumPos() > bndPos || + chain.back() ->MediumPos() > bndPos ) { + MSG("Internal chain - ignore"); + continue; + } + // mesure chain length and compute link position along the chain + double chainLen = 0; + vector< double > linkPos; + MSGBEG( "Link medium nodes: "); + TChain::iterator link0 = chain.begin(), link1 = chain.begin(), link2; + for ( ++link1; link1 != chain.end(); ++link1, ++link0 ) { + MSGBEG( (*link0)->_mediumNode->GetID() << "-" <<(*link1)->_mediumNode->GetID()<<" "); + double len = ((*link0)->MiddlePnt() - (*link1)->MiddlePnt()).Modulus(); + while ( len < numeric_limits::min() ) { // remove degenerated link + link1 = chain.erase( link1 ); + if ( link1 == chain.end() ) + break; + len = ((*link0)->MiddlePnt() - (*link1)->MiddlePnt()).Modulus(); + } + chainLen += len; + linkPos.push_back( chainLen ); + } + MSG(""); + if ( linkPos.size() < 2 ) + continue; + + gp_Vec move0 = chain.front()->_nodeMove; + gp_Vec move1 = chain.back ()->_nodeMove; + + TopoDS_Face face; + bool checkUV = true; + if ( !isInside ) + { + // compute node displacement of end links of chain in parametric space of face + TChainLink& linkOnFace = *(++chain.begin()); + const SMDS_MeshNode* nodeOnFace = linkOnFace->_mediumNode; + TopoDS_Shape f = GetSubShapeByNode( nodeOnFace, GetMeshDS() ); + if ( !f.IsNull() && f.ShapeType() == TopAbs_FACE ) + { + face = TopoDS::Face( f ); + Handle(Geom_Surface) surf = BRep_Tool::Surface(face,loc); + bool isStraight[2]; + for ( int is1 = 0; is1 < 2; ++is1 ) // move0 or move1 + { + TChainLink& link = is1 ? chain.back() : chain.front(); + gp_XY uvm = GetNodeUV( face, link->_mediumNode, nodeOnFace, &checkUV); + gp_XY uv1 = GetNodeUV( face, link->node1(), nodeOnFace, &checkUV); + gp_XY uv2 = GetNodeUV( face, link->node2(), nodeOnFace, &checkUV); + gp_XY uv12 = GetMiddleUV( surf, uv1, uv2); + // uvMove = uvm - uv12 + gp_XY uvMove = applyIn2D(surf, uvm, uv12, gp_XY_Subtracted, /*inPeriod=*/false); + ( is1 ? move1 : move0 ).SetCoord( uvMove.X(), uvMove.Y(), 0 ); + if ( !is1 ) // correct nodeOnFace for move1 (issue 0020919) + nodeOnFace = (*(++chain.rbegin()))->_mediumNode; + isStraight[is1] = isStraightLink( (uv2-uv1).SquareModulus(), + 10 * uvMove.SquareModulus()); + } + if ( isStraight[0] && isStraight[1] ) { + MSG("2D straight - ignore"); + continue; // straight - no need to move nodes of internal links + } + + // check if a chain is already fixed + gp_XY uvm = GetNodeUV( face, linkOnFace->_mediumNode, 0, &checkUV); + gp_XY uv1 = GetNodeUV( face, linkOnFace->node1(), nodeOnFace, &checkUV); + gp_XY uv2 = GetNodeUV( face, linkOnFace->node2(), nodeOnFace, &checkUV); + gp_XY uv12 = GetMiddleUV( surf, uv1, uv2); + if (( uvm - uv12 ).SquareModulus() > 1e-10 ) + { + MSG("Already fixed - ignore"); + continue; + } + } + } + gp_Trsf trsf; + if ( isInside || face.IsNull() ) + { + // compute node displacement of end links in their local coord systems + { + TChainLink& ln0 = chain.front(), ln1 = *(++chain.begin()); + trsf.SetTransformation( gp_Ax3( gp::Origin(), ln0.Normal(), + gp_Vec( ln0->MiddlePnt(), ln1->MiddlePnt() ))); + move0.Transform(trsf); + } + { + TChainLink& ln0 = *(++chain.rbegin()), ln1 = chain.back(); + trsf.SetTransformation( gp_Ax3( gp::Origin(), ln1.Normal(), + gp_Vec( ln0->MiddlePnt(), ln1->MiddlePnt() ))); + move1.Transform(trsf); + } + } + // compute displacement of medium nodes + link2 = chain.begin(); + link0 = link2++; + link1 = link2++; + for ( int i = 0; link2 != chain.end(); ++link0, ++link1, ++link2, ++i ) + { + double r = linkPos[i] / chainLen; + // displacement in local coord system + gp_Vec move = (1. - r) * move0 + r * move1; + if ( isInside || face.IsNull()) { + // transform to global + gp_Vec x01( (*link0)->MiddlePnt(), (*link1)->MiddlePnt() ); + gp_Vec x12( (*link1)->MiddlePnt(), (*link2)->MiddlePnt() ); + gp_Vec x = x01.Normalized() + x12.Normalized(); + trsf.SetTransformation( gp_Ax3( gp::Origin(), link1->Normal(), x), gp_Ax3() ); + move.Transform(trsf); + } + else { + // compute 3D displacement by 2D one + Handle(Geom_Surface) s = BRep_Tool::Surface(face,loc); + gp_XY oldUV = GetNodeUV( face, (*link1)->_mediumNode, 0, &checkUV); + gp_XY newUV = applyIn2D( s, oldUV, gp_XY( move.X(),move.Y()), gp_XY_Added); + gp_Pnt newPnt = s->Value( newUV.X(), newUV.Y()); + move = gp_Vec( XYZ((*link1)->_mediumNode), newPnt.Transformed(loc) ); +#ifdef _DEBUG_ + if ( (XYZ((*link1)->node1()) - XYZ((*link1)->node2())).SquareModulus() < + move.SquareMagnitude()) + { + gp_XY uv0 = GetNodeUV( face, (*link0)->_mediumNode, 0, &checkUV); + gp_XY uv2 = GetNodeUV( face, (*link2)->_mediumNode, 0, &checkUV); + MSG( "TOO LONG MOVE \t" << + "uv0: "<Move( move ); + MSG( "Move " << (*link1)->_mediumNode->GetID() << " following " + << chain.front()->_mediumNode->GetID() <<"-" + << chain.back ()->_mediumNode->GetID() << + " by " << move.Magnitude()); + } + } // loop on chains of links + } // loop on 2 directions of propagation from quadrangle + } // loop on faces + } + + // 4. Move nodes + // ------------- + +// vector vols( 100 ); +// vector volSize( 100 ); +// int nbVols; +// bool ok; + for ( pLink = links.begin(); pLink != links.end(); ++pLink ) { + if ( pLink->IsMoved() ) { + gp_Pnt p = pLink->MiddlePnt() + pLink->Move(); + GetMeshDS()->MoveNode( pLink->_mediumNode, p.X(), p.Y(), p.Z()); + // +// gp_Pnt pNew = pLink->MiddlePnt() + pLink->Move(); +// if ( pLink->MediumPos() != SMDS_TOP_3DSPACE ) +// { +// // avoid making distorted volumes near boundary +// SMDS_ElemIteratorPtr volIt = +// (*pLink)._mediumNode->GetInverseElementIterator( SMDSAbs_Volume ); +// for ( nbVols = 0; volIt->more() && volTool.Set( volIt->next() ); ++nbVols ) +// { +// vols [ nbVols ] = volTool.Element(); +// volSize[ nbVols ] = volTool.GetSize(); +// } +// gp_Pnt pOld = pLink->MediumPnt(); +// const_cast( pLink->_mediumNode )->setXYZ( pNew.X(), pNew.Y(), pNew.Z() ); +// ok = true; +// while ( nbVols-- && ok ) +// { +// volTool.Set( vols[ nbVols ]); +// ok = ( volSize[ nbVols ] * volTool.GetSize() > 1e-20 ); +// } +// if ( !ok ) +// { +// const_cast( pLink->_mediumNode )->setXYZ( pOld.X(), pOld.Y(), pOld.Z() ); +// MSG( "Do NOT move \t" << pLink->_mediumNode->GetID() +// << " because of distortion of volume " << vols[ nbVols+1 ]->GetID()); +// continue; +// } +// } +// GetMeshDS()->MoveNode( pLink->_mediumNode, pNew.X(), pNew.Y(), pNew.Z() ); + } + } + + //return; + + // issue 0020982 + // Move the apex of pyramid together with the most curved link + + TIDSortedNodeSet::iterator apexIt = apexOfPyramid.begin(); + for ( ; apexIt != apexOfPyramid.end(); ++apexIt ) + { + SMESH_TNodeXYZ apex = *apexIt; + + gp_Vec maxMove( 0,0,0 ); + double maxMoveSize2 = 0; + + // shift of node index to get medium nodes between the base nodes + const int base2MediumShift = 5; + + // find maximal movement of medium node + SMDS_ElemIteratorPtr volIt = apex._node->GetInverseElementIterator( SMDSAbs_Volume ); + vector< const SMDS_MeshElement* > pyramids; + while ( volIt->more() ) + { + const SMDS_MeshElement* pyram = volIt->next(); + if ( pyram->GetEntityType() != SMDSEntity_Quad_Pyramid ) continue; + pyramids.push_back( pyram ); + + for ( int iBase = 0; iBase < apexIndex; ++iBase ) + { + SMESH_TNodeXYZ medium = pyram->GetNode( iBase + base2MediumShift ); + if ( medium._node->GetPosition()->GetTypeOfPosition() != SMDS_TOP_3DSPACE ) + { + SMESH_TNodeXYZ n1 = pyram->GetNode( iBase ); + SMESH_TNodeXYZ n2 = pyram->GetNode( ( iBase+1 ) % 4 ); + gp_Pnt middle = 0.5 * ( n1 + n2 ); + gp_Vec move( middle, medium ); + double moveSize2 = move.SquareMagnitude(); + if ( moveSize2 > maxMoveSize2 ) + maxMove = move, maxMoveSize2 = moveSize2; + } + } + } + + // move the apex + if ( maxMoveSize2 > 1e-20 ) + { + apex += maxMove.XYZ(); + GetMeshDS()->MoveNode( apex._node, apex.X(), apex.Y(), apex.Z()); + + // move medium nodes neighboring the apex to the middle + const int base2MediumShift_2 = 9; + for ( unsigned i = 0; i < pyramids.size(); ++i ) + for ( int iBase = 0; iBase < apexIndex; ++iBase ) + { + SMESH_TNodeXYZ base = pyramids[i]->GetNode( iBase ); + const SMDS_MeshNode* medium = pyramids[i]->GetNode( iBase + base2MediumShift_2 ); + gp_XYZ middle = 0.5 * ( apex + base ); + GetMeshDS()->MoveNode( medium, middle.X(), middle.Y(), middle.Z()); + } + } + } +} + diff --git a/src/SMESH/SMESH_MesherHelper.hxx b/src/SMESH/SMESH_MesherHelper.hxx index f88f1fc08..c74dfb858 100644 --- a/src/SMESH/SMESH_MesherHelper.hxx +++ b/src/SMESH/SMESH_MesherHelper.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File: SMESH_MesherHelper.hxx // Created: 15.02.06 14:48:09 // Author: Sergey KUUL @@ -28,19 +29,34 @@ #include "SMESH_SMESH.hxx" -#include -#include +#include "SMESH_MeshEditor.hxx" // needed for many meshers #include +#include + +#include #include +#include #include -#include #include +#include + +class GeomAPI_ProjectPointOnSurf; +class GeomAPI_ProjectPointOnCurve; +class SMESH_ProxyMesh; + +typedef std::map TLinkNodeMap; +typedef std::map::iterator ItTLinkNode; + +typedef SMDS_Iterator PShapeIterator; +typedef boost::shared_ptr< PShapeIterator > PShapeIteratorPtr; + +typedef std::vector TNodeColumn; +typedef std::map< double, TNodeColumn > TParam2ColumnMap; -typedef std::pair NLink; -typedef std::map NLinkNodeMap; -typedef std::map::iterator ItNLinkNode; +typedef gp_XY (*xyFunPtr)(const gp_XY& uv1, const gp_XY& uv2); +//======================================================================= /*! * \brief It helps meshers to add elements * @@ -49,9 +65,7 @@ typedef std::map::iterator ItNLinkNode; * It defines degree of elements to create when IsQuadraticSubMesh() * is called. */ - -typedef std::vector TNodeColumn; -typedef std::map< double, TNodeColumn > TParam2ColumnMap; +//======================================================================= class SMESH_EXPORT SMESH_MesherHelper { @@ -71,28 +85,36 @@ public: * \brief Load nodes bound to face into a map of node columns * \param theParam2ColumnMap - map of node columns to fill * \param theFace - the face on which nodes are searched for - * \param theBaseEdge - the edge nodes of which are columns' bases + * \param theBaseSide - the edges holding nodes on which columns' bases * \param theMesh - the mesh containing nodes * \retval bool - false if something is wrong * * The key of the map is a normalized parameter of each - * base node on theBaseEdge. + * base node on theBaseSide. Edges in theBaseSide must be sequenced. * This method works in supposition that nodes on the face * forms a rectangular grid and elements can be quardrangles or triangles */ + static bool LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, + const TopoDS_Face& theFace, + const std::list& theBaseSide, + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh=0); + /*! + * \brief Variant of LoadNodeColumns() above with theBaseSide given by one edge + */ static bool LoadNodeColumns(TParam2ColumnMap & theParam2ColumnMap, const TopoDS_Face& theFace, const TopoDS_Edge& theBaseEdge, - SMESHDS_Mesh* theMesh); + SMESHDS_Mesh* theMesh, + SMESH_ProxyMesh* theProxyMesh=0); /*! * \brief Return support shape of a node * \param node - the node * \param meshDS - mesh DS * \retval TopoDS_Shape - found support shape */ - static const TopoDS_Shape& GetSubShapeByNode(const SMDS_MeshNode* node, - SMESHDS_Mesh* meshDS) - { return meshDS->IndexToShape( node->GetPosition()->GetShapeId() ); } + static TopoDS_Shape GetSubShapeByNode(const SMDS_MeshNode* node, + const SMESHDS_Mesh* meshDS); /*! * \brief Return a valid node index, fixing the given one if necessary @@ -106,6 +128,43 @@ public: return ind; } + /*! + * \brief Return number of unique ancestors of the shape + */ + static int NbAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType=TopAbs_SHAPE); + /*! + * \brief Return iterator on ancestors of the given type + */ + static PShapeIteratorPtr GetAncestors(const TopoDS_Shape& shape, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType); + /*! + * \brief Find a common ancestors of two shapes of the given type + */ + static TopoDS_Shape GetCommonAncestor(const TopoDS_Shape& shape1, + const TopoDS_Shape& shape2, + const SMESH_Mesh& mesh, + TopAbs_ShapeEnum ancestorType); + + /*! + * \brief Return orientation of sub-shape in the main shape + */ + static TopAbs_Orientation GetSubShapeOri(const TopoDS_Shape& shape, + const TopoDS_Shape& subShape); + + static bool IsSubShape( const TopoDS_Shape& shape, const TopoDS_Shape& mainShape ); + + static bool IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMesh ); + + static double MaxTolerance( const TopoDS_Shape& shape ); + + static bool IsClosedEdge( const TopoDS_Edge& anEdge ); + + static TopoDS_Vertex IthVertex( const bool is2nd, TopoDS_Edge anEdge, const bool CumOri=true ); + + public: // ---------- PUBLIC INSTANCE METHODS ---------- @@ -118,7 +177,7 @@ public: /*! * Check submesh for given shape: if all elements on this shape are quadratic, - * quadratic elements will be created. Also fill myNLinkNodeMap + * quadratic elements will be created. Also fill myTLinkNodeMap */ bool IsQuadraticSubMesh(const TopoDS_Shape& theShape); /*! @@ -131,6 +190,12 @@ public: */ bool GetIsQuadratic() const { return myCreateQuadratic; } + /*! + * \brief Move medium nodes of faces and volumes to fix distorted elements + * \param volumeOnly - fix nodes on geom faces or not if the shape is solid + */ + void FixQuadraticElements(bool volumeOnly=true); + /*! * \brief To set created elements on the shape set by IsQuadraticSubMesh() * or the next methods. By defaul elements are set on the shape if @@ -151,12 +216,12 @@ public: /*! * \brief Return the shape set by IsQuadraticSubMesh() or SetSubShape() */ - TopoDS_Shape GetSubShape() const { return myShape; } + const TopoDS_Shape& GetSubShape() const { return myShape; } /*! * Creates a node */ - SMDS_MeshNode* AddNode(double x, double y, double z, int ID = 0); + SMDS_MeshNode* AddNode(double x, double y, double z, int ID = 0, double u=0., double v=0.); /*! * Creates quadratic or linear edge */ @@ -171,7 +236,7 @@ public: const SMDS_MeshNode* n2, const SMDS_MeshNode* n3, const int id=0, - const bool force3d = false); + const bool force3d = false); /*! * Creates quadratic or linear quadrangle */ @@ -180,16 +245,23 @@ public: const SMDS_MeshNode* n3, const SMDS_MeshNode* n4, const int id = 0, - const bool force3d = false); + const bool force3d = false); + /*! - * Creates quadratic or linear tetraahedron + * Creates polygon, with additional nodes in quadratic mesh + */ + SMDS_MeshFace* AddPolygonalFace (const std::vector& nodes, + const int id = 0, + const bool force3d = false); + /*! + * Creates quadratic or linear tetrahedron */ SMDS_MeshVolume* AddVolume(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* n3, const SMDS_MeshNode* n4, const int id = 0, - const bool force3d = true); + const bool force3d = true); /*! * Creates quadratic or linear pyramid */ @@ -199,7 +271,7 @@ public: const SMDS_MeshNode* n4, const SMDS_MeshNode* n5, const int id = 0, - const bool force3d = true); + const bool force3d = true); /*! * Creates quadratic or linear pentahedron */ @@ -210,7 +282,7 @@ public: const SMDS_MeshNode* n5, const SMDS_MeshNode* n6, const int id = 0, - const bool force3d = true); + const bool force3d = true); /*! * Creates quadratic or linear hexahedron */ @@ -223,28 +295,124 @@ public: const SMDS_MeshNode* n7, const SMDS_MeshNode* n8, const int id = 0, - bool force3d = true); + bool force3d = true); + + /*! + * Creates LINEAR!!!!!!!!! octahedron + */ + SMDS_MeshVolume* AddVolume(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* n3, + const SMDS_MeshNode* n4, + const SMDS_MeshNode* n5, + const SMDS_MeshNode* n6, + const SMDS_MeshNode* n7, + const SMDS_MeshNode* n8, + const SMDS_MeshNode* n9, + const SMDS_MeshNode* n10, + const SMDS_MeshNode* n11, + const SMDS_MeshNode* n12, + const int id = 0, + bool force3d = true); + + /*! + * Creates polyhedron. In quadratic mesh, adds medium nodes + */ + SMDS_MeshVolume* AddPolyhedralVolume (const std::vector& nodes, + const std::vector& quantities, + const int ID=0, + const bool force3d = true); + /*! + * \brief Enables fixing node parameters on EDGEs and FACEs by + * GetNodeU(...,check=true), GetNodeUV(...,check=true), CheckNodeUV() and + * CheckNodeU() in case if a node lies on a shape set via SetSubShape(). + * Default is False + */ + void ToFixNodeParameters(bool toFix); + /*! * \brief Return U of the given node on the edge */ double GetNodeU(const TopoDS_Edge& theEdge, - const SMDS_MeshNode* theNode); + const SMDS_MeshNode* theNode, + const SMDS_MeshNode* inEdgeNode=0, + bool* check=0); /*! * \brief Return node UV on face - * \param inFaceNode - a node of element being created located inside a face + * \param inFaceNode - a node of element being created located inside a face */ gp_XY GetNodeUV(const TopoDS_Face& F, const SMDS_MeshNode* n, - const SMDS_MeshNode* inFaceNode=0) const; + const SMDS_MeshNode* inFaceNode=0, + bool* check=0) const; + /*! + * \brief Check and fix node UV on a face + * \param force - check even if checks of other nodes on this face passed OK + * \param distXYZ - returns result distance and point coordinates + * \retval bool - false if UV is bad and could not be fixed + */ + bool CheckNodeUV(const TopoDS_Face& F, + const SMDS_MeshNode* n, + gp_XY& uv, + const double tol, + const bool force=false, + double distXYZ[4]=0) const; + /*! + * \brief Check and fix node U on an edge + * \param force - check even if checks of other nodes on this edge passed OK + * \param distXYZ - returns result distance and point coordinates + * \retval bool - false if U is bad and could not be fixed + */ + bool CheckNodeU(const TopoDS_Edge& E, + const SMDS_MeshNode* n, + double& u, + const double tol, + const bool force=false, + double distXYZ[4]=0) const; + /*! + * \brief Return middle UV taking in account surface period + */ + static gp_XY GetMiddleUV(const Handle(Geom_Surface)& surface, + const gp_XY& uv1, + const gp_XY& uv2); + /*! + * \brief Define a pointer to wrapper over a function of gp_XY class, + * suitable to pass as xyFunPtr to applyIn2D(). + * For exaple gp_XY_FunPtr(Added) defines pointer gp_XY_Added to function + * calling gp_XY::Added(gp_XY), which is to be used like following + * applyIn2D(surf, uv1, uv2, gp_XY_Added) + */ +#define gp_XY_FunPtr(meth) \ + static gp_XY __gpXY_##meth (const gp_XY& uv1, const gp_XY& uv2) { return uv1.meth( uv2 ); } \ + static xyFunPtr gp_XY_##meth = & __gpXY_##meth + + /*! + * \brief Perform given operation on two 2d points in parameric space of given surface. + * It takes into account period of the surface. Use gp_XY_FunPtr macro + * to easily define pointer to function of gp_XY class. + */ + static gp_XY applyIn2D(const Handle(Geom_Surface)& surface, + const gp_XY& uv1, + const gp_XY& uv2, + xyFunPtr fun, + const bool resultInPeriod=true); + /*! * \brief Check if inFaceNode argument is necessary for call GetNodeUV(F,..) * \retval bool - return true if the face is periodic * - * if F is Null, answer about subshape set through IsQuadraticSubMesh() or + * If F is Null, answer about subshape set through IsQuadraticSubMesh() or * SetSubShape() */ bool GetNodeUVneedInFaceNode(const TopoDS_Face& F = TopoDS_Face()) const; + /*! + * \brief Return projector intitialized by given face without location, which is returned + */ + GeomAPI_ProjectPointOnSurf& GetProjector(const TopoDS_Face& F, + TopLoc_Location& loc, + double tol=0 ) const; + /*! * \brief Check if shape is a degenerated edge or it's vertex * \param subShape - edge or vertex index in SMESHDS @@ -254,6 +422,13 @@ public: */ bool IsDegenShape(const int subShape) const { return myDegenShapeIds.find( subShape ) != myDegenShapeIds.end(); } + /*! + * \brief Check if the shape set through IsQuadraticSubMesh() or SetSubShape() + * has a degenerated edges + * \retval bool - true if it has + */ + bool HasDegeneratedEdges() const { return !myDegenShapeIds.empty(); } + /*! * \brief Check if shape is a seam edge or it's vertex * \param subShape - edge or vertex index in SMESHDS @@ -302,28 +477,40 @@ public: */ double GetOtherParam(const double param) const; - /** - * Special function for search or creation medium node + /*! + * \brief Return existing or create new medium nodes between given ones + * \param force3d - true means node creation at the middle between the + * two given nodes, else node position is found on its + * supporting geometrical shape, if any. */ const SMDS_MeshNode* GetMediumNode(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const bool force3d); /*! - * Auxilary function for filling myNLinkNodeMap + * \brief Return index and type of the shape (EDGE or FACE only) to set a medium node on + */ + std::pair GetMediumPos(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2); + /*! + * \brief Add a link in my data structure */ - void AddNLinkNode(const SMDS_MeshNode* n1, + void AddTLinkNode(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2, const SMDS_MeshNode* n12); - /** - * Auxilary function for filling myNLinkNodeMap + /*! + * \brief Add many links in my data structure */ - void AddNLinkNodeMap(const NLinkNodeMap& aMap) - { myNLinkNodeMap.insert(aMap.begin(), aMap.end()); } + void AddTLinkNodeMap(const TLinkNodeMap& aMap) + { myTLinkNodeMap.insert(aMap.begin(), aMap.end()); } + + void AddTLinks(const SMDS_MeshEdge* edge); + void AddTLinks(const SMDS_MeshFace* face); + void AddTLinks(const SMDS_MeshVolume* vol); /** - * Returns myNLinkNodeMap + * Returns myTLinkNodeMap */ - const NLinkNodeMap& GetNLinkNodeMap() const { return myNLinkNodeMap; } + const TLinkNodeMap& GetTLinkNodeMap() const { return myTLinkNodeMap; } /** * Check mesh without geometry for: if all elements on this shape are quadratic, @@ -333,6 +520,8 @@ public: enum MType{ LINEAR, QUADRATIC, COMP }; MType IsQuadraticMesh(); + virtual ~SMESH_MesherHelper(); + protected: /*! @@ -343,26 +532,38 @@ protected: */ gp_Pnt2d GetUVOnSeam( const gp_Pnt2d& uv1, const gp_Pnt2d& uv2 ) const; + const SMDS_MeshNode* getMediumNodeOnComposedWire(const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + bool force3d); private: // Forbiden copy constructor SMESH_MesherHelper (const SMESH_MesherHelper& theOther) {}; // special map for using during creation of quadratic elements - NLinkNodeMap myNLinkNodeMap; + TLinkNodeMap myTLinkNodeMap; std::set< int > myDegenShapeIds; std::set< int > mySeamShapeIds; - double myPar1, myPar2; // bounds of a closed periodic surface - int myParIndex; // bounds' index (1-U, 2-V) + double myPar1[2], myPar2[2]; // U and V bounds of a closed periodic surface + int myParIndex; // bounds' index (1-U, 2-V, 3-both) + + typedef std::map< int, GeomAPI_ProjectPointOnSurf* > TID2ProjectorOnSurf; + TID2ProjectorOnSurf myFace2Projector; + typedef std::map< int, GeomAPI_ProjectPointOnCurve* > TID2ProjectorOnCurve; + TID2ProjectorOnCurve myEdge2Projector; TopoDS_Shape myShape; SMESH_Mesh* myMesh; int myShapeID; - // to create quadratic elements bool myCreateQuadratic; bool mySetElemOnShape; + bool myFixNodeParameters; + + std::map< int,bool > myNodePosShapesValidity; + bool toCheckPosOnShape(int shapeID ) const; + void setPosOnShapeValidity(int shapeID, bool ok ) const; }; diff --git a/src/SMESH/SMESH_Octree.cxx b/src/SMESH/SMESH_Octree.cxx deleted file mode 100644 index 63457088e..000000000 --- a/src/SMESH/SMESH_Octree.cxx +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESH_Octree : global Octree implementation -// File : SMESH_Octree.cxx -// Created : Tue Jan 16 16:00:00 2007 -// Author : Nicolas Geimer & Aurélien Motteux(OCC) -// Module : SMESH -// -#include "SMESH_Octree.hxx" - -//=========================================================================== -/*! - * \brief SMESH_Octree Constructor - * \param maxLevel - The level max the octree can reach (If <0 unlimited) - */ -//=========================================================================== -SMESH_Octree::SMESH_Octree (const int maxLevel, const double minBoxSize): - myChildren(NULL), - myFather(NULL), - myLevel(0), - myMaxLevel(maxLevel), - myMinBoxSize(minBoxSize), - myIsLeaf(-1) -{ - myBox = new Bnd_B3d(); -} - -//====================================== -/*! - * \brief SMESH_Octree Destructor - */ -//====================================== -SMESH_Octree::~SMESH_Octree () -{ - if(myChildren != NULL) - { - if(!myIsLeaf) - { - for(int i = 0; i<8; i++) - delete myChildren[i]; - delete[] myChildren ; - } - } - delete myBox; -} - -//=========================================================================== -/*! - * \brief Set the bounding box of the Octree - * \param box - 3d Bounding Box of the Octree - */ -//=========================================================================== -void SMESH_Octree::setBox(const Bnd_B3d* box) -{ -// delete myBox; -// myBox=new Bnd_B3d(*box); - *myBox = *box; -} - -//=========================================================================== -/*! - * \brief Set box to the 3d Bounding Box of the Octree - * \param box - Set box to the 3d Bounding Box of the Octree - */ -//=========================================================================== -void SMESH_Octree::getBox(Bnd_B3d& box) -{ -// if(box != NULL) -// delete box; -// box = new Bnd_B3d (*myBox); - box = *myBox; -} - -//=========================================================================== -/*! - * \brief Set the max level of the Octree - * \param maxLevel - The level max the octree can reach (If <0 unlimited) - */ -//=========================================================================== -void SMESH_Octree::setMaxLevel(const int maxLevel) -{myMaxLevel = maxLevel;} - - -//=========================================================================== -/*! - * \brief Compute the bigger dimension of the box - * \param box - 3d Box - * \retval double - bigger dimension of the box - */ -//=========================================================================== -double SMESH_Octree::maxSize(const Bnd_B3d* box) -{ - if(box ==NULL) - return 0; - gp_XYZ min = box->CornerMin(); - gp_XYZ max = box->CornerMax(); - gp_XYZ Size = (max - min); - double returnVal = (Size.X()>Size.Y())?Size.X():Size.Y(); - return (returnVal>Size.Z())?returnVal:Size.Z(); -} - -//============================= -/*! - * \brief Compute the Octree - */ -//============================= -void SMESH_Octree::Compute() -{ - // As soon as the Octree is a Leaf, I stop building his children - if(!isLeaf()) - buildChildren(); -} - -//================================================================= -/*! - * \brief Build the 8 children boxes and call buildChildrenData() - */ -//================================================================= -void SMESH_Octree::buildChildren() -{ - myChildren = new SMESH_Octree*[8]; - - gp_XYZ min = myBox->CornerMin(); - gp_XYZ max = myBox->CornerMax(); - gp_XYZ HSize = (max - min)/2.; - gp_XYZ mid = min + HSize; - gp_XYZ childHsize = HSize/2.; - - Standard_Real XminChild, YminChild, ZminChild; - Bnd_B3d* box; - gp_XYZ minChild; - for (int i =0; i<8; i++) - { - // We build the eight boxes, we need 2 points to do that. - // Min, and Mid - // In binary, we can write i from 0 to 7 - // For instance : - // 5 is 101, it corresponds here in coordinates to ZYX - // If coordinate is 0 in Y-> box from Ymin to Ymid - // If coordinate is 1 in Y-> box from Ymid to Ymax - // Same scheme for X and Z - // I need the minChild to build the Bnd_B3d box. - - XminChild= (i%2==0)?min.X():mid.X(); - YminChild= ((i%4)/2==0)?min.Y():mid.Y(); - ZminChild= (i<4)?min.Z():mid.Z(); - minChild.SetCoord(XminChild, YminChild, ZminChild); - - box = new Bnd_B3d(minChild+childHsize,childHsize); - // The child is of the same type than its father (For instance, a SMESH_OctreeNode) - // We allocate the memory we need fot the child - myChildren[i] = allocateOctreeChild(); - // and we assign to him its box. - myChildren[i]->setBox(box); - delete box; - } - - // After building the 8 boxes, we put the data into the children.. - buildChildrenData(); - - //After we pass to the next level of the Octree - for (int i =0; i<8; i++) - myChildren[i]->Compute(); -} diff --git a/src/SMESH/SMESH_Octree.hxx b/src/SMESH/SMESH_Octree.hxx deleted file mode 100644 index 1d40b39e6..000000000 --- a/src/SMESH/SMESH_Octree.hxx +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESH_Octree : global Octree implementation -// File : SMESH_Octree.hxx -// Created : Tue Jan 16 16:00:00 2007 -// Author : Nicolas Geimer & Aurélien Motteux (OCC) -// Module : SMESH -// -#ifndef _SMESH_OCTREE_HXX_ -#define _SMESH_OCTREE_HXX_ - -#include - -class SMESH_Octree { - -public: - // Constructor - SMESH_Octree (const int maxLevel = -1, const double minBoxSize = 0.); - - // Destructor - virtual ~SMESH_Octree (); - - // Tell if Octree is a leaf or not (has to be implemented in inherited classes) - virtual const bool isLeaf() = 0; - - // Compute the Octree - void Compute(); - - // Set the maximal level of the Octree - void setMaxLevel(const int maxLevel); - - // Set the minimal size of the Box - void setMinBoxSize(const double minBoxSize){myMinBoxSize = minBoxSize;}; - - // Set the bounding box of the Octree - void setBox(const Bnd_B3d* box); - - // Set box to the 3d Bounding Box of the Octree - void getBox(Bnd_B3d & box); - - // Compute the bigger dimension of the box - static double maxSize(const Bnd_B3d* box); - - // Return its level - int level() const { return myLevel; } - -protected: - // Constructor for children (has to be implemented in inherited classes) - virtual SMESH_Octree* allocateOctreeChild() = 0; - - // Build the 8 children boxes - void buildChildren(); - - // Build the data in the 8 children (has to be implemented in inherited classes) - virtual void buildChildrenData() = 0; - - // members - - // Box of the Octree - Bnd_B3d* myBox; - - // Array of 8 Octree children - SMESH_Octree** myChildren; - - // Point the father, set to NULL for the level 0 - SMESH_Octree* myFather; - - // Level of the Octree - int myLevel; - - // MaxLevel of the Octree - int myMaxLevel; - - // Minimal size of the Box - double myMinBoxSize; - - // Tell us if the Octree is a leaf or not (-1 if not initialized) - int myIsLeaf; -}; -#endif diff --git a/src/SMESH/SMESH_OctreeNode.cxx b/src/SMESH/SMESH_OctreeNode.cxx deleted file mode 100644 index 48e8c5a79..000000000 --- a/src/SMESH/SMESH_OctreeNode.cxx +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESH_OctreeNode : Octree with Nodes set -// inherites global class SMESH_Octree -// File : SMESH_OctreeNode.cxx -// Created : Tue Jan 16 16:00:00 2007 -// Author : Nicolas Geimer & Aurélien Motteux (OCC) -// Module : SMESH -// -#include "SMESH_OctreeNode.hxx" - -#include "SMDS_MeshNode.hxx" -#include "SMDS_SetIterator.hxx" -#include - -using namespace std; - -//=============================================================== -/*! - * \brief Constructor : Build all the Octree using Compute() - * \param theNodes - Set of nodes, the Octree is built from this nodes - * \param maxLevel - Maximum level for the leaves - * \param maxNbNodes - Maximum number of nodes, a leaf can contain - * \param minBoxSize - Minimal size of the Octree Box - */ -//================================================================ -SMESH_OctreeNode::SMESH_OctreeNode (const set & theNodes, const int maxLevel, - const int maxNbNodes , const double minBoxSize ) - :SMESH_Octree(maxLevel,minBoxSize), - myMaxNbNodes(maxNbNodes), - myNodes(theNodes) -{ - // We need to compute the first bounding box via a special method - computeBoxForFather(); - myNbNodes = myNodes.size(); - myIsLeaf = (myLevel == myMaxLevel)||(myNbNodes<=myMaxNbNodes)||(myMinBoxSize>=maxSize(myBox)); - // All the children (Boxes and Data) are computed in Compute() - Compute(); -} - -//================================================================================== -/*! - * \brief Construct an empty SMESH_OctreeNode used by SMESH_Octree::buildChildren() - */ -//================================================================================== -SMESH_Octree* SMESH_OctreeNode::allocateOctreeChild() -{ - SMESH_OctreeNode * theOctree = new SMESH_OctreeNode(); - theOctree->myFather = this; - theOctree->myLevel = myLevel + 1; - theOctree->myMaxLevel = myMaxLevel; - theOctree->myMaxNbNodes = myMaxNbNodes; - theOctree->myMinBoxSize = myMinBoxSize; - theOctree->myNbNodes = 0; - return theOctree; -} - - - -//====================================== -/*! - * \brief Compute the first bounding box - * - * We take the max/min coord of the nodes - */ -//====================================== -void SMESH_OctreeNode::computeBoxForFather() -{ - set::iterator it=myNodes.begin(); - for( ;it!=myNodes.end();it++){ - const SMDS_MeshNode* n1 = *it; - gp_XYZ p1( n1->X(), n1->Y(), n1->Z() ); - myBox->Add(p1); - } -} - -//==================================================================================== -/*! - * \brief Tell if Octree is a leaf or not (has to be implemented in inherited classes) - * \retval - True if the Octree is a leaf - */ -//==================================================================================== -const bool SMESH_OctreeNode::isLeaf() -{ - return myIsLeaf; -} - -//==================================================================================== -/*! - * \brief Tells us if Node is inside the current box with the precision "precision" - * \param Node - Node - * \param precision - The box is enlarged with this precision - * \retval bool - True if Node is in the box within precision - */ -//==================================================================================== -const bool SMESH_OctreeNode::isInside(const SMDS_MeshNode * Node, const double precision ) -{ - double X=Node->X(); - double Y=Node->Y(); - double Z=Node->Z(); - bool Out = 1 ; - if (precision<=0.) - return !(myBox->IsOut(gp_XYZ(X,Y,Z))); - Bnd_B3d BoxWithPrecision; - getBox(BoxWithPrecision); - BoxWithPrecision.Enlarge(precision); - Out=BoxWithPrecision.IsOut(gp_XYZ(X,Y,Z)); - return !(Out); -} - - -//================================================ -/*! - * \brief Set the data of the children - * Shares the father's data with each of his child - */ -//================================================ -void SMESH_OctreeNode::buildChildrenData() -{ - gp_XYZ min = myBox->CornerMin(); - gp_XYZ max = myBox->CornerMax(); - gp_XYZ mid = (min + max)/2.; - - set::iterator it=myNodes.begin(); - int ChildBoxNum; - while( it!=myNodes.end()) - { - const SMDS_MeshNode* n1 = *it; - ChildBoxNum= (n1->X()>mid.X()) + (n1->Y()>mid.Y())*2 + (n1->Z()>mid.Z())*4; - SMESH_OctreeNode* myChild = dynamic_cast (myChildren[ChildBoxNum]); - myChild->myNodes.insert(myChild->myNodes.end(),n1); - myNodes.erase( it ); - it=myNodes.begin(); - } - for (int i = 0; i<8; i++) - { - SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); - myChild->myNbNodes = (myChild->myNodes).size(); - myChild->myIsLeaf = (myChild->myLevel == myMaxLevel)||(myChild->myNbNodes<=myMaxNbNodes)||(myMinBoxSize>=maxSize(myChild->myBox)); - } -} - -//=================================================================== -/*! - * \brief Return in Result a list of Nodes potentials to be near Node - * \param Node - Node - * \param precision - precision used - * \param Result - list of Nodes potentials to be near Node - */ -//==================================================================== -void SMESH_OctreeNode::NodesAround( const SMDS_MeshNode * Node, - list* Result, const double precision) -{ - if (isInside(Node,precision)) - { - if (myIsLeaf) - { - Result->insert( Result->end(), myNodes.begin(), myNodes.end() ); - } - else - { - for(int i=0;i<8;i++) - { - SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); - myChild->NodesAround( Node, Result, precision); - } - } - } -} - - - -//============================= -/*! - * \brief Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance - * Search for all the nodes in nodes - * Static Method : no need to create an SMESH_OctreeNode - * \param nodes - set of nodes we look at, modified during research - * \param theGroupsOfNodes - list of nodes closed to each other returned - * \param theTolerance - Precision used, default value is 0.00001 - * \param maxLevel - Maximum level for SMESH_OctreeNode constructed, default value is -1 (Infinite) - * \param maxNbNodes - maximum Nodes in a Leaf of the SMESH_OctreeNode constructed, default value is 5 - */ -//============================= -void SMESH_OctreeNode::FindCoincidentNodes ( set nodes, - list< list< const SMDS_MeshNode*> >* theGroupsOfNodes, - const double theTolerance, const int maxLevel, - const int maxNbNodes) -{ - SMESH_OctreeNode* theOctreeNode = new SMESH_OctreeNode(nodes, maxLevel, maxNbNodes, theTolerance); - theOctreeNode->FindCoincidentNodes (&nodes, theTolerance, theGroupsOfNodes); - delete theOctreeNode; -} - - -//============================= -/*! - * \brief Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance - * Search for all the nodes in nodes - * \param nodes - set of nodes we look at, modified during research - * \param theTolerance - Precision used - * \param theGroupsOfNodes - list of nodes closed to each other returned - */ -//============================= -void SMESH_OctreeNode::FindCoincidentNodes ( set* nodes, - const double theTolerance, - list< list< const SMDS_MeshNode*> >* theGroupsOfNodes) -{ - set::iterator it1 = nodes->begin(); - list::iterator it2; - - while (it1 != nodes->end()) - { - const SMDS_MeshNode * n1 = *it1; - - list ListofCoincidentNodes;// Initialize the lists via a declaration, it's enough - - list * groupPtr = 0; - - // Searching for Nodes around n1 and put them in ListofCoincidentNodes - FindCoincidentNodes(n1, nodes, &ListofCoincidentNodes, theTolerance); - - // We build a list {n1 + his neigbours} and add this list in theGroupsOfNodes - for (it2=ListofCoincidentNodes.begin();it2 != ListofCoincidentNodes.end(); it2++) - { - const SMDS_MeshNode* n2 = *it2; - if ( !groupPtr ) - { - theGroupsOfNodes->push_back( list() ); - groupPtr = & theGroupsOfNodes->back(); - groupPtr->push_back( n1 ); - } - if(groupPtr->front()>n2) - groupPtr->push_front( n2 ); - else - groupPtr->push_back( n2 ); - } - if(groupPtr != 0) - groupPtr->sort(); - - nodes->erase(it1); - it1=nodes->begin(); - } -} - -//====================================================================================== -/*! - * \brief Return a list of nodes closed to Node and remove it from SetOfNodes - * \param Node - We're searching the nodes next to him. - * \param SetOfNodes - set of nodes in which we erase the found nodes - * \param Result - list of nodes closed to Node - * \param precision - Precision used - */ -//====================================================================================== -void SMESH_OctreeNode::FindCoincidentNodes( const SMDS_MeshNode * Node, - set* SetOfNodes, - list* Result, - const double precision) -{ - bool isInsideBool = isInside(Node,precision); - - if (isInsideBool) - { - // I'm only looking in the leaves, since all the nodes are stored there. - if (myIsLeaf) - { - gp_Pnt p1( Node->X(), Node->Y(), Node->Z() ); - - set myNodesCopy = myNodes; - set::iterator it = myNodesCopy.begin(); - double tol2 = precision * precision; - bool squareBool; - - while (it != myNodesCopy.end()) - { - const SMDS_MeshNode* n2 = *it; - // We're only looking at nodes with a superior Id. - if(Node->GetID() < n2->GetID()) - { - gp_Pnt p2( n2->X(), n2->Y(), n2->Z() ); - // Distance optimized computation - squareBool = (p1.SquareDistance( p2 ) <= tol2); - - // If n2 inside the SquareDistance, we add it in Result and remove it from SetOfNodes and myNodes - if(squareBool) - { - Result->insert(Result->begin(), n2); - SetOfNodes->erase( n2 ); - myNodes.erase( n2 ); - } - } - myNodesCopy.erase( it ); - it = myNodesCopy.begin(); - } - - } - else - { - // If I'm not a leaf, I'm going to see my children ! - for(int i = 0; i < 8; i++) - { - SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); - myChild->FindCoincidentNodes(Node, SetOfNodes, Result, precision); - } - } - } -} - -//================================================================================ -/*! - * \brief Return iterator over children - */ -//================================================================================ - -SMESH_OctreeNodeIteratorPtr SMESH_OctreeNode::GetChildrenIterator() -{ - return SMESH_OctreeNodeIteratorPtr - ( new SMDS_SetIterator< SMESH_OctreeNode*, SMESH_Octree** > - ( myChildren, ( isLeaf() ? myChildren : &myChildren[ 8 ] ))); -} - -//================================================================================ -/*! - * \brief Return nodes iterator - */ -//================================================================================ - -SMDS_NodeIteratorPtr SMESH_OctreeNode::GetNodeIterator() -{ - return SMDS_NodeIteratorPtr - ( new SMDS_SetIterator< SMDS_pNode, set< SMDS_pNode >::const_iterator > - ( myNodes.begin(), myNodes.end() )); -} diff --git a/src/SMESH/SMESH_OctreeNode.hxx b/src/SMESH/SMESH_OctreeNode.hxx deleted file mode 100644 index 54ab75cc8..000000000 --- a/src/SMESH/SMESH_OctreeNode.hxx +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESH_OctreeNode : Octree with Nodes set -// inherites global class SMESH_Octree -// File : SMESH_OctreeNode.hxx -// Created : Tue Jan 16 16:00:00 2007 -// Author : Nicolas Geimer & Aurélien Motteux (OCC) -// Module : SMESH -// -#ifndef _SMESH_OCTREENODE_HXX_ -#define _SMESH_OCTREENODE_HXX_ - -#include "SMESH_Octree.hxx" - -#include -#include - -#include "SMDS_ElemIterator.hxx" - -//forward declaration -class SMDS_MeshNode; -class SMESH_OctreeNode; - -typedef SMDS_Iterator SMESH_OctreeNodeIterator; -typedef boost::shared_ptr SMESH_OctreeNodeIteratorPtr; - -class SMESH_OctreeNode : public SMESH_Octree{ - -public: - - // Constructor - SMESH_OctreeNode (const std::set& theNodes, const int maxLevel = -1, - const int maxNbNodes = 5 , const double minBoxSize = 0.); - -//============================= -/*! - * \brief Empty destructor - */ -//============================= - virtual ~SMESH_OctreeNode () {}; - - // Tells us if SMESH_OctreeNode is a leaf or not (-1 = not initialiazed) - virtual const bool isLeaf(); - - // Tells us if Node is inside the current box with the precision "precision" - virtual const bool isInside(const SMDS_MeshNode * Node, const double precision = 0. ); - - // Return in Result a list of Nodes potentials to be near Node - void NodesAround( const SMDS_MeshNode * Node , std::list* Result, - const double precision = 0. ); - - // Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance - // Search for all the nodes in nodes - void FindCoincidentNodes ( std::set* nodes, - const double theTolerance, - std::list< std::list< const SMDS_MeshNode*> >* theGroupsOfNodes); - - // Static method that return in theGroupsOfNodes a list of group of nodes close to each other within - // theTolerance search for all the nodes in nodes - static void FindCoincidentNodes ( std::set nodes, - std::list< std::list< const SMDS_MeshNode*> >* theGroupsOfNodes, - const double theTolerance = 0.00001, const int maxLevel = -1, - const int maxNbNodes = 5); - /*! - * \brief Return iterator over children - */ - SMESH_OctreeNodeIteratorPtr GetChildrenIterator(); - /*! - * \brief Return nodes iterator - */ - SMDS_NodeIteratorPtr GetNodeIterator(); - /*! - * \brief Return nb nodes in a tree - */ - int NbNodes() const { return myNbNodes; } - -protected: - -//============================= -/*! - * \brief Empty constructor - */ -//============================= - SMESH_OctreeNode (){}; - - // Shares the father's data with each of his child - virtual void buildChildrenData(); - - // Compute the bounding box of the whole set of nodes myNodes (only used for OctreeNode level 0) - void computeBoxForFather(); - - // Construct an empty SMESH_OctreeNode used by SMESH_Octree::buildChildren() - virtual SMESH_Octree* allocateOctreeChild(); - - // Return in result a list of nodes closed to Node and remove it from SetOfNodes - void FindCoincidentNodes( const SMDS_MeshNode * Node, - std::set* SetOfNodes, - std::list* Result, - const double precision); - - // The max number of nodes a leaf box can contain - int myMaxNbNodes; - - // The set of nodes inside the box of the Octree (Empty if Octree is not a leaf) - std::set myNodes; - - // The number of nodes I have inside the box - int myNbNodes; -}; -#endif diff --git a/src/SMESH/SMESH_Pattern.cxx b/src/SMESH/SMESH_Pattern.cxx index 7b5746773..cc493ca34 100644 --- a/src/SMESH/SMESH_Pattern.cxx +++ b/src/SMESH/SMESH_Pattern.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Pattern.hxx // Created : Mon Aug 2 10:30:00 2004 // Author : Edward AGAPOV (eap) -// + #include "SMESH_Pattern.hxx" #include @@ -39,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -69,9 +71,12 @@ #include "SMESHDS_SubMesh.hxx" #include "SMESH_Block.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" +#include + +#include #include "utilities.h" using namespace std; @@ -209,6 +214,8 @@ bool SMESH_Pattern::Load (const char* theFileContents) { MESSAGE("Load( file ) "); + Kernel_Utils::Localizer loc; + // file structure: // ! This is a comment @@ -352,6 +359,9 @@ bool SMESH_Pattern::Load (const char* theFileContents) bool SMESH_Pattern::Save (ostream& theFile) { MESSAGE(" ::Save(file) " ); + + Kernel_Utils::Localizer loc; + if ( !IsLoaded() ) { MESSAGE(" Pattern not loaded "); return setErrorCode( ERR_SAVE_NOT_LOADED ); @@ -436,8 +446,13 @@ static gp_XY project (const SMDS_MeshNode* theNode, } double u, v, minVal = DBL_MAX; for ( int i = theProjectorPS.NbExt(); i > 0; i-- ) +#if OCC_VERSION_LARGE > 0x06040000 // Porting to OCCT6.5.1 + if ( theProjectorPS.SquareDistance( i ) < minVal ) { + minVal = theProjectorPS.SquareDistance( i ); +#else if ( theProjectorPS.Value( i ) < minVal ) { minVal = theProjectorPS.Value( i ); +#endif theProjectorPS.Point( i ).Parameter( u, v ); } return gp_XY( u, v ); @@ -456,8 +471,7 @@ template bool areNodesBound( TFaceIterator & faceItr ) while ( nIt->more() ) { const SMDS_MeshNode* node = smdsNode( nIt->next() ); - SMDS_PositionPtr pos = node->GetPosition(); - if ( !pos || !pos->GetShapeId() ) { + if (node->getshapeId() <1) { return false; } } @@ -508,6 +522,7 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, SMESHDS_Mesh * aMeshDS = theMesh->GetMeshDS(); SMESHDS_SubMesh * fSubMesh = aMeshDS->MeshElements( theFace ); + const bool isQuadMesh = aMeshDS->GetMeshInfo().NbFaces( ORDER_QUADRATIC ); SMESH_MesherHelper helper( *theMesh ); helper.SetSubShape( theFace ); @@ -576,15 +591,15 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, { myElemPointIDs.push_back( TElemDef() ); TElemDef& elemPoints = myElemPointIDs.back(); - SMDS_ElemIteratorPtr nIt = (*fIt)->nodesIterator(); - while ( nIt->more() ) + int nbNodes = (*fIt)->NbCornerNodes(); + for ( int i = 0;i < nbNodes; ++i ) { - const SMDS_MeshElement* node = nIt->next(); - TNodePointIDMap::iterator nIdIt = nodePointIDMap.find( node ); - if ( nIdIt == nodePointIDMap.end() ) + const SMDS_MeshElement* node = (*fIt)->GetNode( i ); + TNodePointIDMap::iterator nIdIt = nodePointIDMap.insert( make_pair( node, -1 )).first; + if ( nIdIt->second == -1 ) { elemPoints.push_back( iPoint ); - nodePointIDMap.insert( make_pair( node, iPoint++ )); + nIdIt->second = iPoint++; } else elemPoints.push_back( (*nIdIt).second ); @@ -728,12 +743,17 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, // loop on nodes of an edge: sort them by param on edge typedef map < double, const SMDS_MeshNode* > TParamNodeMap; TParamNodeMap paramNodeMap; + int nbMeduimNodes = 0; SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes(); while ( nIt->more() ) { - const SMDS_MeshNode* node = smdsNode( nIt->next() ); + const SMDS_MeshNode* node = nIt->next(); + if ( isQuadMesh && helper.IsMedium( node, SMDSAbs_Face )) { + ++nbMeduimNodes; + continue; + } const SMDS_EdgePosition* epos = - static_cast(node->GetPosition().get()); + static_cast(node->GetPosition()); double u = epos->GetUParameter(); paramNodeMap.insert( make_pair( u, node )); } @@ -745,7 +765,9 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, paramNodeMap.clear(); nIt = eSubMesh->GetNodes(); for ( int iNode = 0; nIt->more(); ++iNode ) { - const SMDS_MeshNode* node = smdsNode( nIt->next() ); + const SMDS_MeshNode* node = nIt->next(); + if ( isQuadMesh && helper.IsMedium( node, SMDSAbs_Face )) + continue; proj.Perform( gp_Pnt( node->X(), node->Y(), node->Z())); double u = 0; if ( proj.IsDone() ) { @@ -759,7 +781,12 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, } paramNodeMap.insert( make_pair( u, node )); } + + //rnv : To fix the bug IPAL21999 Pattern Mapping - New - collapse of pattern mesh + if ( paramNodeMap.size() != eSubMesh->NbNodes() - nbMeduimNodes ) + return setErrorCode(ERR_UNEXPECTED); } + // put U in [0,1] so that the first key-point has U==0 bool isSeam = helper.IsRealSeam( edge ); double du = l - f; @@ -842,7 +869,9 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, SMDS_NodeIteratorPtr nIt = fSubMesh->GetNodes(); while ( nIt->more() ) { - const SMDS_MeshNode* node = smdsNode( nIt->next() ); + const SMDS_MeshNode* node = nIt->next(); + if ( isQuadMesh && helper.IsMedium( node, SMDSAbs_Face )) + continue; nodePointIDMap.insert( make_pair( node, iPoint )); TPoint* p = &myPoints[ iPoint++ ]; fPoints.push_back( p ); @@ -850,7 +879,7 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, p->myInitUV = project( node, projector ); else { const SMDS_FacePosition* pos = - static_cast(node->GetPosition().get()); + static_cast(node->GetPosition()); p->myInitUV.SetCoord( pos->GetUParameter(), pos->GetVParameter() ); } p->myInitXYZ.SetCoord( p->myInitUV.X(), p->myInitUV.Y(), 0 ); @@ -868,9 +897,12 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, while ( nIt->more() ) { const SMDS_MeshNode* node = smdsNode( nIt->next() ); - iPoint = nodePointIDMap[ node ]; // point index of interest + n_id = nodePointIDMap.find( node ); + if ( n_id == nodePointIDMap.end() ) + continue; // medium node + iPoint = n_id->second; // point index of interest // for a node on a seam edge there are two points - if ( helper.IsRealSeam( node->GetPosition()->GetShapeId() ) && + if ( helper.IsRealSeam( node->getshapeId() ) && ( n_id = closeNodePointIDMap.find( node )) != not_found ) { TPoint & p1 = myPoints[ iPoint ]; @@ -881,7 +913,7 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, // find node not on a seam edge while ( nIt2->more() && !notSeamNode ) { const SMDS_MeshNode* n = smdsNode( nIt2->next() ); - if ( !helper.IsSeamShape( n->GetPosition()->GetShapeId() )) + if ( !helper.IsSeamShape( n->getshapeId() )) notSeamNode = n; } gp_Pnt2d uv = helper.GetNodeUV( theFace, node, notSeamNode ); @@ -894,6 +926,7 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, } } } + myPoints.resize( nodePointIDMap.size() + closeNodePointIDMap.size() ); myIsBoundaryPointsFound = true; } @@ -1228,7 +1261,7 @@ static bool checkQuads (const TIsoNode* node, maxLen2 = Max( maxLen2, ( n[1]->myUV - n[2]->myUV ).SquareModulus() ); } maxLen2 = Max( maxLen2, ( n[2]->myUV - node->myUV ).SquareModulus() ); - minDiag = sqrt( maxLen2 ) * PI / 60.; // ~ maxLen * Sin( 3 deg ) + minDiag = sqrt( maxLen2 ) * M_PI / 60.; // ~ maxLen * Sin( 3 deg ) } // check if newUV is behind 3 dirs: n[0]-n[1], n[1]-n[2] and n[0]-n[2] @@ -1779,7 +1812,7 @@ bool SMESH_Pattern:: double initAngle = initTgt1.Angle( initTgt2 ); double angle = node->myDir[0].Angle( node->myDir[1] ); if ( reversed ) angle = -angle; - if ( initAngle > angle && initAngle - angle > PI / 2.1 ) { + if ( initAngle > angle && initAngle - angle > M_PI / 2.1 ) { // find a close internal node TIsoNode* nClose = 0; list< TIsoNode* > testNodes; @@ -1873,7 +1906,7 @@ bool SMESH_Pattern:: for ( nIt = startNodes.begin(); nIt != startNodes.end(); nIt++ ) { - TIsoNode* prevN[2], *node = *nIt; + TIsoNode *node = *nIt; if ( node->IsUVComputed() || !node->IsMovable() ) continue; gp_XY newUV( 0, 0 ), sumDir( 0, 0 ); @@ -1934,7 +1967,6 @@ bool SMESH_Pattern:: newUV += prevNode1->myUV + dir * step[ iDir ]; } sumDir += dir; - prevN[ iDir ] = prevNode1; nbComp++; } } @@ -1996,7 +2028,7 @@ bool SMESH_Pattern:: } // define ratio bool ok = true; // <- stupid fix TO AVOID PB OF NODES WITH NULL BND NODES - double locR[2] = { 0, 0 }; +// double locR[2] = { 0, 0 }; for ( iDir = 0; iDir < 2; iDir++ ) { const int iCoord = 2 - iDir; // coord changing along an isoline @@ -2010,7 +2042,7 @@ bool SMESH_Pattern:: double par3 = bndNode2->myInitUV.Coord( iCoord ); double r = ( par2 - par1 ) / ( par3 - par1 ); r = Abs ( r - 0.5 ) * 2.0; // [0,1] - distance from the middle - locR[ iDir ] = ( 1 - r * r ) * 0.25; +// locR[ iDir ] = ( 1 - r * r ) * 0.25; } //locR[0] = locR[1] = 0.25; // intersect the 2 lines and move a node @@ -2589,8 +2621,9 @@ bool SMESH_Pattern::Apply (const SMDS_MeshFace* theFace, } // check nb of nodes - if (theFace->NbNodes() != myNbKeyPntInBoundary.front() ) { - MESSAGE( myKeyPointIDs.size() << " != " << theFace->NbNodes() ); + const int nbFaceNodes = theFace->NbCornerNodes(); + if ( nbFaceNodes != myNbKeyPntInBoundary.front() ) { + MESSAGE( myKeyPointIDs.size() << " != " << nbFaceNodes ); return setErrorCode( ERR_APPL_BAD_NB_VERTICES ); } @@ -2609,7 +2642,7 @@ bool SMESH_Pattern::Apply (const SMDS_MeshFace* theFace, list< const SMDS_MeshNode* >::iterator n = nodes.end(); SMDS_ElemIteratorPtr noIt = theFace->nodesIterator(); int iSub = 0; - while ( noIt->more() ) { + while ( noIt->more() && iSub < nbFaceNodes ) { const SMDS_MeshNode* node = smdsNode( noIt->next() ); nodes.push_back( node ); if ( iSub++ == theNodeIndexOnKeyPoint1 ) @@ -2625,7 +2658,7 @@ bool SMESH_Pattern::Apply (const SMDS_MeshFace* theFace, nodes.splice( nodes.end(), nodes, nodes.begin(), n ); } list< gp_XYZ > xyzList; - myOrderedNodes.resize( theFace->NbNodes() ); + myOrderedNodes.resize( nbFaceNodes ); for ( iSub = 0, n = nodes.begin(); n != nodes.end(); ++n ) { xyzList.push_back( gp_XYZ( (*n)->X(), (*n)->Y(), (*n)->Z() )); myOrderedNodes[ iSub++] = *n; @@ -2980,7 +3013,7 @@ bool SMESH_Pattern::Apply (SMESH_Mesh* theMesh, } // put points on links to myIdsOnBoundary, // they will be used to sew new elements on adjacent refined elements - int nbNodes = (*face)->NbNodes(), eID = nbNodes + 1; + int nbNodes = (*face)->NbCornerNodes(), eID = nbNodes + 1; for ( int i = 0; i < nbNodes; i++ ) { list< TPoint* > & linkPoints = getShapePoints( eID++ ); @@ -3140,6 +3173,8 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, myIs2D = false; SMESHDS_SubMesh * aSubMesh; + const bool isQuadMesh = theMesh->NbVolumes( ORDER_QUADRATIC ); + // load shapes in myShapeIDMap SMESH_Block block; TopoDS_Vertex v1, v2; @@ -3172,6 +3207,8 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, // store a node and a point while ( nIt->more() ) { const SMDS_MeshNode* node = smdsNode( nIt->next() ); + if ( isQuadMesh && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Volume )) + continue; nodePointIDMap.insert( make_pair( node, iPoint )); if ( block.IsVertexID( shapeID )) myKeyPointIDs.push_back( iPoint ); @@ -3206,9 +3243,11 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, nIt = aSubMesh->GetNodes(); for ( ; nIt->more(); pIt++ ) { - const SMDS_MeshNode* node = smdsNode( nIt->next() ); + const SMDS_MeshNode* node = nIt->next(); + if ( isQuadMesh && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Edge )) + continue; const SMDS_EdgePosition* epos = - static_cast(node->GetPosition().get()); + static_cast(node->GetPosition()); double u = ( epos->GetUParameter() - f ) / ( l - f ); (*pIt)->myInitXYZ.SetCoord( iCoord, isForward ? u : 1 - u ); } @@ -3232,11 +3271,12 @@ bool SMESH_Pattern::Load (SMESH_Mesh* theMesh, { SMDS_ElemIteratorPtr elemIt = aSubMesh->GetElements(); while ( elemIt->more() ) { - SMDS_ElemIteratorPtr nIt = elemIt->next()->nodesIterator(); + const SMDS_MeshElement* elem = elemIt->next(); myElemPointIDs.push_back( TElemDef() ); TElemDef& elemPoints = myElemPointIDs.back(); - while ( nIt->more() ) - elemPoints.push_back( nodePointIDMap[ nIt->next() ]); + int nbNodes = elem->NbCornerNodes(); + for ( int i = 0;i < nbNodes; ++i ) + elemPoints.push_back( nodePointIDMap[ elem->GetNode( i )]); } } @@ -3684,13 +3724,8 @@ bool SMESH_Pattern:: vector& theQuantity) { bool makePoly = false; -// cout << "FROM FACE NODES: " < bndNodeSet; - for ( int i = 0; i < theNbBndNodes; ++i ) - bndNodeSet.insert( theBndNodes[ i ]); + set< const SMDS_MeshNode* > bndNodeSet( theBndNodes, theBndNodes + theNbBndNodes); map< TNodeSet, list< list< int > > >::iterator nn_IdList; @@ -3699,12 +3734,13 @@ bool SMESH_Pattern:: if ( !myIs2D ) { // for 2D, merge only edges nn_IdList = myIdsOnBoundary.find( bndNodeSet ); if ( nn_IdList != myIdsOnBoundary.end() ) { - makePoly = true; list< int > & faceIds = nn_IdList->second.front(); - ids.insert( faceIds.begin(), faceIds.end() ); + if ( !faceIds.empty() ) { + makePoly = true; + ids.insert( faceIds.begin(), faceIds.end() ); + } } } - //bool hasIdsInFace = !ids.empty(); // add ids on links and bnd nodes int lastFreeId = Max( myXYZIdToNodeMap.rbegin()->first, theNodes.size() ); @@ -3720,22 +3756,26 @@ bool SMESH_Pattern:: bndId = nn_IdList->second.front().front(); ids.insert( bndId ); } - else + else { myXYZIdToNodeMap.insert( make_pair( bndId, theBndNodes[ iN ] )); + } faceDef.push_back( bndId ); // add ids on a link TNodeSet linkNodes; linkNodes.insert( theBndNodes[ iN ]); - linkNodes.insert( theBndNodes[ iN + 1 == theNbBndNodes ? 0 : iN + 1 ]); + linkNodes.insert( theBndNodes[ (iN + 1) % theNbBndNodes] ); nn_IdList = myIdsOnBoundary.find( linkNodes ); if ( nn_IdList != myIdsOnBoundary.end() ) { - makePoly = true; list< int > & linkIds = nn_IdList->second.front(); - ids.insert( linkIds.begin(), linkIds.end() ); - if ( isReversed( theBndNodes[ iN ], linkIds )) - faceDef.insert( faceDef.end(), linkIds.begin(), linkIds.end() ); - else - faceDef.insert( faceDef.end(), linkIds.rbegin(), linkIds.rend() ); + if ( !linkIds.empty() ) + { + makePoly = true; + ids.insert( linkIds.begin(), linkIds.end() ); + if ( isReversed( theBndNodes[ iN ], linkIds )) + faceDef.insert( faceDef.end(), linkIds.begin(), linkIds.end() ); + else + faceDef.insert( faceDef.end(), linkIds.rbegin(), linkIds.rend() ); + } } } @@ -3757,9 +3797,7 @@ bool SMESH_Pattern:: { if ( !checkedVolDefs.insert( *pIdList ).second ) continue; // skip already checked volume definition - vector< int > idVec; - idVec.reserve( (*pIdList)->size() ); - idVec.insert( idVec.begin(), (*pIdList)->begin(), (*pIdList)->end() ); + vector< int > idVec( (*pIdList)->begin(), (*pIdList)->end() ); // loop on face defs of a volume SMDS_VolumeTool::VolumeType volType = vol.GetType( idVec.size() ); if ( volType == SMDS_VolumeTool::UNKNOWN ) @@ -3789,7 +3827,7 @@ bool SMESH_Pattern:: } if ( !defsAdded ) { theQuantity.push_back( faceDef.size() ); - theFaceDefs.splice( theFaceDefs.end(), faceDef, faceDef.begin(), faceDef.end() ); + theFaceDefs.splice( theFaceDefs.end(), faceDef ); } return makePoly; @@ -3853,7 +3891,7 @@ void SMESH_Pattern::clearMesh(SMESH_Mesh* theMesh) const // coordinates computed by either of Apply...() methods // WARNING : StdMeshers_Projection_... relies on MakeMesh() behavior: that // it does not care of nodes and elements already existing on -// subshapes. DO NOT MERGE them or modify also StdMeshers_Projection_.. +// sub-shapes. DO NOT MERGE them or modify also StdMeshers_Projection_.. //======================================================================= bool SMESH_Pattern::MakeMesh(SMESH_Mesh* theMesh, @@ -3922,7 +3960,7 @@ bool SMESH_Pattern::MakeMesh(SMESH_Mesh* theMesh, point->myXYZ.Z()); nodesVector [ pIndex ] = node; - if ( true /*subMeshDS*/ ) { + if ( !S.IsNull() /*subMeshDS*/ ) { // !!!!! do not merge new nodes with ones existing on submeshes (see method comment) switch ( S.ShapeType() ) { case TopAbs_VERTEX: { @@ -3960,6 +3998,8 @@ bool SMESH_Pattern::MakeMesh(SMESH_Mesh* theMesh, createElements( theMesh, nodesVector, myElemPointIDs, myElements ); } + aMeshDS->compactMesh(); + // const map& sm = aMeshDS->SubMeshes(); // map::const_iterator i_sm = sm.begin(); // for ( ; i_sm != sm.end(); i_sm++ ) @@ -4107,10 +4147,12 @@ void SMESH_Pattern::createElements(SMESH_Mesh* theMes SMDS_ElemIteratorPtr noIt = elem->nodesIterator(); while ( noIt->more() ) { SMDS_MeshNode* node = const_cast(smdsNode( noIt->next() )); - if (!node->GetPosition()->GetShapeId() && + if (!node->getshapeId() && shellNodes.find( node ) == shellNodes.end() ) { if ( S.ShapeType() == TopAbs_FACE ) - aMeshDS->SetNodeOnFace( node, shapeID ); + aMeshDS->SetNodeOnFace( node, shapeID, + Precision::Infinite(),// <- it's a sign that UV is not set + Precision::Infinite()); else { aMeshDS->SetNodeInVolume( node, shapeID ); shellNodes.insert( node ); @@ -4540,6 +4582,29 @@ void SMESH_Pattern::Clear() myShapeIDMap.Clear(); myShape.Nullify(); myNbKeyPntInBoundary.clear(); + + myXYZ.clear(); + myElemXYZIDs.clear(); + myXYZIdToNodeMap.clear(); + myElements.clear(); + myOrderedNodes.clear(); + myPolyElems.clear(); + myPolyElemXYZIDs.clear(); + myPolyhedronQuantities.clear(); + myIdsOnBoundary.clear(); + myReverseConnectivity.clear(); +} + +//================================================================================ +/*! + * \brief set ErrorCode and return true if it is Ok + */ +//================================================================================ + +bool SMESH_Pattern::setErrorCode( const ErrorCode theErrorCode ) +{ + myErrorCode = theErrorCode; + return myErrorCode == ERR_OK; } //======================================================================= diff --git a/src/SMESH/SMESH_Pattern.hxx b/src/SMESH/SMESH_Pattern.hxx index ee87cbfb8..2ba7666aa 100644 --- a/src/SMESH/SMESH_Pattern.hxx +++ b/src/SMESH/SMESH_Pattern.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Pattern.hxx // Created : Mon Aug 2 10:30:00 2004 // Author : Edward AGAPOV (eap) @@ -190,7 +191,9 @@ class SMESH_EXPORT SMESH_Pattern { // Apply(mesh_face) ERR_APPLF_BAD_FACE_GEOM, // bad face geometry // MakeMesh - ERR_MAKEM_NOT_COMPUTED // mapping failed + ERR_MAKEM_NOT_COMPUTED, // mapping failed + //Unexpected error + ERR_UNEXPECTED // Unexpected of the pattern mapping alorithm }; ErrorCode GetErrorCode() const { return myErrorCode; } @@ -241,8 +244,7 @@ private: }; friend std::ostream & operator <<(std::ostream & OS, const TPoint& p); - bool setErrorCode( const ErrorCode theErrorCode ) - { myErrorCode = theErrorCode; return myErrorCode == ERR_OK; } + bool setErrorCode( const ErrorCode theErrorCode ); // set ErrorCode and return true if it is Ok bool setShapeToMesh(const TopoDS_Shape& theShape); diff --git a/src/SMESH/SMESH_ProxyMesh.cxx b/src/SMESH/SMESH_ProxyMesh.cxx new file mode 100644 index 000000000..a4e4f5998 --- /dev/null +++ b/src/SMESH/SMESH_ProxyMesh.cxx @@ -0,0 +1,545 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_ProxyMesh.cxx +// Created : Thu Dec 2 12:32:53 2010 +// Author : Edward AGAPOV (eap) + +#include "SMESH_ProxyMesh.hxx" + +#include "SMDS_IteratorOnIterators.hxx" +#include "SMDS_SetIterator.hxx" +#include "SMESH_MesherHelper.hxx" + +#include +#include +#include + +//================================================================================ +/*! + * \brief Constructor; mesh must be set by a descendant class + */ +//================================================================================ + +SMESH_ProxyMesh::SMESH_ProxyMesh():_mesh(0) +{ +} +//================================================================================ +/*! + * \brief Make a proxy mesh from components. Components become empty + */ +//================================================================================ + +SMESH_ProxyMesh::SMESH_ProxyMesh(vector& components): + _mesh(0) +{ + if ( components.empty() ) return; + + for ( unsigned i = 0; i < components.size(); ++i ) + { + SMESH_ProxyMesh* m = components[i].get(); + if ( !m ) continue; + + takeTmpElemsInMesh( m ); + + if ( !_mesh ) _mesh = m->_mesh; + if ( _allowedTypes.empty() ) _allowedTypes = m->_allowedTypes; + + if ( _subMeshes.size() < m->_subMeshes.size() ) + _subMeshes.resize( m->_subMeshes.size(), 0 ); + for ( unsigned j = 0; j < m->_subMeshes.size(); ++j ) + { + if ( !m->_subMeshes[j] ) continue; + if ( _subMeshes[j] ) + { + // unite 2 sub-meshes + set< const SMDS_MeshElement * > elems( _subMeshes[j]->_elements.begin(), + _subMeshes[j]->_elements.end()); + elems.insert( m->_subMeshes[j]->_elements.begin(), + m->_subMeshes[j]->_elements.end()); + _subMeshes[j]->_elements.assign( elems.begin(), elems.end() ); + m->_subMeshes[j]->_elements.clear(); + + if ( !_subMeshes[j]->_n2n ) + _subMeshes[j]->_n2n = m->_subMeshes[j]->_n2n, m->_subMeshes[j]->_n2n = 0; + + else if ( _subMeshes[j]->_n2n && m->_subMeshes[j]->_n2n ) + _subMeshes[j]->_n2n->insert( m->_subMeshes[j]->_n2n->begin(), + m->_subMeshes[j]->_n2n->end()); + } + else + { + _subMeshes[j] = m->_subMeshes[j]; + m->_subMeshes[j] = 0; + } + } + } +} + +//================================================================================ +/*! + * \brief Destructor deletes proxy submeshes and tmp elemens + */ +//================================================================================ + +SMESH_ProxyMesh::~SMESH_ProxyMesh() +{ + for ( unsigned i = 0; i < _subMeshes.size(); ++i ) + delete _subMeshes[i]; + _subMeshes.clear(); + + set< const SMDS_MeshElement* >::iterator i = _elemsInMesh.begin(); + for ( ; i != _elemsInMesh.end(); ++i ) + GetMeshDS()->RemoveFreeElement( *i, 0 ); + _elemsInMesh.clear(); +} + +//================================================================================ +/*! + * \brief Returns index of a shape + */ +//================================================================================ + +int SMESH_ProxyMesh::shapeIndex(const TopoDS_Shape& shape) const +{ + return ( shape.IsNull() || !_mesh->HasShapeToMesh() ? 0 : GetMeshDS()->ShapeToIndex(shape)); +} + +//================================================================================ +/*! + * \brief Returns the submesh of a shape; it can be a proxy sub-mesh + */ +//================================================================================ + +const SMESHDS_SubMesh* SMESH_ProxyMesh::GetSubMesh(const TopoDS_Shape& shape) const +{ + const SMESHDS_SubMesh* sm = 0; + + int i = shapeIndex(shape); + if ( i < _subMeshes.size() ) + sm = _subMeshes[i]; + if ( !sm ) + sm = GetMeshDS()->MeshElements( i ); + + return sm; +} + +//================================================================================ +/*! + * \brief Returns the proxy sub-mesh of a shape; it can be NULL + */ +//================================================================================ + +const SMESH_ProxyMesh::SubMesh* +SMESH_ProxyMesh::GetProxySubMesh(const TopoDS_Shape& shape) const +{ + int i = shapeIndex(shape); + return i < _subMeshes.size() ? _subMeshes[i] : 0; +} + +//================================================================================ +/*! + * \brief Returns the proxy node of a node; the input node is returned if no proxy exists + */ +//================================================================================ + +const SMDS_MeshNode* SMESH_ProxyMesh::GetProxyNode( const SMDS_MeshNode* node ) const +{ + const SMDS_MeshNode* proxy = node; + if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) + { + if ( const SubMesh* proxySM = findProxySubMesh( node->getshapeId() )) + proxy = proxySM->GetProxyNode( node ); + } + else + { + TopoDS_Shape shape = SMESH_MesherHelper::GetSubShapeByNode( node, GetMeshDS()); + TopTools_ListIteratorOfListOfShape ancIt; + if ( !shape.IsNull() ) ancIt.Initialize( _mesh->GetAncestors( shape )); + for ( ; ancIt.More() && proxy == node; ancIt.Next() ) + if ( const SubMesh* proxySM = findProxySubMesh( shapeIndex(ancIt.Value()))) + proxy = proxySM->GetProxyNode( node ); + } + return proxy; +} + +namespace +{ + //================================================================================ + /*! + * \brief Iterator filtering elements by type + */ + //================================================================================ + + class TFilteringIterator : public SMDS_ElemIterator + { + SMDS_ElemIteratorPtr _iter; + const SMDS_MeshElement * _curElem; + vector< SMDSAbs_EntityType> _okTypes; + public: + TFilteringIterator( const vector< SMDSAbs_EntityType>& okTypes, + const SMDS_ElemIteratorPtr& elemIterator) + :_iter(elemIterator), _curElem(0), _okTypes(okTypes) + { + next(); + } + virtual bool more() + { + return _curElem; + } + virtual const SMDS_MeshElement* next() + { + const SMDS_MeshElement* res = _curElem; + _curElem = 0; + while ( _iter->more() && !_curElem ) + { + _curElem = _iter->next(); + if ( find( _okTypes.begin(), _okTypes.end(), _curElem->GetEntityType()) == _okTypes.end()) + _curElem = 0; + } + return res; + } + }; +} + +//================================================================================ +/*! + * \brief Returns iterator on all faces on the shape taking into account substitutions + */ +//================================================================================ + +SMDS_ElemIteratorPtr SMESH_ProxyMesh::GetFaces(const TopoDS_Shape& shape) const +{ + if ( !_mesh->HasShapeToMesh() ) + return SMDS_ElemIteratorPtr(); + + _subContainer.RemoveAllSubmeshes(); + + TopTools_IndexedMapOfShape FF; + TopExp::MapShapes( shape, TopAbs_FACE, FF ); + for ( int i = 1; i <= FF.Extent(); ++i ) + if ( const SMESHDS_SubMesh* sm = GetSubMesh( FF(i))) + _subContainer.AddSubMesh( sm ); + + return _subContainer.SMESHDS_SubMesh::GetElements(); +} + +//================================================================================ +/*! + * \brief Returns iterator on all faces of the mesh taking into account substitutions + * To be used in case of mesh without shape + */ +//================================================================================ + +SMDS_ElemIteratorPtr SMESH_ProxyMesh::GetFaces() const +{ + if ( _mesh->HasShapeToMesh() ) + return SMDS_ElemIteratorPtr(); + + _subContainer.RemoveAllSubmeshes(); + for ( unsigned i = 0; i < _subMeshes.size(); ++i ) + if ( _subMeshes[i] ) + _subContainer.AddSubMesh( _subMeshes[i] ); + + if ( _subContainer.NbSubMeshes() == 0 ) // no elements substituted + return GetMeshDS()->elementsIterator(SMDSAbs_Face); + + // if _allowedTypes is empty, only elements from _subMeshes are returned,... + SMDS_ElemIteratorPtr proxyIter = _subContainer.SMESHDS_SubMesh::GetElements(); + if ( _allowedTypes.empty() || NbFaces() == _mesh->NbFaces() ) + return proxyIter; + + // ... else elements filtered using allowedTypes are additionally returned + SMDS_ElemIteratorPtr facesIter = GetMeshDS()->elementsIterator(SMDSAbs_Face); + SMDS_ElemIteratorPtr filterIter( new TFilteringIterator( _allowedTypes, facesIter )); + vector< SMDS_ElemIteratorPtr > iters(2); + iters[0] = proxyIter; + iters[1] = filterIter; + + typedef vector< SMDS_ElemIteratorPtr > TElemIterVector; + typedef SMDS_IteratorOnIterators TItersIter; + return SMDS_ElemIteratorPtr( new TItersIter( iters )); +} + +//================================================================================ +/*! + * \brief Return total nb of faces taking into account substitutions + */ +//================================================================================ + +int SMESH_ProxyMesh::NbFaces() const +{ + int nb = 0; + if ( _mesh->HasShapeToMesh() ) + { + TopTools_IndexedMapOfShape FF; + TopExp::MapShapes( _mesh->GetShapeToMesh(), TopAbs_FACE, FF ); + for ( int i = 1; i <= FF.Extent(); ++i ) + if ( const SMESHDS_SubMesh* sm = GetSubMesh( FF(i))) + nb += sm->NbElements(); + } + else + { + if ( _subMeshes.empty() ) + return GetMeshDS()->NbFaces(); + + for ( unsigned i = 0; i < _subMeshes.size(); ++i ) + if ( _subMeshes[i] ) + nb += _subMeshes[i]->NbElements(); + + // if _allowedTypes is empty, only elements from _subMeshes are returned, + // else elements filtered using allowedTypes are additionally returned + if ( !_allowedTypes.empty() ) + { + for ( int t = SMDSEntity_Triangle; t <= SMDSEntity_Quad_Quadrangle; ++t ) + { + bool allowed = + ( find( _allowedTypes.begin(), _allowedTypes.end(), t ) != _allowedTypes.end() ); + if ( allowed ) + nb += GetMeshDS()->GetMeshInfo().NbEntities( SMDSAbs_EntityType( t )); + } + } + } + return nb; +} + +//================================================================================ +/*! + * \brief Returns a proxy sub-mesh; it is created if not yet exists + */ +//================================================================================ + +SMESH_ProxyMesh::SubMesh* SMESH_ProxyMesh::getProxySubMesh(int index) +{ + if ( int(_subMeshes.size()) <= index ) + _subMeshes.resize( index+1, 0 ); + if ( !_subMeshes[index] ) + _subMeshes[index] = new SubMesh( index ); + return _subMeshes[index]; +} + +//================================================================================ +/*! + * \brief Returns a proxy sub-mesh; it is created if not yet exists + */ +//================================================================================ + +SMESH_ProxyMesh::SubMesh* SMESH_ProxyMesh::getProxySubMesh(const TopoDS_Shape& shape) +{ + return getProxySubMesh( shapeIndex( shape )); +} + +//================================================================================ +/*! + * \brief Returns a proxy sub-mesh + */ +//================================================================================ + +SMESH_ProxyMesh::SubMesh* SMESH_ProxyMesh::findProxySubMesh(int shapeIndex) const +{ + return shapeIndex < int(_subMeshes.size()) ? _subMeshes[shapeIndex] : 0; +} + +//================================================================================ +/*! + * \brief Returns mesh DS + */ +//================================================================================ + +SMESHDS_Mesh* SMESH_ProxyMesh::GetMeshDS() const +{ + return (SMESHDS_Mesh*)( _mesh ? _mesh->GetMeshDS() : 0 ); +} + +//================================================================================ +/*! + * \brief Move proxy sub-mesh from other proxy mesh to this, returns true if sub-mesh found + */ +//================================================================================ + +bool SMESH_ProxyMesh::takeProxySubMesh( const TopoDS_Shape& shape, + SMESH_ProxyMesh* proxyMesh ) +{ + if ( proxyMesh && proxyMesh->_mesh == _mesh ) + { + int iS = shapeIndex( shape ); + if ( SubMesh* sm = proxyMesh->findProxySubMesh( iS )) + { + if ( iS >= int(_subMeshes.size()) ) + _subMeshes.resize( iS + 1, 0 ); + _subMeshes[iS] = sm; + proxyMesh->_subMeshes[iS] = 0; + return true; + } + } + return false; +} + +//================================================================================ +/*! + * \brief Move tmp elements residing the _mesh from other proxy mesh to this + */ +//================================================================================ + +void SMESH_ProxyMesh::takeTmpElemsInMesh( SMESH_ProxyMesh* proxyMesh ) +{ + if ( proxyMesh ) + { + _elemsInMesh.insert( proxyMesh->_elemsInMesh.begin(), + proxyMesh->_elemsInMesh.end()); + proxyMesh->_elemsInMesh.clear(); + } +} + +//================================================================================ +/*! + * \brief Removes tmp faces from the _mesh + */ +//================================================================================ + +void SMESH_ProxyMesh::removeTmpElement( const SMDS_MeshElement* face ) +{ + if ( face && face->GetID() > 0 ) + { + set< const SMDS_MeshElement* >::iterator i = _elemsInMesh.find( face ); + if ( i != _elemsInMesh.end() ) + { + GetMeshDS()->RemoveFreeElement( face, 0 ); + _elemsInMesh.erase( i ); + } + } + else + { + delete face; + } +} + +//================================================================================ +/*! + * \brief Stores tmp element residing the _mesh + */ +//================================================================================ + +void SMESH_ProxyMesh::storeTmpElement( const SMDS_MeshElement* face ) +{ + _elemsInMesh.insert( face ); +} + +//================================================================================ +/*! + * \brief Set node-node correspondence + */ +//================================================================================ + +void SMESH_ProxyMesh::setNode2Node(const SMDS_MeshNode* srcNode, + const SMDS_MeshNode* proxyNode, + const SubMesh* subMesh) +{ + SubMesh* sm = const_cast( subMesh ); + if ( !subMesh->_n2n ) + sm->_n2n = new TN2NMap; + sm->_n2n->insert( make_pair( srcNode, proxyNode )); +} + +//================================================================================ +/*! + * \brief Return true if the element is a temporary one + */ +//================================================================================ + +bool SMESH_ProxyMesh::IsTemporary(const SMDS_MeshElement* elem ) const +{ + return ( elem->GetID() < 1 ) || _elemsInMesh.count( elem ); +} + +//================================================================================ +/*! + * \brief Return a proxy node or an input node + */ +//================================================================================ + +const SMDS_MeshNode* SMESH_ProxyMesh::SubMesh::GetProxyNode( const SMDS_MeshNode* n ) const +{ + TN2NMap::iterator n2n; + if ( _n2n && ( n2n = _n2n->find( n )) != _n2n->end()) + return n2n->second; + return n; +} + +//================================================================================ +/*! + * \brief Deletes temporary elements + */ +//================================================================================ + +void SMESH_ProxyMesh::SubMesh::Clear() +{ + for ( unsigned i = 0; i < _elements.size(); ++i ) + if ( _elements[i]->GetID() < 0 ) + delete _elements[i]; + _elements.clear(); + if ( _n2n ) + delete _n2n, _n2n = 0; +} + +//================================================================================ +/*! + * \brief Return number of elements in a proxy submesh + */ +//================================================================================ + +int SMESH_ProxyMesh::SubMesh::NbElements() const +{ + return _elements.size(); +} + +//================================================================================ +/*! + * \brief Return elements of a proxy submesh + */ +//================================================================================ + +SMDS_ElemIteratorPtr SMESH_ProxyMesh::SubMesh::GetElements() const +{ + return SMDS_ElemIteratorPtr + ( new SMDS_ElementVectorIterator( _elements.begin(), _elements.end() )); +} + +//================================================================================ +/*! + * \brief Store an element + */ +//================================================================================ + +void SMESH_ProxyMesh::SubMesh::AddElement(const SMDS_MeshElement * e) +{ + _elements.push_back( e ); +} + +//================================================================================ +/*! + * \brief Check presence of element inside it-self + */ +//================================================================================ + +bool SMESH_ProxyMesh::SubMesh::Contains(const SMDS_MeshElement * ME) const +{ + if ( ME->GetType() != SMDSAbs_Node ) + return find( _elements.begin(), _elements.end(), ME ) != _elements.end(); + return false; +} diff --git a/src/SMESH/SMESH_ProxyMesh.hxx b/src/SMESH/SMESH_ProxyMesh.hxx new file mode 100644 index 000000000..faf67734f --- /dev/null +++ b/src/SMESH/SMESH_ProxyMesh.hxx @@ -0,0 +1,173 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESH_ProxyMesh.hxx +// Created : Thu Dec 2 10:05:35 2010 +// Author : Edward AGAPOV (eap) + +#ifndef __SMESH_ProxyMesh_HXX__ +#define __SMESH_ProxyMesh_HXX__ + +#include "SMESH_SMESH.hxx" + +#include "SMDS_MeshElement.hxx" +#include "SMESHDS_SubMesh.hxx" + +#include + +#include +#include +#include + +class SMDS_MeshNode; +class SMESHDS_Mesh; +class SMESH_Mesh; + +/*! + * \brief Container of mesh faces substituting other faces in the input mesh of 3D algorithm + */ +class SMESH_EXPORT SMESH_ProxyMesh +{ +public: + + typedef boost::shared_ptr Ptr; + + typedef std::map TN2NMap; + + //-------------------------------------------------------------------------------- + /*! + * \brief Proxy sub-mesh + */ + class SubMesh : public SMESHDS_SubMesh + { + public: + + const TN2NMap* GetNodeNodeMap() const { return _n2n; } + const SMDS_MeshNode* GetProxyNode( const SMDS_MeshNode* n ) const; + virtual void AddElement(const SMDS_MeshElement * e); + virtual int NbElements() const; + virtual SMDS_ElemIteratorPtr GetElements() const; + virtual void Clear(); + virtual bool Contains(const SMDS_MeshElement * ME) const; + + template< class ITERATOR > + void ChangeElements( ITERATOR it, ITERATOR end ) + { + // change SubMesh contents without deleting tmp faces + // for which the caller is responsible + _elements.clear(); + while ( it != end ) _elements.push_back( *it++ ); + } + SubMesh(int index=0):SMESHDS_SubMesh(0,index),_n2n(0) {} + ~SubMesh() { Clear(); } + + private: + std::vector _elements; + TN2NMap* _n2n; + friend class SMESH_ProxyMesh; + }; + //-------------------------------------------------------------------------------- + // Public interface + + SMESH_ProxyMesh(); + SMESH_ProxyMesh(std::vector& components); + SMESH_ProxyMesh(const SMESH_Mesh& mesh) { _mesh = &mesh; } + virtual ~SMESH_ProxyMesh(); + + // Returns the submesh of a face; it can be a proxy sub-mesh + const SMESHDS_SubMesh* GetSubMesh(const TopoDS_Shape& face) const; + + // Returns the proxy sub-mesh of a face; it can be NULL + const SubMesh* GetProxySubMesh(const TopoDS_Shape& face) const; + + // Returns the proxy node of a node; the input node is returned if no proxy exists + const SMDS_MeshNode* GetProxyNode( const SMDS_MeshNode* node ) const; + + // Returns iterator on all faces of the mesh taking into account substitutions + // To be used in case of mesh without shape + SMDS_ElemIteratorPtr GetFaces() const; + + // Returns iterator on all faces on the face taking into account substitutions + SMDS_ElemIteratorPtr GetFaces(const TopoDS_Shape& face) const; + + // Return total nb of faces taking into account substitutions + int NbFaces() const; + + bool IsTemporary(const SMDS_MeshElement* elem ) const; + + + + const SMESH_Mesh* GetMesh() const { return _mesh; } + + SMESHDS_Mesh* GetMeshDS() const; + + //-------------------------------------------------------------------------------- + // Interface for descendants + protected: + + void setMesh(const SMESH_Mesh& mesh) { _mesh = &mesh; } + + int shapeIndex(const TopoDS_Shape& shape) const; + + // returns a proxy sub-mesh; zero index is for the case of mesh w/o shape + SubMesh* findProxySubMesh(int shapeIndex=0) const; + + // returns a proxy sub-mesh; it is created if not yet exists + SubMesh* getProxySubMesh(int shapeIndex); + + // returns a proxy sub-mesh; it is created if not yet exists + SubMesh* getProxySubMesh(const TopoDS_Shape& shape=TopoDS_Shape()); + + // move proxy sub-mesh from other proxy mesh to this, returns true if sub-mesh found + bool takeProxySubMesh( const TopoDS_Shape& shape, SMESH_ProxyMesh* proxyMesh ); + + // move tmp elements residing the _mesh from other proxy mesh to this + void takeTmpElemsInMesh( SMESH_ProxyMesh* proxyMesh ); + + // removes tmp faces from the _mesh + void removeTmpElement( const SMDS_MeshElement* face ); + + // stores tmp element residing the _mesh + void storeTmpElement( const SMDS_MeshElement* face ); + + // store node-node correspondence + void setNode2Node(const SMDS_MeshNode* srcNode, + const SMDS_MeshNode* proxyNode, + const SubMesh* subMesh); + + // types of elements needed to implement NbFaces() and GetFaces(); + // if _allowedTypes is empty, only elements from _subMeshes are returned, + // else elements of _mesh filtered using allowedTypes are additionally returned + std::vector< SMDSAbs_EntityType> _allowedTypes; + + private: + + const SMESH_Mesh* _mesh; + + // proxy sub-meshes; index in vector == shapeIndex(shape) + std::vector< SubMesh* > _subMeshes; + + // tmp elements residing the _mesh, to be deleted at destruction + std::set< const SMDS_MeshElement* > _elemsInMesh; + + // Complex submesh used to iterate over elements in other sub-meshes + mutable SubMesh _subContainer; +}; + +#endif diff --git a/src/SMESH/SMESH_SMESH.hxx b/src/SMESH/SMESH_SMESH.hxx index 249fc6a85..977f5fa1f 100755 --- a/src/SMESH/SMESH_SMESH.hxx +++ b/src/SMESH/SMESH_SMESH.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_SMESH.hxx // Author : Alexander A. BORODIN // Module : SMESH @@ -27,7 +28,7 @@ #define _SMESH_SMESH_HXX_ #ifdef WNT - #if defined SMESH_EXPORTS + #if defined SMESHimpl_EXPORTS #define SMESH_EXPORT __declspec( dllexport ) #else #define SMESH_EXPORT __declspec( dllimport ) diff --git a/src/SMESH/SMESH_SequenceOfElemPtr.hxx b/src/SMESH/SMESH_SequenceOfElemPtr.hxx deleted file mode 100644 index 77c92ad5a..000000000 --- a/src/SMESH/SMESH_SequenceOfElemPtr.hxx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File: SMESH_SequenceOfElemPtr.hxx -// Created: 26.09.05 17:41:10 -// Author: Sergey KUUL -// -#ifndef SMESH_SequenceOfElemPtr_HeaderFile -#define SMESH_SequenceOfElemPtr_HeaderFile - -#include "SMESH_SMESH.hxx" - -#include - -#include - -typedef const SMDS_MeshElement* SMDS_MeshElementPtr; - -DEFINE_BASECOLLECTION (SMESH_BaseCollectionElemPtr, SMDS_MeshElementPtr) -DEFINE_SEQUENCE (SMESH_SequenceOfElemPtr, SMESH_BaseCollectionElemPtr, SMDS_MeshElementPtr) - -#endif diff --git a/src/SMESH/SMESH_SequenceOfNode.hxx b/src/SMESH/SMESH_SequenceOfNode.hxx deleted file mode 100644 index 4eabe0ee6..000000000 --- a/src/SMESH/SMESH_SequenceOfNode.hxx +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File: SMESH_SequenceOfNode.hxx -// Created: 11.11.05 10:00:04 -// Author: Sergey KUUL -// -#ifndef SMESH_SequenceOfNode_HeaderFile -#define SMESH_SequenceOfNode_HeaderFile - -#include "SMESH_SMESH.hxx" - -#include - -typedef const SMDS_MeshNode* SMDS_MeshNodePtr; - -DEFINE_BASECOLLECTION (SMESH_BaseCollectionNodePtr, SMDS_MeshNodePtr) -DEFINE_SEQUENCE(SMESH_SequenceOfNode, - SMESH_BaseCollectionNodePtr, SMDS_MeshNodePtr) - - -#endif diff --git a/src/SMESH/SMESH_subMesh.cxx b/src/SMESH/SMESH_subMesh.cxx index 6c484c80a..65ffac712 100644 --- a/src/SMESH/SMESH_subMesh.cxx +++ b/src/SMESH/SMESH_subMesh.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : SMESH_subMesh.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ -// + #include "SMESH_subMesh.hxx" #include "SMESH_Algo.hxx" @@ -36,9 +36,13 @@ #include "SMESH_subMeshEventListener.hxx" #include "SMESH_Comment.hxx" #include "SMDS_SetIterator.hxx" +#include "SMDSAbs_ElementType.hxx" + +#include #include "utilities.h" #include "OpUtil.hxx" +#include "Basics_Utils.hxx" #include #include @@ -54,6 +58,8 @@ #include #include +#include + using namespace std; //============================================================================= @@ -82,22 +88,22 @@ SMESH_subMesh::SMESH_subMesh(int Id, SMESHDS_Mesh * meshDS, const TopoDS_Shape & aSubShape) { - _subShape = aSubShape; - _subMeshDS = meshDS->MeshElements(_subShape); // may be null ... - _father = father; - _Id = Id; - _dependenceAnalysed = _alwaysComputed = false; - - if (_subShape.ShapeType() == TopAbs_VERTEX) - { - _algoState = HYP_OK; - _computeState = READY_TO_COMPUTE; - } - else - { + _subShape = aSubShape; + _subMeshDS = meshDS->MeshElements(_subShape); // may be null ... + _father = father; + _Id = Id; + _dependenceAnalysed = _alwaysComputed = false; + + if (_subShape.ShapeType() == TopAbs_VERTEX) + { + _algoState = HYP_OK; + _computeState = READY_TO_COMPUTE; + } + else + { _algoState = NO_ALGO; _computeState = NOT_READY; - } + } } //============================================================================= @@ -110,7 +116,7 @@ SMESH_subMesh::~SMESH_subMesh() { MESSAGE("SMESH_subMesh::~SMESH_subMesh"); // **** - DeleteOwnListeners(); + deleteOwnListeners(); } //============================================================================= @@ -171,7 +177,18 @@ SMESH_subMesh *SMESH_subMesh::GetFirstToCompute() //================================================================================ /*! - * \brief Allow algo->Compute() if a subshape of lower dim is meshed but + * \brief Returns a current algorithm + */ +//================================================================================ + +SMESH_Algo* SMESH_subMesh::GetAlgo() const +{ + return _father->GetGen()->GetAlgo(*_father, _subShape); +} + +//================================================================================ +/*! + * \brief Allow algo->Compute() if a sub-shape of lower dim is meshed but * none mesh entity is bound to it (PAL13615, 2nd part) */ //================================================================================ @@ -240,7 +257,7 @@ bool SMESH_subMesh::IsMeshComputed() const */ //============================================================================= -bool SMESH_subMesh::SubMeshesComputed() +bool SMESH_subMesh::subMeshesComputed() { int myDim = SMESH_Gen::GetShapeDim( _subShape ); int dimToCheck = myDim - 1; @@ -261,7 +278,7 @@ bool SMESH_subMesh::SubMeshesComputed() break; // the rest subMeshes are all of less dimension SMESHDS_SubMesh * ds = sm->GetSubMeshDS(); bool computeOk = (sm->GetComputeState() == COMPUTE_OK || - (ds && ( ds->NbNodes() || ds->NbElements() ))); + (ds && ( dimToCheck ? ds->NbElements() : ds->NbNodes() ))); if (!computeOk) { int type = ss.ShapeType(); @@ -324,23 +341,23 @@ bool SMESH_subMesh::SubMeshesComputed() */ //============================================================================= -bool SMESH_subMesh::SubMeshesReady() -{ - bool subMeshesReady = true; - SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,true); - while ( smIt->more() ) { - SMESH_subMesh *sm = smIt->next(); - bool computeOk = (sm->GetComputeState() == COMPUTE_OK || - sm->GetComputeState() == READY_TO_COMPUTE); - if (!computeOk) - { - subMeshesReady = false; - SCRUTE(sm->GetId()); - break; - } - } - return subMeshesReady; -} +// bool SMESH_subMesh::SubMeshesReady() +// { +// bool subMeshesReady = true; +// SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,true); +// while ( smIt->more() ) { +// SMESH_subMesh *sm = smIt->next(); +// bool computeOk = (sm->GetComputeState() == COMPUTE_OK || +// sm->GetComputeState() == READY_TO_COMPUTE); +// if (!computeOk) +// { +// subMeshesReady = false; +// SCRUTE(sm->GetId()); +// break; +// } +// } +// return subMeshesReady; +// } //============================================================================= /*! @@ -368,55 +385,53 @@ const map < int, SMESH_subMesh * >& SMESH_subMesh::DependsOn() case TopAbs_COMPOUND: { //MESSAGE("compound"); - for (TopExp_Explorer exp(_subShape, TopAbs_SOLID); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_SOLID); exp.More();exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } - for (TopExp_Explorer exp(_subShape, TopAbs_SHELL, TopAbs_SOLID); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_SHELL, TopAbs_SOLID); exp.More(); exp.Next()) { - InsertDependence(exp.Current()); //only shell not in solid + if ( BRep_Tool::IsClosed(exp.Current() )) + insertDependence(exp.Current()); //only shell not in solid + else + for (TopExp_Explorer expF(exp.Current(), TopAbs_FACE); expF.More();expF.Next()) + insertDependence(expF.Current()); // issue 0020959: HEXA_3D fails on shell + } - for (TopExp_Explorer exp(_subShape, TopAbs_FACE, TopAbs_SHELL); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_FACE, TopAbs_SHELL); exp.More();exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } - for (TopExp_Explorer exp(_subShape, TopAbs_EDGE, TopAbs_FACE); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_EDGE, TopAbs_FACE); exp.More();exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } break; } case TopAbs_COMPSOLID: { - //MESSAGE("compsolid"); - for (TopExp_Explorer exp(_subShape, TopAbs_SOLID); exp.More(); - exp.Next()) + //MESSAGE("compsolid"); + for (TopExp_Explorer exp(_subShape, TopAbs_SOLID); exp.More(); exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } break; } case TopAbs_SHELL: { //MESSAGE("shell"); - for (TopExp_Explorer exp(_subShape, TopAbs_FACE); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_FACE); exp.More(); exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } break; } case TopAbs_WIRE: { //MESSAGE("wire"); - for (TopExp_Explorer exp(_subShape, TopAbs_EDGE); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_EDGE); exp.More(); exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } break; } @@ -424,10 +439,9 @@ const map < int, SMESH_subMesh * >& SMESH_subMesh::DependsOn() { //MESSAGE("solid"); if(_father->HasShapeToMesh()) { - for (TopExp_Explorer exp(_subShape, TopAbs_FACE); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_FACE); exp.More();exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } } break; @@ -435,21 +449,19 @@ const map < int, SMESH_subMesh * >& SMESH_subMesh::DependsOn() case TopAbs_FACE: { //MESSAGE("face"); - for (TopExp_Explorer exp(_subShape, TopAbs_EDGE); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_EDGE); exp.More();exp.Next()) { - InsertDependence(exp.Current()); + insertDependence(exp.Current()); } break; } case TopAbs_EDGE: { //MESSAGE("edge"); - for (TopExp_Explorer exp(_subShape, TopAbs_VERTEX); exp.More(); - exp.Next()) + for (TopExp_Explorer exp(_subShape, TopAbs_VERTEX); exp.More(); exp.Next()) { - InsertDependence(exp.Current()); - } + insertDependence(exp.Current()); + } break; } case TopAbs_VERTEX: @@ -471,9 +483,8 @@ const map < int, SMESH_subMesh * >& SMESH_subMesh::DependsOn() */ //============================================================================= -void SMESH_subMesh::InsertDependence(const TopoDS_Shape aSubShape) +void SMESH_subMesh::insertDependence(const TopoDS_Shape aSubShape) { - //MESSAGE("SMESH_subMesh::InsertDependence"); SMESH_subMesh *aSubMesh = _father->GetSubMesh(aSubShape); int type = aSubShape.ShapeType(); int ordType = 9 - type; // 2 = Vertex, 8 = CompSolid @@ -495,8 +506,8 @@ void SMESH_subMesh::InsertDependence(const TopoDS_Shape aSubShape) const TopoDS_Shape & SMESH_subMesh::GetSubShape() const { - //MESSAGE("SMESH_subMesh::GetSubShape"); - return _subShape; + //MESSAGE("SMESH_subMesh::GetSubShape"); + return _subShape; } @@ -510,12 +521,13 @@ bool SMESH_subMesh::CanAddHypothesis(const SMESH_Hypothesis* theHypothesis) cons { int aHypDim = theHypothesis->GetDim(); int aShapeDim = SMESH_Gen::GetShapeDim(_subShape); - if (aHypDim == 3 && aShapeDim == 3) { - // check case of open shell - //if (_subShape.ShapeType() == TopAbs_SHELL && !_subShape.Closed()) - if (_subShape.ShapeType() == TopAbs_SHELL && !BRep_Tool::IsClosed(_subShape)) - return false; - } + // issue 21106. Forbid 3D mesh on the SHELL + // if (aHypDim == 3 && aShapeDim == 3) { + // // check case of open shell + // //if (_subShape.ShapeType() == TopAbs_SHELL && !_subShape.Closed()) + // if (_subShape.ShapeType() == TopAbs_SHELL && !BRep_Tool::IsClosed(_subShape)) + // return false; + // } if ( aHypDim <= aShapeDim ) return true; @@ -531,8 +543,14 @@ bool SMESH_subMesh::IsApplicableHypotesis(const SMESH_Hypothesis* theHypothesis, const TopAbs_ShapeEnum theShapeType) { if ( theHypothesis->GetType() > SMESHDS_Hypothesis::PARAM_ALGO) + { // algorithm - return ( theHypothesis->GetShapeType() & (1<< theShapeType)); + if ( theHypothesis->GetShapeType() & (1<< theShapeType)) + // issue 21106. Forbid 3D mesh on the SHELL + return !( theHypothesis->GetDim() == 3 && theShapeType == TopAbs_SHELL ); + else + return false; + } // hypothesis switch ( theShapeType ) { @@ -579,7 +597,7 @@ SMESH_Hypothesis::Hypothesis_Status SMESH_Hypothesis::Hypothesis_Status aux_ret, ret = SMESH_Hypothesis::HYP_OK; SMESHDS_Mesh* meshDS =_father->GetMeshDS(); - SMESH_Gen* gen =_father->GetGen(); + //SMESH_Gen* gen =_father->GetGen(); SMESH_Algo* algo = 0; if (_subShape.ShapeType() == TopAbs_VERTEX ) @@ -596,7 +614,7 @@ SMESH_Hypothesis::Hypothesis_Status if ( event != REMOVE_FATHER_ALGO ) { _algoState = NO_ALGO; - algo = gen->GetAlgo(*_father, _subShape); + algo = GetAlgo(); if ( algo ) { _algoState = MISSING_HYP; if ( event == REMOVE_FATHER_HYP || @@ -609,6 +627,7 @@ SMESH_Hypothesis::Hypothesis_Status int oldAlgoState = _algoState; bool modifiedHyp = (event == MODIF_HYP); // if set to true, force event MODIF_ALGO_STATE + bool needFullClean = false; bool isApplicableHyp = IsApplicableHypotesis( anHyp ); @@ -625,6 +644,15 @@ SMESH_Hypothesis::Hypothesis_Status // ---------------------- if (isApplicableHyp && !_father->IsNotConformAllowed() && !IsConform( algo )) return SMESH_Hypothesis::HYP_NOTCONFORM; + + // check if all-dimensional algo is hidden by other local one + if ( event == ADD_ALGO ) { + SMESH_HypoFilter filter( SMESH_HypoFilter::HasType( algo->GetType() )); + filter.Or( SMESH_HypoFilter::HasType( algo->GetType()+1 )); + filter.Or( SMESH_HypoFilter::HasType( algo->GetType()+2 )); + if ( SMESH_Algo * curAlgo = (SMESH_Algo*) _father->GetHypothesis( _subShape, filter, true )) + needFullClean = ( !curAlgo->NeedDiscreteBoundary() ); + } } // ---------------------------------- @@ -635,7 +663,7 @@ SMESH_Hypothesis::Hypothesis_Status if ( ! CanAddHypothesis( anHyp )) // check dimension return SMESH_Hypothesis::HYP_BAD_DIM; - if ( /*!anHyp->IsAuxiliary() &&*/ GetSimilarAttached( _subShape, anHyp ) ) + if ( /*!anHyp->IsAuxiliary() &&*/ getSimilarAttached( _subShape, anHyp ) ) return SMESH_Hypothesis::HYP_ALREADY_EXIST; if ( !meshDS->AddHypothesis(_subShape, anHyp)) @@ -653,15 +681,12 @@ SMESH_Hypothesis::Hypothesis_Status if (event == REMOVE_ALGO) { algo = dynamic_cast (anHyp); - if (!algo->NeedDescretBoundary()) + if (!algo->NeedDiscreteBoundary()) { // clean all mesh in the tree of the current submesh; // we must perform it now because later // we will have no information about the type of the removed algo - CleanDependants(); - ComputeStateEngine( CLEAN ); - CleanDependsOn(); - ComputeSubMeshStateEngine( CHECK_COMPUTE_STATE ); + needFullClean = true; } } } @@ -682,16 +707,16 @@ SMESH_Hypothesis::Hypothesis_Status case ADD_HYP: break; case ADD_ALGO: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if (algo->CheckHypothesis((*_father),_subShape, aux_ret)) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else if ( algo->IsStatusFatal( aux_ret )) { meshDS->RemoveHypothesis(_subShape, anHyp); ret = aux_ret; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case REMOVE_HYP: @@ -699,26 +724,26 @@ SMESH_Hypothesis::Hypothesis_Status case ADD_FATHER_HYP: break; case ADD_FATHER_ALGO: { // Algo just added in father - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo == anHyp ) { if ( algo->CheckHypothesis((*_father),_subShape, aux_ret)) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } case REMOVE_FATHER_HYP: break; case REMOVE_FATHER_ALGO: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if (algo) { if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } @@ -735,10 +760,10 @@ SMESH_Hypothesis::Hypothesis_Status switch (event) { case ADD_HYP: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); if (SMESH_Hypothesis::IsStatusFatal( ret )) meshDS->RemoveHypothesis(_subShape, anHyp); else if (!_father->IsUsedHypothesis( anHyp, this )) @@ -749,70 +774,70 @@ SMESH_Hypothesis::Hypothesis_Status break; } case ADD_ALGO: { //already existing algo : on father ? - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret ))// ignore hyp status - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else if ( algo->IsStatusFatal( aux_ret )) { meshDS->RemoveHypothesis(_subShape, anHyp); ret = aux_ret; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case REMOVE_HYP: break; case REMOVE_ALGO: { // perhaps a father algo applies ? - algo = gen->GetAlgo((*_father), _subShape); - if (algo == NULL) // no more algo applying on subShape... + algo = GetAlgo(); + if (algo == NULL) // no more algo applying on sub-shape... { - SetAlgoState(NO_ALGO); + setAlgoState(NO_ALGO); } else { if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } case MODIF_HYP: // assigned hypothesis value may become good case ADD_FATHER_HYP: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case ADD_FATHER_ALGO: { // new father algo - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT( algo ); if ( algo == anHyp ) { if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } case REMOVE_FATHER_HYP: // nothing to do break; case REMOVE_FATHER_ALGO: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if (algo == NULL) // no more applying algo on father { - SetAlgoState(NO_ALGO); + setAlgoState(NO_ALGO); } else { if ( algo->CheckHypothesis((*_father),_subShape , aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } @@ -828,7 +853,7 @@ SMESH_Hypothesis::Hypothesis_Status switch (event) { case ADD_HYP: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if (!algo->CheckHypothesis((*_father),_subShape, ret )) { @@ -851,7 +876,7 @@ SMESH_Hypothesis::Hypothesis_Status break; } case ADD_ALGO: { //already existing algo : on father ? - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) { // check if algo changes SMESH_HypoFilter f; @@ -864,24 +889,24 @@ SMESH_Hypothesis::Hypothesis_Status modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case REMOVE_HYP: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) - SetAlgoState(HYP_OK); + setAlgoState(HYP_OK); else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); modifiedHyp = true; break; } case REMOVE_ALGO: { // perhaps a father algo applies ? - algo = gen->GetAlgo((*_father), _subShape); - if (algo == NULL) // no more algo applying on subShape... + algo = GetAlgo(); + if (algo == NULL) // no more algo applying on sub-shape... { - SetAlgoState(NO_ALGO); + setAlgoState(NO_ALGO); } else { @@ -891,13 +916,13 @@ SMESH_Hypothesis::Hypothesis_Status modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } case MODIF_HYP: // hypothesis value may become bad case ADD_FATHER_HYP: { // new father hypothesis ? - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) { @@ -905,11 +930,11 @@ SMESH_Hypothesis::Hypothesis_Status modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case ADD_FATHER_ALGO: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if ( algo == anHyp ) { // a new algo on father if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) { // check if algo changes @@ -923,27 +948,33 @@ SMESH_Hypothesis::Hypothesis_Status modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } case REMOVE_FATHER_HYP: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); if ( algo->CheckHypothesis((*_father),_subShape, aux_ret )) { // is there the same local hyp or maybe a new father algo applied? - if ( !GetSimilarAttached( _subShape, anHyp ) ) + if ( !getSimilarAttached( _subShape, anHyp ) ) modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } case REMOVE_FATHER_ALGO: { - algo = gen->GetAlgo((*_father), _subShape); + // IPAL21346. Edges not removed when Netgen 1d-2d is removed from a SOLID. + // CLEAN was not called at event REMOVE_ALGO because the algo is not applicable to SOLID. + algo = dynamic_cast (anHyp); + if (!algo->NeedDiscreteBoundary()) + needFullClean = true; + + algo = GetAlgo(); if (algo == NULL) // no more applying algo on father { - SetAlgoState(NO_ALGO); + setAlgoState(NO_ALGO); } else { @@ -953,7 +984,7 @@ SMESH_Hypothesis::Hypothesis_Status modifiedHyp = true; } else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; } @@ -981,16 +1012,19 @@ SMESH_Hypothesis::Hypothesis_Status TopTools_ListIteratorOfListOfShape it( _father->GetAncestors( _subShape )); for ( ; ( ret == SMESH_Hypothesis::HYP_OK && it.More()); it.Next() ) { if ( SMESH_Algo* upperAlgo = gen->GetAlgo( *_father, it.Value() )) - if ( !upperAlgo->NeedDescretBoundary() && !upperAlgo->SupportSubmeshes()) + if ( !upperAlgo->NeedDiscreteBoundary() && !upperAlgo->SupportSubmeshes()) ret = SMESH_Hypothesis::HYP_HIDDEN_ALGO; } // is algo hiding? if ( ret == SMESH_Hypothesis::HYP_OK && - !algo->NeedDescretBoundary() && + !algo->NeedDiscreteBoundary() && !algo->SupportSubmeshes()) { + TopoDS_Shape algoAssignedTo, otherAssignedTo; + gen->GetAlgo( *_father, _subShape, &algoAssignedTo ); map::reverse_iterator i_sm = _mapDepend.rbegin(); for ( ; ( ret == SMESH_Hypothesis::HYP_OK && i_sm != _mapDepend.rend()) ; ++i_sm ) - if ( gen->GetAlgo( *_father, i_sm->second->_subShape )) + if ( gen->GetAlgo( *_father, i_sm->second->_subShape, &otherAssignedTo ) && + SMESH_MesherHelper::IsSubShape( /*sub=*/otherAssignedTo, /*main=*/algoAssignedTo )) ret = SMESH_Hypothesis::HYP_HIDING_ALGO; } } @@ -1000,10 +1034,10 @@ SMESH_Hypothesis::Hypothesis_Status if ( stateChange && _algoState == HYP_OK ) // hyp becomes OK algo->SetEventListener( this ); - NotifyListenersOnEvent( event, ALGO_EVENT, anHyp ); + notifyListenersOnEvent( event, ALGO_EVENT, anHyp ); if ( stateChange && oldAlgoState == HYP_OK ) { // hyp becomes KO - DeleteOwnListeners(); + deleteOwnListeners(); SetIsAlwaysComputed( false ); if (_subShape.ShapeType() == TopAbs_VERTEX ) { // restore default states @@ -1012,6 +1046,13 @@ SMESH_Hypothesis::Hypothesis_Status } } + if ( needFullClean ) { + // added or removed algo is all-dimensional + ComputeStateEngine( CLEAN ); + cleanDependsOn(); + ComputeSubMeshStateEngine( CHECK_COMPUTE_STATE ); + } + if (stateChange || modifiedHyp) ComputeStateEngine(MODIF_ALGO_STATE); @@ -1031,9 +1072,9 @@ bool SMESH_subMesh::IsConform(const SMESH_Algo* theAlgo) // Suppose that theAlgo is applicable to _subShape, do not check it here //if ( !IsApplicableHypotesis( theAlgo )) return false; - // check only algo that doesn't NeedDescretBoundary(): because mesh made + // check only algo that doesn't NeedDiscreteBoundary(): because mesh made // on a sub-shape will be ignored by theAlgo - if ( theAlgo->NeedDescretBoundary() || + if ( theAlgo->NeedDiscreteBoundary() || !theAlgo->OnlyUnaryInput() ) // all adjacent shapes will be meshed by this algo? return true; @@ -1062,7 +1103,7 @@ bool SMESH_subMesh::IsConform(const SMESH_Algo* theAlgo) // check algo attached to smAdjacent SMESH_Algo * algo = gen->GetAlgo((*_father), adjacent); if (algo && - !algo->NeedDescretBoundary() && + !algo->NeedDiscreteBoundary() && algo->OnlyUnaryInput()) return false; // NOT CONFORM MESH WILL BE PRODUCED } @@ -1077,7 +1118,7 @@ bool SMESH_subMesh::IsConform(const SMESH_Algo* theAlgo) */ //============================================================================= -void SMESH_subMesh::SetAlgoState(int state) +void SMESH_subMesh::setAlgoState(int state) { _algoState = state; } @@ -1113,7 +1154,7 @@ SMESH_Hypothesis::Hypothesis_Status */ //============================================================================= -void SMESH_subMesh::CleanDependsOn() +void SMESH_subMesh::cleanDependsOn() { SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,false); while ( smIt->more() ) @@ -1128,48 +1169,48 @@ void SMESH_subMesh::CleanDependsOn() void SMESH_subMesh::DumpAlgoState(bool isMain) { - int dim = SMESH_Gen::GetShapeDim(_subShape); + int dim = SMESH_Gen::GetShapeDim(_subShape); // if (dim < 1) return; - if (isMain) - { - const map < int, SMESH_subMesh * >&subMeshes = DependsOn(); - - map < int, SMESH_subMesh * >::const_iterator itsub; - for (itsub = subMeshes.begin(); itsub != subMeshes.end(); itsub++) - { - SMESH_subMesh *sm = (*itsub).second; - sm->DumpAlgoState(false); - } - } - int type = _subShape.ShapeType(); - MESSAGE("dim = " << dim << " type of shape " << type); - switch (_algoState) - { - case NO_ALGO: - MESSAGE(" AlgoState = NO_ALGO"); - break; - case MISSING_HYP: - MESSAGE(" AlgoState = MISSING_HYP"); - break; - case HYP_OK: - MESSAGE(" AlgoState = HYP_OK"); - break; - } - switch (_computeState) - { - case NOT_READY: - MESSAGE(" ComputeState = NOT_READY"); - break; - case READY_TO_COMPUTE: - MESSAGE(" ComputeState = READY_TO_COMPUTE"); - break; - case COMPUTE_OK: - MESSAGE(" ComputeState = COMPUTE_OK"); - break; - case FAILED_TO_COMPUTE: - MESSAGE(" ComputeState = FAILED_TO_COMPUTE"); - break; - } + if (isMain) + { + const map < int, SMESH_subMesh * >&subMeshes = DependsOn(); + + map < int, SMESH_subMesh * >::const_iterator itsub; + for (itsub = subMeshes.begin(); itsub != subMeshes.end(); itsub++) + { + SMESH_subMesh *sm = (*itsub).second; + sm->DumpAlgoState(false); + } + } + int type = _subShape.ShapeType(); + MESSAGE("dim = " << dim << " type of shape " << type); + switch (_algoState) + { + case NO_ALGO: + MESSAGE(" AlgoState = NO_ALGO"); + break; + case MISSING_HYP: + MESSAGE(" AlgoState = MISSING_HYP"); + break; + case HYP_OK: + MESSAGE(" AlgoState = HYP_OK"); + break; + } + switch (_computeState) + { + case NOT_READY: + MESSAGE(" ComputeState = NOT_READY"); + break; + case READY_TO_COMPUTE: + MESSAGE(" ComputeState = READY_TO_COMPUTE"); + break; + case COMPUTE_OK: + MESSAGE(" ComputeState = COMPUTE_OK"); + break; + case FAILED_TO_COMPUTE: + MESSAGE(" ComputeState = FAILED_TO_COMPUTE"); + break; + } } //================================================================================ @@ -1225,7 +1266,7 @@ bool SMESH_subMesh::ComputeStateEngine(int event) SMESHDS_SubMesh* smDS = GetSubMeshDS(); if ( smDS && smDS->NbNodes() ) { if ( event == CLEAN ) { - CleanDependants(); + cleanDependants(); cleanSubMesh( this ); } else @@ -1240,7 +1281,7 @@ bool SMESH_subMesh::ComputeStateEngine(int event) } } if ( event == MODIF_ALGO_STATE ) - CleanDependants(); + cleanDependants(); return true; } SMESH_Gen *gen = _father->GetGen(); @@ -1258,25 +1299,33 @@ bool SMESH_subMesh::ComputeStateEngine(int event) switch (event) { case MODIF_ALGO_STATE: - algo = gen->GetAlgo((*_father), _subShape); - if (algo && !algo->NeedDescretBoundary()) - CleanDependsOn(); // clean sub-meshes with event CLEAN + algo = GetAlgo(); + if (algo && !algo->NeedDiscreteBoundary()) + cleanDependsOn(); // clean sub-meshes with event CLEAN if ( _algoState == HYP_OK ) _computeState = READY_TO_COMPUTE; break; - case COMPUTE: // nothing to do + case COMPUTE: // nothing to do + break; +#ifdef WITH_SMESH_CANCEL_COMPUTE + case COMPUTE_CANCELED: // nothing to do break; +#endif case CLEAN: - CleanDependants(); - RemoveSubMeshElementsAndNodes(); + cleanDependants(); + removeSubMeshElementsAndNodes(); break; - case SUBMESH_COMPUTED: // nothing to do + case SUBMESH_COMPUTED: // nothing to do break; case SUBMESH_RESTORED: ComputeSubMeshStateEngine( SUBMESH_RESTORED ); break; case MESH_ENTITY_REMOVED: break; + case SUBMESH_LOADED: + loadDependentMeshes(); + ComputeSubMeshStateEngine( SUBMESH_LOADED ); + //break; case CHECK_COMPUTE_STATE: if ( IsMeshComputed() ) _computeState = COMPUTE_OK; @@ -1294,25 +1343,25 @@ bool SMESH_subMesh::ComputeStateEngine(int event) { case MODIF_ALGO_STATE: _computeState = NOT_READY; - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if (algo) { - if (!algo->NeedDescretBoundary()) - CleanDependsOn(); // clean sub-meshes with event CLEAN + if (!algo->NeedDiscreteBoundary()) + cleanDependsOn(); // clean sub-meshes with event CLEAN if ( _algoState == HYP_OK ) _computeState = READY_TO_COMPUTE; } break; case COMPUTE: { - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); ASSERT(algo); ret = algo->CheckHypothesis((*_father), _subShape, hyp_status); if (!ret) { MESSAGE("***** verify compute state *****"); _computeState = NOT_READY; - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); break; } TopoDS_Shape shape = _subShape; @@ -1320,34 +1369,37 @@ bool SMESH_subMesh::ComputeStateEngine(int event) if (_father->HasShapeToMesh() ) { bool subComputed = false; if (!algo->OnlyUnaryInput()) - shape = GetCollection( gen, algo, subComputed ); + shape = getCollection( gen, algo, subComputed ); else - subComputed = SubMeshesComputed(); - ret = ( algo->NeedDescretBoundary() ? subComputed : + subComputed = subMeshesComputed(); + ret = ( algo->NeedDiscreteBoundary() ? subComputed : algo->SupportSubmeshes() ? true : ( !subComputed || _father->IsNotConformAllowed() )); if (!ret) { _computeState = FAILED_TO_COMPUTE; - if ( !algo->NeedDescretBoundary() ) + if ( !algo->NeedDiscreteBoundary() ) _computeError = SMESH_ComputeError::New(COMPERR_BAD_INPUT_MESH, "Unexpected computed submesh",algo); break; } } - // compute -// CleanDependants(); for "UseExisting_*D" algos -// RemoveSubMeshElementsAndNodes(); + // Compute + + //cleanDependants(); for "UseExisting_*D" algos + //removeSubMeshElementsAndNodes(); + loadDependentMeshes(); ret = false; _computeState = FAILED_TO_COMPUTE; _computeError = SMESH_ComputeError::New(COMPERR_OK,"",algo); try { -#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 +#if OCC_VERSION_LARGE > 0x06010000 OCC_CATCH_SIGNALS; #endif algo->InitComputeError(); MemoryReserve aMemoryReserve; SMDS_Mesh::CheckMemory(); + Kernel_Utils::Localizer loc; if ( !_father->HasShapeToMesh() ) // no shape { SMESH_MesherHelper helper( *_father ); @@ -1362,6 +1414,11 @@ bool SMESH_subMesh::ComputeStateEngine(int event) if ( !_computeError || ( !ret && _computeError->IsOK() ) ) // algo can set _computeError of submesh _computeError = algo->GetComputeError(); } + catch ( ::SMESH_ComputeError& comperr ) { + cout << " SMESH_ComputeError caught" << endl; + if ( !_computeError ) _computeError = SMESH_ComputeError::New(); + *_computeError = comperr; + } catch ( std::bad_alloc& exc ) { MESSAGE("std::bad_alloc thrown inside algo->Compute()"); if ( _computeError ) { @@ -1405,38 +1462,53 @@ bool SMESH_subMesh::ComputeStateEngine(int event) else ret = false; } - if (ret && !_alwaysComputed && shape == _subShape) { // check if anything was built - ret = ( GetSubMeshDS() && ( GetSubMeshDS()->NbElements() || GetSubMeshDS()->NbNodes() )); + TopExp_Explorer subS(shape, _subShape.ShapeType()); + if (ret) // check if anything was built + { + for (; ret && subS.More(); subS.Next()) + ret = _father->GetSubMesh( subS.Current() )->IsMeshComputed(); } - bool isComputeErrorSet = !CheckComputeError( algo, shape ); + bool isComputeErrorSet = !checkComputeError( algo, shape ); if (!ret && !isComputeErrorSet) { // Set _computeError - if ( !_computeError ) - _computeError = SMESH_ComputeError::New(); - if ( _computeError->IsOK() ) - _computeError->myName = COMPERR_ALGO_FAILED; - _computeState = FAILED_TO_COMPUTE; + for (subS.ReInit(); subS.More(); subS.Next()) + { + SMESH_subMesh* sm = _father->GetSubMesh( subS.Current() ); + if ( !sm->IsMeshComputed() ) + { + if ( !sm->_computeError ) + sm->_computeError = SMESH_ComputeError::New(); + if ( sm->_computeError->IsOK() ) + sm->_computeError->myName = COMPERR_ALGO_FAILED; + sm->_computeState = FAILED_TO_COMPUTE; + sm->_computeError->myAlgo = algo; + } + } } - if (ret) + if (ret && _computeError && _computeError->myName != COMPERR_WARNING ) { _computeError.reset(); } - UpdateDependantsState( SUBMESH_COMPUTED ); // send event SUBMESH_COMPUTED + updateDependantsState( SUBMESH_COMPUTED ); // send event SUBMESH_COMPUTED } break; +#ifdef WITH_SMESH_CANCEL_COMPUTE + case COMPUTE_CANCELED: // nothing to do + break; +#endif case CLEAN: - CleanDependants(); - RemoveSubMeshElementsAndNodes(); + cleanDependants(); + removeSubMeshElementsAndNodes(); _computeState = NOT_READY; - algo = gen->GetAlgo((*_father), _subShape); + algo = GetAlgo(); if (algo) { ret = algo->CheckHypothesis((*_father), _subShape, hyp_status); if (ret) _computeState = READY_TO_COMPUTE; else - SetAlgoState(MISSING_HYP); + setAlgoState(MISSING_HYP); } break; case SUBMESH_COMPUTED: // nothing to do @@ -1446,11 +1518,15 @@ bool SMESH_subMesh::ComputeStateEngine(int event) // happen after retrieval from a file ComputeStateEngine( CHECK_COMPUTE_STATE ); ComputeSubMeshStateEngine( SUBMESH_RESTORED ); - algo = gen->GetAlgo(*_father, _subShape); + algo = GetAlgo(); if (algo) algo->SubmeshRestored( this ); break; case MESH_ENTITY_REMOVED: break; + case SUBMESH_LOADED: + loadDependentMeshes(); + ComputeSubMeshStateEngine( SUBMESH_LOADED ); + //break; case CHECK_COMPUTE_STATE: if ( IsMeshComputed() ) _computeState = COMPUTE_OK; @@ -1468,15 +1544,19 @@ bool SMESH_subMesh::ComputeStateEngine(int event) { case MODIF_ALGO_STATE: ComputeStateEngine( CLEAN ); - algo = gen->GetAlgo((*_father), _subShape); - if (algo && !algo->NeedDescretBoundary()) - CleanDependsOn(); // clean sub-meshes with event CLEAN + algo = GetAlgo(); + if (algo && !algo->NeedDiscreteBoundary()) + cleanDependsOn(); // clean sub-meshes with event CLEAN break; case COMPUTE: // nothing to do break; +#ifdef WITH_SMESH_CANCEL_COMPUTE + case COMPUTE_CANCELED: // nothing to do + break; +#endif case CLEAN: - CleanDependants(); // clean sub-meshes, dependant on this one, with event CLEAN - RemoveSubMeshElementsAndNodes(); + cleanDependants(); // clean sub-meshes, dependant on this one, with event CLEAN + removeSubMeshElementsAndNodes(); _computeState = NOT_READY; if ( _algoState == HYP_OK ) _computeState = READY_TO_COMPUTE; @@ -1486,20 +1566,24 @@ bool SMESH_subMesh::ComputeStateEngine(int event) case SUBMESH_RESTORED: ComputeStateEngine( CHECK_COMPUTE_STATE ); ComputeSubMeshStateEngine( SUBMESH_RESTORED ); - algo = gen->GetAlgo(*_father, _subShape); + algo = GetAlgo(); if (algo) algo->SubmeshRestored( this ); break; case MESH_ENTITY_REMOVED: - UpdateDependantsState( CHECK_COMPUTE_STATE ); - ComputeStateEngine( CHECK_COMPUTE_STATE ); + updateDependantsState ( CHECK_COMPUTE_STATE ); + ComputeStateEngine ( CHECK_COMPUTE_STATE ); ComputeSubMeshStateEngine( CHECK_COMPUTE_STATE ); break; case CHECK_COMPUTE_STATE: - if ( !IsMeshComputed() ) + if ( !IsMeshComputed() ) { if (_algoState == HYP_OK) _computeState = READY_TO_COMPUTE; else _computeState = NOT_READY; + } + break; + case SUBMESH_LOADED: + // already treated event, thanks to which _computeState == COMPUTE_OK break; default: ASSERT(0); @@ -1513,9 +1597,11 @@ bool SMESH_subMesh::ComputeStateEngine(int event) switch (event) { case MODIF_ALGO_STATE: - algo = gen->GetAlgo((*_father), _subShape); - if (algo && !algo->NeedDescretBoundary()) - CleanDependsOn(); // clean sub-meshes with event CLEAN + if ( !IsEmpty() ) + ComputeStateEngine( CLEAN ); + algo = GetAlgo(); + if (algo && !algo->NeedDiscreteBoundary()) + cleanDependsOn(); // clean sub-meshes with event CLEAN if (_algoState == HYP_OK) _computeState = READY_TO_COMPUTE; else @@ -1523,9 +1609,15 @@ bool SMESH_subMesh::ComputeStateEngine(int event) break; case COMPUTE: // nothing to do break; + case COMPUTE_CANCELED: + { + algo = GetAlgo(); + algo->CancelCompute(); + } + break; case CLEAN: - CleanDependants(); // submeshes dependent on me should be cleaned as well - RemoveSubMeshElementsAndNodes(); + cleanDependants(); // submeshes dependent on me should be cleaned as well + removeSubMeshElementsAndNodes(); break; case SUBMESH_COMPUTED: // allow retry compute if (_algoState == HYP_OK) @@ -1547,6 +1639,8 @@ bool SMESH_subMesh::ComputeStateEngine(int event) else _computeState = NOT_READY; break; + // case SUBMESH_LOADED: + // break; default: ASSERT(0); break; @@ -1559,11 +1653,68 @@ bool SMESH_subMesh::ComputeStateEngine(int event) break; } - NotifyListenersOnEvent( event, COMPUTE_EVENT ); + notifyListenersOnEvent( event, COMPUTE_EVENT ); + + return ret; +} + + +//============================================================================= +/*! + * + */ +//============================================================================= + +bool SMESH_subMesh::Evaluate(MapShapeNbElems& aResMap) +{ + _computeError.reset(); + + bool ret = true; + + if (_subShape.ShapeType() == TopAbs_VERTEX) { + vector aVec(SMDSEntity_Last,0); + aVec[SMDSEntity_Node] = 1; + aResMap.insert(make_pair(this,aVec)); + return ret; + } + + //SMESH_Gen *gen = _father->GetGen(); + SMESH_Algo *algo = 0; + SMESH_Hypothesis::Hypothesis_Status hyp_status; + + algo = GetAlgo(); + if(algo && !aResMap.count(this) ) + { + ret = algo->CheckHypothesis((*_father), _subShape, hyp_status); + if (!ret) return false; + + if (_father->HasShapeToMesh() && algo->NeedDiscreteBoundary()) + { + // check submeshes needed + bool subMeshEvaluated = true; + int dimToCheck = SMESH_Gen::GetShapeDim( _subShape ) - 1; + SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,/*complexShapeFirst=*/true); + while ( smIt->more() && subMeshEvaluated ) + { + SMESH_subMesh* sm = smIt->next(); + int dim = SMESH_Gen::GetShapeDim( sm->GetSubShape() ); + if (dim < dimToCheck) break; // the rest subMeshes are all of less dimension + const vector & nbs = aResMap[ sm ]; + subMeshEvaluated = (std::accumulate( nbs.begin(), nbs.end(), 0 ) > 0 ); + } + if ( !subMeshEvaluated ) + return false; + } + _computeError = SMESH_ComputeError::New(COMPERR_OK,"",algo); + ret = algo->Evaluate((*_father), _subShape, aResMap); + + aResMap.insert( make_pair( this,vector(0))); + } return ret; } + //======================================================================= /*! * \brief Update compute_state by _computeError and send proper events to @@ -1572,18 +1723,18 @@ bool SMESH_subMesh::ComputeStateEngine(int event) */ //======================================================================= -bool SMESH_subMesh::CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& theShape) +bool SMESH_subMesh::checkComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& theShape) { bool noErrors = true; if ( !theShape.IsNull() ) { // Check state of submeshes - if ( !theAlgo->NeedDescretBoundary()) + if ( !theAlgo->NeedDiscreteBoundary()) { SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,false); while ( smIt->more() ) - if ( !smIt->next()->CheckComputeError( theAlgo )) + if ( !smIt->next()->checkComputeError( theAlgo )) noErrors = false; } @@ -1595,9 +1746,9 @@ bool SMESH_subMesh::CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& t for (TopoDS_Iterator subIt( theShape ); subIt.More(); subIt.Next()) { SMESH_subMesh* sm = _father->GetSubMesh( subIt.Value() ); if ( sm != this ) { - if ( !sm->CheckComputeError( theAlgo, sm->GetSubShape() )) + if ( !sm->checkComputeError( theAlgo, sm->GetSubShape() )) noErrors = false; - UpdateDependantsState( SUBMESH_COMPUTED ); // send event SUBMESH_COMPUTED + updateDependantsState( SUBMESH_COMPUTED ); // send event SUBMESH_COMPUTED } } } @@ -1606,7 +1757,8 @@ bool SMESH_subMesh::CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& t // Check my state if ( !_computeError || _computeError->IsOK() ) { - _computeState = COMPUTE_OK; + // no error description is set to this sub-mesh, check if any mesh is computed + _computeState = IsMeshComputed() ? COMPUTE_OK : FAILED_TO_COMPUTE; } else { @@ -1615,7 +1767,7 @@ bool SMESH_subMesh::CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& t // Show error SMESH_Comment text; - text << theAlgo->GetName() << " failed on subshape #" << _Id << " with error "; + text << theAlgo->GetName() << " failed on sub-shape #" << _Id << " with error "; if (_computeError->IsCommon() ) text << _computeError->CommonName(); else @@ -1623,73 +1775,22 @@ bool SMESH_subMesh::CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& t if ( _computeError->myComment.size() > 0 ) text << " \"" << _computeError->myComment << "\""; -#ifdef _DEBUG_ - MESSAGE_BEGIN ( text ); - // Show vertices location of a failed shape - TopTools_IndexedMapOfShape vMap; - TopExp::MapShapes( _subShape, TopAbs_VERTEX, vMap ); - MESSAGE_ADD ( "Subshape vertices " << ( vMap.Extent()>10 ? "(first 10):" : ":") ); - for ( int iv = 1; iv <= vMap.Extent() && iv < 11; ++iv ) { - gp_Pnt P( BRep_Tool::Pnt( TopoDS::Vertex( vMap( iv ) ))); - MESSAGE_ADD ( "#" << _father->GetMeshDS()->ShapeToIndex( vMap( iv )) << " " - << P.X() << " " << P.Y() << " " << P.Z() << " " ); - } -#else INFOS( text ); -#endif - _computeState = FAILED_TO_COMPUTE; - noErrors = false; - } - } - return noErrors; -} - -//======================================================================= -//function : ApplyToCollection -//purpose : Apply theAlgo to all subshapes in theCollection -//======================================================================= - -bool SMESH_subMesh::ApplyToCollection (SMESH_Algo* theAlgo, - const TopoDS_Shape& theCollection) -{ - MESSAGE("SMESH_subMesh::ApplyToCollection"); - ASSERT ( !theAlgo->NeedDescretBoundary() ); - - if ( _computeError ) - _computeError->myName = COMPERR_OK; - bool ok = theAlgo->Compute( *_father, theCollection ); + _computeState = _computeError->IsKO() ? FAILED_TO_COMPUTE : COMPUTE_OK; - // set _computeState of subshapes - TopExp_Explorer anExplorer( theCollection, _subShape.ShapeType() ); - for ( ; anExplorer.More(); anExplorer.Next() ) - { - if ( SMESH_subMesh* subMesh = _father->GetSubMeshContaining( anExplorer.Current() )) - { - bool localOK = subMesh->CheckComputeError( theAlgo ); - if ( !ok && localOK && !subMesh->IsMeshComputed() ) - { - subMesh->_computeError = theAlgo->GetComputeError(); - if ( subMesh->_computeError->IsOK() ) - _computeError = SMESH_ComputeError::New(COMPERR_ALGO_FAILED); - localOK = CheckComputeError( theAlgo ); - } - if ( localOK ) - subMesh->UpdateDependantsState( SUBMESH_COMPUTED ); - subMesh->UpdateSubMeshState( localOK ? COMPUTE_OK : FAILED_TO_COMPUTE ); + noErrors = false; } } - - return true; + return noErrors; } - //======================================================================= -//function : UpdateSubMeshState +//function : updateSubMeshState //purpose : //======================================================================= -void SMESH_subMesh::UpdateSubMeshState(const compute_state theState) +void SMESH_subMesh::updateSubMeshState(const compute_state theState) { SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,false); while ( smIt->more() ) @@ -1701,21 +1802,20 @@ void SMESH_subMesh::UpdateSubMeshState(const compute_state theState) //purpose : //======================================================================= -void SMESH_subMesh::ComputeSubMeshStateEngine(int event) +void SMESH_subMesh::ComputeSubMeshStateEngine(int event, const bool includeSelf) { - SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(false,false); + SMESH_subMeshIteratorPtr smIt = getDependsOnIterator(includeSelf,false); while ( smIt->more() ) smIt->next()->ComputeStateEngine(event); } //======================================================================= -//function : UpdateDependantsState +//function : updateDependantsState //purpose : //======================================================================= -void SMESH_subMesh::UpdateDependantsState(const compute_event theEvent) +void SMESH_subMesh::updateDependantsState(const compute_event theEvent) { - //MESSAGE("SMESH_subMesh::UpdateDependantsState"); TopTools_ListIteratorOfListOfShape it( _father->GetAncestors( _subShape )); for (; it.More(); it.Next()) { @@ -1733,7 +1833,7 @@ void SMESH_subMesh::UpdateDependantsState(const compute_event theEvent) */ //============================================================================= -void SMESH_subMesh::CleanDependants() +void SMESH_subMesh::cleanDependants() { int dimToClean = SMESH_Gen::GetShapeDim( _subShape ) + 1; @@ -1746,7 +1846,8 @@ void SMESH_subMesh::CleanDependants() // will erase mesh on other shapes in a compound if ( ancestor.ShapeType() >= TopAbs_SOLID ) { SMESH_subMesh *aSubMesh = _father->GetSubMeshContaining(ancestor); - if (aSubMesh) + if (aSubMesh && + !aSubMesh->IsEmpty() ) // prevent infinite CLEAN via event lesteners aSubMesh->ComputeStateEngine(CLEAN); } } @@ -1759,10 +1860,8 @@ void SMESH_subMesh::CleanDependants() */ //============================================================================= -void SMESH_subMesh::RemoveSubMeshElementsAndNodes() +void SMESH_subMesh::removeSubMeshElementsAndNodes() { - //SCRUTE(_subShape.ShapeType()); - cleanSubMesh( this ); // algo may bind a submesh not to _subShape, eg 3D algo @@ -1783,18 +1882,16 @@ void SMESH_subMesh::RemoveSubMeshElementsAndNodes() } //======================================================================= -//function : GetCollection +//function : getCollection //purpose : return a shape containing all sub-shapes of the MainShape that can be // meshed at once along with _subShape //======================================================================= -TopoDS_Shape SMESH_subMesh::GetCollection(SMESH_Gen * theGen, +TopoDS_Shape SMESH_subMesh::getCollection(SMESH_Gen * theGen, SMESH_Algo* theAlgo, bool & theSubComputed) { - MESSAGE("SMESH_subMesh::GetCollection"); - - theSubComputed = SubMeshesComputed(); + theSubComputed = subMeshesComputed(); TopoDS_Shape mainShape = _father->GetMeshDS()->ShapeToMesh(); @@ -1827,7 +1924,7 @@ TopoDS_Shape SMESH_subMesh::GetCollection(SMESH_Gen * theGen, if (strcmp( anAlgo->GetName(), theAlgo->GetName()) == 0 && // same algo anAlgo->GetUsedHypothesis( *_father, S, ignoreAuxiliaryHyps ) == aUsedHyp) // same hyps aBuilder.Add( aCompound, S ); - if ( !subMesh->SubMeshesComputed() ) + if ( !subMesh->subMeshesComputed() ) theSubComputed = false; } } @@ -1836,14 +1933,14 @@ TopoDS_Shape SMESH_subMesh::GetCollection(SMESH_Gen * theGen, } //======================================================================= -//function : GetSimilarAttached +//function : getSimilarAttached //purpose : return a hypothesis attached to theShape. // If theHyp is provided, similar but not same hypotheses // is returned; else only applicable ones having theHypType // is returned //======================================================================= -const SMESH_Hypothesis* SMESH_subMesh::GetSimilarAttached(const TopoDS_Shape& theShape, +const SMESH_Hypothesis* SMESH_subMesh::getSimilarAttached(const TopoDS_Shape& theShape, const SMESH_Hypothesis * theHyp, const int theHypType) { @@ -1876,7 +1973,7 @@ SMESH_Hypothesis::Hypothesis_Status MESSAGE ("SMESH_subMesh::CheckConcurentHypothesis"); // is there local hypothesis on me? - if ( GetSimilarAttached( _subShape, 0, theHypType ) ) + if ( getSimilarAttached( _subShape, 0, theHypType ) ) return SMESH_Hypothesis::HYP_OK; @@ -1886,7 +1983,7 @@ SMESH_Hypothesis::Hypothesis_Status for (; it.More(); it.Next()) { const TopoDS_Shape& ancestor = it.Value(); - const SMESH_Hypothesis* hyp = GetSimilarAttached( ancestor, 0, theHypType ); + const SMESH_Hypothesis* hyp = getSimilarAttached( ancestor, 0, theHypType ); if ( hyp ) { if ( aPrevWithHyp.IsNull() || aPrevWithHyp.IsSame( ancestor )) @@ -1903,14 +2000,26 @@ SMESH_Hypothesis::Hypothesis_Status return SMESH_Hypothesis::HYP_OK; } +//================================================================================ +/*! + * \brief Constructor of OwnListenerData + */ +//================================================================================ + +SMESH_subMesh::OwnListenerData::OwnListenerData( SMESH_subMesh* sm, EventListener* el): + mySubMesh( sm ), + myMeshID( sm ? sm->GetFather()->GetId() : -1 ), + mySubMeshID( sm ? sm->GetId() : -1 ), + myListener( el ) +{ +} + //================================================================================ /*! * \brief Sets an event listener and its data to a submesh * \param listener - the listener to store * \param data - the listener data to store * \param where - the submesh to store the listener and it's data - * \param deleteListener - if true then the listener will be deleted as - * it is removed from where submesh * * It remembers the submesh where it puts the listener in order to delete * them when HYP_OK algo_state is lost @@ -1923,8 +2032,8 @@ void SMESH_subMesh::SetEventListener(EventListener* listener, SMESH_subMesh* where) { if ( listener && where ) { - where->SetEventListener( listener, data ); - myOwnListeners.push_back( make_pair( where, listener )); + where->setEventListener( listener, data ); + _ownListeners.push_back( OwnListenerData( where, listener )); } } @@ -1938,18 +2047,18 @@ void SMESH_subMesh::SetEventListener(EventListener* listener, */ //================================================================================ -void SMESH_subMesh::SetEventListener(EventListener* listener, EventListenerData* data) +void SMESH_subMesh::setEventListener(EventListener* listener, EventListenerData* data) { map< EventListener*, EventListenerData* >::iterator l_d = - myEventListeners.find( listener ); - if ( l_d != myEventListeners.end() ) { + _eventListeners.find( listener ); + if ( l_d != _eventListeners.end() ) { EventListenerData* curData = l_d->second; if ( curData && curData != data && curData->IsDeletable() ) delete curData; l_d->second = data; } else - myEventListeners.insert( make_pair( listener, data )); + _eventListeners.insert( make_pair( listener, data )); } //================================================================================ @@ -1963,8 +2072,8 @@ void SMESH_subMesh::SetEventListener(EventListener* listener, EventListenerData* EventListenerData* SMESH_subMesh::GetEventListenerData(EventListener* listener) const { map< EventListener*, EventListenerData* >::const_iterator l_d = - myEventListeners.find( listener ); - if ( l_d != myEventListeners.end() ) + _eventListeners.find( listener ); + if ( l_d != _eventListeners.end() ) return l_d->second; return 0; } @@ -1974,19 +2083,27 @@ EventListenerData* SMESH_subMesh::GetEventListenerData(EventListener* listener) * \brief Notify stored event listeners on the occured event * \param event - algo_event or compute_event itself * \param eventType - algo_event or compute_event - * \param subMesh - the submesh where the event occures - * \param data - listener data stored in the subMesh * \param hyp - hypothesis, if eventType is algo_event */ //================================================================================ -void SMESH_subMesh::NotifyListenersOnEvent( const int event, +void SMESH_subMesh::notifyListenersOnEvent( const int event, const event_type eventType, SMESH_Hypothesis* hyp) { - map< EventListener*, EventListenerData* >::iterator l_d = myEventListeners.begin(); - for ( ; l_d != myEventListeners.end(); ++l_d ) - l_d->first->ProcessEvent( event, eventType, this, l_d->second, hyp ); + map< EventListener*, EventListenerData* >::iterator l_d = _eventListeners.begin(); + for ( ; l_d != _eventListeners.end(); ++l_d ) + { + std::pair< EventListener*, EventListenerData* > li_da = *l_d; /* copy to enable removal + of a listener from + _eventListeners by + its ProcessEvent() */ + if ( li_da.first->myBusySM.insert( this ).second ) + { + li_da.first->ProcessEvent( event, eventType, this, li_da.second, hyp ); + li_da.first->myBusySM.erase( this ); + } + } } //================================================================================ @@ -1999,11 +2116,11 @@ void SMESH_subMesh::NotifyListenersOnEvent( const int event, void SMESH_subMesh::DeleteEventListener(EventListener* listener) { map< EventListener*, EventListenerData* >::iterator l_d = - myEventListeners.find( listener ); - if ( l_d != myEventListeners.end() ) { + _eventListeners.find( listener ); + if ( l_d != _eventListeners.end() ) { if ( l_d->first && l_d->first->IsDeletable() ) delete l_d->first; if ( l_d->second && l_d->second->IsDeletable() ) delete l_d->second; - myEventListeners.erase( l_d ); + _eventListeners.erase( l_d ); } } @@ -2013,12 +2130,42 @@ void SMESH_subMesh::DeleteEventListener(EventListener* listener) */ //================================================================================ -void SMESH_subMesh::DeleteOwnListeners() +void SMESH_subMesh::deleteOwnListeners() +{ + list< OwnListenerData >::iterator d; + for ( d = _ownListeners.begin(); d != _ownListeners.end(); ++d ) + { + if ( !_father->MeshExists( d->myMeshID )) + continue; + if ( _father->GetId() == d->myMeshID && !_father->GetSubMeshContaining( d->mySubMeshID )) + continue; + d->mySubMesh->DeleteEventListener( d->myListener ); + } + _ownListeners.clear(); +} + +//======================================================================= +//function : loadDependentMeshes +//purpose : loads dependent meshes on SUBMESH_LOADED event +//======================================================================= + +void SMESH_subMesh::loadDependentMeshes() { - list< pair< SMESH_subMesh*, EventListener* > >::iterator sm_l; - for ( sm_l = myOwnListeners.begin(); sm_l != myOwnListeners.end(); ++sm_l) - sm_l->first->DeleteEventListener( sm_l->second ); - myOwnListeners.clear(); + list< OwnListenerData >::iterator d; + for ( d = _ownListeners.begin(); d != _ownListeners.end(); ++d ) + if ( _father != d->mySubMesh->_father ) + d->mySubMesh->_father->Load(); + + // map< EventListener*, EventListenerData* >::iterator l_d = _eventListeners.begin(); + // for ( ; l_d != _eventListeners.end(); ++l_d ) + // if ( l_d->second ) + // { + // const list& smList = l_d->second->mySubMeshes; + // list::const_iterator sm = smList.begin(); + // for ( ; sm != smList.end(); ++sm ) + // if ( _father != (*sm)->_father ) + // (*sm)->_father->Load(); + // } } //================================================================================ @@ -2129,3 +2276,29 @@ SMESH_subMeshIteratorPtr SMESH_subMesh::getDependsOnIterator(const bool includeS ( new _Iterator( new SMDS_mapIterator( DependsOn() ), prepend, append )); } } + +//================================================================================ +/*! + * \brief Find common submeshes (based on shared sub-shapes with other + * \param theOther submesh to check + * \param theSetOfCommon set of common submesh + */ +//================================================================================ + +bool SMESH_subMesh::FindIntersection(const SMESH_subMesh* theOther, + std::set& theSetOfCommon ) const +{ + int oldNb = theSetOfCommon.size(); + // check main submeshes + const map ::const_iterator otherEnd = theOther->_mapDepend.end(); + if ( theOther->_mapDepend.find(this->GetId()) != otherEnd ) + theSetOfCommon.insert( this ); + if ( _mapDepend.find(theOther->GetId()) != _mapDepend.end() ) + theSetOfCommon.insert( theOther ); + // check common submeshes + map ::const_iterator mapIt = _mapDepend.begin(); + for( ; mapIt != _mapDepend.end(); mapIt++ ) + if ( theOther->_mapDepend.find((*mapIt).first) != otherEnd ) + theSetOfCommon.insert( (*mapIt).second ); + return oldNb < theSetOfCommon.size(); +} diff --git a/src/SMESH/SMESH_subMesh.hxx b/src/SMESH/SMESH_subMesh.hxx index ace3a346f..ee4b96658 100644 --- a/src/SMESH/SMESH_subMesh.hxx +++ b/src/SMESH/SMESH_subMesh.hxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : SMESH_subMesh.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_SUBMESH_HXX_ #define _SMESH_SUBMESH_HXX_ @@ -34,6 +33,7 @@ #include "SMESHDS_SubMesh.hxx" #include "SMESH_Hypothesis.hxx" #include "SMESH_ComputeError.hxx" +#include "SMESH_Algo.hxx" #include "Utils_SALOME_Exception.hxx" @@ -60,7 +60,7 @@ class SMESH_EXPORT SMESH_subMesh { public: SMESH_subMesh(int Id, SMESH_Mesh * father, SMESHDS_Mesh * meshDS, - const TopoDS_Shape & aSubShape); + const TopoDS_Shape & aSubShape); virtual ~ SMESH_subMesh(); int GetId() const; @@ -74,8 +74,9 @@ class SMESH_EXPORT SMESH_subMesh SMESH_subMesh *GetFirstToCompute(); + SMESH_Algo* GetAlgo() const; + const std::map < int, SMESH_subMesh * >& DependsOn(); - //const map < int, SMESH_subMesh * >&Dependants(); /*! * \brief Return iterator on the submeshes this one depends on */ @@ -88,11 +89,11 @@ class SMESH_EXPORT SMESH_subMesh { NOT_READY, READY_TO_COMPUTE, COMPUTE_OK, FAILED_TO_COMPUTE - }; + }; enum algo_state { NO_ALGO, MISSING_HYP, HYP_OK - }; + }; enum algo_event { ADD_HYP , ADD_ALGO, @@ -100,13 +101,13 @@ class SMESH_EXPORT SMESH_subMesh ADD_FATHER_HYP , ADD_FATHER_ALGO, REMOVE_FATHER_HYP, REMOVE_FATHER_ALGO, MODIF_HYP - }; + }; enum compute_event { - MODIF_ALGO_STATE, COMPUTE, - CLEAN, SUBMESH_COMPUTED, SUBMESH_RESTORED, + MODIF_ALGO_STATE, COMPUTE, COMPUTE_CANCELED, + CLEAN, SUBMESH_COMPUTED, SUBMESH_RESTORED, SUBMESH_LOADED, MESH_ENTITY_REMOVED, CHECK_COMPUTE_STATE - }; + }; enum event_type { ALGO_EVENT, COMPUTE_EVENT @@ -123,7 +124,7 @@ class SMESH_EXPORT SMESH_subMesh * \param where - the submesh to store the listener and it's data * * The method remembers the submesh \awhere it puts the listener in order to delete - * them when HYP_OK algo_state is lost + * it when HYP_OK algo_state is lost * After being set, event listener is notified on each event of \awhere submesh. */ void SetEventListener(EventListener* listener, @@ -146,9 +147,17 @@ class SMESH_EXPORT SMESH_subMesh protected: //!< event listeners to notify - std::map< EventListener*, EventListenerData* > myEventListeners; + std::map< EventListener*, EventListenerData* > _eventListeners; + //!< event listeners to delete when HYP_OK algo_state is lost - std::list< std::pair< SMESH_subMesh*, EventListener* > > myOwnListeners; + struct OwnListenerData { + SMESH_subMesh* mySubMesh; + int myMeshID; // id of mySubMesh->GetFather() + int mySubMeshID; + EventListener* myListener; + OwnListenerData( SMESH_subMesh* sm=0, EventListener* el=0); + }; + std::list< OwnListenerData > _ownListeners; /*! * \brief Sets an event listener and its data to a submesh @@ -157,7 +166,7 @@ protected: * * After being set, event listener is notified on each event of a submesh. */ - void SetEventListener(EventListener* listener, EventListenerData* data); + void setEventListener(EventListener* listener, EventListenerData* data); /*! * \brief Notify stored event listeners on the occured event @@ -165,16 +174,22 @@ protected: * \param eventType - algo_event or compute_event * \param hyp - hypothesis, if eventType is algo_event */ - void NotifyListenersOnEvent( const int event, + void notifyListenersOnEvent( const int event, const event_type eventType, SMESH_Hypothesis* hyp = 0); /*! * \brief Delete event listeners depending on algo of this submesh */ - void DeleteOwnListeners(); + void deleteOwnListeners(); - // ================================================================== + /*! + * \brief loads dependent meshes on SUBMESH_LOADED event + */ + void loadDependentMeshes(); + + // END: Members to track non hierarchical dependencies between submeshes + // ===================================================================== public: @@ -191,6 +206,9 @@ public: void DumpAlgoState(bool isMain); bool ComputeStateEngine(int event); + void ComputeSubMeshStateEngine(int event, const bool includeSelf=false); + + bool Evaluate(MapShapeNbElems& aResMap); bool IsConform(const SMESH_Algo* theAlgo); // check if a conform mesh will be produced by the Algo @@ -223,43 +241,44 @@ public: * none mesh entity is bound to it */ void SetIsAlwaysComputed(bool isAlCo); + bool IsAlwaysComputed() { return _alwaysComputed; } + + /*! + * \brief Find common submeshes (based on shared subshapes with other + * \param theOther submesh to check + * \param theCommonIds set of common submesh IDs + * NOTE: this method does not cleat set before collect common IDs + */ + bool FindIntersection( const SMESH_subMesh * theOther, + std::set& theSetOfCommon ) const; protected: // ================================================================== - void InsertDependence(const TopoDS_Shape aSubShape); + void insertDependence(const TopoDS_Shape aSubShape); - bool SubMeshesComputed(); + bool subMeshesComputed(); + //bool SubMeshesReady(); - bool SubMeshesReady(); - - void RemoveSubMeshElementsAndNodes(); - void UpdateDependantsState(const compute_event theEvent); - void UpdateSubMeshState(const compute_state theState); - void ComputeSubMeshStateEngine(int event); - void CleanDependants(); - void CleanDependsOn(); - void SetAlgoState(int state); + void removeSubMeshElementsAndNodes(); + void updateDependantsState(const compute_event theEvent); + void updateSubMeshState(const compute_state theState); + void cleanDependants(); + void cleanDependsOn(); + void setAlgoState(int state); /*! * \brief Return a shape containing all sub-shapes of the MainShape that can be * meshed at once along with _subShape */ - TopoDS_Shape GetCollection(SMESH_Gen * theGen, + TopoDS_Shape getCollection(SMESH_Gen * theGen, SMESH_Algo* theAlgo, bool & theSubComputed); - - /*! - * \brief Apply theAlgo to all subshapes in theCollection - */ - bool ApplyToCollection (SMESH_Algo* theAlgo, - const TopoDS_Shape& theCollection); - /*! * \brief Update compute_state by _computeError * \retval bool - false if there are errors */ - bool CheckComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& theShape=TopoDS_Shape()); + bool checkComputeError(SMESH_Algo* theAlgo, const TopoDS_Shape& theShape=TopoDS_Shape()); /*! * \brief Return a hypothesis attached to theShape. @@ -268,7 +287,7 @@ protected: * is returned; else an applicable ones having theHypType * is returned */ - const SMESH_Hypothesis* GetSimilarAttached(const TopoDS_Shape& theShape, + const SMESH_Hypothesis* getSimilarAttached(const TopoDS_Shape& theShape, const SMESH_Hypothesis * theHyp, const int theHypType = 0); // diff --git a/src/SMESH/SMESH_subMeshEventListener.hxx b/src/SMESH/SMESH_subMeshEventListener.hxx index 51a79a9c2..ae911cfef 100644 --- a/src/SMESH/SMESH_subMeshEventListener.hxx +++ b/src/SMESH/SMESH_subMeshEventListener.hxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions -// File : SMESH_subMeshEventListener.hxx -// Created : Mon Nov 13 10:45:49 2006 -// Author : Edward AGAPOV (eap) + +// File : SMESH_subMeshEventListener.hxx +// Created : Mon Nov 13 10:45:49 2006 +// Author : Edward AGAPOV (eap) // #ifndef SMESH_subMeshEventListener_HeaderFile #define SMESH_subMeshEventListener_HeaderFile @@ -30,6 +30,7 @@ #include "SMESH_SMESH.hxx" #include +#include class SMESH_subMesh; class SMESH_Hypothesis; @@ -41,10 +42,21 @@ struct SMESH_subMeshEventListenerData; */ // ------------------------------------------------------------------ -class SMESH_EXPORT SMESH_subMeshEventListener { +class SMESH_EXPORT SMESH_subMeshEventListener +{ bool myIsDeletable; //!< if true, it will be deleted by SMESH_subMesh -public: - SMESH_subMeshEventListener(bool isDeletable):myIsDeletable(isDeletable) {} + mutable std::set myBusySM; //!< to avoid infinite recursion via events + friend class SMESH_subMesh; +#ifdef _DEBUG_ + const char* myName; //!< identifier used for debug +#endif + + public: + SMESH_subMeshEventListener(bool isDeletable, const char* name) :myIsDeletable(isDeletable) +#ifdef _DEBUG_ + ,myName(name) +#endif + {} bool IsDeletable() const { return myIsDeletable; } /*! * \brief Do something on a certain event @@ -54,9 +66,9 @@ public: * \param data - listener data stored in the subMesh * \param hyp - hypothesis, if eventType is algo_event * - * The base implementation translates CLEAN event to the subMesh stored - * in the listener data. Also it sends SUBMESH_COMPUTED event in case of - * successful COMPUTE event. + * The base implementation (see SMESH_subMesh.cxx) translates CLEAN event + * to the subMeshes stored in the listener data. Also it sends SUBMESH_COMPUTED + * event in case of successful COMPUTE event. */ virtual void ProcessEvent(const int event, const int eventType, @@ -75,10 +87,13 @@ struct SMESH_subMeshEventListenerData { bool myIsDeletable; //!< if true, it will be deleted by SMESH_subMesh int myType; //!< to recognize data type - std::list mySubMeshes; //!< generally: submeshes depending - // on the one storing this data + std::list mySubMeshes; /* generally: submeshes depending + on the one storing this data; + !! they are used to track intermesh + dependencies at mesh loading as well !! */ public: SMESH_subMeshEventListenerData(bool isDeletable):myIsDeletable(isDeletable) {} + virtual ~SMESH_subMeshEventListenerData() {} bool IsDeletable() const { return myIsDeletable; } /*! diff --git a/src/SMESH/memoire.h b/src/SMESH/memoire.h new file mode 100644 index 000000000..258eda28b --- /dev/null +++ b/src/SMESH/memoire.h @@ -0,0 +1,43 @@ +// Copyright (C) 2010-2012 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 +// + +#ifndef _MEMOIRE_H_ +#define _MEMOIRE_H_ + +#include +#include + +void memostat(const char* f, int l); + +void memostat(const char* f, int l) +{ +#ifdef WIN32 + //rnv: TODO: find alternative of the malloc_stats() on windows platform +#else + /* struct mallinfo mem = mallinfo(); */ + /* std::cerr << f << ":"<< l << " " << mem.arena << " " << mem.ordblks << " " << mem.hblks << " " << mem.hblkhd << " " << mem.uordblks << " " << mem.fordblks << " " << mem.keepcost << std::endl; */ + std::cerr << f << ":" << l << " --------------------------" << std::endl; + malloc_stats(); + std::cerr << f << ":" << l << " --------------------------" << std::endl; +#endif +} + +#define MEMOSTAT //memostat( __FILE__, __LINE__ ) + +#endif diff --git a/src/SMESHClient/Makefile.am b/src/SMESHClient/Makefile.am index ebb0d97f7..86fd92dc0 100644 --- a/src/SMESHClient/Makefile.am +++ b/src/SMESHClient/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # GEOM GEOMClient : tool to transfer BREP files from GEOM server to GEOM client # File : Makefile.in # Author : Pavel TELKOV (OCC) @@ -47,6 +45,7 @@ libSMESHClient_la_CPPFLAGS = \ $(MED_CXXFLAGS) \ $(GEOM_CXXFLAGS) \ $(BOOST_CPPFLAGS) \ + $(VTK_INCLUDES) \ $(CAS_CPPFLAGS) \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ @@ -59,8 +58,7 @@ libSMESHClient_la_CPPFLAGS = \ -I$(srcdir)/../SMDS \ -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../SMESH \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix + -I$(top_builddir)/idl libSMESHClient_la_LDFLAGS = \ ../../idl/libSalomeIDLSMESH.la \ @@ -68,15 +66,16 @@ libSMESHClient_la_LDFLAGS = \ ../SMESH/libSMESHimpl.la \ ../SMESHDS/libSMESHDS.la \ ../Controls/libSMESHControls.la \ - $(KERNEL_LDFLAGS) -lSalomeLifeCycleCORBA \ - $(GEOM_LDFLAGS) -lNMTDS \ - $(MED_LDFLAGS) -lMEDWrapper_V2_2 -lMEDWrapper_V2_1 \ + $(KERNEL_LDFLAGS) -lSalomeLifeCycleCORBA -lSalomeNS -lOpUtil -lSALOMEBasics \ + -lSalomeIDLKernel -lSALOMELocalTrace \ + $(GEOM_LDFLAGS) -lNMTDS -lSalomeIDLGEOM \ + $(MED_LDFLAGS) -lMEDWrapper_V2_2 -lMEDWrapper_V2_1 -lSalomeIDLMED -lMEDWrapper \ $(CAS_KERNEL) SMESHClientBin_CPPFLAGS = \ $(libSMESHClient_la_CPPFLAGS) SMESHClientBin_LDADD = \ - $(libSMESHClient_la_LDFLAGS) + $(libSMESHClient_la_LDFLAGS) @CORBA_LIBS@ diff --git a/src/SMESHClient/SMESHClientBin.cxx b/src/SMESHClient/SMESHClientBin.cxx index df47e31f1..3183818ff 100644 --- a/src/SMESHClient/SMESHClientBin.cxx +++ b/src/SMESHClient/SMESHClientBin.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : // Author : // Module : diff --git a/src/SMESHClient/SMESH_Client.cxx b/src/SMESHClient/SMESH_Client.cxx index af83fe041..f7195b89e 100644 --- a/src/SMESHClient/SMESH_Client.cxx +++ b/src/SMESHClient/SMESH_Client.cxx @@ -1,31 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESHClient : tool to update client mesh structure by mesh from server // File : SMESH_Client.cxx // Author : Pavel TELKOV // Module : SMESH -// + #include "SMESH_Client.hxx" #include "SMESH_Mesh.hxx" +#include "SMESHDS_Script.hxx" #include "SALOME_NamingService.hxx" #include "SALOME_LifeCycleCORBA.hxx" @@ -54,7 +54,7 @@ #endif #ifdef _DEBUG_ -static int MYDEBUG = 0; +static int MYDEBUG = 1; #else static int MYDEBUG = 0; #endif @@ -83,9 +83,9 @@ namespace //======================================================================= //function : AddNodesWithID //======================================================================= - inline void AddNodesWithID(SMDS_Mesh* theMesh, - SMESH::log_array_var& theSeq, - CORBA::Long theId) + inline void AddNodesWithID(SMDS_Mesh* theMesh, + SMESH::log_array_var& theSeq, + CORBA::Long theId) { const SMESH::double_array& aCoords = theSeq[theId].coords; const SMESH::long_array& anIndexes = theSeq[theId].indexes; @@ -94,11 +94,59 @@ namespace EXCEPTION(runtime_error,"AddNodesWithID - 3*aNbElems != aCoords.length()"); for(CORBA::Long aCoordId = 0; anElemId < aNbElems; anElemId++, aCoordId+=3){ SMDS_MeshElement* anElem = theMesh->AddNodeWithID(aCoords[aCoordId], - aCoords[aCoordId+1], - aCoords[aCoordId+2], - anIndexes[anElemId]); + aCoords[aCoordId+1], + aCoords[aCoordId+2], + anIndexes[anElemId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddNodeWithID for ID = "<Add0DElementWithID(anIndexes[anIndexId+1], + anIndexes[anIndexId]); + if (!anElem) + EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot Add0DElementWithID for ID = "<AddBallWithID(anIndexes[anIndexId+1], + aDiameter[anElemId], + anIndexes[anIndexId]); + if (!anElem) + EXCEPTION(runtime_error,"cannot SMDS_Mesh::AddBallsWithID for ID = "<AddEdgeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddEdgeWithID for ID = "<AddFaceWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddFaceWithID for ID = "<AddFaceWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddFaceWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId+11], + anIndexes[anIndexId+12], + anIndexes[anIndexId]); + if(!anElem) + EXCEPTION(runtime_error,"AddHexPrismWithID - cannot AddVolumeWithID for ID = "<AddPolyhedralVolumeWithID(nodes_ids, quantities, aFaceId); if (!anElem) - EXCEPTION(runtime_error, "SMDS_Mesh::FindElement - cannot AddPolyhedralVolumeWithID for ID = " + EXCEPTION(runtime_error, "SMDS_Mesh::FindElement - cannot AddPolyhedralVolumeWithID for ID = " << anElemId); } } @@ -332,7 +410,7 @@ namespace //======================================================================= //function : AddQuadEdgesWithID //======================================================================= - inline void AddQuadEdgesWithID(SMDS_Mesh* theMesh, + inline void AddQuadEdgesWithID(SMDS_Mesh* theMesh, SMESH::log_array_var& theSeq, CORBA::Long theId) { @@ -342,11 +420,11 @@ namespace EXCEPTION(runtime_error,"AddQuadEdgeWithID - 4*aNbElems != aCoords.length()"); for(CORBA::Long anIndexId = 0; anElemId < aNbElems; anElemId++, anIndexId+=4){ SMDS_MeshElement* anElem = theMesh->AddEdgeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddEdgeWithID for ID = "<AddFaceWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddFaceWithID for ID = "<AddFaceWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddFaceWithID for ID = "<AddFaceWithID(anIndexes[anIndexId+1], + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId]); + if(!anElem) + EXCEPTION(runtime_error,"AddBiQuadQuadsWithID() - cannot AddFaceWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId+9], - anIndexes[anIndexId+10], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId+9], - anIndexes[anIndexId+10], - anIndexes[anIndexId+11], - anIndexes[anIndexId+12], - anIndexes[anIndexId+13], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId+11], + anIndexes[anIndexId+12], + anIndexes[anIndexId+13], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId+9], - anIndexes[anIndexId+10], - anIndexes[anIndexId+11], - anIndexes[anIndexId+12], - anIndexes[anIndexId+13], - anIndexes[anIndexId+14], - anIndexes[anIndexId+15], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId+11], + anIndexes[anIndexId+12], + anIndexes[anIndexId+13], + anIndexes[anIndexId+14], + anIndexes[anIndexId+15], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], - anIndexes[anIndexId+2], - anIndexes[anIndexId+3], - anIndexes[anIndexId+4], - anIndexes[anIndexId+5], - anIndexes[anIndexId+6], - anIndexes[anIndexId+7], - anIndexes[anIndexId+8], - anIndexes[anIndexId+9], - anIndexes[anIndexId+10], - anIndexes[anIndexId+11], - anIndexes[anIndexId+12], - anIndexes[anIndexId+13], - anIndexes[anIndexId+14], - anIndexes[anIndexId+15], - anIndexes[anIndexId+16], - anIndexes[anIndexId+17], - anIndexes[anIndexId+18], - anIndexes[anIndexId+19], - anIndexes[anIndexId+20], - anIndexes[anIndexId]); + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId+11], + anIndexes[anIndexId+12], + anIndexes[anIndexId+13], + anIndexes[anIndexId+14], + anIndexes[anIndexId+15], + anIndexes[anIndexId+16], + anIndexes[anIndexId+17], + anIndexes[anIndexId+18], + anIndexes[anIndexId+19], + anIndexes[anIndexId+20], + anIndexes[anIndexId]); if(!anElem) - EXCEPTION(runtime_error,"SMDS_Mesh::FindElement - cannot AddVolumeWithID for ID = "<AddVolumeWithID(anIndexes[anIndexId+1], + anIndexes[anIndexId+2], + anIndexes[anIndexId+3], + anIndexes[anIndexId+4], + anIndexes[anIndexId+5], + anIndexes[anIndexId+6], + anIndexes[anIndexId+7], + anIndexes[anIndexId+8], + anIndexes[anIndexId+9], + anIndexes[anIndexId+10], + anIndexes[anIndexId+11], + anIndexes[anIndexId+12], + anIndexes[anIndexId+13], + anIndexes[anIndexId+14], + anIndexes[anIndexId+15], + anIndexes[anIndexId+16], + anIndexes[anIndexId+17], + anIndexes[anIndexId+18], + anIndexes[anIndexId+19], + anIndexes[anIndexId+20], + anIndexes[anIndexId+21], + anIndexes[anIndexId+22], + anIndexes[anIndexId+23], + anIndexes[anIndexId+24], + anIndexes[anIndexId+25], + anIndexes[anIndexId+26], + anIndexes[anIndexId+27], + anIndexes[anIndexId]); + if(!anElem) + EXCEPTION(runtime_error,"AddTriQuadHexasWithID() - cannot AddVolumeWithID for ID = "<GetContainerRef(); CORBA::String_var aServerHostName = aServerContainer->getHostName(); @@ -604,14 +754,15 @@ SMESH_Client::GetSMESHGen(CORBA::ORB_ptr theORB, //======================================================================= // function : Create() -// purpose : +// purpose : //======================================================================= SMESH_Client::SMESH_Client(CORBA::ORB_ptr theORB, - SMESH::SMESH_Mesh_ptr theMesh): + SMESH::SMESH_Mesh_ptr theMesh): myMeshServer(SMESH::SMESH_Mesh::_duplicate(theMesh)), mySMESHDSMesh(NULL), mySMDSMesh(NULL) { + MESSAGE("SMESH_Client::SMESH_Client"); myMeshServer->Register(); CORBA::Boolean anIsEmbeddedMode; @@ -627,7 +778,8 @@ SMESH_Client::SMESH_Client(CORBA::ORB_ptr theORB, SMESH_Mesh* aMesh = reinterpret_cast (pointeur); if ( MYDEBUG ) MESSAGE("SMESH_Client::SMESH_Client aMesh "<GetMeshDS()->IsEmbeddedMode()){ + //if(aMesh->GetMeshDS()->IsEmbeddedMode()){ + if(anIsEmbeddedMode){ mySMESHDSMesh = aMesh->GetMeshDS(); mySMDSMesh = mySMESHDSMesh; } @@ -643,17 +795,17 @@ SMESH_Client::SMESH_Client(CORBA::ORB_ptr theORB, //================================================================================= SMESH_Client::~SMESH_Client() { - myMeshServer->Destroy(); + myMeshServer->UnRegister(); if(!mySMESHDSMesh) delete mySMDSMesh; } //================================================================================= -SMDS_Mesh* -SMESH_Client::GetMesh() const +SMDS_Mesh* +SMESH_Client::GetMesh() const { - return mySMDSMesh; + return mySMDSMesh; } @@ -669,7 +821,7 @@ SMESH_Client::operator->() const SMESH::SMESH_Mesh_ptr SMESH_Client::GetMeshServer() { - return myMeshServer.in(); + return myMeshServer.in(); } @@ -682,16 +834,18 @@ SMESH_Client::Update(bool theIsClear) { bool anIsModified = true; if(mySMESHDSMesh){ + MESSAGE("Update mySMESHDSMesh"); SMESHDS_Script* aScript = mySMESHDSMesh->GetScript(); anIsModified = aScript->IsModified(); aScript->SetModified(false); }else{ + MESSAGE("Update CORBA"); SMESH::log_array_var aSeq = myMeshServer->GetLog( theIsClear ); CORBA::Long aLength = aSeq->length(); anIsModified = aLength > 0; if( MYDEBUG ) MESSAGE( "Update: length of the script is "<Clear(); @@ -734,7 +893,7 @@ SMESH_Client::Update(bool theIsClear) for( ; anElemId < aNbElems; anElemId++ ) mySMDSMesh->RemoveNode( FindNode( mySMDSMesh, anIndexes[anElemId] ) ); break; - + case SMESH::REMOVE_ELEMENT: for( ; anElemId < aNbElems; anElemId++ ) mySMDSMesh->RemoveElement( FindElement( mySMDSMesh, anIndexes[anElemId] ) ); @@ -775,9 +934,9 @@ SMESH_Client::Update(bool theIsClear) mySMDSMesh->Renumber( anIndexes[i], anIndexes[i+1], anIndexes[i+2] ); } break; - + default:; - } + } } } catch ( SALOME::SALOME_Exception& exc ) @@ -796,11 +955,12 @@ SMESH_Client::Update(bool theIsClear) if ( MYDEBUG && mySMDSMesh ) { MESSAGE("Update - mySMDSMesh->NbNodes() = "<NbNodes()); + MESSAGE("Update - mySMDSMesh->Nb0DElements() = "<Nb0DElements()); MESSAGE("Update - mySMDSMesh->NbEdges() = "<NbEdges()); MESSAGE("Update - mySMDSMesh->NbFaces() = "<NbFaces()); MESSAGE("Update - mySMDSMesh->NbVolumes() = "<NbVolumes()); } } // end of update mesh by log script - + return anIsModified; } diff --git a/src/SMESHClient/SMESH_Client.hxx b/src/SMESHClient/SMESH_Client.hxx index 99e3de4b8..65c111c2e 100644 --- a/src/SMESHClient/SMESH_Client.hxx +++ b/src/SMESHClient/SMESH_Client.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHClient : tool to update client mesh structure by mesh from server // File : SMESH_Client.hxx // Author : Pavel TELKOV @@ -32,7 +33,7 @@ #include CORBA_SERVER_HEADER(SMESH_Mesh) #ifdef WNT -# ifdef SMESHCLIENT_EXPORTS +# if defined SMESHCLIENT_EXPORTS || defined SMESHClient_EXPORTS # define SMESHCLIENT_EXPORT __declspec( dllexport ) # else # define SMESHCLIENT_EXPORT __declspec( dllimport ) @@ -54,10 +55,10 @@ public: static SMESH::SMESH_Gen_var GetSMESHGen(CORBA::ORB_ptr theORB, - CORBA::Boolean& theIsEmbeddedMode); + CORBA::Boolean& theIsEmbeddedMode); SMESH_Client(CORBA::ORB_ptr theORB, - SMESH::SMESH_Mesh_ptr theMesh); + SMESH::SMESH_Mesh_ptr theMesh); ~SMESH_Client(); bool diff --git a/src/SMESHDS/Makefile.am b/src/SMESHDS/Makefile.am index b9c3673f3..745ccca8f 100644 --- a/src/SMESHDS/Makefile.am +++ b/src/SMESHDS/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESHDS : management of mesh data and SMESH document # File : Makefile.in # Author : Patrick GOLDBRONN (CEA) @@ -39,8 +37,10 @@ salomeinclude_HEADERS = \ SMESHDS_GroupBase.hxx \ SMESHDS_Group.hxx \ SMESHDS_GroupOnGeom.hxx \ + SMESHDS_GroupOnFilter.hxx \ SMESH_SMESHDS.hxx \ - SMESHDS_DataMapOfShape.hxx + SMESHDS_DataMapOfShape.hxx \ + SMESH_Controls.hxx # Libraries targets lib_LTLIBRARIES = libSMESHDS.la @@ -54,12 +54,14 @@ dist_libSMESHDS_la_SOURCES = \ SMESHDS_Mesh.cxx \ SMESHDS_GroupBase.cxx \ SMESHDS_Group.cxx \ - SMESHDS_GroupOnGeom.cxx + SMESHDS_GroupOnGeom.cxx \ + SMESHDS_GroupOnFilter.cxx # additionnal information to compil and link file libSMESHDS_la_CPPFLAGS = \ $(KERNEL_CXXFLAGS) \ $(CAS_CPPFLAGS) \ + $(VTK_INCLUDES) \ $(BOOST_CPPFLAGS) \ -I$(srcdir)/../SMDS diff --git a/src/SMESHDS/SMESHDS_Command.cxx b/src/SMESHDS/SMESHDS_Command.cxx index be0e5e632..658ad24ae 100644 --- a/src/SMESHDS/SMESHDS_Command.cxx +++ b/src/SMESHDS/SMESHDS_Command.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESH_Command.cxx // Author : Yves FRICAUD, OCC // Module : SMESH -// $Header: // #include "SMESHDS_Command.hxx" @@ -54,16 +54,16 @@ SMESHDS_Command::~SMESHDS_Command() //======================================================================= void SMESHDS_Command::AddNode(int NewNodeID, double x, double y, double z) { - if (!myType == SMESHDS_AddNode) - { - MESSAGE("SMESHDS_Command::AddNode : Bad Type"); - return; - } - myIntegers.push_back(NewNodeID); - myReals.push_back(x); - myReals.push_back(y); - myReals.push_back(z); - myNumber++; + if (!myType == SMESHDS_AddNode) + { + MESSAGE("SMESHDS_Command::AddNode : Bad Type"); + return; + } + myIntegers.push_back(NewNodeID); + myReals.push_back(x); + myReals.push_back(y); + myReals.push_back(z); + myNumber++; } //======================================================================= @@ -72,16 +72,32 @@ void SMESHDS_Command::AddNode(int NewNodeID, double x, double y, double z) //======================================================================= void SMESHDS_Command::MoveNode(int NodeID, double x, double y, double z) { - if (!myType == SMESHDS_MoveNode) - { - MESSAGE("SMESHDS_Command::MoveNode : Bad Type"); - return; - } - myIntegers.push_back(NodeID); - myReals.push_back(x); - myReals.push_back(y); - myReals.push_back(z); - myNumber++; + if (!myType == SMESHDS_MoveNode) + { + MESSAGE("SMESHDS_Command::MoveNode : Bad Type"); + return; + } + myIntegers.push_back(NodeID); + myReals.push_back(x); + myReals.push_back(y); + myReals.push_back(z); + myNumber++; +} + +//======================================================================= +//function : +//purpose : +//======================================================================= +void SMESHDS_Command::Add0DElement(int New0DElementID, int idnode) +{ + if (!myType == SMESHDS_Add0DElement) + { + MESSAGE("SMESHDS_Command::Add0DElement : Bad Type"); + return; + } + myIntegers.push_back(New0DElementID); + myIntegers.push_back(idnode); + myNumber++; } //======================================================================= @@ -90,15 +106,15 @@ void SMESHDS_Command::MoveNode(int NodeID, double x, double y, double z) //======================================================================= void SMESHDS_Command::AddEdge(int NewEdgeID, int idnode1, int idnode2) { - if (!myType == SMESHDS_AddEdge) - { - MESSAGE("SMESHDS_Command::AddEdge : Bad Type"); - return; - } - myIntegers.push_back(NewEdgeID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myNumber++; + if (!myType == SMESHDS_AddEdge) + { + MESSAGE("SMESHDS_Command::AddEdge : Bad Type"); + return; + } + myIntegers.push_back(NewEdgeID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myNumber++; } //======================================================================= @@ -106,18 +122,18 @@ void SMESHDS_Command::AddEdge(int NewEdgeID, int idnode1, int idnode2) //purpose : //======================================================================= void SMESHDS_Command::AddFace(int NewFaceID, - int idnode1, int idnode2, int idnode3) + int idnode1, int idnode2, int idnode3) { - if (!myType == SMESHDS_AddTriangle) - { - MESSAGE("SMESHDS_Command::AddFace : Bad Type"); - return; - } - myIntegers.push_back(NewFaceID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myNumber++; + if (!myType == SMESHDS_AddTriangle) + { + MESSAGE("SMESHDS_Command::AddFace : Bad Type"); + return; + } + myIntegers.push_back(NewFaceID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myNumber++; } //======================================================================= @@ -125,19 +141,19 @@ void SMESHDS_Command::AddFace(int NewFaceID, //purpose : //======================================================================= void SMESHDS_Command::AddFace(int NewFaceID, - int idnode1, int idnode2, int idnode3, int idnode4) + int idnode1, int idnode2, int idnode3, int idnode4) { - if (!myType == SMESHDS_AddQuadrangle) - { - MESSAGE("SMESHDS_Command::AddFace : Bad Type"); - return; - } - myIntegers.push_back(NewFaceID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myIntegers.push_back(idnode4); - myNumber++; + if (!myType == SMESHDS_AddQuadrangle) + { + MESSAGE("SMESHDS_Command::AddFace : Bad Type"); + return; + } + myIntegers.push_back(NewFaceID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myNumber++; } //======================================================================= @@ -145,19 +161,19 @@ void SMESHDS_Command::AddFace(int NewFaceID, //purpose : //======================================================================= void SMESHDS_Command::AddVolume(int NewVolID, - int idnode1, int idnode2, int idnode3, int idnode4) + int idnode1, int idnode2, int idnode3, int idnode4) { - if (!myType == SMESHDS_AddTetrahedron) - { - MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); - return; - } - myIntegers.push_back(NewVolID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myIntegers.push_back(idnode4); - myNumber++; + if (!myType == SMESHDS_AddTetrahedron) + { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myNumber++; } //======================================================================= @@ -165,20 +181,20 @@ void SMESHDS_Command::AddVolume(int NewVolID, //purpose : //======================================================================= void SMESHDS_Command::AddVolume(int NewVolID, - int idnode1, int idnode2, int idnode3, int idnode4, int idnode5) + int idnode1, int idnode2, int idnode3, int idnode4, int idnode5) { - if (!myType == SMESHDS_AddPyramid) - { - MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); - return; - } - myIntegers.push_back(NewVolID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myIntegers.push_back(idnode4); - myIntegers.push_back(idnode5); - myNumber++; + if (!myType == SMESHDS_AddPyramid) + { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myIntegers.push_back(idnode5); + myNumber++; } //======================================================================= @@ -186,22 +202,22 @@ void SMESHDS_Command::AddVolume(int NewVolID, //purpose : //======================================================================= void SMESHDS_Command::AddVolume(int NewVolID, - int idnode1, - int idnode2, int idnode3, int idnode4, int idnode5, int idnode6) + int idnode1, + int idnode2, int idnode3, int idnode4, int idnode5, int idnode6) { - if (!myType == SMESHDS_AddPrism) - { - MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); - return; - } - myIntegers.push_back(NewVolID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myIntegers.push_back(idnode4); - myIntegers.push_back(idnode5); - myIntegers.push_back(idnode6); - myNumber++; + if (!myType == SMESHDS_AddPrism) + { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myIntegers.push_back(idnode5); + myIntegers.push_back(idnode6); + myNumber++; } //======================================================================= @@ -209,34 +225,60 @@ void SMESHDS_Command::AddVolume(int NewVolID, //purpose : //======================================================================= void SMESHDS_Command::AddVolume(int NewVolID, - int idnode1, - int idnode2, - int idnode3, - int idnode4, int idnode5, int idnode6, int idnode7, int idnode8) -{ - if (!myType == SMESHDS_AddHexahedron) - { - MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); - return; - } - myIntegers.push_back(NewVolID); - myIntegers.push_back(idnode1); - myIntegers.push_back(idnode2); - myIntegers.push_back(idnode3); - myIntegers.push_back(idnode4); - myIntegers.push_back(idnode5); - myIntegers.push_back(idnode6); - myIntegers.push_back(idnode7); - myIntegers.push_back(idnode8); - myNumber++; + int idnode1, + int idnode2, + int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8) +{ + if (!myType == SMESHDS_AddHexahedron) + { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myIntegers.push_back(idnode5); + myIntegers.push_back(idnode6); + myIntegers.push_back(idnode7); + myIntegers.push_back(idnode8); + myNumber++; +} + +void SMESHDS_Command::AddVolume(int NewVolID, + int idnode1,int idnode2,int idnode3,int idnode4, + int idnode5, int idnode6, int idnode7, int idnode8, + int idnode9, int idnode10, int idnode11, int idnode12) +{ + if (myType != SMESHDS_AddHexagonalPrism) + { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(idnode1); + myIntegers.push_back(idnode2); + myIntegers.push_back(idnode3); + myIntegers.push_back(idnode4); + myIntegers.push_back(idnode5); + myIntegers.push_back(idnode6); + myIntegers.push_back(idnode7); + myIntegers.push_back(idnode8); + myIntegers.push_back(idnode9); + myIntegers.push_back(idnode10); + myIntegers.push_back(idnode11); + myIntegers.push_back(idnode12); + myNumber++; } //======================================================================= //function : AddPolygonalFace //purpose : //======================================================================= -void SMESHDS_Command::AddPolygonalFace (const int ElementID, - std::vector nodes_ids) +void SMESHDS_Command::AddPolygonalFace (const int ElementID, + const std::vector& nodes_ids) { if (!myType == SMESHDS_AddPolygon) { MESSAGE("SMESHDS_Command::AddPolygonalFace : Bad Type"); @@ -257,9 +299,9 @@ void SMESHDS_Command::AddPolygonalFace (const int ElementID, //function : AddPolyhedralVolume //purpose : //======================================================================= -void SMESHDS_Command::AddPolyhedralVolume (const int ElementID, - std::vector nodes_ids, - std::vector quantities) +void SMESHDS_Command::AddPolyhedralVolume (const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities) { if (!myType == SMESHDS_AddPolyhedron) { MESSAGE("SMESHDS_Command::AddPolyhedralVolume : Bad Type"); @@ -288,13 +330,13 @@ void SMESHDS_Command::AddPolyhedralVolume (const int ElementID, //======================================================================= void SMESHDS_Command::RemoveNode(int NodeID) { - if (!myType == SMESHDS_RemoveNode) - { - MESSAGE("SMESHDS_Command::RemoveNode : Bad Type"); - return; - } - myIntegers.push_back(NodeID); - myNumber++; + if (!myType == SMESHDS_RemoveNode) + { + MESSAGE("SMESHDS_Command::RemoveNode : Bad Type"); + return; + } + myIntegers.push_back(NodeID); + myNumber++; } //======================================================================= @@ -303,13 +345,13 @@ void SMESHDS_Command::RemoveNode(int NodeID) //======================================================================= void SMESHDS_Command::RemoveElement(int ElementID) { - if (!myType == SMESHDS_RemoveElement) - { - MESSAGE("SMESHDS_Command::RemoveElement : Bad Type"); - return; - } - myIntegers.push_back(ElementID); - myNumber++; + if (!myType == SMESHDS_RemoveElement) + { + MESSAGE("SMESHDS_Command::RemoveElement : Bad Type"); + return; + } + myIntegers.push_back(ElementID); + myNumber++; } //======================================================================= @@ -336,9 +378,9 @@ void SMESHDS_Command::ChangeElementNodes(int ElementID, int nodes[], int nbnodes //function : ChangePolyhedronNodes //purpose : //======================================================================= -void SMESHDS_Command::ChangePolyhedronNodes (const int ElementID, - std::vector nodes_ids, - std::vector quantities) +void SMESHDS_Command::ChangePolyhedronNodes (const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities) { if (myType != SMESHDS_ChangePolyhedronNodes) { @@ -386,7 +428,7 @@ void SMESHDS_Command::Renumber (const bool isNodes, const int startID, const int //======================================================================= SMESHDS_CommandType SMESHDS_Command::GetType() { - return myType; + return myType; } //======================================================================= @@ -395,7 +437,7 @@ SMESHDS_CommandType SMESHDS_Command::GetType() //======================================================================= int SMESHDS_Command::GetNumber() { - return myNumber; + return myNumber; } //======================================================================= @@ -404,7 +446,7 @@ int SMESHDS_Command::GetNumber() //======================================================================= const list < int >&SMESHDS_Command::GetIndexes() { - return myIntegers; + return myIntegers; } //======================================================================= @@ -413,7 +455,7 @@ const list < int >&SMESHDS_Command::GetIndexes() //======================================================================= const list < double >&SMESHDS_Command::GetCoords() { - return myReals; + return myReals; } @@ -484,6 +526,31 @@ void SMESHDS_Command::AddFace(int NewFaceID, myNumber++; } +//======================================================================= +//function : AddFace +//purpose : +//======================================================================= +void SMESHDS_Command::AddFace(int NewFaceID, + int n1, int n2, int n3, int n4, + int n12, int n23, int n34, int n41, int nCenter) +{ + if (myType != SMESHDS_AddBiQuadQuadrangle) { + MESSAGE("SMESHDS_Command::AddFace : Bad Type"); + return; + } + myIntegers.push_back(NewFaceID); + myIntegers.push_back(n1); + myIntegers.push_back(n2); + myIntegers.push_back(n3); + myIntegers.push_back(n4); + myIntegers.push_back(n12); + myIntegers.push_back(n23); + myIntegers.push_back(n34); + myIntegers.push_back(n41); + myIntegers.push_back(nCenter); + myNumber++; +} + //======================================================================= //function : AddVolume //purpose : @@ -611,3 +678,68 @@ void SMESHDS_Command::AddVolume(int NewVolID, int n1, int n2, int n3, myNumber++; } +//======================================================================= +//function : AddVolume +//purpose : +//======================================================================= +void SMESHDS_Command::AddVolume(int NewVolID, int n1, int n2, int n3, + int n4, int n5, int n6, int n7, int n8, + int n12, int n23, int n34, int n41, + int n56, int n67, int n78, int n85, + int n15, int n26, int n37, int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter) +{ + if (!myType == SMESHDS_AddQuadHexahedron) { + MESSAGE("SMESHDS_Command::AddVolume : Bad Type"); + return; + } + myIntegers.push_back(NewVolID); + myIntegers.push_back(n1); + myIntegers.push_back(n2); + myIntegers.push_back(n3); + myIntegers.push_back(n4); + myIntegers.push_back(n5); + myIntegers.push_back(n6); + myIntegers.push_back(n7); + myIntegers.push_back(n8); + myIntegers.push_back(n12); + myIntegers.push_back(n23); + myIntegers.push_back(n34); + myIntegers.push_back(n41); + myIntegers.push_back(n56); + myIntegers.push_back(n67); + myIntegers.push_back(n78); + myIntegers.push_back(n85); + myIntegers.push_back(n15); + myIntegers.push_back(n26); + myIntegers.push_back(n37); + myIntegers.push_back(n48); + myIntegers.push_back(n1234); + myIntegers.push_back(n1256); + myIntegers.push_back(n2367); + myIntegers.push_back(n3478); + myIntegers.push_back(n1458); + myIntegers.push_back(n5678); + myIntegers.push_back(nCenter); + myNumber++; +} + +//================================================================================ +/*! + * \brief Record adding a Ball + */ +//================================================================================ + +void SMESHDS_Command::AddBall(int NewBallID, int node, double diameter) +{ + if (!myType == SMESHDS_AddBall) + { + MESSAGE("SMESHDS_Command::SMESHDS_AddBall : Bad Type"); + return; + } + myIntegers.push_back(NewBallID); + myIntegers.push_back(node); + myReals.push_back(diameter); + myNumber++; +} diff --git a/src/SMESHDS/SMESHDS_Command.hxx b/src/SMESHDS/SMESHDS_Command.hxx index 09d720b9a..72acadc58 100644 --- a/src/SMESHDS/SMESHDS_Command.hxx +++ b/src/SMESHDS/SMESHDS_Command.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Command.hxx // Module : SMESH @@ -36,31 +37,38 @@ class SMESHDS_EXPORT SMESHDS_Command { public: - SMESHDS_Command(const SMESHDS_CommandType aType); - void AddNode(int NewNodeID, double x, double y, double z); - void AddEdge(int NewEdgeID, int idnode1, int idnode2); - void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3); - void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3, - int idnode4); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5, int idnode6); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5, int idnode6, int idnode7, int idnode8); - void AddPolygonalFace (const int ElementID, - std::vector nodes_ids); - void AddPolyhedralVolume (const int ElementID, - std::vector nodes_ids, - std::vector quantities); + SMESHDS_Command(const SMESHDS_CommandType aType); + void AddNode(int NewNodeID, double x, double y, double z); + void Add0DElement(int New0DElementID, int idnode); + void AddEdge(int NewEdgeID, int idnode1, int idnode2); + void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3); + void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3, + int idnode4); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8, + int idnode9, int idnode10, int idnode11, int idnode12); + void AddPolygonalFace (const int ElementID, + const std::vector& nodes_ids); + void AddPolyhedralVolume (const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities); + void AddBall(int NewBallID, int node, double diameter); // special methods for quadratic elements - void AddEdge(int NewEdgeID, int n1, int n2, int n12); + void AddEdge(int NewEdgeID, int n1, int n2, int n12); void AddFace(int NewFaceID, int n1, int n2, int n3, int n12, int n23, int n31); void AddFace(int NewFaceID, int n1, int n2, int n3, int n4, int n12, int n23, int n34, int n41); + void AddFace(int NewFaceID, int n1, int n2, int n3, int n4, + int n12, int n23, int n34, int n41, int nCenter); void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, int n12, int n23, int n31, int n14, int n24, int n34); void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, int n5, @@ -72,28 +80,35 @@ class SMESHDS_EXPORT SMESHDS_Command int n45, int n56, int n64, int n14, int n25, int n36); void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, - int n5, int n6, int n7, int n8, + int n5, int n6, int n7, int n8, int n12, int n23, int n34, int n41, int n56, int n67, int n78, int n85, int n15, int n26, int n37, int n48); + void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12, int n23, int n34, int n41, + int n56, int n67, int n78, int n85, + int n15, int n26, int n37, int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter); - void MoveNode(int NewNodeID, double x, double y, double z); - void RemoveNode(int NodeID); - void RemoveElement(int ElementID); - void ChangeElementNodes(int ElementID, int nodes[], int nbnodes); - void ChangePolyhedronNodes(const int ElementID, - std::vector nodes_ids, - std::vector quantities); - void Renumber (const bool isNodes, const int startID, const int deltaID); - SMESHDS_CommandType GetType(); - int GetNumber(); - const std::list & GetIndexes(); - const std::list & GetCoords(); - ~SMESHDS_Command(); + void MoveNode(int NewNodeID, double x, double y, double z); + void RemoveNode(int NodeID); + void RemoveElement(int ElementID); + void ChangeElementNodes(int ElementID, int nodes[], int nbnodes); + void ChangePolyhedronNodes(const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities); + void Renumber (const bool isNodes, const int startID, const int deltaID); + SMESHDS_CommandType GetType(); + int GetNumber(); + const std::list & GetIndexes(); + const std::list & GetCoords(); + ~SMESHDS_Command(); private: - SMESHDS_CommandType myType; - int myNumber; - std::list myReals; - std::list myIntegers; + SMESHDS_CommandType myType; + int myNumber; + std::list myReals; + std::list myIntegers; }; #endif diff --git a/src/SMESHDS/SMESHDS_CommandType.hxx b/src/SMESHDS/SMESHDS_CommandType.hxx index bafaba863..7870d1e67 100644 --- a/src/SMESHDS/SMESHDS_CommandType.hxx +++ b/src/SMESHDS/SMESHDS_CommandType.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_CommandType.hxx // Module : SMESH @@ -26,8 +27,6 @@ #ifndef _SMESHDS_CommandType_HeaderFile #define _SMESHDS_CommandType_HeaderFile -//#include - enum SMESHDS_CommandType { SMESHDS_AddNode, SMESHDS_AddEdge, @@ -53,7 +52,13 @@ enum SMESHDS_CommandType { SMESHDS_AddQuadTetrahedron, SMESHDS_AddQuadPyramid, SMESHDS_AddQuadPentahedron, - SMESHDS_AddQuadHexahedron + SMESHDS_AddQuadHexahedron, + // + SMESHDS_Add0DElement, + SMESHDS_AddBiQuadQuadrangle, + SMESHDS_AddTriQuadHexa, + SMESHDS_AddHexagonalPrism, + SMESHDS_AddBall }; diff --git a/src/SMESHDS/SMESHDS_DataMapOfShape.hxx b/src/SMESHDS/SMESHDS_DataMapOfShape.hxx index ae7489564..e9482e7bb 100755 --- a/src/SMESHDS/SMESHDS_DataMapOfShape.hxx +++ b/src/SMESHDS/SMESHDS_DataMapOfShape.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File: SMESHDS_DataMapOfShape.hxx // Created: 20.09.05 09:51:12 // Author: Alexander BORODIN diff --git a/src/SMESHDS/SMESHDS_Document.cxx b/src/SMESHDS/SMESHDS_Document.cxx index 9e568e297..315275e33 100644 --- a/src/SMESHDS/SMESHDS_Document.cxx +++ b/src/SMESHDS/SMESHDS_Document.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Document.cxx // Author : Yves FRICAUD, OCC @@ -69,13 +70,13 @@ int SMESHDS_Document::NewMesh(bool theIsEmbeddedMode) //======================================================================= SMESHDS_Mesh *SMESHDS_Document::GetMesh(int MeshID) { - map::iterator it=myMeshes.find(MeshID); - if (it==myMeshes.end()) - { - MESSAGE("SMESHDS_Document::GetMesh : ID not found"); - return NULL; - } - else return (*it).second; + map::iterator it=myMeshes.find(MeshID); + if (it==myMeshes.end()) + { + MESSAGE("SMESHDS_Document::GetMesh : ID not found"); + return NULL; + } + else return (*it).second; } //======================================================================= @@ -84,10 +85,9 @@ SMESHDS_Mesh *SMESHDS_Document::GetMesh(int MeshID) //======================================================================= void SMESHDS_Document::RemoveMesh(int MeshID) { - map::iterator it=myMeshes.find(MeshID); - if (it==myMeshes.end()) - MESSAGE("SMESHDS_Document::RemoveMesh : ID not found"); - myMeshes.erase(it); + map::iterator it=myMeshes.find(MeshID); + if (it!=myMeshes.end()) + myMeshes.erase(it); } //======================================================================= @@ -96,7 +96,7 @@ void SMESHDS_Document::RemoveMesh(int MeshID) //======================================================================= void SMESHDS_Document::AddHypothesis(SMESHDS_Hypothesis * H) { - myHypothesis[H->GetID()]=H; + myHypothesis[H->GetID()]=H; } //======================================================================= @@ -105,13 +105,13 @@ void SMESHDS_Document::AddHypothesis(SMESHDS_Hypothesis * H) //======================================================================= SMESHDS_Hypothesis * SMESHDS_Document::GetHypothesis(int HypID) { - map::iterator it=myHypothesis.find(HypID); - if (it==myHypothesis.end()) - { - MESSAGE("SMESHDS_Document::GetHypothesis : ID not found"); - return NULL; - } - else return (*it).second; + map::iterator it=myHypothesis.find(HypID); + if (it==myHypothesis.end()) + { + MESSAGE("SMESHDS_Document::GetHypothesis : ID not found"); + return NULL; + } + else return (*it).second; } //======================================================================= @@ -120,10 +120,10 @@ SMESHDS_Hypothesis * SMESHDS_Document::GetHypothesis(int HypID) //======================================================================= void SMESHDS_Document::RemoveHypothesis(int HypID) { - map::iterator it=myHypothesis.find(HypID); - if (it==myHypothesis.end()) - MESSAGE("SMESHDS_Document::RemoveHypothesis : ID not found"); - myHypothesis.erase(it); + map::iterator it=myHypothesis.find(HypID); + if (it==myHypothesis.end()) + MESSAGE("SMESHDS_Document::RemoveHypothesis : ID not found"); + myHypothesis.erase(it); } //======================================================================= @@ -132,7 +132,7 @@ void SMESHDS_Document::RemoveHypothesis(int HypID) //======================================================================= int SMESHDS_Document::NbMeshes() { - return myMeshes.size(); + return myMeshes.size(); } //======================================================================= @@ -141,7 +141,7 @@ int SMESHDS_Document::NbMeshes() //======================================================================= int SMESHDS_Document::NbHypothesis() { - return myHypothesis.size(); + return myHypothesis.size(); } //======================================================================= @@ -150,7 +150,7 @@ int SMESHDS_Document::NbHypothesis() //======================================================================= void SMESHDS_Document::InitMeshesIterator() { - myMeshesIt=myMeshes.begin(); + myMeshesIt=myMeshes.begin(); } //======================================================================= @@ -159,9 +159,9 @@ void SMESHDS_Document::InitMeshesIterator() //======================================================================= SMESHDS_Mesh * SMESHDS_Document::NextMesh() { - SMESHDS_Mesh * toReturn=(*myMeshesIt).second; - myMeshesIt++; - return toReturn; + SMESHDS_Mesh * toReturn=(*myMeshesIt).second; + myMeshesIt++; + return toReturn; } //======================================================================= @@ -170,7 +170,7 @@ SMESHDS_Mesh * SMESHDS_Document::NextMesh() //======================================================================= bool SMESHDS_Document::MoreMesh() { - return myMeshesIt!=myMeshes.end(); + return myMeshesIt!=myMeshes.end(); } //======================================================================= @@ -179,7 +179,7 @@ bool SMESHDS_Document::MoreMesh() //======================================================================= void SMESHDS_Document::InitHypothesisIterator() { - myHypothesisIt=myHypothesis.begin(); + myHypothesisIt=myHypothesis.begin(); } //======================================================================= @@ -188,9 +188,9 @@ void SMESHDS_Document::InitHypothesisIterator() //======================================================================= SMESHDS_Hypothesis * SMESHDS_Document::NextHypothesis() { - SMESHDS_Hypothesis * toReturn=(*myHypothesisIt).second; - myHypothesisIt++; - return toReturn; + SMESHDS_Hypothesis * toReturn=(*myHypothesisIt).second; + myHypothesisIt++; + return toReturn; } //======================================================================= @@ -199,5 +199,5 @@ SMESHDS_Hypothesis * SMESHDS_Document::NextHypothesis() //======================================================================= bool SMESHDS_Document::MoreHypothesis() { - return myHypothesisIt!=myHypothesis.end(); + return myHypothesisIt!=myHypothesis.end(); } diff --git a/src/SMESHDS/SMESHDS_Document.hxx b/src/SMESHDS/SMESHDS_Document.hxx index 6abea04a4..182581256 100644 --- a/src/SMESHDS/SMESHDS_Document.hxx +++ b/src/SMESHDS/SMESHDS_Document.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Document.hxx // Module : SMESH @@ -36,29 +37,29 @@ class SMESHDS_EXPORT SMESHDS_Document { public: - SMESHDS_Document(int UserID); - int NewMesh(bool theIsEmbeddedMode); - void RemoveMesh(int MeshID); - SMESHDS_Mesh * GetMesh(int MeshID); - void AddHypothesis(SMESHDS_Hypothesis * H); - void RemoveHypothesis(int HypID); - SMESHDS_Hypothesis * GetHypothesis(int HypID); - int NbMeshes(); - int NbHypothesis(); - void InitMeshesIterator(); - SMESHDS_Mesh * NextMesh(); - bool MoreMesh(); - void InitHypothesisIterator(); - SMESHDS_Hypothesis * NextHypothesis(); - bool MoreHypothesis(); - ~SMESHDS_Document(); + SMESHDS_Document(int UserID); + int NewMesh(bool theIsEmbeddedMode); + void RemoveMesh(int MeshID); + SMESHDS_Mesh * GetMesh(int MeshID); + void AddHypothesis(SMESHDS_Hypothesis * H); + void RemoveHypothesis(int HypID); + SMESHDS_Hypothesis * GetHypothesis(int HypID); + int NbMeshes(); + int NbHypothesis(); + void InitMeshesIterator(); + SMESHDS_Mesh * NextMesh(); + bool MoreMesh(); + void InitHypothesisIterator(); + SMESHDS_Hypothesis * NextHypothesis(); + bool MoreHypothesis(); + ~SMESHDS_Document(); private: - int myUserID; - std::map myMeshes; - std::map myHypothesis; - std::map::iterator myMeshesIt; - std::map::iterator myHypothesisIt; + int myUserID; + std::map myMeshes; + std::map myHypothesis; + std::map::iterator myMeshesIt; + std::map::iterator myHypothesisIt; }; #endif diff --git a/src/SMESHDS/SMESHDS_Group.cxx b/src/SMESHDS/SMESHDS_Group.cxx index fd0ba61d5..c3e1820e5 100644 --- a/src/SMESHDS/SMESHDS_Group.cxx +++ b/src/SMESHDS/SMESHDS_Group.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : idl implementation based on 'SMESH' unit's classes // File : SMESHDS_Group.cxx // Module : SMESH @@ -48,7 +49,7 @@ SMESHDS_Group::SMESHDS_Group (const int theID, //purpose : //======================================================================= -int SMESHDS_Group::Extent() +int SMESHDS_Group::Extent() const { return myGroup.Extent(); } @@ -155,11 +156,22 @@ class MyGroupIterator: public SMDS_ElemIterator //purpose : //======================================================================= -SMDS_ElemIteratorPtr SMESHDS_Group::GetElements() +SMDS_ElemIteratorPtr SMESHDS_Group::GetElements() const { return SMDS_ElemIteratorPtr( new MyGroupIterator ( myGroup )); } +//================================================================================ +/*! + * \brief Return a value allowing to find out if a group has changed or not + */ +//================================================================================ + +int SMESHDS_Group::GetTic() const +{ + return myGroup.Tic(); +} + //======================================================================= //function : SetType //purpose : diff --git a/src/SMESHDS/SMESHDS_Group.hxx b/src/SMESHDS/SMESHDS_Group.hxx index 85d7d14c2..6c798d961 100644 --- a/src/SMESHDS/SMESHDS_Group.hxx +++ b/src/SMESHDS/SMESHDS_Group.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Group.hxx // Module : SMESH @@ -45,7 +46,7 @@ class SMESHDS_EXPORT SMESHDS_Group : public SMESHDS_GroupBase virtual void SetType(SMDSAbs_ElementType theType); - virtual int Extent(); + virtual int Extent() const; virtual bool IsEmpty(); @@ -53,7 +54,9 @@ class SMESHDS_EXPORT SMESHDS_Group : public SMESHDS_GroupBase virtual bool Contains (const SMDS_MeshElement* elem); - virtual SMDS_ElemIteratorPtr GetElements(); + virtual SMDS_ElemIteratorPtr GetElements() const; + + virtual int GetTic() const; bool Add (const int theID); diff --git a/src/SMESHDS/SMESHDS_GroupBase.cxx b/src/SMESHDS/SMESHDS_GroupBase.cxx index b14afd408..383d796a2 100644 --- a/src/SMESHDS/SMESHDS_GroupBase.cxx +++ b/src/SMESHDS/SMESHDS_GroupBase.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : idl implementation based on 'SMESH' unit's classes // File : SMESHDS_Group.cxx // Module : SMESH @@ -104,7 +105,7 @@ void SMESHDS_GroupBase::resetIterator() //purpose : //======================================================================= -int SMESHDS_GroupBase::Extent() +int SMESHDS_GroupBase::Extent() const { SMDS_ElemIteratorPtr it = GetElements(); int nb = 0; diff --git a/src/SMESHDS/SMESHDS_GroupBase.hxx b/src/SMESHDS/SMESHDS_GroupBase.hxx index ced9543fa..cc9411f99 100644 --- a/src/SMESHDS/SMESHDS_GroupBase.hxx +++ b/src/SMESHDS/SMESHDS_GroupBase.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Group.hxx // Module : SMESH @@ -57,7 +58,7 @@ class SMESHDS_EXPORT SMESHDS_GroupBase const char* GetStoreName () const { return myStoreName.c_str(); } - virtual int Extent(); + virtual int Extent() const; virtual bool IsEmpty(); @@ -65,11 +66,13 @@ class SMESHDS_EXPORT SMESHDS_GroupBase virtual bool Contains (const SMDS_MeshElement* elem); - virtual SMDS_ElemIteratorPtr GetElements() = 0; + virtual SMDS_ElemIteratorPtr GetElements() const = 0; - int GetID (const int theIndex); + virtual int GetID (const int theIndex); // use it for iterations 1..Extent() + virtual int GetTic() const = 0; + virtual ~SMESHDS_GroupBase() {} void SetColor (const Quantity_Color& theColor) @@ -96,11 +99,12 @@ class SMESHDS_EXPORT SMESHDS_GroupBase const SMESHDS_Mesh* myMesh; SMDSAbs_ElementType myType; std::string myStoreName; + Quantity_Color myColor; + // for GetID() int myCurIndex; int myCurID; SMDS_ElemIteratorPtr myIterator; - Quantity_Color myColor; }; #endif diff --git a/src/SMESHDS/SMESHDS_GroupOnFilter.cxx b/src/SMESHDS/SMESHDS_GroupOnFilter.cxx new file mode 100644 index 000000000..32bd8c7f3 --- /dev/null +++ b/src/SMESHDS/SMESHDS_GroupOnFilter.cxx @@ -0,0 +1,190 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHDS_GroupOnFilter.cxx +// Module : SMESH +// +#include "SMESHDS_GroupOnFilter.hxx" + +#include "SMESHDS_Mesh.hxx" +#include "SMDS_SetIterator.hxx" + +using namespace std; + +//============================================================================= +/*! + * Creates a group based on thePredicate + */ +//============================================================================= + +SMESHDS_GroupOnFilter::SMESHDS_GroupOnFilter (const int theID, + const SMESHDS_Mesh* theMesh, + const SMDSAbs_ElementType theType, + const SMESH_PredicatePtr& thePredicate) + : SMESHDS_GroupBase(theID,theMesh,theType), myMeshModifTime(0), myPredicateTic(0) +{ + setChanged(); + SetPredicate( thePredicate ); +} + +//================================================================================ +/*! + * \brief Sets a new predicate + */ +//================================================================================ + +void SMESHDS_GroupOnFilter::SetPredicate( const SMESH_PredicatePtr& thePredicate) +{ + myPredicate = thePredicate; + ++myPredicateTic; + setChanged(); + if ( myPredicate ) + myPredicate->SetMesh( GetMesh() ); +} + +//================================================================================ +/*! + * \brief Returns nb of elements + */ +//================================================================================ + +int SMESHDS_GroupOnFilter::Extent() const +{ + update(); + return myElements.size(); +} + +//================================================================================ +/*! + * \brief Checks if the element belongs to the group + */ +//================================================================================ + +bool SMESHDS_GroupOnFilter::Contains (const int theID) +{ + return myPredicate ? myPredicate->IsSatisfy( theID ) : false; +} + +//================================================================================ +/*! + * \brief Checks if the element belongs to the group + */ +//================================================================================ + +bool SMESHDS_GroupOnFilter::Contains (const SMDS_MeshElement* elem) +{ + return myPredicate ? myPredicate->IsSatisfy( elem->GetID() ) : false; +} + +//================================================================================ +/*! + * \brief Return iterator on all elements + */ +//================================================================================ + +SMDS_ElemIteratorPtr SMESHDS_GroupOnFilter::GetElements() const +{ + update(); + return SMDS_ElemIteratorPtr + ( new SMDS_ElementVectorIterator( myElements.begin(), myElements.end() )); +} + +//================================================================================ +/*! + * \brief return ID of theIndex-th element + * \param theIndex - index countered from 1 + * \retval int - element ID + */ +//================================================================================ + +int SMESHDS_GroupOnFilter::GetID (const int theIndex) +{ + update(); + if ( theIndex < 1 || theIndex > myElements.size() ) + return -1; + return myElements[ theIndex-1 ]->GetID(); +} + +//================================================================================ +/*! + * \brief Return a value allowing to find out if a group has changed or not + */ +//================================================================================ + +int SMESHDS_GroupOnFilter::GetTic() const +{ + return GetMesh()->GetMTime() * myPredicateTic; +} + +//================================================================================ +/*! + * \brief Return false if update() is needed + */ +//================================================================================ + +bool SMESHDS_GroupOnFilter::IsUpToDate() const +{ + return !( myMeshModifTime < GetMesh()->GetMTime() ); +} + +//================================================================================ +/*! + * \brief Updates myElements if necessary + */ +//================================================================================ + +void SMESHDS_GroupOnFilter::update() const +{ + if ( !IsUpToDate() ) + { + SMESHDS_GroupOnFilter* me = const_cast( this ); + me->myElements.clear(); + if ( myPredicate ) + { + myPredicate->SetMesh( GetMesh() ); // hope myPredicate updates self here if necessary + me->myElements.reserve( GetMesh()->GetMeshInfo().NbElements(GetType())); + SMDS_ElemIteratorPtr elIt = GetMesh()->elementsIterator(GetType()); + while ( elIt->more() ) + { + const SMDS_MeshElement* e = elIt->next(); + if ( myPredicate->IsSatisfy( e->GetID() )) + me->myElements.push_back( e ); + } + vector< const SMDS_MeshElement*> elems( me->myElements.begin(), me->myElements.end() ); + me->myElements.swap( elems ); + } + me->setChanged( false ); + } +} + +//================================================================================ +/*! + * \brief Sets myMeshModifTime according to modification state + */ +//================================================================================ + +void SMESHDS_GroupOnFilter::setChanged(bool changed) +{ + myMeshModifTime = GetMesh()->GetMTime(); + if ( changed && myMeshModifTime != 0 ) + --myMeshModifTime; +} diff --git a/src/SMESHDS/SMESHDS_GroupOnFilter.hxx b/src/SMESHDS/SMESHDS_GroupOnFilter.hxx new file mode 100644 index 000000000..b1e33e5fc --- /dev/null +++ b/src/SMESHDS/SMESHDS_GroupOnFilter.hxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHDS_GroupOnFilter.hxx +// Module : SMESH +// +#ifndef _SMESHDS_GroupOnFilter_HeaderFile +#define _SMESHDS_GroupOnFilter_HeaderFile + +#include "SMESH_SMESHDS.hxx" + +#include "SMESHDS_GroupBase.hxx" +#include "SMESH_Controls.hxx" + +/*! + * \brief Groups whose contents is dynamically updated using the filter + */ +class SMESHDS_EXPORT SMESHDS_GroupOnFilter: public SMESHDS_GroupBase +{ + public: + + SMESHDS_GroupOnFilter (const int theID, + const SMESHDS_Mesh* theMesh, + const SMDSAbs_ElementType theType, + const SMESH_PredicatePtr& thePredicate); + + void SetPredicate( const SMESH_PredicatePtr& thePredicate); + + SMESH_PredicatePtr GetPredicate() const { return myPredicate; } + + virtual int Extent() const; + + virtual bool Contains (const int theID); + + virtual bool Contains (const SMDS_MeshElement* elem); + + virtual SMDS_ElemIteratorPtr GetElements() const; + + virtual int GetID (const int theIndex); + + virtual int GetTic() const; + + bool IsUpToDate() const; + + private: + + void update() const; + void setChanged(bool changed=true); + + SMESH_PredicatePtr myPredicate; + std::vector< const SMDS_MeshElement*> myElements; + unsigned long myMeshModifTime; // when myElements was filled + int myPredicateTic; +}; + +#endif diff --git a/src/SMESHDS/SMESHDS_GroupOnGeom.cxx b/src/SMESHDS/SMESHDS_GroupOnGeom.cxx index 73e1654db..51d762028 100644 --- a/src/SMESHDS/SMESHDS_GroupOnGeom.cxx +++ b/src/SMESHDS/SMESHDS_GroupOnGeom.cxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : idl implementation based on 'SMESH' unit's classes // File : SMESHDS_GroupOnGeom.cxx // Module : SMESH -// $Header$ // #include "SMESHDS_GroupOnGeom.hxx" #include "SMESHDS_Mesh.hxx" @@ -40,10 +40,16 @@ SMESHDS_GroupOnGeom::SMESHDS_GroupOnGeom (const int theID, const SMESHDS_Mesh* theMesh, const SMDSAbs_ElementType theType, const TopoDS_Shape& theShape) - : SMESHDS_GroupBase(theID,theMesh,theType), myShape(theShape) + : SMESHDS_GroupBase(theID,theMesh,theType) +{ + SetShape( theShape ); +} + +void SMESHDS_GroupOnGeom::SetShape( const TopoDS_Shape& theShape) { - SMESHDS_Mesh* aMesh = const_cast(theMesh); + SMESHDS_Mesh* aMesh = const_cast( GetMesh() ); mySubMesh = aMesh->MeshElements( aMesh->AddCompoundSubmesh( theShape )); + myShape = theShape; } // ===================== @@ -60,13 +66,14 @@ class MyIterator: public SMDS_ElemIterator MyIterator(SMDSAbs_ElementType type, const SMESHDS_SubMesh* subMesh) : myType(type), myElem(0) { - if ( subMesh ) + if ( subMesh ) { if ( myType == SMDSAbs_Node ) myNodeIt = subMesh->GetNodes(); else { myElemIt = subMesh->GetElements(); next(); } + } } bool more() { @@ -96,7 +103,7 @@ class MyIterator: public SMDS_ElemIterator //purpose : //======================================================================= -SMDS_ElemIteratorPtr SMESHDS_GroupOnGeom::GetElements() +SMDS_ElemIteratorPtr SMESHDS_GroupOnGeom::GetElements() const { return SMDS_ElemIteratorPtr( new MyIterator ( GetType(), mySubMesh )); } @@ -121,3 +128,14 @@ bool SMESHDS_GroupOnGeom::Contains (const SMDS_MeshElement* elem) return mySubMesh->Contains( elem ); } +//================================================================================ +/*! + * \brief Return a value allowing to find out if a group has changed or not + */ +//================================================================================ + +int SMESHDS_GroupOnGeom::GetTic() const +{ + return GetMesh()->GetMTime(); +} + diff --git a/src/SMESHDS/SMESHDS_GroupOnGeom.hxx b/src/SMESHDS/SMESHDS_GroupOnGeom.hxx index a1fa3cb26..1895912b4 100644 --- a/src/SMESHDS/SMESHDS_GroupOnGeom.hxx +++ b/src/SMESHDS/SMESHDS_GroupOnGeom.hxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_GroupOnGeom.hxx // Module : SMESH -// $Header$ // #ifndef _SMESHDS_GroupOnGeom_HeaderFile #define _SMESHDS_GroupOnGeom_HeaderFile @@ -43,13 +43,17 @@ class SMESHDS_EXPORT SMESHDS_GroupOnGeom: public SMESHDS_GroupBase const SMDSAbs_ElementType theType, const TopoDS_Shape& theShape); + void SetShape( const TopoDS_Shape& theShape); + TopoDS_Shape GetShape() const { return myShape; } virtual bool Contains (const int theID); virtual bool Contains (const SMDS_MeshElement* elem); - virtual SMDS_ElemIteratorPtr GetElements(); + virtual SMDS_ElemIteratorPtr GetElements() const; + + virtual int GetTic() const; private: diff --git a/src/SMESHDS/SMESHDS_Hypothesis.cxx b/src/SMESHDS/SMESHDS_Hypothesis.cxx index 84b3a6c32..ea7c5c6e5 100644 --- a/src/SMESHDS/SMESHDS_Hypothesis.cxx +++ b/src/SMESHDS/SMESHDS_Hypothesis.cxx @@ -1,32 +1,34 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Hypothesis.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "SMESHDS_Hypothesis.hxx" +#include + using namespace std; //============================================================================= @@ -37,11 +39,8 @@ using namespace std; SMESHDS_Hypothesis::SMESHDS_Hypothesis(int hypId) { -// MESSAGE("SMESHDS_Hypothesis::SMESHDS_Hypothesis"); _hypId = hypId; _name = "generic"; -// SCRUTE(_name); -// SCRUTE(_hypId); } //============================================================================= @@ -52,7 +51,6 @@ SMESHDS_Hypothesis::SMESHDS_Hypothesis(int hypId) SMESHDS_Hypothesis::~SMESHDS_Hypothesis() { -// MESSAGE("SMESHDS_Hypothesis::~SMESHDS_Hypothesis"); } //============================================================================= @@ -63,9 +61,6 @@ SMESHDS_Hypothesis::~SMESHDS_Hypothesis() const char* SMESHDS_Hypothesis::GetName() const { -// MESSAGE("SMESHDS_Hypothesis::GetName"); -// SCRUTE(_name); -// SCRUTE(&_name); return _name.c_str(); } @@ -77,8 +72,6 @@ const char* SMESHDS_Hypothesis::GetName() const int SMESHDS_Hypothesis::GetID() const { -// MESSAGE("SMESHDS_Hypothesis::GetId"); -// SCRUTE(_hypId); return _hypId; } @@ -90,8 +83,23 @@ int SMESHDS_Hypothesis::GetID() const int SMESHDS_Hypothesis::GetType() const { -// MESSAGE("SMESHDS_Hypothesis::GetType"); -// SCRUTE(_type); return _type; } +//============================================================================= +/*! + * Equality + */ +//============================================================================= + +bool SMESHDS_Hypothesis::operator==(const SMESHDS_Hypothesis& other) const +{ + if ( this == &other ) + return true; + if ( _name != other._name ) + return false; + ostringstream mySave, otherSave; + ((SMESHDS_Hypothesis*)this )->SaveTo(mySave); + ((SMESHDS_Hypothesis*)&other)->SaveTo(otherSave); + return mySave.str() == otherSave.str(); +} diff --git a/src/SMESHDS/SMESHDS_Hypothesis.hxx b/src/SMESHDS/SMESHDS_Hypothesis.hxx index 4478b4a0b..d819684af 100644 --- a/src/SMESHDS/SMESHDS_Hypothesis.hxx +++ b/src/SMESHDS/SMESHDS_Hypothesis.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Hypothesis.hxx // Author : Paul RASCLE, EDF @@ -46,6 +47,9 @@ public: virtual std::ostream & SaveTo(std::ostream & save)=0; virtual std::istream & LoadFrom(std::istream & load)=0; + virtual bool operator==(const SMESHDS_Hypothesis& other) const; + bool operator!=(const SMESHDS_Hypothesis& other) const { return !(*this==other); } + enum hypothesis_type {PARAM_ALGO, ALGO_0D, ALGO_1D, ALGO_2D, ALGO_3D}; protected: diff --git a/src/SMESHDS/SMESHDS_Mesh.cxx b/src/SMESHDS/SMESHDS_Mesh.cxx index b2dab7719..a42b9acc0 100644 --- a/src/SMESHDS/SMESHDS_Mesh.cxx +++ b/src/SMESHDS/SMESHDS_Mesh.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESH_Mesh.cxx // Author : Yves FRICAUD, OCC // Module : SMESH -// $Header: // #include "SMESHDS_Mesh.hxx" @@ -32,21 +32,25 @@ #include "SMDS_EdgePosition.hxx" #include "SMDS_FacePosition.hxx" #include "SMDS_SpacePosition.hxx" +#include "SMDS_Downward.hxx" #include "SMESHDS_GroupOnGeom.hxx" +#include "SMESHDS_Script.hxx" -#include +#include +#include #include +#include +#include +#include #include +#include +#include +#include #include "utilities.h" using namespace std; -/*Standard_Boolean IsEqual( const TopoDS_Shape& S1, const TopoDS_Shape& S2 ) - { - return S1.IsSame( S2 ); - }*/ - //======================================================================= //function : Create //purpose : @@ -58,6 +62,7 @@ SMESHDS_Mesh::SMESHDS_Mesh(int theMeshID, bool theIsEmbeddedMode): { myScript = new SMESHDS_Script(theIsEmbeddedMode); myCurSubMesh = 0; + SetPersistentId(theMeshID); } //======================================================================= @@ -66,6 +71,30 @@ bool SMESHDS_Mesh::IsEmbeddedMode() return myIsEmbeddedMode; } +//================================================================================ +/*! + * \brief Store ID persistent during lifecycle + * + * Initially it was used to have a persistent reference to the mesh from the hypothesis + */ +//================================================================================ + +void SMESHDS_Mesh::SetPersistentId(int id) +{ + if (NbNodes() == 0) + myPersistentID = id; +} +//================================================================================ +/*! + * \brief Return ID persistent during lifecycle + */ +//================================================================================ + +int SMESHDS_Mesh::GetPersistentId() const +{ + return myPersistentID; +} + //======================================================================= //function : ShapeToMesh //purpose : @@ -83,12 +112,15 @@ void SMESHDS_Mesh::ShapeToMesh(const TopoDS_Shape & S) if ( !i_sub->second->IsComplexSubmesh() ) { SMDS_NodeIteratorPtr nIt = i_sub->second->GetNodes(); while ( nIt->more() ) - nIt->next()->GetPosition()->SetShapeId( 0 ); + i_sub->second->RemoveNode(nIt->next(), false); } } // - sub-meshes - myIndexToShape.Clear(); + TShapeIndexToSubMesh::iterator i_sm = myShapeIndexToSubMesh.begin(); + for ( ; i_sm != myShapeIndexToSubMesh.end(); ++i_sm ) + delete i_sm->second; myShapeIndexToSubMesh.clear(); + myIndexToShape.Clear(); // - groups on geometry set::iterator gr = myGroups.begin(); while ( gr != myGroups.end() ) { @@ -186,6 +218,7 @@ bool SMESHDS_Mesh::ChangeElementNodes(const SMDS_MeshElement * elem, const SMDS_MeshNode * nodes[], const int nbnodes) { + //MESSAGE("SMESHDS_Mesh::ChangeElementNodes"); if ( ! SMDS_Mesh::ChangeElementNodes( elem, nodes, nbnodes )) return false; @@ -241,14 +274,71 @@ bool SMESHDS_Mesh::ChangePolyhedronNodes void SMESHDS_Mesh::Renumber (const bool isNodes, const int startID, const int deltaID) { - SMDS_Mesh::Renumber( isNodes, startID, deltaID ); - myScript->Renumber( isNodes, startID, deltaID ); + // TODO not possible yet to have node numbers not starting to O and continuous. + if (!this->isCompacted()) + this->compactMesh(); +// SMDS_Mesh::Renumber( isNodes, startID, deltaID ); +// myScript->Renumber( isNodes, startID, deltaID ); +} + +//======================================================================= +//function : Add0DElement +//purpose : +//======================================================================= +SMDS_Mesh0DElement* SMESHDS_Mesh::Add0DElementWithID(int nodeID, int ID) +{ + SMDS_Mesh0DElement* anElem = SMDS_Mesh::Add0DElementWithID(nodeID, ID); + if (anElem) myScript->Add0DElement(ID, nodeID); + return anElem; +} + +SMDS_Mesh0DElement* SMESHDS_Mesh::Add0DElementWithID + (const SMDS_MeshNode * node, int ID) +{ + return Add0DElementWithID(node->GetID(), ID); +} + +SMDS_Mesh0DElement* SMESHDS_Mesh::Add0DElement(const SMDS_MeshNode * node) +{ + SMDS_Mesh0DElement* anElem = SMDS_Mesh::Add0DElement(node); + if (anElem) myScript->Add0DElement(anElem->GetID(), node->GetID()); + return anElem; +} + +//======================================================================= +//function :AddBallWithID +//purpose : +//======================================================================= + +SMDS_BallElement* SMESHDS_Mesh::AddBallWithID(int node, double diameter, int ID) +{ + SMDS_BallElement* anElem = SMDS_Mesh::AddBallWithID(node,diameter,ID); + if (anElem) myScript->AddBall(anElem->GetID(), node, diameter); + return anElem; +} + +SMDS_BallElement* SMESHDS_Mesh::AddBallWithID(const SMDS_MeshNode * node, + double diameter, + int ID) +{ + SMDS_BallElement* anElem = SMDS_Mesh::AddBallWithID(node,diameter,ID); + if (anElem) myScript->AddBall(anElem->GetID(), node->GetID(), diameter); + return anElem; +} + +SMDS_BallElement* SMESHDS_Mesh::AddBall (const SMDS_MeshNode * node, + double diameter) +{ + SMDS_BallElement* anElem = SMDS_Mesh::AddBall(node,diameter); + if (anElem) myScript->AddBall(anElem->GetID(), node->GetID(), diameter); + return anElem; } //======================================================================= //function :AddEdgeWithID //purpose : //======================================================================= + SMDS_MeshEdge* SMESHDS_Mesh::AddEdgeWithID(int n1, int n2, int ID) { SMDS_MeshEdge* anElem = SMDS_Mesh::AddEdgeWithID(n1,n2,ID); @@ -257,21 +347,21 @@ SMDS_MeshEdge* SMESHDS_Mesh::AddEdgeWithID(int n1, int n2, int ID) } SMDS_MeshEdge* SMESHDS_Mesh::AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - int ID) + const SMDS_MeshNode * n2, + int ID) { return AddEdgeWithID(n1->GetID(), - n2->GetID(), - ID); + n2->GetID(), + ID); } SMDS_MeshEdge* SMESHDS_Mesh::AddEdge(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2) + const SMDS_MeshNode * n2) { SMDS_MeshEdge* anElem = SMDS_Mesh::AddEdge(n1,n2); if(anElem) myScript->AddEdge(anElem->GetID(), - n1->GetID(), - n2->GetID()); + n1->GetID(), + n2->GetID()); return anElem; } @@ -287,25 +377,25 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int ID) } SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + int ID) { return AddFaceWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - ID); + n2->GetID(), + n3->GetID(), + ID); } SMDS_MeshFace* SMESHDS_Mesh::AddFace( const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3) { SMDS_MeshFace *anElem = SMDS_Mesh::AddFace(n1, n2, n3); if(anElem) myScript->AddFace(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID()); return anElem; } @@ -321,29 +411,29 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n4, int I } SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID) { return AddFaceWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - ID); + n2->GetID(), + n3->GetID(), + n4->GetID(), + ID); } SMDS_MeshFace* SMESHDS_Mesh::AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4) { SMDS_MeshFace *anElem = SMDS_Mesh::AddFace(n1, n2, n3, n4); if(anElem) myScript->AddFace(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID()); return anElem; } @@ -359,29 +449,29 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, i } SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID) { return AddVolumeWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - ID); + n2->GetID(), + n3->GetID(), + n4->GetID(), + ID); } SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1, n2, n3, n4); if(anElem) myScript->AddVolume(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID()); return anElem; } @@ -397,33 +487,33 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, i } SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + int ID) { return AddVolumeWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID(), - ID); + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + ID); } SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1, n2, n3, n4, n5); if(anElem) myScript->AddVolume(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID()); return anElem; } @@ -439,37 +529,37 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, i } SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + int ID) { return AddVolumeWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID(), - n6->GetID(), - ID); + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID(), + ID); } SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1, n2, n3, n4, n5, n6); if(anElem) myScript->AddVolume(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID(), - n6->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID()); return anElem; } @@ -485,54 +575,129 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, i } SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, - int ID) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + int ID) { return AddVolumeWithID(n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID(), - n6->GetID(), - n7->GetID(), - n8->GetID(), - ID); + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID(), + n7->GetID(), + n8->GetID(), + ID); } SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8) + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1, n2, n3, n4, n5, n6, n7, n8); if(anElem) myScript->AddVolume(anElem->GetID(), - n1->GetID(), - n2->GetID(), - n3->GetID(), - n4->GetID(), - n5->GetID(), - n6->GetID(), - n7->GetID(), - n8->GetID()); + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID(), + n7->GetID(), + n8->GetID()); return anElem; } + +//======================================================================= +//function :AddVolume +//purpose : add hexagonal prism +//======================================================================= +SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n9, int n10, int n11, int n12, + int ID) +{ + SMDS_MeshVolume *anElem= SMDS_Mesh::AddVolumeWithID(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, ID); + if(anElem) myScript->AddVolume(ID, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12); + return anElem; +} + +SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12, + int ID) +{ + return AddVolumeWithID(n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID(), + n7->GetID(), + n8->GetID(), + n9->GetID(), + n10->GetID(), + n11->GetID(), + n12->GetID(), + ID); +} + +SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12) +{ + SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12); + if(anElem) myScript->AddVolume(anElem->GetID(), + n1->GetID(), + n2->GetID(), + n3->GetID(), + n4->GetID(), + n5->GetID(), + n6->GetID(), + n7->GetID(), + n8->GetID(), + n9->GetID(), + n10->GetID(), + n11->GetID(), + n12->GetID()); + return anElem; +} + + //======================================================================= //function : AddPolygonalFace //purpose : //======================================================================= -SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFaceWithID (std::vector nodes_ids, - const int ID) +SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFaceWithID (const std::vector& nodes_ids, + const int ID) { SMDS_MeshFace *anElem = SMDS_Mesh::AddPolygonalFaceWithID(nodes_ids, ID); if (anElem) { @@ -542,8 +707,8 @@ SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFaceWithID (std::vector nodes_ids, } SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFaceWithID - (std::vector nodes, - const int ID) + (const std::vector& nodes, + const int ID) { SMDS_MeshFace *anElem = SMDS_Mesh::AddPolygonalFaceWithID(nodes, ID); if (anElem) { @@ -558,7 +723,7 @@ SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFaceWithID } SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFace - (std::vector nodes) + (const std::vector& nodes) { SMDS_MeshFace *anElem = SMDS_Mesh::AddPolygonalFace(nodes); if (anElem) { @@ -576,9 +741,9 @@ SMDS_MeshFace* SMESHDS_Mesh::AddPolygonalFace //function : AddPolyhedralVolume //purpose : //======================================================================= -SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolumeWithID (std::vector nodes_ids, - std::vector quantities, - const int ID) +SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolumeWithID (const std::vector& nodes_ids, + const std::vector& quantities, + const int ID) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddPolyhedralVolumeWithID(nodes_ids, quantities, ID); if (anElem) { @@ -588,9 +753,9 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolumeWithID (std::vector nodes } SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolumeWithID - (std::vector nodes, - std::vector quantities, - const int ID) + (const std::vector& nodes, + const std::vector& quantities, + const int ID) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddPolyhedralVolumeWithID(nodes, quantities, ID); if (anElem) { @@ -605,8 +770,8 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolumeWithID } SMDS_MeshVolume* SMESHDS_Mesh::AddPolyhedralVolume - (std::vector nodes, - std::vector quantities) + (const std::vector& nodes, + const std::vector& quantities) { SMDS_MeshVolume *anElem = SMDS_Mesh::AddPolyhedralVolume(nodes, quantities); if (anElem) { @@ -652,43 +817,50 @@ static void removeFromContainers (map& theSubMeshes, } } + const bool deleted=true; + // Rm from sub-meshes // Element should belong to only one sub-mesh - map::iterator SubIt = theSubMeshes.begin(); - for ( ; SubIt != theSubMeshes.end(); SubIt++ ) + if ( !theSubMeshes.empty() ) { - int size = isNode ? (*SubIt).second->NbNodes() : (*SubIt).second->NbElements(); - if ( size == 0 ) continue; - + SMESHDS_Mesh* mesh = theSubMeshes.begin()->second->getParent(); list::iterator elIt = theElems.begin(); - while ( elIt != theElems.end() ) - { - bool removed = false; - if ( isNode ) - removed = (*SubIt).second->RemoveNode( static_cast (*elIt) ); - else - removed = (*SubIt).second->RemoveElement( *elIt ); - - if (removed) - { - elIt = theElems.erase( elIt ); - if ( theElems.empty() ) - return; // all elements are found and removed - } - else - { - elIt++ ; - } + if ( isNode ) { + for ( ; elIt != theElems.end(); ++elIt ) + if ( SMESHDS_SubMesh* sm = mesh->MeshElements( (*elIt)->getshapeId() )) + sm->RemoveNode( static_cast (*elIt), deleted ); + } + else { + for ( ; elIt != theElems.end(); ++elIt ) + if ( SMESHDS_SubMesh* sm = mesh->MeshElements( (*elIt)->getshapeId() )) + sm->RemoveElement( *elIt, deleted ); } } } - + //======================================================================= //function : RemoveNode //purpose : //======================================================================= void SMESHDS_Mesh::RemoveNode(const SMDS_MeshNode * n) { + if ( n->NbInverseElements() == 0 && !(hasConstructionEdges() || hasConstructionFaces())) + { + SMESHDS_SubMesh* subMesh=0; + map::iterator SubIt = + myShapeIndexToSubMesh.find( n->getshapeId() ); + if ( SubIt != myShapeIndexToSubMesh.end() ) + subMesh = SubIt->second; + else + SubIt = myShapeIndexToSubMesh.begin(); + for ( ; !subMesh && SubIt != myShapeIndexToSubMesh.end(); SubIt++ ) + if (!SubIt->second->IsComplexSubmesh() && SubIt->second->Contains( n )) + subMesh = SubIt->second; + + RemoveFreeNode( n, subMesh, true); + return; + } + myScript->RemoveNode(n->GetID()); list removedElems; @@ -724,7 +896,7 @@ void SMESHDS_Mesh::RemoveFreeNode(const SMDS_MeshNode * n, // Rm from sub-mesh // Node should belong to only one sub-mesh if( subMesh ) - subMesh->RemoveNode(n); + subMesh->RemoveNode(n,/*deleted=*/false); SMDS_Mesh::RemoveFreeElement(n); } @@ -740,7 +912,18 @@ void SMESHDS_Mesh::RemoveElement(const SMDS_MeshElement * elt) RemoveNode( static_cast( elt )); return; } - + if (!hasConstructionEdges() && !hasConstructionFaces()) + { + SMESHDS_SubMesh* subMesh=0; + map::iterator SubIt = myShapeIndexToSubMesh.begin(); + for ( ; !subMesh && SubIt != myShapeIndexToSubMesh.end(); SubIt++ ) + if (!SubIt->second->IsComplexSubmesh() && SubIt->second->Contains( elt )) + subMesh = SubIt->second; + //MESSAGE("subMesh " << elt->getshapeId()); + RemoveFreeElement( elt, subMesh, true); + return; + } + myScript->RemoveElement(elt->GetID()); list removedElems; @@ -759,6 +942,7 @@ void SMESHDS_Mesh::RemoveFreeElement(const SMDS_MeshElement * elt, SMESHDS_SubMesh * subMesh, bool fromGroups) { + //MESSAGE(" --------------------------------> SMESHDS_Mesh::RemoveFreeElement " << subMesh << " " << fromGroups); if (elt->GetType() == SMDSAbs_Node) { RemoveFreeNode( static_cast(elt), subMesh); return; @@ -784,7 +968,7 @@ void SMESHDS_Mesh::RemoveFreeElement(const SMDS_MeshElement * elt, // Rm from sub-mesh // Element should belong to only one sub-mesh if( subMesh ) - subMesh->RemoveElement(elt); + subMesh->RemoveElement(elt, /*deleted=*/false); SMDS_Mesh::RemoveFreeElement(elt); } @@ -819,7 +1003,7 @@ void SMESHDS_Mesh::ClearMesh() //================================================================================ /*! * \brief return submesh by shape - * \param shape - the subshape + * \param shape - the sub-shape * \retval SMESHDS_SubMesh* - the found submesh * * search of submeshes is optimized @@ -841,8 +1025,8 @@ SMESHDS_SubMesh* SMESHDS_Mesh::getSubmesh( const TopoDS_Shape & shape ) //================================================================================ /*! - * \brief return submesh by subshape index - * \param Index - the subshape index + * \brief return submesh by sub-shape index + * \param Index - the sub-shape index * \retval SMESHDS_SubMesh* - the found submesh * search of submeshes is optimized */ @@ -854,7 +1038,7 @@ SMESHDS_SubMesh* SMESHDS_Mesh::getSubmesh( const int Index ) if ( Index != myCurSubID ) { map::iterator it = myShapeIndexToSubMesh.find( Index ); if ( it == myShapeIndexToSubMesh.end() ) - it = myShapeIndexToSubMesh.insert( make_pair(Index, new SMESHDS_SubMesh() )).first; + it = myShapeIndexToSubMesh.insert( make_pair(Index, new SMESHDS_SubMesh(this, Index) )).first; myCurSubMesh = it->second; myCurSubID = Index; myCurSubShape.Nullify(); // myCurSubShape no more corresponds to submesh @@ -882,22 +1066,6 @@ bool SMESHDS_Mesh::add(const SMDS_MeshElement* elem, SMESHDS_SubMesh* subMesh ) return false; } -namespace { - - //================================================================================ - /*! - * \brief Creates a node position in volume - */ - //================================================================================ - - inline SMDS_PositionPtr volumePosition(int volId) - { - SMDS_SpacePosition* pos = new SMDS_SpacePosition(); - pos->SetShapeId( volId ); - return SMDS_PositionPtr(pos); - } -} - //======================================================================= //function : SetNodeOnVolume //purpose : @@ -906,8 +1074,9 @@ void SMESHDS_Mesh::SetNodeInVolume(SMDS_MeshNode * aNode, const TopoDS_Shell & S) { if ( add( aNode, getSubmesh(S) )) - aNode->SetPosition ( volumePosition( myCurSubID )); + aNode->SetPosition ( SMDS_SpacePosition::originSpacePosition() ); } + //======================================================================= //function : SetNodeOnVolume //purpose : @@ -916,7 +1085,7 @@ void SMESHDS_Mesh::SetNodeInVolume(SMDS_MeshNode * aNode, const TopoDS_Solid & S) { if ( add( aNode, getSubmesh(S) )) - aNode->SetPosition ( volumePosition( myCurSubID )); + aNode->SetPosition ( SMDS_SpacePosition::originSpacePosition() ); } //======================================================================= @@ -929,7 +1098,7 @@ void SMESHDS_Mesh::SetNodeOnFace(SMDS_MeshNode * aNode, double v) { if ( add( aNode, getSubmesh(S) )) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_FacePosition(myCurSubID, u, v))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_FacePosition( u, v))); } //======================================================================= @@ -941,7 +1110,7 @@ void SMESHDS_Mesh::SetNodeOnEdge(SMDS_MeshNode * aNode, double u) { if ( add( aNode, getSubmesh(S) )) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition(myCurSubID, u))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition(u))); } //======================================================================= @@ -952,7 +1121,7 @@ void SMESHDS_Mesh::SetNodeOnVertex(SMDS_MeshNode * aNode, const TopoDS_Vertex & S) { if ( add( aNode, getSubmesh(S) )) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_VertexPosition(myCurSubID))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_VertexPosition())); } //======================================================================= @@ -961,12 +1130,13 @@ void SMESHDS_Mesh::SetNodeOnVertex(SMDS_MeshNode * aNode, //======================================================================= void SMESHDS_Mesh::UnSetNodeOnShape(const SMDS_MeshNode* aNode) { - if ( aNode && aNode->GetPosition() ) { - map::iterator it = - myShapeIndexToSubMesh.find( aNode->GetPosition()->GetShapeId() ); - if ( it != myShapeIndexToSubMesh.end() ) - it->second->RemoveNode( aNode ); - } + int shapeId = aNode->getshapeId(); + if (shapeId >= 0) + { + map::iterator it = myShapeIndexToSubMesh.find(shapeId); + if (it != myShapeIndexToSubMesh.end()) + it->second->RemoveNode(aNode, /*deleted=*/false); + } } //======================================================================= @@ -990,10 +1160,12 @@ void SMESHDS_Mesh::UnSetMeshElementOnShape(const SMDS_MeshElement * elem, map::iterator it = myShapeIndexToSubMesh.find( Index ); if ( it != myShapeIndexToSubMesh.end() ) - if ( elem->GetType() == SMDSAbs_Node ) - it->second->RemoveNode( static_cast( elem )); - else - it->second->RemoveElement( elem ); + { + if (elem->GetType() == SMDSAbs_Node) + it->second->RemoveNode(static_cast (elem), /*deleted=*/false); + else + it->second->RemoveElement(elem, /*deleted=*/false); + } } //======================================================================= @@ -1002,26 +1174,24 @@ void SMESHDS_Mesh::UnSetMeshElementOnShape(const SMDS_MeshElement * elem, //======================================================================= TopoDS_Shape SMESHDS_Mesh::ShapeToMesh() const { - return myShape; + return myShape; } //======================================================================= //function : IsGroupOfSubShapes -//purpose : return true if at least one subshape of theShape is a subshape +//purpose : return true if at least one sub-shape of theShape is a sub-shape // of myShape or theShape == myShape //======================================================================= bool SMESHDS_Mesh::IsGroupOfSubShapes (const TopoDS_Shape& theShape) const { - if ( myShape.IsSame( theShape )) + if ( myIndexToShape.Contains(theShape) ) return true; - for ( TopoDS_Iterator it( theShape ); it.More(); it.Next() ) { - if (myIndexToShape.Contains( it.Value() ) || - IsGroupOfSubShapes( it.Value() )) + for ( TopoDS_Iterator it( theShape ); it.More(); it.Next() ) + if (IsGroupOfSubShapes( it.Value() )) return true; - } - + return false; } @@ -1042,7 +1212,7 @@ SMESHDS_SubMesh * SMESHDS_Mesh::MeshElements(const TopoDS_Shape & S) const /////////////////////////////////////////////////////////////////////////////// /// Return the sub mesh by Id of shape it is linked to /////////////////////////////////////////////////////////////////////////////// -SMESHDS_SubMesh * SMESHDS_Mesh::MeshElements(const int Index) +SMESHDS_SubMesh * SMESHDS_Mesh::MeshElements(const int Index) const { TShapeIndexToSubMesh::const_iterator anIter = myShapeIndexToSubMesh.find(Index); if (anIter != myShapeIndexToSubMesh.end()) @@ -1055,10 +1225,10 @@ SMESHDS_SubMesh * SMESHDS_Mesh::MeshElements(const int Index) //function : SubMeshIndices //purpose : //======================================================================= -list SMESHDS_Mesh::SubMeshIndices() +list SMESHDS_Mesh::SubMeshIndices() const { list anIndices; - std::map::iterator anIter = myShapeIndexToSubMesh.begin(); + std::map::const_iterator anIter = myShapeIndexToSubMesh.begin(); for (; anIter != myShapeIndexToSubMesh.end(); anIter++) { anIndices.push_back((*anIter).first); } @@ -1080,13 +1250,28 @@ SMESHDS_Mesh::GetHypothesis(const TopoDS_Shape & S) const return empty; } +//================================================================================ +/*! + * \brief returns true if the hypothesis is assigned to any sub-shape + */ +//================================================================================ + +bool SMESHDS_Mesh::IsUsedHypothesis(const SMESHDS_Hypothesis * H) const +{ + ShapeToHypothesis::Iterator s2h( myShapeToHypothesis ); + for ( ; s2h.More(); s2h.Next() ) + if ( std::find( s2h.Value().begin(), s2h.Value().end(), H ) != s2h.Value().end() ) + return true; + return false; +} + //======================================================================= //function : GetScript //purpose : //======================================================================= SMESHDS_Script* SMESHDS_Mesh::GetScript() { - return myScript; + return myScript; } //======================================================================= @@ -1095,18 +1280,18 @@ SMESHDS_Script* SMESHDS_Mesh::GetScript() //======================================================================= void SMESHDS_Mesh::ClearScript() { - myScript->Clear(); + myScript->Clear(); } //======================================================================= //function : HasMeshElements //purpose : //======================================================================= -bool SMESHDS_Mesh::HasMeshElements(const TopoDS_Shape & S) +bool SMESHDS_Mesh::HasMeshElements(const TopoDS_Shape & S) const { - if (myShape.IsNull()) MESSAGE("myShape is NULL"); - int Index = myIndexToShape.FindIndex(S); - return myShapeIndexToSubMesh.find(Index)!=myShapeIndexToSubMesh.end(); + if (myShape.IsNull()) MESSAGE("myShape is NULL"); + int Index = myIndexToShape.FindIndex(S); + return myShapeIndexToSubMesh.find(Index)!=myShapeIndexToSubMesh.end(); } //======================================================================= @@ -1128,7 +1313,7 @@ SMESHDS_SubMesh * SMESHDS_Mesh::NewSubMesh(int Index) TShapeIndexToSubMesh::iterator anIter = myShapeIndexToSubMesh.find(Index); if (anIter == myShapeIndexToSubMesh.end()) { - SM = new SMESHDS_SubMesh(); + SM = new SMESHDS_SubMesh(this, Index); myShapeIndexToSubMesh[Index]=SM; } else @@ -1145,7 +1330,7 @@ int SMESHDS_Mesh::AddCompoundSubmesh(const TopoDS_Shape& S, TopAbs_ShapeEnum type) { int aMainIndex = 0; - if ( IsGroupOfSubShapes( S ) || (S.ShapeType() == TopAbs_VERTEX && myIndexToShape.Contains(S)) ) + if ( IsGroupOfSubShapes( S )) { aMainIndex = myIndexToShape.Add( S ); bool all = ( type == TopAbs_SHAPE ); @@ -1155,7 +1340,7 @@ int SMESHDS_Mesh::AddCompoundSubmesh(const TopoDS_Shape& S, SMESHDS_SubMesh * aNewSub = NewSubMesh( aMainIndex ); if ( !aNewSub->IsComplexSubmesh() ) // is empty { - int shapeType = all ? myShape.ShapeType() : type; + int shapeType = Max( TopAbs_SOLID, all ? myShape.ShapeType() : type ); int typeLimit = all ? TopAbs_VERTEX : type; for ( ; shapeType <= typeLimit; shapeType++ ) { @@ -1178,7 +1363,26 @@ int SMESHDS_Mesh::AddCompoundSubmesh(const TopoDS_Shape& S, //======================================================================= const TopoDS_Shape& SMESHDS_Mesh::IndexToShape(int ShapeIndex) const { - return myIndexToShape.FindKey(ShapeIndex); + try + { + return myIndexToShape.FindKey(ShapeIndex); + } + catch ( Standard_OutOfRange ) + { + } + static TopoDS_Shape nullShape; + return nullShape; +} + +//================================================================================ +/*! + * \brief Return max index of sub-mesh + */ +//================================================================================ + +int SMESHDS_Mesh::MaxSubMeshIndex() const +{ + return myShapeIndexToSubMesh.empty() ? 0 : myShapeIndexToSubMesh.rbegin()->first; } //======================================================================= @@ -1201,8 +1405,9 @@ int SMESHDS_Mesh::ShapeToIndex(const TopoDS_Shape & S) const //======================================================================= void SMESHDS_Mesh::SetNodeInVolume(const SMDS_MeshNode* aNode, int Index) { + //add(aNode, getSubmesh(Index)); if ( add( aNode, getSubmesh( Index ))) - ((SMDS_MeshNode*) aNode)->SetPosition( volumePosition( Index )); + ((SMDS_MeshNode*) aNode)->SetPosition( SMDS_SpacePosition::originSpacePosition()); } //======================================================================= @@ -1213,7 +1418,7 @@ void SMESHDS_Mesh::SetNodeOnFace(SMDS_MeshNode* aNode, int Index, double u, doub { //Set Position on Node if ( add( aNode, getSubmesh( Index ))) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_FacePosition(Index, u, v))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_FacePosition( u, v))); } //======================================================================= @@ -1226,7 +1431,7 @@ void SMESHDS_Mesh::SetNodeOnEdge(SMDS_MeshNode* aNode, { //Set Position on Node if ( add( aNode, getSubmesh( Index ))) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition(Index, u))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition(u))); } //======================================================================= @@ -1237,7 +1442,7 @@ void SMESHDS_Mesh::SetNodeOnVertex(SMDS_MeshNode* aNode, int Index) { //Set Position on Node if ( add( aNode, getSubmesh( Index ))) - aNode->SetPosition(SMDS_PositionPtr(new SMDS_VertexPosition(Index))); + aNode->SetPosition(SMDS_PositionPtr(new SMDS_VertexPosition())); } //======================================================================= @@ -1294,8 +1499,8 @@ SMDS_MeshEdge* SMESHDS_Mesh::AddEdge(const SMDS_MeshNode* n1, { SMDS_MeshEdge* anElem = SMDS_Mesh::AddEdge(n1,n2,n12); if(anElem) myScript->AddEdge(anElem->GetID(), - n1->GetID(), - n2->GetID(), + n1->GetID(), + n2->GetID(), n12->GetID()); return anElem; } @@ -1310,9 +1515,9 @@ SMDS_MeshEdge* SMESHDS_Mesh::AddEdgeWithID(const SMDS_MeshNode * n1, int ID) { return AddEdgeWithID(n1->GetID(), - n2->GetID(), + n2->GetID(), n12->GetID(), - ID); + ID); } @@ -1329,7 +1534,7 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFace(const SMDS_MeshNode * n1, { SMDS_MeshFace *anElem = SMDS_Mesh::AddFace(n1,n2,n3,n12,n23,n31); if(anElem) myScript->AddFace(anElem->GetID(), - n1->GetID(), n2->GetID(), n3->GetID(), + n1->GetID(), n2->GetID(), n3->GetID(), n12->GetID(), n23->GetID(), n31->GetID()); return anElem; } @@ -1360,7 +1565,7 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, { return AddFaceWithID(n1->GetID(), n2->GetID(), n3->GetID(), n12->GetID(), n23->GetID(), n31->GetID(), - ID); + ID); } @@ -1379,7 +1584,7 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFace(const SMDS_MeshNode * n1, { SMDS_MeshFace *anElem = SMDS_Mesh::AddFace(n1,n2,n3,n4,n12,n23,n34,n41); if(anElem) myScript->AddFace(anElem->GetID(), - n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), + n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID()); return anElem; } @@ -1412,7 +1617,63 @@ SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, { return AddFaceWithID(n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID(), - ID); + ID); +} + + +//======================================================================= +//function : AddFace +//purpose : +//======================================================================= +SMDS_MeshFace* SMESHDS_Mesh::AddFace(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter) +{ + SMDS_MeshFace *anElem = SMDS_Mesh::AddFace(n1,n2,n3,n4,n12,n23,n34,n41,nCenter); + if(anElem) myScript->AddFace(anElem->GetID(), + n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), + n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID(), + nCenter->GetID()); + return anElem; +} + +//======================================================================= +//function : AddFaceWithID +//purpose : +//======================================================================= +SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(int n1, int n2, int n3, int n4, + int n12,int n23,int n34,int n41, + int nCenter, int ID) +{ + SMDS_MeshFace *anElem = SMDS_Mesh::AddFaceWithID(n1,n2,n3,n4,n12,n23,n34,n41,nCenter,ID); + if(anElem) myScript->AddFace(ID,n1,n2,n3,n4,n12,n23,n34,n41,nCenter); + return anElem; +} + +//======================================================================= +//function : AddFaceWithID +//purpose : +//======================================================================= +SMDS_MeshFace* SMESHDS_Mesh::AddFaceWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter, + int ID) +{ + return AddFaceWithID(n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), + n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID(), + nCenter->GetID(), ID); } @@ -1433,8 +1694,8 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, { SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1,n2,n3,n4,n12,n23,n31,n14,n24,n34); if(anElem) myScript->AddVolume(anElem->GetID(), - n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), - n12->GetID(), n23->GetID(), n31->GetID(), + n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), + n12->GetID(), n23->GetID(), n31->GetID(), n14->GetID(), n24->GetID(), n34->GetID()); return anElem; } @@ -1452,7 +1713,7 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, if(anElem) myScript->AddVolume(ID,n1,n2,n3,n4,n12,n23,n31,n14,n24,n34); return anElem; } - + //======================================================================= //function : AddVolumeWithID //purpose : 2d order tetrahedron of 10 nodes @@ -1518,7 +1779,7 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, i n15,n25,n35,n45); return anElem; } - + //======================================================================= //function : AddVolumeWithID //purpose : 2d order pyramid of 13 nodes @@ -1595,7 +1856,7 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, n45,n56,n64,n14,n25,n36); return anElem; } - + //======================================================================= //function : AddVolumeWithID //purpose : 2d order Pentahedron with 15 nodes @@ -1628,7 +1889,7 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, //======================================================================= //function : AddVolume -//purpose : +//purpose : add quadratic hexahedron //======================================================================= SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, @@ -1683,7 +1944,7 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, n56,n67,n78,n85,n15,n26,n37,n48); return anElem; } - + //======================================================================= //function : AddVolumeWithID //purpose : 2d order Hexahedrons with 20 nodes @@ -1718,4 +1979,293 @@ SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, ID); } +//======================================================================= +//function : AddVolume +//purpose : add tri-quadratic hexahedron of 27 nodes +//======================================================================= + +SMDS_MeshVolume* SMESHDS_Mesh::AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter) +{ + SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolume(n1,n2,n3,n4,n5,n6,n7,n8, + n12,n23,n34,n41, + n56,n67,n78,n85, + n15,n26,n37,n48, + n1234,n1256,n2367,n3478,n1458,n5678,nCenter); + if(anElem) + myScript->AddVolume(anElem->GetID(), n1->GetID(), n2->GetID(), + n3->GetID(), n4->GetID(), n5->GetID(), + n6->GetID(), n7->GetID(), n8->GetID(), + n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID(), + n56->GetID(), n67->GetID(), n78->GetID(), n85->GetID(), + n15->GetID(), n26->GetID(), n37->GetID(), n48->GetID(), + n1234->GetID(),n1256->GetID(),n2367->GetID(),n3478->GetID(), + n1458->GetID(),n5678->GetID(),nCenter->GetID()); + return anElem; +} + +SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12,int n23,int n34,int n41, + int n56,int n67,int n78,int n85, + int n15,int n26,int n37,int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter, + int ID) +{ + SMDS_MeshVolume *anElem = SMDS_Mesh::AddVolumeWithID(n1,n2,n3,n4,n5,n6,n7,n8, + n12,n23,n34,n41, + n56,n67,n78,n85, + n15,n26,n37,n48, + n1234, n1256, n2367, n3478, + n1458, n5678, nCenter, + ID); + if(anElem) myScript->AddVolume(ID,n1,n2,n3,n4,n5,n6,n7,n8,n12,n23,n34,n41, + n56,n67,n78,n85,n15,n26,n37,n48, + n1234, n1256, n2367, n3478, + n1458, n5678, nCenter); + return anElem; +} + +SMDS_MeshVolume* SMESHDS_Mesh::AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter, + int ID) +{ + return AddVolumeWithID(n1->GetID(), n2->GetID(), n3->GetID(), n4->GetID(), + n5->GetID(), n6->GetID(), n7->GetID(), n8->GetID(), + n12->GetID(), n23->GetID(), n34->GetID(), n41->GetID(), + n56->GetID(), n67->GetID(), n78->GetID(), n85->GetID(), + n15->GetID(), n26->GetID(), n37->GetID(), n48->GetID(), + n1234->GetID(),n1256->GetID(),n2367->GetID(),n3478->GetID(), + n1458->GetID(),n5678->GetID(),nCenter->GetID(), ID); +} + +void SMESHDS_Mesh::compactMesh() +{ + int newNodeSize = 0; + int nbNodes = myNodes.size(); + int nbVtkNodes = myGrid->GetNumberOfPoints(); + MESSAGE("nbNodes=" << nbNodes << " nbVtkNodes=" << nbVtkNodes); + int nbNodeTemp = nbVtkNodes; + if (nbNodes > nbVtkNodes) + nbNodeTemp = nbNodes; + vector idNodesOldToNew; + idNodesOldToNew.clear(); + idNodesOldToNew.resize(nbNodeTemp, -1); // all unused id will be -1 + + for (int i = 0; i < nbNodes; i++) + { + if (myNodes[i]) + { + int vtkid = myNodes[i]->getVtkId(); + idNodesOldToNew[vtkid] = i; // old vtkId --> old smdsId (valid smdsId are >= 0) + newNodeSize++; + } + } + bool areNodesModified = (newNodeSize < nbVtkNodes); + MESSAGE("------------------------- compactMesh Nodes Modified: " << areNodesModified); + areNodesModified = true; + + int newCellSize = 0; + int nbCells = myCells.size(); + int nbVtkCells = myGrid->GetNumberOfCells(); + MESSAGE("nbCells=" << nbCells << " nbVtkCells=" << nbVtkCells); + int nbCellTemp = nbVtkCells; + if (nbCells > nbVtkCells) + nbCellTemp = nbCells; + vector idCellsOldToNew; + idCellsOldToNew.clear(); + idCellsOldToNew.resize(nbCellTemp, -1); // all unused id will be -1 + + for (int i = 0; i < nbCells; i++) + { + if (myCells[i]) + { +// //idCellsOldToNew[i] = myCellIdVtkToSmds[i]; // valid vtk indexes are > = 0 +// int vtkid = myCells[i]->getVtkId(); +// idCellsOldToNew[vtkid] = i; // old vtkId --> old smdsId (not used in input) + newCellSize++; + } + } + if (areNodesModified) + myGrid->compactGrid(idNodesOldToNew, newNodeSize, idCellsOldToNew, newCellSize); + else + myGrid->compactGrid(idNodesOldToNew, 0, idCellsOldToNew, newCellSize); + + int nbVtkPts = myGrid->GetNumberOfPoints(); + nbVtkCells = myGrid->GetNumberOfCells(); + if (nbVtkPts != newNodeSize) + { + MESSAGE("===> nbVtkPts != newNodeSize " << nbVtkPts << " " << newNodeSize); + if (nbVtkPts > newNodeSize) newNodeSize = nbVtkPts; // several points with same SMDS Id + } + if (nbVtkCells != newCellSize) + { + MESSAGE("===> nbVtkCells != newCellSize " << nbVtkCells << " " << newCellSize); + if (nbVtkCells > newCellSize) newCellSize = nbVtkCells; // several cells with same SMDS Id + } + + // --- SMDS_MeshNode and myNodes (id in SMDS and in VTK are the same), myNodeIdFactory + + if (areNodesModified) + { + MESSAGE("-------------- modify myNodes"); + SetOfNodes newNodes; + newNodes.resize(newNodeSize+1,0); // 0 not used, SMDS numbers 1..n + int newSmdsId = 0; + for (int i = 0; i < nbNodes; i++) + { + if (myNodes[i]) + { + newSmdsId++; // SMDS id start to 1 + int oldVtkId = myNodes[i]->getVtkId(); + int newVtkId = idNodesOldToNew[oldVtkId]; + //MESSAGE("myNodes["<< i << "] vtkId " << oldVtkId << " --> " << newVtkId); + myNodes[i]->setVtkId(newVtkId); + myNodes[i]->setId(newSmdsId); + newNodes[newSmdsId] = myNodes[i]; + //MESSAGE("myNodes["<< i << "] --> newNodes[" << newSmdsId << "]"); + } + } + myNodes.swap(newNodes); + this->myNodeIDFactory->emptyPool(newSmdsId); // newSmdsId = number of nodes + MESSAGE("myNodes.size " << myNodes.size()); + } + + // --- SMDS_MeshCell, myCellIdVtkToSmds, myCellIdSmdsToVtk, myCells + + int vtkIndexSize = myCellIdVtkToSmds.size(); + int maxVtkId = -1; + for (int oldVtkId = 0; oldVtkId < vtkIndexSize; oldVtkId++) + { + int oldSmdsId = this->myCellIdVtkToSmds[oldVtkId]; + if (oldSmdsId > 0) + { + int newVtkId = idCellsOldToNew[oldVtkId]; + if (newVtkId > maxVtkId) + maxVtkId = newVtkId; + //MESSAGE("myCells["<< oldSmdsId << "] vtkId " << oldVtkId << " --> " << newVtkId); + myCells[oldSmdsId]->setVtkId(newVtkId); + } + } +// MESSAGE("myCells.size()=" << myCells.size() +// << " myCellIdSmdsToVtk.size()=" << myCellIdSmdsToVtk.size() +// << " myCellIdVtkToSmds.size()=" << myCellIdVtkToSmds.size() ); + + SetOfCells newCells; + //vector newSmdsToVtk; + vector newVtkToSmds; + + assert(maxVtkId < newCellSize); + newCells.resize(newCellSize+1, 0); // 0 not used, SMDS numbers 1..n + //newSmdsToVtk.resize(newCellSize+1, -1); + newVtkToSmds.resize(newCellSize+1, -1); + + int myCellsSize = myCells.size(); + int newSmdsId = 0; + for (int i = 0; i < myCellsSize; i++) + { + if (myCells[i]) + { + newSmdsId++; // SMDS id start to 1 + assert(newSmdsId <= newCellSize); + newCells[newSmdsId] = myCells[i]; + newCells[newSmdsId]->setId(newSmdsId); + //MESSAGE("myCells["<< i << "] --> newCells[" << newSmdsId << "]"); + int idvtk = myCells[i]->getVtkId(); + //newSmdsToVtk[newSmdsId] = idvtk; + assert(idvtk < newCellSize); + newVtkToSmds[idvtk] = newSmdsId; + } + } + + myCells.swap(newCells); + //myCellIdSmdsToVtk.swap(newSmdsToVtk); + myCellIdVtkToSmds.swap(newVtkToSmds); + MESSAGE("myCells.size()=" << myCells.size() + << " myCellIdVtkToSmds.size()=" << myCellIdVtkToSmds.size() ); + this->myElementIDFactory->emptyPool(newSmdsId); + + this->myScript->SetModified(true); // notify GUI client for buildPrs when update + + // --- compact list myNodes and myElements in submeshes + + map::iterator it = myShapeIndexToSubMesh.begin(); + for(; it != myShapeIndexToSubMesh.end(); ++it) + { + (*it).second->compactList(); + } + +} + +void SMESHDS_Mesh::CleanDownWardConnectivity() +{ + myGrid->CleanDownwardConnectivity(); +} +void SMESHDS_Mesh::BuildDownWardConnectivity(bool withEdges) +{ + myGrid->BuildDownwardConnectivity(withEdges); +} + +/*! change some nodes in cell without modifying type or internal connectivity. + * Nodes inverse connectivity is maintained up to date. + * @param vtkVolId vtk id of the cell. + * @param localClonedNodeIds map old node id to new node id. + * @return ok if success. + */ +bool SMESHDS_Mesh::ModifyCellNodes(int vtkVolId, std::map localClonedNodeIds) +{ + myGrid->ModifyCellNodes(vtkVolId, localClonedNodeIds); + return true; +} diff --git a/src/SMESHDS/SMESHDS_Mesh.hxx b/src/SMESHDS/SMESHDS_Mesh.hxx index d3df2e688..573e06b91 100644 --- a/src/SMESHDS/SMESHDS_Mesh.hxx +++ b/src/SMESHDS/SMESHDS_Mesh.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Mesh.hxx // Module : SMESH @@ -29,36 +30,43 @@ #include "SMESH_SMESHDS.hxx" #include "SMDS_Mesh.hxx" -#include "SMDS_MeshNode.hxx" -#include "SMDS_MeshEdge.hxx" -#include "SMDS_MeshFace.hxx" -#include "SMDS_MeshVolume.hxx" -#include "SMESHDS_Hypothesis.hxx" #include "SMESHDS_SubMesh.hxx" -#include "SMESHDS_Script.hxx" #include #include -#include -#include -#include -#include -#include + +class TopoDS_Solid ; +class TopoDS_Shell ; +class TopoDS_Face ; +class TopoDS_Vertex; +class TopoDS_Edge ; + +class SMESHDS_Script; +class SMESHDS_Hypothesis; +class SMDS_MeshNode ; +class SMDS_MeshEdge ; +class SMDS_MeshFace ; +class SMDS_MeshVolume ; +class SMDS_Mesh0DElement; +class SMDS_BallElement; #include #include /* - * Using of native haah_map isn't portable and don't work on WIN32 platform. + * Using of native hash_map isn't portable and don't work on WIN32 platform. * So this functionality implement on new NCollection_DataMap technology */ #include "SMESHDS_DataMapOfShape.hxx" class SMESHDS_GroupBase; +class DownIdType; class SMESHDS_EXPORT SMESHDS_Mesh:public SMDS_Mesh{ public: SMESHDS_Mesh(int theMeshID, bool theIsEmbeddedMode); bool IsEmbeddedMode(); + void SetPersistentId(int id); + int GetPersistentId() const; void ShapeToMesh(const TopoDS_Shape & S); TopoDS_Shape ShapeToMesh() const; @@ -66,160 +74,219 @@ public: bool RemoveHypothesis(const TopoDS_Shape & S, const SMESHDS_Hypothesis * H); virtual SMDS_MeshNode* AddNodeWithID(double x, double y, double z, int ID); - virtual SMDS_MeshNode * AddNode(double x, double y, double z); + virtual SMDS_MeshNode* AddNode(double x, double y, double z); + virtual SMDS_Mesh0DElement* Add0DElementWithID(int nodeID, int ID); + virtual SMDS_Mesh0DElement* Add0DElementWithID(const SMDS_MeshNode * node, int ID); + virtual SMDS_Mesh0DElement* Add0DElement (const SMDS_MeshNode * node); + + virtual SMDS_BallElement* AddBallWithID(int n, double diameter, int ID); + virtual SMDS_BallElement* AddBallWithID(const SMDS_MeshNode * n, double diameter, int ID); + virtual SMDS_BallElement* AddBall (const SMDS_MeshNode * n, double diameter); + virtual SMDS_MeshEdge* AddEdgeWithID(int n1, int n2, int ID); virtual SMDS_MeshEdge* AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - int ID); + const SMDS_MeshNode * n2, + int ID); virtual SMDS_MeshEdge* AddEdge(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2); + const SMDS_MeshNode * n2); // 2d order edge with 3 nodes: n12 - node between n1 and n2 virtual SMDS_MeshEdge* AddEdgeWithID(int n1, int n2, int n12, int ID); virtual SMDS_MeshEdge* AddEdgeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n12, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n12, + int ID); virtual SMDS_MeshEdge* AddEdge(const SMDS_MeshNode * n1, const SMDS_MeshNode * n2, const SMDS_MeshNode * n12); - + // tria 3 virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3); + // quad 4 virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4); // 2d order triangle of 6 nodes virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n12,int n23,int n31, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31); + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31); // 2d order quadrangle virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n34,int n41, int ID); virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + int ID); virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41); + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41); + // bi-quadratic quadrangle of 9 nodes + virtual SMDS_MeshFace* AddFaceWithID(int n1, int n2, int n3, int n4, + int n12,int n23,int n34,int n41, int nCenter, int ID); + virtual SMDS_MeshFace* AddFaceWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter, + int ID); + virtual SMDS_MeshFace* AddFace(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * nCenter); + // tetra 4 virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4); + // pyra 5 virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5); + // penta 6 virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6); + // hexa 8 virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8); - + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8); + // hexagonal prism of 12 nodes + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n5, int n6, + int n7, int n8, int n9, int n10, int n11, int n12, int ID); + virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12, + int ID); + virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n9, + const SMDS_MeshNode * n10, + const SMDS_MeshNode * n11, + const SMDS_MeshNode * n12); + // 2d order tetrahedron of 10 nodes virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, int n12,int n23,int n31, int n14,int n24,int n34, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - const SMDS_MeshNode * n14, - const SMDS_MeshNode * n24, - const SMDS_MeshNode * n34, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + const SMDS_MeshNode * n14, + const SMDS_MeshNode * n24, + const SMDS_MeshNode * n34, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31, @@ -233,24 +300,24 @@ public: int n15,int n25,int n35,int n45, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - const SMDS_MeshNode * n15, - const SMDS_MeshNode * n25, - const SMDS_MeshNode * n35, - const SMDS_MeshNode * n45, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n25, + const SMDS_MeshNode * n35, + const SMDS_MeshNode * n45, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, @@ -268,27 +335,27 @@ public: int n14,int n25,int n36, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n31, - const SMDS_MeshNode * n45, - const SMDS_MeshNode * n56, - const SMDS_MeshNode * n64, - const SMDS_MeshNode * n14, - const SMDS_MeshNode * n25, - const SMDS_MeshNode * n36, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n31, + const SMDS_MeshNode * n45, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n64, + const SMDS_MeshNode * n14, + const SMDS_MeshNode * n25, + const SMDS_MeshNode * n36, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n31, @@ -307,34 +374,34 @@ public: int n15,int n26,int n37,int n48, int ID); virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, - const SMDS_MeshNode * n12, - const SMDS_MeshNode * n23, - const SMDS_MeshNode * n34, - const SMDS_MeshNode * n41, - const SMDS_MeshNode * n56, - const SMDS_MeshNode * n67, - const SMDS_MeshNode * n78, - const SMDS_MeshNode * n85, - const SMDS_MeshNode * n15, - const SMDS_MeshNode * n26, - const SMDS_MeshNode * n37, - const SMDS_MeshNode * n48, - int ID); + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + int ID); virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, - const SMDS_MeshNode * n2, - const SMDS_MeshNode * n3, - const SMDS_MeshNode * n4, - const SMDS_MeshNode * n5, - const SMDS_MeshNode * n6, - const SMDS_MeshNode * n7, - const SMDS_MeshNode * n8, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, const SMDS_MeshNode * n12, const SMDS_MeshNode * n23, const SMDS_MeshNode * n34, @@ -348,27 +415,92 @@ public: const SMDS_MeshNode * n37, const SMDS_MeshNode * n48); - virtual SMDS_MeshFace* AddPolygonalFaceWithID (std::vector nodes_ids, - const int ID); + // 2d order Hexahedrons with 27 nodes + virtual SMDS_MeshVolume* AddVolumeWithID(int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12,int n23,int n34,int n41, + int n56,int n67,int n78,int n85, + int n15,int n26,int n37,int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter, + int ID); + virtual SMDS_MeshVolume* AddVolumeWithID(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter, + int ID); + virtual SMDS_MeshVolume* AddVolume(const SMDS_MeshNode * n1, + const SMDS_MeshNode * n2, + const SMDS_MeshNode * n3, + const SMDS_MeshNode * n4, + const SMDS_MeshNode * n5, + const SMDS_MeshNode * n6, + const SMDS_MeshNode * n7, + const SMDS_MeshNode * n8, + const SMDS_MeshNode * n12, + const SMDS_MeshNode * n23, + const SMDS_MeshNode * n34, + const SMDS_MeshNode * n41, + const SMDS_MeshNode * n56, + const SMDS_MeshNode * n67, + const SMDS_MeshNode * n78, + const SMDS_MeshNode * n85, + const SMDS_MeshNode * n15, + const SMDS_MeshNode * n26, + const SMDS_MeshNode * n37, + const SMDS_MeshNode * n48, + const SMDS_MeshNode * n1234, + const SMDS_MeshNode * n1256, + const SMDS_MeshNode * n2367, + const SMDS_MeshNode * n3478, + const SMDS_MeshNode * n1458, + const SMDS_MeshNode * n5678, + const SMDS_MeshNode * nCenter); + + virtual SMDS_MeshFace* AddPolygonalFaceWithID (const std::vector& nodes_ids, + const int ID); - virtual SMDS_MeshFace* AddPolygonalFaceWithID (std::vector nodes, - const int ID); + virtual SMDS_MeshFace* AddPolygonalFaceWithID (const std::vector& nodes, + const int ID); - virtual SMDS_MeshFace* AddPolygonalFace (std::vector nodes); + virtual SMDS_MeshFace* AddPolygonalFace (const std::vector& nodes); virtual SMDS_MeshVolume* AddPolyhedralVolumeWithID - (std::vector nodes_ids, - std::vector quantities, - const int ID); + (const std::vector& nodes_ids, + const std::vector& quantities, + const int ID); virtual SMDS_MeshVolume* AddPolyhedralVolumeWithID - (std::vector nodes, - std::vector quantities, - const int ID); + (const std::vector& nodes, + const std::vector& quantities, + const int ID); virtual SMDS_MeshVolume* AddPolyhedralVolume - (std::vector nodes, - std::vector quantities); + (const std::vector& nodes, + const std::vector& quantities); void MoveNode(const SMDS_MeshNode *, double x, double y, double z); virtual void RemoveNode(const SMDS_MeshNode *); @@ -391,6 +523,7 @@ public: bool ChangePolyhedronNodes(const SMDS_MeshElement * elem, std::vector nodes, std::vector quantities); + bool ModifyCellNodes(int smdsVolId, std::map localClonedNodeIds); void Renumber (const bool isNodes, const int startID=1, const int deltaID=1); void SetNodeInVolume(SMDS_MeshNode * aNode, const TopoDS_Shell & S); @@ -400,23 +533,25 @@ public: void SetNodeOnVertex(SMDS_MeshNode * aNode, const TopoDS_Vertex & S); void UnSetNodeOnShape(const SMDS_MeshNode * aNode); void SetMeshElementOnShape(const SMDS_MeshElement * anElt, - const TopoDS_Shape & S); + const TopoDS_Shape & S); void UnSetMeshElementOnShape(const SMDS_MeshElement * anElt, - const TopoDS_Shape & S); - bool HasMeshElements(const TopoDS_Shape & S); + const TopoDS_Shape & S); + bool HasMeshElements(const TopoDS_Shape & S) const; SMESHDS_SubMesh * MeshElements(const TopoDS_Shape & S) const; - SMESHDS_SubMesh * MeshElements(const int Index); - std::list SubMeshIndices(); + SMESHDS_SubMesh * MeshElements(const int Index) const; + std::list SubMeshIndices() const; const std::map& SubMeshes() const { return myShapeIndexToSubMesh; } bool HasHypothesis(const TopoDS_Shape & S); const std::list& GetHypothesis(const TopoDS_Shape & S) const; + bool IsUsedHypothesis(const SMESHDS_Hypothesis * H) const; SMESHDS_Script * GetScript(); void ClearScript(); int ShapeToIndex(const TopoDS_Shape & aShape) const; const TopoDS_Shape& IndexToShape(int ShapeIndex) const; int MaxShapeIndex() const { return myIndexToShape.Extent(); } + int MaxSubMeshIndex() const; SMESHDS_SubMesh * NewSubMesh(int Index); int AddCompoundSubmesh(const TopoDS_Shape& S, TopAbs_ShapeEnum type = TopAbs_SHAPE); @@ -426,6 +561,7 @@ public: void SetNodeOnVertex(SMDS_MeshNode * aNode, int Index); void SetMeshElementOnShape(const SMDS_MeshElement * anElt, int Index); + // Groups. SMESHDS_Mesh is not an owner of groups void AddGroup (SMESHDS_GroupBase* theGroup) { myGroups.insert(theGroup); } void RemoveGroup (SMESHDS_GroupBase* theGroup) { myGroups.erase(theGroup); } int GetNbGroups() const { return myGroups.size(); } @@ -433,6 +569,10 @@ public: bool IsGroupOfSubShapes (const TopoDS_Shape& aSubShape) const; + virtual void compactMesh(); + void CleanDownWardConnectivity(); + void BuildDownWardConnectivity(bool withEdges); + ~SMESHDS_Mesh(); private: @@ -441,9 +581,9 @@ private: //Update or build submesh std::map::iterator it = myShapeIndexToSubMesh.find( Index ); if ( it == myShapeIndexToSubMesh.end() ) - it = myShapeIndexToSubMesh.insert( std::make_pair(Index, new SMESHDS_SubMesh() )).first; + it = myShapeIndexToSubMesh.insert( std::make_pair(Index, new SMESHDS_SubMesh(this, Index) )).first; it->second->AddNode( aNode ); // add aNode to submesh - } + } /*int HashCode( const TopoDS_Shape& S, const Standard_Integer theUpper ) const { @@ -456,7 +596,7 @@ private: ShapeToHypothesis myShapeToHypothesis; - int myMeshID; + int myMeshID, myPersistentID; TopoDS_Shape myShape; typedef std::map TShapeIndexToSubMesh; diff --git a/src/SMESHDS/SMESHDS_Script.cxx b/src/SMESHDS/SMESHDS_Script.cxx index 9b6daa8b1..b4ec13f7c 100644 --- a/src/SMESHDS/SMESHDS_Script.cxx +++ b/src/SMESHDS/SMESHDS_Script.cxx @@ -1,31 +1,32 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESH_Script.cxx // Author : Yves FRICAUD, OCC // Module : SMESH -// $Header: // #include "SMESHDS_Script.hxx" +#include using namespace std; @@ -35,7 +36,9 @@ using namespace std; //======================================================================= SMESHDS_Script::SMESHDS_Script(bool theIsEmbeddedMode): myIsEmbeddedMode(theIsEmbeddedMode) -{} +{ + //cerr << "=========================== myIsEmbeddedMode " << myIsEmbeddedMode << endl; +} //======================================================================= //function : Destructor @@ -95,6 +98,19 @@ void SMESHDS_Script::AddNode(int NewNodeID, double x, double y, double z) getCommand(SMESHDS_AddNode)->AddNode(NewNodeID, x, y, z); } +//======================================================================= +//function : +//purpose : +//======================================================================= +void SMESHDS_Script::Add0DElement (int New0DElementID, int idnode) +{ + if (myIsEmbeddedMode) { + myIsModified = true; + return; + } + getCommand(SMESHDS_Add0DElement)->Add0DElement(New0DElementID, idnode); +} + //======================================================================= //function : //purpose : @@ -208,11 +224,29 @@ void SMESHDS_Script::AddVolume(int NewID, idnode5, idnode6, idnode7, idnode8); } +//======================================================================= +//function : +//purpose : +//======================================================================= +void SMESHDS_Script::AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8, + int idnode9, int idnode10, int idnode11, int idnode12) +{ + if(myIsEmbeddedMode){ + myIsModified = true; + return; + } + getCommand(SMESHDS_AddHexagonalPrism)->AddVolume(NewVolID, + idnode1, idnode2, idnode3, idnode4, + idnode5, idnode6, idnode7, idnode8, + idnode9, idnode10, idnode11, idnode12); +} + //======================================================================= //function : AddPolygonalFace //purpose : //======================================================================= -void SMESHDS_Script::AddPolygonalFace (int NewFaceID, std::vector nodes_ids) +void SMESHDS_Script::AddPolygonalFace (int NewFaceID, const std::vector& nodes_ids) { if(myIsEmbeddedMode){ myIsModified = true; @@ -225,9 +259,9 @@ void SMESHDS_Script::AddPolygonalFace (int NewFaceID, std::vector nodes_ids //function : AddPolyhedralVolume //purpose : //======================================================================= -void SMESHDS_Script::AddPolyhedralVolume (int NewID, - std::vector nodes_ids, - std::vector quantities) +void SMESHDS_Script::AddPolyhedralVolume (int NewID, + const std::vector& nodes_ids, + const std::vector& quantities) { if(myIsEmbeddedMode){ myIsModified = true; @@ -237,6 +271,19 @@ void SMESHDS_Script::AddPolyhedralVolume (int NewID, (NewID, nodes_ids, quantities); } +//======================================================================= +//function : AddBall +//purpose : Record adding a Ball +//======================================================================= + +void SMESHDS_Script::AddBall(int NewBallID, int node, double diameter) +{ + if ( myIsEmbeddedMode ) + myIsModified = true; + else + getCommand(SMESHDS_AddBall)->AddBall(NewBallID, node, diameter); +} + //======================================================================= //function : //purpose : @@ -294,9 +341,9 @@ void SMESHDS_Script::ChangeElementNodes(int ElementID, int nodes[], int nbnodes) //function : ChangePolyhedronNodes //purpose : //======================================================================= -void SMESHDS_Script::ChangePolyhedronNodes (const int ElementID, - std::vector nodes_ids, - std::vector quantities) +void SMESHDS_Script::ChangePolyhedronNodes (const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities) { if(myIsEmbeddedMode){ myIsModified = true; @@ -403,6 +450,21 @@ void SMESHDS_Script::AddFace(int NewFaceID, int n1, int n2, int n3, int n4, n12, n23, n34, n41); } +//======================================================================= +//function : AddFace +//purpose : +//======================================================================= +void SMESHDS_Script::AddFace(int NewFaceID, int n1, int n2, int n3, int n4, + int n12, int n23, int n34, int n41, int nCenter) +{ + if(myIsEmbeddedMode){ + myIsModified = true; + return; + } + getCommand(SMESHDS_AddBiQuadQuadrangle)->AddFace(NewFaceID, n1, n2, n3, n4, + n12, n23, n34, n41, nCenter); +} + //======================================================================= //function : AddVolume //purpose : @@ -477,3 +539,28 @@ void SMESHDS_Script::AddVolume(int NewVolID, int n1, int n2, int n3, n15, n26, n37, n48); } +//======================================================================= +//function : AddVolume +//purpose : +//======================================================================= +void SMESHDS_Script::AddVolume(int NewVolID, int n1, int n2, int n3, + int n4, int n5, int n6, int n7, int n8, + int n12, int n23, int n34, int n41, + int n56, int n67, int n78, int n85, + int n15, int n26, int n37, int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter) +{ + if(myIsEmbeddedMode){ + myIsModified = true; + return; + } + getCommand(SMESHDS_AddTriQuadHexa)->AddVolume(NewVolID, n1, n2, n3, n4, + n5, n6, n7, n8, + n12, n23, n34, n41, + n56, n67, n78, n85, + n15, n26, n37, n48, + n1234, n1256, n2367, n3478, + n1458, n5678, nCenter); +} + diff --git a/src/SMESHDS/SMESHDS_Script.hxx b/src/SMESHDS/SMESHDS_Script.hxx index 808066bc3..8bd3fc579 100644 --- a/src/SMESHDS/SMESHDS_Script.hxx +++ b/src/SMESHDS/SMESHDS_Script.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_Script.hxx // Module : SMESH @@ -37,38 +38,45 @@ class SMESHDS_EXPORT SMESHDS_Script { public: - SMESHDS_Script(bool theIsEmbeddedMode); - ~SMESHDS_Script(); + SMESHDS_Script(bool theIsEmbeddedMode); + ~SMESHDS_Script(); void SetModified(bool theModified); bool IsModified(); - void AddNode(int NewNodeID, double x, double y, double z); - void AddEdge(int NewEdgeID, int idnode1, int idnode2); - void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3); - void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3, - int idnode4); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5, int idnode6); - void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, - int idnode4, int idnode5, int idnode6, int idnode7, int idnode8); + void AddNode(int NewNodeID, double x, double y, double z); + void Add0DElement(int New0DElementID, int idnode); + void AddEdge(int NewEdgeID, int idnode1, int idnode2); + void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3); + void AddFace(int NewFaceID, int idnode1, int idnode2, int idnode3, + int idnode4); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8); + void AddVolume(int NewVolID, int idnode1, int idnode2, int idnode3, + int idnode4, int idnode5, int idnode6, int idnode7, int idnode8, + int idnode9, int idnode10, int idnode11, int idnode12); - void AddPolygonalFace (const int NewFaceID, - std::vector nodes_ids); - void AddPolyhedralVolume (const int NewVolID, - std::vector nodes_ids, - std::vector quantities); + void AddPolygonalFace (const int NewFaceID, + const std::vector& nodes_ids); + void AddPolyhedralVolume (const int NewVolID, + const std::vector& nodes_ids, + const std::vector& quantities); + void AddBall(int NewBallID, int node, double diameter); // special methods for quadratic elements - void AddEdge(int NewEdgeID, int n1, int n2, int n12); + void AddEdge(int NewEdgeID, int n1, int n2, int n12); void AddFace(int NewFaceID, int n1, int n2, int n3, int n12, int n23, int n31); void AddFace(int NewFaceID, int n1, int n2, int n3, int n4, int n12, int n23, int n34, int n41); + void AddFace(int NewFaceID, int n1, int n2, int n3, int n4, + int n12, int n23, int n34, int n41, int nCenter); void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, int n12, int n23, int n31, int n14, int n24, int n34); void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, int n5, @@ -84,22 +92,29 @@ class SMESHDS_EXPORT SMESHDS_Script int n12, int n23, int n34, int n41, int n56, int n67, int n78, int n85, int n15, int n26, int n37, int n48); + void AddVolume(int NewVolID, int n1, int n2, int n3, int n4, + int n5, int n6, int n7, int n8, + int n12, int n23, int n34, int n41, + int n56, int n67, int n78, int n85, + int n15, int n26, int n37, int n48, + int n1234,int n1256,int n2367,int n3478, + int n1458,int n5678,int nCenter); void MoveNode(int NewNodeID, double x, double y, double z); - void RemoveNode(int NodeID); - void RemoveElement(int ElementID); - void ChangeElementNodes(int ElementID, int nodes[], int nbnodes); - void ChangePolyhedronNodes(const int ElementID, - std::vector nodes_ids, - std::vector quantities); - void Renumber (const bool isNodes, const int startID, const int deltaID); - void ClearMesh(); - void Clear(); - const std::list & GetCommands(); + void RemoveNode(int NodeID); + void RemoveElement(int ElementID); + void ChangeElementNodes(int ElementID, int nodes[], int nbnodes); + void ChangePolyhedronNodes(const int ElementID, + const std::vector& nodes_ids, + const std::vector& quantities); + void Renumber (const bool isNodes, const int startID, const int deltaID); + void ClearMesh(); + void Clear(); + const std::list & GetCommands(); private: - SMESHDS_Command* getCommand(const SMESHDS_CommandType aType); + SMESHDS_Command* getCommand(const SMESHDS_CommandType aType); - std::list myCommands; + std::list myCommands; bool myIsEmbeddedMode; bool myIsModified; diff --git a/src/SMESHDS/SMESHDS_SubMesh.cxx b/src/SMESHDS/SMESHDS_SubMesh.cxx index 127c5c2d9..32073bb50 100644 --- a/src/SMESHDS/SMESHDS_SubMesh.cxx +++ b/src/SMESHDS/SMESHDS_SubMesh.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESH_SubMesh.cxx // Author : Yves FRICAUD, OCC @@ -26,31 +27,127 @@ // $Header: // #include "SMESHDS_SubMesh.hxx" +#include "SMESHDS_Mesh.hxx" #include "utilities.h" #include "SMDS_SetIterator.hxx" +#include +#include using namespace std; + +//================================================================================ +/*! + * \brief Constructor + */ +//================================================================================ + +SMESHDS_SubMesh::SMESHDS_SubMesh(SMESHDS_Mesh *parent, int index) +{ + myParent = parent; + myElements.clear(); + myNodes.clear(); + myIndex = index; + myUnusedIdNodes = 0; + myUnusedIdElements = 0; +} + +//================================================================================ +/*! + * \brief Destructor + */ +//================================================================================ + +SMESHDS_SubMesh::~SMESHDS_SubMesh() +{ +} + //======================================================================= //function : AddElement //purpose : //======================================================================= void SMESHDS_SubMesh::AddElement(const SMDS_MeshElement * ME) { - if ( !IsComplexSubmesh() ) - myElements.insert(ME); + if (!IsComplexSubmesh()) + { + if ( ME->GetType() == SMDSAbs_Node ) + { + AddNode( static_cast< const SMDS_MeshNode* >( ME )); + return; + } + //MESSAGE("in " << myIndex << " AddElement "<< ME->GetID()); + int oldShapeId = ME->getshapeId(); + if ( oldShapeId > 0 ) + { + if (oldShapeId != myIndex) + { + MESSAGE("add element in subshape already belonging to another subshape " + << ME->GetID() << " " << oldShapeId << " " << myIndex); + throw SALOME_Exception(LOCALIZED("add element in subshape already belonging to a subshape")); + } + else + { + int idInSubShape = ME->getIdInShape(); + if (idInSubShape >= 0) + { + MESSAGE("add element in subshape already belonging to that subshape " + << ME->GetID() << " " << oldShapeId << " " << idInSubShape); + // check if ok: do nothing if ok + if (idInSubShape >= myElements.size()) + { + MESSAGE("out of bounds " << idInSubShape << " " << myElements.size()); + throw SALOME_Exception(LOCALIZED("out of bounds")); + } + if (ME != myElements[idInSubShape]) + { + MESSAGE("not the same element"); + throw SALOME_Exception(LOCALIZED("not the same element")); + } + MESSAGE("already done, OK, nothing to do"); + return; + } + } + } + + SMDS_MeshElement* elem = (SMDS_MeshElement*) (ME); + elem->setShapeId(myIndex); + elem->setIdInShape(myElements.size()); + myElements.push_back(ME); + } } //======================================================================= //function : RemoveElement //purpose : //======================================================================= -bool SMESHDS_SubMesh::RemoveElement(const SMDS_MeshElement * ME) +bool SMESHDS_SubMesh::RemoveElement(const SMDS_MeshElement * ME, bool isElemDeleted) { - if ( !IsComplexSubmesh() && NbElements() ) - return myElements.erase(ME); - + if (!ME) + { + MESSAGE("-----------------> Remove Null Element " << isElemDeleted); + return false; + } + //MESSAGE("-----------------> RemoveElement "<< ME->GetID() << " " << isElemDeleted); + if (!IsComplexSubmesh()) + { + if ( ME->getshapeId() != myIndex ) + return false; + int idInSubShape = ME->getIdInShape(); + //MESSAGE("in "<< myIndex << " RemoveElement " << ME->GetID() << " " << idInSubShape << " " << myUnusedIdElements); + SMDS_MeshElement* elem = (SMDS_MeshElement*) (ME); + elem->setShapeId(0); + elem->setIdInShape(-1); + if ((idInSubShape >= 0) && (idInSubShape < myElements.size())) + { + myElements[idInSubShape] = 0; // this vector entry is no more used + myUnusedIdElements++; + return true; + } + return false; + // } + } + MESSAGE("Try to remove an element from a complex submesh "); return false; } @@ -61,7 +158,22 @@ bool SMESHDS_SubMesh::RemoveElement(const SMDS_MeshElement * ME) void SMESHDS_SubMesh::AddNode(const SMDS_MeshNode * N) { if ( !IsComplexSubmesh() ) - myNodes.insert(N); + { + int idInSubShape = N->getIdInShape(); + int shapeId = N->getshapeId(); + if ((shapeId > 0) && (idInSubShape >= 0)) + { +// MESSAGE("========== AddNode already belonging to other subShape " << N->GetID()); + // OK for vertex nodes + throw SALOME_Exception(LOCALIZED("add node in subshape already belonging to a subshape")); + } + SMDS_MeshNode* node = (SMDS_MeshNode*)(N); + node->setShapeId(myIndex); + node->setIdInShape(myNodes.size()); + myNodes.push_back(N); + //MESSAGE("in "<< myIndex << " AddNode " << node->GetID()); + } + //MESSAGE("try to add node in a complex submesh " << N->GetID()); } //======================================================================= @@ -69,11 +181,30 @@ void SMESHDS_SubMesh::AddNode(const SMDS_MeshNode * N) //purpose : //======================================================================= -bool SMESHDS_SubMesh::RemoveNode(const SMDS_MeshNode * N) +bool SMESHDS_SubMesh::RemoveNode(const SMDS_MeshNode * N, bool isNodeDeleted) { - if ( !IsComplexSubmesh() && NbNodes() ) - return myNodes.erase(N); - + if (!IsComplexSubmesh()) + { + // if (!isNodeDeleted) // alive node has valid ID and can be found + // { + if ( N->getshapeId() != myIndex ) + return false; + int idInSubShape = N->getIdInShape(); + //int shapeId = N->getshapeId(); + //MESSAGE("in "<< myIndex << " RemoveNode " << shapeId << " " << idInSubShape << " " << N->GetID()); + SMDS_MeshNode* node = (SMDS_MeshNode*) (N); + node->setShapeId(0); + node->setIdInShape(-1); + if ((idInSubShape >= 0) && (idInSubShape < myNodes.size())) + { + myNodes[idInSubShape] = 0; // this vector entry is no more used + myUnusedIdNodes++; + return true; + } + return false; + // } + } + MESSAGE("Try to remove a node from a complex submesh"); return false; } @@ -83,15 +214,12 @@ bool SMESHDS_SubMesh::RemoveNode(const SMDS_MeshNode * N) //======================================================================= int SMESHDS_SubMesh::NbElements() const { + //MESSAGE(this << " NbElements " << IsComplexSubmesh() << " " << myElements.size() - myUnusedIdElements); if ( !IsComplexSubmesh() ) - return myElements.size(); + return myElements.size() - myUnusedIdElements; int nbElems = 0; -#ifndef WNT - set::iterator it = mySubMeshes.begin(); -#else set::const_iterator it = mySubMeshes.begin(); -#endif for ( ; it != mySubMeshes.end(); it++ ) nbElems += (*it)->NbElements(); @@ -105,46 +233,53 @@ int SMESHDS_SubMesh::NbElements() const int SMESHDS_SubMesh::NbNodes() const { - if ( !IsComplexSubmesh() ) - return myNodes.size(); + //MESSAGE(this << " NbNodes " << IsComplexSubmesh() << " " << myNodes.size() - myUnusedIdNodes); + if ( !IsComplexSubmesh() ) + return myNodes.size() - myUnusedIdNodes; int nbElems = 0; -#ifndef WNT - set::iterator it = mySubMeshes.begin(); -#else set::const_iterator it = mySubMeshes.begin(); -#endif for ( ; it != mySubMeshes.end(); it++ ) nbElems += (*it)->NbNodes(); return nbElems; } -// ===================== -// class MySetIterator -// ===================== - -template class MySetIterator:public SMDS_Iterator +/*! + * template class used for iteration on submesh elements. Interface of iterator remains + * unchanged after redesign of SMDS to avoid modification everywhere in SMESH. + * instances are stored in shared_ptr for automatic destruction. + * Container is copied for iteration, because original can be modified + * by addition of elements, for instance, and then reallocated (vector) + */ +template class MySetIterator : public SMDS_Iterator { - typedef const set TSet; - typename TSet::const_iterator myIt; - TSet& mySet; - - public: - MySetIterator(const set& s):mySet(s), myIt(s.begin()) - { - } - - bool more() - { - return myIt!=mySet.end(); - } - const T* next() - { - const T* t=*myIt; - myIt++; - return t; - } +protected: + typename TSET::const_iterator _it, _end; + TSET _table; +public: + MySetIterator(const TSET& table) + { + _table = table; + _it = _table.begin(); + _end = _table.end(); + while ((_it != _end) && (*_it == 0)) + _it++; + } + + virtual bool more() + { + while ((_it != _end) && (*_it == 0)) + _it++; + return (_it != _end); + } + + virtual ELEM next() + { + ELEM e = *_it; + _it++; + return e; + } }; // ===================== @@ -155,12 +290,11 @@ template class MyIterator : public SMDS_Iterator { public: MyIterator (const set& theSubMeshes) - : mySubMeshes( theSubMeshes ), mySubIt( theSubMeshes.begin() ), myMore(false) + : mySubIt( theSubMeshes.begin() ), mySubEnd( theSubMeshes.end() ), myMore(false) {} bool more() { - while (( !myElemIt.get() || !myElemIt->more() ) && - mySubIt != mySubMeshes.end()) + while (( !myElemIt.get() || !myElemIt->more() ) && mySubIt != mySubEnd) { myElemIt = getElements(*mySubIt); mySubIt++; @@ -181,8 +315,7 @@ template class MyIterator : public SMDS_Iterator private: bool myMore; - const set& mySubMeshes; - set::const_iterator mySubIt; + set::const_iterator mySubIt, mySubEnd; boost::shared_ptr< SMDS_Iterator > myElemIt; }; @@ -221,8 +354,7 @@ SMDS_ElemIteratorPtr SMESHDS_SubMesh::GetElements() const { if ( IsComplexSubmesh() ) return SMDS_ElemIteratorPtr( new MyElemIterator( mySubMeshes )); - - return SMDS_ElemIteratorPtr(new MySetIterator(myElements)); + return SMDS_ElemIteratorPtr(new MySetIterator >(myElements)); } //======================================================================= @@ -235,7 +367,7 @@ SMDS_NodeIteratorPtr SMESHDS_SubMesh::GetNodes() const if ( IsComplexSubmesh() ) return SMDS_NodeIteratorPtr( new MyNodeIterator( mySubMeshes )); - return SMDS_NodeIteratorPtr(new MySetIterator(myNodes)); + return SMDS_NodeIteratorPtr(new MySetIterator >(myNodes)); } //======================================================================= @@ -246,25 +378,34 @@ SMDS_NodeIteratorPtr SMESHDS_SubMesh::GetNodes() const bool SMESHDS_SubMesh::Contains(const SMDS_MeshElement * ME) const { // DO NOT TRY TO FIND A REMOVED ELEMENT !! - if ( !ME ) - return false; - - if ( IsComplexSubmesh() ) - { - set::const_iterator aSubIt = mySubMeshes.begin(); - for ( ; aSubIt != mySubMeshes.end(); aSubIt++ ) - if ( (*aSubIt)->Contains( ME )) - return true; + //if ( IsComplexSubmesh() || !ME ) + if (!ME) return false; - } - if ( ME->GetType() == SMDSAbs_Node ) - { - const SMDS_MeshNode* n = static_cast( ME ); - return ( myNodes.find( n ) != myNodes.end() ); - } + if (IsComplexSubmesh()) + { + set::const_iterator aSubIt = mySubMeshes.begin(); + for (; aSubIt != mySubMeshes.end(); aSubIt++) + if ((*aSubIt)->Contains(ME)) + return true; + return false; + } - return ( myElements.find( ME ) != myElements.end() ); + if (ME->GetType() == SMDSAbs_Node) + { + int idInShape = ME->getIdInShape(); + if ((idInShape >= 0) && (idInShape < myNodes.size())) + if (myNodes[idInShape] == ME) + return true; + } + else + { + int idInShape = ME->getIdInShape(); + if ((idInShape >= 0) && (idInShape < myElements.size())) + if (myElements[idInShape] == ME) + return true; + } + return false; } //======================================================================= @@ -288,6 +429,16 @@ bool SMESHDS_SubMesh::RemoveSubMesh( const SMESHDS_SubMesh* theSubMesh ) return mySubMeshes.erase( theSubMesh ); } +//======================================================================= +//function : RemoveAllSubmeshes +//purpose : +//======================================================================= + +void SMESHDS_SubMesh::RemoveAllSubmeshes() +{ + mySubMeshes.clear(); +} + //======================================================================= //function : ContainsSubMesh //purpose : @@ -320,9 +471,66 @@ void SMESHDS_SubMesh::Clear() { myElements.clear(); myNodes.clear(); + myUnusedIdNodes = 0; + myUnusedIdElements = 0; SMESHDS_SubMeshIteratorPtr sub = GetSubMeshIterator(); while ( sub->more() ) { if ( SMESHDS_SubMesh* sm = (SMESHDS_SubMesh*) sub->next()) sm->Clear(); } } + +int SMESHDS_SubMesh::getSize() +{ + int c = NbNodes(); + int d = NbElements(); + //cerr << "SMESHDS_SubMesh::NbNodes " << c << endl; + //cerr << "SMESHDS_SubMesh::NbElements " << d << endl; + return c+d; +} + +void SMESHDS_SubMesh::compactList() +{ + //MESSAGE("compactList old: nodes " << myNodes.size() << " elements " << myElements.size()); + //stringstream a; + //stringstream b; + //stringstream c; + //stringstream d; + + std::vector newElems; + newElems.clear(); + for (int i = 0; i < myElements.size(); i++) + if (myElements[i]) + { + SMDS_MeshElement* elem = (SMDS_MeshElement*)myElements[i]; + elem->setIdInShape(newElems.size()); + newElems.push_back(elem); + //a << elem->GetID() << " "; + //b << elem->GetID() << " "; + } + //else + // a << "_ "; + myElements.swap(newElems); + myUnusedIdElements = 0; + //MESSAGE("in " << myIndex << " oldElems " << a.str()); + //MESSAGE("in " << myIndex << " newElems " << b.str()); + + std::vector newNodes; + newNodes.clear(); + for (int i = 0; i < myNodes.size(); i++) + if (myNodes[i]) + { + SMDS_MeshNode* node = (SMDS_MeshNode*)myNodes[i]; + node->setIdInShape(newNodes.size()); + newNodes.push_back(node); + //c << node->GetID() << " "; + //d << node->GetID() << " "; + } + //else + // c << "_ "; + myNodes.swap(newNodes); + myUnusedIdNodes = 0; + //MESSAGE("in " << myIndex << " oldNodes " << c.str()); + //MESSAGE("in " << myIndex << " newNodes " << d.str()); + //MESSAGE("compactList new: nodes " << myNodes.size() << " elements " << myElements.size()); +} diff --git a/src/SMESHDS/SMESHDS_SubMesh.hxx b/src/SMESHDS/SMESHDS_SubMesh.hxx index 7b2eaa416..049c73e33 100644 --- a/src/SMESHDS/SMESHDS_SubMesh.hxx +++ b/src/SMESHDS/SMESHDS_SubMesh.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHDS : management of mesh data and SMESH document // File : SMESHDS_SubMesh.hxx // Module : SMESH @@ -30,44 +31,59 @@ #include "SMDS_Mesh.hxx" #include +#include class SMESHDS_SubMesh; typedef SMDS_Iterator SMESHDS_SubMeshIterator; typedef boost::shared_ptr< SMESHDS_SubMeshIterator > SMESHDS_SubMeshIteratorPtr; +class SMESHDS_Mesh; + class SMESHDS_EXPORT SMESHDS_SubMesh { public: + SMESHDS_SubMesh(SMESHDS_Mesh *parent, int index); + virtual ~SMESHDS_SubMesh(); - bool IsComplexSubmesh() const { return !mySubMeshes.empty(); } + virtual bool IsComplexSubmesh() const { return !mySubMeshes.empty(); } // if !IsComplexSubmesh() - void AddElement(const SMDS_MeshElement * ME); - bool RemoveElement(const SMDS_MeshElement * ME); // ret true if ME was in - void AddNode(const SMDS_MeshNode * ME); - bool RemoveNode(const SMDS_MeshNode * ME); // ret true if ME was in + virtual void AddElement(const SMDS_MeshElement * ME); + virtual bool RemoveElement(const SMDS_MeshElement * ME, bool isElemDeleted); // ret true if ME was in + virtual void AddNode(const SMDS_MeshNode * ME); + virtual bool RemoveNode(const SMDS_MeshNode * ME, bool isNodeDeleted); // ret true if ME was in // if IsComplexSubmesh() void AddSubMesh( const SMESHDS_SubMesh* theSubMesh ); bool RemoveSubMesh( const SMESHDS_SubMesh* theSubMesh ); + void RemoveAllSubmeshes(); bool ContainsSubMesh( const SMESHDS_SubMesh* theSubMesh ) const; int NbSubMeshes() const { return mySubMeshes.size(); } SMESHDS_SubMeshIteratorPtr GetSubMeshIterator() const; // for both types - int NbElements() const; - SMDS_ElemIteratorPtr GetElements() const; - int NbNodes() const; - SMDS_NodeIteratorPtr GetNodes() const; - bool Contains(const SMDS_MeshElement * ME) const; // check if elem or node is in + virtual int NbElements() const; + virtual SMDS_ElemIteratorPtr GetElements() const; + virtual int NbNodes() const; + virtual SMDS_NodeIteratorPtr GetNodes() const; + virtual bool Contains(const SMDS_MeshElement * ME) const; // check if elem or node is in // clear the contents - void Clear(); + virtual void Clear(); + int getSize(); + void compactList(); + + inline SMESHDS_Mesh *getParent() {return myParent; }; private: - //const SMDS_Mesh * myMesh; - std::set myElements; - std::set myNodes; - std::set mySubMeshes; + SMESHDS_Mesh * myParent; + std::vector myElements; + std::vector myNodes; + + int myUnusedIdNodes; + int myUnusedIdElements; + int myIndex; + + std::set mySubMeshes; }; #endif diff --git a/src/SMESHDS/SMESH_Controls.hxx b/src/SMESHDS/SMESH_Controls.hxx new file mode 100644 index 000000000..49d6ab1d2 --- /dev/null +++ b/src/SMESHDS/SMESH_Controls.hxx @@ -0,0 +1,79 @@ +// Copyright (C) 2007-2012 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 +// + +#ifndef _SMESH_CONTROLS_HXX_ +#define _SMESH_CONTROLS_HXX_ + +#include "SMDSAbs_ElementType.hxx" + +#include + +#ifdef WNT + #if defined SMESHCONTROLS_EXPORTS || defined SMESHControls_EXPORTS + #define SMESHCONTROLS_EXPORT __declspec( dllexport ) + #else + #define SMESHCONTROLS_EXPORT __declspec( dllimport ) + #endif +#else + #define SMESHCONTROLS_EXPORT +#endif + +class SMDS_Mesh; + +namespace SMESH{ + namespace Controls{ + + /* + Class : Functor + Description : Root of all Functors + */ + class SMESHCONTROLS_EXPORT Functor + { + public: + ~Functor(){} + virtual void SetMesh( const SMDS_Mesh* theMesh ) = 0; + virtual SMDSAbs_ElementType GetType() const = 0; + }; + typedef boost::shared_ptr FunctorPtr; + + + class NumericalFunctor; + typedef boost::shared_ptr NumericalFunctorPtr; + + /* + Class : Predicate + Description : Base class for all predicates + */ + class SMESHCONTROLS_EXPORT Predicate: public virtual Functor{ + public: + virtual bool IsSatisfy( long theElementId ) = 0; + virtual SMDSAbs_ElementType GetType() const = 0; + }; + typedef boost::shared_ptr PredicatePtr; + + } +} + +typedef SMESH::Controls::PredicatePtr SMESH_PredicatePtr; + + +#endif diff --git a/src/SMESHDS/SMESH_SMESHDS.hxx b/src/SMESHDS/SMESH_SMESHDS.hxx index 4e45bbfaa..cc22f1f6d 100755 --- a/src/SMESHDS/SMESH_SMESHDS.hxx +++ b/src/SMESHDS/SMESH_SMESHDS.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_SMESHDS.hxx // Author : Alexander A. BORODIN // Module : SMESH diff --git a/src/SMESHFiltersSelection/Makefile.am b/src/SMESHFiltersSelection/Makefile.am index 183083cc7..6b15a26b3 100644 --- a/src/SMESHFiltersSelection/Makefile.am +++ b/src/SMESHFiltersSelection/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESHFiltersSelection : filter selector for viewer # File : Makefile.in # Author : Patrick GOLDBRONN (CEA) @@ -57,12 +55,12 @@ libSMESHFiltersSelection_la_CPPFLAGS = \ $(BOOST_CPPFLAGS) \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix + -I$(top_builddir)/idl libSMESHFiltersSelection_la_LDFLAGS = \ ../../idl/libSalomeIDLSMESH.la \ $(KERNEL_LDFLAGS) -lSalomeDSClient -lSalomeDS \ - $(GUI_LDFLAGS) -lSalomeApp -lsuit \ - $(GEOM_LDFLAGS) -lGEOM \ - $(CAS_LDPATH) -lTKernel + $(GUI_LDFLAGS) -lSalomeApp -lsuit -lLightApp \ + $(GEOM_LDFLAGS) -lGEOM -lGEOMClient \ + $(CAS_LDPATH) -lTKernel -lTKMath -lTKBRep \ + $(QT_MT_LIBS) diff --git a/src/SMESHFiltersSelection/SMESH_LogicalFilter.cxx b/src/SMESHFiltersSelection/SMESH_LogicalFilter.cxx index f8f908f83..bcfa96bcb 100644 --- a/src/SMESHFiltersSelection/SMESH_LogicalFilter.cxx +++ b/src/SMESHFiltersSelection/SMESH_LogicalFilter.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_LogicalFilter.cxx // Module : SMESH // @@ -29,7 +30,9 @@ // Purpose : Constructor //======================================================================= SMESH_LogicalFilter::SMESH_LogicalFilter (const QList& theFilters, - const int theLogOp) + const int theLogOp, + bool takeOwnership) + : myOwnership( takeOwnership ) { setFilters(theFilters); setOperation(theLogOp); @@ -41,6 +44,7 @@ SMESH_LogicalFilter::SMESH_LogicalFilter (const QList& th //======================================================================= SMESH_LogicalFilter::~SMESH_LogicalFilter() { + deleteFilters(); } //======================================================================= @@ -69,6 +73,7 @@ bool SMESH_LogicalFilter::isOk (const SUIT_DataOwner* owner) const //======================================================================= void SMESH_LogicalFilter::setFilters (const QList& theFilters) { + deleteFilters(); myFilters = theFilters; } @@ -98,3 +103,18 @@ int SMESH_LogicalFilter::getOperation() const { return myOperation; } +//================================================================================ +/*! + * \brief Deletes filters if has an ownership + */ +//================================================================================ + +void SMESH_LogicalFilter::deleteFilters() +{ + if ( myOwnership ) + { + SUIT_SelectionFilter* filter; + foreach( filter, myFilters ) + delete filter; + } +} diff --git a/src/SMESHFiltersSelection/SMESH_LogicalFilter.hxx b/src/SMESHFiltersSelection/SMESH_LogicalFilter.hxx index 928313f80..3069fa890 100644 --- a/src/SMESHFiltersSelection/SMESH_LogicalFilter.hxx +++ b/src/SMESHFiltersSelection/SMESH_LogicalFilter.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_LogicalFilter.hxx // Module : SMESH // @@ -37,7 +38,7 @@ class SMESHFILTERSSELECTION_EXPORT SMESH_LogicalFilter : public SUIT_SelectionFi enum { LO_OR, LO_AND, LO_NOT, LO_UNDEFINED }; public: - SMESH_LogicalFilter( const QList&, const int ); + SMESH_LogicalFilter( const QList&, const int, bool takeOwnership=false ); virtual ~SMESH_LogicalFilter(); virtual bool isOk (const SUIT_DataOwner*) const; @@ -47,9 +48,13 @@ class SMESHFILTERSSELECTION_EXPORT SMESH_LogicalFilter : public SUIT_SelectionFi const QList getFilters() const; int getOperation() const; +private: + void deleteFilters(); + private: QList myFilters; int myOperation; + bool myOwnership; }; #endif diff --git a/src/SMESHFiltersSelection/SMESH_NumberFilter.cxx b/src/SMESHFiltersSelection/SMESH_NumberFilter.cxx index d8633f307..5699e263c 100644 --- a/src/SMESHFiltersSelection/SMESH_NumberFilter.cxx +++ b/src/SMESHFiltersSelection/SMESH_NumberFilter.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_NumberFilter.cxx // Module : SMESH // diff --git a/src/SMESHFiltersSelection/SMESH_NumberFilter.hxx b/src/SMESHFiltersSelection/SMESH_NumberFilter.hxx index 45724cfa1..6400d9b36 100644 --- a/src/SMESHFiltersSelection/SMESH_NumberFilter.hxx +++ b/src/SMESHFiltersSelection/SMESH_NumberFilter.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_NumberFilter.hxx // Module : SMESH // diff --git a/src/SMESHFiltersSelection/SMESH_Type.h b/src/SMESHFiltersSelection/SMESH_Type.h index 106110e09..e29c918a0 100644 --- a/src/SMESHFiltersSelection/SMESH_Type.h +++ b/src/SMESHFiltersSelection/SMESH_Type.h @@ -1,36 +1,36 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Type.h // Created : Mon Jun 03 15:14:15 2002 // Author : Nicolas REJNERI // Project : SALOME // Module : SMESH -// $Header$ // #ifndef SMESH_TYPE_HEADER #define SMESH_TYPE_HEADER #ifdef WNT - #if defined SMESHFILTERSSELECTION_EXPORTS + #if defined SMESHFILTERSSELECTION_EXPORTS || defined SMESHFiltersSelection_EXPORTS #define SMESHFILTERSSELECTION_EXPORT __declspec( dllexport ) #else #define SMESHFILTERSSELECTION_EXPORT __declspec( dllimport ) @@ -51,7 +51,14 @@ enum MeshObjectType { SUBMESH_SOLID, SUBMESH_COMPOUND, GROUP, - COMPONENT + GROUP_NODE, + GROUP_EDGE, + GROUP_FACE, + GROUP_VOLUME, + GROUP_0D, + GROUP_BALL, + COMPONENT, + IDSOURCE }; #endif diff --git a/src/SMESHFiltersSelection/SMESH_TypeFilter.cxx b/src/SMESHFiltersSelection/SMESH_TypeFilter.cxx index 509b87240..e9b83166c 100644 --- a/src/SMESHFiltersSelection/SMESH_TypeFilter.cxx +++ b/src/SMESHFiltersSelection/SMESH_TypeFilter.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + #include "SMESH_TypeFilter.hxx" #include @@ -80,6 +81,7 @@ bool SMESH_TypeFilter::isOk (const SUIT_DataOwner* theDataOwner) const // 4 | |- Applied algorithms ( selectable in Use Case Browser ) // 5 | |- Regular 1D // |- Group Of Nodes + // |- Group 1 if (aLevel <= 0) return false; @@ -87,90 +89,132 @@ bool SMESH_TypeFilter::isOk (const SUIT_DataOwner* theDataOwner) const switch (myType) { case HYPOTHESIS: - { - if (aLevel == 2 && (objFather->Tag() == SMESH::Tag_HypothesisRoot)) + { + if (aLevel == 2 && (objFather->Tag() == SMESH::Tag_HypothesisRoot)) // hypo definition - Ok = true; - else if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_RefOnAppliedHypothesis)) + Ok = true; + else if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_RefOnAppliedHypothesis)) // applied global hypo - Ok = true; - else if (aLevel == 5 && (objFather->Tag() == SMESH::Tag_RefOnAppliedHypothesis)) + Ok = true; + else if (aLevel == 5 && (objFather->Tag() == SMESH::Tag_RefOnAppliedHypothesis)) // applied local hypo - Ok = true; - break; - } + Ok = true; + break; + } case ALGORITHM: - { - if (aLevel == 2 && (objFather->Tag() == SMESH::Tag_AlgorithmsRoot)) + { + if (aLevel == 2 && (objFather->Tag() == SMESH::Tag_AlgorithmsRoot)) // algo definition - Ok = true; - else if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_RefOnAppliedAlgorithms)) + Ok = true; + else if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_RefOnAppliedAlgorithms)) // applied global algo - Ok = true; - else if (aLevel == 5 && (objFather->Tag() == SMESH::Tag_RefOnAppliedAlgorithms)) + Ok = true; + else if (aLevel == 5 && (objFather->Tag() == SMESH::Tag_RefOnAppliedAlgorithms)) // applied local algo - Ok = true; - break; - } + Ok = true; + break; + } case MESH: - { - if (aLevel == 1 && (obj->Tag() >= SMESH::Tag_FirstMeshRoot)) - Ok = true; - break; - } + { + if (aLevel == 1 && (obj->Tag() >= SMESH::Tag_FirstMeshRoot)) + Ok = true; + break; + } case SUBMESH: - { - // see SMESH_Gen_i.cxx for tag numbers - if (aLevel == 3 && (objFather->Tag() >= SMESH::Tag_FirstSubMesh && + { + // see SMESH_Gen_i.cxx for tag numbers + if (aLevel == 3 && (objFather->Tag() >= SMESH::Tag_FirstSubMesh && objFather->Tag() <= SMESH::Tag_LastSubMesh)) - Ok = true; - break; - } + Ok = true; + break; + } case MESHorSUBMESH: - { - if (aLevel == 1 && (obj->Tag() >= SMESH::Tag_FirstMeshRoot)) - Ok = true; // mesh + { + if (aLevel == 1 && (obj->Tag() >= SMESH::Tag_FirstMeshRoot)) + Ok = true; // mesh else if (aLevel == 3 && (objFather->Tag() >= SMESH::Tag_FirstSubMesh && objFather->Tag() <= SMESH::Tag_LastSubMesh)) - Ok = true; - break; - } + Ok = true; + break; + } case SUBMESH_VERTEX: // Label "SubMeshes on vertexes" - { - if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnVertex)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnVertex)) + Ok = true; + break; + } case SUBMESH_EDGE: - { - if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnEdge)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnEdge)) + Ok = true; + break; + } case SUBMESH_FACE: - { - if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnFace)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnFace)) + Ok = true; + break; + } case SUBMESH_SOLID: - { - if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnSolid)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnSolid)) + Ok = true; + break; + } case SUBMESH_COMPOUND: - { - if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnCompound)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_SubMeshOnCompound)) + Ok = true; + break; + } case GROUP: - { - if (aLevel == 3 && (objFather->Tag() >= SMESH::Tag_FirstGroup)) - Ok = true; - break; - } + { + if (aLevel == 3 && (objFather->Tag() >= SMESH::Tag_FirstGroup)) + Ok = true; + break; + } + case GROUP_NODE: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_NodeGroups)) + Ok = true; + break; + } + case GROUP_EDGE: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_EdgeGroups)) + Ok = true; + break; + } + case GROUP_FACE: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_FaceGroups)) + Ok = true; + break; + } + case GROUP_VOLUME: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_VolumeGroups)) + Ok = true; + break; + } + case GROUP_0D: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_0DElementsGroups)) + Ok = true; + break; + } + case GROUP_BALL: + { + if (aLevel == 3 && (objFather->Tag() == SMESH::Tag_BallElementsGroups)) + Ok = true; + break; + } + case IDSOURCE: + { + Ok = ( SMESH_TypeFilter(MESHorSUBMESH).isOk( theDataOwner ) || + SMESH_TypeFilter(GROUP) .isOk( theDataOwner )); + break; + } } } return Ok; diff --git a/src/SMESHFiltersSelection/SMESH_TypeFilter.hxx b/src/SMESHFiltersSelection/SMESH_TypeFilter.hxx index d966bfaa5..0821917d0 100644 --- a/src/SMESHFiltersSelection/SMESH_TypeFilter.hxx +++ b/src/SMESHFiltersSelection/SMESH_TypeFilter.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_TypeFilter.hxx // Module : SMESH // diff --git a/src/SMESHGUI/Makefile.am b/src/SMESHGUI/Makefile.am index db288673a..15925385f 100644 --- a/src/SMESHGUI/Makefile.am +++ b/src/SMESHGUI/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESHGUI : GUI for SMESH component # File : Makefile.am # Author : Alexander BORODIN, Open CASCADE S.A.S. @@ -49,12 +47,10 @@ salomeinclude_HEADERS = \ SMESHGUI_GroupDlg.h \ SMESHGUI_RemoveNodesDlg.h \ SMESHGUI_RemoveElementsDlg.h \ - SMESHGUI_MeshInfosDlg.h \ - SMESHGUI_StandardMeshInfosDlg.h \ - SMESHGUI_WhatIsDlg.h \ + SMESHGUI_MeshInfo.h \ + SMESHGUI_Measurements.h \ SMESHGUI_Preferences_ColorDlg.h \ SMESHGUI_Preferences_ScalarBarDlg.h \ - SMESHGUI_MoveNodesDlg.h \ SMESHGUI_AddMeshElementDlg.h \ SMESHGUI_XmlHandler.h \ SMESHGUI_Filter.h \ @@ -71,9 +67,11 @@ salomeinclude_HEADERS = \ SMESHGUI_RevolutionDlg.h \ SMESHGUI_RotationDlg.h \ SMESHGUI_TranslationDlg.h \ + SMESHGUI_ScaleDlg.h \ SMESHGUI_SymmetryDlg.h \ SMESHGUI_SewingDlg.h \ - SMESHGUI_EditMeshDlg.h \ + SMESHGUI_DuplicateNodesDlg.h \ + SMESHGUI_MergeDlg.h \ SMESHGUI_MeshUtils.h \ SMESHGUI_CreatePolyhedralVolumeDlg.h \ SMESHGUI_Operation.h \ @@ -90,6 +88,15 @@ salomeinclude_HEADERS = \ SMESHGUI_MakeNodeAtPointDlg.h \ SMESHGUI_MeshEditPreview.h \ SMESHGUI_IdValidator.h \ + SMESHGUI_MeshInfosBox.h \ + SMESHGUI_Make2DFrom3DOp.h \ + SMESHGUI_FindElemByPointDlg.h \ + SMESHGUI_MeshOrderDlg.h \ + SMESHGUI_MeshOrderOp.h \ + SMESHGUI_FileValidator.h \ + SMESHGUI_CopyMeshDlg.h \ + SMESHGUI_PreviewDlg.h \ + SMESHGUI_ReorientFacesDlg.h \ SMESH_SMESHGUI.hxx # Libraries targets @@ -106,12 +113,10 @@ dist_libSMESH_la_SOURCES = \ SMESHGUI_GroupDlg.cxx \ SMESHGUI_RemoveNodesDlg.cxx \ SMESHGUI_RemoveElementsDlg.cxx \ - SMESHGUI_MeshInfosDlg.cxx \ - SMESHGUI_StandardMeshInfosDlg.cxx \ - SMESHGUI_WhatIsDlg.cxx \ + SMESHGUI_MeshInfo.cxx \ + SMESHGUI_Measurements.cxx \ SMESHGUI_Preferences_ColorDlg.cxx \ SMESHGUI_Preferences_ScalarBarDlg.cxx \ - SMESHGUI_MoveNodesDlg.cxx \ SMESHGUI_AddMeshElementDlg.cxx \ SMESHGUI_XmlHandler.cxx \ SMESHGUI_Filter.cxx \ @@ -128,9 +133,11 @@ dist_libSMESH_la_SOURCES = \ SMESHGUI_RevolutionDlg.cxx \ SMESHGUI_RotationDlg.cxx \ SMESHGUI_TranslationDlg.cxx \ + SMESHGUI_ScaleDlg.cxx \ SMESHGUI_SymmetryDlg.cxx \ SMESHGUI_SewingDlg.cxx \ - SMESHGUI_EditMeshDlg.cxx \ + SMESHGUI_DuplicateNodesDlg.cxx \ + SMESHGUI_MergeDlg.cxx \ SMESHGUI_Utils.cxx \ SMESHGUI_GEOMGenUtils.cxx \ SMESHGUI_MeshUtils.cxx \ @@ -157,7 +164,16 @@ dist_libSMESH_la_SOURCES = \ SMESHGUI_MakeNodeAtPointDlg.cxx \ SMESHGUI_MeshEditPreview.cxx \ SMESHGUI_GroupOnShapeDlg.cxx \ - SMESHGUI_FileInfoDlg.cxx + SMESHGUI_FileInfoDlg.cxx \ + SMESHGUI_MeshInfosBox.cxx \ + SMESHGUI_Make2DFrom3DOp.cxx \ + SMESHGUI_FindElemByPointDlg.cxx \ + SMESHGUI_MeshOrderDlg.cxx \ + SMESHGUI_MeshOrderOp.cxx \ + SMESHGUI_CopyMeshDlg.cxx \ + SMESHGUI_FileValidator.cxx \ + SMESHGUI_PreviewDlg.cxx \ + SMESHGUI_ReorientFacesDlg.cxx MOC_FILES = \ SMESHGUI_moc.cxx \ @@ -171,12 +187,10 @@ MOC_FILES = \ SMESHGUI_GroupDlg_moc.cxx \ SMESHGUI_RemoveNodesDlg_moc.cxx \ SMESHGUI_RemoveElementsDlg_moc.cxx \ - SMESHGUI_MeshInfosDlg_moc.cxx \ - SMESHGUI_StandardMeshInfosDlg_moc.cxx \ - SMESHGUI_WhatIsDlg_moc.cxx \ + SMESHGUI_MeshInfo_moc.cxx \ + SMESHGUI_Measurements_moc.cxx \ SMESHGUI_Preferences_ColorDlg_moc.cxx \ SMESHGUI_Preferences_ScalarBarDlg_moc.cxx \ - SMESHGUI_MoveNodesDlg_moc.cxx \ SMESHGUI_AddMeshElementDlg_moc.cxx \ SMESHGUI_FilterDlg_moc.cxx \ SMESHGUI_FilterLibraryDlg_moc.cxx \ @@ -191,9 +205,11 @@ MOC_FILES = \ SMESHGUI_RevolutionDlg_moc.cxx \ SMESHGUI_RotationDlg_moc.cxx \ SMESHGUI_TranslationDlg_moc.cxx \ + SMESHGUI_ScaleDlg_moc.cxx \ SMESHGUI_SymmetryDlg_moc.cxx \ SMESHGUI_SewingDlg_moc.cxx \ - SMESHGUI_EditMeshDlg_moc.cxx \ + SMESHGUI_DuplicateNodesDlg_moc.cxx \ + SMESHGUI_MergeDlg_moc.cxx \ SMESHGUI_CreatePolyhedralVolumeDlg_moc.cxx \ SMESHGUI_Operation_moc.cxx \ SMESHGUI_SelectionOp_moc.cxx \ @@ -209,7 +225,15 @@ MOC_FILES = \ SMESHGUI_ComputeDlg_moc.cxx \ SMESHGUI_MakeNodeAtPointDlg_moc.cxx \ SMESHGUI_GroupOnShapeDlg_moc.cxx \ - SMESHGUI_FileInfoDlg_moc.cxx + SMESHGUI_FileInfoDlg_moc.cxx \ + SMESHGUI_MeshInfosBox_moc.cxx \ + SMESHGUI_Make2DFrom3DOp_moc.cxx \ + SMESHGUI_FindElemByPointDlg_moc.cxx \ + SMESHGUI_MeshOrderDlg_moc.cxx \ + SMESHGUI_CopyMeshDlg_moc.cxx \ + SMESHGUI_MeshOrderOp_moc.cxx \ + SMESHGUI_PreviewDlg_moc.cxx \ + SMESHGUI_ReorientFacesDlg_moc.cxx nodist_libSMESH_la_SOURCES= \ $(MOC_FILES) @@ -223,33 +247,38 @@ libSMESH_la_CPPFLAGS = \ $(PYTHON_INCLUDES) \ $(KERNEL_CXXFLAGS) \ $(GUI_CXXFLAGS) \ + $(QWT_INCLUDES) \ $(GEOM_CXXFLAGS) \ $(MED_CXXFLAGS) \ $(BOOST_CPPFLAGS) \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ - $(MED2_INCLUDES) \ + $(MED3_INCLUDES) \ $(HDF5_INCLUDES) \ -I$(srcdir)/../OBJECT \ -I$(srcdir)/../SMESHFiltersSelection \ -I$(srcdir)/../SMDS \ + -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../SMESH \ + -I$(srcdir)/../SMESHUtils \ -I$(srcdir)/../SMESH_I \ -I$(srcdir)/../Controls \ -I$(srcdir)/../SMESHClient \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix - + -I$(top_builddir) \ + -I$(top_builddir)/idl libSMESH_la_LDFLAGS = \ ../SMESHFiltersSelection/libSMESHFiltersSelection.la \ ../SMDS/libSMDS.la \ ../Controls/libSMESHControls.la \ ../OBJECT/libSMESHObject.la \ - $(GEOM_LDFLAGS) \ - -lGEOMBase + $(CAS_LDFLAGS) -lTKV3d \ + $(GUI_LDFLAGS) -lSalomePrs -lsuit -lSUITApp -lCAM -lstd \ + $(GEOM_LDFLAGS) -lGEOMBase -lGEOMFiltersSelection -lGEOMObject \ + $(PYTHON_LIBS) # resources files nodist_salomeres_DATA= \ SMESH_images.qm \ - SMESH_msg_en.qm + SMESH_msg_en.qm \ + SMESH_msg_fr.qm diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 50f44fb18..52f3180ad 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -1,72 +1,82 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI.cxx -// Author : Nicolas REJNERI, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI.cxx +// Author : Nicolas REJNERI, Open CASCADE S.A.S. + +#include // E.A. must be included before Python.h to fix compilation on windows +#ifdef HAVE_FINITE +#undef HAVE_FINITE // VSR: avoid compilation warning on Linux : "HAVE_FINITE" redefined +#endif +#include "Python.h" +// SMESH includes #include "SMESHGUI.h" -#include "SMESHGUI_NodesDlg.h" -#include "SMESHGUI_TransparencyDlg.h" -#include "SMESHGUI_ClippingDlg.h" -#include "SMESHGUI_GroupDlg.h" -#include "SMESHGUI_RemoveNodesDlg.h" -#include "SMESHGUI_RemoveElementsDlg.h" -#include "SMESHGUI_MeshInfosDlg.h" -#include "SMESHGUI_StandardMeshInfosDlg.h" -#include "SMESHGUI_WhatIsDlg.h" -#include "SMESHGUI_Preferences_ColorDlg.h" -#include "SMESHGUI_Preferences_ScalarBarDlg.h" -#include "SMESHGUI_Hypotheses.h" -#include "SMESHGUI_MoveNodesDlg.h" #include "SMESHGUI_AddMeshElementDlg.h" #include "SMESHGUI_AddQuadraticElementDlg.h" +#include "SMESHGUI_BuildCompoundDlg.h" +#include "SMESHGUI_ClippingDlg.h" +#include "SMESHGUI_ComputeDlg.h" +#include "SMESHGUI_ConvToQuadOp.h" +#include "SMESHGUI_CreatePolyhedralVolumeDlg.h" +#include "SMESHGUI_DeleteGroupDlg.h" +#include "SMESHGUI_Displayer.h" +#include "SMESHGUI_MergeDlg.h" +#include "SMESHGUI_ExtrusionAlongPathDlg.h" +#include "SMESHGUI_ExtrusionDlg.h" +#include "SMESHGUI_FileInfoDlg.h" +#include "SMESHGUI_FileValidator.h" #include "SMESHGUI_FilterDlg.h" #include "SMESHGUI_FilterLibraryDlg.h" -#include "SMESHGUI_SingleEditDlg.h" -#include "SMESHGUI_MultiEditDlg.h" -#include "SMESHGUI_GroupOpDlg.h" +#include "SMESHGUI_FindElemByPointDlg.h" +#include "SMESHGUI_GroupDlg.h" #include "SMESHGUI_GroupOnShapeDlg.h" -#include "SMESHGUI_DeleteGroupDlg.h" -#include "SMESHGUI_SmoothingDlg.h" +#include "SMESHGUI_GroupOpDlg.h" +#include "SMESHGUI_Hypotheses.h" +#include "SMESHGUI_Make2DFrom3DOp.h" +#include "SMESHGUI_MakeNodeAtPointDlg.h" +#include "SMESHGUI_Measurements.h" +#include "SMESHGUI_MeshInfo.h" +#include "SMESHGUI_MeshOp.h" +#include "SMESHGUI_MeshOrderOp.h" +#include "SMESHGUI_MeshPatternDlg.h" +#include "SMESHGUI_MultiEditDlg.h" +#include "SMESHGUI_NodesDlg.h" +#include "SMESHGUI_Preferences_ColorDlg.h" +#include "SMESHGUI_Preferences_ScalarBarDlg.h" +#include "SMESHGUI_RemoveElementsDlg.h" +#include "SMESHGUI_RemoveNodesDlg.h" #include "SMESHGUI_RenumberingDlg.h" -#include "SMESHGUI_ExtrusionDlg.h" -#include "SMESHGUI_ExtrusionAlongPathDlg.h" #include "SMESHGUI_RevolutionDlg.h" -#include "SMESHGUI_TranslationDlg.h" #include "SMESHGUI_RotationDlg.h" -#include "SMESHGUI_SymmetryDlg.h" -#include "SMESHGUI_SewingDlg.h" -#include "SMESHGUI_EditMeshDlg.h" -#include "SMESHGUI_MeshPatternDlg.h" #include "SMESHGUI_Selection.h" -#include "SMESHGUI_CreatePolyhedralVolumeDlg.h" -#include "SMESHGUI_ConvToQuadOp.h" -#include "SMESHGUI_MeshOp.h" -#include "SMESHGUI_Displayer.h" -#include "SMESHGUI_MakeNodeAtPointDlg.h" -#include "SMESHGUI_BuildCompoundDlg.h" -#include "SMESHGUI_ComputeDlg.h" -#include "SMESHGUI_FileInfoDlg.h" +#include "SMESHGUI_SewingDlg.h" +#include "SMESHGUI_SingleEditDlg.h" +#include "SMESHGUI_SmoothingDlg.h" +#include "SMESHGUI_SymmetryDlg.h" +#include "SMESHGUI_TranslationDlg.h" +#include "SMESHGUI_ScaleDlg.h" +#include "SMESHGUI_TransparencyDlg.h" +#include "SMESHGUI_DuplicateNodesDlg.h" +#include "SMESHGUI_CopyMeshDlg.h" +#include "SMESHGUI_ReorientFacesDlg.h" #include "SMESHGUI_Utils.h" #include "SMESHGUI_MeshUtils.h" @@ -76,15 +86,21 @@ #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_HypothesesUtils.h" +#include + #include #include +#include +#include #include +#include "SMESH_ControlsDef.hxx" // SALOME GUI includes #include #include #include #include +#include #include #include @@ -96,6 +112,8 @@ #include #include +#include + #include #include #include @@ -109,23 +127,32 @@ #include #include +#ifndef DISABLE_PLOT2DVIEWER +#include +#include +#endif + // IDL includes #include #include CORBA_CLIENT_HEADER(SALOMEDS_Attributes) #include CORBA_CLIENT_HEADER(SMESH_MeshEditor) +#include CORBA_CLIENT_HEADER(SMESH_Measurements) // Qt includes -// #define INCLUDE_MENUITEM_DEF // VSR commented ???????? +// #define INCLUDE_MENUITEM_DEF // VSR commented ???????? +#include #include +#include // BOOST includes #include // VTK includes -#include #include #include #include +#include +#include // SALOME KERNEL includes #include @@ -138,15 +165,19 @@ #include #include +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //namespace{ // Declarations //============================================================= void ImportMeshesFromFile(SMESH::SMESH_Gen_ptr theComponentMesh, - int theCommandID); + int theCommandID); void ExportMeshToFile(int theCommandID); - void SetDisplayMode(int theCommandID); + void SetDisplayMode(int theCommandID, SMESHGUI_StudyId2MarkerMap& theMarkerMap); void SetDisplayEntity(int theCommandID); @@ -156,20 +187,30 @@ // Definitions //============================================================= void ImportMeshesFromFile( SMESH::SMESH_Gen_ptr theComponentMesh, - int theCommandID ) + int theCommandID ) { QStringList filter; std::string myExtension; if ( theCommandID == 113 ) { - filter.append( QObject::tr( "MED files (*.med)" ) ); - filter.append( QObject::tr( "All files (*)" ) ); + filter.append( QObject::tr( "MED_FILES_FILTER" ) + " (*.med)" ); + filter.append( QObject::tr( "ALL_FILES_FILTER" ) + " (*)" ); } else if ( theCommandID == 112 ) { - filter.append( QObject::tr( "IDEAS files (*.unv)" ) ); + filter.append( QObject::tr( "IDEAS_FILES_FILTER" ) + " (*.unv)" ); } else if ( theCommandID == 111 ) { - filter.append( QObject::tr( "DAT files (*.dat)" ) ); + filter.append( QObject::tr( "DAT_FILES_FILTER" ) + " (*.dat)" ); + } + else if ( theCommandID == 115 ) { + filter.append( QObject::tr( "STL_ASCII_FILES_FILTER" ) + " (*.stl)" ); + } + else if ( theCommandID == 116 ) { + filter.append( QObject::tr( "CGNS_FILES_FILTER" ) + " (*.cgns)" ); + } + else if ( theCommandID == 117 ) { + filter.append( QObject::tr( "SAUV files (*.sauv*)" ) ); + filter.append( QObject::tr( "All files (*)" ) ); } QString anInitialPath = ""; @@ -177,89 +218,143 @@ anInitialPath = QDir::currentPath(); QStringList filenames = SUIT_FileDlg::getOpenFileNames( SMESHGUI::desktop(), - anInitialPath, - filter, - QObject::tr( "SMESH_IMPORT_MESH" ) ); + anInitialPath, + filter, + QObject::tr( "SMESH_IMPORT_MESH" ) ); if ( filenames.count() > 0 ) { SUIT_OverrideCursor wc; _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); QStringList errors; + QStringList anEntryList; bool isEmpty = false; for ( QStringList::ConstIterator it = filenames.begin(); it != filenames.end(); ++it ) { - QString filename = *it; - SMESH::mesh_array_var aMeshes = new SMESH::mesh_array; - try { - switch ( theCommandID ) { - case 111: - { - // DAT format (currently unsupported) - errors.append( QString( "%1 :\n\t%2" ).arg( filename ). - arg( QObject::tr( "SMESH_ERR_NOT_SUPPORTED_FORMAT" ) ) ); - break; - } - case 112: - { - // UNV format - aMeshes->length( 1 ); - aMeshes[0] = theComponentMesh->CreateMeshesFromUNV( filename.toLatin1().constData() ); - if ( aMeshes[0]->_is_nil() ) - errors.append( QString( "%1 :\n\t%2" ).arg( filename ). - arg( QObject::tr( "SMESH_ERR_UNKNOWN_IMPORT_ERROR" ) ) ); - break; - } - case 113: - { - // MED format - SMESH::DriverMED_ReadStatus res; - aMeshes = theComponentMesh->CreateMeshesFromMED( filename.toLatin1().constData(), res ); - if ( res != SMESH::DRS_OK ) { - errors.append( QString( "%1 :\n\t%2" ).arg( filename ). - arg( QObject::tr( QString( "SMESH_DRS_%1" ).arg( res ).toLatin1().data() ) ) ); - } - break; - } - } - } - catch ( const SALOME::SALOME_Exception& S_ex ) { - errors.append( QString( "%1 :\n\t%2" ).arg( filename ). - arg( QObject::tr( "SMESH_ERR_UNKNOWN_IMPORT_ERROR" ) ) ); - } - - for ( int i = 0, iEnd = aMeshes->length(); i < iEnd; i++ ) { - _PTR(SObject) aMeshSO = SMESH::FindSObject( aMeshes[i] ); - if ( aMeshSO ) { - _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); - _PTR(AttributePixMap) aPixmap = aBuilder->FindOrCreateAttribute( aMeshSO, "AttributePixMap" ); - aPixmap->SetPixMap( "ICON_SMESH_TREE_MESH_IMPORTED" ); - if ( theCommandID == 112 ) // mesh names aren't taken from the file for UNV import - SMESH::SetName( aMeshSO, QFileInfo(filename).fileName() ); - } - else { - isEmpty = true; - } - } + QString filename = *it; + SMESH::mesh_array_var aMeshes = new SMESH::mesh_array; + try { + switch ( theCommandID ) { + case 111: + { + // DAT format (currently unsupported) + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( "SMESH_ERR_NOT_SUPPORTED_FORMAT" ) ) ); + break; + } + case 112: + { + // UNV format + aMeshes->length( 1 ); + aMeshes[0] = theComponentMesh->CreateMeshesFromUNV( filename.toLatin1().constData() ); + if ( aMeshes[0]->_is_nil() ) + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( "SMESH_ERR_UNKNOWN_IMPORT_ERROR" ) ) ); + break; + } + case 113: + { + // MED format + SMESH::DriverMED_ReadStatus res; + aMeshes = theComponentMesh->CreateMeshesFromMED( filename.toLatin1().constData(), res ); + if ( res != SMESH::DRS_OK ) { + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( QString( "SMESH_DRS_%1" ).arg( res ).toLatin1().data() ) ) ); + } + break; + } + case 115: + { + // STL format + aMeshes->length( 1 ); + aMeshes[0] = theComponentMesh->CreateMeshesFromSTL( filename.toLatin1().constData() ); + if ( aMeshes[0]->_is_nil() ) { + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( "SMESH_ERR_UNKNOWN_IMPORT_ERROR" ) ) ); + } + break; + } + case 116: + { + // CGNS format + SMESH::DriverMED_ReadStatus res; + aMeshes = theComponentMesh->CreateMeshesFromCGNS( filename.toLatin1().constData(), res ); + if ( res != SMESH::DRS_OK ) { + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( QString( "SMESH_DRS_%1" ).arg( res ).toLatin1().data() ) ) ); + } + break; + } + case 117: + { + // SAUV format + SMESH::DriverMED_ReadStatus res; + aMeshes = theComponentMesh->CreateMeshesFromSAUV( filename.toLatin1().constData(), res ); + if ( res != SMESH::DRS_OK ) { + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( QString( "SMESH_DRS_%1" ).arg( res ).toLatin1().data() ) ) ); + } + break; + } + } + } + catch ( const SALOME::SALOME_Exception& S_ex ) { + errors.append( QString( "%1 :\n\t%2" ).arg( filename ). + arg( QObject::tr( "SMESH_ERR_UNKNOWN_IMPORT_ERROR" ) ) ); + } + + for ( int i = 0, iEnd = aMeshes->length(); i < iEnd; i++ ) { + _PTR(SObject) aMeshSO = SMESH::FindSObject( aMeshes[i] ); + if ( aMeshSO ) { + _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); + _PTR(AttributePixMap) aPixmap = aBuilder->FindOrCreateAttribute( aMeshSO, "AttributePixMap" ); + aPixmap->SetPixMap( "ICON_SMESH_TREE_MESH_IMPORTED" ); + if ( theCommandID == 112 ) // mesh names aren't taken from the file for UNV import + SMESH::SetName( aMeshSO, QFileInfo(filename).fileName() ); + + anEntryList.append( aMeshSO->GetID().c_str() ); + +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + aMeshes[i]->UnRegister(); +#endif + } + else { + isEmpty = true; + } + } } // update Object browser SMESHGUI::GetSMESHGUI()->updateObjBrowser(); + // browse to the published meshes + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList ); + // show Error message box if there were errors if ( errors.count() > 0 ) { - SUIT_MessageBox::critical( SMESHGUI::desktop(), - QObject::tr( "SMESH_ERROR" ), - QObject::tr( "SMESH_IMPORT_ERRORS" ) + "\n" + errors.join( "\n" ) ); + SUIT_MessageBox::critical( SMESHGUI::desktop(), + QObject::tr( "SMESH_ERROR" ), + QObject::tr( "SMESH_IMPORT_ERRORS" ) + "\n" + errors.join( "\n" ) ); } // show warning message box, if some imported mesh is empty if ( isEmpty ) { - SUIT_MessageBox::warning( SMESHGUI::desktop(), - QObject::tr( "SMESH_WRN_WARNING" ), - QObject::tr( "SMESH_DRS_SOME_EMPTY" ) ); + SUIT_MessageBox::warning( SMESHGUI::desktop(), + QObject::tr( "SMESH_WRN_WARNING" ), + QObject::tr( "SMESH_DRS_SOME_EMPTY" ) ); } } } + //================================================================================ + /*! + * \brief Export selected meshes or groups into a file + */ + //================================================================================ + void ExportMeshToFile( int theCommandID ) { LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); @@ -267,132 +362,261 @@ if( aSel ) aSel->selectedObjects( selected ); - SMESH::SMESH_Mesh_var aMesh; - if(selected.Extent() == 1) - aMesh = SMESH::IObjectToInterface(selected.First()); - if ( aMesh->_is_nil() ) { - SUIT_MessageBox::warning( SMESHGUI::desktop(), - QObject::tr( "SMESH_WRN_WARNING" ), - QObject::tr( "SMESH_BAD_MESH_SELECTION" )); + const bool isMED = ( theCommandID == 122 || theCommandID == 125 ); + const bool isDAT = ( theCommandID == 121 || theCommandID == 124 ); + const bool isUNV = ( theCommandID == 123 || theCommandID == 126 ); + const bool isSTL = ( theCommandID == 140 || theCommandID == 141 ); + const bool isCGNS= ( theCommandID == 142 || theCommandID == 143 ); + const bool isSAUV= ( theCommandID == 144 || theCommandID == 145 ); + + // actually, the following condition can't be met (added for insurance) + if( selected.Extent() == 0 || + ( selected.Extent() > 1 && !isMED && !isSTL )) return; - } - Handle(SALOME_InteractiveObject) anIObject = selected.First(); - QString aFilter, aTitle = QObject::tr("Export mesh"); - QMap aFilterMap; - QMap aFilterMapSTL; - switch ( theCommandID ) { - case 125: - case 122: - { - if (aMesh->HasDuplicatedGroupNamesMED()) { - int aRet = SUIT_MessageBox::warning - (SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_MED_DUPLICATED_GRP").arg(anIObject->getName()), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::Yes); - if (aRet != SUIT_MessageBox::Yes) - return; - } - // PAL18696 - QString v21 (aMesh->GetVersionString(SMESH::MED_V2_1, 2)); - QString v22 (aMesh->GetVersionString(SMESH::MED_V2_2, 2)); - aFilterMap.insert( QString("MED ") + v21 + " (*.med)", SMESH::MED_V2_1 ); - aFilterMap.insert( QString("MED ") + v22 + " (*.med)", SMESH::MED_V2_2 ); - } - break; - case 124: - case 121: - aFilter = QObject::tr("DAT files (*.dat)"); - break; - case 126: - case 123: - { - if (aMesh->NbPyramids()) { - int aRet = SUIT_MessageBox::warning - (SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_UNV").arg(anIObject->getName()), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::Yes); - if (aRet != SUIT_MessageBox::Yes) - return; - } - aFilter = QObject::tr("IDEAS files (*.unv)"); + // get mesh object from selection and check duplication of their names + bool hasDuplicatedMeshNames = false; + QList< QPair< SMESH::SMESH_IDSource_var, QString > > aMeshList; + QList< QPair< SMESH::SMESH_IDSource_var, QString > >::iterator aMeshIter; + SALOME_ListIteratorOfListIO It( selected ); + for( ; It.More(); It.Next() ) + { + Handle(SALOME_InteractiveObject) anIObject = It.Value(); + SMESH::SMESH_IDSource_var aMeshItem = SMESH::IObjectToInterface(anIObject); + if ( aMeshItem->_is_nil() ) { + SUIT_MessageBox::warning( SMESHGUI::desktop(), + QObject::tr( "SMESH_WRN_WARNING" ), + QObject::tr( "SMESH_BAD_MESH_SELECTION" )); + return; } - break; - case 140: - case 141: - { - // export STL - /* - there must be check on others mesh elements not equal triangles - */ - if (aMesh->NbTriangles() < 1) { - SUIT_MessageBox::warning - (SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_STL1").arg(anIObject->getName())); - return; + + QString aMeshName = anIObject->getName(); + + // check for name duplications + if ( !hasDuplicatedMeshNames ) + for( aMeshIter = aMeshList.begin(); aMeshIter != aMeshList.end(); aMeshIter++ ) { + if( aMeshName == (*aMeshIter).second ) { + hasDuplicatedMeshNames = true; + break; + } } - if (!(aMesh->NbElements() - aMesh->NbTriangles())) { + + aMeshList.append( QPair< SMESH::SMESH_IDSource_var, QString >( aMeshItem, aMeshName ) ); + } + + if( hasDuplicatedMeshNames && isMED ) { + int aRet = SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_EXPORT_MED_DUPLICATED_MESH_NAMES"), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), 0, 1); + if (aRet != 0) + return; + } + + aMeshIter = aMeshList.begin(); + SMESH::SMESH_IDSource_var aMeshOrGroup = (*aMeshIter).first; + SMESH::SMESH_Mesh_var aMesh = aMeshOrGroup->GetMesh(); + QString aMeshName = (*aMeshIter).second; + + if ( isMED || isCGNS || isSAUV ) + { + // check for equal group names within each mesh + for( aMeshIter = aMeshList.begin(); aMeshIter != aMeshList.end(); aMeshIter++ ) { + SMESH::SMESH_Mesh_var aMeshItem = SMESH::SMESH_Mesh::_narrow( (*aMeshIter).first ); + if ( !aMeshItem->_is_nil() && aMeshItem->HasDuplicatedGroupNamesMED()) { int aRet = SUIT_MessageBox::warning (SMESHGUI::desktop(), QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_STL2").arg(anIObject->getName()), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::Yes); - if (aRet != SUIT_MessageBox::Yes) + QObject::tr("SMESH_EXPORT_MED_DUPLICATED_GRP").arg((*aMeshIter).second), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), 0, 1); + if (aRet != 0) return; } - - aFilterMapSTL.insert( QObject::tr("STL ASCII (*.stl)"), 1 ); // 1 - ASCII mode - aFilterMapSTL.insert( QObject::tr("STL Binary (*.stl)"), 0 ); // 0 - Binary mode } - break; - default: - return; + } + + // Warn the user about presence of not supported elements + QString format; + std::vector< SMESH::EntityType > notSupportedElemTypes, presentNotSupported; + if ( isDAT ) + { + format = "DAT"; + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Quadrangle ); + notSupportedElemTypes.push_back( SMESH::Entity_BiQuad_Quadrangle ); + notSupportedElemTypes.push_back( SMESH::Entity_Polygon ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Polygon ); + notSupportedElemTypes.push_back( SMESH::Entity_Tetra ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Tetra ); + notSupportedElemTypes.push_back( SMESH::Entity_Pyramid ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Pyramid ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Hexa ); + notSupportedElemTypes.push_back( SMESH::Entity_TriQuad_Hexa ); + notSupportedElemTypes.push_back( SMESH::Entity_Penta ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Penta ); + notSupportedElemTypes.push_back( SMESH::Entity_Hexagonal_Prism ); + notSupportedElemTypes.push_back( SMESH::Entity_Polyhedra ); + notSupportedElemTypes.push_back( SMESH::Entity_0D ); + notSupportedElemTypes.push_back( SMESH::Entity_Ball ); + } + else if ( isUNV ) + { + format = "UNV"; + notSupportedElemTypes.push_back( SMESH::Entity_Polygon ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Polygon ); + notSupportedElemTypes.push_back( SMESH::Entity_Polyhedra ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Polyhedra ); + notSupportedElemTypes.push_back( SMESH::Entity_Pyramid ); + notSupportedElemTypes.push_back( SMESH::Entity_Hexagonal_Prism ); + notSupportedElemTypes.push_back( SMESH::Entity_0D ); + notSupportedElemTypes.push_back( SMESH::Entity_Ball ); + } + else if ( isSTL ) + { + format = "STL"; + notSupportedElemTypes.push_back( SMESH::Entity_Edge ); + notSupportedElemTypes.push_back( SMESH::Entity_Quad_Edge ); + notSupportedElemTypes.push_back( SMESH::Entity_0D ); + notSupportedElemTypes.push_back( SMESH::Entity_Ball ); + } + else if ( isCGNS ) + { + format = "CGNS"; + notSupportedElemTypes.push_back( SMESH::Entity_Ball ); + } + else if ( isSAUV ) + { + format = "SAUV"; + notSupportedElemTypes.push_back( SMESH::Entity_Ball ); + notSupportedElemTypes.push_back( SMESH::Entity_BiQuad_Quadrangle ); + notSupportedElemTypes.push_back( SMESH::Entity_TriQuad_Hexa ); + notSupportedElemTypes.push_back( SMESH::Entity_Hexagonal_Prism ); + notSupportedElemTypes.push_back( SMESH::Entity_Polygon ); + notSupportedElemTypes.push_back( SMESH::Entity_Polyhedra ); + } + if ( ! notSupportedElemTypes.empty() ) + { + SMESH::long_array_var nbElems = aMeshOrGroup->GetMeshInfo(); + for ( size_t iType = 0; iType < notSupportedElemTypes.size(); ++iType ) + if ( nbElems[ notSupportedElemTypes[ iType ]] > 0 ) + presentNotSupported.push_back( notSupportedElemTypes[ iType ]); + } + if ( !presentNotSupported.empty() ) + { + QString typeNames; + const char* typeMsg[SMESH::Entity_Last] = { "SMESH_NODES", + "SMESH_ELEMS0D","SMESH_EDGES","SMESH_QUADRATIC_EDGES","SMESH_TRIANGLES", + "SMESH_QUADRATIC_TRIANGLES","SMESH_QUADRANGLES","SMESH_QUADRATIC_QUADRANGLES", + "SMESH_BIQUADRATIC_QUADRANGLES","SMESH_POLYGONS","SMESH_QUADRATIC_POLYGONS", + "SMESH_TETRAHEDRA","SMESH_QUADRATIC_TETRAHEDRONS","SMESH_PYRAMIDS", + "SMESH_QUADRATIC_PYRAMIDS","SMESH_HEXAHEDRA","SMESH_QUADRATIC_HEXAHEDRONS", + "SMESH_TRIQUADRATIC_HEXAHEDRONS","SMESH_PENTAHEDRA","SMESH_QUADRATIC_PENTAHEDRONS", + "SMESH_OCTAHEDRA","SMESH_POLYEDRONS","SMESH_QUADRATIC_POLYEDRONS","SMESH_BALLS" + }; + QString andStr = " " + QObject::tr("SMESH_AND") + " ", comma(", "); + for ( size_t iType = 0; iType < presentNotSupported.size(); ++iType ) { + typeNames += QObject::tr( typeMsg[ presentNotSupported[ iType ]]); + if ( iType != presentNotSupported.size() - 1 ) + typeNames += ( iType == presentNotSupported.size() - 2 ) ? andStr : comma; + } + int aRet = SUIT_MessageBox::warning + (SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("EXPORT_NOT_SUPPORTED").arg(aMeshName).arg(format).arg(typeNames), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), 0, 1); + if (aRet != 0) + return; } + // Get parameters of export operation + QString aFilename; SMESH::MED_VERSION aFormat; - // Init the parameter with the default value + // Init the parameters with the default values bool aIsASCII_STL = true; bool toCreateGroups = false; SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); if ( resMgr ) toCreateGroups = resMgr->booleanValue( "SMESH", "auto_groups", false ); + bool toOverwrite = true; + QString aFilter, aTitle = QObject::tr("SMESH_EXPORT_MESH"); QString anInitialPath = ""; if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) anInitialPath = QDir::currentPath(); - if ( theCommandID != 122 && theCommandID != 125 && theCommandID != 140 && theCommandID != 141) { + if ( isUNV || isDAT ) + { + if ( isUNV ) + aFilter = QObject::tr( "IDEAS_FILES_FILTER" ) + " (*.unv)"; + else + aFilter = QObject::tr( "DAT_FILES_FILTER" ) + " (*.dat)"; if ( anInitialPath.isEmpty() ) anInitialPath = SUIT_FileDlg::getLastVisitedPath(); - aFilename = SUIT_FileDlg::getFileName(SMESHGUI::desktop(), anInitialPath + QString("/") + anIObject->getName(), + aFilename = SUIT_FileDlg::getFileName(SMESHGUI::desktop(), + anInitialPath + QString("/") + aMeshName, aFilter, aTitle, false); } - else if(theCommandID == 140 || theCommandID == 141) { // Export to STL + else if ( isCGNS )// Export to CGNS + { + SUIT_FileDlg* fd = new SUIT_FileDlg( SMESHGUI::desktop(), false, true, true ); + fd->setWindowTitle( aTitle ); + fd->setNameFilter( QObject::tr( "CGNS_FILES_FILTER" ) + " (*.cgns)" ); + if ( !anInitialPath.isEmpty() ) + fd->setDirectory( anInitialPath ); + fd->selectFile(aMeshName); + SMESHGUI_FileValidator* fv = new SMESHGUI_FileValidator( fd ); + fd->setValidator( fv ); + + if ( fd->exec() ) + aFilename = fd->selectedFile(); + toOverwrite = fv->isOverwrite(); + + delete fd; + } + else if ( isSTL ) // Export to STL + { + QMap aFilterMap; + aFilterMap.insert( QObject::tr( "STL_ASCII_FILES_FILTER" ) + " (*.stl)", 1 ); + aFilterMap.insert( QObject::tr( "STL_BIN_FILES_FILTER" ) + " (*.stl)", 0 ); + QStringList filters; - QMap::const_iterator it = aFilterMapSTL.begin(); - for ( ; it != aFilterMapSTL.end(); ++it ) + QMap::const_iterator it = aFilterMap.begin(); + for ( ; it != aFilterMap.end(); ++it ) filters.push_back( it.key() ); SUIT_FileDlg* fd = new SUIT_FileDlg( SMESHGUI::desktop(), false, true, true ); fd->setWindowTitle( aTitle ); - fd->setFilters( filters ); - fd->selectFilter( QObject::tr("STL ASCII (*.stl)") ); + fd->setNameFilters( filters ); + fd->selectNameFilter( QObject::tr( "STL_ASCII_FILES_FILTER" ) + " (*.stl)" ); if ( !anInitialPath.isEmpty() ) fd->setDirectory( anInitialPath ); - fd->selectFile(anIObject->getName()); + fd->selectFile(aMeshName); bool is_ok = false; while (!is_ok) { if ( fd->exec() ) aFilename = fd->selectedFile(); - aIsASCII_STL = (aFilterMapSTL[fd->selectedFilter()]) == 1 ? true: false; + aIsASCII_STL = (aFilterMap[fd->selectedNameFilter()]) == 1 ? true: false; is_ok = true; } delete fd; } - else { // Export to MED + else if ( isMED || isSAUV ) // Export to MED or SAUV + { + QMap aFilterMap; + //QString v21 (aMesh->GetVersionString(SMESH::MED_V2_1, 2)); + if ( isMED ) { + QString v22 (aMesh->GetVersionString(SMESH::MED_V2_2, 2)); + //aFilterMap.insert( QObject::tr( "MED_VX_FILES_FILTER" ).arg( v21 ) + " (*.med)", SMESH::MED_V2_1 ); + aFilterMap.insert( QObject::tr( "MED_VX_FILES_FILTER" ).arg( v22 ) + " (*.med)", SMESH::MED_V2_2 ); + } + else { // isSAUV + aFilterMap.insert("All files (*)", SMESH::MED_V2_1 ); + aFilterMap.insert("SAUV files (*.sauv)", SMESH::MED_V2_2 ); + aFilterMap.insert("SAUV files (*.sauve)", SMESH::MED_V2_1 ); + } + QStringList filters; QString aDefaultFilter; QMap::const_iterator it = aFilterMap.begin(); @@ -402,76 +626,180 @@ aDefaultFilter = it.key(); } - //SUIT_FileDlg* fd = new SUIT_FileDlg( SMESHGUI::desktop(), false, true, true ); SalomeApp_CheckFileDlg* fd = new SalomeApp_CheckFileDlg ( SMESHGUI::desktop(), false, QObject::tr("SMESH_AUTO_GROUPS"), true, true ); fd->setWindowTitle( aTitle ); - fd->setFilters( filters ); - //fd->setSelectedFilter( QObject::tr("MED 2.2 (*.med)") ); - fd->selectFilter(aDefaultFilter); + fd->setNameFilters( filters ); + fd->selectNameFilter(aDefaultFilter); fd->SetChecked(toCreateGroups); if ( !anInitialPath.isEmpty() ) fd->setDirectory( anInitialPath ); - fd->selectFile(anIObject->getName()); + fd->selectFile(aMeshName); + + SMESHGUI_FileValidator* fv = new SMESHGUI_FileValidator( fd ); + fd->setValidator( fv ); + bool is_ok = false; while (!is_ok) { if ( fd->exec() ) aFilename = fd->selectedFile(); - aFormat = aFilterMap[fd->selectedFilter()]; + else { + aFilename = QString::null; + break; + } + aFormat = aFilterMap[fd->selectedNameFilter()]; + toOverwrite = fv->isOverwrite(); is_ok = true; - if ( !aFilename.isEmpty() - && (aMesh->NbPolygons()>0 || aMesh->NbPolyhedrons()>0) - && aFormat==SMESH::MED_V2_1) { - int aRet = SUIT_MessageBox::warning(SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_MED_V2_1").arg(anIObject->getName()), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, SUIT_MessageBox::Yes); - if (aRet != SUIT_MessageBox::Yes) { - is_ok = false; + if ( !aFilename.isEmpty() ) { + // med-2.1 does not support poly elements + if ( aFormat==SMESH::MED_V2_1 ) + for( aMeshIter = aMeshList.begin(); aMeshIter != aMeshList.end(); aMeshIter++ ) { + SMESH::SMESH_IDSource_var aMeshItem = (*aMeshIter).first; + SMESH::long_array_var nbElems = aMeshItem->GetMeshInfo(); + if ( nbElems[ SMESH::Entity_Polygon ] + nbElems[ SMESH::Entity_Quad_Polygon ] + + nbElems[ SMESH::Entity_Polyhedra ] + nbElems[ SMESH::Entity_Quad_Polyhedra ]) + { + int aRet = SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_EXPORT_MED_V2_1").arg((*aMeshIter).second), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), 0, 1); + if (aRet != 0) { + is_ok = false; + break; + } + } + } + if( !toOverwrite ) { + // can't append to an existing using other format + SMESH::MED_VERSION aVersion = SMESH::MED_V2_1; + bool isVersionOk = SMESHGUI::GetSMESHGen()->GetMEDVersion( aFilename.toLatin1().constData(), aVersion ); + if( !isVersionOk || aVersion != aFormat ) { + int aRet = SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_EXPORT_MED_VERSION_COLLISION").arg(aFilename), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), 0, 1); + if (aRet == 0) + toOverwrite = true; + else + is_ok = false; + } + + QStringList aMeshNamesCollisionList; + SMESH::string_array_var aMeshNames = SMESHGUI::GetSMESHGen()->GetMeshNames( aFilename.toLatin1().constData() ); + for( int i = 0, n = aMeshNames->length(); i < n; i++ ) { + QString anExistingMeshName( aMeshNames[ i ] ); + for( aMeshIter = aMeshList.begin(); aMeshIter != aMeshList.end(); aMeshIter++ ) { + QString anExportMeshName = (*aMeshIter).second; + if( anExportMeshName == anExistingMeshName ) { + aMeshNamesCollisionList.append( anExportMeshName ); + break; + } + } + } + if( !aMeshNamesCollisionList.isEmpty() ) { + QString aMeshNamesCollisionString = aMeshNamesCollisionList.join( ", " ); + int aRet = SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_EXPORT_MED_MESH_NAMES_COLLISION").arg(aMeshNamesCollisionString), + QObject::tr("SMESH_BUT_YES"), + QObject::tr("SMESH_BUT_NO"), + QObject::tr("SMESH_BUT_CANCEL"), 0, 2); + if (aRet == 0) + toOverwrite = true; + else if (aRet == 2) + is_ok = false; + } } } } toCreateGroups = fd->IsChecked(); delete fd; } + else + { + return; + } + + // Perform export + if ( !aFilename.isEmpty() ) { // Check whether the file already exists and delete it if yes QFile aFile( aFilename ); - if ( aFile.exists() ) + if ( aFile.exists() && toOverwrite ) aFile.remove(); SUIT_OverrideCursor wc; try { - bool Renumber = false; - // PAL 14172 : Check of we have to renumber or not from the preferences before export - if (resMgr) - Renumber= resMgr->booleanValue("SMESH","renumbering"); - if (Renumber){ - SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); - aMeshEditor->RenumberNodes(); - aMeshEditor->RenumberElements(); - if ( SMESHGUI::automaticUpdate() ) - SMESH::UpdateView(); + // Renumbering is not needed since SMDS redesign in V6.2.0 (Nov 2010) +// bool Renumber = false; +// // PAL 14172 : Check of we have to renumber or not from the preferences before export +// if (resMgr) +// Renumber= resMgr->booleanValue("renumbering"); +// if (Renumber){ +// SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); +// aMeshEditor->RenumberNodes(); +// aMeshEditor->RenumberElements(); +// if ( SMESHGUI::automaticUpdate() ) +// SMESH::UpdateView(); +// } + if ( isMED ) + { + aMeshIter = aMeshList.begin(); + for( int aMeshIndex = 0; aMeshIter != aMeshList.end(); aMeshIter++, aMeshIndex++ ) + { + SMESH::SMESH_IDSource_var aMeshOrGroup = (*aMeshIter).first; + SMESH::SMESH_Mesh_var aMeshItem = aMeshOrGroup->GetMesh(); + if ( aMeshOrGroup->_is_equivalent( aMeshItem )) + aMeshItem->ExportToMEDX( aFilename.toLatin1().data(), toCreateGroups, + aFormat, toOverwrite && aMeshIndex == 0 ); + else + aMeshItem->ExportPartToMED( aMeshOrGroup, aFilename.toLatin1().data(), toCreateGroups, + aFormat, toOverwrite && aMeshIndex == 0 ); + } } - switch ( theCommandID ) { - case 125: - case 122: - aMesh->ExportToMED( aFilename.toLatin1().data(), toCreateGroups, aFormat ); - break; - case 124: - case 121: - aMesh->ExportDAT( aFilename.toLatin1().data() ); - break; - case 126: - case 123: - aMesh->ExportUNV( aFilename.toLatin1().data() ); - break; - case 140: - case 141: - aMesh->ExportSTL( aFilename.toLatin1().data(), aIsASCII_STL ); - break; - default: - break; + else if ( isSAUV ) + { + for( aMeshIter = aMeshList.begin(); aMeshIter != aMeshList.end(); aMeshIter++ ) + { + SMESH::SMESH_Mesh_var aMeshItem = SMESH::SMESH_Mesh::_narrow( (*aMeshIter).first ); + if( !aMeshItem->_is_nil() ) + aMeshItem->ExportSAUV( aFilename.toLatin1().data(), toCreateGroups ); + } + } + else if ( isDAT ) + { + if ( aMeshOrGroup->_is_equivalent( aMesh )) + aMesh->ExportDAT( aFilename.toLatin1().data() ); + else + aMesh->ExportPartToDAT( aMeshOrGroup, aFilename.toLatin1().data() ); + } + else if ( isUNV ) + { + if ( aMeshOrGroup->_is_equivalent( aMesh )) + aMesh->ExportUNV( aFilename.toLatin1().data() ); + else + aMesh->ExportPartToUNV( aMeshOrGroup, aFilename.toLatin1().data() ); + } + else if ( isSTL ) + { + if ( aMeshOrGroup->_is_equivalent( aMesh )) + aMesh->ExportSTL( aFilename.toLatin1().data(), aIsASCII_STL ); + else + aMesh->ExportPartToSTL( aMeshOrGroup, aFilename.toLatin1().data(), aIsASCII_STL ); + } + else if ( isCGNS ) + { + aMeshIter = aMeshList.begin(); + for( int aMeshIndex = 0; aMeshIter != aMeshList.end(); aMeshIter++, aMeshIndex++ ) + { + SMESH::SMESH_IDSource_var aMeshOrGroup = (*aMeshIter).first; + SMESH::SMESH_Mesh_var aMeshItem = aMeshOrGroup->GetMesh(); + aMeshItem->ExportCGNS( aMeshOrGroup, + aFilename.toLatin1().data(), + toOverwrite && aMeshIndex == 0 ); + } } } catch (const SALOME::SALOME_Exception& S_ex){ @@ -485,7 +813,7 @@ } inline void InverseEntityMode(unsigned int& theOutputMode, - unsigned int theMode) + unsigned int theMode) { bool anIsNotPresent = ~theOutputMode & theMode; if(anIsNotPresent) @@ -503,28 +831,34 @@ if(selected.Extent() >= 1){ SALOME_ListIteratorOfListIO It( selected ); for( ; It.More(); It.Next()){ - Handle(SALOME_InteractiveObject) IObject = It.Value(); - if(IObject->hasEntry()){ - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ - unsigned int aMode = anActor->GetEntityMode(); - switch(theCommandID){ - case 217: - InverseEntityMode(aMode,SMESH_Actor::eEdges); - break; - case 218: - InverseEntityMode(aMode,SMESH_Actor::eFaces); - break; - case 219: - InverseEntityMode(aMode,SMESH_Actor::eVolumes); - break; - case 220: - aMode = SMESH_Actor::eAllEntity; - break; - } - if(aMode) - anActor->SetEntityMode(aMode); - } - } + Handle(SALOME_InteractiveObject) IObject = It.Value(); + if(IObject->hasEntry()){ + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ + unsigned int aMode = anActor->GetEntityMode(); + switch(theCommandID){ + case 222: + InverseEntityMode(aMode,SMESH_Actor::eBallElem); + break; + case 216: + InverseEntityMode(aMode,SMESH_Actor::e0DElements); + break; + case 217: + InverseEntityMode(aMode,SMESH_Actor::eEdges); + break; + case 218: + InverseEntityMode(aMode,SMESH_Actor::eFaces); + break; + case 219: + InverseEntityMode(aMode,SMESH_Actor::eVolumes); + break; + case 220: + aMode = SMESH_Actor::eAllEntity; + break; + } + if(aMode) + anActor->SetEntityMode(aMode); + } + } } } } @@ -552,35 +886,231 @@ if( aMainObject->_is_nil() ) return; - aMainObject->SetAutoColor( true ); - - QList aReservedColors; + aMainObject->SetAutoColor( true ); // mesh groups are re-colored here SMESH::ListOfGroups aListOfGroups = *aMainObject->GetGroups(); for( int i = 0, n = aListOfGroups.length(); i < n; i++ ) { SMESH::SMESH_GroupBase_var aGroupObject = aListOfGroups[i]; - SALOMEDS::Color aCurrentColor = aGroupObject->GetColor(); - - SALOMEDS::Color aColor = SMESHGUI::getUniqueColor( aReservedColors ); - aGroupObject->SetColor( aColor ); - aReservedColors.append( aColor ); - + SALOMEDS::Color aColor = aGroupObject->GetColor(); _PTR(SObject) aGroupSObject = SMESH::FindSObject(aGroupObject); - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(aGroupSObject->GetID().c_str())) - { - if( aGroupObject->GetType() == SMESH::NODE ) - anActor->SetNodeColor( aColor.R, aColor.G, aColor.B ); - else if( aGroupObject->GetType() == SMESH::EDGE ) - anActor->SetEdgeColor( aColor.R, aColor.G, aColor.B ); - else - anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B ); + if (aGroupSObject) { + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(aGroupSObject->GetID().c_str())) { + switch ( aGroupObject->GetType ()) { + case SMESH::NODE: + anActor->SetNodeColor( aColor.R, aColor.G, aColor.B ); break; + case SMESH::EDGE: + anActor->SetEdgeColor( aColor.R, aColor.G, aColor.B ); break; + case SMESH::ELEM0D: + anActor->Set0DColor( aColor.R, aColor.G, aColor.B ); break; + case SMESH::BALL: + anActor->SetBallColor( aColor.R, aColor.G, aColor.B ); break; + default: + QColor c; + int delta; + SMESH::GetColor("SMESH", "fill_color", c, delta, "0,170,255|-100"); + anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B, delta ); + } + } } } SMESH::RepaintCurrentView(); } + QString functorToString( SMESH::Controls::FunctorPtr f ) + { + QString type = QObject::tr( "UNKNOWN_CONTROL" ); + if ( dynamic_cast< SMESH::Controls::Volume* >( f.get() ) ) + type = QObject::tr( "VOLUME_3D_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::MaxElementLength2D* >( f.get() ) ) + type = QObject::tr( "MAX_ELEMENT_LENGTH_2D" ); + else if ( dynamic_cast< SMESH::Controls::MaxElementLength3D* >( f.get() ) ) + type = QObject::tr( "MAX_ELEMENT_LENGTH_3D" ); + else if ( dynamic_cast< SMESH::Controls::MinimumAngle* >( f.get() ) ) + type = QObject::tr( "MINIMUMANGLE_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::AspectRatio* >( f.get() ) ) + type = QObject::tr( "ASPECTRATIO_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::AspectRatio3D* >( f.get() ) ) + type = QObject::tr( "ASPECTRATIO_3D_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::Warping* >( f.get() ) ) + type = QObject::tr( "WARP_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::Taper* >( f.get() ) ) + type = QObject::tr( "TAPER_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::Skew* >( f.get() ) ) + type = QObject::tr( "SKEW_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::Area* >( f.get() ) ) + type = QObject::tr( "AREA_ELEMENTS" ); + else if ( dynamic_cast< SMESH::Controls::Length* >( f.get() ) ) + type = QObject::tr( "LENGTH_EDGES" ); + else if ( dynamic_cast< SMESH::Controls::Length2D* >( f.get() ) ) + type = QObject::tr( "LENGTH2D_EDGES" ); + else if ( dynamic_cast< SMESH::Controls::MultiConnection* >( f.get() ) ) + type = QObject::tr( "MULTI_BORDERS" ); + else if ( dynamic_cast< SMESH::Controls::MultiConnection2D* >( f.get() ) ) + type = QObject::tr( "MULTI2D_BORDERS" ); + else if ( dynamic_cast< SMESH::Controls::FreeNodes* >( f.get() ) ) + type = QObject::tr( "FREE_NODES" ); + else if ( dynamic_cast< SMESH::Controls::FreeEdges* >( f.get() ) ) + type = QObject::tr( "FREE_EDGES" ); + else if ( dynamic_cast< SMESH::Controls::FreeBorders* >( f.get() ) ) + type = QObject::tr( "FREE_BORDERS" ); + else if ( dynamic_cast< SMESH::Controls::FreeFaces* >( f.get() ) ) + type = QObject::tr( "FREE_FACES" ); + else if ( dynamic_cast< SMESH::Controls::BareBorderVolume* >( f.get() ) ) + type = QObject::tr( "BARE_BORDER_VOLUME" ); + else if ( dynamic_cast< SMESH::Controls::BareBorderFace* >( f.get() ) ) + type = QObject::tr( "BARE_BORDER_FACE" ); + else if ( dynamic_cast< SMESH::Controls::OverConstrainedVolume* >( f.get() ) ) + type = QObject::tr( "OVER_CONSTRAINED_VOLUME" ); + else if ( dynamic_cast< SMESH::Controls::OverConstrainedFace* >( f.get() ) ) + type = QObject::tr( "OVER_CONSTRAINED_FACE" ); + else if ( dynamic_cast< SMESH::Controls::CoincidentNodes* >( f.get() ) ) + type = QObject::tr( "EQUAL_NODE" ); + else if ( dynamic_cast< SMESH::Controls::CoincidentElements1D* >( f.get() ) ) + type = QObject::tr( "EQUAL_EDGE" ); + else if ( dynamic_cast< SMESH::Controls::CoincidentElements2D* >( f.get() ) ) + type = QObject::tr( "EQUAL_FACE" ); + else if ( dynamic_cast< SMESH::Controls::CoincidentElements3D* >( f.get() ) ) + type = QObject::tr( "EQUAL_VOLUME" ); + return type; + } + + void SaveDistribution() + { + LightApp_SelectionMgr* aSel = SMESHGUI::selectionMgr(); + SALOME_ListIO selected; + if ( aSel ) + aSel->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) anIO = selected.First(); + if ( anIO->hasEntry() ) { + SMESH_Actor* anActor = SMESH::FindActorByEntry( anIO->getEntry() ); + if ( anActor && anActor->GetScalarBarActor() && anActor->GetControlMode() != SMESH_Actor::eNone ) { + SMESH_ScalarBarActor* aScalarBarActor = anActor->GetScalarBarActor(); + SMESH::Controls::FunctorPtr aFunctor = anActor->GetFunctor(); + if ( aScalarBarActor && aFunctor ) { + SMESH::Controls::NumericalFunctor* aNumFun = dynamic_cast( aFunctor.get() ); + if ( aNumFun ) { + std::vector elements; + SMESH::SMESH_Mesh_var mesh = SMESH::IObjectToInterface(anIO); + if ( mesh->_is_nil() ) { + SMESH::SMESH_IDSource_var idSource = + SMESH::IObjectToInterface(anIO); + if ( !idSource->_is_nil() ) + { + SMESH::long_array_var ids = idSource->GetIDs(); + elements.resize( ids->length() ); + for ( unsigned i = 0; i < elements.size(); ++i ) + elements[i] = ids[i]; + } + } + int nbIntervals = aScalarBarActor->GetMaximumNumberOfColors(); + vtkLookupTable* lookupTable = + static_cast(aScalarBarActor->GetLookupTable()); + double * minmax = lookupTable->GetRange(); + std::vector nbEvents; + std::vector funValues; + aNumFun->GetHistogram( nbIntervals, nbEvents, funValues, elements, minmax ); + QString anInitialPath = ""; + if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() ) + anInitialPath = QDir::currentPath(); + QString aMeshName = anIO->getName(); + QStringList filter; + filter.append( QObject::tr( "TEXT_FILES_FILTER" ) + " (*.txt)" ); + filter.append( QObject::tr( "ALL_FILES_FILTER" ) + " (*)" ); + QString aFilename = anInitialPath + "/" + aMeshName + "_" + + functorToString( aFunctor ).toLower().simplified().replace( QRegExp( " |-" ), "_" ) + ".txt"; + aFilename = SUIT_FileDlg::getFileName( SMESHGUI::desktop(), + aFilename, + filter, + QObject::tr( "SMESH_SAVE_DISTRIBUTION" ), + false ); + if ( !aFilename.isEmpty() ) { + QFile f( aFilename ); + if ( f.open( QFile::WriteOnly | QFile::Truncate ) ) { + QTextStream out( &f ); + out << "# Mesh: " << aMeshName << endl; + out << "# Control: " << functorToString( aFunctor ) << endl; + out << "#" << endl; + out.setFieldWidth( 10 ); + for ( int i = 0; i < qMin( nbEvents.size(), funValues.size()-1 ); i++ ) + out << funValues[i] << "\t" << funValues[i+1] << "\t" << nbEvents[i] << endl; + f.close(); + } + } + } + } + } + } + } + } + + void ShowDistribution() { + LightApp_SelectionMgr* aSel = SMESHGUI::selectionMgr(); + SALOME_ListIO selected; + if ( aSel ) + aSel->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) anIO = selected.First(); + if ( anIO->hasEntry() ) { + SMESH_Actor* anActor = SMESH::FindActorByEntry( anIO->getEntry() ); + if ( anActor && anActor->GetScalarBarActor() && anActor->GetControlMode() != SMESH_Actor::eNone ) { + SMESH_ScalarBarActor *aScalarBarActor = anActor->GetScalarBarActor(); + aScalarBarActor->SetDistributionVisibility(!aScalarBarActor->GetDistributionVisibility()); + } + } + } + } + +#ifndef DISABLE_PLOT2DVIEWER + void PlotDistribution() { + SalomeApp_Application* app = dynamic_cast< SalomeApp_Application* >( SUIT_Session::session()->activeApplication() ); + if( !app ) + return; + + LightApp_SelectionMgr* aSel = SMESHGUI::selectionMgr(); + SALOME_ListIO selected; + if ( aSel ) + aSel->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) anIO = selected.First(); + if ( anIO->hasEntry() ) { + //Find Actor by entry before getting Plot2d viewer, + //because after call getViewManager( Plot2d_Viewer::Type(), true ) active window is Plot2d Viewer + SMESH_Actor* anActor = SMESH::FindActorByEntry( anIO->getEntry() ); + + SUIT_ViewManager* aViewManager = app->getViewManager( Plot2d_Viewer::Type(), true ); // create if necessary + + if( !aViewManager ) + return; + + SPlot2d_Viewer* aView = dynamic_cast(aViewManager->getViewModel()); + if ( !aView ) + return; + + Plot2d_ViewFrame* aPlot = aView->getActiveViewFrame(); + if ( !aPlot ) + return; + + if ( anActor && anActor->GetControlMode() != SMESH_Actor::eNone ) { + SPlot2d_Histogram* aHistogram = anActor->UpdatePlot2Histogram(); + QString functorName = functorToString( anActor->GetFunctor()); + QString aHistogramName("%1 : %2"); + aHistogramName = aHistogramName.arg(anIO->getName()).arg(functorName); + aHistogram->setName(aHistogramName); + aHistogram->setHorTitle(functorName); + aHistogram->setVerTitle(QObject::tr("DISTRIBUTION_NB_ENT")); + aPlot->displayObject(aHistogram, true); + } + } + } + } +#endif //DISABLE_PLOT2DVIEWER + void DisableAutoColor(){ LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); SALOME_ListIO selected; @@ -591,12 +1121,13 @@ Handle(SALOME_InteractiveObject) anIObject = selected.First(); SMESH::SMESH_Mesh_var aMesh = SMESH::IObjectToInterface(anIObject); if ( !aMesh->_is_nil() ) { - aMesh->SetAutoColor( false ); + aMesh->SetAutoColor( false ); } } } - void SetDisplayMode(int theCommandID){ + void SetDisplayMode(int theCommandID, SMESHGUI_StudyId2MarkerMap& theMarkerMap) + { SALOME_ListIO selected; SalomeApp_Application* app = dynamic_cast< SalomeApp_Application* >( SUIT_Session::session()->activeApplication() ); if( !app ) @@ -607,131 +1138,302 @@ if( !aSel || !appStudy ) return; + if( theCommandID == 1134 ) { // Clipping dialog can be activated without selection + if( SMESHGUI* aModule = SMESHGUI::GetSMESHGUI() ) { + aModule->EmitSignalDeactivateDialog(); + if( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( aModule ) ) + (new SMESHGUI_ClippingDlg( aModule, aViewWindow ))->show(); + } + return; + } + _PTR(Study) aStudy = appStudy->studyDS(); aSel->selectedObjects( selected ); if(selected.Extent() >= 1){ switch(theCommandID){ - case 1134:{ - SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog(); - (new SMESHGUI_ClippingDlg( SMESHGUI::GetSMESHGUI() ))->show(); - return; - } case 1133:{ - SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog(); - (new SMESHGUI_TransparencyDlg( SMESHGUI::GetSMESHGUI() ))->show(); - return; - }} + SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog(); + (new SMESHGUI_TransparencyDlg( SMESHGUI::GetSMESHGUI() ))->show(); + return; + } + case 1132:{ + QColor c, e, b, n, c0D, cBall, o, outl, selection, preselection; + int delta; + int size0D = 0, ballSize = 0; + int Edgewidth = 0; + vtkFloatingPointType Shrink = 0.0; + vtkFloatingPointType faces_orientation_scale = 0.0; + bool faces_orientation_3dvectors = false; + + VTK::MarkerType aMarkerTypeCurrent = VTK::MT_NONE; + VTK::MarkerScale aMarkerScaleCurrent = VTK::MS_NONE; + int aMarkerTextureCurrent = 0; + + SALOME_ListIteratorOfListIO It( selected ); + for( ; It.More(); It.Next()){ + Handle(SALOME_InteractiveObject) IObject = It.Value(); + if(IObject->hasEntry()){ + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ + vtkFloatingPointType color[3]; + anActor->GetSufaceColor(color[0], color[1], color[2],delta); + int c0 = int (color[0] * 255); + int c1 = int (color[1] * 255); + int c2 = int (color[2] * 255); + c.setRgb(c0, c1, c2); + + vtkFloatingPointType edgecolor[3]; + anActor->GetEdgeColor(edgecolor[0], edgecolor[1], edgecolor[2]); + c0 = int (edgecolor[0] * 255); + c1 = int (edgecolor[1] * 255); + c2 = int (edgecolor[2] * 255); + e.setRgb(c0, c1, c2); + + vtkFloatingPointType nodecolor[3]; + anActor->GetNodeColor(nodecolor[0], nodecolor[1], nodecolor[2]); + c0 = int (nodecolor[0] * 255); + c1 = int (nodecolor[1] * 255); + c2 = int (nodecolor[2] * 255); + n.setRgb(c0, c1, c2); + + vtkFloatingPointType color0D[3]; + anActor->Get0DColor(color0D[0], color0D[1], color0D[2]); + c0 = int (color0D[0] * 255); + c1 = int (color0D[1] * 255); + c2 = int (color0D[2] * 255); + c0D.setRgb(c0, c1, c2); + + vtkFloatingPointType ballcolor[3]; + anActor->GetBallColor(ballcolor[0], ballcolor[1], ballcolor[2]); + c0 = int (ballcolor[0] * 255); + c1 = int (ballcolor[1] * 255); + c2 = int (ballcolor[2] * 255); + cBall.setRgb(c0, c1, c2); + + vtkFloatingPointType outlineColor[3]; + anActor->GetOutlineColor(outlineColor[0], outlineColor[1], outlineColor[2]); + c0 = int (outlineColor[0] * 255); + c1 = int (outlineColor[1] * 255); + c2 = int (outlineColor[2] * 255); + outl.setRgb(c0, c1, c2); + + vtkFloatingPointType hColor[3]; + anActor->GetHighlightColor(hColor[0], hColor[1], hColor[2]); + c0 = int (hColor[0] * 255); + c1 = int (hColor[1] * 255); + c2 = int (hColor[2] * 255); + selection.setRgb(c0, c1, c2); + + vtkFloatingPointType phColor[3]; + anActor->GetPreHighlightColor(phColor[0], phColor[1], phColor[2]); + c0 = int (phColor[0] * 255); + c1 = int (phColor[1] * 255); + c2 = int (phColor[2] * 255); + preselection.setRgb(c0, c1, c2); + + size0D = (int)anActor->Get0DSize(); + if(size0D == 0) + size0D = 1; + ballSize = (int)anActor->GetBallSize(); + if(ballSize == 0) + ballSize = 1; + Edgewidth = (int)anActor->GetLineWidth(); + if(Edgewidth == 0) + Edgewidth = 1; + Shrink = anActor->GetShrinkFactor(); + + vtkFloatingPointType faces_orientation_color[3]; + anActor->GetFacesOrientationColor(faces_orientation_color); + c0 = int (faces_orientation_color[0] * 255); + c1 = int (faces_orientation_color[1] * 255); + c2 = int (faces_orientation_color[2] * 255); + o.setRgb(c0, c1, c2); + + faces_orientation_scale = anActor->GetFacesOrientationScale(); + faces_orientation_3dvectors = anActor->GetFacesOrientation3DVectors(); + + aMarkerTypeCurrent = anActor->GetMarkerType(); + aMarkerScaleCurrent = anActor->GetMarkerScale(); + aMarkerTextureCurrent = anActor->GetMarkerTexture(); + + // even if there are multiple objects in the selection, + // we need only the first one to get values for the dialog + break; + } + } + } + + SMESHGUI_Preferences_ColorDlg *aDlg = + new SMESHGUI_Preferences_ColorDlg( SMESHGUI::GetSMESHGUI() ); + aDlg->SetBooleanValue(1, faces_orientation_3dvectors); + aDlg->SetColor(1, c); + aDlg->SetColor(2, e); + aDlg->SetColor(3, n); + aDlg->SetColor(4, outl); + aDlg->SetColor(5, c0D); + aDlg->SetColor(6, cBall); + aDlg->SetColor(7, o); + aDlg->SetColor(8, selection); + aDlg->SetColor(9, preselection); + aDlg->SetDeltaBrightness(delta); + aDlg->SetDoubleValue(1, faces_orientation_scale); + aDlg->SetIntValue(1, Edgewidth); + aDlg->SetIntValue(2, int(Shrink*100.)); + aDlg->SetIntValue(3, size0D); + aDlg->SetIntValue(4, ballSize); + + aDlg->setCustomMarkerMap( theMarkerMap[ aStudy->StudyId() ] ); + + if( aMarkerTypeCurrent != VTK::MT_USER ) + aDlg->setStandardMarker( aMarkerTypeCurrent, aMarkerScaleCurrent ); + else + aDlg->setCustomMarker( aMarkerTextureCurrent ); + + if(aDlg->exec()){ + QColor color = aDlg->GetColor(1); + QColor edgecolor = aDlg->GetColor(2); + QColor nodecolor = aDlg->GetColor(3); + QColor outlinecolor = aDlg->GetColor(4); + QColor color0D = aDlg->GetColor(5); + QColor ballcolor = aDlg->GetColor(6); + QColor faces_orientation_color = aDlg->GetColor(7); + QColor selectioncolor = aDlg->GetColor(8); + QColor preSelectioncolor = aDlg->GetColor(9); + int delta = aDlg->GetDeltaBrightness(); + + /* Point marker */ + theMarkerMap[ aStudy->StudyId() ] = aDlg->getCustomMarkerMap(); + + SALOME_ListIteratorOfListIO It( selected ); + for( ; It.More(); It.Next()){ + Handle(SALOME_InteractiveObject) IObject = It.Value(); + if(IObject->hasEntry()){ + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ + /* actor color and backface color */ + anActor->SetSufaceColor(vtkFloatingPointType (color.red()) / 255., + vtkFloatingPointType (color.green()) / 255., + vtkFloatingPointType (color.blue()) / 255., + delta); + /* edge color */ + anActor->SetEdgeColor(vtkFloatingPointType (edgecolor.red()) / 255., + vtkFloatingPointType (edgecolor.green()) / 255., + vtkFloatingPointType (edgecolor.blue()) / 255.); + /* edge outline */ + anActor->SetOutlineColor(vtkFloatingPointType (outlinecolor.red()) / 255., + vtkFloatingPointType (outlinecolor.green()) / 255., + vtkFloatingPointType (outlinecolor.blue()) / 255.); + + /* selection */ + anActor->SetHighlightColor(vtkFloatingPointType (selectioncolor.red()) / 255., + vtkFloatingPointType (selectioncolor.green()) / 255., + vtkFloatingPointType (selectioncolor.blue()) / 255.); + /* pre-selection */ + anActor->SetPreHighlightColor(vtkFloatingPointType (preSelectioncolor.red()) / 255., + vtkFloatingPointType (preSelectioncolor.green()) / 255., + vtkFloatingPointType (preSelectioncolor.blue()) / 255.); + + + /* Shrink factor and size edges */ + anActor->SetShrinkFactor(aDlg->GetIntValue(2) / 100.); + anActor->SetLineWidth(aDlg->GetIntValue(1)); + + /* Nodes color and size */ + anActor->SetNodeColor(vtkFloatingPointType (nodecolor.red()) / 255., + vtkFloatingPointType (nodecolor.green()) / 255., + vtkFloatingPointType (nodecolor.blue()) / 255.); + + /* 0D elements */ + anActor->Set0DColor(vtkFloatingPointType (color0D.red()) / 255., + vtkFloatingPointType (color0D.green()) / 255., + vtkFloatingPointType (color0D.blue()) / 255.); + anActor->Set0DSize(aDlg->GetIntValue(3)); + + /* Ball elements */ + anActor->SetBallColor(vtkFloatingPointType (ballcolor.red()) / 255., + vtkFloatingPointType (ballcolor.green()) / 255., + vtkFloatingPointType (ballcolor.blue()) / 255.); + anActor->SetBallSize(aDlg->GetIntValue(4)); + + /* Faces orientation */ + vtkFloatingPointType c[3] = {vtkFloatingPointType(faces_orientation_color.redF()), + vtkFloatingPointType(faces_orientation_color.greenF()), + vtkFloatingPointType(faces_orientation_color.blueF())}; + anActor->SetFacesOrientationColor(c); + anActor->SetFacesOrientationScale(aDlg->GetDoubleValue(1)); + anActor->SetFacesOrientation3DVectors(aDlg->GetBooleanValue(1)); + + VTK::MarkerType aMarkerTypeNew = aDlg->getMarkerType(); + VTK::MarkerScale aMarkerScaleNew = aDlg->getStandardMarkerScale(); + int aMarkerTextureNew = aDlg->getCustomMarkerID(); + if( aMarkerTypeNew != VTK::MT_USER ) + anActor->SetMarkerStd( aMarkerTypeNew, aMarkerScaleNew ); + else { + const VTK::MarkerMap& aMarkerMap = theMarkerMap[ aStudy->StudyId() ]; + VTK::MarkerMap::const_iterator anIter = aMarkerMap.find( aMarkerTextureNew ); + if( anIter != aMarkerMap.end() ) + anActor->SetMarkerTexture( aMarkerTextureNew, anIter->second.second ); + } + + SMESH::SMESH_GroupBase_var aGroupObject = SMESH::IObjectToInterface(IObject); + if( !aGroupObject->_is_nil() ) + { + SMESH::ElementType anElementType = aGroupObject->GetType(); + QColor aColor; + switch( anElementType ) + { + case SMESH::NODE: aColor = nodecolor; break; + case SMESH::EDGE: aColor = edgecolor; break; + default: aColor = color; break; + } + + SALOMEDS::Color aGroupColor; + aGroupColor.R = (float)aColor.red() / 255.0; + aGroupColor.G = (float)aColor.green() / 255.0; + aGroupColor.B = (float)aColor.blue() / 255.0; + aGroupObject->SetColor( aGroupColor ); + } + } + } + } + SMESH::RepaintCurrentView(); + } + delete aDlg; + return; + } + } SALOME_ListIteratorOfListIO It( selected ); for( ; It.More(); It.Next()){ - Handle(SALOME_InteractiveObject) IObject = It.Value(); - if(IObject->hasEntry()){ - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ - switch(theCommandID){ - case 211: - anActor->SetRepresentation(SMESH_Actor::eEdge); - break; - case 212: - anActor->SetRepresentation(SMESH_Actor::eSurface); - break; - case 213: - if(anActor->IsShrunk()) - anActor->UnShrink(); - else - anActor->SetShrink(); - break; - case 215: - anActor->SetRepresentation(SMESH_Actor::ePoint); - break; - case 1132:{ - vtkFloatingPointType color[3]; - anActor->GetSufaceColor(color[0], color[1], color[2]); - int c0 = int (color[0] * 255); - int c1 = int (color[1] * 255); - int c2 = int (color[2] * 255); - QColor c(c0, c1, c2); - - vtkFloatingPointType edgecolor[3]; - anActor->GetEdgeColor(edgecolor[0], edgecolor[1], edgecolor[2]); - c0 = int (edgecolor[0] * 255); - c1 = int (edgecolor[1] * 255); - c2 = int (edgecolor[2] * 255); - QColor e(c0, c1, c2); - - vtkFloatingPointType backfacecolor[3]; - anActor->GetBackSufaceColor(backfacecolor[0], backfacecolor[1], backfacecolor[2]); - c0 = int (backfacecolor[0] * 255); - c1 = int (backfacecolor[1] * 255); - c2 = int (backfacecolor[2] * 255); - QColor b(c0, c1, c2); - - vtkFloatingPointType nodecolor[3]; - anActor->GetNodeColor(nodecolor[0], nodecolor[1], nodecolor[2]); - c0 = int (nodecolor[0] * 255); - c1 = int (nodecolor[1] * 255); - c2 = int (nodecolor[2] * 255); - QColor n(c0, c1, c2); - - int Edgewidth = (int)anActor->GetLineWidth(); - if(Edgewidth == 0) - Edgewidth = 1; - int intValue = int(anActor->GetNodeSize()); - vtkFloatingPointType Shrink = anActor->GetShrinkFactor(); - - SMESHGUI_Preferences_ColorDlg *aDlg = - new SMESHGUI_Preferences_ColorDlg( SMESHGUI::GetSMESHGUI() ); - aDlg->SetColor(1, c); - aDlg->SetColor(2, e); - aDlg->SetColor(3, n); - aDlg->SetColor(4, b); - aDlg->SetIntValue(1, Edgewidth); - aDlg->SetIntValue(2, intValue); - aDlg->SetIntValue(3, int(Shrink*100.)); - if(aDlg->exec()){ - QColor color = aDlg->GetColor(1); - QColor edgecolor = aDlg->GetColor(2); - QColor nodecolor = aDlg->GetColor(3); - QColor backfacecolor = aDlg->GetColor(4); - /* actor color and backface color */ - anActor->SetSufaceColor(vtkFloatingPointType (color.red()) / 255., - vtkFloatingPointType (color.green()) / 255., - vtkFloatingPointType (color.blue()) / 255.); - anActor->SetBackSufaceColor(vtkFloatingPointType (backfacecolor.red()) / 255., - vtkFloatingPointType (backfacecolor.green()) / 255., - vtkFloatingPointType (backfacecolor.blue()) / 255.); - - /* edge color */ - anActor->SetEdgeColor(vtkFloatingPointType (edgecolor.red()) / 255., - vtkFloatingPointType (edgecolor.green()) / 255., - vtkFloatingPointType (edgecolor.blue()) / 255.); - - /* Shrink factor and size edges */ - anActor->SetShrinkFactor(aDlg->GetIntValue(3) / 100.); - anActor->SetLineWidth(aDlg->GetIntValue(1)); - - /* Nodes color and size */ - anActor->SetNodeColor(vtkFloatingPointType (nodecolor.red()) / 255., - vtkFloatingPointType (nodecolor.green()) / 255., - vtkFloatingPointType (nodecolor.blue()) / 255.); - anActor->SetNodeSize(aDlg->GetIntValue(2)); - - SMESH::SMESH_GroupBase_var aGroupObject = SMESH::IObjectToInterface(IObject); - if( !aGroupObject->_is_nil() ) - { - SALOMEDS::Color aColor; - aColor.R = (float)color.red() / 255.0; - aColor.G = (float)color.green() / 255.0; - aColor.B = (float)color.blue() / 255.0; - aGroupObject->SetColor( aColor ); - } - - delete aDlg; - } - break; - }} - } - } + Handle(SALOME_InteractiveObject) IObject = It.Value(); + if(IObject->hasEntry()){ + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(IObject->getEntry())){ + switch(theCommandID){ + case 211: + anActor->SetRepresentation(SMESH_Actor::eEdge); + break; + case 212: + anActor->SetRepresentation(SMESH_Actor::eSurface); + break; + case 213: + if(anActor->IsShrunk()) + anActor->UnShrink(); + else + anActor->SetShrink(); + break; + case 215: + anActor->SetRepresentation(SMESH_Actor::ePoint); + break; + case 231: + if(anActor->GetQuadratic2DRepresentation() != SMESH_Actor::eLines) + anActor->SetQuadratic2DRepresentation(SMESH_Actor::eLines); + break; + case 232: + if(anActor->GetQuadratic2DRepresentation() != SMESH_Actor::eArcs) + anActor->SetQuadratic2DRepresentation(SMESH_Actor::eArcs); + break; + } + } + } } SMESH::RepaintCurrentView(); } @@ -747,88 +1449,113 @@ if( !selected.IsEmpty() ){ Handle(SALOME_InteractiveObject) anIO = selected.First(); if(!anIO.IsNull()){ - QString aTitle; - SMESH_Actor::eControl aControl = SMESH_Actor::eNone; - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIO->getEntry())){ - switch ( theCommandID ){ - case 6001: - aTitle = QObject::tr( "LENGTH_EDGES" ); - aControl = SMESH_Actor::eLength; - break; - case 6018: - aTitle = QObject::tr( "LENGTH2D_EDGES" ); - aControl = SMESH_Actor::eLength2D; - break; - case 6002: - aTitle = QObject::tr( "FREE_EDGES" ); - aControl = SMESH_Actor::eFreeEdges; - break; - case 6003: - aTitle = QObject::tr( "FREE_BORDERS" ); - aControl = SMESH_Actor::eFreeBorders; - break; - case 6004: - aTitle = QObject::tr( "MULTI_BORDERS" ); - aControl = SMESH_Actor::eMultiConnection; - break; - case 6005: - aTitle = QObject::tr( "FREE_NODES" ); - aControl = SMESH_Actor::eFreeNodes; - break; - case 6019: - aTitle = QObject::tr( "MULTI2D_BORDERS" ); - aControl = SMESH_Actor::eMultiConnection2D; - break; - case 6011: - aTitle = QObject::tr( "AREA_ELEMENTS" ); - aControl = SMESH_Actor::eArea; - break; - case 6012: - aTitle = QObject::tr( "TAPER_ELEMENTS" ); - aControl = SMESH_Actor::eTaper; - break; - case 6013: - aTitle = QObject::tr( "ASPECTRATIO_ELEMENTS" ); - aControl = SMESH_Actor::eAspectRatio; - break; - case 6017: - aTitle = QObject::tr( "ASPECTRATIO_3D_ELEMENTS" ); - aControl = SMESH_Actor::eAspectRatio3D; - break; - case 6014: - aTitle = QObject::tr( "MINIMUMANGLE_ELEMENTS" ); - aControl = SMESH_Actor::eMinimumAngle; - break; - case 6015: - aTitle = QObject::tr( "WARP_ELEMENTS" ); - aControl = SMESH_Actor::eWarping; - break; - case 6016: - aTitle = QObject::tr( "SKEW_ELEMENTS" ); - aControl = SMESH_Actor::eSkew; - break; - case 6009: - aTitle = QObject::tr( "SMESH_VOLUME" ); - aControl = SMESH_Actor::eVolume3D; - break; - case 6021: - aTitle = QObject::tr( "FREE_FACES" ); - aControl = SMESH_Actor::eFreeFaces; - break; - } - anActor->SetControlMode(aControl); - anActor->GetScalarBarActor()->SetTitle(aTitle.toLatin1().data()); - SMESH::RepaintCurrentView(); - } + SMESH_Actor::eControl aControl = SMESH_Actor::eNone; + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIO->getEntry())) { + switch ( theCommandID ){ + case 6001: + aControl = SMESH_Actor::eLength; + break; + case 6018: + aControl = SMESH_Actor::eLength2D; + break; + case 6002: + aControl = SMESH_Actor::eFreeEdges; + break; + case 6003: + aControl = SMESH_Actor::eFreeBorders; + break; + case 6004: + aControl = SMESH_Actor::eMultiConnection; + break; + case 6005: + aControl = SMESH_Actor::eFreeNodes; + break; + case 6019: + aControl = SMESH_Actor::eMultiConnection2D; + break; + case 6011: + aControl = SMESH_Actor::eArea; + break; + case 6012: + aControl = SMESH_Actor::eTaper; + break; + case 6013: + aControl = SMESH_Actor::eAspectRatio; + break; + case 6017: + aControl = SMESH_Actor::eAspectRatio3D; + break; + case 6014: + aControl = SMESH_Actor::eMinimumAngle; + break; + case 6015: + aControl = SMESH_Actor::eWarping; + break; + case 6016: + aControl = SMESH_Actor::eSkew; + break; + case 6009: + aControl = SMESH_Actor::eVolume3D; + break; + case 6021: + aControl = SMESH_Actor::eFreeFaces; + break; + case 6022: + aControl = SMESH_Actor::eMaxElementLength2D; + break; + case 6023: + aControl = SMESH_Actor::eMaxElementLength3D; + break; + case 6024: + aControl = SMESH_Actor::eBareBorderVolume; + break; + case 6025: + aControl = SMESH_Actor::eBareBorderFace; + break; + case 6026: + aControl = SMESH_Actor::eOverConstrainedVolume; + break; + case 6027: + aControl = SMESH_Actor::eOverConstrainedFace; + break; + case 6028: + aControl = SMESH_Actor::eCoincidentNodes; + break; + case 6029: + aControl = SMESH_Actor::eCoincidentElems1D; + break; + case 6030: + aControl = SMESH_Actor:: eCoincidentElems2D; + break; + case 6031: + aControl = SMESH_Actor::eCoincidentElems3D; + break; + } + + anActor->SetControlMode(aControl); + anActor->GetScalarBarActor()->SetTitle( functorToString( anActor->GetFunctor() ).toLatin1().constData() ); + SMESH::RepaintCurrentView(); +#ifndef DISABLE_PLOT2DVIEWER + if(anActor->GetPlot2Histogram()) { + SPlot2d_Histogram* aHistogram = anActor->UpdatePlot2Histogram(); + QString functorName = functorToString( anActor->GetFunctor()); + QString aHistogramName("%1 : %2"); + aHistogramName = aHistogramName.arg(anIO->getName()).arg(functorName); + aHistogram->setName(aHistogramName); + aHistogram->setHorTitle(functorName); + SMESH::ProcessIn2DViewers(anActor); + } +#endif + } } } } bool CheckOIType(const Handle(SALOME_InteractiveObject) & theIO, - MeshObjectType theType, - const QString theInTypeName, - QString & theOutTypeName) + MeshObjectType theType, + const QString theInTypeName, + QString & theOutTypeName) { SMESH_TypeFilter aTypeFilter( theType ); QString entry; @@ -853,17 +1580,17 @@ _PTR(SComponent) aSComp = aSObj->GetFatherComponent(); CORBA::String_var anID = aSComp->GetID().c_str(); if (!strcmp(anID.in(),theIO->getEntry())) - return "Component"; + return "Component"; } QString aTypeName; if ( - CheckOIType ( theIO, HYPOTHESIS, "Hypothesis", aTypeName ) || - CheckOIType ( theIO, ALGORITHM, "Algorithm", aTypeName ) || - CheckOIType ( theIO, MESH, "Mesh", aTypeName ) || - CheckOIType ( theIO, SUBMESH, "SubMesh", aTypeName ) || - CheckOIType ( theIO, GROUP, "Group", aTypeName ) - ) + CheckOIType ( theIO, HYPOTHESIS, "Hypothesis", aTypeName ) || + CheckOIType ( theIO, ALGORITHM, "Algorithm", aTypeName ) || + CheckOIType ( theIO, MESH, "Mesh", aTypeName ) || + CheckOIType ( theIO, SUBMESH, "SubMesh", aTypeName ) || + CheckOIType ( theIO, GROUP, "Group", aTypeName ) + ) return aTypeName; return "NoType"; @@ -882,10 +1609,10 @@ SALOME_ListIteratorOfListIO It(selected); for ( ; It.More(); It.Next()) { - Handle(SALOME_InteractiveObject) IObject = It.Value(); - QString Type = CheckTypeObject(IObject); - if (Type.compare(RefType) != 0) - return "Heterogeneous Selection"; + Handle(SALOME_InteractiveObject) IObject = It.Value(); + QString Type = CheckTypeObject(IObject); + if (Type.compare(RefType) != 0) + return "Heterogeneous Selection"; } return RefType; @@ -913,17 +1640,17 @@ QString cur = anIO->getComponentDataType(); _PTR(SObject) aSO = aStudy->FindObjectID(anIO->getEntry()); if (aSO) { - // check if object is reference - _PTR(SObject) aRefSObj; - aNameList.append("\n - "); - if ( aSO->ReferencedObject( aRefSObj ) ) { - QString aRefName = QString::fromStdString ( aRefSObj->GetName() ); - aNameList.append( aRefName ); - cur = QString::fromStdString ( aRefSObj->GetFatherComponent()->ComponentDataType() ); - } - else - aNameList.append(anIO->getName()); - objectCount++; + // check if object is reference + _PTR(SObject) aRefSObj; + aNameList.append("\n - "); + if ( aSO->ReferencedObject( aRefSObj ) ) { + QString aRefName = QString::fromStdString ( aRefSObj->GetName() ); + aNameList.append( aRefName ); + cur = QString::fromStdString ( aRefSObj->GetFatherComponent()->ComponentDataType() ); + } + else + aNameList.append(anIO->getName()); + objectCount++; } if( aParentComponent.isNull() ) @@ -937,46 +1664,42 @@ if ( aParentComponent != SMESHGUI::GetSMESHGUI()->name() ) { SUIT_MessageBox::warning( SMESHGUI::desktop(), - QObject::tr("ERR_ERROR"), - QObject::tr("NON_SMESH_OBJECTS_SELECTED").arg( SMESHGUI::GetSMESHGUI()->moduleName() ) ); + QObject::tr("ERR_ERROR"), + QObject::tr("NON_SMESH_OBJECTS_SELECTED").arg( SMESHGUI::GetSMESHGUI()->moduleName() ) ); return; } // VSR 17/11/04: check if all objects selected belong to SMESH component <-- finish if (SUIT_MessageBox::warning - (SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_REALLY_DELETE").arg( objectCount ).arg( aNameList ), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, - SUIT_MessageBox::Yes) != SUIT_MessageBox::Yes) + (SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_REALLY_DELETE").arg( objectCount ).arg( aNameList ), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::Yes) != SUIT_MessageBox::Yes) return; SalomeApp_Application* anApp = dynamic_cast( SUIT_Session::session()->activeApplication() ); - SUIT_ViewManager* vm = anApp->activeViewManager(); - int nbSf = vm->getViewsCount(); + // Put the whole hierarchy of sub-objects of the selected SO's into a list and + // then treat them all starting from the deepest objects (at list back) + std::list< _PTR(SObject) > listSO; SALOME_ListIteratorOfListIO It(selected); - - aStudyBuilder->NewCommand(); // There is a transaction - for( ; It.More(); It.Next()){ // loop on selected IO's + for( ; It.More(); It.Next()) // loop on selected IO's + { Handle(SALOME_InteractiveObject) IObject = It.Value(); if(IObject->hasEntry()) { - _PTR(SObject) aSO = aStudy->FindObjectID(IObject->getEntry()); - - // disable removal of "SMESH" component object - if(aSO->FindAttribute(anAttr, "AttributeIOR")){ - anIOR = anAttr; - if ( engineIOR() == anIOR->Value().c_str() ) - continue; - } - //Check the referenced object - _PTR(SObject) aRefSObject; - if ( aSO && aSO->ReferencedObject( aRefSObject ) ) - aSO = aRefSObject; // Delete main Object instead of reference - - // put the whole hierarchy of sub-objects of the selected SO into a list and - // then treat them all starting from the deepest objects (at list back) - - std::list< _PTR(SObject) > listSO; + _PTR(SObject) aSO = aStudy->FindObjectID(IObject->getEntry()); + + // disable removal of "SMESH" component object + if(aSO->FindAttribute(anAttr, "AttributeIOR")){ + anIOR = anAttr; + if ( engineIOR() == anIOR->Value().c_str() ) + continue; + } + //Check the referenced object + _PTR(SObject) aRefSObject; + if ( aSO && aSO->ReferencedObject( aRefSObject ) ) + aSO = aRefSObject; // Delete main Object instead of reference + listSO.push_back( aSO ); std::list< _PTR(SObject) >::iterator itSO = listSO.begin(); for ( ; itSO != listSO.end(); ++itSO ) { @@ -984,60 +1707,87 @@ for (it->InitEx(false); it->More(); it->Next()) listSO.push_back( it->Value() ); } + } + } + // Check if none of objects to delete is referred from outside + std::list< _PTR(SObject) >::reverse_iterator ritSO; + for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO ) + { + _PTR(SObject) SO = *ritSO; + if ( !SO ) continue; + std::vector<_PTR(SObject)> aReferences = aStudy->FindDependances( *ritSO ); + for (size_t i = 0; i < aReferences.size(); i++) { + _PTR(SComponent) aComponent = aReferences[i]->GetFatherComponent(); + std::string type = aComponent->ComponentDataType(); + if ( type != "SMESH" ) + { + SUIT_MessageBox::warning( anApp->desktop(), + QObject::tr("WRN_WARNING"), + QObject::tr("DEP_OBJECT") ); + return; // outside SMESH, there is an object depending on a SMESH object + } + } + } - // treat SO's in the list starting from the back - - std::list< _PTR(SObject) >::reverse_iterator ritSO = listSO.rbegin(); - for ( ; ritSO != listSO.rend(); ++ritSO ) { - _PTR(SObject) SO = *ritSO; - if ( !SO ) continue; - std::string anEntry = SO->GetID(); - - /** Erase graphical object **/ - if(SO->FindAttribute(anAttr, "AttributeIOR")){ - QVector aViews = vm->getViews(); - for(int i = 0; i < nbSf; i++){ - SUIT_ViewWindow *sf = aViews[i]; - if(SMESH_Actor* anActor = SMESH::FindActorByEntry(sf,anEntry.c_str())){ - SMESH::RemoveActor(sf,anActor); - } - } - } - - /** Remove an object from data structures **/ - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( SO )); - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( SO )); - if ( !aGroup->_is_nil() ) { // DELETE GROUP - SMESH::SMESH_Mesh_var aMesh = aGroup->GetMesh(); - aMesh->RemoveGroup( aGroup ); + // Treat SO's in the list starting from the back + aStudyBuilder->NewCommand(); // There is a transaction + for ( ritSO = listSO.rbegin(); ritSO != listSO.rend(); ++ritSO ) + { + _PTR(SObject) SO = *ritSO; + if ( !SO ) continue; + std::string anEntry = SO->GetID(); + + /** Erase graphical object **/ + if(SO->FindAttribute(anAttr, "AttributeIOR")){ + ViewManagerList aViewMenegers = anApp->viewManagers(); + ViewManagerList::const_iterator it = aViewMenegers.begin(); + for( ; it != aViewMenegers.end(); it++) { + SUIT_ViewManager* vm = *it; + int nbSf = vm ? vm->getViewsCount() : 0; + if(vm) { + QVector aViews = vm->getViews(); + for(int i = 0; i < nbSf; i++){ + SUIT_ViewWindow *sf = aViews[i]; + if(SMESH_Actor* anActor = SMESH::FindActorByEntry(sf,anEntry.c_str())){ + SMESH::RemoveActor(sf,anActor); + } + } } - else if ( !aSubMesh->_is_nil() ) { // DELETE SUBMESH - SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); - aMesh->RemoveSubMesh( aSubMesh ); + } + } + /** Remove an object from data structures **/ + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( SO )); + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( SO )); + if ( !aGroup->_is_nil() ) { // DELETE GROUP + SMESH::SMESH_Mesh_var aMesh = aGroup->GetMesh(); + aMesh->RemoveGroup( aGroup ); + } + else if ( !aSubMesh->_is_nil() ) { // DELETE SUBMESH + SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); + aMesh->RemoveSubMesh( aSubMesh ); - _PTR(SObject) aMeshSO = SMESH::FindSObject(aMesh); - if (aMeshSO) - SMESH::ModifiedMesh(aMeshSO, false, aMesh->NbNodes()==0); - } - else { - IObject = new SALOME_InteractiveObject - ( anEntry.c_str(), engineIOR().toLatin1().data(), SO->GetName().c_str() ); - QString objType = CheckTypeObject(IObject); - if ( objType == "Hypothesis" || objType == "Algorithm" ) {// DELETE HYPOTHESIS - SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject); - aStudyBuilder->RemoveObjectWithChildren( SO ); - } - else {// default action: remove SObject from the study + _PTR(SObject) aMeshSO = SMESH::FindSObject(aMesh); + if (aMeshSO) + SMESH::ModifiedMesh(aMeshSO, false, aMesh->NbNodes()==0); + } + else { + Handle(SALOME_InteractiveObject) IObject = new SALOME_InteractiveObject + ( anEntry.c_str(), engineIOR().toLatin1().data(), SO->GetName().c_str() ); + QString objType = CheckTypeObject(IObject); + if ( objType == "Hypothesis" || objType == "Algorithm" ) {// DELETE HYPOTHESIS + SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject); + aStudyBuilder->RemoveObjectWithChildren( SO ); + } + else {// default action: remove SObject from the study // san - it's no use opening a transaction here until UNDO/REDO is provided in SMESH //SUIT_Operation *op = new SALOMEGUI_ImportOperation(myActiveStudy); //op->start(); - aStudyBuilder->RemoveObjectWithChildren( SO ); - //op->finish(); - } - } - } /* listSO back loop */ - } /* IObject->hasEntry() */ - } /* more/next */ + aStudyBuilder->RemoveObjectWithChildren( SO ); + //op->finish(); + } + } + } /* listSO back loop */ + aStudyBuilder->CommitCommand(); /* Clear any previous selection */ @@ -1046,13 +1796,17 @@ SMESHGUI::GetSMESHGUI()->updateObjBrowser(); } -//} +//} namespace extern "C" { SMESHGUI_EXPORT CAM_Module* createModule() { return new SMESHGUI(); } + + SMESHGUI_EXPORT char* getModuleVersion() { + return (char*)SMESH_VERSION_STR; + } } SMESH::SMESH_Gen_var SMESHGUI::myComponentSMESH = SMESH::SMESH_Gen::_nil(); @@ -1063,12 +1817,14 @@ SMESH::SMESH_Gen_var SMESHGUI::myComponentSMESH = SMESH::SMESH_Gen::_nil(); */ //============================================================================= SMESHGUI::SMESHGUI() : -SalomeApp_Module( "SMESH" ) +SalomeApp_Module( "SMESH" ), +LightApp_Module( "SMESH" ) { if ( CORBA::is_nil( myComponentSMESH ) ) { CORBA::Boolean anIsEmbeddedMode; myComponentSMESH = SMESH_Client::GetSMESHGen(getApp()->orb(),anIsEmbeddedMode); + MESSAGE("-------------------------------> anIsEmbeddedMode=" << anIsEmbeddedMode); // 0019923: EDF 765 SMESH : default values of hypothesis SUIT_ResourceMgr* aResourceMgr = SMESH::GetResourceMgr(this); @@ -1076,6 +1832,14 @@ SalomeApp_Module( "SMESH" ) myComponentSMESH->SetBoundaryBoxSegmentation( nbSeg ); nbSeg = aResourceMgr->integerValue( "SMESH", "nb_segments_per_edge", 15 ); myComponentSMESH->SetDefaultNbSegments( nbSeg ); + + const char* options[] = { "historical_python_dump", "forget_mesh_on_hyp_modif" }; + for ( size_t i = 0; i < sizeof(options)/sizeof(char*); ++i ) + if ( aResourceMgr->hasValue( "SMESH", options[i] )) + { + QString val = aResourceMgr->stringValue( "SMESH", options[i] ); + myComponentSMESH->SetOption( options[i], val.toLatin1().constData() ); + } } myActiveDialogBox = 0; @@ -1083,8 +1847,15 @@ SalomeApp_Module( "SMESH" ) myState = -1; myDisplayer = 0; + myEventCallbackCommand = vtkCallbackCommand::New(); + myEventCallbackCommand->Delete(); + myEventCallbackCommand->SetClientData( this ); + myEventCallbackCommand->SetCallback( SMESHGUI::ProcessEvents ); + myPriority = 0.0; + SMESH::GetFilterManager(); SMESH::GetPattern(); + SMESH::GetMeasurements(); /* load resources for all available meshers */ SMESH::InitAvailableHypotheses(); @@ -1097,8 +1868,12 @@ SalomeApp_Module( "SMESH" ) //============================================================================= SMESHGUI::~SMESHGUI() { - SMESH::GetFilterManager()->Destroy(); +#ifdef WITHGENERICOBJ + SMESH::GetFilterManager()->UnRegister(); + SMESH::GetMeasurements()->UnRegister(); +#endif SMESH::GetFilterManager() = SMESH::FilterManager::_nil(); + SMESH::GetMeasurements() = SMESH::Measurements::_nil(); } //============================================================================= @@ -1115,13 +1890,22 @@ LightApp_SelectionMgr* SMESHGUI::selectionMgr() return 0; } -bool SMESHGUI::automaticUpdate() +//============================================================================= +/*! + * + */ +//============================================================================= +bool SMESHGUI::automaticUpdate(unsigned int requestedSize, bool* limitExceeded) { SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); if ( !resMgr ) return false; - return resMgr->booleanValue( "SMESH", "auto_update", false ); + bool autoUpdate = resMgr->booleanValue( "SMESH", "auto_update", false ); + long updateLimit = resMgr->integerValue( "SMESH", "update_limit", 500000 ); + bool exceeded = updateLimit > 0 && requestedSize > updateLimit; + if ( limitExceeded ) *limitExceeded = autoUpdate && exceeded; + return autoUpdate && !exceeded; } //============================================================================= @@ -1156,7 +1940,7 @@ SMESHGUI* SMESHGUI::GetSMESHGUI() { _PTR(Study) aStudy = study->studyDS(); if ( aStudy ) - GetSMESHGen()->SetCurrentStudy( _CAST(Study,aStudy)->GetStudy() ); + GetSMESHGen()->SetCurrentStudy( _CAST(Study,aStudy)->GetStudy() ); } } @@ -1221,6 +2005,16 @@ void SMESHGUI::EmitSignalCloseAllDialogs() emit SignalCloseAllDialogs(); } +//============================================================================= +/*! + * + */ +//============================================================================= +void SMESHGUI::EmitSignalVisibilityChanged() +{ + emit SignalVisibilityChanged(); +} + //============================================================================= /*! * @@ -1275,9 +2069,15 @@ SalomeApp_Study* SMESHGUI::activeStudy() * */ //============================================================================= -char* SMESHGUI::JoinObjectParameters(const QStringList& theParametersList) +void SMESHGUI::Modified( bool theIsUpdateActions ) { - return theParametersList.join(":").toLatin1().data(); + if( SalomeApp_Application* app = dynamic_cast( SUIT_Session::session()->activeApplication() ) ) { + if( SalomeApp_Study* appStudy = dynamic_cast( app->activeStudy() ) ) { + appStudy->Modified(); + if( theIsUpdateActions ) + app->updateActions(); + } + } } //============================================================================= @@ -1308,8 +2108,8 @@ static int isStudyLocked(_PTR(Study) theStudy){ static bool checkLock(_PTR(Study) theStudy) { if (isStudyLocked(theStudy)) { SUIT_MessageBox::warning( SMESHGUI::desktop(), - QObject::tr("WRN_WARNING"), - QObject::tr("WRN_STUDY_LOCKED") ); + QObject::tr("WRN_WARNING"), + QObject::tr("WRN_STUDY_LOCKED") ); return true; } return false; @@ -1351,15 +2151,18 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) //QAction* act = action( theCommandID ); - switch (theCommandID) { - case 33: // DELETE + switch (theCommandID) { + case 33: // DELETE if(checkLock(aStudy)) break; OnEditDelete(); break; - case 113: // IMPORT + case 116: + case 115: + case 117: + case 113: case 112: - case 111: + case 111: // IMPORT { if(checkLock(aStudy)) break; ::ImportMeshesFromFile(GetSMESHGen(),theCommandID); @@ -1385,7 +2188,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } - case 122: // EXPORT MED + case 122: // EXPORT MED case 121: case 123: case 124: @@ -1393,12 +2196,16 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) case 126: case 140: case 141: + case 142: + case 143: + case 144: + case 145: { ::ExportMeshToFile(theCommandID); break; } - case 200: // SCALAR BAR + case 200: // SCALAR BAR { LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); SALOME_ListIO selected; @@ -1406,12 +2213,15 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) aSel->selectedObjects( selected ); if( selected.Extent() ) { - Handle(SALOME_InteractiveObject) anIO = selected.First(); - if( anIO->hasEntry() ) { - if( SMESH_Actor* anActor = SMESH::FindActorByEntry( anIO->getEntry() ) ) { - anActor->SetControlMode( SMESH_Actor::eNone ); - } - } + Handle(SALOME_InteractiveObject) anIO = selected.First(); + if( anIO->hasEntry() ) { + if( SMESH_Actor* anActor = SMESH::FindActorByEntry( anIO->getEntry() ) ) { + anActor->SetControlMode( SMESH_Actor::eNone ); +#ifndef DISABLE_PLOT2DVIEWER + SMESH::ProcessIn2DViewers(anActor,SMESH::RemoveFrom2dViewer); +#endif + } + } } break; } @@ -1420,6 +2230,28 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SMESHGUI_Preferences_ScalarBarDlg::ScalarBarProperties( this ); break; } + case 2021: + { + // dump control distribution data to the text file + ::SaveDistribution(); + break; + } + + case 2022: + { + // show/ distribution + ::ShowDistribution(); + break; + } + +#ifndef DISABLE_PLOT2DVIEWER + case 2023: + { + // plot distribution + ::PlotDistribution(); + break; + } +#endif // Auto-color case 1136: @@ -1432,21 +2264,29 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) case 1134: // Clipping case 1133: // Tranparency - case 1132: // Colors / Size + case 1132: // Display preferences (colors, shrink size, line width, ...) // Display Mode case 215: // Nodes case 213: // Nodes case 212: // Nodes case 211: // Nodes - ::SetDisplayMode(theCommandID); + ::SetDisplayMode(theCommandID, myMarkerMap); break; - // Display Entity + //2D quadratic representation + case 231: + case 232: + ::SetDisplayMode(theCommandID, myMarkerMap); + break; + + // Display Entity + case 216: // 0D elements case 217: // Edges case 218: // Faces case 219: // Volumes case 220: // All Entity + case 222: // Balls ::SetDisplayEntity(theCommandID); break; @@ -1458,16 +2298,16 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SALOME_ListIteratorOfListIO it(selected); for( ; it.More(); it.Next()) { Handle(SALOME_InteractiveObject) anIObject = it.Value(); - if(anIObject->hasEntry()) { - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ - anActor->SetFacesOriented( !anActor->GetFacesOriented() ); - } - } + if(anIObject->hasEntry()) { + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ + anActor->SetFacesOriented( !anActor->GetFacesOriented() ); + } + } } break; } - case 214: // UPDATE + case 214: // UPDATE { if(checkLock(aStudy)) break; try { @@ -1490,15 +2330,15 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } - case 300: // ERASE - case 301: // DISPLAY - case 302: // DISPLAY ONLY + case 300: // ERASE + case 301: // DISPLAY + case 302: // DISPLAY ONLY { SMESH::EDisplaing anAction; switch (theCommandID) { - case 300: anAction = SMESH::eErase; break; - case 301: anAction = SMESH::eDisplay; break; - case 302: anAction = SMESH::eDisplayOnly; break; + case 300: anAction = SMESH::eErase; break; + case 301: anAction = SMESH::eDisplay; break; + case 302: anAction = SMESH::eDisplayOnly; break; } LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); @@ -1507,7 +2347,10 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) aSel->selectedObjects( sel_objects ); if( theCommandID==302 ) - startOperation( myEraseAll ); + { + MESSAGE("anAction = SMESH::eDisplayOnly"); + startOperation( myEraseAll ); + } extractContainers( sel_objects, to_process ); @@ -1518,19 +2361,29 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if (vtkwnd) { SALOME_ListIteratorOfListIO It( to_process ); for ( ; It.More(); It.Next()) { + MESSAGE("---"); Handle(SALOME_InteractiveObject) IOS = It.Value(); if (IOS->hasEntry()) { - if (!SMESH::UpdateView(anAction, IOS->getEntry())) + MESSAGE("---"); + if (!SMESH::UpdateView(anAction, IOS->getEntry())) { + SMESHGUI::GetSMESHGUI()->EmitSignalVisibilityChanged(); break; // PAL16774 (Crash after display of many groups) + } if (anAction == SMESH::eDisplayOnly) + { + MESSAGE("anAction = SMESH::eDisplayOnly"); anAction = SMESH::eDisplay; + } } } } // PAL13338 + PAL15161 --> - if ( ( theCommandID==301 || theCommandID==302 ) && !checkLock(aStudy)) + if ( ( theCommandID==301 || theCommandID==302 ) && !checkLock(aStudy)) { + MESSAGE("anAction = SMESH::eDisplayOnly"); SMESH::UpdateView(); + SMESHGUI::GetSMESHGUI()->EmitSignalVisibilityChanged(); + } // PAL13338 + PAL15161 <-- } catch (...) { // PAL16774 (Crash after display of many groups) @@ -1538,28 +2391,29 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) } if (anAction == SMESH::eErase) { - SALOME_ListIO l1; - aSel->setSelectedObjects( l1 ); + MESSAGE("anAction == SMESH::eErase"); + SALOME_ListIO l1; + aSel->setSelectedObjects( l1 ); } else - aSel->setSelectedObjects( to_process ); + aSel->setSelectedObjects( to_process ); break; } - case 400: // NODES + case 4000: // NODES { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); + EmitSignalDeactivateDialog(); - ( new SMESHGUI_NodesDlg( this ) )->show(); + ( new SMESHGUI_NodesDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), - tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -1574,33 +2428,22 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } - case 406: // MOVE NODE - { - if ( !vtkwnd ) - { - SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); - break; - } - - if(checkLock(aStudy)) break; - ( new SMESHGUI_MoveNodesDlg( this ) )->show(); - break; - } - - case 701: // COMPUTE MESH - case 711: // PRECOMPUTE MESH - { - if (checkLock(aStudy)) break; - startOperation( theCommandID ); - } - break; - + case 701: // COMPUTE MESH + case 711: // PRECOMPUTE MESH + case 712: // EVALUATE MESH + case 713: // MESH ORDER case 702: // Create mesh case 703: // Create sub-mesh case 704: // Edit mesh/sub-mesh startOperation( theCommandID ); break; + case 705: // copy mesh + { + if (checkLock(aStudy)) break; + EmitSignalDeactivateDialog(); + ( new SMESHGUI_CopyMeshDlg( this ) )->show(); + } + break; case 710: // Build compound mesh { if (checkLock(aStudy)) break; @@ -1615,7 +2458,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1638,14 +2481,15 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) ( new SMESHGUI_UnionOfTwoTrianglesDlg( this ) )->show(); break; } - case 409: // Change orientation - case 410: // Union of triangles - case 411: // Cutting of quadrangles + case 409: // Change orientation + case 410: // Union of triangles + case 411: // Cutting of quadrangles + case 419: // Splitting volumes into tetrahedra { if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1658,6 +2502,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) aDlg = new SMESHGUI_ChangeOrientationDlg(this); else if ( theCommandID == 410 ) aDlg = new SMESHGUI_UnionOfTrianglesDlg(this); + else if ( theCommandID == 419 ) + aDlg = new SMESHGUI_CuttingIntoTetraDlg(this); else aDlg = new SMESHGUI_CuttingOfQuadsDlg(this); @@ -1668,12 +2514,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_SmoothingDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_SmoothingDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -1681,11 +2527,11 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if (checkLock(aStudy)) break; if (vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_ExtrusionDlg ( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_ExtrusionDlg ( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -1693,12 +2539,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RevolutionDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RevolutionDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -1712,8 +2558,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) ( new SMESHGUI_MeshPatternDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -1721,30 +2567,20 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if (checkLock(aStudy)) break; if (vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_ExtrusionAlongPathDlg( this ) )->show(); - } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); - } - break; - } - case 417: // Convert mesh to quadratic - { - startOperation( 417 ); - /* if (checkLock(aStudy)) break; - if (vtkwnd) { - EmitSignalDeactivateDialog(); - new SMESHGUI_ConvToQuadDlg(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_ExtrusionAlongPathDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); - }*/ + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } break; } - case 806: // CREATE GEO GROUP + case 417: // Convert mesh to quadratic + case 418: // create 2D mesh from 3D + case 420: // Reorient faces + case 806: // CREATE GEO GROUP { - startOperation( 806 ); + startOperation( theCommandID ); break; } case 801: // CREATE GROUP @@ -1752,7 +2588,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1767,7 +2603,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) int nbSel = selected.Extent(); if (nbSel == 1) { - // check if mesh is selected + // check if mesh is selected aMesh = SMESH::GetMeshByIO( selected.First() ); } SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, aMesh); @@ -1780,7 +2616,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1794,52 +2630,61 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) int nbSel = selected.Extent(); if (nbSel == 1) { - // check if submesh is selected - Handle(SALOME_InteractiveObject) IObject = selected.First(); - if (IObject->hasEntry()) { - _PTR(SObject) aSObj = aStudy->FindObjectID(IObject->getEntry()); - if( aSObj ) { - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( aSObj ) ); - if (!aSubMesh->_is_nil()) { - try { - SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); - // get submesh elements list by types - SMESH::long_array_var aNodes = aSubMesh->GetElementsByType(SMESH::NODE); - SMESH::long_array_var aEdges = aSubMesh->GetElementsByType(SMESH::EDGE); - SMESH::long_array_var aFaces = aSubMesh->GetElementsByType(SMESH::FACE); - SMESH::long_array_var aVolumes = aSubMesh->GetElementsByType(SMESH::VOLUME); - // create group for each type o elements - QString aName = IObject->getName(); - if (aNodes->length() > 0) { - SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::NODE, aName + "_Nodes"); - aGroup->Add(aNodes.inout()); - } - if (aEdges->length() > 0) { - SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::EDGE, aName + "_Edges"); - aGroup->Add(aEdges.inout()); - } - if (aFaces->length() > 0) { - SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::FACE, aName + "_Faces"); - aGroup->Add(aFaces.inout()); - } - if (aVolumes->length() > 0) { - SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::VOLUME, aName + "_Volumes"); - aGroup->Add(aVolumes.inout()); - } - updateObjBrowser(); - - } + // check if submesh is selected + Handle(SALOME_InteractiveObject) IObject = selected.First(); + if (IObject->hasEntry()) { + _PTR(SObject) aSObj = aStudy->FindObjectID(IObject->getEntry()); + if( aSObj ) { + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( SMESH::SObjectToObject( aSObj ) ); + if (!aSubMesh->_is_nil()) { + try { + SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); + // get submesh elements list by types + SMESH::long_array_var aNodes = aSubMesh->GetElementsByType(SMESH::NODE); + SMESH::long_array_var aEdges = aSubMesh->GetElementsByType(SMESH::EDGE); + SMESH::long_array_var aFaces = aSubMesh->GetElementsByType(SMESH::FACE); + SMESH::long_array_var aVolumes = aSubMesh->GetElementsByType(SMESH::VOLUME); + // create group for each type o elements + QString aName = IObject->getName(); + QStringList anEntryList; + if (aNodes->length() > 0) { + SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::NODE, aName + "_Nodes"); + aGroup->Add(aNodes.inout()); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aGroup ) ) + anEntryList.append( aSObject->GetID().c_str() ); + } + if (aEdges->length() > 0) { + SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::EDGE, aName + "_Edges"); + aGroup->Add(aEdges.inout()); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aGroup ) ) + anEntryList.append( aSObject->GetID().c_str() ); + } + if (aFaces->length() > 0) { + SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::FACE, aName + "_Faces"); + aGroup->Add(aFaces.inout()); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aGroup ) ) + anEntryList.append( aSObject->GetID().c_str() ); + } + if (aVolumes->length() > 0) { + SMESH::SMESH_Group_var aGroup = SMESH::AddGroup(aMesh, SMESH::VOLUME, aName + "_Volumes"); + aGroup->Add(aVolumes.inout()); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aGroup ) ) + anEntryList.append( aSObject->GetID().c_str() ); + } + updateObjBrowser(); + anApp->browseObjects( anEntryList ); + } catch(const SALOME::SALOME_Exception & S_ex){ - SalomeApp_Tools::QtCatchCorbaException(S_ex); - } - } - } - } + SalomeApp_Tools::QtCatchCorbaException(S_ex); + } + } + } + } } else if(nbSel==0) { SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), - tr("SMESH_WRN_NO_AVAILABLE_DATA")); + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_NO_AVAILABLE_DATA")); } break; } @@ -1849,7 +2694,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1868,16 +2713,16 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SMESH::SMESH_GroupBase_var aGroup = SMESH::IObjectToInterface(It.Value()); if (!aGroup->_is_nil()) { - nbSelectedGroups++; + nbSelectedGroups++; SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, aGroup); aDlg->show(); - } + } } if (nbSelectedGroups == 0) - { - SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, SMESH::SMESH_GroupBase::_nil()); - aDlg->show(); - } + { + SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, SMESH::SMESH_GroupBase::_nil()); + aDlg->show(); + } break; } @@ -1885,8 +2730,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if (myState == 800) { - SMESHGUI_GroupDlg *aDlg = (SMESHGUI_GroupDlg*) myActiveDialogBox; - if (aDlg) aDlg->onAdd(); + SMESHGUI_GroupDlg *aDlg = (SMESHGUI_GroupDlg*) myActiveDialogBox; + if (aDlg) aDlg->onAdd(); } break; } @@ -1895,8 +2740,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if (myState == 800) { - SMESHGUI_GroupDlg *aDlg = (SMESHGUI_GroupDlg*) myActiveDialogBox; - if (aDlg) aDlg->onRemove(); + SMESHGUI_GroupDlg *aDlg = (SMESHGUI_GroupDlg*) myActiveDialogBox; + if (aDlg) aDlg->onRemove(); } break; } @@ -1906,7 +2751,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1926,7 +2771,16 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if (!aGroup->_is_nil()) { SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, aGroup, true ); aDlg->show(); - } + } + else + { + SMESH::SMESH_GroupOnFilter_var aGroup = + SMESH::IObjectToInterface(It.Value()); + if (!aGroup->_is_nil()) { + SMESHGUI_GroupDlg *aDlg = new SMESHGUI_GroupDlg( this, aGroup, true ); + aDlg->show(); + } + } } break; } @@ -1938,7 +2792,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1977,7 +2831,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if ( !vtkwnd ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "NOT_A_VTK_VIEWER" ) ); break; } @@ -1990,8 +2844,10 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } - case 900: // MESH INFOS + case 900: // MESH INFOS + case 903: // WHAT IS { + int page = theCommandID == 900 ? SMESHGUI_MeshInfoDlg::BaseInfo : SMESHGUI_MeshInfoDlg::ElemInfo; EmitSignalDeactivateDialog(); LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); SALOME_ListIO selected; @@ -1999,54 +2855,27 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) aSel->selectedObjects( selected ); if ( selected.Extent() > 1 ) { // a dlg for each IO - SALOME_ListIO IOs; - SALOME_ListIteratorOfListIO It (selected); + SALOME_ListIteratorOfListIO It( selected ); for ( ; It.More(); It.Next() ) { - IOs.Clear(); IOs.Append( It.Value() ); - aSel->setSelectedObjects( IOs ); - ( new SMESHGUI_MeshInfosDlg( this ) )->show(); + SMESHGUI_MeshInfoDlg* dlg = new SMESHGUI_MeshInfoDlg( SMESHGUI::desktop(), page ); + dlg->showInfo( It.Value() ); + dlg->show(); } - // restore selection - aSel->setSelectedObjects( selected ); } - else - ( new SMESHGUI_MeshInfosDlg( this ) )->show(); - break; - } - - case 902: // STANDARD MESH INFOS - { - EmitSignalDeactivateDialog(); - LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); - SALOME_ListIO selected; - if( aSel ) - aSel->selectedObjects( selected ); - - if ( selected.Extent() > 1 ) { // a dlg for each IO - SALOME_ListIO IOs; - SALOME_ListIteratorOfListIO It (selected); - for ( ; It.More(); It.Next() ) { - IOs.Clear(); - IOs.Append( It.Value() ); - aSel->setSelectedObjects( IOs ); - ( new SMESHGUI_StandardMeshInfosDlg( this ) )->show(); - } - // restore selection - aSel->setSelectedObjects( selected ); + else { + SMESHGUI_MeshInfoDlg* dlg = new SMESHGUI_MeshInfoDlg( SMESHGUI::desktop(), page ); + dlg->show(); } - else - ( new SMESHGUI_StandardMeshInfosDlg( this ) )->show(); break; } - case 903: // WHAT IS + case 904: // FIND ELEM { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_WhatIsDlg( this ) )->show(); + startOperation( theCommandID ); break; } - case 1100: // EDIT HYPOTHESIS + case 1100: // EDIT HYPOTHESIS { if(checkLock(aStudy)) break; @@ -2059,82 +2888,28 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) if (nbSel == 1) { Handle(SALOME_InteractiveObject) anIObject = selected.First(); - SMESH::SMESH_Hypothesis_var aHypothesis = SMESH::IObjectToInterface(anIObject); + SMESH::SMESH_Hypothesis_var aHypothesis = SMESH::IObjectToInterface(anIObject); /* Look for all mesh objects that have this hypothesis affected in order to flag as ModifiedMesh */ /* At end below '...->updateObjBrowser(true)' will change icon of mesh objects */ /* Warning : however by internal mechanism all subMeshes icons are changed ! */ if ( !aHypothesis->_is_nil() ) { + // BUG 0020378 + //SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(aHypothesis->GetName()); SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(aHypothesis->GetName()); - if (aCreator) - aCreator->edit( aHypothesis.in(), anIObject->getName(), desktop() ); + if (aCreator) { + aCreator->edit( aHypothesis.in(), anIObject->getName(), desktop(), this, SLOT( onHypothesisEdit( int ) ) ); + } else { // report error } } } - updateObjBrowser( true ); - break; - } - - case 1101: // RENAME - { - if ( checkLock( aStudy ) ) - break; - - LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr(); - SALOME_ListIO selected; - if( aSel ) - aSel->selectedObjects( selected ); - - bool isAny = false; // is there any appropriate object selected - - SALOME_ListIteratorOfListIO It( selected ); - for ( ; It.More(); It.Next() ) - { - Handle(SALOME_InteractiveObject) IObject = It.Value(); - _PTR(SObject) obj = aStudy->FindObjectID( IObject->getEntry() ); - _PTR(GenericAttribute) anAttr; - _PTR(AttributeName) aName; - if ( obj ) - { - if ( obj->FindAttribute(anAttr, "AttributeName") ) - { - aName = anAttr; - QString newName = QString(aName->Value().c_str()); - - // check type to prevent renaming of inappropriate objects - int aType = SMESHGUI_Selection::type(IObject->getEntry(), aStudy); - if (aType == MESH || aType == GROUP || - aType == SUBMESH || aType == SUBMESH_COMPOUND || - aType == SUBMESH_SOLID || aType == SUBMESH_FACE || - aType == SUBMESH_EDGE || aType == SUBMESH_VERTEX || - aType == HYPOTHESIS || aType == ALGORITHM) - { - isAny = true; - newName = LightApp_NameDlg::getName(desktop(), newName); - if ( !newName.isEmpty() ) - { - SMESHGUI::GetSMESHGen()->SetName(obj->GetIOR().c_str(), newName.toLatin1().data()); - - updateObjBrowser(); - } - } - } - } - } // for - - if (!isAny) { - SUIT_MessageBox::warning(desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_WRN_NO_APPROPRIATE_SELECTION")); - } break; } - - case 1102: // REMOVE HYPOTHESIS / ALGORITHMS + case 1102: // REMOVE HYPOTHESIS / ALGORITHMS { if(checkLock(aStudy)) break; SUIT_OverrideCursor wc; @@ -2146,8 +2921,8 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SALOME_ListIteratorOfListIO It(selected); for (int i = 0; It.More(); It.Next(), i++) { - Handle(SALOME_InteractiveObject) IObject = It.Value(); - SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject); + Handle(SALOME_InteractiveObject) IObject = It.Value(); + SMESH::RemoveHypothesisOrAlgorithmOnMesh(IObject); } SALOME_ListIO l1; aSel->setSelectedObjects( l1 ); @@ -2155,118 +2930,126 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) break; } - case 401: // GEOM::EDGE - case 4021: // TRIANGLE - case 4022: // QUAD - case 4023: // POLYGON - case 4031: // TETRA - case 4032: // HEXA + case 4008: // BALL + case 4009: // ELEM0D + case 4010: // EDGE + case 4021: // TRIANGLE + case 4022: // QUAD + case 4023: // POLYGON + case 4031: // TETRA + case 4032: // HEXA + case 4133: // PENTA + case 4134: // PYRAMID + case 4135: // OCTA12 { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - SMDSAbs_ElementType type = SMDSAbs_Edge; - int nbNodes = 2; + EmitSignalDeactivateDialog(); + SMDSAbs_EntityType type = SMDSEntity_Edge; switch (theCommandID) { - case 4021: // TRIANGLE - type = SMDSAbs_Face; nbNodes = 3; break; - case 4022: // QUAD - type = SMDSAbs_Face; nbNodes = 4; break; - case 4031: // TETRA - type = SMDSAbs_Volume; nbNodes = 4; break; - case 4023: // POLYGON - type = SMDSAbs_Face; nbNodes = 5; break; // 5 - identificator for POLYGON - case 4032: // HEXA - type = SMDSAbs_Volume; nbNodes = 8; break; - case 4033: // POLYHEDRE - type = SMDSAbs_Volume; nbNodes = 9; break; // 9 - identificator for POLYHEDRE + case 4008: type = SMDSEntity_Ball; break; + case 4009: type = SMDSEntity_0D; break; + case 4021: type = SMDSEntity_Triangle; break; + case 4022: type = SMDSEntity_Quadrangle; break; + case 4031: type = SMDSEntity_Tetra; break; + case 4023: type = SMDSEntity_Polygon; break; + case 4032: type = SMDSEntity_Hexa; break; + case 4133: type = SMDSEntity_Penta; break; + case 4134: type = SMDSEntity_Pyramid; break; + case 4135: type = SMDSEntity_Hexagonal_Prism; break; default:; } - ( new SMESHGUI_AddMeshElementDlg( this, type, nbNodes ) )->show(); + ( new SMESHGUI_AddMeshElementDlg( this, type ) )->show(); } else { - SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } - case 4033: // POLYHEDRON + case 4033: // POLYHEDRON { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_CreatePolyhedralVolumeDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_CreatePolyhedralVolumeDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } case 4034: // QUADRATIC EDGE case 4035: // QUADRATIC TRIANGLE case 4036: // QUADRATIC QUADRANGLE + case 4136: // BIQUADRATIC QUADRANGLE case 4037: // QUADRATIC TETRAHEDRON case 4038: // QUADRATIC PYRAMID case 4039: // QUADRATIC PENTAHEDRON case 4040: // QUADRATIC HEXAHEDRON + case 4140: // TRIQUADRATIC HEXAHEDRON { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - int type; - - switch (theCommandID) { - case 4034: - type = QUAD_EDGE; break; - case 4035: - type = QUAD_TRIANGLE; break; - case 4036: - type = QUAD_QUADRANGLE; break; - case 4037: - type = QUAD_TETRAHEDRON; break; - case 4038: - type = QUAD_PYRAMID; break; - case 4039: - type = QUAD_PENTAHEDRON; break; - case 4040: - type = QUAD_HEXAHEDRON; - break; - default:; - } - ( new SMESHGUI_AddQuadraticElementDlg( this, type ) )->show(); + EmitSignalDeactivateDialog(); + SMDSAbs_EntityType type; + + switch (theCommandID) { + case 4034: + type = SMDSEntity_Quad_Edge; break; + case 4035: + type = SMDSEntity_Quad_Triangle; break; + case 4036: + type = SMDSEntity_Quad_Quadrangle; break; + case 4136: + type = SMDSEntity_BiQuad_Quadrangle; break; + case 4037: + type = SMDSEntity_Quad_Tetra; break; + case 4038: + type = SMDSEntity_Quad_Pyramid; break; + case 4039: + type = SMDSEntity_Quad_Penta; break; + case 4040: + type = SMDSEntity_Quad_Hexa; + case 4140: + type = SMDSEntity_TriQuad_Hexa; + break; + default:; + } + ( new SMESHGUI_AddQuadraticElementDlg( this, type ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } - case 4041: // REMOVES NODES + case 4041: // REMOVES NODES { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RemoveNodesDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RemoveNodesDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } - case 4042: // REMOVES ELEMENTS + case 4042: // REMOVES ELEMENTS { if(checkLock(aStudy)) break; if( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RemoveElementsDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RemoveElementsDlg( this ) )->show(); } else - { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); - } + { + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } break; } case 4043: { // CLEAR_MESH @@ -2286,7 +3069,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SMESH::IObjectToInterface(IOS); if ( aMesh->_is_nil()) continue; try { - SMESH::UpdateView(SMESH::eErase, IOS->getEntry()); + SMESH::RemoveVisualObjectWithActors(IOS->getEntry(), true); aMesh->Clear(); _PTR(SObject) aMeshSObj = SMESH::FindSObject(aMesh); SMESH::ModifiedMesh( aMeshSObj, false, true); @@ -2296,57 +3079,95 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) for ( anIter->InitEx(true); anIter->More(); anIter->Next() ) { _PTR(SObject) so = anIter->Value(); - SMESH::UpdateView(SMESH::eErase, so->GetID().c_str()); + SMESH::RemoveVisualObjectWithActors(so->GetID().c_str(), true); } } catch (const SALOME::SALOME_Exception& S_ex){ - wc.suspend(); - SalomeApp_Tools::QtCatchCorbaException(S_ex); - wc.resume(); + wc.suspend(); + SalomeApp_Tools::QtCatchCorbaException(S_ex); + wc.resume(); } } SMESH::UpdateView(); updateObjBrowser(); break; } - case 4051: // RENUMBERING NODES + case 4044: // REMOVE ORPHAN NODES + { + if(checkLock(aStudy)) break; + SALOME_ListIO selected; + if( LightApp_SelectionMgr *aSel = SMESHGUI::selectionMgr() ) + aSel->selectedObjects( selected ); + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) anIO = selected.First(); + SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(anIO); + if ( !aMesh->_is_nil() ) { + bool confirm = SUIT_MessageBox::question( SMESHGUI::desktop(), + tr( "SMESH_WARNING" ), + tr( "REMOVE_ORPHAN_NODES_QUESTION"), + SUIT_MessageBox::Yes | + SUIT_MessageBox::No, + SUIT_MessageBox::No ) == SUIT_MessageBox::Yes; + if( confirm ) { + try { + SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); + int removed = aMeshEditor->RemoveOrphanNodes(); + SUIT_MessageBox::information(SMESHGUI::desktop(), + tr("SMESH_INFORMATION"), + tr("NB_NODES_REMOVED").arg(removed)); + if ( removed > 0 ) { + SMESH::UpdateView(); + SMESHGUI::Modified(); + } + } + catch (const SALOME::SALOME_Exception& S_ex) { + SalomeApp_Tools::QtCatchCorbaException(S_ex); + } + catch (...) { + } + } + } + } + break; + } + case 4051: // RENUMBERING NODES { if(checkLock(aStudy)) break; if( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RenumberingDlg( this, 0 ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RenumberingDlg( this, 0 ) )->show(); } else - { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); - } + { + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } break; } - case 4052: // RENUMBERING ELEMENTS + case 4052: // RENUMBERING ELEMENTS { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RenumberingDlg( this, 1 ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RenumberingDlg( this, 1 ) )->show(); } else - { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); - } + { + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } break; } case 4061: // TRANSLATION { if(checkLock(aStudy)) break; if ( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_TranslationDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_TranslationDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2354,12 +3175,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if( vtkwnd ) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_RotationDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_RotationDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2367,12 +3188,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if(vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_SymmetryDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_SymmetryDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2380,12 +3201,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if(vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_SewingDlg( this ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_SewingDlg( this ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2393,12 +3214,12 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if(checkLock(aStudy)) break; if(vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_EditMeshDlg( this, 0 ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_MergeDlg( this, 0 ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2406,11 +3227,11 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) { if (checkLock(aStudy)) break; if (vtkwnd) { - EmitSignalDeactivateDialog(); - ( new SMESHGUI_EditMeshDlg( this, 1 ) )->show(); + EmitSignalDeactivateDialog(); + ( new SMESHGUI_MergeDlg( this, 1 ) )->show(); } else { - SUIT_MessageBox::warning(SMESHGUI::desktop(), - tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); } break; } @@ -2419,6 +3240,34 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) startOperation( 4067 ); break; + case 4068: // SCALE + { + if(checkLock(aStudy)) break; + if ( vtkwnd ) { + EmitSignalDeactivateDialog(); + ( new SMESHGUI_ScaleDlg( this ) )->show(); + } + else { + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } + break; + } + + case 4069: // DUPLICATE NODES + { + if(checkLock(aStudy)) break; + if ( vtkwnd ) { + EmitSignalDeactivateDialog(); + ( new SMESHGUI_DuplicateNodesDlg( this ) )->show(); + } + else { + SUIT_MessageBox::warning(SMESHGUI::desktop(), + tr("SMESH_WRN_WARNING"), tr("SMESH_WRN_VIEWER_VTK")); + } + break; + } + case 5105: // Library of selection filters { static QList aTypes; @@ -2437,7 +3286,7 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) } break; - case 6017: // CONTROLS + case 6017: // CONTROLS case 6016: case 6015: case 6014: @@ -2453,33 +3302,43 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) case 6005: case 6009: case 6021: + case 6022: + case 6023: + case 6024: + case 6025: + case 6026: + case 6027: + case 6028: + case 6029: + case 6030: + case 6031: if ( vtkwnd ) { LightApp_SelectionMgr* mgr = selectionMgr(); SALOME_ListIO selected; mgr->selectedObjects( selected ); if ( selected.Extent() == 1 && selected.First()->hasEntry() ) { - _PTR(SObject) SO = aStudy->FindObjectID( selected.First()->getEntry() ); - if ( SO ) { - CORBA::Object_var aObject = SMESH::SObjectToObject( SO ); - SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( aObject ); - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( aObject ); - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( aObject ); - if ( !aMesh->_is_nil() || !aSubMesh->_is_nil() || !aGroup->_is_nil() ) { - ::Control( theCommandID ); - break; - } - } + _PTR(SObject) SO = aStudy->FindObjectID( selected.First()->getEntry() ); + if ( SO ) { + CORBA::Object_var aObject = SMESH::SObjectToObject( SO ); + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( aObject ); + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( aObject ); + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( aObject ); + if ( !aMesh->_is_nil() || !aSubMesh->_is_nil() || !aGroup->_is_nil() ) { + ::Control( theCommandID ); + break; + } + } } SUIT_MessageBox::warning(desktop(), - tr( "SMESH_WRN_WARNING" ), - tr( "SMESH_BAD_SELECTION" ) ); + tr( "SMESH_WRN_WARNING" ), + tr( "SMESH_BAD_SELECTION" ) ); break; } else { SUIT_MessageBox::warning(desktop(), - tr( "SMESH_WRN_WARNING" ), - tr( "NOT_A_VTK_VIEWER" ) ); + tr( "SMESH_WRN_WARNING" ), + tr( "NOT_A_VTK_VIEWER" ) ); } break; case 9010: @@ -2490,11 +3349,11 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) SALOME_ListIteratorOfListIO it(selected); for( ; it.More(); it.Next()) { Handle(SALOME_InteractiveObject) anIObject = it.Value(); - if(anIObject->hasEntry()) { - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ - anActor->SetPointsLabeled( !anActor->GetPointsLabeled() ); - } - } + if(anIObject->hasEntry()) { + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ + anActor->SetPointsLabeled( !anActor->GetPointsLabeled() ); + } + } } break; } @@ -2503,15 +3362,25 @@ bool SMESHGUI::OnGUIEvent( int theCommandID ) LightApp_SelectionMgr* mgr = selectionMgr(); SALOME_ListIO selected; mgr->selectedObjects( selected ); - if (selected.Extent() == 1) { - Handle(SALOME_InteractiveObject) anIObject = selected.First(); - if(anIObject->hasEntry()) - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ - anActor->SetCellsLabeled( !anActor->GetCellsLabeled() ); - } + SALOME_ListIteratorOfListIO it(selected); + for( ; it.More(); it.Next()) { + Handle(SALOME_InteractiveObject) anIObject = it.Value(); + if(anIObject->hasEntry()) + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(anIObject->getEntry())){ + anActor->SetCellsLabeled( !anActor->GetCellsLabeled() ); + } } break; } + case 501: + case 502: + { + int page = theCommandID == 501 ? SMESHGUI_MeasureDlg::MinDistance : SMESHGUI_MeasureDlg::BoundingBox; + EmitSignalDeactivateDialog(); + SMESHGUI_MeasureDlg* dlg = new SMESHGUI_MeasureDlg( SMESHGUI::desktop(), page ); + dlg->show(); + break; + } } anApp->updateActions(); //SRN: To update a Save button in the toolbar @@ -2567,7 +3436,8 @@ void SMESHGUI::BuildPresentation( const Handle(SALOME_InteractiveObject) & theIO // function : createSMESHAction // purpose : //======================================================================= -void SMESHGUI::createSMESHAction( const int id, const QString& po_id, const QString& icon_id, const int key, const bool toggle ) +void SMESHGUI::createSMESHAction( const int id, const QString& po_id, const QString& icon_id, + const int key, const bool toggle, const QString& shortcutAction ) { QIcon icon; QWidget* parent = application()->desktop(); @@ -2584,7 +3454,8 @@ void SMESHGUI::createSMESHAction( const int id, const QString& po_id, const QStr menu = tr( QString( "MEN_%1" ).arg( po_id ).toLatin1().data() ), status_bar = tr( QString( "STB_%1" ).arg( po_id ).toLatin1().data() ); - createAction( id, tooltip, icon, menu, status_bar, key, parent, toggle, this, SLOT( OnGUIEvent() ) ); + createAction( id, tooltip, icon, menu, status_bar, key, parent, + toggle, this, SLOT( OnGUIEvent() ), shortcutAction ); } //======================================================================= @@ -2595,7 +3466,7 @@ void SMESHGUI::createPopupItem( const int id, const QString& clients, const QString& types, const QString& theRule, - const int pId ) + const int pId ) { int parentId = pId; if( pId!=-1 ) @@ -2638,18 +3509,25 @@ void SMESHGUI::initialize( CAM_Application* app ) // ----- create actions -------------- - createSMESHAction( 111, "DAT", "", (Qt::CTRL+Qt::Key_B) ); - createSMESHAction( 112, "UNV", "", (Qt::CTRL+Qt::Key_U) ); - createSMESHAction( 113, "MED", "", (Qt::CTRL+Qt::Key_M) ); + createSMESHAction( 111, "IMPORT_DAT", "", (Qt::CTRL+Qt::Key_B) ); + createSMESHAction( 112, "IMPORT_UNV", "", (Qt::CTRL+Qt::Key_U) ); + createSMESHAction( 113, "IMPORT_MED", "", (Qt::CTRL+Qt::Key_M) ); createSMESHAction( 114, "NUM" ); + createSMESHAction( 115, "IMPORT_STL" ); + createSMESHAction( 116, "IMPORT_CGNS" ); + createSMESHAction( 117, "IMPORT_SAUV" ); createSMESHAction( 121, "DAT" ); createSMESHAction( 122, "MED" ); createSMESHAction( 123, "UNV" ); createSMESHAction( 140, "STL" ); + createSMESHAction( 142, "CGNS" ); + createSMESHAction( 144, "SAUV" ); createSMESHAction( 124, "EXPORT_DAT" ); createSMESHAction( 125, "EXPORT_MED" ); createSMESHAction( 126, "EXPORT_UNV" ); createSMESHAction( 141, "EXPORT_STL" ); + createSMESHAction( 143, "EXPORT_CGNS" ); + createSMESHAction( 145, "EXPORT_SAUV" ); createSMESHAction( 150, "FILE_INFO" ); createSMESHAction( 33, "DELETE", "ICON_DELETE", Qt::Key_Delete ); createSMESHAction( 5105, "SEL_FILTER_LIB" ); @@ -2657,8 +3535,11 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 702, "CREATE_MESH", "ICON_DLG_INIT_MESH" ); createSMESHAction( 703, "CREATE_SUBMESH", "ICON_DLG_ADD_SUBMESH" ); createSMESHAction( 704, "EDIT_MESHSUBMESH","ICON_DLG_EDIT_MESH" ); + createSMESHAction( 705, "COPY_MESH", "ICON_COPY_MESH" ); createSMESHAction( 710, "BUILD_COMPOUND", "ICON_BUILD_COMPOUND" ); createSMESHAction( 711, "PRECOMPUTE", "ICON_PRECOMPUTE" ); + createSMESHAction( 712, "EVALUATE", "ICON_COMPUTE" ); + createSMESHAction( 713, "MESH_ORDER", "ICON_COMPUTE" ); createSMESHAction( 806, "CREATE_GEO_GROUP","ICON_CREATE_GEO_GROUP" ); createSMESHAction( 801, "CREATE_GROUP", "ICON_CREATE_GROUP" ); createSMESHAction( 802, "CONSTRUCT_GROUP", "ICON_CONSTRUCT_GROUP" ); @@ -2672,11 +3553,22 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 814, "UNDERLYING_ELEMS","ICON_UNDERLYING_ELEMS" ); createSMESHAction( 813, "DEL_GROUP", "ICON_DEL_GROUP" ); createSMESHAction( 900, "ADV_INFO", "ICON_ADV_INFO" ); - createSMESHAction( 902, "STD_INFO", "ICON_STD_INFO" ); - createSMESHAction( 903, "WHAT_IS", "ICON_WHAT_IS" ); + //createSMESHAction( 902, "STD_INFO", "ICON_STD_INFO" ); + //createSMESHAction( 903, "WHAT_IS", "ICON_WHAT_IS" ); // VSR: issue #0021242 (eliminate "Mesh Element Information" command) + createSMESHAction( 904, "FIND_ELEM", "ICON_FIND_ELEM" ); createSMESHAction( 6001, "LENGTH", "ICON_LENGTH", 0, true ); createSMESHAction( 6002, "FREE_EDGE", "ICON_FREE_EDGE", 0, true ); createSMESHAction( 6021, "FREE_FACES", "ICON_FREE_FACES", 0, true ); + createSMESHAction( 6022, "MAX_ELEMENT_LENGTH_2D", "ICON_MAX_ELEMENT_LENGTH_2D", 0, true ); + createSMESHAction( 6023, "MAX_ELEMENT_LENGTH_3D", "ICON_MAX_ELEMENT_LENGTH_3D", 0, true ); + createSMESHAction( 6024, "BARE_BORDER_VOLUME", "ICON_BARE_BORDER_VOLUME", 0, true ); + createSMESHAction( 6025, "BARE_BORDER_FACE", "ICON_BARE_BORDER_FACE", 0, true ); + createSMESHAction( 6026, "OVER_CONSTRAINED_VOLUME","ICON_OVER_CONSTRAINED_VOLUME", 0, true ); + createSMESHAction( 6027, "OVER_CONSTRAINED_FACE", "ICON_OVER_CONSTRAINED_FACE", 0, true ); + createSMESHAction( 6028, "EQUAL_NODE", "ICON_EQUAL_NODE", 0, true ); + createSMESHAction( 6029, "EQUAL_EDGE", "ICON_EQUAL_EDGE", 0, true ); + createSMESHAction( 6030, "EQUAL_FACE", "ICON_EQUAL_FACE", 0, true ); + createSMESHAction( 6031, "EQUAL_VOLUME", "ICON_EQUAL_VOLUME", 0, true ); createSMESHAction( 6003, "FREE_BORDER", "ICON_FREE_EDGE_2D", 0, true ); createSMESHAction( 6004, "CONNECTION", "ICON_CONNECTION", 0, true ); createSMESHAction( 6005, "FREE_NODE", "ICON_FREE_NODE", 0, true ); @@ -2690,15 +3582,31 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 6018, "LENGTH_2D", "ICON_LENGTH_2D", 0, true ); createSMESHAction( 6019, "CONNECTION_2D", "ICON_CONNECTION_2D", 0, true ); createSMESHAction( 6009, "VOLUME_3D", "ICON_VOLUME_3D", 0, true ); - createSMESHAction( 400, "NODE", "ICON_DLG_NODE" ); - createSMESHAction( 401, "EDGE", "ICON_DLG_EDGE" ); + createSMESHAction( 4000, "NODE", "ICON_DLG_NODE" ); + createSMESHAction( 4009, "ELEM0D", "ICON_DLG_ELEM0D" ); + createSMESHAction( 4008, "BALL", "ICON_DLG_BALL" ); + createSMESHAction( 4010, "EDGE", "ICON_DLG_EDGE" ); createSMESHAction( 4021, "TRIANGLE", "ICON_DLG_TRIANGLE" ); createSMESHAction( 4022, "QUAD", "ICON_DLG_QUADRANGLE" ); createSMESHAction( 4023, "POLYGON", "ICON_DLG_POLYGON" ); createSMESHAction( 4031, "TETRA", "ICON_DLG_TETRAS" ); createSMESHAction( 4032, "HEXA", "ICON_DLG_HEXAS" ); - createSMESHAction( 4041, "REMOVE_NODES", "ICON_DLG_REM_NODE" ); - createSMESHAction( 4042, "REMOVE_ELEMENTS", "ICON_DLG_REM_ELEMENT" ); + createSMESHAction( 4133, "PENTA", "ICON_DLG_PENTA" ); + createSMESHAction( 4134, "PYRAMID", "ICON_DLG_PYRAMID" ); + createSMESHAction( 4135, "OCTA", "ICON_DLG_OCTA" ); + createSMESHAction( 4033, "POLYHEDRON", "ICON_DLG_POLYHEDRON" ); + createSMESHAction( 4034, "QUADRATIC_EDGE", "ICON_DLG_QUADRATIC_EDGE" ); + createSMESHAction( 4035, "QUADRATIC_TRIANGLE", "ICON_DLG_QUADRATIC_TRIANGLE" ); + createSMESHAction( 4036, "QUADRATIC_QUADRANGLE", "ICON_DLG_QUADRATIC_QUADRANGLE" ); + createSMESHAction( 4136, "BIQUADRATIC_QUADRANGLE", "ICON_DLG_BIQUADRATIC_QUADRANGLE" ); + createSMESHAction( 4037, "QUADRATIC_TETRAHEDRON", "ICON_DLG_QUADRATIC_TETRAHEDRON" ); + createSMESHAction( 4038, "QUADRATIC_PYRAMID", "ICON_DLG_QUADRATIC_PYRAMID" ); + createSMESHAction( 4039, "QUADRATIC_PENTAHEDRON", "ICON_DLG_QUADRATIC_PENTAHEDRON" ); + createSMESHAction( 4040, "QUADRATIC_HEXAHEDRON", "ICON_DLG_QUADRATIC_HEXAHEDRON" ); + createSMESHAction( 4140, "TRIQUADRATIC_HEXAHEDRON", "ICON_DLG_TRIQUADRATIC_HEXAHEDRON" ); + createSMESHAction( 4041, "REMOVE_NODES", "ICON_DLG_REM_NODE" ); + createSMESHAction( 4042, "REMOVE_ELEMENTS", "ICON_DLG_REM_ELEMENT" ); + createSMESHAction( 4044, "REMOVE_ORPHAN_NODES", "ICON_DLG_REM_ORPHAN_NODES" ); createSMESHAction( 4043, "CLEAR_MESH" , "ICON_CLEAR_MESH" ); createSMESHAction( 4051, "RENUM_NODES", "ICON_DLG_RENUMBERING_NODES" ); createSMESHAction( 4052, "RENUM_ELEMENTS", "ICON_DLG_RENUMBERING_ELEMENTS" ); @@ -2708,8 +3616,9 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 4064, "SEW", "ICON_SMESH_SEWING_FREEBORDERS" ); createSMESHAction( 4065, "MERGE", "ICON_SMESH_MERGE_NODES" ); createSMESHAction( 4066, "MERGE_ELEMENTS", "ICON_DLG_MERGE_ELEMENTS" ); - createSMESHAction( 4067, "MESH_THROU_POINT","ICON_DLG_MESH_THROU_POINT" ); - createSMESHAction( 406, "MOVE", "ICON_DLG_MOVE_NODE" ); + createSMESHAction( 4067, "MESH_THROU_POINT","ICON_DLG_MOVE_NODE" ); + createSMESHAction( 4068, "SCALE", "ICON_DLG_MESH_SCALE" ); + createSMESHAction( 4069, "DUPLICATE_NODES", "ICON_SMESH_DUPLICATE_NODES" ); createSMESHAction( 407, "INV", "ICON_DLG_MESH_DIAGONAL" ); createSMESHAction( 408, "UNION2", "ICON_UNION2TRI" ); createSMESHAction( 409, "ORIENT", "ICON_DLG_MESH_ORIENTATION" ); @@ -2721,20 +3630,33 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 415, "MAP", "ICON_MAP" ); createSMESHAction( 416, "EXTRUSION_ALONG", "ICON_EXTRUSION_ALONG" ); createSMESHAction( 417, "CONV_TO_QUAD", "ICON_CONV_TO_QUAD" ); + createSMESHAction( 418, "2D_FROM_3D", "ICON_2D_FROM_3D" ); + createSMESHAction( 419, "SPLIT_TO_TETRA", "ICON_SPLIT_TO_TETRA" ); + createSMESHAction( 420, "REORIENT_2D", "ICON_REORIENT_2D" ); createSMESHAction( 200, "RESET" ); createSMESHAction( 201, "SCALAR_BAR_PROP" ); + createSMESHAction( 2021, "SAVE_DISTRIBUTION" ); + createSMESHAction( 2022, "SHOW_DISTRIBUTION","",0, true ); +#ifndef DISABLE_PLOT2DVIEWER + createSMESHAction( 2023, "PLOT_DISTRIBUTION" ); +#endif createSMESHAction( 211, "WIRE", "ICON_WIRE", 0, true ); createSMESHAction( 212, "SHADE", "ICON_SHADE", 0, true ); createSMESHAction( 213, "SHRINK", "ICON_SHRINK", 0, true ); createSMESHAction( 214, "UPDATE", "ICON_UPDATE" ); createSMESHAction( 215, "NODES", "ICON_POINTS", 0, true ); + createSMESHAction( 222, "BALLS", "ICON_DLG_BALL", 0, true ); + createSMESHAction( 216, "ELEMS0D", "ICON_DLG_ELEM0D", 0, true ); createSMESHAction( 217, "EDGES", "ICON_DLG_EDGE", 0, true ); createSMESHAction( 218, "FACES", "ICON_DLG_TRIANGLE", 0, true ); createSMESHAction( 219, "VOLUMES", "ICON_DLG_TETRAS", 0, true ); createSMESHAction( 220, "ALL" ); createSMESHAction( 221, "FACE_ORIENTATION", "", 0, true ); + + createSMESHAction( 231, "LINE_REPRESENTATION", "", 0, true ); + createSMESHAction( 232, "ARC_REPRESENTATION", "", 0, true ); + createSMESHAction( 1100, "EDIT_HYPO" ); - createSMESHAction( 1101, "RENAME", "", Qt::Key_F2 ); createSMESHAction( 1102, "UNASSIGN" ); createSMESHAction( 9010, "NUM_NODES", "", 0, true ); createSMESHAction( 9011, "NUM_ELEMENTS", "", 0, true ); @@ -2747,31 +3669,31 @@ void SMESHGUI::initialize( CAM_Application* app ) createSMESHAction( 1137, "DISABLE_AUTO_COLOR" ); createSMESHAction( 2000, "CTRL" ); - createSMESHAction( 300, "ERASE" ); - createSMESHAction( 301, "DISPLAY" ); + createSMESHAction( 501, "MEASURE_MIN_DIST", "ICON_MEASURE_MIN_DIST" ); + createSMESHAction( 502, "MEASURE_BND_BOX", "ICON_MEASURE_BND_BOX" ); + + createSMESHAction( 300, "HIDE" ); + createSMESHAction( 301, "SHOW" ); createSMESHAction( 302, "DISPLAY_ONLY" ); - createSMESHAction( 4033, "POLYHEDRON", "ICON_DLG_POLYHEDRON" ); - createSMESHAction( 4034, "QUADRATIC_EDGE", "ICON_DLG_QUADRATIC_EDGE" ); - createSMESHAction( 4035, "QUADRATIC_TRIANGLE", "ICON_DLG_QUADRATIC_TRIANGLE" ); - createSMESHAction( 4036, "QUADRATIC_QUADRANGLE", "ICON_DLG_QUADRATIC_QUADRANGLE" ); - createSMESHAction( 4037, "QUADRATIC_TETRAHEDRON", "ICON_DLG_QUADRATIC_TETRAHEDRON" ); - createSMESHAction( 4038, "QUADRATIC_PYRAMID", "ICON_DLG_QUADRATIC_PYRAMID" ); - createSMESHAction( 4039, "QUADRATIC_PENTAHEDRON", "ICON_DLG_QUADRATIC_PENTAHEDRON" ); - createSMESHAction( 4040, "QUADRATIC_HEXAHEDRON", "ICON_DLG_QUADRATIC_HEXAHEDRON" ); // ----- create menu -------------- - int fileId = createMenu( tr( "MEN_FILE" ), -1, 1 ), - editId = createMenu( tr( "MEN_EDIT" ), -1, 3 ), - toolsId = createMenu( tr( "MEN_TOOLS" ), -1, 5, 50 ), - meshId = createMenu( tr( "MEN_MESH" ), -1, 70, 10 ), - ctrlId = createMenu( tr( "MEN_CTRL" ), -1, 60, 10 ), - modifyId = createMenu( tr( "MEN_MODIFY" ), -1, 40, 10 ), - viewId = createMenu( tr( "MEN_VIEW" ), -1, 2 ); + int fileId = createMenu( tr( "MEN_FILE" ), -1, 1 ), + editId = createMenu( tr( "MEN_EDIT" ), -1, 3 ), + toolsId = createMenu( tr( "MEN_TOOLS" ), -1, 5, 50 ), + meshId = createMenu( tr( "MEN_MESH" ), -1, 70, 10 ), + ctrlId = createMenu( tr( "MEN_CTRL" ), -1, 60, 10 ), + modifyId = createMenu( tr( "MEN_MODIFY" ), -1, 40, 10 ), + measureId = createMenu( tr( "MEN_MEASURE" ), -1, 50, 10 ), + viewId = createMenu( tr( "MEN_VIEW" ), -1, 2 ); createMenu( separator(), fileId ); int importId = createMenu( tr( "MEN_IMPORT" ), fileId, -1, 10 ), exportId = createMenu( tr( "MEN_EXPORT" ), fileId, -1, 10 ), + nodeId = createMenu( tr( "MEN_NODE_CTRL" ), ctrlId, -1, 10 ), + edgeId = createMenu( tr( "MEN_EDGE_CTRL" ), ctrlId, -1, 10 ), + faceId = createMenu( tr( "MEN_FACE_CTRL" ), ctrlId, -1, 10 ), + volumeId = createMenu( tr( "MEN_VOLUME_CTRL" ), ctrlId, -1, 10 ), addId = createMenu( tr( "MEN_ADD" ), modifyId, 402 ), removeId = createMenu( tr( "MEN_REMOVE" ), modifyId, 403 ), renumId = createMenu( tr( "MEN_RENUM" ), modifyId, 404 ), @@ -2780,12 +3702,19 @@ void SMESHGUI::initialize( CAM_Application* app ) createMenu( 111, importId, -1 ); createMenu( 112, importId, -1 ); createMenu( 113, importId, -1 ); - + createMenu( 115, importId, -1 ); +#ifdef WITH_CGNS + createMenu( 116, importId, -1 ); +#endif + createMenu( 117, importId, -1 ); createMenu( 121, exportId, -1 ); createMenu( 122, exportId, -1 ); createMenu( 123, exportId, -1 ); - createMenu( 140, exportId, -1 ); // export to stl STL - + createMenu( 140, exportId, -1 ); // export to STL +#ifdef WITH_CGNS + createMenu( 142, exportId, -1 ); // export to CGNS +#endif + createMenu( 144, exportId, -1 ); // export to SAUV createMenu( separator(), fileId, 10 ); createMenu( 33, editId, -1 ); @@ -2796,9 +3725,12 @@ void SMESHGUI::initialize( CAM_Application* app ) createMenu( 703, meshId, -1 ); createMenu( 704, meshId, -1 ); createMenu( 710, meshId, -1 ); + createMenu( 705, meshId, -1 ); createMenu( separator(), meshId, -1 ); createMenu( 701, meshId, -1 ); createMenu( 711, meshId, -1 ); + createMenu( 712, meshId, -1 ); + createMenu( 713, meshId, -1 ); createMenu( separator(), meshId, -1 ); createMenu( 801, meshId, -1 ); createMenu( 806, meshId, -1 ); @@ -2812,52 +3744,69 @@ void SMESHGUI::initialize( CAM_Application* app ) createMenu( separator(), meshId, -1 ); createMenu( 814, meshId, -1 ); createMenu( separator(), meshId, -1 ); - createMenu( 813, meshId, -1 ); - createMenu( separator(), meshId, -1 ); createMenu( 900, meshId, -1 ); - createMenu( 902, meshId, -1 ); - createMenu( 903, meshId, -1 ); + //createMenu( 902, meshId, -1 ); + //createMenu( 903, meshId, -1 ); // VSR: issue #0021242 (eliminate "Mesh Element Information" command) + createMenu( 904, meshId, -1 ); createMenu( separator(), meshId, -1 ); - createMenu( 6003, ctrlId, -1 ); - createMenu( 6001, ctrlId, -1 ); - createMenu( 6004, ctrlId, -1 ); - createMenu( separator(), ctrlId, -1 ); - createMenu( 6005, ctrlId, -1 ); - createMenu( 6002, ctrlId, -1 ); - createMenu( 6018, ctrlId, -1 ); - createMenu( 6019, ctrlId, -1 ); - createMenu( 6011, ctrlId, -1 ); - createMenu( 6012, ctrlId, -1 ); - createMenu( 6013, ctrlId, -1 ); - createMenu( 6014, ctrlId, -1 ); - createMenu( 6015, ctrlId, -1 ); - createMenu( 6016, ctrlId, -1 ); - createMenu( separator(), ctrlId, -1 ); - createMenu( 6017, ctrlId, -1 ); - createMenu( 6009, ctrlId, -1 ); - createMenu( 6021, ctrlId, -1 ); - createMenu( separator(), ctrlId, -1 ); - - createMenu( 400, addId, -1 ); - createMenu( 401, addId, -1 ); + createMenu( 6005, nodeId, -1 ); + createMenu( 6028, nodeId, -1 ); + createMenu( 6002, edgeId, -1 ); + createMenu( 6003, edgeId, -1 ); + createMenu( 6001, edgeId, -1 ); + createMenu( 6004, edgeId, -1 ); + createMenu( 6029, edgeId, -1 ); + createMenu( 6021, faceId, -1 ); + createMenu( 6025, faceId, -1 ); + createMenu( 6027, faceId, -1 ); + createMenu( 6018, faceId, -1 ); + createMenu( 6019, faceId, -1 ); + createMenu( 6011, faceId, -1 ); + createMenu( 6012, faceId, -1 ); + createMenu( 6013, faceId, -1 ); + createMenu( 6014, faceId, -1 ); + createMenu( 6015, faceId, -1 ); + createMenu( 6016, faceId, -1 ); + createMenu( 6022, faceId, -1 ); + createMenu( 6030, faceId, -1 ); + createMenu( 6017, volumeId, -1 ); + createMenu( 6009, volumeId, -1 ); + createMenu( 6023, volumeId, -1 ); + createMenu( 6024, volumeId, -1 ); + createMenu( 6026, volumeId, -1 ); + createMenu( 6031, volumeId, -1 ); + + createMenu( 4000, addId, -1 ); + createMenu( 4009, addId, -1 ); + createMenu( 4008, addId, -1 ); + createMenu( 4010, addId, -1 ); createMenu( 4021, addId, -1 ); createMenu( 4022, addId, -1 ); createMenu( 4023, addId, -1 ); createMenu( 4031, addId, -1 ); createMenu( 4032, addId, -1 ); + createMenu( 4133, addId, -1 ); + createMenu( 4134, addId, -1 ); + createMenu( 4135, addId, -1 ); createMenu( 4033, addId, -1 ); createMenu( separator(), addId, -1 ); createMenu( 4034, addId, -1 ); createMenu( 4035, addId, -1 ); createMenu( 4036, addId, -1 ); + createMenu( 4136, addId, -1 ); createMenu( 4037, addId, -1 ); createMenu( 4038, addId, -1 ); createMenu( 4039, addId, -1 ); createMenu( 4040, addId, -1 ); + createMenu( 4140, addId, -1 ); createMenu( 4041, removeId, -1 ); createMenu( 4042, removeId, -1 ); + createMenu( 4044, removeId, -1 ); + createMenu( separator(), removeId, -1 ); + createMenu( 813, removeId, -1 ); + createMenu( separator(), removeId, -1 ); createMenu( 4043, removeId, -1 ); createMenu( 4051, renumId, -1 ); @@ -2866,24 +3815,30 @@ void SMESHGUI::initialize( CAM_Application* app ) createMenu( 4061, transfId, -1 ); createMenu( 4062, transfId, -1 ); createMenu( 4063, transfId, -1 ); + createMenu( 4068, transfId, -1 ); createMenu( 4064, transfId, -1 ); createMenu( 4065, transfId, -1 ); createMenu( 4066, transfId, -1 ); + createMenu( 4069, transfId, -1 ); - createMenu( 406, modifyId, -1 ); createMenu( 4067,modifyId, -1 ); createMenu( 407, modifyId, -1 ); createMenu( 408, modifyId, -1 ); createMenu( 409, modifyId, -1 ); + createMenu( 420, modifyId, -1 ); createMenu( 410, modifyId, -1 ); createMenu( 411, modifyId, -1 ); + createMenu( 419, modifyId, -1 ); createMenu( 412, modifyId, -1 ); createMenu( 413, modifyId, -1 ); createMenu( 416, modifyId, -1 ); createMenu( 414, modifyId, -1 ); createMenu( 415, modifyId, -1 ); createMenu( 417, modifyId, -1 ); + createMenu( 418, modifyId, -1 ); + createMenu( 501, measureId, -1 ); + createMenu( 502, measureId, -1 ); createMenu( 214, viewId, -1 ); // ----- create toolbars -------------- @@ -2897,9 +3852,12 @@ void SMESHGUI::initialize( CAM_Application* app ) createTool( 703, meshTb ); createTool( 704, meshTb ); createTool( 710, meshTb ); + createTool( 705, meshTb ); createTool( separator(), meshTb ); createTool( 701, meshTb ); createTool( 711, meshTb ); + createTool( 712, meshTb ); + createTool( 713, meshTb ); createTool( separator(), meshTb ); createTool( 801, meshTb ); createTool( 806, meshTb ); @@ -2908,16 +3866,23 @@ void SMESHGUI::initialize( CAM_Application* app ) //createTool( 815, meshTb ); createTool( separator(), meshTb ); createTool( 900, meshTb ); - createTool( 902, meshTb ); - createTool( 903, meshTb ); + //createTool( 902, meshTb ); + //createTool( 903, meshTb ); // VSR: issue #0021242 (eliminate "Mesh Element Information" command) + createTool( 904, meshTb ); createTool( separator(), meshTb ); - createTool( 6001, ctrlTb ); + createTool( 6005, ctrlTb ); + createTool( 6028, ctrlTb ); + createTool( separator(), ctrlTb ); + createTool( 6002, ctrlTb ); createTool( 6003, ctrlTb ); + createTool( 6001, ctrlTb ); createTool( 6004, ctrlTb ); + createTool( 6029, ctrlTb ); createTool( separator(), ctrlTb ); - createTool( 6005, ctrlTb ); - createTool( 6002, ctrlTb ); + createTool( 6021, ctrlTb ); + createTool( 6025, ctrlTb ); + createTool( 6027, ctrlTb ); createTool( 6018, ctrlTb ); createTool( 6019, ctrlTb ); createTool( 6011, ctrlTb ); @@ -2926,31 +3891,44 @@ void SMESHGUI::initialize( CAM_Application* app ) createTool( 6014, ctrlTb ); createTool( 6015, ctrlTb ); createTool( 6016, ctrlTb ); + createTool( 6022, ctrlTb ); + createTool( 6030, ctrlTb ); createTool( separator(), ctrlTb ); createTool( 6017, ctrlTb ); createTool( 6009, ctrlTb ); - createTool( 6021, ctrlTb ); + createTool( 6023, ctrlTb ); + createTool( 6024, ctrlTb ); + createTool( 6026, ctrlTb ); + createTool( 6031, ctrlTb ); createTool( separator(), ctrlTb ); - createTool( 400, addRemTb ); - createTool( 401, addRemTb ); + createTool( 4000, addRemTb ); + createTool( 4009, addRemTb ); + createTool( 4008, addRemTb ); + createTool( 4010, addRemTb ); createTool( 4021, addRemTb ); createTool( 4022, addRemTb ); createTool( 4023, addRemTb ); createTool( 4031, addRemTb ); createTool( 4032, addRemTb ); + createTool( 4133, addRemTb ); + createTool( 4134, addRemTb ); + createTool( 4135, addRemTb ); createTool( 4033, addRemTb ); createTool( separator(), addRemTb ); createTool( 4034, addRemTb ); createTool( 4035, addRemTb ); createTool( 4036, addRemTb ); + createTool( 4136, addRemTb ); createTool( 4037, addRemTb ); createTool( 4038, addRemTb ); createTool( 4039, addRemTb ); createTool( 4040, addRemTb ); + createTool( 4140, addRemTb ); createTool( separator(), addRemTb ); createTool( 4041, addRemTb ); createTool( 4042, addRemTb ); + createTool( 4044, addRemTb ); createTool( 4043, addRemTb ); createTool( separator(), addRemTb ); createTool( 4051, addRemTb ); @@ -2959,24 +3937,28 @@ void SMESHGUI::initialize( CAM_Application* app ) createTool( 4061, addRemTb ); createTool( 4062, addRemTb ); createTool( 4063, addRemTb ); + createTool( 4068, addRemTb ); createTool( 4064, addRemTb ); createTool( 4065, addRemTb ); createTool( 4066, addRemTb ); + createTool( 4069, addRemTb ); createTool( separator(), addRemTb ); - createTool( 406, modifyTb ); createTool( 4067,modifyTb ); createTool( 407, modifyTb ); createTool( 408, modifyTb ); createTool( 409, modifyTb ); + createTool( 420, modifyTb ); createTool( 410, modifyTb ); createTool( 411, modifyTb ); + createTool( 419, modifyTb ); createTool( 412, modifyTb ); createTool( 413, modifyTb ); createTool( 416, modifyTb ); createTool( 414, modifyTb ); createTool( 415, modifyTb ); createTool( 417, modifyTb ); + createTool( 418, modifyTb ); createTool( 214, dispModeTb ); @@ -2985,59 +3967,83 @@ void SMESHGUI::initialize( CAM_Application* app ) myRules.clear(); QString OB = "'ObjectBrowser'", - View = "'" + SVTK_Viewer::Type() + "'", - pat = "'%1'", - mesh = pat.arg( SMESHGUI_Selection::typeName( MESH ) ), - group = pat.arg( SMESHGUI_Selection::typeName( GROUP ) ), - hypo = pat.arg( SMESHGUI_Selection::typeName( HYPOTHESIS ) ), - algo = pat.arg( SMESHGUI_Selection::typeName( ALGORITHM ) ), - elems = QString( "'%1' '%2' '%3' '%4' '%5' '%6'" ). + View = "'" + SVTK_Viewer::Type() + "'", + pat = "'%1'", + mesh = pat.arg( SMESHGUI_Selection::typeName( MESH ) ), + group = pat.arg( SMESHGUI_Selection::typeName( GROUP ) ), + hypo = pat.arg( SMESHGUI_Selection::typeName( HYPOTHESIS ) ), + algo = pat.arg( SMESHGUI_Selection::typeName( ALGORITHM ) ), + elems = QString( "'%1' '%2' '%3' '%4' '%5' '%6'" ). arg( SMESHGUI_Selection::typeName( SUBMESH_VERTEX ) ). - arg( SMESHGUI_Selection::typeName( SUBMESH_EDGE ) ). - arg( SMESHGUI_Selection::typeName( SUBMESH_FACE ) ). - arg( SMESHGUI_Selection::typeName( SUBMESH_SOLID ) ). - arg( SMESHGUI_Selection::typeName( SUBMESH_COMPOUND ) ). - arg( SMESHGUI_Selection::typeName( SUBMESH ) ), + arg( SMESHGUI_Selection::typeName( SUBMESH_EDGE ) ). + arg( SMESHGUI_Selection::typeName( SUBMESH_FACE ) ). + arg( SMESHGUI_Selection::typeName( SUBMESH_SOLID ) ). + arg( SMESHGUI_Selection::typeName( SUBMESH_COMPOUND ) ). + arg( SMESHGUI_Selection::typeName( SUBMESH ) ), subMesh = elems, - mesh_group = mesh + " " + subMesh + " " + group, - hyp_alg = hypo + " " + algo; + mesh_part = mesh + " " + subMesh + " " + group, + mesh_group = mesh + " " + group, + hyp_alg = hypo + " " + algo; // popup for object browser + QString + isInvisible("not( isVisible )"), + isEmpty("numberOfNodes = 0"), + isNotEmpty("numberOfNodes <> 0"), + + // has nodes, edges, etc in VISIBLE! actor + hasNodes("(numberOfNodes > 0 )"),//&& isVisible)"), + hasElems("(count( elemTypes ) > 0)"), + hasDifferentElems("(count( elemTypes ) > 1)"), + hasBalls("({'BallElem'} in elemTypes)"), + hasElems0d("({'Elem0d'} in elemTypes)"), + hasEdges("({'Edge'} in elemTypes)"), + hasFaces("({'Face'} in elemTypes)"), + hasVolumes("({'Volume'} in elemTypes)"); createPopupItem( 150, OB, mesh, "&& selcount=1 && isImported" ); // FILE INFORMATION createPopupItem( 703, OB, mesh, "&& isComputable"); // CREATE_SUBMESH - //createPopupItem( 703, OB, subMesh, "&& isComputable" ); // CREATE_SUBMESH createPopupItem( 704, OB, mesh, "&& isComputable"); // EDIT_MESHSUBMESH createPopupItem( 704, OB, subMesh, "&& isComputable" ); // EDIT_MESHSUBMESH createPopupItem( 803, OB, group ); // EDIT_GROUP - createPopupItem( 815, OB, group, "&& groupType = 'GroupOnGeom'" ); // EDIT_GROUP + createPopupItem( 815, OB, group, "&& groupType != 'Group'" ); // EDIT AS STANDALONE popupMgr()->insert( separator(), -1, 0 ); createPopupItem( 701, OB, mesh, "&& isComputable" ); // COMPUTE - createPopupItem( 711, OB, mesh, "&& isComputable" ); // PRECOMPUTE - createPopupItem( 214, OB, mesh_group ); // UPDATE - createPopupItem( 900, OB, mesh_group ); // ADV_INFO - createPopupItem( 902, OB, mesh ); // STD_INFO - createPopupItem( 903, OB, mesh_group ); // WHAT_IS + createPopupItem( 711, OB, mesh, "&& isComputable && isPreComputable" ); // PRECOMPUTE + createPopupItem( 712, OB, mesh, "&& isComputable" ); // EVALUATE + createPopupItem( 713, OB, mesh, "&& isComputable" ); // MESH ORDER + createPopupItem( 214, OB, mesh_part ); // UPDATE + createPopupItem( 900, OB, mesh_part ); // ADV_INFO + createPopupItem( 904, OB, mesh_group ); // FIND_ELEM popupMgr()->insert( separator(), -1, 0 ); createPopupItem( 801, OB, mesh ); // CREATE_GROUP createPopupItem( 806, OB, mesh ); // CREATE_GEO_GROUP createPopupItem( 802, OB, subMesh ); // CONSTRUCT_GROUP popupMgr()->insert( separator(), -1, 0 ); createPopupItem( 1100, OB, hypo); // EDIT HYPOTHESIS - createPopupItem( 1102, OB, hyp_alg ); // REMOVE HYPOTHESIS / ALGORITHMS - createPopupItem( 1101, OB, mesh_group + " " + hyp_alg ); // RENAME + createPopupItem( 1102, OB, hyp_alg ); // REMOVE HYPOTHESIS / ALGORITHMS popupMgr()->insert( separator(), -1, 0 ); createPopupItem( 4043, OB, mesh ); // CLEAR_MESH popupMgr()->insert( separator(), -1, 0 ); + createPopupItem( 417, OB, mesh + " " + subMesh ); // convert to quadratic + createPopupItem( 418, OB, mesh + " " + group, // create 2D mesh from 3D + "&& dim>=2"); + popupMgr()->insert( separator(), -1, 0 ); QString only_one_non_empty = QString( " && %1=1 && numberOfNodes>0" ).arg( dc ); - - createPopupItem( 125, OB, mesh, only_one_non_empty ); // EXPORT_MED - createPopupItem( 126, OB, mesh, only_one_non_empty ); // EXPORT_UNV - createPopupItem( 141, OB, mesh, only_one_non_empty ); // EXPORT_STL - //createPopupItem( 33, OB, subMesh + " " + group ); // DELETE - createPopupItem( 33, OB, mesh_group + " " + hyp_alg ); // DELETE + QString multiple_non_empty = QString( " && %1>0 && numberOfNodes>0" ).arg( dc ); + QString only_one_2D = only_one_non_empty + " && dim>1"; + + createPopupItem( 125, OB, mesh_group, multiple_non_empty ); // EXPORT_MED + createPopupItem( 126, OB, mesh_group, only_one_non_empty ); // EXPORT_UNV + createPopupItem( 141, OB, mesh_group, only_one_2D ); // EXPORT_STL +#ifdef WITH_CGNS + createPopupItem( 143, OB, mesh_group, multiple_non_empty ); // EXPORT_CGNS +#endif + createPopupItem( 145, OB, mesh_group, multiple_non_empty ); // EXPORT_SAUV + createPopupItem( 33, OB, mesh_part + " " + hyp_alg ); // DELETE + createPopupItem( 813, OB, group ); // DEL_GROUP with contents popupMgr()->insert( separator(), -1, 0 ); // popup for viewer @@ -3046,10 +4052,9 @@ void SMESHGUI::initialize( CAM_Application* app ) createPopupItem( 805, View, elems ); // REMOVE popupMgr()->insert( separator(), -1, 0 ); - createPopupItem( 214, View, mesh_group ); // UPDATE - createPopupItem( 900, View, mesh_group ); // ADV_INFO - createPopupItem( 902, View, mesh ); // STD_INFO - createPopupItem( 903, View, mesh_group ); // WHAT_IS + createPopupItem( 214, View, mesh_part ); // UPDATE + createPopupItem( 900, View, mesh_part ); // ADV_INFO + createPopupItem( 904, View, mesh ); // FIND_ELEM popupMgr()->insert( separator(), -1, 0 ); createPopupItem( 1136, OB + " " + View, mesh, "&& (not isAutoColor)" ); // AUTO_COLOR @@ -3057,22 +4062,9 @@ void SMESHGUI::initialize( CAM_Application* app ) popupMgr()->insert( separator(), -1, 0 ); int anId; - QString - isInvisible("not( isVisible )"), - isEmpty("numberOfNodes = 0"), - isNotEmpty("numberOfNodes <> 0"), - - // has nodes, edges, etc in VISIBLE! actor - hasNodes("(numberOfNodes > 0 )"),//&& isVisible)"), - hasElems("(count( elemTypes ) > 0)"), - hasDifferentElems("(count( elemTypes ) > 1)"), - hasEdges("({'Edge'} in elemTypes)"), - hasFaces("({'Face'} in elemTypes)"), - hasVolumes("({'Volume'} in elemTypes)"); - QString aClient = QString( "%1client in {%2}" ).arg( lc ).arg( "'VTKViewer'" ); QString aType = QString( "%1type in {%2}" ).arg( lc ); - aType = aType.arg( mesh_group ); + aType = aType.arg( mesh_part ); QString aMeshInVTK = aClient + "&&" + aType; aClient = "($client in {'VTKViewer' 'ObjectBrowser'})"; @@ -3124,6 +4116,10 @@ void SMESHGUI::initialize( CAM_Application* app ) anId = popupMgr()->insert( tr( "MEN_DISP_ENT" ), -1, -1 ); + popupMgr()->insert( action(216), anId, -1 ); // ELEMS 0D + popupMgr()->setRule(action(216), aDiffElemsInVTK + "&& isVisible &&" + hasElems0d, QtxPopupMgr::VisibleRule); + popupMgr()->setRule(action(216), "{'Elem0d'} in entityMode", QtxPopupMgr::ToggleRule); + popupMgr()->insert( action( 217 ), anId, -1 ); // EDGES popupMgr()->setRule( action( 217 ), aDiffElemsInVTK + "&& isVisible &&" + hasEdges, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 217 ), "{'Edge'} in entityMode", QtxPopupMgr::ToggleRule ); @@ -3136,11 +4132,28 @@ void SMESHGUI::initialize( CAM_Application* app ) popupMgr()->setRule( action( 219 ), aDiffElemsInVTK + "&& isVisible &&" + hasVolumes, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 219 ), "{'Volume'} in entityMode", QtxPopupMgr::ToggleRule ); + popupMgr()->insert( action( 222 ), anId, -1 ); // BALLS + popupMgr()->setRule( action( 222 ), aDiffElemsInVTK + "&& isVisible &&" + hasBalls, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 222 ), "{'BallElem'} in entityMode", QtxPopupMgr::ToggleRule ); + popupMgr()->insert( separator(), anId, -1 ); popupMgr()->insert( action( 220 ), anId, -1 ); // ALL popupMgr()->setRule( action( 220 ), aDiffElemsInVTK + "&& isVisible && not( elemTypes in entityMode )", QtxPopupMgr::VisibleRule ); + + //------------------------------------------------- + // Representation of the 2D Quadratic elements + //------------------------------------------------- + anId = popupMgr()->insert( tr( "MEN_QUADRATIC_REPRESENT" ), -1, -1 ); + popupMgr()->insert( action( 231 ), anId, -1 ); // LINE REPRESENTATION + popupMgr()->setRule( action( 231 ), aMeshInVTK + "and isVisible",QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 231 ), "quadratic2DMode = 'eLines'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert( action( 232 ), anId, -1 ); // ARC REPRESENTATION + popupMgr()->setRule( action( 232 ), aMeshInVTK + "and isVisible", QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 232 ), "quadratic2DMode = 'eArcs'", QtxPopupMgr::ToggleRule ); + //------------------------------------------------- // Orientation of faces //------------------------------------------------- @@ -3160,14 +4173,6 @@ void SMESHGUI::initialize( CAM_Application* app ) popupMgr()->insert( action( 1133 ), -1, -1 ); popupMgr()->setRule( action( 1133 ), aMeshInVTK + "&& isVisible", QtxPopupMgr::VisibleRule ); - //------------------------------------------------- - // Clipping - //------------------------------------------------- - popupMgr()->insert( action( 1134 ), -1, -1 ); - popupMgr()->setRule( action( 1134 ), aMeshInVTK + "&& selcount=1 && isVisible", QtxPopupMgr::VisibleRule ); - - popupMgr()->insert( separator(), -1, -1 ); - //------------------------------------------------- // Controls //------------------------------------------------- @@ -3184,85 +4189,142 @@ void SMESHGUI::initialize( CAM_Application* app ) popupMgr()->insert( separator(), anId, -1 ); - popupMgr()->insert( action( 6003 ), anId, -1 ); // FREE_BORDER + int aSubId = popupMgr()->insert( tr( "MEN_NODE_CTRL" ), anId, -1 ); // NODE CONTROLS + + popupMgr()->insert( action( 6005 ), aSubId, -1 ); // FREE_NODE + popupMgr()->setRule( action( 6005 ), aMeshInVtkHasNodes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6005 ), "controlMode = 'eFreeNodes'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert ( action( 6028 ), aSubId, -1 ); // EQUAL_NODE + popupMgr()->setRule( action( 6028 ), aMeshInVtkHasNodes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6028 ), "controlMode = 'eCoincidentNodes'", QtxPopupMgr::ToggleRule); + + aSubId = popupMgr()->insert( tr( "MEN_EDGE_CTRL" ), anId, -1 ); // EDGE CONTROLS + + popupMgr()->insert( action( 6002 ), aSubId, -1 ); // FREE_EDGE + popupMgr()->setRule( action( 6002 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6002 ), "controlMode = 'eFreeEdges'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert( action( 6003 ), aSubId, -1 ); // FREE_BORDER popupMgr()->setRule( action( 6003 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6003 ), "controlMode = 'eFreeBorders'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6001 ), anId, -1 ); // LENGTH + popupMgr()->insert( action( 6001 ), aSubId, -1 ); // LENGTH popupMgr()->setRule( action( 6001 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6001 ), "controlMode = 'eLength'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6004 ), anId, -1 ); // CONNECTION + popupMgr()->insert( action( 6004 ), aSubId, -1 ); // CONNECTION popupMgr()->setRule( action( 6004 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6004 ), "controlMode = 'eMultiConnection'", QtxPopupMgr::ToggleRule ); + popupMgr()->insert ( action( 6029 ), aSubId, -1 ); // EQUAL_EDGE + popupMgr()->setRule( action( 6029 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6029 ), "controlMode = 'eCoincidentElems1D'", QtxPopupMgr::ToggleRule); - popupMgr()->insert( separator(), anId, -1 ); - - popupMgr()->insert( action( 6005 ), anId, -1 ); // FREE_NODE - popupMgr()->setRule( action( 6005 ), aMeshInVtkHasNodes, QtxPopupMgr::VisibleRule ); - popupMgr()->setRule( action( 6005 ), "controlMode = 'eFreeNodes'", QtxPopupMgr::ToggleRule ); + aSubId = popupMgr()->insert( tr( "MEN_FACE_CTRL" ), anId, -1 ); // FACE CONTROLS - popupMgr()->insert( action( 6002 ), anId, -1 ); // FREE_EDGE - popupMgr()->setRule( action( 6002 ), aMeshInVtkHasEdges, QtxPopupMgr::VisibleRule ); - popupMgr()->setRule( action( 6002 ), "controlMode = 'eFreeEdges'", QtxPopupMgr::ToggleRule ); + popupMgr()->insert ( action( 6021 ), aSubId, -1 ); // FREE_FACE + popupMgr()->setRule( action( 6021 ), aMeshInVtkHasFaces /*aMeshInVtkHasVolumes*/, + QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6021 ), "controlMode = 'eFreeFaces'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6018 ), anId, -1 ); // LENGTH_2D + popupMgr()->insert ( action( 6018 ), aSubId, -1 ); // LENGTH_2D popupMgr()->setRule( action( 6018 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6018 ), "controlMode = 'eLength2D'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6019 ), anId, -1 ); // CONNECTION_2D + popupMgr()->insert ( action( 6019 ), aSubId, -1 ); // CONNECTION_2D popupMgr()->setRule( action( 6019 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6019 ), "controlMode = 'eMultiConnection2D'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6011 ), anId, -1 ); // AREA + popupMgr()->insert ( action( 6011 ), aSubId, -1 ); // AREA popupMgr()->setRule( action( 6011 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6011 ), "controlMode = 'eArea'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6012 ), anId, -1 ); // TAPER + popupMgr()->insert ( action( 6012 ), aSubId, -1 ); // TAPER popupMgr()->setRule( action( 6012 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6012 ), "controlMode = 'eTaper'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6013 ), anId, -1 ); // ASPECT + popupMgr()->insert ( action( 6013 ), aSubId, -1 ); // ASPECT popupMgr()->setRule( action( 6013 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6013 ), "controlMode = 'eAspectRatio'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6014 ), anId, -1 ); // MIN_ANG + popupMgr()->insert ( action( 6014 ), aSubId, -1 ); // MIN_ANG popupMgr()->setRule( action( 6014 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6014 ), "controlMode = 'eMinimumAngle'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6015 ), anId, -1 ); // WARP + popupMgr()->insert ( action( 6015 ), aSubId, -1 ); // WARP popupMgr()->setRule( action( 6015 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6015 ), "controlMode = 'eWarping'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6016 ), anId, -1 ); // SKEW + popupMgr()->insert ( action( 6016 ), aSubId, -1 ); // SKEW popupMgr()->setRule( action( 6016 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6016 ), "controlMode = 'eSkew'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( separator(), anId, -1 ); + popupMgr()->insert ( action( 6022 ), aSubId, -1 ); // MAX_ELEMENT_LENGTH_2D + popupMgr()->setRule( action( 6022 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6022 ), "controlMode = 'eMaxElementLength2D'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert ( action( 6025 ), aSubId, -1 ); // BARE_BORDER_FACE + popupMgr()->setRule( action( 6025 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6025 ), "controlMode = 'eBareBorderFace'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6017 ), anId, -1 ); // ASPECT_3D + popupMgr()->insert ( action( 6027 ), aSubId, -1 ); // OVER_CONSTRAINED_FACE + popupMgr()->setRule( action( 6027 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6027 ), "controlMode = 'eOverConstrainedFace'", QtxPopupMgr::ToggleRule ); + popupMgr()->insert ( action( 6030 ), aSubId, -1 ); // EQUAL_FACE + popupMgr()->setRule( action( 6030 ), aMeshInVtkHasFaces, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6030 ), "controlMode = 'eCoincidentElems2D'", QtxPopupMgr::ToggleRule ); + + aSubId = popupMgr()->insert( tr( "MEN_VOLUME_CTRL" ), anId, -1 ); // VOLUME CONTROLS + + popupMgr()->insert ( action( 6017 ), aSubId, -1 ); // ASPECT_3D popupMgr()->setRule( action( 6017 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6017 ), "controlMode = 'eAspectRatio3D'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert ( action( 6009 ), anId, -1 ); // VOLUME_3D + popupMgr()->insert ( action( 6009 ), aSubId, -1 ); // VOLUME_3D popupMgr()->setRule( action( 6009 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); popupMgr()->setRule( action( 6009 ), "controlMode = 'eVolume3D'", QtxPopupMgr::ToggleRule ); - popupMgr()->insert( action( 6021 ), anId, -1 ); // FREE_FACE - popupMgr()->setRule( action( 6021 ), aMeshInVtkHasFaces /*aMeshInVtkHasVolumes*/, - QtxPopupMgr::VisibleRule ); - popupMgr()->setRule( action( 6021 ), "controlMode = 'eFreeFaces'", QtxPopupMgr::ToggleRule ); + popupMgr()->insert ( action( 6023 ), aSubId, -1 ); // MAX_ELEMENT_LENGTH_3D + popupMgr()->setRule( action( 6023 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6023 ), "controlMode = 'eMaxElementLength3D'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert ( action( 6024 ), aSubId, -1 ); // BARE_BORDER_VOLUME + popupMgr()->setRule( action( 6024 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6024 ), "controlMode = 'eBareBorderVolume'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert ( action( 6026 ), aSubId, -1 ); // OVER_CONSTRAINED_VOLUME + popupMgr()->setRule( action( 6026 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6026 ), "controlMode = 'eOverConstrainedVolume'", QtxPopupMgr::ToggleRule ); + + popupMgr()->insert ( action( 6031 ), aSubId, -1 ); // EQUAL_VOLUME + popupMgr()->setRule( action( 6031 ), aMeshInVtkHasVolumes, QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 6031 ), "controlMode = 'eCoincidentElems3D'", QtxPopupMgr::ToggleRule ); popupMgr()->insert( separator(), anId, -1 ); popupMgr()->insert( action( 201 ), anId, -1 ); // SCALAR_BAR_PROP popupMgr()->setRule( action( 201 ), aMeshInVTK + "&& controlMode <> 'eNone'", QtxPopupMgr::VisibleRule ); - popupMgr()->insert( separator(), -1, -1 ); + popupMgr()->insert( separator(), anId, -1 ); + + aSubId = popupMgr()->insert( tr( "MEN_DISTRIBUTION_CTRL" ), anId, -1 ); // NODE CONTROLS + + popupMgr()->insert( action( 2021 ), aSubId, -1 ); // SAVE_DISTRIBUTION + popupMgr()->setRule( action( 2021 ), aMeshInVTK + "&& isNumFunctor", QtxPopupMgr::VisibleRule ); + + popupMgr()->insert( action( 2022 ), aSubId, -1 ); // SHOW_DISTRIBUTION + popupMgr()->setRule( action( 2022 ), aMeshInVTK + "&& isNumFunctor", QtxPopupMgr::VisibleRule ); + popupMgr()->setRule( action( 2022 ), aMeshInVTK + "&& isNumFunctor && isDistributionVisible", QtxPopupMgr::ToggleRule); + +#ifndef DISABLE_PLOT2DVIEWER + popupMgr()->insert( action( 2023 ), aSubId, -1 ); // PLOT_DISTRIBUTION + popupMgr()->setRule( action( 2023 ), aMeshInVTK + "&& isNumFunctor", QtxPopupMgr::VisibleRule ); +#endif //------------------------------------------------- // Display / Erase //------------------------------------------------- + popupMgr()->insert( separator(), -1, -1 ); QString aRule = "$component={'SMESH'} and ( type='Component' or (" + aClient + " and " + aType + " and " + aSelCount + " and " + anActiveVTK + " and " + isNotEmpty + " %1 ) )"; popupMgr()->insert( action( 301 ), -1, -1 ); // DISPLAY @@ -3276,8 +4338,19 @@ void SMESHGUI::initialize( CAM_Application* app ) popupMgr()->insert( separator(), -1, -1 ); + //------------------------------------------------- + // Clipping + //------------------------------------------------- + popupMgr()->insert( action( 1134 ), -1, -1 ); + popupMgr()->setRule( action( 1134 ), "client='VTKViewer'", QtxPopupMgr::VisibleRule ); + + popupMgr()->insert( separator(), -1, -1 ); + connect( application(), SIGNAL( viewManagerActivated( SUIT_ViewManager* ) ), - this, SLOT( onViewManagerActivated( SUIT_ViewManager* ) ) ); + this, SLOT( onViewManagerActivated( SUIT_ViewManager* ) ) ); + + connect( application(), SIGNAL( viewManagerRemoved( SUIT_ViewManager* ) ), + this, SLOT( onViewManagerRemoved( SUIT_ViewManager* ) ) ); } //================================================================================ @@ -3304,6 +4377,13 @@ bool SMESHGUI::isSelectionCompatible() return isCompatible; } + +bool SMESHGUI::reusableOperation( const int id ) +{ + // compute, evaluate and precompute are not reusable operations + return ( id == 701 || id == 711 || id == 712 ) ? false : SalomeApp_Module::reusableOperation( id ); +} + bool SMESHGUI::activateModule( SUIT_Study* study ) { bool res = SalomeApp_Module::activateModule( study ); @@ -3311,13 +4391,45 @@ bool SMESHGUI::activateModule( SUIT_Study* study ) setMenuShown( true ); setToolShown( true ); + // import Python module that manages SMESH plugins (need to be here because SalomePyQt API uses active module) + PyGILState_STATE gstate = PyGILState_Ensure(); + PyObject* pluginsmanager=PyImport_ImportModuleNoBlock((char*)"salome_pluginsmanager"); + if(pluginsmanager==NULL) + PyErr_Print(); + else + { + PyObject* result=PyObject_CallMethod( pluginsmanager, (char*)"initialize", (char*)"isss",1,"smesh",tr("MEN_MESH").toStdString().c_str(),tr("SMESH_PLUGINS_OTHER").toStdString().c_str()); + if(result==NULL) + PyErr_Print(); + Py_XDECREF(result); + } + PyGILState_Release(gstate); + // end of GEOM plugins loading + // Reset actions accelerator keys action(111)->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B)); // Import DAT action(112)->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); // Import UNV action(113)->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); // Import MED action( 33)->setEnabled(true); // Delete: Key_Delete - action(1101)->setEnabled(true); // Rename: Key_F2 + + // 0020210. Make SMESH_Gen update meshes at switching GEOM->SMESH + GetSMESHGen()->SetCurrentStudy(SALOMEDS::Study::_nil()); + if ( SalomeApp_Study* s = dynamic_cast( study )) + if ( _PTR(Study) aStudy = s->studyDS()) { + GetSMESHGen()->SetCurrentStudy( _CAST(Study,aStudy)->GetStudy() ); + updateObjBrowser(); // objects can be removed + } + + // get all view currently opened in the study and connect their signals to + // the corresponding slots of the class. + SUIT_Desktop* aDesk = study->application()->desktop(); + if ( aDesk ) { + QList wndList = aDesk->windows(); + SUIT_ViewWindow* wnd; + foreach ( wnd, wndList ) + connectView( wnd ); + } return res; } @@ -3335,7 +4447,6 @@ bool SMESHGUI::deactivateModule( SUIT_Study* study ) action(113)->setShortcut(QKeySequence()); // Import MED action( 33)->setEnabled(false); // Delete: Key_Delete - action(1101)->setEnabled(false); // Rename: Key_F2 return SalomeApp_Module::deactivateModule( study ); } @@ -3391,7 +4502,7 @@ void SMESHGUI::contextMenuPopup( const QString& client, QMenu* menu, QString& ti if ( obj ) { QString aName = QString( obj->GetName().c_str() ); while ( aName.at( aName.length() - 1 ) == ' ' ) // Remove extraspaces in Name of Popup - aName.remove( (aName.length() - 1), 1 ); + aName.remove( (aName.length() - 1), 1 ); title = aName; } } @@ -3415,8 +4526,58 @@ void SMESHGUI::viewManagers( QStringList& list ) const void SMESHGUI::onViewManagerActivated( SUIT_ViewManager* mgr ) { - if ( dynamic_cast( mgr ) ) + if ( dynamic_cast( mgr ) ) { SMESH::UpdateSelectionProp( this ); + + QVector aViews = mgr->getViews(); + for(int i = 0; i < aViews.count() ; i++){ + SUIT_ViewWindow *sf = aViews[i]; + connectView( sf ); + } + } +} + +void SMESHGUI::onViewManagerRemoved( SUIT_ViewManager* theViewManager ) +{ + if( theViewManager && theViewManager->getType() == SVTK_Viewer::Type() ) + myClippingPlaneInfoMap.erase( theViewManager ); +} + +void SMESHGUI::addActorAsObserver( SMESH_Actor* theActor ) +{ + theActor->AddObserver( SMESH::DeleteActorEvent, + myEventCallbackCommand.GetPointer(), + myPriority ); +} + +void SMESHGUI::ProcessEvents( vtkObject* theObject, + unsigned long theEvent, + void* theClientData, + void* theCallData ) +{ + if( SMESHGUI* aSMESHGUI = reinterpret_cast( theClientData ) ) { + if( theObject && theEvent == SMESH::DeleteActorEvent ) { + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( theObject ) ) { + SMESHGUI_ClippingPlaneInfoMap& aClippingPlaneInfoMap = aSMESHGUI->getClippingPlaneInfoMap(); + SMESHGUI_ClippingPlaneInfoMap::iterator anIter1 = aClippingPlaneInfoMap.begin(); + for( ; anIter1 != aClippingPlaneInfoMap.end(); anIter1++ ) { + SMESHGUI_ClippingPlaneInfoList& aClippingPlaneInfoList = anIter1->second; + SMESHGUI_ClippingPlaneInfoList::iterator anIter2 = aClippingPlaneInfoList.begin(); + for( ; anIter2 != aClippingPlaneInfoList.end(); anIter2++ ) { + SMESH::ClippingPlaneInfo& aClippingPlaneInfo = *anIter2; + std::list& anActorList = aClippingPlaneInfo.ActorList; + SMESH::TActorList::iterator anIter3 = anActorList.begin(); + for ( ; anIter3 != anActorList.end(); anIter3++ ) { + if( anActor == *anIter3 ) { + anActorList.erase( anIter3 ); + break; + } + } + } + } + } + } + } } void SMESHGUI::createPreferences() @@ -3424,8 +4585,12 @@ void SMESHGUI::createPreferences() // General tab ------------------------------------------------------------------------ int genTab = addPreference( tr( "PREF_TAB_GENERAL" ) ); - int updateGroup = addPreference( tr( "PREF_GROUP_UPDATE" ), genTab ); - addPreference( tr( "PREF_AUTO_UPDATE" ), updateGroup, LightApp_Preferences::Bool, "SMESH", "auto_update" ); + int autoUpdate = addPreference( tr( "PREF_AUTO_UPDATE" ), genTab, LightApp_Preferences::Auto, "SMESH", "auto_update" ); + int lim = addPreference( tr( "PREF_UPDATE_LIMIT" ), autoUpdate, LightApp_Preferences::IntSpin, "SMESH", "update_limit" ); + setPreferenceProperty( lim, "min", 0 ); + setPreferenceProperty( lim, "max", 100000000 ); + setPreferenceProperty( lim, "step", 1000 ); + setPreferenceProperty( lim, "special", tr( "PREF_UPDATE_LIMIT_NOLIMIT" ) ); int qaGroup = addPreference( tr( "PREF_GROUP_QUALITY" ), genTab ); setPreferenceProperty( qaGroup, "columns", 2 ); @@ -3434,15 +4599,20 @@ void SMESHGUI::createPreferences() int prec = addPreference( tr( "PREF_PRECISION_VALUE" ), qaGroup, LightApp_Preferences::IntSpin, "SMESH", "controls_precision" ); setPreferenceProperty( prec, "min", 0 ); setPreferenceProperty( prec, "max", 16 ); + int doubleNodesTol = addPreference( tr( "PREF_EQUAL_NODES_TOL" ), qaGroup, LightApp_Preferences::DblSpin, "SMESH", "equal_nodes_tolerance" ); + setPreferenceProperty( doubleNodesTol, "precision", 10 ); + setPreferenceProperty( doubleNodesTol, "min", 0.0000000001 ); + setPreferenceProperty( doubleNodesTol, "max", 1000000.0 ); + setPreferenceProperty( doubleNodesTol, "step", 0.0000001 ); int dispgroup = addPreference( tr( "PREF_DISPLAY_MODE" ), genTab ); setPreferenceProperty( dispgroup, "columns", 2 ); int dispmode = addPreference( tr( "PREF_DISPLAY_MODE" ), dispgroup, LightApp_Preferences::Selector, "SMESH", "display_mode" ); QStringList modes; - modes.append( "Wireframe" ); - modes.append( "Shading" ); - modes.append( "Nodes" ); - modes.append( "Shrink" ); + modes.append( tr("MEN_WIRE") ); + modes.append( tr("MEN_SHADE") ); + modes.append( tr("MEN_NODES") ); + modes.append( tr("MEN_SHRINK") ); QList indices; indices.append( 0 ); indices.append( 1 ); @@ -3451,6 +4621,25 @@ void SMESHGUI::createPreferences() setPreferenceProperty( dispmode, "strings", modes ); setPreferenceProperty( dispmode, "indexes", indices ); + int arcgroup = addPreference( tr( "QUADRATIC_REPRESENT_MODE" ), genTab ); + setPreferenceProperty( arcgroup, "columns", 2 ); + int quadraticmode = addPreference( tr( "QUADRATIC_REPRESENT_MODE" ), arcgroup, LightApp_Preferences::Selector, "SMESH", "quadratic_mode" ); + QStringList quadraticModes; + quadraticModes.append(tr("MEN_LINE_REPRESENTATION")); + quadraticModes.append(tr("MEN_ARC_REPRESENTATION")); + indices.clear(); + indices.append( 0 ); + indices.append( 1 ); + setPreferenceProperty( quadraticmode, "strings", quadraticModes ); + setPreferenceProperty( quadraticmode, "indexes", indices ); + + int maxAngle = addPreference( tr( "MAX_ARC_ANGLE" ), arcgroup, LightApp_Preferences::IntSpin, + "SMESH", "max_angle" ); + setPreferenceProperty( maxAngle, "min", 1 ); + setPreferenceProperty( maxAngle, "max", 90 ); + + + int exportgroup = addPreference( tr( "PREF_GROUP_EXPORT" ), genTab ); setPreferenceProperty( exportgroup, "columns", 2 ); addPreference( tr( "PREF_AUTO_GROUPS" ), exportgroup, LightApp_Preferences::Bool, "SMESH", "auto_groups" ); @@ -3460,9 +4649,9 @@ void SMESHGUI::createPreferences() setPreferenceProperty( computeGroup, "columns", 2 ); int notifyMode = addPreference( tr( "PREF_NOTIFY_MODE" ), computeGroup, LightApp_Preferences::Selector, "SMESH", "show_result_notification" ); modes.clear(); - modes.append( "Never" ); - modes.append( "Errors only" ); - modes.append( "Always" ); + modes.append( tr( "PREF_NOTIFY_NEVER" ) ); + modes.append( tr( "PREF_NOTIFY_ERROR" ) ); + modes.append( tr( "PREF_NOTIFY_ALWAYS" ) ); indices.clear(); indices.append( 0 ); indices.append( 1 ); @@ -3470,6 +4659,23 @@ void SMESHGUI::createPreferences() setPreferenceProperty( notifyMode, "strings", modes ); setPreferenceProperty( notifyMode, "indexes", indices ); + int infoGroup = addPreference( tr( "PREF_GROUP_INFO" ), genTab ); + setPreferenceProperty( infoGroup, "columns", 4 ); + int elemInfo = addPreference( tr( "PREF_ELEM_INFO" ), infoGroup, LightApp_Preferences::Selector, "SMESH", "mesh_elem_info" ); + modes.clear(); + modes.append( tr( "PREF_ELEM_INFO_SIMPLE" ) ); + modes.append( tr( "PREF_ELEM_INFO_TREE" ) ); + indices.clear(); + indices.append( 0 ); + indices.append( 1 ); + setPreferenceProperty( elemInfo, "strings", modes ); + setPreferenceProperty( elemInfo, "indexes", indices ); + int nodesLim = addPreference( tr( "PREF_GPP_NODES_LIMIT" ), infoGroup, LightApp_Preferences::IntSpin, "SMESH", "info_groups_nodes_limit" ); + setPreferenceProperty( nodesLim, "min", 0 ); + setPreferenceProperty( nodesLim, "max", 10000000 ); + setPreferenceProperty( nodesLim, "step", 10000 ); + setPreferenceProperty( nodesLim, "special", tr( "PREF_UPDATE_LIMIT_NOLIMIT" ) ); + int segGroup = addPreference( tr( "PREF_GROUP_SEGMENT_LENGTH" ), genTab ); setPreferenceProperty( segGroup, "columns", 2 ); int segLen = addPreference( tr( "PREF_SEGMENT_LENGTH" ), segGroup, LightApp_Preferences::IntSpin, @@ -3481,30 +4687,109 @@ void SMESHGUI::createPreferences() setPreferenceProperty( nbSeg, "min", 1 ); setPreferenceProperty( nbSeg, "max", 10000000 ); + int loadGroup = addPreference( tr( "SMESH_PREF_MESH_LOADING" ), genTab ); + addPreference( tr( "PREF_FORGET_MESH_AT_HYP_MODIF" ), loadGroup, LightApp_Preferences::Bool, + "SMESH", "forget_mesh_on_hyp_modif" ); + + + // Quantities with individual precision settings + int precGroup = addPreference( tr( "SMESH_PREF_GROUP_PRECISION" ), genTab ); + setPreferenceProperty( precGroup, "columns", 2 ); + + const int nbQuantities = 6; + int precs[nbQuantities], ii = 0; + precs[ii++] = addPreference( tr( "SMESH_PREF_length_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "length_precision" ); + precs[ii++] = addPreference( tr( "SMESH_PREF_angle_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "angle_precision" ); + precs[ii++] = addPreference( tr( "SMESH_PREF_len_tol_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "len_tol_precision" ); + precs[ii++] = addPreference( tr( "SMESH_PREF_parametric_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "parametric_precision" ); + precs[ii++] = addPreference( tr( "SMESH_PREF_area_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "area_precision" ); + precs[ii ] = addPreference( tr( "SMESH_PREF_vol_precision" ), precGroup, + LightApp_Preferences::IntSpin, "SMESH", "vol_precision" ); + + // Set property for precision value for spinboxes + for ( ii = 0; ii < nbQuantities; ii++ ){ + setPreferenceProperty( precs[ii], "min", -14 ); + setPreferenceProperty( precs[ii], "max", 14 ); + setPreferenceProperty( precs[ii], "precision", 2 ); + } + + int previewGroup = addPreference( tr( "SMESH_PREF_GROUP_PREVIEW" ), genTab ); + setPreferenceProperty( previewGroup, "columns", 2 ); + int chunkSize = addPreference( tr( "PREF_PREVIEW_CHUNK_SIZE" ), previewGroup, LightApp_Preferences::IntSpin, "SMESH", "preview_actor_chunk_size" ); + setPreferenceProperty( chunkSize, "min", 0 ); + setPreferenceProperty( chunkSize, "max", 1000 ); + setPreferenceProperty( chunkSize, "step", 50 ); + + int pyDumpGroup = addPreference( tr( "PREF_PYTHON_DUMP" ), genTab ); + addPreference( tr( "PREF_HISTORICAL_PYTHON_DUMP" ), pyDumpGroup, LightApp_Preferences::Bool, "SMESH", "historical_python_dump" ); + // Mesh tab ------------------------------------------------------------------------ int meshTab = addPreference( tr( "PREF_TAB_MESH" ) ); int nodeGroup = addPreference( tr( "PREF_GROUP_NODES" ), meshTab ); - setPreferenceProperty( nodeGroup, "columns", 2 ); + setPreferenceProperty( nodeGroup, "columns", 3 ); addPreference( tr( "PREF_COLOR" ), nodeGroup, LightApp_Preferences::Color, "SMESH", "node_color" ); - int nodeSz = addPreference( tr( "PREF_SIZE" ), nodeGroup, LightApp_Preferences::IntSpin, "SMESH", "node_size" ); - setPreferenceProperty( nodeSz, "min", 1 ); - setPreferenceProperty( nodeSz, "max", 5 ); + int typeOfMarker = addPreference( tr( "PREF_TYPE_OF_MARKER" ), nodeGroup, LightApp_Preferences::Selector, "SMESH", "type_of_marker" ); + + SUIT_ResourceMgr* aResourceMgr = SMESH::GetResourceMgr(this); + QList aMarkerTypeIndicesList; + QList aMarkerTypeIconsList; + for ( int i = VTK::MT_POINT; i < VTK::MT_USER; i++ ) { + QString icoFile = QString( "ICON_VERTEX_MARKER_%1" ).arg( i ); + QPixmap pixmap = aResourceMgr->loadPixmap( "VTKViewer", tr( qPrintable( icoFile ) ) ); + aMarkerTypeIndicesList << i; + aMarkerTypeIconsList << pixmap; + } + setPreferenceProperty( typeOfMarker, "indexes", aMarkerTypeIndicesList ); + setPreferenceProperty( typeOfMarker, "icons", aMarkerTypeIconsList ); + + int markerScale = addPreference( tr( "PREF_MARKER_SCALE" ), nodeGroup, LightApp_Preferences::Selector, "SMESH", "marker_scale" ); + + QList aMarkerScaleIndicesList; + QStringList aMarkerScaleValuesList; + for ( int i = VTK::MS_10; i <= VTK::MS_70; i++ ) { + aMarkerScaleIndicesList << i; + aMarkerScaleValuesList << QString::number( (i-(int)VTK::MS_10)*0.5 + 1.0 ); + } + setPreferenceProperty( markerScale, "strings", aMarkerScaleValuesList ); + setPreferenceProperty( markerScale, "indexes", aMarkerScaleIndicesList ); int elemGroup = addPreference( tr( "PREF_GROUP_ELEMENTS" ), meshTab ); setPreferenceProperty( elemGroup, "columns", 2 ); - addPreference( tr( "PREF_FILL" ), elemGroup, LightApp_Preferences::Color, "SMESH", "fill_color" ); - addPreference( tr( "PREF_OUTLINE" ), elemGroup, LightApp_Preferences::Color, "SMESH", "outline_color" ); - addPreference( tr( "PREF_BACKFACE" ), elemGroup, LightApp_Preferences::Color, "SMESH", "backface_color" ); - int sp = addPreference( "", elemGroup, LightApp_Preferences::Space ); + int ColorId = addPreference( tr( "PREF_FILL" ), elemGroup, LightApp_Preferences::BiColor, "SMESH", "fill_color" ); + addPreference( tr( "PREF_COLOR_0D" ), elemGroup, LightApp_Preferences::Color, "SMESH", "elem0d_color" ); + addPreference( tr( "PREF_BALL_COLOR" ), elemGroup, LightApp_Preferences::Color, "SMESH", "ball_elem_color" ); + addPreference( tr( "PREF_OUTLINE" ), elemGroup, LightApp_Preferences::Color, "SMESH", "outline_color" ); + addPreference( tr( "PREF_WIREFRAME" ), elemGroup, LightApp_Preferences::Color, "SMESH", "wireframe_color" ); - setPreferenceProperty( sp, "hstretch", 0 ); - setPreferenceProperty( sp, "vstretch", 0 ); + setPreferenceProperty( ColorId, "text", tr("PREF_BACKFACE") ); - int elemW = addPreference( tr( "PREF_WIDTH" ), elemGroup, LightApp_Preferences::IntSpin, "SMESH", "element_width" ); - int shrink = addPreference( tr( "PREF_SHRINK_COEFF" ), elemGroup, LightApp_Preferences::IntSpin, "SMESH", "shrink_coeff" ); + int grpGroup = addPreference( tr( "PREF_GROUP_GROUPS" ), meshTab ); + setPreferenceProperty( grpGroup, "columns", 2 ); + + addPreference( tr( "PREF_GRP_NAMES" ), grpGroup, LightApp_Preferences::Color, "SMESH", "group_name_color" ); + + int size0d = addPreference(tr("PREF_SIZE_0D"), elemGroup, + LightApp_Preferences::IntSpin, "SMESH", "elem0d_size"); + int ballSize = addPreference(tr("PREF_BALL_SIZE"), elemGroup, + LightApp_Preferences::IntSpin, "SMESH", "ball_elem_size"); + int elemW = addPreference(tr("PREF_WIDTH"), elemGroup, + LightApp_Preferences::IntSpin, "SMESH", "element_width"); + int shrink = addPreference(tr("PREF_SHRINK_COEFF"), elemGroup, + LightApp_Preferences::IntSpin, "SMESH", "shrink_coeff"); + + setPreferenceProperty( size0d, "min", 1 ); + setPreferenceProperty( size0d, "max", 10 ); + + setPreferenceProperty( ballSize, "min", 1 ); + setPreferenceProperty( ballSize, "max", 10 ); setPreferenceProperty( elemW, "min", 1 ); setPreferenceProperty( elemW, "max", 5 ); @@ -3532,19 +4817,11 @@ void SMESHGUI::createPreferences() addPreference( tr( "PREF_OBJECT_COLOR" ), selGroup, LightApp_Preferences::Color, "SMESH", "selection_object_color" ); addPreference( tr( "PREF_ELEMENT_COLOR" ), selGroup, LightApp_Preferences::Color, "SMESH", "selection_element_color" ); - int selW = addPreference( tr( "PREF_WIDTH" ), selGroup, LightApp_Preferences::IntSpin, "SMESH", "selection_width" ); - - setPreferenceProperty( selW, "min", 1 ); - setPreferenceProperty( selW, "max", 5 ); int preGroup = addPreference( tr( "PREF_GROUP_PRESELECTION" ), selTab ); setPreferenceProperty( preGroup, "columns", 2 ); addPreference( tr( "PREF_HIGHLIGHT_COLOR" ), preGroup, LightApp_Preferences::Color, "SMESH", "highlight_color" ); - int preW = addPreference( tr( "PREF_WIDTH" ), preGroup, LightApp_Preferences::IntSpin, "SMESH", "highlight_width" ); - - setPreferenceProperty( preW, "min", 1 ); - setPreferenceProperty( preW, "max", 5 ); int precSelGroup = addPreference( tr( "PREF_GROUP_PRECISION" ), selTab ); setPreferenceProperty( precSelGroup, "columns", 2 ); @@ -3625,6 +4902,18 @@ void SMESHGUI::createPreferences() setPreferenceProperty( hh, "min", 0.0 ); setPreferenceProperty( hh, "max", 1.0 ); setPreferenceProperty( hh, "step", 0.1 ); + + int distributionGr = addPreference( tr( "SMESH_DISTRIBUTION_SCALARBAR" ), sbarTab, LightApp_Preferences::Auto, "SMESH", "distribution_visibility" ); + int coloringType = addPreference( tr( "SMESH_DISTRIBUTION_COLORING_TYPE" ), distributionGr, LightApp_Preferences::Selector, "SMESH", "distribution_coloring_type" ); + setPreferenceProperty( distributionGr, "columns", 3 ); + QStringList types; + types.append( tr( "SMESH_MONOCOLOR" ) ); + types.append( tr( "SMESH_MULTICOLOR" ) ); + indices.clear(); indices.append( 0 ); indices.append( 1 ); + setPreferenceProperty( coloringType, "strings", types ); + setPreferenceProperty( coloringType, "indexes", indices ); + addPreference( tr( "SMESH_DISTRIBUTION_COLOR" ), distributionGr, LightApp_Preferences::Color, "SMESH", "distribution_color" ); + } void SMESHGUI::preferencesChanged( const QString& sect, const QString& name ) @@ -3635,50 +4924,50 @@ void SMESHGUI::preferencesChanged( const QString& sect, const QString& name ) std::string aWarning; SUIT_ResourceMgr* aResourceMgr = SMESH::GetResourceMgr(this); if( name=="selection_object_color" || name=="selection_element_color" || - name=="selection_width" || name=="highlight_color" || name=="highlight_width" || + name=="highlight_color" || name=="selection_precision_node" || name=="selection_precision_element" || - name=="selection_precision_object") + name=="selection_precision_object") SMESH::UpdateSelectionProp( this ); else if (name == QString("scalar_bar_vertical_x") || name == QString("scalar_bar_vertical_width")){ sbX1 = aResourceMgr->doubleValue("SMESH", "scalar_bar_vertical_x", sbX1); sbW = aResourceMgr->doubleValue("SMESH", "scalar_bar_vertical_width", sbW); if(sbX1+sbW > aTol){ - aWarning = "Origin and Size Vertical: X+Width > 1\n"; - sbX1=0.01; - sbW=0.08; - aResourceMgr->setValue("SMESH", "scalar_bar_vertical_x", sbX1); - aResourceMgr->setValue("SMESH", "scalar_bar_vertical_width", sbW); + aWarning = "Origin and Size Vertical: X+Width > 1\n"; + sbX1=0.01; + sbW=0.08; + aResourceMgr->setValue("SMESH", "scalar_bar_vertical_x", sbX1); + aResourceMgr->setValue("SMESH", "scalar_bar_vertical_width", sbW); } } else if(name == QString("scalar_bar_vertical_y") || name == QString("scalar_bar_vertical_height")){ sbY1 = aResourceMgr->doubleValue("SMESH", "scalar_bar_vertical_y", sbY1); sbH = aResourceMgr->doubleValue("SMESH", "scalar_bar_vertical_height",sbH); if(sbY1+sbH > aTol){ - aWarning = "Origin and Size Vertical: Y+Height > 1\n"; - aResourceMgr->setValue("SMESH", "scalar_bar_vertical_y", sbY1); - aResourceMgr->setValue("SMESH", "scalar_bar_vertical_height",sbH); + aWarning = "Origin and Size Vertical: Y+Height > 1\n"; + aResourceMgr->setValue("SMESH", "scalar_bar_vertical_y", sbY1); + aResourceMgr->setValue("SMESH", "scalar_bar_vertical_height",sbH); } } else if(name == QString("scalar_bar_horizontal_x") || name == QString("scalar_bar_horizontal_width")){ sbX1 = aResourceMgr->doubleValue("SMESH", "scalar_bar_horizontal_x", sbX1); sbW = aResourceMgr->doubleValue("SMESH", "scalar_bar_horizontal_width", sbW); if(sbX1+sbW > aTol){ - aWarning = "Origin and Size Horizontal: X+Width > 1\n"; - sbX1=0.1; - sbW=0.08; - aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_x", sbX1); - aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_width", sbW); + aWarning = "Origin and Size Horizontal: X+Width > 1\n"; + sbX1=0.1; + sbW=0.08; + aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_x", sbX1); + aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_width", sbW); } } else if(name == QString("scalar_bar_horizontal_y") || name == QString("scalar_bar_horizontal_height")){ sbY1 = aResourceMgr->doubleValue("SMESH", "scalar_bar_horizontal_y", sbY1); sbH = aResourceMgr->doubleValue("SMESH", "scalar_bar_horizontal_height",sbH); if(sbY1+sbH > aTol){ - aWarning = "Origin and Size Horizontal: Y+Height > 1\n"; - sbY1=0.01; - sbH=0.08; - aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_y", sbY1); - aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_height",sbH); + aWarning = "Origin and Size Horizontal: Y+Height > 1\n"; + sbY1=0.01; + sbH=0.08; + aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_y", sbY1); + aResourceMgr->setValue("SMESH", "scalar_bar_horizontal_height",sbH); } } else if ( name == "segmentation" ) { @@ -3689,12 +4978,17 @@ void SMESHGUI::preferencesChanged( const QString& sect, const QString& name ) int nbSeg = aResourceMgr->integerValue( "SMESH", "nb_segments_per_edge", 15 ); myComponentSMESH->SetDefaultNbSegments( nbSeg ); } + else if ( name == "historical_python_dump" || + name == "forget_mesh_on_hyp_modif") { + QString val = aResourceMgr->stringValue( "SMESH", name ); + myComponentSMESH->SetOption( name.toLatin1().constData(), val.toLatin1().constData() ); + } if(aWarning.size() != 0){ aWarning += "The default values are applied instead."; SUIT_MessageBox::warning(SMESHGUI::desktop(), - QObject::tr("SMESH_ERR_SCALARBAR_PARAMS"), - QObject::tr(aWarning.c_str())); + QObject::tr("SMESH_ERR_SCALARBAR_PARAMS"), + QObject::tr(aWarning.c_str())); } } } @@ -3710,7 +5004,7 @@ void SMESHGUI::preferencesChanged( const QString& sect, const QString& name ) //================================================================================ void SMESHGUI::update( const int flags ) { - if ( flags & UF_Viewer | flags & UF_Forced ) + if ( (flags & UF_Viewer) | (flags & UF_Forced) ) SMESH::UpdateView(); else SalomeApp_Module::update( flags ); @@ -3762,6 +5056,15 @@ LightApp_Operation* SMESHGUI::createOperation( const int id ) const // to do : create operation here switch( id ) { + case 417: //convert to quadratic + op = new SMESHGUI_ConvToQuadOp(); + break; + case 418: // create 2D mesh as boundary on 3D + op = new SMESHGUI_Make2DFrom3DOp(); + break; + case 420: // Reorient faces + op = new SMESHGUI_ReorientFacesOp(); + break; case 701: // Compute mesh op = new SMESHGUI_ComputeOp(); break; @@ -3777,12 +5080,18 @@ LightApp_Operation* SMESHGUI::createOperation( const int id ) const case 711: // Precompute mesh op = new SMESHGUI_PrecomputeOp(); break; + case 712: // Evaluate mesh + op = new SMESHGUI_EvaluateOp(); + break; + case 713: // Evaluate mesh + op = new SMESHGUI_MeshOrderOp(); + break; case 806: // Create group on geom op = new SMESHGUI_GroupOnShapeOp(); break; - case 417: //convert to quadratic - op = new SMESHGUI_ConvToQuadOp(); - break; + case 904: // Find element + op = new SMESHGUI_FindElemByPointOp(); + break; case 4067: // make mesh pass through point op = new SMESHGUI_MakeNodeAtPointOp(); break; @@ -3830,14 +5139,11 @@ SALOMEDS::Color SMESHGUI::getUniqueColor( const QList& theReser { aTolerance /= 2; if( aTolerance < 1 ) - break; + break; } - //cout << "Iteration N" << anIterations << " (tolerance=" << aTolerance << ")"<< endl; aHue = (int)( 360.0 * rand() / RAND_MAX ); - //cout << "Hue = " << aHue << endl; - //cout << "Auto colors : "; bool ok = true; QList::const_iterator it = theReservedColors.constBegin(); QList::const_iterator itEnd = theReservedColors.constEnd(); @@ -3848,21 +5154,17 @@ SALOMEDS::Color SMESHGUI::getUniqueColor( const QList& theReser int h, s, v; aQColor.getHsv( &h, &s, &v ); - //cout << h << " "; if( abs( h - aHue ) < aTolerance ) { - ok = false; - //cout << "break (diff = " << abs( h - aHue ) << ")"; - break; + ok = false; + break; } } - //cout << endl; if( ok ) break; } - //cout << "Hue of the returned color = " << aHue << endl; QColor aColor; aColor.setHsv( aHue, 255, 255 ); @@ -3876,6 +5178,7 @@ SALOMEDS::Color SMESHGUI::getUniqueColor( const QList& theReser const char gSeparator = '_'; // character used to separate parameter names const char gDigitsSep = ':'; // character used to separate numeric parameter values (color = r:g:b) +const char gPathSep = '|'; // character used to separate paths /*! * \brief Store visual parameters @@ -3901,6 +5204,39 @@ void SMESHGUI::storeVisualParameters (int savePoint) savePoint); _PTR(IParameters) ip = ClientFactory::getIParameters(ap); + // store map of custom markers + const VTK::MarkerMap& aMarkerMap = myMarkerMap[ studyDS->StudyId() ]; + if( !aMarkerMap.empty() ) + { + VTK::MarkerMap::const_iterator anIter = aMarkerMap.begin(); + for( ; anIter != aMarkerMap.end(); anIter++ ) + { + int anId = anIter->first; + VTK::MarkerData aMarkerData = anIter->second; + std::string aMarkerFileName = aMarkerData.first; + VTK::MarkerTexture aMarkerTexture = aMarkerData.second; + if( aMarkerTexture.size() < 3 ) + continue; // should contain at least width, height and the first value + + QString aPropertyName( "texture" ); + aPropertyName += gSeparator; + aPropertyName += QString::number( anId ); + + QString aPropertyValue = aMarkerFileName.c_str(); + aPropertyValue += gPathSep; + + VTK::MarkerTexture::const_iterator aTextureIter = aMarkerTexture.begin(); + ushort aWidth = *aTextureIter++; + ushort aHeight = *aTextureIter++; + aPropertyValue += QString::number( aWidth ); aPropertyValue += gDigitsSep; + aPropertyValue += QString::number( aHeight ); aPropertyValue += gDigitsSep; + for( ; aTextureIter != aMarkerTexture.end(); aTextureIter++ ) + aPropertyValue += QString::number( *aTextureIter ); + + ip->setProperty( aPropertyName.toStdString(), aPropertyValue.toStdString() ); + } + } + // viewers counters are used for storing view_numbers in IParameters int vtkViewers = 0; @@ -3916,21 +5252,53 @@ void SMESHGUI::storeVisualParameters (int savePoint) // saving VTK actors properties if (vType == SVTK_Viewer::Type()) { + // store the clipping planes attached to the view manager + SMESHGUI_ClippingPlaneInfoList aClippingPlaneInfoList; + SMESHGUI_ClippingPlaneInfoMap::const_iterator anIter = myClippingPlaneInfoMap.find( vman ); + if( anIter != myClippingPlaneInfoMap.end() ) + aClippingPlaneInfoList = anIter->second; + + if( !aClippingPlaneInfoList.empty() ) { + SMESHGUI_ClippingPlaneInfoList::const_iterator anIter = aClippingPlaneInfoList.begin(); + for( int anId = 0; anIter != aClippingPlaneInfoList.end(); anIter++, anId++ ) + { + const SMESH::ClippingPlaneInfo& aClippingPlaneInfo = *anIter; + SMESH::OrientedPlane* aPlane = aClippingPlaneInfo.Plane; + + QString aPropertyName( "ClippingPlane" ); + aPropertyName += gSeparator; + aPropertyName += QString::number( vtkViewers ); + aPropertyName += gSeparator; + aPropertyName += QString::number( anId ); + + QString aPropertyValue = QString::number( (int)aPlane->GetOrientation() ).toLatin1().constData(); + aPropertyValue += gDigitsSep; + aPropertyValue += QString::number( aPlane->GetDistance() ).toLatin1().constData(); + aPropertyValue += gDigitsSep; + aPropertyValue += QString::number( aPlane->myAngle[0] ).toLatin1().constData(); + aPropertyValue += gDigitsSep; + aPropertyValue += QString::number( aPlane->myAngle[1] ).toLatin1().constData(); + + ip->setProperty( aPropertyName.toStdString(), aPropertyValue.toStdString() ); + } + } + QVector views = vman->getViews(); for (int i = 0, iEnd = vman->getViewsCount(); i < iEnd; i++) { - if (SVTK_ViewWindow* vtkView = dynamic_cast(views[i])) + if (SVTK_ViewWindow* vtkView = dynamic_cast(views[i])) { - vtkActorCollection* allActors = vtkView->getRenderer()->GetActors(); - allActors->InitTraversal(); - while (vtkActor* actor = allActors->GetNextActor()) + VTK::ActorCollectionCopy aCopy(vtkView->getRenderer()->GetActors()); + vtkActorCollection* allActors = aCopy.GetActors(); + allActors->InitTraversal(); + while (vtkActor* actor = allActors->GetNextActor()) { - if (actor->GetVisibility()) // store only visible actors + if (actor->GetVisibility()) // store only visible actors { SMESH_Actor* aSmeshActor = 0; if (actor->IsA("SMESH_Actor")) aSmeshActor = SMESH_Actor::SafeDownCast(actor); - if (aSmeshActor && aSmeshActor->hasIO()) + if (aSmeshActor && aSmeshActor->hasIO()) { Handle(SALOME_InteractiveObject) io = aSmeshActor->getIO(); if (io->hasEntry()) @@ -3976,18 +5344,17 @@ void SMESHGUI::storeVisualParameters (int savePoint) // Colors (surface:edge:) vtkFloatingPointType r, g, b; + int delta; - aSmeshActor->GetSufaceColor(r, g, b); + aSmeshActor->GetSufaceColor(r, g, b, delta); QString colorStr ("surface"); colorStr += gDigitsSep; colorStr += QString::number(r); colorStr += gDigitsSep; colorStr += QString::number(g); colorStr += gDigitsSep; colorStr += QString::number(b); - aSmeshActor->GetBackSufaceColor(r, g, b); colorStr += gDigitsSep; colorStr += "backsurface"; - colorStr += gDigitsSep; colorStr += QString::number(r); - colorStr += gDigitsSep; colorStr += QString::number(g); - colorStr += gDigitsSep; colorStr += QString::number(b); + colorStr += gDigitsSep; colorStr += QString::number(delta); + aSmeshActor->GetEdgeColor(r, g, b); colorStr += gDigitsSep; colorStr += "edge"; @@ -4001,20 +5368,44 @@ void SMESHGUI::storeVisualParameters (int savePoint) colorStr += gDigitsSep; colorStr += QString::number(g); colorStr += gDigitsSep; colorStr += QString::number(b); + aSmeshActor->GetOutlineColor(r, g, b); + colorStr += gDigitsSep; colorStr += "outline"; + colorStr += gDigitsSep; colorStr += QString::number(r); + colorStr += gDigitsSep; colorStr += QString::number(g); + colorStr += gDigitsSep; colorStr += QString::number(b); + param = vtkParam + "Colors"; ip->setParameter(entry, param, colorStr.toLatin1().data()); // Sizes of lines and points QString sizeStr ("line"); sizeStr += gDigitsSep; sizeStr += QString::number((int)aSmeshActor->GetLineWidth()); - sizeStr += gDigitsSep; sizeStr += "node"; - sizeStr += gDigitsSep; sizeStr += QString::number((int)aSmeshActor->GetNodeSize()); sizeStr += gDigitsSep; sizeStr += "shrink"; sizeStr += gDigitsSep; sizeStr += QString::number(aSmeshActor->GetShrinkFactor()); param = vtkParam + "Sizes"; ip->setParameter(entry, param, sizeStr.toLatin1().data()); + // Point marker + QString markerStr; + + VTK::MarkerType aMarkerType = aSmeshActor->GetMarkerType(); + if( aMarkerType == VTK::MT_USER ) { + markerStr += "custom"; + markerStr += gDigitsSep; + markerStr += QString::number( aSmeshActor->GetMarkerTexture() ); + } + else { + markerStr += "std"; + markerStr += gDigitsSep; + markerStr += QString::number( (int)aMarkerType ); + markerStr += gDigitsSep; + markerStr += QString::number( (int)aSmeshActor->GetMarkerScale() ); + } + + param = vtkParam + "PointMarker"; + ip->setParameter(entry, param, markerStr.toLatin1().data()); + // Opacity param = vtkParam + "Opacity"; ip->setParameter(entry, param, @@ -4022,33 +5413,55 @@ void SMESHGUI::storeVisualParameters (int savePoint) // Clipping param = vtkParam + "ClippingPlane"; - int nPlanes = aSmeshActor->GetNumberOfClippingPlanes(); - if (!nPlanes) - ip->setParameter(entry, param, "Off"); - for (int ipl = 0; ipl < nPlanes; ipl++) { - //vtkPlane* plane = aSmeshActor->GetClippingPlane(ipl); - SMESH::Orientation anOrientation; - double aDistance; - vtkFloatingPointType anAngle[2]; - SMESHGUI_ClippingDlg::GetPlaneParam(aSmeshActor, ipl, anOrientation, aDistance, anAngle); - std::string planeValue = QString::number((int)anOrientation).toLatin1().data(); - planeValue += gDigitsSep; planeValue += QString::number(aDistance).toLatin1().data(); - planeValue += gDigitsSep; planeValue += QString::number(anAngle[0]).toLatin1().data(); - planeValue += gDigitsSep; planeValue += QString::number(anAngle[1]).toLatin1().data(); - - ip->setParameter(entry, param + QString::number(ipl+1).toLatin1().data(), planeValue); + int aPlaneId = 0; + if( !aClippingPlaneInfoList.empty() ) { + SMESHGUI_ClippingPlaneInfoList::const_iterator anIter1 = aClippingPlaneInfoList.begin(); + for( int anId = 0; anIter1 != aClippingPlaneInfoList.end(); anIter1++, anId++ ) + { + const SMESH::ClippingPlaneInfo& aClippingPlaneInfo = *anIter1; + std::list anActorList = aClippingPlaneInfo.ActorList; + SMESH::TActorList::iterator anIter2 = anActorList.begin(); + for ( ; anIter2 != anActorList.end(); anIter2++ ) { + if( aSmeshActor == *anIter2 ) { + ip->setParameter( entry, param + QString::number( ++aPlaneId ).toLatin1().constData(), + QString::number( anId ).toLatin1().constData() ); + break; + } + } + } } + if( aPlaneId == 0 ) + ip->setParameter( entry, param, "Off" ); } // if (io->hasEntry()) - } // SMESH_Actor && hasIO - } // isVisible - } // while.. actors traversal - } // if (vtkView) + } // SMESH_Actor && hasIO + } // isVisible + } // while.. actors traversal + } // if (vtkView) } // for (views) vtkViewers++; } // if (SVTK view model) } // for (viewManagers) } +// data structures for clipping planes processing +typedef struct { + int Id; + vtkIdType Orientation; + vtkFloatingPointType Distance; + vtkFloatingPointType Angle[2]; +} TPlaneData; +typedef std::list TPlaneDataList; +typedef std::map TPlaneDataMap; + +typedef std::list TActorList; +typedef struct { + int PlaneId; + TActorList ActorList; + SUIT_ViewManager* ViewManager; +} TPlaneInfo; +typedef std::list TPlaneInfoList; +typedef std::map TPlaneInfoMap; + /*! * \brief Restore visual parameters * @@ -4073,6 +5486,115 @@ void SMESHGUI::restoreVisualParameters (int savePoint) savePoint); _PTR(IParameters) ip = ClientFactory::getIParameters(ap); + // restore map of custom markers and map of clipping planes + VTK::MarkerMap& aMarkerMap = myMarkerMap[ studyDS->StudyId() ]; + TPlaneDataMap aPlaneDataMap; + + std::vector properties = ip->getProperties(); + for (std::vector::iterator propIt = properties.begin(); propIt != properties.end(); ++propIt) + { + std::string property = *propIt; + QString aPropertyName( property.c_str() ); + QString aPropertyValue( ip->getProperty( property ).c_str() ); + + QStringList aPropertyNameList = aPropertyName.split( gSeparator, QString::SkipEmptyParts ); + if( aPropertyNameList.isEmpty() ) + continue; + + QString aPropertyType = aPropertyNameList[0]; + if( aPropertyType == "texture" ) + { + if( aPropertyNameList.size() != 2 ) + continue; + + bool ok = false; + int anId = aPropertyNameList[1].toInt( &ok ); + if( !ok || anId < 1 ) + continue; + + QStringList aPropertyValueList = aPropertyValue.split( gPathSep, QString::SkipEmptyParts ); + if( aPropertyValueList.size() != 2 ) + continue; + + std::string aMarkerFileName = aPropertyValueList[0].toStdString(); + QString aMarkerTextureString = aPropertyValueList[1]; + QStringList aMarkerTextureStringList = aMarkerTextureString.split( gDigitsSep, QString::SkipEmptyParts ); + if( aMarkerTextureStringList.size() != 3 ) + continue; + + ok = false; + ushort aWidth = aMarkerTextureStringList[0].toUShort( &ok ); + if( !ok ) + continue; + + ok = false; + ushort aHeight = aMarkerTextureStringList[1].toUShort( &ok ); + if( !ok ) + continue; + + VTK::MarkerTexture aMarkerTexture; + aMarkerTexture.push_back( aWidth ); + aMarkerTexture.push_back( aHeight ); + + QString aMarkerTextureData = aMarkerTextureStringList[2]; + for( int i = 0, n = aMarkerTextureData.length(); i < n; i++ ) + { + QChar aChar = aMarkerTextureData.at( i ); + if( aChar.isDigit() ) + aMarkerTexture.push_back( aChar.digitValue() ); + } + + aMarkerMap[ anId ] = VTK::MarkerData( aMarkerFileName, aMarkerTexture ); + } + else if( aPropertyType == "ClippingPlane" ) + { + if( aPropertyNameList.size() != 3 ) + continue; + + bool ok = false; + int aViewId = aPropertyNameList[1].toInt( &ok ); + if( !ok || aViewId < 0 ) + continue; + + ok = false; + int aClippingPlaneId = aPropertyNameList[2].toInt( &ok ); + if( !ok || aClippingPlaneId < 0 ) + continue; + + QStringList aPropertyValueList = aPropertyValue.split( gDigitsSep, QString::SkipEmptyParts ); + if( aPropertyValueList.size() != 4 ) + continue; + + TPlaneData aPlaneData; + aPlaneData.Id = aClippingPlaneId; + + ok = false; + aPlaneData.Orientation = aPropertyValueList[0].toInt( &ok ); + if( !ok ) + continue; + + ok = false; + aPlaneData.Distance = aPropertyValueList[1].toDouble( &ok ); + if( !ok ) + continue; + + ok = false; + aPlaneData.Angle[0] = aPropertyValueList[2].toDouble( &ok ); + if( !ok ) + continue; + + ok = false; + aPlaneData.Angle[1] = aPropertyValueList[3].toDouble( &ok ); + if( !ok ) + continue; + + TPlaneDataList& aPlaneDataList = aPlaneDataMap[ aViewId ]; + aPlaneDataList.push_back( aPlaneData ); + } + } + + TPlaneInfoMap aPlaneInfoMap; + std::vector entries = ip->getEntries(); for (std::vector::iterator entIt = entries.begin(); entIt != entries.end(); ++entIt) @@ -4110,7 +5632,7 @@ void SMESHGUI::restoreVisualParameters (int savePoint) bool ok; int viewIndex = viewIndexStr.toUInt(&ok); if (!ok) // bad conversion of view index to integer - continue; + continue; // viewers if (viewerTypStr == SVTK_Viewer::Type()) @@ -4119,48 +5641,50 @@ void SMESHGUI::restoreVisualParameters (int savePoint) if (vtkActors.IsBound(viewIndex)) aSmeshActor = vtkActors.Find(viewIndex); + QList lst; + getApp()->viewManagers(viewerTypStr, lst); + + // SVTK ViewManager always has 1 ViewWindow, so view index is index of view manager + SUIT_ViewManager* vman = NULL; + if (viewIndex >= 0 && viewIndex < lst.count()) + vman = lst.at(viewIndex); + if (paramNameStr == "Visibility") { - if (!aSmeshActor && displayer()) + if (!aSmeshActor && displayer() && vman) { - QList lst; - getApp()->viewManagers(viewerTypStr, lst); - - // SVTK ViewManager always has 1 ViewWindow, so view index is index of view manager - if (viewIndex >= 0 && viewIndex < lst.count()) { - SUIT_ViewManager* vman = lst.at(viewIndex); - SUIT_ViewModel* vmodel = vman->getViewModel(); - // SVTK view model can be casted to SALOME_View - displayer()->Display(entry, true, dynamic_cast(vmodel)); - - // store displayed actor in a temporary map for quicker - // access later when restoring other parameters - SVTK_ViewWindow* vtkView = (SVTK_ViewWindow*) vman->getActiveView(); - vtkRenderer* Renderer = vtkView->getRenderer(); - vtkActorCollection* theActors = Renderer->GetActors(); - theActors->InitTraversal(); - bool isFound = false; - vtkActor *ac = theActors->GetNextActor(); - for (; ac != NULL && !isFound; ac = theActors->GetNextActor()) { - if (ac->IsA("SMESH_Actor")) { - SMESH_Actor* aGeomAc = SMESH_Actor::SafeDownCast(ac); - if (aGeomAc->hasIO()) { - Handle(SALOME_InteractiveObject) io = - Handle(SALOME_InteractiveObject)::DownCast(aGeomAc->getIO()); - if (io->hasEntry() && strcmp(io->getEntry(), entry.toLatin1().data()) == 0) { - isFound = true; - vtkActors.Bind(viewIndex, aGeomAc); - } + SUIT_ViewModel* vmodel = vman->getViewModel(); + // SVTK view model can be casted to SALOME_View + displayer()->Display(entry, true, dynamic_cast(vmodel)); + + // store displayed actor in a temporary map for quicker + // access later when restoring other parameters + SVTK_ViewWindow* vtkView = (SVTK_ViewWindow*) vman->getActiveView(); + vtkRenderer* Renderer = vtkView->getRenderer(); + VTK::ActorCollectionCopy aCopy(Renderer->GetActors()); + vtkActorCollection* theActors = aCopy.GetActors(); + theActors->InitTraversal(); + bool isFound = false; + vtkActor *ac = theActors->GetNextActor(); + for (; ac != NULL && !isFound; ac = theActors->GetNextActor()) { + if (ac->IsA("SMESH_Actor")) { + SMESH_Actor* aGeomAc = SMESH_Actor::SafeDownCast(ac); + if (aGeomAc->hasIO()) { + Handle(SALOME_InteractiveObject) io = + Handle(SALOME_InteractiveObject)::DownCast(aGeomAc->getIO()); + if (io->hasEntry() && strcmp(io->getEntry(), entry.toLatin1().data()) == 0) { + isFound = true; + vtkActors.Bind(viewIndex, aGeomAc); } } } } } } // if (paramNameStr == "Visibility") - else + else { // the rest properties "work" with SMESH_Actor - if (aSmeshActor) + if (aSmeshActor) { QString val ((*valuesIt).c_str()); @@ -4201,49 +5725,115 @@ void SMESHGUI::restoreVisualParameters (int savePoint) // Colors else if (paramNameStr == "Colors") { QStringList colors = val.split(gDigitsSep, QString::SkipEmptyParts); - if (colors.count() == 16) { + if (colors.count() == 16 || colors.count() == 18 ) { if (colors[0] != "surface" || colors[4] != "backsurface" || - colors[8] != "edge" || colors[12] != "node") { + (colors[8] != "edge" && colors[6] != "edge" ) || (colors[12] != "node" && colors[10] != "node") || + (colors.count() == 18 && colors[14] != "outline")) { MESSAGE("Invalid order of data in Colors, must be: " - "surface:r:g:b:backsurface:r:g:b:edge:r:g:b:node:r:g:b"); + "surface:r:g:b:backsurface:r:g:b:edge:r:g:b:node:r:g:b or surface:r:g:b:backsurface:delta:edge:r:g:b:node:r:g:b:outline:r:g:b"); } else { - aSmeshActor->SetSufaceColor(colors[1].toFloat(), colors[2].toFloat(), colors[3].toFloat()); - aSmeshActor->SetBackSufaceColor(colors[5].toFloat(), colors[6].toFloat(), colors[7].toFloat()); - aSmeshActor->SetEdgeColor(colors[9].toFloat(), colors[10].toFloat(), colors[11].toFloat()); - aSmeshActor->SetNodeColor(colors[13].toFloat(), colors[14].toFloat(), colors[15].toFloat()); + int delta = 0; + float er,eg,eb; + float nr,ng,nb; + vtkFloatingPointType otr,otg,otb; + //Old case backsurface color is independent + if( colors.count() == 16 ) { + QColor ffc; + SMESH::GetColor( "SMESH", "fill_color", ffc, delta, "0,170,255|-100" ) ; + er = colors[9].toFloat(); + eg = colors[10].toFloat(); + eb = colors[11].toFloat(); + + nr = colors[13].toFloat(); + ng = colors[14].toFloat(); + nb = colors[15].toFloat(); + SMESH::GetColor("SMESH", "outline_color", otr, otg, otb, QColor( 0, 70, 0 ) ); + } else { + //New case backsurface color depends on surface color + delta = colors[5].toInt(); + + er = colors[7].toFloat(); + eg = colors[8].toFloat(); + eb = colors[9].toFloat(); + + nr = colors[11].toFloat(); + ng = colors[12].toFloat(); + nb = colors[13].toFloat(); + + otr = colors[15].toFloat(); + otg = colors[16].toFloat(); + otb = colors[17].toFloat(); + } + aSmeshActor->SetSufaceColor(colors[1].toFloat(), colors[2].toFloat(), colors[3].toFloat(), delta); + aSmeshActor->SetEdgeColor(er,eg,eb); + aSmeshActor->SetNodeColor(nr,ng,nb); + aSmeshActor->SetOutlineColor(otr,otg,otb); } } } // Sizes of lines and points else if (paramNameStr == "Sizes") { QStringList sizes = val.split(gDigitsSep, QString::SkipEmptyParts); - if (sizes.count() == 6) { + if (sizes.count() == 4) { + if (sizes[0] != "line" || sizes[2] != "shrink") { + MESSAGE("Invalid order of data in Sizes, must be: " + "line:int:shrink:float"); + } + else { + aSmeshActor->SetLineWidth(sizes[1].toInt()); + aSmeshActor->SetShrinkFactor(sizes[3].toFloat()); + } + } + else if (sizes.count() == 6) { // just to support old format if (sizes[0] != "line" || sizes[2] != "node" || sizes[4] != "shrink") { MESSAGE("Invalid order of data in Sizes, must be: " "line:int:node:int:shrink:float"); } else { aSmeshActor->SetLineWidth(sizes[1].toInt()); - aSmeshActor->SetNodeSize(sizes[3].toInt()); + //aSmeshActor->SetNodeSize(sizes[3].toInt()); // made obsolete aSmeshActor->SetShrinkFactor(sizes[5].toFloat()); } } } + // Point marker + else if (paramNameStr == "PointMarker") { + QStringList data = val.split(gDigitsSep, QString::SkipEmptyParts); + if( data.count() >= 2 ) { + bool ok = false; + int aParam1 = data[1].toInt( &ok ); + if( ok ) { + if( data[0] == "std" && data.count() == 3 ) { + int aParam2 = data[2].toInt( &ok ); + aSmeshActor->SetMarkerStd( (VTK::MarkerType)aParam1, (VTK::MarkerScale)aParam2 ); + } + else if( data[0] == "custom" ) { + VTK::MarkerMap::const_iterator markerIt = aMarkerMap.find( aParam1 ); + if( markerIt != aMarkerMap.end() ) { + VTK::MarkerData aMarkerData = markerIt->second; + aSmeshActor->SetMarkerTexture( aParam1, aMarkerData.second ); + } + } + } + } + } // Opacity else if (paramNameStr == "Opacity") { aSmeshActor->SetOpacity(val.toFloat()); } // Clipping else if (paramNameStr.startsWith("ClippingPlane")) { - cout << "$$$ ClippingPlane 1" << endl; - if (paramNameStr == "ClippingPlane1" || val == "Off") - aSmeshActor->RemoveAllClippingPlanes(); - if (val != "Off") { - cout << "$$$ ClippingPlane 2" << endl; - QStringList vals = val.split(gDigitsSep, QString::SkipEmptyParts); - if (vals.count() == 4) { // format check: 4 values - cout << "$$$ ClippingPlane 3" << endl; + QStringList vals = val.split(gDigitsSep, QString::SkipEmptyParts); + // old format - val looks like "Off" or "0:0.5:0:0" (orientation, distance, two angles) + // new format - val looks like "Off" or "0" (plane id) + // (note: in new format "Off" value is used only for consistency, + // so it is processed together with values in old format) + bool anIsOldFormat = ( vals.count() == 4 || val == "Off" ); + if( anIsOldFormat ) { + if (paramNameStr == "ClippingPlane1" || val == "Off") + aSmeshActor->RemoveAllClippingPlanes(); + if (val != "Off") { SMESH::Orientation anOrientation = (SMESH::Orientation)vals[0].toInt(); double aDistance = vals[1].toFloat(); vtkFloatingPointType anAngle[2]; @@ -4256,18 +5846,156 @@ void SMESHGUI::restoreVisualParameters (int savePoint) if (viewIndex >= 0 && viewIndex < lst.count()) { SUIT_ViewManager* vman = lst.at(viewIndex); SVTK_ViewWindow* vtkView = (SVTK_ViewWindow*) vman->getActiveView(); - SMESHGUI_ClippingDlg::AddPlane(aSmeshActor, vtkView, - anOrientation, aDistance, anAngle); + + SMESHGUI_ClippingPlaneInfoList& aClippingPlaneInfoList = myClippingPlaneInfoMap[ vman ]; + + SMESH::TActorList anActorList; + anActorList.push_back( aSmeshActor ); + SMESH::OrientedPlane* aPlane = + SMESHGUI_ClippingDlg::AddPlane(anActorList, vtkView, anOrientation, aDistance, anAngle); + if( aPlane ) { + SMESH::ClippingPlaneInfo aClippingPlaneInfo; + aClippingPlaneInfo.Plane = aPlane; + aClippingPlaneInfo.ActorList = anActorList; + aClippingPlaneInfoList.push_back( aClippingPlaneInfo ); + } + } + } + } + else { + bool ok = false; + int aPlaneId = val.toInt( &ok ); + if( ok && aPlaneId >= 0 ) { + bool anIsDefinedPlane = false; + TPlaneInfoList& aPlaneInfoList = aPlaneInfoMap[ viewIndex ]; + TPlaneInfoList::iterator anIter = aPlaneInfoList.begin(); + for( ; anIter != aPlaneInfoList.end(); anIter++ ) { + TPlaneInfo& aPlaneInfo = *anIter; + if( aPlaneInfo.PlaneId == aPlaneId ) { + aPlaneInfo.ActorList.push_back( aSmeshActor ); + anIsDefinedPlane = true; + break; + } + } + if( !anIsDefinedPlane ) { + TPlaneInfo aPlaneInfo; + aPlaneInfo.PlaneId = aPlaneId; + aPlaneInfo.ActorList.push_back( aSmeshActor ); + aPlaneInfo.ViewManager = vman; + + // to make the list sorted by plane id + anIter = aPlaneInfoList.begin(); + for( ; anIter != aPlaneInfoList.end(); anIter++ ) { + const TPlaneInfo& aPlaneInfoRef = *anIter; + if( aPlaneInfoRef.PlaneId > aPlaneId ) + break; + } + aPlaneInfoList.insert( anIter, aPlaneInfo ); } } } } } // if (aSmeshActor) - } // other parameters than Visibility + } // other parameters than Visibility } } // for names/parameters iterator } // for entries iterator + // take into account planes with empty list of actors referred to them + QList aVMList; + getApp()->viewManagers(SVTK_Viewer::Type(), aVMList); + + TPlaneDataMap::const_iterator aPlaneDataIter = aPlaneDataMap.begin(); + for( ; aPlaneDataIter != aPlaneDataMap.end(); aPlaneDataIter++ ) { + int aViewId = aPlaneDataIter->first; + if( aViewId >= 0 && aViewId < aVMList.count() ) { + SUIT_ViewManager* aViewManager = aVMList.at( aViewId ); + + const TPlaneDataList& aPlaneDataList = aPlaneDataIter->second; + + TPlaneInfoList& aPlaneInfoList = aPlaneInfoMap[ aViewId ]; + TPlaneDataList::const_iterator anIter2 = aPlaneDataList.begin(); + for( ; anIter2 != aPlaneDataList.end(); anIter2++ ) { + const TPlaneData& aPlaneData = *anIter2; + int aPlaneId = aPlaneData.Id; + + bool anIsFound = false; + TPlaneInfoList::const_iterator anIter3 = aPlaneInfoList.begin(); + for( ; anIter3 != aPlaneInfoList.end(); anIter3++ ) { + const TPlaneInfo& aPlaneInfo = *anIter3; + if( aPlaneInfo.PlaneId == aPlaneId ) { + anIsFound = true; + break; + } + } + + if( !anIsFound ) { + TPlaneInfo aPlaneInfo; // ActorList field is empty + aPlaneInfo.PlaneId = aPlaneId; + aPlaneInfo.ViewManager = aViewManager; + + // to make the list sorted by plane id + TPlaneInfoList::iterator anIter4 = aPlaneInfoList.begin(); + for( ; anIter4 != aPlaneInfoList.end(); anIter4++ ) { + const TPlaneInfo& aPlaneInfoRef = *anIter4; + if( aPlaneInfoRef.PlaneId > aPlaneId ) + break; + } + aPlaneInfoList.insert( anIter4, aPlaneInfo ); + } + } + } + } + + // add clipping planes to actors according to the restored parameters + // and update the clipping plane map + TPlaneInfoMap::const_iterator anIter1 = aPlaneInfoMap.begin(); + for( ; anIter1 != aPlaneInfoMap.end(); anIter1++ ) { + int aViewId = anIter1->first; + const TPlaneInfoList& aPlaneInfoList = anIter1->second; + + TPlaneDataMap::const_iterator anIter2 = aPlaneDataMap.find( aViewId ); + if( anIter2 == aPlaneDataMap.end() ) + continue; + const TPlaneDataList& aPlaneDataList = anIter2->second; + + TPlaneInfoList::const_iterator anIter3 = aPlaneInfoList.begin(); + for( ; anIter3 != aPlaneInfoList.end(); anIter3++ ) { + const TPlaneInfo& aPlaneInfo = *anIter3; + int aPlaneId = aPlaneInfo.PlaneId; + const TActorList& anActorList = aPlaneInfo.ActorList; + SUIT_ViewManager* aViewManager = aPlaneInfo.ViewManager; + if( !aViewManager ) + continue; + + SVTK_ViewWindow* aViewWindow = dynamic_cast( aViewManager->getActiveView() ); + if( !aViewWindow ) + continue; + + SMESHGUI_ClippingPlaneInfoList& aClippingPlaneInfoList = myClippingPlaneInfoMap[ aViewManager ]; + + TPlaneDataList::const_iterator anIter4 = aPlaneDataList.begin(); + for( ; anIter4 != aPlaneDataList.end(); anIter4++ ) { + const TPlaneData& aPlaneData = *anIter4; + if( aPlaneData.Id == aPlaneId ) { + SMESH::OrientedPlane* aPlane = + SMESHGUI_ClippingDlg::AddPlane( anActorList, + aViewWindow, + (SMESH::Orientation)aPlaneData.Orientation, + aPlaneData.Distance, + aPlaneData.Angle ); + if( aPlane ) { + SMESH::ClippingPlaneInfo aClippingPlaneInfo; + aClippingPlaneInfo.Plane = aPlane; + aClippingPlaneInfo.ActorList = anActorList; + aClippingPlaneInfoList.push_back( aClippingPlaneInfo ); + } + break; + } + } + } + } + // update all VTK views QList lst; getApp()->viewManagers(lst); @@ -4290,8 +6018,8 @@ void SMESHGUI::restoreVisualParameters (int savePoint) */ int SMESHGUI::addVtkFontPref( const QString& label, const int pId, const QString& param ) { - int tfont = addPreference( label, pId, LightApp_Preferences::Font, "VISU", param ); - + int tfont = addPreference( label, pId, LightApp_Preferences::Font, "SMESH", param ); + setPreferenceProperty( tfont, "mode", QtxFontEdit::Custom ); QStringList fam; @@ -4307,8 +6035,167 @@ int SMESHGUI::addVtkFontPref( const QString& label, const int pId, const QString return tfont; } +/*! + \brief Actions after hypothesis edition + Updates object browser after hypothesis edition +*/ +void SMESHGUI::onHypothesisEdit( int result ) +{ + if( result == 1 ) + SMESHGUI::Modified(); + updateObjBrowser( true ); +} + + +/*! + \brief Signal handler closing(SUIT_ViewWindow*) of a view + \param pview view being closed +*/ +void SMESHGUI::onViewClosed( SUIT_ViewWindow* pview ) { +#ifndef DISABLE_PLOT2DVIEWER + //Crear all Plot2d Viewers if need. + SMESH::ClearPlot2Viewers(pview); +#endif +} + +void SMESHGUI::message( const QString& msg ) +{ + // dispatch message + QStringList data = msg.split("/"); + if ( data.count() > 0 ) { + if ( data.first() == "mesh_loading" ) { + // get mesh entry + QString entry = data.count() > 1 ? data[1] : QString(); + if ( entry.isEmpty() ) + return; + // get study + _PTR(Study) study = dynamic_cast( application()->activeStudy() )->studyDS(); + // get mesh name + _PTR(SObject) obj = study->FindObjectID( entry.toLatin1().constData() ); + QString name; + if ( obj ) + name = obj->GetName().c_str(); + if ( name.isEmpty() ) + return; + + if ( data.last() == "stop" ) + application()->putInfo( tr( "MESH_LOADING_MSG_FINISHED" ).arg( name ) ); + else + application()->putInfo( tr( "MESH_LOADING_MSG" ).arg( name ) ); + QApplication::processEvents(); + } + } +} + +/*! + \brief Connects or disconnects signals about activating and cloning view on the module slots + \param pview view which is connected/disconnected +*/ +void SMESHGUI::connectView( const SUIT_ViewWindow* pview ) { + if(!pview) + return; + + SUIT_ViewManager* viewMgr = pview->getViewManager(); + if ( viewMgr ) { + disconnect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ), + this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) ); + + connect( viewMgr, SIGNAL( deleteView( SUIT_ViewWindow* ) ), + this, SLOT( onViewClosed( SUIT_ViewWindow* ) ) ); + } +} + +/*! + \brief Return \c true if object can be renamed +*/ +bool SMESHGUI::renameAllowed( const QString& entry) const { + SalomeApp_Application* anApp = dynamic_cast( application() ); + if( !anApp ) + return false; + + SalomeApp_Study* appStudy = dynamic_cast( anApp->activeStudy() ); + if( !appStudy ) + return false; + + SalomeApp_DataObject* obj = dynamic_cast(appStudy->findObjectByEntry(entry)); + + if(!obj) + return false; + + if(appStudy->isComponent(entry) || obj->isReference()) + return false; + + // check type to prevent renaming of inappropriate objects + int aType = SMESHGUI_Selection::type(qPrintable(entry), SMESH::GetActiveStudyDocument()); + if (aType == MESH || aType == GROUP || + aType == SUBMESH || aType == SUBMESH_COMPOUND || + aType == SUBMESH_SOLID || aType == SUBMESH_FACE || + aType == SUBMESH_EDGE || aType == SUBMESH_VERTEX || + aType == HYPOTHESIS || aType == ALGORITHM) + return true; + + return false; +} +/*! + Rename object by entry. + \param entry entry of the object + \param name new name of the object + \brief Return \c true if rename operation finished successfully, \c false otherwise. +*/ +bool SMESHGUI::renameObject( const QString& entry, const QString& name) { + SalomeApp_Application* anApp = dynamic_cast( application() ); + if( !anApp ) + return false; + + SalomeApp_Study* appStudy = dynamic_cast( anApp->activeStudy() ); + if(!appStudy) + return false; + + _PTR(Study) aStudy = appStudy->studyDS(); + + if(!aStudy) + return false; + + bool aLocked = (_PTR(AttributeStudyProperties)(appStudy->studyDS()->GetProperties()))->IsLocked(); + if ( aLocked ) { + SUIT_MessageBox::warning ( anApp->desktop(), QObject::tr("WRN_WARNING"), QObject::tr("WRN_STUDY_LOCKED") ); + return false; + } + _PTR(SObject) obj = aStudy->FindObjectID( qPrintable(entry) ); + _PTR(GenericAttribute) anAttr; + _PTR(AttributeName) aName; + if ( obj ) { + if ( obj->FindAttribute(anAttr, "AttributeName") ) { + aName = anAttr; + // check type to prevent renaming of inappropriate objects + int aType = SMESHGUI_Selection::type( qPrintable(entry), SMESH::GetActiveStudyDocument() ); + if (aType == MESH || aType == GROUP || + aType == SUBMESH || aType == SUBMESH_COMPOUND || + aType == SUBMESH_SOLID || aType == SUBMESH_FACE || + aType == SUBMESH_EDGE || aType == SUBMESH_VERTEX || + aType == HYPOTHESIS || aType == ALGORITHM) { + if ( !name.isEmpty() ) { + SMESHGUI::GetSMESHGen()->SetName(obj->GetIOR().c_str(), qPrintable(name) ); + + // update name of group object and its actor + Handle(SALOME_InteractiveObject) IObject = + new SALOME_InteractiveObject ( qPrintable(entry), "SMESH", qPrintable(name) ); + + SMESH::SMESH_GroupBase_var aGroupObject = SMESH::IObjectToInterface(IObject); + if( !aGroupObject->_is_nil() ) { + aGroupObject->SetName( qPrintable(name) ); + if ( SMESH_Actor *anActor = SMESH::FindActorByEntry( qPrintable(entry) ) ) + anActor->setName( qPrintable(name) ); + } + return true; + } + } + } + } + return false; +} diff --git a/src/SMESHGUI/SMESHGUI.h b/src/SMESHGUI/SMESHGUI.h index 230bc3162..6f5237ce3 100644 --- a/src/SMESHGUI/SMESHGUI.h +++ b/src/SMESHGUI/SMESHGUI.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -31,12 +32,21 @@ // SALOME GUI includes #include +#include #include // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Gen) +// VTK includes +#include +#include + +class vtkActor; +class vtkCallbackCommand; +class vtkObject; + class QDialog; class SUIT_Desktop; @@ -50,8 +60,24 @@ class SalomeApp_Study; class LightApp_Selection; class LightApp_SelectionMgr; +class SMESH_Actor; class SMESHGUI_FilterLibraryDlg; +typedef std::map SMESHGUI_StudyId2MarkerMap; + +namespace SMESH +{ + class OrientedPlane; + struct ClippingPlaneInfo + { + OrientedPlane* Plane; + std::list ActorList; + }; +} + +typedef std::list SMESHGUI_ClippingPlaneInfoList; +typedef std::map SMESHGUI_ClippingPlaneInfoMap; + //================================================================================= // class : SMESHGUI // purpose : @@ -70,11 +96,12 @@ public : static SUIT_ResourceMgr* resourceMgr(); static SUIT_Desktop* desktop(); static SalomeApp_Study* activeStudy(); - static char* JoinObjectParameters(const QStringList& theParametersList); bool isActiveStudyLocked(); - static bool automaticUpdate(); + static bool automaticUpdate(unsigned int requestedSize = 0, bool* limitExceeded = 0); + + static void Modified( bool = true ); virtual LightApp_Displayer* displayer(); virtual QString engineIOR() const; @@ -104,11 +131,14 @@ public : void EmitSignalDeactivateDialog(); void EmitSignalStudyFrameChanged(); void EmitSignalCloseAllDialogs(); + void EmitSignalVisibilityChanged(); virtual void contextMenuPopup( const QString&, QMenu*, QString& ); virtual void createPreferences(); virtual void preferencesChanged( const QString&, const QString& ); + virtual void message( const QString& ); + virtual void update( const int ); static SALOMEDS::Color getUniqueColor( const QList& ); @@ -116,29 +146,41 @@ public : virtual void storeVisualParameters (int savePoint); virtual void restoreVisualParameters(int savePoint); + virtual void addActorAsObserver( SMESH_Actor* theActor ); + + virtual bool renameAllowed( const QString& ) const; + virtual bool renameObject( const QString&, const QString& ); + + + SMESHGUI_ClippingPlaneInfoMap& getClippingPlaneInfoMap() { return myClippingPlaneInfoMap; } + public slots: virtual bool deactivateModule( SUIT_Study* ); virtual bool activateModule( SUIT_Study* ); virtual void studyClosed( SUIT_Study* ); + void onViewClosed( SUIT_ViewWindow* ); private slots: void OnGUIEvent(); void onViewManagerActivated( SUIT_ViewManager* ); + void onViewManagerRemoved( SUIT_ViewManager* ); void onOperationCommited( SUIT_Operation* ); void onOperationAborted( SUIT_Operation* ); - + void onHypothesisEdit( int result ); signals: void SignalDeactivateActiveDialog(); void SignalStudyFrameChanged(); void SignalCloseAllDialogs(); + void SignalVisibilityChanged(); protected: void createSMESHAction( const int, const QString&, const QString& = QString(), const int = 0, - const bool = false ); + const bool = false, + const QString& = QString() ); void createPopupItem( const int, const QString&, const QString&, @@ -149,12 +191,22 @@ protected: virtual bool isSelectionCompatible(); + virtual bool reusableOperation( const int id ); + + static void ProcessEvents( vtkObject* theObject, + unsigned long theEvent, + void* theClientData, + void* theCallData ); + private: void OnEditDelete(); int addVtkFontPref( const QString& label, const int pId, const QString& param ); + void connectView( const SUIT_ViewWindow* ); + + private : static SMESH::SMESH_Gen_var myComponentSMESH; QDialog* myActiveDialogBox; @@ -163,6 +215,12 @@ private : LightApp_Displayer* myDisplayer; SMESHGUI_FilterLibraryDlg* myFilterLibraryDlg; + + SMESHGUI_StudyId2MarkerMap myMarkerMap; + SMESHGUI_ClippingPlaneInfoMap myClippingPlaneInfoMap; + + vtkSmartPointer myEventCallbackCommand; + vtkFloatingPointType myPriority; }; #endif // SMESHGUI_H diff --git a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx index 97e393a24..2febf76f8 100644 --- a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_AddMeshElementDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -27,10 +28,12 @@ #include "SMESHGUI_AddMeshElementDlg.h" #include "SMESHGUI.h" +#include "SMESHGUI_GroupUtils.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_SpinBox.h" #include "SMESHGUI_Utils.h" #include "SMESHGUI_VTKUtils.h" -#include "SMESHGUI_MeshUtils.h" -#include "SMESHGUI_IdValidator.h" #include #include @@ -64,6 +67,7 @@ #include // Qt includes +#include #include #include #include @@ -154,43 +158,19 @@ namespace SMESH typedef std::vector TVTKIds; void SetPosition (SMESH_Actor* theActor, - vtkIdType theType, - const TVTKIds& theIds) + vtkIdType theType, + TVTKIds& theIds) { vtkUnstructuredGrid *aGrid = theActor->GetUnstructuredGrid(); myGrid->SetPoints(aGrid->GetPoints()); + myGrid->Reset(); - const int* aConn = NULL; - switch (theType) { - case VTK_TETRA: - { - static int anIds[] = {0,2,1,3}; - aConn = anIds; - break; - } - case VTK_PYRAMID: - { - static int anIds[] = {0,3,2,1,4}; - aConn = anIds; - break; - } - case VTK_HEXAHEDRON: - { - static int anIds[] = {0,3,2,1,4,7,6,5}; - aConn = anIds; - break; - } - } + const std::vector& interlace = SMDS_MeshCell::toVtkOrder( VTKCellType( theType )); + SMDS_MeshCell::applyInterlace( interlace, theIds ); - myGrid->Reset(); vtkIdList *anIds = vtkIdList::New(); - - if(aConn) - for (int i = 0, iEnd = theIds.size(); i < iEnd; i++) - anIds->InsertId(i,theIds[aConn[i]]); - else - for (int i = 0, iEnd = theIds.size(); i < iEnd; i++) - anIds->InsertId(i,theIds[i]); + for (int i = 0, iEnd = theIds.size(); i < iEnd; i++) + anIds->InsertId(i,theIds[i]); myGrid->InsertNextCell(theType,anIds); anIds->Delete(); @@ -212,8 +192,8 @@ namespace SMESH ~TElementSimulation() { if (FindVtkViewWindow(myApplication->activeViewManager(), myViewWindow)) { - myVTKViewWindow->RemoveActor(myPreviewActor); - myVTKViewWindow->RemoveActor(myFaceOrientation); + myVTKViewWindow->RemoveActor(myPreviewActor); + myVTKViewWindow->RemoveActor(myFaceOrientation); } myPreviewActor->Delete(); myFaceOrientation->Delete(); @@ -235,12 +215,12 @@ namespace SMESH // function : SMESHGUI_AddMeshElementDlg() // purpose : constructor //================================================================================= -SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, - SMDSAbs_ElementType ElementType, - int nbNodes ) +SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, + SMDSAbs_EntityType ElementType) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myBusy ( false ) { setModal( false ); setAttribute( Qt::WA_DeleteOnClose, true ); @@ -250,55 +230,80 @@ SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, myIsPoly = false; mySimulation = new SMESH::TElementSimulation (anApp); mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); + myGeomType = ElementType; + myElementType = SMDSAbs_Volume; // verify nb nodes and type - myNbNodes = nbNodes; - myElementType = ElementType; - switch (ElementType) { - case SMDSAbs_Face: - // if (myNbNodes != 3 && myNbNodes != 4) - // myNbNodes = 3; - // break; - case SMDSAbs_Volume: - // if (myNbNodes != 4 && myNbNodes != 8) //(nbNodes < 4 || nbNodes > 8 || nbNodes == 7) - // myNbNodes = 4; + QString elemName; + switch ( myGeomType ) { + case SMDSEntity_0D: + myNbNodes = 1; + myElementType = SMDSAbs_0DElement; + elemName = "ELEM0D"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_0delems_anchor"; break; - default: - myElementType = SMDSAbs_Edge; + case SMDSEntity_Ball: + myNbNodes = 1; + myElementType = SMDSAbs_Ball; + elemName = "BALL"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_ball_anchor"; + break; + case SMDSEntity_Edge: myNbNodes = 2; - } - - QString elemName; - if (myNbNodes == 2) { + myElementType = SMDSAbs_Edge; elemName = "EDGE"; myHelpFileName = "adding_nodes_and_elements_page.html#adding_edges_anchor"; - } - else if (myNbNodes == 3) { + break; + case SMDSEntity_Triangle: + myNbNodes = 3; elemName = "TRIANGLE"; + myElementType = SMDSAbs_Face; myHelpFileName = "adding_nodes_and_elements_page.html#adding_triangles_anchor"; - } - else if (myNbNodes == 4) - if (myElementType == SMDSAbs_Face) { - elemName = "QUADRANGLE"; - myHelpFileName = "adding_nodes_and_elements_page.html#adding_quadrangles_anchor"; - } - else { - elemName = "TETRAS"; - myHelpFileName = "adding_nodes_and_elements_page.html#adding_tetrahedrons_anchor"; - } - else if (myNbNodes == 8) { - elemName = "HEXAS"; - myHelpFileName = "adding_nodes_and_elements_page.html#adding_hexahedrons_anchor"; - } - else if (myElementType == SMDSAbs_Face) { + break; + case SMDSEntity_Quadrangle: + myNbNodes = 4; + myElementType = SMDSAbs_Face; + elemName = "QUADRANGLE"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_quadrangles_anchor"; + break; + case SMDSEntity_Polygon: + myNbNodes = 0; + myElementType = SMDSAbs_Face; elemName = "POLYGON"; myIsPoly = true; myHelpFileName = "adding_nodes_and_elements_page.html#adding_polygons_anchor"; + break; + case SMDSEntity_Tetra: + myNbNodes = 4; + elemName = "TETRAS"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_tetrahedrons_anchor"; + break; + case SMDSEntity_Pyramid: + myNbNodes = 5; + elemName = "PYRAMID"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_pyramids_anchor"; + break; + case SMDSEntity_Hexa: + myNbNodes = 8; + elemName = "HEXAS"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_hexahedrons_anchor"; + break; + case SMDSEntity_Penta: + myNbNodes = 6; + elemName = "PENTA"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_pentahedrons_anchor"; + break; + case SMDSEntity_Hexagonal_Prism: + myNbNodes = 12; + elemName = "OCTA"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_octahedrons_anchor"; + break; + default: + myNbNodes = 2; + elemName = "EDGE"; + myHelpFileName = "adding_nodes_and_elements_page.html#adding_edges_anchor"; } - else if (myElementType == SMDSAbs_Volume) { - myHelpFileName = "adding_nodes_and_elements_page.html#adding_polyhedrons_anchor"; - } - + QString iconName = tr(QString("ICON_DLG_%1").arg(elemName).toLatin1().data()); QString buttonGrTitle = tr(QString("SMESH_%1").arg(elemName).toLatin1().data()); QString caption = tr(QString("SMESH_ADD_%1_TITLE").arg(elemName).toLatin1().data()); @@ -314,7 +319,7 @@ SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, aTopLayout->setSpacing(SPACING); aTopLayout->setMargin(MARGIN); - /***************************************************************/ + /* Constructor *************************************************/ GroupConstructors = new QGroupBox(buttonGrTitle, this); QButtonGroup* ButtonGroup = new QButtonGroup(this); QHBoxLayout* GroupConstructorsLayout = new QHBoxLayout(GroupConstructors); @@ -328,7 +333,7 @@ SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, GroupConstructorsLayout->addWidget(Constructor1); ButtonGroup->addButton( Constructor1, 0 ); - /***************************************************************/ + /* Nodes & Reverse *********************************************/ GroupC1 = new QGroupBox(grBoxTitle, this); QGridLayout* GroupC1Layout = new QGridLayout(GroupC1); GroupC1Layout->setSpacing(SPACING); @@ -338,18 +343,42 @@ SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, SelectButtonC1A1 = new QPushButton(GroupC1); SelectButtonC1A1->setIcon(image1); LineEditC1A1 = new QLineEdit(GroupC1); - // LineEditC1A1->setReadOnly(true); - if (!myIsPoly) - LineEditC1A1->setValidator(new SMESHGUI_IdValidator(this, myNbNodes)); + LineEditC1A1->setValidator(new SMESHGUI_IdValidator(this, myIsPoly ? 1000 : myNbNodes)); - Reverse = myElementType == SMDSAbs_Face ? new QCheckBox(tr("SMESH_REVERSE"), GroupC1) : 0; + Reverse = (myElementType == SMDSAbs_Face || myElementType == SMDSAbs_Volume ) ? new QCheckBox(tr("SMESH_REVERSE"), GroupC1) : 0; + + DiameterSpinBox = ( myGeomType == SMDSEntity_Ball ) ? new SMESHGUI_SpinBox(GroupC1) : 0; + QLabel* diameterLabel = DiameterSpinBox ? new QLabel( tr("BALL_DIAMETER"),GroupC1) : 0; GroupC1Layout->addWidget(TextLabelC1A1, 0, 0); GroupC1Layout->addWidget(SelectButtonC1A1, 0, 1); GroupC1Layout->addWidget(LineEditC1A1, 0, 2); - if ( Reverse ) GroupC1Layout->addWidget(Reverse, 1, 0, 1, 3); + if ( Reverse ) { + GroupC1Layout->addWidget(Reverse, 1, 0, 1, 3); + } + if ( DiameterSpinBox ) { + GroupC1Layout->addWidget(diameterLabel, 1, 0); + GroupC1Layout->addWidget(DiameterSpinBox, 1, 1, 1, 2); - /***************************************************************/ + DiameterSpinBox->RangeStepAndValidator( 1e-7, 1e+9, 0.1 ); + DiameterSpinBox->SetValue( 1. ); + } + /* Add to group ************************************************/ + GroupGroups = new QGroupBox( tr( "SMESH_ADD_TO_GROUP" ), this ); + GroupGroups->setCheckable( true ); + QHBoxLayout* GroupGroupsLayout = new QHBoxLayout(GroupGroups); + GroupGroupsLayout->setSpacing(SPACING); + GroupGroupsLayout->setMargin(MARGIN); + + TextLabel_GroupName = new QLabel( tr( "SMESH_GROUP" ), GroupGroups ); + ComboBox_GroupName = new QComboBox( GroupGroups ); + ComboBox_GroupName->setEditable( true ); + ComboBox_GroupName->setInsertPolicy( QComboBox::NoInsert ); + + GroupGroupsLayout->addWidget( TextLabel_GroupName ); + GroupGroupsLayout->addWidget( ComboBox_GroupName, 1 ); + + /* Apply etc ***************************************************/ GroupButtons = new QGroupBox(this); QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons); GroupButtonsLayout->setSpacing(SPACING); @@ -376,6 +405,7 @@ SMESHGUI_AddMeshElementDlg::SMESHGUI_AddMeshElementDlg( SMESHGUI* theModule, /***************************************************************/ aTopLayout->addWidget(GroupConstructors); aTopLayout->addWidget(GroupC1); + aTopLayout->addWidget(GroupGroups); aTopLayout->addWidget(GroupButtons); Init(); /* Initialisations */ @@ -401,6 +431,10 @@ void SMESHGUI_AddMeshElementDlg::Init() myEditCurrentArgument = LineEditC1A1; mySMESHGUI->SetActiveDialogBox((QDialog*)this); + /* reset "Add to group" control */ + GroupGroups->setChecked( false ); + //GroupGroups->setVisible( myElementType != SMDSAbs_0DElement ); + myNbOkNodes = 0; myActor = 0; @@ -416,6 +450,7 @@ void SMESHGUI_AddMeshElementDlg::Init() connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(SelectionIntoArgument())); /* to close dialog if study frame change */ connect(mySMESHGUI, SIGNAL (SignalStudyFrameChanged()), SLOT(ClickOnCancel())); + connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), SLOT(ClickOnCancel())); if (Reverse) connect(Reverse, SIGNAL(stateChanged(int)), SLOT(CheckBox(int))); @@ -437,33 +472,99 @@ void SMESHGUI_AddMeshElementDlg::Init() //================================================================================= void SMESHGUI_AddMeshElementDlg::ClickOnApply() { + if( !isValid() ) + return; + if (myNbOkNodes && !mySMESHGUI->isActiveStudyLocked()) { myBusy = true; - SMESH::long_array_var anArrayOfIdeces = new SMESH::long_array; - anArrayOfIdeces->length(myNbNodes); - bool reverse = (Reverse && Reverse->isChecked()); + SMESH::long_array_var anArrayOfIndices = new SMESH::long_array; + anArrayOfIndices->length(myNbNodes); QStringList aListId = myEditCurrentArgument->text().split(" ", QString::SkipEmptyParts); - for (int i = 0; i < aListId.count(); i++) - if (reverse) - anArrayOfIdeces[i] = aListId[ myNbNodes - i - 1 ].toInt(); - else - anArrayOfIdeces[i] = aListId[ i ].toInt(); + const std::vector& revIndex = SMDS_MeshCell::reverseSmdsOrder( myGeomType ); + if ( Reverse && Reverse->isChecked() && !revIndex.empty() ) + for (int i = 0; i < aListId.count(); i++) + anArrayOfIndices[i] = aListId[ revIndex[i] ].toInt(); + else if ( Reverse && Reverse->isChecked() && revIndex.empty() ) // polygon + for (int i = 0; i < aListId.count(); i++) + anArrayOfIndices[i] = aListId[ aListId.count()-1 - i ].toInt(); + else + for (int i = 0; i < aListId.count(); i++) + anArrayOfIndices[i] = aListId[ i ].toInt(); + + bool addToGroup = GroupGroups->isChecked(); + QString aGroupName; + + SMESH::SMESH_GroupBase_var aGroup; + int idx = 0; + if( addToGroup ) { + aGroupName = ComboBox_GroupName->currentText(); + for ( int i = 1; i < ComboBox_GroupName->count(); i++ ) { + QString aName = ComboBox_GroupName->itemText( i ); + if ( aGroupName == aName && ( i == ComboBox_GroupName->currentIndex() || idx == 0 ) ) + idx = i; + } + if ( idx > 0 && idx < myGroups.count() ) { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( myGroups[idx-1] ); + if ( !aGeomGroup->_is_nil() ) { + int res = SUIT_MessageBox::question( this, tr( "SMESH_WRN_WARNING" ), + tr( "MESH_STANDALONE_GRP_CHOSEN" ).arg( aGroupName ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 0, 1 ); + if ( res == 1 ) return; + } + aGroup = myGroups[idx-1]; + } + } + long anElemId = -1; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); switch (myElementType) { + case SMDSAbs_0DElement: + anElemId = aMeshEditor->Add0DElement(anArrayOfIndices[0]); break; + case SMDSAbs_Ball: + if ( myGeomType == SMDSEntity_Ball ) + anElemId = aMeshEditor->AddBall(anArrayOfIndices[0], + DiameterSpinBox->GetValue()); break; case SMDSAbs_Edge: - aMeshEditor->AddEdge(anArrayOfIdeces.inout()); break; - case SMDSAbs_Face:{ - if(myIsPoly) - aMeshEditor->AddPolygonalFace(anArrayOfIdeces.inout()); + anElemId = aMeshEditor->AddEdge(anArrayOfIndices.inout()); break; + case SMDSAbs_Face: + if ( myIsPoly ) + anElemId = aMeshEditor->AddPolygonalFace(anArrayOfIndices.inout()); else - aMeshEditor->AddFace(anArrayOfIdeces.inout()); + anElemId = aMeshEditor->AddFace(anArrayOfIndices.inout()); break; - + default: + anElemId = aMeshEditor->AddVolume(anArrayOfIndices.inout()); break; } - case SMDSAbs_Volume: - aMeshEditor->AddVolume(anArrayOfIdeces.inout()); break; - default:; + + if ( anElemId != -1 && addToGroup && !aGroupName.isEmpty() ) { + SMESH::SMESH_Group_var aGroupUsed; + if ( aGroup->_is_nil() ) { + // create new group + aGroupUsed = SMESH::AddGroup( myMesh, (SMESH::ElementType)myElementType, aGroupName ); + if ( !aGroupUsed->_is_nil() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroupUsed)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + else { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( aGroup ); + if ( !aGeomGroup->_is_nil() ) { + aGroupUsed = myMesh->ConvertToStandalone( aGeomGroup ); + if ( !aGroupUsed->_is_nil() && idx > 0 ) { + myGroups[idx-1] = SMESH::SMESH_GroupBase::_duplicate(aGroupUsed); + SMESHGUI::GetSMESHGUI()->getApp()->updateObjectBrowser(); + } + } + else + aGroupUsed = SMESH::SMESH_Group::_narrow( aGroup ); + } + + if ( !aGroupUsed->_is_nil() ) { + SMESH::long_array_var anIdList = new SMESH::long_array; + anIdList->length( 1 ); + anIdList[0] = anElemId; + aGroupUsed->Add( anIdList.inout() ); + } } SALOME_ListIO aList; aList.Append( myActor->getIO() ); @@ -479,6 +580,8 @@ void SMESHGUI_AddMeshElementDlg::ClickOnApply() myEditCurrentArgument->setText(""); myBusy = false; + + SMESHGUI::Modified(); } } @@ -515,8 +618,9 @@ void SMESHGUI_AddMeshElementDlg::ClickOnCancel() void SMESHGUI_AddMeshElementDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), + myHelpFileName); else { QString platform; #ifdef WIN32 @@ -525,10 +629,10 @@ void SMESHGUI_AddMeshElementDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -555,40 +659,40 @@ void SMESHGUI_AddMeshElementDlg::onTextChange (const QString& theNewText) if (aMesh) { TColStd_MapOfInteger newIndices; - + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); bool allOk = true; for (int i = 0; i < aListId.count(); i++) { if( const SMDS_MeshNode * n = aMesh->FindNode( aListId[ i ].toInt() ) ) - { - newIndices.Add( n->GetID() ); - myNbOkNodes++; - } + { + newIndices.Add( n->GetID() ); + myNbOkNodes++; + } else - allOk = false; + allOk = false; } - + mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->highlight( myActor->getIO(), true, true ); - + myNbOkNodes = ( allOk && myNbNodes == aListId.count() ); - + if (myIsPoly) { - if ( !allOk || myElementType != SMDSAbs_Face || aListId.count() < 3 ) - myNbOkNodes = 0; - else - myNbOkNodes = aListId.count(); + if ( !allOk || myElementType != SMDSAbs_Face || aListId.count() < 3 ) + myNbOkNodes = 0; + else + myNbOkNodes = aListId.count(); } } - + if(myNbOkNodes) { buttonOk->setEnabled(true); buttonApply->setEnabled(true); displaySimulation(); } - + myBusy = false; } @@ -617,6 +721,8 @@ void SMESHGUI_AddMeshElementDlg::SelectionIntoArgument() mySimulation->SetVisibility(false); // SMESH::SetPointRepresentation(true); + QString aCurrentEntry = myEntry; + // get selected mesh SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); @@ -625,10 +731,29 @@ void SMESHGUI_AddMeshElementDlg::SelectionIntoArgument() return; Handle(SALOME_InteractiveObject) anIO = aList.First(); + myEntry = anIO->getEntry(); myMesh = SMESH::GetMeshByIO(anIO); if (myMesh->_is_nil()) return; + // process groups + if ( !myMesh->_is_nil() && myEntry != aCurrentEntry ) { + myGroups.clear(); + ComboBox_GroupName->clear(); + ComboBox_GroupName->addItem( QString() ); + SMESH::ListOfGroups aListOfGroups = *myMesh->GetGroups(); + for ( int i = 0, n = aListOfGroups.length(); i < n; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i]; + if ( !aGroup->_is_nil() && aGroup->GetType() == (SMESH::ElementType)myElementType ) { + QString aGroupName( aGroup->GetName() ); + if ( !aGroupName.isEmpty() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + } + } + myActor = SMESH::FindActorByEntry(anIO->getEntry()); if (!myActor) return; @@ -667,24 +792,15 @@ void SMESHGUI_AddMeshElementDlg::displaySimulation() anIds.push_back(myActor->GetObject()->GetNodeVTKId(aListId[ i ].toInt())); if (Reverse && Reverse->isChecked()) - reverse(anIds.begin(),anIds.end()); - - vtkIdType aType = 0; - if (myIsPoly) - switch ( myElementType ) { - case SMDSAbs_Face : aType = VTK_POLYGON; break; - default: return; - } - else { - switch (myNbNodes) { - case 2: aType = VTK_LINE; break; - case 3: aType = VTK_TRIANGLE; break; - case 4: aType = myElementType == SMDSAbs_Face ? VTK_QUAD : VTK_TETRA; break; - case 8: aType = VTK_HEXAHEDRON; break; - default: return; - } + { + const std::vector& i = SMDS_MeshCell::reverseSmdsOrder( myGeomType ); + if ( i.empty() ) // polygon + std::reverse( anIds.begin(), anIds.end() ); + else + SMDS_MeshCell::applyInterlace( i, anIds ); } + vtkIdType aType = SMDS_MeshCell::toVtkType( myGeomType ); mySimulation->SetPosition(myActor,aType,anIds); SMESH::UpdateView(); } @@ -795,9 +911,22 @@ void SMESHGUI_AddMeshElementDlg::keyPressEvent( QKeyEvent* e ) QDialog::keyPressEvent( e ); if ( e->isAccepted() ) return; - + if ( e->key() == Qt::Key_F1 ) { e->accept(); ClickOnHelp(); } } + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= +bool SMESHGUI_AddMeshElementDlg::isValid() +{ + if( GroupGroups->isChecked() && ComboBox_GroupName->currentText().isEmpty() ) { + SUIT_MessageBox::warning( this, tr( "SMESH_WRN_WARNING" ), tr( "GROUP_NAME_IS_EMPTY" ) ); + return false; + } + return true; +} diff --git a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.h b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.h index ee10b312f..72ad06d93 100644 --- a/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.h +++ b/src/SMESHGUI/SMESHGUI_AddMeshElementDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_AddMeshElementDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -38,6 +39,7 @@ #include #include CORBA_SERVER_HEADER(SMESH_Mesh) +class QComboBox; class QGroupBox; class QLabel; class QLineEdit; @@ -48,6 +50,7 @@ class SMESHGUI; class SMESH_Actor; class SVTK_Selector; class LightApp_SelectionMgr; +class SMESHGUI_SpinBox; namespace SMESH { @@ -63,7 +66,7 @@ class SMESHGUI_EXPORT SMESHGUI_AddMeshElementDlg : public QDialog Q_OBJECT public: - SMESHGUI_AddMeshElementDlg( SMESHGUI*, SMDSAbs_ElementType = SMDSAbs_Edge, int = 2 ); + SMESHGUI_AddMeshElementDlg( SMESHGUI*, SMDSAbs_EntityType = SMDSEntity_Edge ); ~SMESHGUI_AddMeshElementDlg(); private: @@ -74,6 +77,10 @@ private: void keyPressEvent( QKeyEvent* ); void displaySimulation(); + bool isValid(); + + typedef QList GrpList; + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ int myNbOkNodes; /* to check when arguments is defined */ @@ -82,6 +89,7 @@ private: QLineEdit* myEditCurrentArgument; /* Current LineEdit */ + SMDSAbs_EntityType myGeomType; int myElementType; int myNbNodes; bool myIsPoly; @@ -89,9 +97,14 @@ private: SMESH::SMESH_Mesh_var myMesh; SMESH_Actor* myActor; SMESH::TElementSimulation* mySimulation; + QString myEntry; + GrpList myGroups; QGroupBox* GroupConstructors; QRadioButton* Constructor1; + QGroupBox* GroupGroups; + QLabel* TextLabel_GroupName; + QComboBox* ComboBox_GroupName; QGroupBox* GroupButtons; QPushButton* buttonOk; QPushButton* buttonCancel; @@ -102,6 +115,7 @@ private: QPushButton* SelectButtonC1A1; QLineEdit* LineEditC1A1; QCheckBox* Reverse; + SMESHGUI_SpinBox* DiameterSpinBox; QString myHelpFileName; diff --git a/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.cxx b/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.cxx index 1c3106e73..0472de715 100644 --- a/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_AddMeshElementDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -30,6 +31,7 @@ #include "SMESHGUI_Utils.h" #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_GroupUtils.h" #include "SMESHGUI_IdValidator.h" #include @@ -69,6 +71,7 @@ #include // Qt includes +#include #include #include #include @@ -88,63 +91,26 @@ #define SPACING 6 #define MARGIN 11 -namespace SMESH +namespace { - void ReverseConnectivity( std::vector & ids, int type ) + void ReverseConnectivity( std::vector & ids, SMDSAbs_EntityType type, + bool toReverse, // inverse element + bool toVtkOrder ) // smds connectivity to vtk one { - // for reverse connectivity of other types keeping the first id, see - // void SMESH_VisualObjDef::buildElemPrs() in SMESH_Object.cxx:900 - const int* conn = 0; - - switch ( type ) { - case QUAD_TETRAHEDRON: { - static int aConn[] = {0,2,1,3,6,5,4,7,9,8}; - conn = aConn; - break; - } - case QUAD_PYRAMID: { - static int aConn[] = {0,3,2,1,4,8,7,6,5,9,12,11,10}; - conn = aConn; - break; - } - case QUAD_PENTAHEDRON: { - static int aConn[] = {0,2,1,3,5,4,8,7,6,11,10,9,12,14,13}; - conn = aConn; - break; - } - case QUAD_HEXAHEDRON: { - static int aConn[] = {0,3,2,1,4,7,6,5,11,10,9,8,15,14,13,12,16,19,18,17}; - conn = aConn; - break; - } - case QUAD_EDGE: { - static int aConn[] = {1,0,2}; - conn = aConn; - break; - } - case QUAD_TRIANGLE: { - static int aConn[] = {0,2,1,5,4,3}; - conn = aConn; - break; - } - case QUAD_QUADRANGLE: { - static int aConn[] = {0,3,2,1,7,6,5,4}; - conn = aConn; - break; - } - default:; - } - if ( !conn ) { - reverse( ids.begin(), ids.end() ); + if ( toReverse ) // first reverse smds order + { + const std::vector& index = SMDS_MeshCell::reverseSmdsOrder(type); + SMDS_MeshCell::applyInterlace( index, ids ); } - else { - std::vector aRevIds( ids.size() ); - for ( int i = 0; i < ids.size(); i++) - aRevIds[ i ] = ids[ conn[ i ]]; - ids = aRevIds; + if ( toVtkOrder ) // from smds to vtk connectivity + { + const std::vector& index = SMDS_MeshCell::toVtkOrder(type); + SMDS_MeshCell::applyInterlace( index, ids ); } } - +} +namespace SMESH +{ class TElementSimulation { SalomeApp_Application* myApplication; SUIT_ViewWindow* myViewWindow; @@ -218,85 +184,36 @@ namespace SMESH } typedef std::vector TVTKIds; - void SetPosition (SMESH_Actor* theActor, - const int theType, - TVTKIds& theIds, - const int theMode, - const bool theReverse) + void SetPosition (SMESH_Actor* theActor, + SMDSAbs_EntityType theType, + TVTKIds& theIds, + const int theMode, + const bool theReverse) { vtkUnstructuredGrid *aGrid = theActor->GetUnstructuredGrid(); myGrid->SetPoints(aGrid->GetPoints()); //add points - vtkIdType aType = 0; + ReverseConnectivity( theIds, theType, theReverse, /*toVtkOrder=*/true); - switch (theType) { - case QUAD_EDGE: - aType = VTK_QUADRATIC_EDGE; - break; - case QUAD_TRIANGLE: - aType = VTK_QUADRATIC_TRIANGLE; - break; - case QUAD_QUADRANGLE: - aType = VTK_QUADRATIC_QUAD; - break; - case QUAD_TETRAHEDRON: - aType = VTK_QUADRATIC_TETRA; - break; - case QUAD_PYRAMID: - //aType = VTK_QUADRATIC_PYRAMID; // NOT SUPPORTED IN VTK4.2 - aType = VTK_CONVEX_POINT_SET; - break; - case QUAD_PENTAHEDRON: - aType = VTK_QUADRATIC_WEDGE; - //aType = VTK_CONVEX_POINT_SET; - break; - case QUAD_HEXAHEDRON: - aType = VTK_QUADRATIC_HEXAHEDRON; - break; - } - - // take care of orientation - if ( aType == VTK_CONVEX_POINT_SET ) { - if ( theReverse && theMode == VTK_SURFACE ) { - //myPreviewActor->GetProperty()->SetColor( myBackRGB[0], myBackRGB[1], myBackRGB[2] ); - } - } - else { - // VTK cell connectivity opposites the MED one for volumic elements - if( aType != VTK_QUADRATIC_WEDGE) { - if ( theIds.size() > 8 ? !theReverse : theReverse ) { - ReverseConnectivity( theIds, theType ); - } - } - else if(theReverse) - ReverseConnectivity( theIds, theType ); - } - myGrid->Reset(); vtkIdList *anIds = vtkIdList::New(); - + for (int i = 0, iEnd = theIds.size(); i < iEnd; i++) { anIds->InsertId(i,theIds[i]); //std::cout << i<< ": " << theIds[i] << std::endl; } - + + vtkIdType aType = SMDS_MeshCell::toVtkType(theType); myGrid->InsertNextCell(aType,anIds); anIds->Delete(); - + myGrid->Modified(); myPreviewActor->GetMapper()->Update(); myPreviewActor->SetRepresentation( theMode ); SetVisibility(true, theActor->GetFacesOriented()); - - // restore normal orientation - if ( aType == VTK_CONVEX_POINT_SET ) { - if ( theReverse && theMode == VTK_SURFACE ) { - //myPreviewActor->GetProperty()->SetColor( myRGB[0], myRGB[1], myRGB[2] ); - } - } } @@ -311,8 +228,8 @@ namespace SMESH ~TElementSimulation() { if (FindVtkViewWindow(myApplication->activeViewManager(), myViewWindow)) { - myVTKViewWindow->RemoveActor(myPreviewActor); - myVTKViewWindow->RemoveActor(myFaceOrientation); + myVTKViewWindow->RemoveActor(myPreviewActor); + myVTKViewWindow->RemoveActor(myFaceOrientation); } myPreviewActor->Delete(); myFaceOrientation->Delete(); @@ -356,8 +273,6 @@ static int LastPentahedronIds[] = {1,2,0,4,5,3,3,4,5}; static int FirstHexahedronIds[] = {0,1,2,3,4,5,6,7,0,1,2,3}; static int LastHexahedronIds[] = {1,2,3,0,5,6,7,4,4,5,6,7}; - - /*! \class BusyLocker \brief Simple 'busy state' flag locker. @@ -411,11 +326,12 @@ QWidget* IdEditItem::createEditor() const // purpose : constructor //================================================================================= SMESHGUI_AddQuadraticElementDlg::SMESHGUI_AddQuadraticElementDlg( SMESHGUI* theModule, - const int theType ) + const SMDSAbs_EntityType theType ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), - myType( theType ), + myGeomType( theType ), + //myType( theType ), myBusy( false ) { setModal( false ); @@ -423,36 +339,42 @@ SMESHGUI_AddQuadraticElementDlg::SMESHGUI_AddQuadraticElementDlg( SMESHGUI* theM SalomeApp_Application* anApp = dynamic_cast (SUIT_Session::session()->activeApplication()); - + mySimulation = new SMESH::TElementSimulation (anApp); mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); QString anElementName; - switch ( myType ) { - case QUAD_EDGE: + switch ( myGeomType ) { + case SMDSEntity_Quad_Edge: anElementName = QString("QUADRATIC_EDGE"); break; - case QUAD_TRIANGLE: + case SMDSEntity_Quad_Triangle: anElementName = QString("QUADRATIC_TRIANGLE"); - break; - case QUAD_QUADRANGLE: + break; + case SMDSEntity_Quad_Quadrangle: anElementName = QString("QUADRATIC_QUADRANGLE"); break; - case QUAD_TETRAHEDRON: + case SMDSEntity_BiQuad_Quadrangle: + anElementName = QString("BIQUADRATIC_QUADRANGLE"); + break; + case SMDSEntity_Quad_Tetra: anElementName = QString("QUADRATIC_TETRAHEDRON"); break; - case QUAD_PYRAMID: + case SMDSEntity_Quad_Pyramid: anElementName = QString("QUADRATIC_PYRAMID"); break; - case QUAD_PENTAHEDRON: + case SMDSEntity_Quad_Penta: anElementName = QString("QUADRATIC_PENTAHEDRON"); break; - case QUAD_HEXAHEDRON: + case SMDSEntity_Quad_Hexa: anElementName = QString("QUADRATIC_HEXAHEDRON"); break; + case SMDSEntity_TriQuad_Hexa: + anElementName = QString("TRIQUADRATIC_HEXAHEDRON"); + break; default: - myType = QUAD_EDGE; + myGeomType = SMDSEntity_Quad_Edge; anElementName = QString("QUADRATIC_EDGE"); } @@ -460,12 +382,12 @@ SMESHGUI_AddQuadraticElementDlg::SMESHGUI_AddQuadraticElementDlg( SMESHGUI* theM QString caption = tr(QString("SMESH_ADD_%1_TITLE").arg(anElementName).toLatin1().data()); QString argumentsGrTitle = tr(QString("SMESH_ADD_%1").arg(anElementName).toLatin1().data()); QString constructorGrTitle = tr(QString("SMESH_%1").arg(anElementName).toLatin1().data()); - + QPixmap image0 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", iconName)); QPixmap image1 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT"))); setWindowTitle(caption); - + setSizeGripEnabled(true); QVBoxLayout* aDialogLayout = new QVBoxLayout(this); @@ -490,21 +412,58 @@ SMESHGUI_AddQuadraticElementDlg::SMESHGUI_AddQuadraticElementDlg( SMESHGUI* theM aGroupArgumentsLayout->setSpacing(SPACING); aGroupArgumentsLayout->setMargin(MARGIN); + // Corner nodes QLabel* aCornerNodesLabel = new QLabel(tr("SMESH_CORNER_NODES"), GroupArguments); - mySelectButton = new QPushButton(GroupArguments); - mySelectButton->setIcon(image1); + myCornerSelectButton = new QPushButton(GroupArguments); + myCornerSelectButton->setIcon(image1); myCornerNodes = new QLineEdit(GroupArguments); + // Mid-edge nodes myTable = new QTableWidget(GroupArguments); + // Mid-face nodes + myMidFaceLabel = new QLabel(tr("SMESH_MIDFACE_NODES"), GroupArguments); + myMidFaceSelectButton = new QPushButton(GroupArguments); + myMidFaceSelectButton->setIcon(image1); + myMidFaceNodes = new QLineEdit(GroupArguments); + myMidFaceNodes->setValidator(new SMESHGUI_IdValidator(this, 6)); + + // Central node + myCenterLabel = new QLabel(tr("SMESH_CENTER_NODE"), GroupArguments); + myCenterSelectButton = new QPushButton(GroupArguments); + myCenterSelectButton->setIcon(image1); + myCenterNode = new QLineEdit(GroupArguments); + myCenterNode->setValidator(new SMESHGUI_IdValidator(this, 1)); + myReverseCB = new QCheckBox(tr("SMESH_REVERSE"), GroupArguments); - aGroupArgumentsLayout->addWidget(aCornerNodesLabel, 0, 0); - aGroupArgumentsLayout->addWidget(mySelectButton, 0, 1); - aGroupArgumentsLayout->addWidget(myCornerNodes, 0, 2); - aGroupArgumentsLayout->addWidget(myTable, 1, 0, 1, 3); - aGroupArgumentsLayout->addWidget(myReverseCB, 2, 0, 1, 3); - + aGroupArgumentsLayout->addWidget(aCornerNodesLabel, 0, 0); + aGroupArgumentsLayout->addWidget(myCornerSelectButton, 0, 1); + aGroupArgumentsLayout->addWidget(myCornerNodes, 0, 2); + aGroupArgumentsLayout->addWidget(myTable, 1, 0, 1, 3); + aGroupArgumentsLayout->addWidget(myMidFaceLabel, 2, 0); + aGroupArgumentsLayout->addWidget(myMidFaceSelectButton, 2, 1); + aGroupArgumentsLayout->addWidget(myMidFaceNodes, 2, 2); + aGroupArgumentsLayout->addWidget(myCenterLabel, 3, 0); + aGroupArgumentsLayout->addWidget(myCenterSelectButton, 3, 1); + aGroupArgumentsLayout->addWidget(myCenterNode, 3, 2); + aGroupArgumentsLayout->addWidget(myReverseCB, 4, 0, 1, 3); + + /***************************************************************/ + GroupGroups = new QGroupBox( tr( "SMESH_ADD_TO_GROUP" ), this ); + GroupGroups->setCheckable( true ); + QHBoxLayout* GroupGroupsLayout = new QHBoxLayout(GroupGroups); + GroupGroupsLayout->setSpacing(SPACING); + GroupGroupsLayout->setMargin(MARGIN); + + TextLabel_GroupName = new QLabel( tr( "SMESH_GROUP" ), GroupGroups ); + ComboBox_GroupName = new QComboBox( GroupGroups ); + ComboBox_GroupName->setEditable( true ); + ComboBox_GroupName->setInsertPolicy( QComboBox::NoInsert ); + + GroupGroupsLayout->addWidget( TextLabel_GroupName ); + GroupGroupsLayout->addWidget( ComboBox_GroupName, 1 ); + /***************************************************************/ GroupButtons = new QGroupBox(this); QHBoxLayout* aGroupButtonsLayout = new QHBoxLayout(GroupButtons); @@ -532,6 +491,7 @@ SMESHGUI_AddQuadraticElementDlg::SMESHGUI_AddQuadraticElementDlg( SMESHGUI* theM /***************************************************************/ aDialogLayout->addWidget(GroupConstructors); aDialogLayout->addWidget(GroupArguments); + aDialogLayout->addWidget(GroupGroups); aDialogLayout->addWidget(GroupButtons); Init(); /* Initialisations */ @@ -554,49 +514,74 @@ void SMESHGUI_AddQuadraticElementDlg::Init() { myRadioButton1->setChecked(true); mySMESHGUI->SetActiveDialogBox((QDialog*)this); - + + /* reset "Add to group" control */ + GroupGroups->setChecked( false ); + myActor = 0; + myNbMidFaceNodes = 0; + myNbCenterNodes = 0; int aNumRows; - switch (myType) { - case QUAD_EDGE: + switch (myGeomType) { + case SMDSEntity_Quad_Edge: aNumRows = 1; myNbCorners = 2; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_edges break; - case QUAD_TRIANGLE: + case SMDSEntity_Quad_Triangle: aNumRows = 3; myNbCorners = 3; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_triangles break; - case QUAD_QUADRANGLE: + case SMDSEntity_Quad_Quadrangle: + aNumRows = 4; + myNbCorners = 4; + myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_quadrangles + break; + case SMDSEntity_BiQuad_Quadrangle: aNumRows = 4; myNbCorners = 4; + myNbCenterNodes = 1; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_quadrangles break; - case QUAD_TETRAHEDRON: + case SMDSEntity_Quad_Tetra: aNumRows = 6; myNbCorners = 4; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_tetrahedrons break; - case QUAD_PYRAMID: + case SMDSEntity_Quad_Pyramid: aNumRows = 8; myNbCorners = 5; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_pyramids break; - case QUAD_PENTAHEDRON: + case SMDSEntity_Quad_Penta: aNumRows = 9; myNbCorners = 6; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_pentahedrons - break; - case QUAD_HEXAHEDRON: + break; + case SMDSEntity_Quad_Hexa: + aNumRows = 12; + myNbCorners = 8; + myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_hexahedrons + break; + case SMDSEntity_TriQuad_Hexa: aNumRows = 12; myNbCorners = 8; + myNbMidFaceNodes = 6; + myNbCenterNodes = 1; myHelpFileName = "adding_quadratic_elements_page.html#?"; //Adding_hexahedrons break; } - + + myMidFaceLabel ->setVisible( myNbMidFaceNodes ); + myMidFaceSelectButton->setVisible( myNbMidFaceNodes ); + myMidFaceNodes ->setVisible( myNbMidFaceNodes ); + myCenterLabel ->setVisible( myNbCenterNodes ); + myCenterSelectButton ->setVisible( myNbCenterNodes ); + myCenterNode ->setVisible( myNbCenterNodes ); + myCornerNodes->setValidator(new SMESHGUI_IdValidator(this, myNbCorners)); /* initialize table */ @@ -608,7 +593,7 @@ void SMESHGUI_AddQuadraticElementDlg::Init() aColLabels.append(tr("SMESH_MIDDLE")); aColLabels.append(tr("SMESH_LAST")); myTable->setHorizontalHeaderLabels(aColLabels); - + for ( int col = 0; col < myTable->columnCount(); col++ ) myTable->setColumnWidth(col, 80); @@ -616,7 +601,7 @@ void SMESHGUI_AddQuadraticElementDlg::Init() //myTable->setColumnReadOnly(2, true); // VSR: TODO myTable->setEnabled( false ); - + for ( int row = 0; row < myTable->rowCount(); row++ ) { myTable->setItem( row, 0, new QTableWidgetItem( "" ) ); @@ -629,13 +614,17 @@ void SMESHGUI_AddQuadraticElementDlg::Init() myTable->setItem( row, 2, new QTableWidgetItem( "" ) ); myTable->item( row, 2 )->setFlags(0); } - + /* signals and slots connections */ - connect(mySelectButton, SIGNAL(clicked()), SLOT(SetEditCorners())); + connect(myCornerSelectButton, SIGNAL(clicked()), SLOT(SetCurrentSelection())); + connect(myMidFaceSelectButton, SIGNAL(clicked()), SLOT(SetCurrentSelection())); + connect(myCenterSelectButton, SIGNAL(clicked()), SLOT(SetCurrentSelection())); connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(SelectionIntoArgument())); connect(myTable, SIGNAL(cellDoubleClicked(int, int)), SLOT(onCellDoubleClicked(int, int))); connect(myTable, SIGNAL(cellChanged (int, int)), SLOT(onCellTextChange(int, int))); connect(myCornerNodes, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); + connect(myMidFaceNodes, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); + connect(myCenterNode, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); connect(myReverseCB, SIGNAL(stateChanged(int)), SLOT(onReverse(int))); connect(buttonOk, SIGNAL(clicked()), SLOT(ClickOnOk())); @@ -645,6 +634,9 @@ void SMESHGUI_AddQuadraticElementDlg::Init() connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), SLOT(DeactivateActiveDialog())); connect(mySMESHGUI, SIGNAL (SignalStudyFrameChanged()), SLOT(ClickOnCancel())); + connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), SLOT(ClickOnCancel())); + + myCurrentLineEdit = myCornerNodes; // set selection mode SMESH::SetPointRepresentation(true); @@ -652,7 +644,7 @@ void SMESHGUI_AddQuadraticElementDlg::Init() if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->SetSelectionMode( NodeSelection ); - SetEditCorners(); + SelectionIntoArgument(); } //================================================================================= @@ -661,6 +653,9 @@ void SMESHGUI_AddQuadraticElementDlg::Init() //================================================================================= void SMESHGUI_AddQuadraticElementDlg::ClickOnApply() { + if( !isValid() ) + return; + if ( mySMESHGUI->isActiveStudyLocked() || myBusy || !IsValid() ) return; @@ -668,59 +663,137 @@ void SMESHGUI_AddQuadraticElementDlg::ClickOnApply() std::vector anIds; - switch (myType) { - case QUAD_EDGE: + switch (myGeomType) { + case SMDSEntity_Quad_Edge: anIds.push_back(myTable->item(0, 0)->text().toInt()); anIds.push_back(myTable->item(0, 2)->text().toInt()); anIds.push_back(myTable->item(0, 1)->text().toInt()); break; - case QUAD_TRIANGLE: - case QUAD_QUADRANGLE: - case QUAD_TETRAHEDRON: - case QUAD_PYRAMID: - case QUAD_PENTAHEDRON: - case QUAD_HEXAHEDRON: + case SMDSEntity_Quad_Triangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + case SMDSEntity_Quad_Tetra: + case SMDSEntity_Quad_Pyramid: + case SMDSEntity_Quad_Penta: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: for ( int row = 0; row < myNbCorners; row++ ) anIds.push_back(myTable->item(row, 0)->text().toInt()); for ( int row = 0; row < myTable->rowCount(); row++ ) anIds.push_back(myTable->item(row, 1)->text().toInt()); + if ( myNbMidFaceNodes ) + { + QStringList aListId = myMidFaceNodes->text().split(" ", QString::SkipEmptyParts); + for (int i = 0; i < aListId.count(); i++) + anIds.push_back( aListId[ i ].toInt() ); + } + if ( myNbCenterNodes ) + { + QStringList aListId = myCenterNode->text().split(" ", QString::SkipEmptyParts); + anIds.push_back( aListId[ 0 ].toInt() ); + } break; } if ( myReverseCB->isChecked()) - SMESH::ReverseConnectivity( anIds, myType ); - + ReverseConnectivity( anIds, myGeomType, /*toReverse=*/true, /*toVtkOrder=*/false ); + int aNumberOfIds = anIds.size(); SMESH::long_array_var anArrayOfIdeces = new SMESH::long_array; anArrayOfIdeces->length( aNumberOfIds ); - + for (int i = 0; i < aNumberOfIds; i++) anArrayOfIdeces[i] = anIds[ i ]; + bool addToGroup = GroupGroups->isChecked(); + QString aGroupName; + + SMESH::SMESH_GroupBase_var aGroup; + int idx = 0; + if( addToGroup ) { + aGroupName = ComboBox_GroupName->currentText(); + for ( int i = 1; i < ComboBox_GroupName->count(); i++ ) { + QString aName = ComboBox_GroupName->itemText( i ); + if ( aGroupName == aName && ( i == ComboBox_GroupName->currentIndex() || idx == 0 ) ) + idx = i; + } + if ( idx > 0 && idx < myGroups.count() ) { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( myGroups[idx-1] ); + if ( !aGeomGroup->_is_nil() ) { + int res = SUIT_MessageBox::question( this, tr( "SMESH_WRN_WARNING" ), + tr( "MESH_STANDALONE_GRP_CHOSEN" ).arg( aGroupName ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 0, 1 ); + if ( res == 1 ) return; + } + aGroup = myGroups[idx-1]; + } + } + + SMESH::ElementType anElementType; + long anElemId = -1; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - switch (myType) { - case QUAD_EDGE: - aMeshEditor->AddEdge(anArrayOfIdeces.inout()); break; - case QUAD_TRIANGLE: - case QUAD_QUADRANGLE: - aMeshEditor->AddFace(anArrayOfIdeces.inout()); break; - case QUAD_TETRAHEDRON: - case QUAD_PYRAMID: - case QUAD_PENTAHEDRON: - case QUAD_HEXAHEDRON: - aMeshEditor->AddVolume(anArrayOfIdeces.inout()); break; + switch (myGeomType) { + case SMDSEntity_Quad_Edge: + anElementType = SMESH::EDGE; + anElemId = aMeshEditor->AddEdge(anArrayOfIdeces.inout()); break; + case SMDSEntity_Quad_Triangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + anElementType = SMESH::FACE; + anElemId = aMeshEditor->AddFace(anArrayOfIdeces.inout()); break; + case SMDSEntity_Quad_Tetra: + case SMDSEntity_Quad_Pyramid: + case SMDSEntity_Quad_Penta: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + anElementType = SMESH::VOLUME; + anElemId = aMeshEditor->AddVolume(anArrayOfIdeces.inout()); break; + default: break; + } + + if ( anElemId != -1 && addToGroup && !aGroupName.isEmpty() ) { + SMESH::SMESH_Group_var aGroupUsed; + if ( aGroup->_is_nil() ) { + // create new group + aGroupUsed = SMESH::AddGroup( myMesh, anElementType, aGroupName ); + if ( !aGroupUsed->_is_nil() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroupUsed)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + else { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( aGroup ); + if ( !aGeomGroup->_is_nil() ) { + aGroupUsed = myMesh->ConvertToStandalone( aGeomGroup ); + if ( !aGroupUsed->_is_nil() && idx > 0 ) { + myGroups[idx-1] = SMESH::SMESH_GroupBase::_duplicate(aGroupUsed); + SMESHGUI::GetSMESHGUI()->getApp()->updateObjectBrowser(); + } + } + else + aGroupUsed = SMESH::SMESH_Group::_narrow( aGroup ); + } + + if ( !aGroupUsed->_is_nil() ) { + SMESH::long_array_var anIdList = new SMESH::long_array; + anIdList->length( 1 ); + anIdList[0] = anElemId; + aGroupUsed->Add( anIdList.inout() ); + } } - + SALOME_ListIO aList; aList.Append( myActor->getIO() ); mySelector->ClearIndex(); mySelectionMgr->setSelectedObjects( aList, false ); mySimulation->SetVisibility(false); SMESH::UpdateView(); - + UpdateTable(); - SetEditCorners(); + SetCurrentSelection(); updateButtons(); + + SMESHGUI::Modified(); } //================================================================================= @@ -756,7 +829,7 @@ void SMESHGUI_AddQuadraticElementDlg::ClickOnCancel() void SMESHGUI_AddQuadraticElementDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { QString platform; @@ -766,10 +839,10 @@ void SMESHGUI_AddQuadraticElementDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -781,7 +854,7 @@ void SMESHGUI_AddQuadraticElementDlg::onTextChange (const QString& theNewText) { if (myBusy) return; BusyLocker lock( myBusy ); - + mySimulation->SetVisibility(false); // hilight entered nodes @@ -789,31 +862,37 @@ void SMESHGUI_AddQuadraticElementDlg::onTextChange (const QString& theNewText) if (myActor) aMesh = myActor->GetObject()->GetMesh(); + QLineEdit* send = (QLineEdit*)sender(); + if (send == myCornerNodes || + send == myMidFaceNodes || + send == myCenterNode) + myCurrentLineEdit = send; + if (aMesh) { TColStd_MapOfInteger newIndices; - + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); bool allOk = true; for (int i = 0; i < aListId.count(); i++) { if ( const SMDS_MeshNode * n = aMesh->FindNode( aListId[ i ].toInt() ) ) { - newIndices.Add( n->GetID() ); + newIndices.Add( n->GetID() ); } else { - allOk = false; - break; + allOk = false; + break; } } - + mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->highlight( myActor->getIO(), true, true ); - - if ( sender() == myCornerNodes ) + + if ( myCurrentLineEdit == myCornerNodes ) UpdateTable( allOk ); } - + updateButtons(); displaySimulation(); } @@ -826,55 +905,93 @@ void SMESHGUI_AddQuadraticElementDlg::SelectionIntoArgument() { if (myBusy) return; BusyLocker lock( myBusy ); - - if ( myIsEditCorners ) + + QString aCurrentEntry = myEntry; + + if ( myCurrentLineEdit ) { // clear myActor = 0; - - myCornerNodes->setText(""); - + + myCurrentLineEdit->setText(""); + if (!GroupButtons->isEnabled()) // inactive return; - + mySimulation->SetVisibility(false); - + // get selected mesh SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); - + if (aList.Extent() != 1) { UpdateTable(); updateButtons(); return; } - + Handle(SALOME_InteractiveObject) anIO = aList.First(); + myEntry = anIO->getEntry(); myMesh = SMESH::GetMeshByIO(anIO); if (myMesh->_is_nil()) { updateButtons(); return; } - + myActor = SMESH::FindActorByEntry(anIO->getEntry()); - + } - + + // process groups + if ( !myMesh->_is_nil() && myEntry != aCurrentEntry ) { + SMESH::ElementType anElementType; + switch ( myGeomType ) { + case SMDSEntity_Quad_Edge: + anElementType = SMESH::EDGE; break; + case SMDSEntity_Quad_Triangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + anElementType = SMESH::FACE; break; + case SMDSEntity_Quad_Tetra: + case SMDSEntity_Quad_Pyramid: + case SMDSEntity_Quad_Penta: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + anElementType = SMESH::VOLUME; break; + } + myGroups.clear(); + ComboBox_GroupName->clear(); + ComboBox_GroupName->addItem( QString() ); + SMESH::ListOfGroups aListOfGroups = *myMesh->GetGroups(); + for ( int i = 0, n = aListOfGroups.length(); i < n; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i]; + if ( !aGroup->_is_nil() && aGroup->GetType() == anElementType ) { + QString aGroupName( aGroup->GetName() ); + if ( !aGroupName.isEmpty() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + } + } + if (!myActor) { updateButtons(); return; } - + // get selected nodes QString aString = ""; int nbNodes = SMESH::GetNameOfSelectedNodes(mySelector,myActor->getIO(),aString); - - if ( myIsEditCorners ) + + if ( myCurrentLineEdit ) { - myCornerNodes->setText(aString); - - UpdateTable(); + if ( myCurrentLineEdit != myCenterNode || nbNodes == 1 ) + myCurrentLineEdit->setText(aString); + + if ( myCurrentLineEdit == myCornerNodes ) + UpdateTable(); } else if ( myTable->isEnabled() && nbNodes == 1 ) { @@ -882,7 +999,7 @@ void SMESHGUI_AddQuadraticElementDlg::SelectionIntoArgument() if ( theCol == 1 ) myTable->item(theRow, 1)->setText(aString); } - + updateButtons(); displaySimulation(); } @@ -896,13 +1013,13 @@ void SMESHGUI_AddQuadraticElementDlg::displaySimulation() if ( IsValid() ) { SMESH::TElementSimulation::TVTKIds anIds; - + // Collect ids from the dialog int anID; bool ok; int aDisplayMode = VTK_SURFACE; - - if ( myType == QUAD_EDGE ) + + if ( myGeomType == SMDSEntity_Quad_Edge ) { anIds.push_back( myActor->GetObject()->GetNodeVTKId( myTable->item(0, 0)->text().toInt() ) ); anIds.push_back( myActor->GetObject()->GetNodeVTKId( myTable->item(0, 2)->text().toInt() ) ); @@ -914,20 +1031,31 @@ void SMESHGUI_AddQuadraticElementDlg::displaySimulation() else { for ( int row = 0; row < myNbCorners; row++ ) - anIds.push_back( myActor->GetObject()->GetNodeVTKId( myTable->item(row, 0)->text().toInt() ) ); - + anIds.push_back( myActor->GetObject()->GetNodeVTKId( myTable->item(row, 0)->text().toInt() ) ); + for ( int row = 0; row < myTable->rowCount(); row++ ) { - anID = myTable->item(row, 1)->text().toInt(&ok); - if (!ok) { - anID = myTable->item(row, 0)->text().toInt(); - aDisplayMode = VTK_WIREFRAME; - } - anIds.push_back( myActor->GetObject()->GetNodeVTKId(anID) ); + anID = myTable->item(row, 1)->text().toInt(&ok); + if (!ok) { + anID = myTable->item(row, 0)->text().toInt(); + aDisplayMode = VTK_WIREFRAME; + } + anIds.push_back( myActor->GetObject()->GetNodeVTKId(anID) ); + } + if ( myNbMidFaceNodes ) + { + QStringList aListId = myMidFaceNodes->text().split(" ", QString::SkipEmptyParts); + for (int i = 0; i < aListId.count(); i++) + anIds.push_back( myActor->GetObject()->GetNodeVTKId( aListId[ i ].toInt() )); + } + if ( myNbCenterNodes ) + { + QStringList aListId = myCenterNode->text().split(" ", QString::SkipEmptyParts); + anIds.push_back( myActor->GetObject()->GetNodeVTKId( aListId[ 0 ].toInt() )); } } - - mySimulation->SetPosition(myActor,myType,anIds,aDisplayMode,myReverseCB->isChecked()); + + mySimulation->SetPosition(myActor,myGeomType,anIds,aDisplayMode,myReverseCB->isChecked()); } else { @@ -937,15 +1065,26 @@ void SMESHGUI_AddQuadraticElementDlg::displaySimulation() } //================================================================================= -// function : SetEditCorners() +// function : SetCurrentSelection() // purpose : //================================================================================= -void SMESHGUI_AddQuadraticElementDlg::SetEditCorners() +void SMESHGUI_AddQuadraticElementDlg::SetCurrentSelection() { - myCornerNodes->setFocus(); - myIsEditCorners = true; - SelectionIntoArgument(); - updateButtons(); + QPushButton* send = (QPushButton*)sender(); + myCurrentLineEdit = 0; + + if (send == myCornerSelectButton) + myCurrentLineEdit = myCornerNodes; + else if ( send == myMidFaceSelectButton ) + myCurrentLineEdit = myMidFaceNodes; + else if ( send == myCenterSelectButton ) + myCurrentLineEdit = myCenterNode; + + if ( myCurrentLineEdit ) + { + myCurrentLineEdit->setFocus(); + SelectionIntoArgument(); + } } //================================================================================= @@ -1040,19 +1179,37 @@ bool SMESHGUI_AddQuadraticElementDlg::IsValid() return false; bool ok; - + std::set< int > okIDs; for ( int row = 0; row < myTable->rowCount(); row++ ) { int anID = myTable->item(row, 1)->text().toInt(&ok); if ( !ok ) return false; - + const SMDS_MeshNode * aNode = aMesh->FindNode(anID); if ( !aNode ) return false; + okIDs.insert( anID ); } - - return true; + + QStringList aListId; + if ( myNbMidFaceNodes ) + aListId += myMidFaceNodes->text().split(" ", QString::SkipEmptyParts); + if ( myNbCenterNodes ) + aListId += myCenterNode->text().split(" ", QString::SkipEmptyParts); + + for (int i = 0; i < aListId.count(); i++) + { + int anID = aListId[ i ].toInt(&ok); + if ( !ok ) + return false; + + if ( !aMesh->FindNode(anID) ) + return false; + okIDs.insert( anID ); + } + + return okIDs.size() == myTable->rowCount() + myNbMidFaceNodes + myNbCenterNodes; } //================================================================================= @@ -1062,53 +1219,55 @@ bool SMESHGUI_AddQuadraticElementDlg::IsValid() void SMESHGUI_AddQuadraticElementDlg::UpdateTable( bool theConersValidity ) { QStringList aListCorners = myCornerNodes->text().split(" ", QString::SkipEmptyParts); - + if ( aListCorners.count() == myNbCorners && theConersValidity ) { myTable->setEnabled( true ); - - // clear the Middle column + + // clear the Middle column for ( int row = 0; row < myTable->rowCount(); row++ ) myTable->item( row, 1 )->setText(""); - + int* aFirstColIds; int* aLastColIds; - - switch (myType) { - case QUAD_EDGE: + + switch (myGeomType) { + case SMDSEntity_Quad_Edge: aFirstColIds = FirstEdgeIds; aLastColIds = LastEdgeIds; break; - case QUAD_TRIANGLE: + case SMDSEntity_Quad_Triangle: aFirstColIds = FirstTriangleIds; aLastColIds = LastTriangleIds; break; - case QUAD_QUADRANGLE: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: aFirstColIds = FirstQuadrangleIds; aLastColIds = LastQuadrangleIds; break; - case QUAD_TETRAHEDRON: + case SMDSEntity_Quad_Tetra: aFirstColIds = FirstTetrahedronIds; aLastColIds = LastTetrahedronIds; break; - case QUAD_PYRAMID: + case SMDSEntity_Quad_Pyramid: aFirstColIds = FirstPyramidIds; aLastColIds = LastPyramidIds; break; - case QUAD_PENTAHEDRON: + case SMDSEntity_Quad_Penta: aFirstColIds = FirstPentahedronIds; aLastColIds = LastPentahedronIds; - break; - case QUAD_HEXAHEDRON: + break; + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: aFirstColIds = FirstHexahedronIds; aLastColIds = LastHexahedronIds; break; } - + // fill the First and the Last columns for (int i = 0, iEnd = myTable->rowCount(); i < iEnd; i++) myTable->item( i, 0 )->setText( aListCorners[ aFirstColIds[i] ] ); - + for (int i = 0, iEnd = myTable->rowCount(); i < iEnd; i++) myTable->item( i, 2 )->setText( aListCorners[ aLastColIds[i] ] ); } @@ -1117,8 +1276,8 @@ void SMESHGUI_AddQuadraticElementDlg::UpdateTable( bool theConersValidity ) // clear table for ( int row = 0; row < myTable->rowCount(); row++ ) for ( int col = 0; col < myTable->columnCount(); col++ ) - if ( QTableWidgetItem* aTWI = myTable->item(row, col) ) aTWI->setText(""); - + if ( QTableWidgetItem* aTWI = myTable->item(row, col) ) aTWI->setText(""); + myTable->setEnabled( false ); } } @@ -1130,7 +1289,7 @@ void SMESHGUI_AddQuadraticElementDlg::UpdateTable( bool theConersValidity ) //================================================================================= void SMESHGUI_AddQuadraticElementDlg::onCellDoubleClicked( int theRow, int theCol ) { - myIsEditCorners = false; + myCurrentLineEdit = 0; displaySimulation(); updateButtons(); } @@ -1142,7 +1301,7 @@ void SMESHGUI_AddQuadraticElementDlg::onCellDoubleClicked( int theRow, int theCo //================================================================================= void SMESHGUI_AddQuadraticElementDlg::onCellTextChange(int theRow, int theCol) { - myIsEditCorners = false; + myCurrentLineEdit = 0; displaySimulation(); updateButtons(); } @@ -1169,3 +1328,16 @@ void SMESHGUI_AddQuadraticElementDlg::updateButtons() buttonOk->setEnabled( valid ); buttonApply->setEnabled( valid ); } + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= +bool SMESHGUI_AddQuadraticElementDlg::isValid() +{ + if( GroupGroups->isChecked() && ComboBox_GroupName->currentText().isEmpty() ) { + SUIT_MessageBox::warning( this, tr( "SMESH_WRN_WARNING" ), tr( "GROUP_NAME_IS_EMPTY" ) ); + return false; + } + return true; +} diff --git a/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.h b/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.h index a048a6760..3b2c360b2 100644 --- a/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.h +++ b/src/SMESHGUI/SMESHGUI_AddQuadraticElementDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_AddMeshElementDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -28,6 +29,7 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +#include "SMDSAbs_ElementType.hxx" // Qt includes #include @@ -36,7 +38,10 @@ #include #include CORBA_SERVER_HEADER(SMESH_Mesh) +class QComboBox; +class QFrame; class QGroupBox; +class QLabel; class QLineEdit; class QPushButton; class QRadioButton; @@ -52,9 +57,6 @@ namespace SMESH struct TElementSimulation; } -enum { QUAD_EDGE, QUAD_TRIANGLE, QUAD_QUADRANGLE, QUAD_TETRAHEDRON, - QUAD_PYRAMID, QUAD_PENTAHEDRON, QUAD_HEXAHEDRON }; - //================================================================================= // class : SMESHGUI_AddQuadraticElementDlg // purpose : @@ -64,10 +66,12 @@ class SMESHGUI_EXPORT SMESHGUI_AddQuadraticElementDlg : public QDialog Q_OBJECT public: - SMESHGUI_AddQuadraticElementDlg( SMESHGUI*, const int ); + SMESHGUI_AddQuadraticElementDlg( SMESHGUI*, const SMDSAbs_EntityType ); ~SMESHGUI_AddQuadraticElementDlg(); private: + typedef QList GrpList; + void Init(); void closeEvent( QCloseEvent* ); void hideEvent( QHideEvent* ); /* ESC key */ @@ -78,28 +82,44 @@ private: bool IsValid(); void updateButtons(); + bool isValid(); + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ int myNbCorners; /* The required number of corners */ + int myNbMidFaceNodes; + int myNbCenterNodes; bool myBusy; SVTK_Selector* mySelector; SMESH::SMESH_Mesh_var myMesh; SMESH_Actor* myActor; SMESH::TElementSimulation* mySimulation; + QString myEntry; + GrpList myGroups; - int myType; - bool myIsEditCorners; + SMDSAbs_EntityType myGeomType; + QLineEdit* myCurrentLineEdit; QGroupBox* GroupConstructors; QRadioButton* myRadioButton1; QGroupBox* GroupArguments; + QPushButton* myCornerSelectButton; QLineEdit* myCornerNodes; - QPushButton* mySelectButton; + QLabel* myMidFaceLabel; + QPushButton* myMidFaceSelectButton; + QLineEdit* myMidFaceNodes; + QLabel* myCenterLabel; + QPushButton* myCenterSelectButton; + QLineEdit* myCenterNode; QTableWidget* myTable; QCheckBox* myReverseCB; + QGroupBox* GroupGroups; + QLabel* TextLabel_GroupName; + QComboBox* ComboBox_GroupName; + QGroupBox* GroupButtons; QPushButton* buttonOk; QPushButton* buttonCancel; @@ -118,7 +138,7 @@ private slots: void ClickOnCancel(); void ClickOnApply(); void ClickOnHelp(); - void SetEditCorners(); + void SetCurrentSelection(); void SelectionIntoArgument(); void DeactivateActiveDialog(); void ActivateThisDialog(); diff --git a/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.cxx b/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.cxx index 2d6b7435b..edee4cecb 100644 --- a/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_BuildCompoundDlg.cxx -// Author : Alexander KOVALEV, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_BuildCompoundDlg.cxx +// Author : Alexander KOVALEV, Open CASCADE S.A.S. +// SMESH includes + #include "SMESHGUI_BuildCompoundDlg.h" #include "SMESHGUI.h" @@ -66,6 +65,10 @@ #define SPACING 6 #define MARGIN 11 +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //================================================================================= // name : SMESHGUI_BuildCompoundDlg // Purpose : @@ -73,7 +76,8 @@ SMESHGUI_BuildCompoundDlg::SMESHGUI_BuildCompoundDlg( SMESHGUI* theModule ) : QDialog(SMESH::GetDesktop(theModule)), mySMESHGUI(theModule), - mySelectionMgr(SMESH::GetSelectionMgr(theModule)) + mySelectionMgr(SMESH::GetSelectionMgr(theModule)), + myIsApplyAndClose( false ) { setModal(false); setAttribute(Qt::WA_DeleteOnClose, true); @@ -137,12 +141,12 @@ SMESHGUI_BuildCompoundDlg::SMESHGUI_BuildCompoundDlg( SMESHGUI* theModule ) TextLabelTol = new QLabel(tr("SMESH_TOLERANCE"), GroupArgs); TextLabelTol->setAlignment(Qt::AlignCenter); SpinBoxTol = new SMESHGUI_SpinBox(GroupArgs); - SpinBoxTol->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, 6); + SpinBoxTol->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, "len_tol_precision" ); GroupArgsLayout->addWidget(TextLabelMeshes, 0, 0); GroupArgsLayout->addWidget(SelectButton, 0, 1); GroupArgsLayout->addWidget(LineEditMeshes, 0, 2, 1, 2); - GroupArgsLayout->addWidget(TextLabelUnion, 1, 0, 1, 3); + GroupArgsLayout->addWidget(TextLabelUnion, 1, 0, 1, 3); GroupArgsLayout->addWidget(ComboBoxUnion, 1, 3); GroupArgsLayout->addWidget(CheckBoxCommon, 2, 0, 1, 4); GroupArgsLayout->addWidget(CheckBoxMerge, 3, 0, 1, 4); @@ -289,29 +293,37 @@ bool SMESHGUI_BuildCompoundDlg::ClickOnApply() if (!isValid()) return false; - if (!myMesh->_is_nil()) { + SMESH::SMESH_Mesh_var aCompoundMesh; + + if (!myMesh->_is_nil()) + { QStringList aParameters; aParameters << (CheckBoxMerge->isChecked() ? SpinBoxTol->text() : QString(" ")); - try { + + QStringList anEntryList; + try { SUIT_OverrideCursor aWaitCursor; + myMeshArray[0]->SetParameters( aParameters.join(":").toLatin1().constData() ); + SMESH::SMESH_Gen_var aSMESHGen = SMESHGUI::GetSMESHGen(); // concatenate meshes - SMESH::SMESH_Mesh_var aCompoundMesh; if(CheckBoxCommon->isChecked()) - aCompoundMesh = aSMESHGen->ConcatenateWithGroups(myMeshArray, - !(ComboBoxUnion->currentIndex()), - CheckBoxMerge->isChecked(), - SpinBoxTol->GetValue()); + aCompoundMesh = aSMESHGen->ConcatenateWithGroups(myMeshArray, + !(ComboBoxUnion->currentIndex()), + CheckBoxMerge->isChecked(), + SpinBoxTol->GetValue()); else - aCompoundMesh = aSMESHGen->Concatenate(myMeshArray, - !(ComboBoxUnion->currentIndex()), - CheckBoxMerge->isChecked(), - SpinBoxTol->GetValue()); - - aCompoundMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - - SMESH::SetName( SMESH::FindSObject( aCompoundMesh ), LineEditName->text() ); + aCompoundMesh = aSMESHGen->Concatenate(myMeshArray, + !(ComboBoxUnion->currentIndex()), + CheckBoxMerge->isChecked(), + SpinBoxTol->GetValue()); + + _PTR(SObject) aSO = SMESH::FindSObject( aCompoundMesh ); + if( aSO ) { + SMESH::SetName( aSO, LineEditName->text() ); + anEntryList.append( aSO->GetID().c_str() ); + } mySMESHGUI->updateObjBrowser(); } catch(...) { return false; @@ -319,8 +331,30 @@ bool SMESHGUI_BuildCompoundDlg::ClickOnApply() LineEditName->setText(GetDefaultName(tr("COMPOUND_MESH"))); - //mySelectionMgr->clearSelected(); - SMESH::UpdateView(); + // IPAL21468 Compound is hidden after creation. + if ( SMESHGUI::automaticUpdate() ) { + mySelectionMgr->clearSelected(); + SMESH::UpdateView(); + + _PTR(SObject) aSO = SMESH::FindSObject(aCompoundMesh.in()); + if ( SMESH_Actor* anActor = SMESH::CreateActor(aSO->GetStudy(), aSO->GetID().c_str()) ) + SMESH::DisplayActor(SMESH::GetActiveWindow(), anActor); + }// end IPAL21468 + + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + + SMESHGUI::Modified(); + +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + if (!CORBA::is_nil(aCompoundMesh)) + aCompoundMesh->UnRegister(); +#endif + return true; } return false; @@ -332,6 +366,7 @@ bool SMESHGUI_BuildCompoundDlg::ClickOnApply() //================================================================================= void SMESHGUI_BuildCompoundDlg::ClickOnOk() { + setIsApplyAndClose( true ); if (ClickOnApply()) ClickOnCancel(); } @@ -356,14 +391,14 @@ void SMESHGUI_BuildCompoundDlg::ClickOnCancel() void SMESHGUI_BuildCompoundDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - "application")). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + "application")). + arg(myHelpFileName)); } } @@ -491,7 +526,6 @@ void SMESHGUI_BuildCompoundDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_BuildCompoundDlg::onSelectMerge(bool toMerge) { - TextLabelTol->setEnabled(toMerge); SpinBoxTol->setEnabled(toMerge); if(!toMerge) @@ -518,3 +552,23 @@ bool SMESHGUI_BuildCompoundDlg::isValid() } return true; } + +//================================================================ +// function : setIsApplyAndClose +// Purpose : Set value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +void SMESHGUI_BuildCompoundDlg::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +//================================================================ +// function : isApplyAndClose +// Purpose : Get value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +bool SMESHGUI_BuildCompoundDlg::isApplyAndClose() const +{ + return myIsApplyAndClose; +} diff --git a/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.h b/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.h index c1085739d..6230022e8 100644 --- a/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.h +++ b/src/SMESHGUI/SMESHGUI_BuildCompoundDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_BuildCompoundDlg.h // Author : Alexander KOVALEV, Open CASCADE S.A.S. @@ -73,6 +74,9 @@ private: void keyPressEvent( QKeyEvent* ); bool isValid(); + + void setIsApplyAndClose( const bool theFlag ); + bool isApplyAndClose() const; private: SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ @@ -109,6 +113,8 @@ private: QString myHelpFileName; + bool myIsApplyAndClose; + private slots: void ClickOnOk(); void ClickOnCancel(); diff --git a/src/SMESHGUI/SMESHGUI_ClippingDlg.cxx b/src/SMESHGUI/SMESHGUI_ClippingDlg.cxx index 6f2d9293e..3143626c6 100644 --- a/src/SMESHGUI/SMESHGUI_ClippingDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ClippingDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ClippingDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -40,11 +41,15 @@ #include #include #include +#include #include +#include + #include -#include + +#include #include @@ -58,138 +63,139 @@ #include #include #include +#include // VTK includes #include -#include #include #include #include #include +#include #define SPACING 6 #define MARGIN 11 -class OrientedPlane: public vtkPlane +//================================================================================= +// class : OrientedPlane +// purpose : +//================================================================================= +SMESH::OrientedPlane* SMESH::OrientedPlane::New() { - SVTK_ViewWindow* myViewWindow; - - vtkDataSetMapper* myMapper; - -public: - static OrientedPlane *New() - { - return new OrientedPlane(); - } - static OrientedPlane *New(SVTK_ViewWindow* theViewWindow) - { - return new OrientedPlane(theViewWindow); - } - vtkTypeMacro (OrientedPlane, vtkPlane); - - SMESH::Orientation myOrientation; - float myDistance; - double myAngle[2]; - - vtkPlaneSource* myPlaneSource; - SALOME_Actor *myActor; - - void SetOrientation (SMESH::Orientation theOrientation) { myOrientation = theOrientation; } - SMESH::Orientation GetOrientation() { return myOrientation; } + return new OrientedPlane(); +} - void SetDistance (float theDistance) { myDistance = theDistance; } - float GetDistance() { return myDistance; } +SMESH::OrientedPlane* SMESH::OrientedPlane::New(SVTK_ViewWindow* theViewWindow) +{ + return new OrientedPlane(theViewWindow); +} - void ShallowCopy (OrientedPlane* theOrientedPlane) - { - SetNormal(theOrientedPlane->GetNormal()); - SetOrigin(theOrientedPlane->GetOrigin()); +void SMESH::OrientedPlane::ShallowCopy(SMESH::OrientedPlane* theOrientedPlane) +{ + SetNormal(theOrientedPlane->GetNormal()); + SetOrigin(theOrientedPlane->GetOrigin()); - myOrientation = theOrientedPlane->GetOrientation(); - myDistance = theOrientedPlane->GetDistance(); + myOrientation = theOrientedPlane->GetOrientation(); + myDistance = theOrientedPlane->GetDistance(); - myAngle[0] = theOrientedPlane->myAngle[0]; - myAngle[1] = theOrientedPlane->myAngle[1]; + myAngle[0] = theOrientedPlane->myAngle[0]; + myAngle[1] = theOrientedPlane->myAngle[1]; - myPlaneSource->SetNormal(theOrientedPlane->myPlaneSource->GetNormal()); - myPlaneSource->SetOrigin(theOrientedPlane->myPlaneSource->GetOrigin()); - myPlaneSource->SetPoint1(theOrientedPlane->myPlaneSource->GetPoint1()); - myPlaneSource->SetPoint2(theOrientedPlane->myPlaneSource->GetPoint2()); - } + myPlaneSource->SetNormal(theOrientedPlane->myPlaneSource->GetNormal()); + myPlaneSource->SetOrigin(theOrientedPlane->myPlaneSource->GetOrigin()); + myPlaneSource->SetPoint1(theOrientedPlane->myPlaneSource->GetPoint1()); + myPlaneSource->SetPoint2(theOrientedPlane->myPlaneSource->GetPoint2()); +} -protected: - OrientedPlane(SVTK_ViewWindow* theViewWindow): - myViewWindow(theViewWindow), - myOrientation(SMESH::XY), - myDistance(0.5) - { - Init(); - myViewWindow->AddActor(myActor); - } +SMESH::OrientedPlane::OrientedPlane(SVTK_ViewWindow* theViewWindow): + myViewWindow(theViewWindow), + myOrientation(SMESH::XY), + myDistance(0.5) +{ + Init(); + myViewWindow->AddActor(myActor, false, false); // don't adjust actors +} - OrientedPlane(): - myOrientation(SMESH::XY), - myViewWindow(NULL), - myDistance(0.5) - { - Init(); - } +SMESH::OrientedPlane::OrientedPlane(): + myOrientation(SMESH::XY), + myViewWindow(NULL), + myDistance(0.5) +{ + Init(); +} - void Init() - { - myPlaneSource = vtkPlaneSource::New(); - - myAngle[0] = myAngle[1] = 0.0; - - // Create and display actor - myMapper = vtkDataSetMapper::New(); - myMapper->SetInput(myPlaneSource->GetOutput()); - - myActor = SALOME_Actor::New(); - myActor->VisibilityOff(); - myActor->PickableOff(); - myActor->SetInfinitive(true); - myActor->SetMapper(myMapper); - - vtkFloatingPointType anRGB[3]; - vtkProperty* aProp = vtkProperty::New(); - SMESH::GetColor( "SMESH", "fill_color", anRGB[0], anRGB[1], anRGB[2], QColor( 0, 170, 255 ) ); - aProp->SetColor(anRGB[0],anRGB[1],anRGB[2]); - aProp->SetOpacity(0.75); - myActor->SetProperty(aProp); - aProp->Delete(); - - vtkProperty* aBackProp = vtkProperty::New(); - SMESH::GetColor( "SMESH", "backface_color", anRGB[0], anRGB[1], anRGB[2], QColor( 0, 0, 255 ) ); - aBackProp->SetColor(anRGB[0],anRGB[1],anRGB[2]); - aBackProp->SetOpacity(0.75); - myActor->SetBackfaceProperty(aBackProp); - aBackProp->Delete(); - } +void SMESH::OrientedPlane::Init() +{ + myPlaneSource = vtkPlaneSource::New(); + + myAngle[0] = myAngle[1] = 0.0; + + // Create and display actor + myMapper = vtkDataSetMapper::New(); + myMapper->SetInput(myPlaneSource->GetOutput()); + + myActor = SALOME_Actor::New(); + myActor->VisibilityOff(); + myActor->PickableOff(); + myActor->SetInfinitive(true); + myActor->SetMapper(myMapper); + + vtkFloatingPointType anRGB[3]; + vtkProperty* aProp = vtkProperty::New(); + SMESH::GetColor( "SMESH", "fill_color", anRGB[0], anRGB[1], anRGB[2], QColor( 0, 170, 255 ) ); + aProp->SetColor(anRGB[0],anRGB[1],anRGB[2]); + aProp->SetOpacity(0.75); + myActor->SetProperty(aProp); + aProp->Delete(); + + vtkProperty* aBackProp = vtkProperty::New(); + SMESH::GetColor( "SMESH", "backface_color", anRGB[0], anRGB[1], anRGB[2], QColor( 0, 0, 255 ) ); + aBackProp->SetColor(anRGB[0],anRGB[1],anRGB[2]); + aBackProp->SetOpacity(0.75); + myActor->SetBackfaceProperty(aBackProp); + aBackProp->Delete(); +} - ~OrientedPlane(){ +SMESH::OrientedPlane::~OrientedPlane() +{ + if (myViewWindow) myViewWindow->RemoveActor(myActor); - myActor->Delete(); + myActor->Delete(); - myMapper->RemoveAllInputs(); - myMapper->Delete(); + myMapper->RemoveAllInputs(); + myMapper->Delete(); - // commented: porting to vtk 5.0 - // myPlaneSource->UnRegisterAllOutputs(); - myPlaneSource->Delete(); - }; + // commented: porting to vtk 5.0 + // myPlaneSource->UnRegisterAllOutputs(); + myPlaneSource->Delete(); +} -private: - // Not implemented. - OrientedPlane (const OrientedPlane&); - void operator= (const OrientedPlane&); +//================================================================================= +// class : ActorItem +// purpose : +//================================================================================= +class ActorItem : public QListWidgetItem +{ +public: + ActorItem( SMESH_Actor* theActor, const QString& theName, QListWidget* theListWidget ) : + QListWidgetItem( theName, theListWidget ), + myActor( theActor ) {} + + SMESH_Actor* getActor() const { return myActor; } +private: + SMESH_Actor* myActor; }; -struct TSetVisiblity { - TSetVisiblity(int theIsVisible): myIsVisible(theIsVisible){} - void operator()(SMESH::TVTKPlane& theOrientedPlane){ - theOrientedPlane->myActor->SetVisibility(myIsVisible); +//================================================================================= +// class : TSetVisibility +// purpose : +//================================================================================= +struct TSetVisibility { + TSetVisibility(int theIsVisible): myIsVisible(theIsVisible){} + void operator()(SMESH::TPlaneData& thePlaneData){ + bool anIsEmpty = thePlaneData.ActorList.empty(); + thePlaneData.Plane.GetPointer()->myActor->SetVisibility(myIsVisible && !anIsEmpty); } int myIsVisible; }; @@ -198,13 +204,13 @@ struct TSetVisiblity { // used in SMESHGUI::restoreVisualParameters() to avoid // declaration of OrientedPlane outside of SMESHGUI_ClippingDlg.cxx //================================================================================= -void SMESHGUI_ClippingDlg::AddPlane (SMESH_Actor* theActor, - SVTK_ViewWindow* theViewWindow, - SMESH::Orientation theOrientation, - double theDistance, - vtkFloatingPointType theAngle[2]) +SMESH::OrientedPlane* SMESHGUI_ClippingDlg::AddPlane (SMESH::TActorList theActorList, + SVTK_ViewWindow* theViewWindow, + SMESH::Orientation theOrientation, + double theDistance, + const vtkFloatingPointType theAngle[2]) { - OrientedPlane* aPlane = OrientedPlane::New(theViewWindow); + SMESH::OrientedPlane* aPlane = SMESH::OrientedPlane::New(theViewWindow); aPlane->myAngle[0] = theAngle[0]; aPlane->myAngle[1] = theAngle[1]; @@ -254,41 +260,64 @@ void SMESHGUI_ClippingDlg::AddPlane (SMESH_Actor* theActor, vtkMath::Cross(aNormal,aDir[1],aDir[0]); } - // ??? - theActor->SetPlaneParam(aNormal, theDistance, aPlane); - - vtkDataSet* aDataSet = theActor->GetInput(); - vtkFloatingPointType *aPnt = aDataSet->GetCenter(); - - vtkFloatingPointType* anOrigin = aPlane->GetOrigin(); - vtkFloatingPointType aDel = aDataSet->GetLength()/2.0; + vtkFloatingPointType aBounds[6]; + vtkFloatingPointType anOrigin[3]; + + bool anIsOk = false; + if( theActorList.empty() ) { + // to support planes with empty actor list we should create + // a nullified plane that will be initialized later + anOrigin[0] = anOrigin[1] = anOrigin[2] = 0; + aBounds[0] = aBounds[2] = aBounds[4] = 0; + aBounds[1] = aBounds[3] = aBounds[5] = 0; + anIsOk = true; + } + else + anIsOk = SMESH::ComputeClippingPlaneParameters( theActorList, + aNormal, + theDistance, + aBounds, + anOrigin ); + if( !anIsOk ) + return NULL; + + aPlane->SetNormal( aNormal ); + aPlane->SetOrigin( anOrigin ); + + vtkFloatingPointType aPnt[3] = { ( aBounds[0] + aBounds[1] ) / 2., + ( aBounds[2] + aBounds[3] ) / 2., + ( aBounds[4] + aBounds[5] ) / 2. }; + + vtkFloatingPointType aDel = pow( pow( aBounds[1] - aBounds[0], 2 ) + + pow( aBounds[3] - aBounds[2], 2 ) + + pow( aBounds[5] - aBounds[4], 2 ), 0.5 ); vtkFloatingPointType aDelta[2][3] = {{aDir[0][0]*aDel, aDir[0][1]*aDel, aDir[0][2]*aDel}, - {aDir[1][0]*aDel, aDir[1][1]*aDel, aDir[1][2]*aDel}}; + {aDir[1][0]*aDel, aDir[1][1]*aDel, aDir[1][2]*aDel}}; vtkFloatingPointType aParam, aPnt0[3], aPnt1[3], aPnt2[3]; vtkFloatingPointType aPnt01[3] = {aPnt[0] - aDelta[0][0] - aDelta[1][0], - aPnt[1] - aDelta[0][1] - aDelta[1][1], - aPnt[2] - aDelta[0][2] - aDelta[1][2]}; + aPnt[1] - aDelta[0][1] - aDelta[1][1], + aPnt[2] - aDelta[0][2] - aDelta[1][2]}; vtkFloatingPointType aPnt02[3] = {aPnt01[0] + aNormal[0], - aPnt01[1] + aNormal[1], - aPnt01[2] + aNormal[2]}; + aPnt01[1] + aNormal[1], + aPnt01[2] + aNormal[2]}; vtkPlane::IntersectWithLine(aPnt01,aPnt02,aNormal,anOrigin,aParam,aPnt0); vtkFloatingPointType aPnt11[3] = {aPnt[0] - aDelta[0][0] + aDelta[1][0], - aPnt[1] - aDelta[0][1] + aDelta[1][1], - aPnt[2] - aDelta[0][2] + aDelta[1][2]}; + aPnt[1] - aDelta[0][1] + aDelta[1][1], + aPnt[2] - aDelta[0][2] + aDelta[1][2]}; vtkFloatingPointType aPnt12[3] = {aPnt11[0] + aNormal[0], - aPnt11[1] + aNormal[1], - aPnt11[2] + aNormal[2]}; + aPnt11[1] + aNormal[1], + aPnt11[2] + aNormal[2]}; vtkPlane::IntersectWithLine(aPnt11,aPnt12,aNormal,anOrigin,aParam,aPnt1); vtkFloatingPointType aPnt21[3] = {aPnt[0] + aDelta[0][0] - aDelta[1][0], - aPnt[1] + aDelta[0][1] - aDelta[1][1], - aPnt[2] + aDelta[0][2] - aDelta[1][2]}; + aPnt[1] + aDelta[0][1] - aDelta[1][1], + aPnt[2] + aDelta[0][2] - aDelta[1][2]}; vtkFloatingPointType aPnt22[3] = {aPnt21[0] + aNormal[0], - aPnt21[1] + aNormal[1], - aPnt21[2] + aNormal[2]}; + aPnt21[1] + aNormal[1], + aPnt21[2] + aNormal[2]}; vtkPlane::IntersectWithLine(aPnt21,aPnt22,aNormal,anOrigin,aParam,aPnt2); vtkPlaneSource* aPlaneSource = aPlane->myPlaneSource; @@ -297,28 +326,13 @@ void SMESHGUI_ClippingDlg::AddPlane (SMESH_Actor* theActor, aPlaneSource->SetPoint1(aPnt1[0],aPnt1[1],aPnt1[2]); aPlaneSource->SetPoint2(aPnt2[0],aPnt2[1],aPnt2[2]); - theActor->AddClippingPlane(aPlane); - aPlane->Delete(); -} + SMESH::TActorList::iterator anIter = theActorList.begin(); + for ( ; anIter != theActorList.end(); anIter++ ) + if( vtkActor* aVTKActor = *anIter ) + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) + anActor->AddClippingPlane( aPlane ); -//================================================================================= -// used in SMESHGUI::restoreVisualParameters() to avoid -// declaration of OrientedPlane outside of SMESHGUI_ClippingDlg.cxx -//================================================================================= -void SMESHGUI_ClippingDlg::GetPlaneParam (SMESH_Actor* theActor, - int thePlaneIndex, - SMESH::Orientation& theOrientation, - double& theDistance, - vtkFloatingPointType* theAngle) -{ - if (vtkPlane* aPln = theActor->GetClippingPlane(thePlaneIndex)) { - if (OrientedPlane* aPlane = OrientedPlane::SafeDownCast(aPln)) { - theOrientation = aPlane->GetOrientation(); - theDistance = aPlane->GetDistance(); - theAngle[0] = aPlane->myAngle[0]; - theAngle[1] = aPlane->myAngle[1]; - } - } + return aPlane; } //================================================================================= @@ -326,11 +340,10 @@ void SMESHGUI_ClippingDlg::GetPlaneParam (SMESH_Actor* theActor, // purpose : // //================================================================================= -SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): +SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule, SVTK_ViewWindow* theViewWindow ): QDialog( SMESH::GetDesktop(theModule) ), - mySelector(SMESH::GetViewWindow(theModule)->GetSelector()), - mySelectionMgr(SMESH::GetSelectionMgr(theModule)), - mySMESHGUI(theModule) + mySMESHGUI(theModule), + myViewWindow(theViewWindow) { setModal( false ); setAttribute( Qt::WA_DeleteOnClose, true ); @@ -342,8 +355,8 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): SMESHGUI_ClippingDlgLayout->setMargin(MARGIN); // Controls for selecting, creating, deleting planes - QGroupBox* GroupPlanes = new QGroupBox(tr("Clipping planes"), this); - QHBoxLayout* GroupPlanesLayout = new QHBoxLayout(GroupPlanes); + QGroupBox* GroupPlanes = new QGroupBox(tr("CLIP_PLANES"), this); + QGridLayout* GroupPlanesLayout = new QGridLayout(GroupPlanes); GroupPlanesLayout->setSpacing(SPACING); GroupPlanesLayout->setMargin(MARGIN); @@ -353,10 +366,21 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): buttonDelete = new QPushButton(tr("SMESH_BUT_DELETE"), GroupPlanes); - GroupPlanesLayout->addWidget(ComboBoxPlanes); - GroupPlanesLayout->addStretch(); - GroupPlanesLayout->addWidget(buttonNew); - GroupPlanesLayout->addWidget(buttonDelete); + QLabel* aLabel = new QLabel(tr("MESHES_SUBMESHES_GROUPS"), GroupPlanes); + + ActorList = new QListWidget(GroupPlanes); + ActorList->setSelectionMode(QAbstractItemView::SingleSelection); + + SelectAllCheckBox = new QCheckBox(tr("SELECT_ALL"), GroupPlanes); + + GroupPlanesLayout->addWidget(ComboBoxPlanes, 0, 0); + GroupPlanesLayout->addWidget(new QWidget(), 0, 1); + GroupPlanesLayout->addWidget(buttonNew, 0, 2); + GroupPlanesLayout->addWidget(buttonDelete, 0, 3); + GroupPlanesLayout->addWidget(aLabel, 1, 0, 1, 4); + GroupPlanesLayout->addWidget(ActorList, 2, 0, 1, 4); + GroupPlanesLayout->addWidget(SelectAllCheckBox, 3, 0, 1, 4); + GroupPlanesLayout->setColumnStretch( 1, 1 ); // Controls for defining plane parameters QGroupBox* GroupParameters = new QGroupBox(tr("SMESH_PARAMETERS"), this); @@ -372,18 +396,18 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): SpinBoxDistance = new SMESHGUI_SpinBox(GroupParameters); - TextLabelRot1 = new QLabel(tr("Rotation around X (Y to Z):"), GroupParameters); + TextLabelRot1 = new QLabel(tr("ROTATION_AROUND_X_Y2Z"), GroupParameters); SpinBoxRot1 = new SMESHGUI_SpinBox(GroupParameters); - TextLabelRot2 = new QLabel(tr("Rotation around Y (X to Z):"), GroupParameters); + TextLabelRot2 = new QLabel(tr("ROTATION_AROUND_Y_X2Z"), GroupParameters); SpinBoxRot2 = new SMESHGUI_SpinBox(GroupParameters); - PreviewCheckBox = new QCheckBox(tr("Show preview"), GroupParameters); + PreviewCheckBox = new QCheckBox(tr("SHOW_PREVIEW"), GroupParameters); PreviewCheckBox->setChecked(true); - AutoApplyCheckBox = new QCheckBox(tr("Auto Apply"), GroupParameters); + AutoApplyCheckBox = new QCheckBox(tr("AUTO_APPLY"), GroupParameters); AutoApplyCheckBox->setChecked(false); GroupParametersLayout->addWidget(TextLabelOrientation, 0, 0); @@ -425,19 +449,20 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): SMESHGUI_ClippingDlgLayout->addWidget(GroupButtons); // Initial state - SpinBoxDistance->RangeStepAndValidator(0.0, 1.0, 0.01, 3); - SpinBoxRot1->RangeStepAndValidator(-180.0, 180.0, 1, 3); - SpinBoxRot2->RangeStepAndValidator(-180.0, 180.0, 1, 3); + SpinBoxDistance->RangeStepAndValidator(0.0, 1.0, 0.01, "length_precision" ); + SpinBoxRot1->RangeStepAndValidator(-180.0, 180.0, 1, "angle_precision" ); + SpinBoxRot2->RangeStepAndValidator(-180.0, 180.0, 1, "angle_precision" ); - ComboBoxOrientation->addItem(tr("|| X-Y")); - ComboBoxOrientation->addItem(tr("|| Y-Z")); - ComboBoxOrientation->addItem(tr("|| Z-X")); + ComboBoxOrientation->addItem(tr("ALONG_XY")); + ComboBoxOrientation->addItem(tr("ALONG_YZ")); + ComboBoxOrientation->addItem(tr("ALONG_ZX")); SpinBoxDistance->SetValue(0.5); - myActor = 0; myIsSelectPlane = false; - onSelectionChanged(); + + initializePlaneData(); + synchronize(); myHelpFileName = "clipping_page.html"; @@ -445,6 +470,8 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): connect(ComboBoxPlanes, SIGNAL(activated(int)), this, SLOT(onSelectPlane(int))); connect(buttonNew, SIGNAL(clicked()), this, SLOT(ClickOnNew())); connect(buttonDelete, SIGNAL(clicked()), this, SLOT(ClickOnDelete())); + connect(ActorList, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(onActorItemChanged(QListWidgetItem*))); + connect(SelectAllCheckBox, SIGNAL(stateChanged(int)), this, SLOT(onSelectAll(int))); connect(ComboBoxOrientation, SIGNAL(activated(int)), this, SLOT(onSelectOrientation(int))); connect(SpinBoxDistance, SIGNAL(valueChanged(double)), this, SLOT(SetCurrentPlaneParam())); connect(SpinBoxRot1, SIGNAL(valueChanged(double)), this, SLOT(SetCurrentPlaneParam())); @@ -456,7 +483,6 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): connect(buttonApply, SIGNAL(clicked()), this, SLOT(ClickOnApply())); connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onSelectionChanged())); /* to close dialog if study frame change */ connect(mySMESHGUI, SIGNAL (SignalStudyFrameChanged()), this, SLOT(ClickOnCancel())); @@ -470,10 +496,9 @@ SMESHGUI_ClippingDlg::SMESHGUI_ClippingDlg( SMESHGUI* theModule ): SMESHGUI_ClippingDlg::~SMESHGUI_ClippingDlg() { // no need to delete child widgets, Qt does it all for us - std::for_each(myPlanes.begin(),myPlanes.end(),TSetVisiblity(false)); - if (mySMESHGUI) - if (SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(mySMESHGUI)) - SMESH::RenderViewWindow(aViewWindow); + std::for_each(myPlanes.begin(),myPlanes.end(),TSetVisibility(false)); + if (myViewWindow) + SMESH::RenderViewWindow(myViewWindow); } double SMESHGUI_ClippingDlg::getDistance() const @@ -502,27 +527,58 @@ double SMESHGUI_ClippingDlg::getRotation2() const //======================================================================= void SMESHGUI_ClippingDlg::ClickOnApply() { - if (!myActor) - return; - - if (SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(mySMESHGUI)) { + if (myViewWindow) { SUIT_OverrideCursor wc; QWidget *aCurrWid = this->focusWidget(); aCurrWid->clearFocus(); aCurrWid->setFocus(); - myActor->RemoveAllClippingPlanes(); + SMESHGUI_ClippingPlaneInfoMap& aClippingPlaneInfoMap = mySMESHGUI->getClippingPlaneInfoMap(); + SMESHGUI_ClippingPlaneInfoList& aClippingPlaneInfoList = aClippingPlaneInfoMap[ myViewWindow->getViewManager() ]; + + // clean memory allocated for planes + SMESHGUI_ClippingPlaneInfoList::iterator anIter1 = aClippingPlaneInfoList.begin(); + for( ; anIter1 != aClippingPlaneInfoList.end(); anIter1++ ) + if( SMESH::OrientedPlane* aPlane = (*anIter1).Plane ) + aPlane->Delete(); + + aClippingPlaneInfoList.clear(); + + VTK::ActorCollectionCopy aCopy( myViewWindow->getRenderer()->GetActors() ); + vtkActorCollection* anAllActors = aCopy.GetActors(); + anAllActors->InitTraversal(); + while( vtkActor* aVTKActor = anAllActors->GetNextActor() ) + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) + anActor->RemoveAllClippingPlanes(); + + SMESH::TPlaneDataVector::iterator anIter2 = myPlanes.begin(); + for( ; anIter2 != myPlanes.end(); anIter2++ ) { + SMESH::TPlaneData aPlaneData = *anIter2; + SMESH::TPlane aPlane = aPlaneData.Plane; + SMESH::TActorList anActorList = aPlaneData.ActorList; + + // the check is disabled to support planes with empty actor list + //if( anActorList.empty() ) + // continue; - SMESH::TPlanes::iterator anIter = myPlanes.begin(); - for ( ; anIter != myPlanes.end(); anIter++) { - OrientedPlane* anOrientedPlane = OrientedPlane::New(aViewWindow); - anOrientedPlane->ShallowCopy(anIter->GetPointer()); - myActor->AddClippingPlane(anOrientedPlane); - anOrientedPlane->Delete(); + SMESH::OrientedPlane* anOrientedPlane = SMESH::OrientedPlane::New(myViewWindow); + anOrientedPlane->ShallowCopy(aPlane.GetPointer()); + + SMESH::TActorList::iterator anIter3 = anActorList.begin(); + for( ; anIter3 != anActorList.end(); anIter3++ ) + if( vtkActor* aVTKActor = *anIter3 ) + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) + anActor->AddClippingPlane(anOrientedPlane); + + SMESH::ClippingPlaneInfo aClippingPlaneInfo; + aClippingPlaneInfo.Plane = anOrientedPlane; + aClippingPlaneInfo.ActorList = anActorList; + + aClippingPlaneInfoList.push_back( aClippingPlaneInfo ); } - SMESH::RenderViewWindow(aViewWindow); + SMESH::RenderViewWindow( myViewWindow ); } } @@ -555,67 +611,31 @@ void SMESHGUI_ClippingDlg::ClickOnHelp() if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { - QString platform; + QString platform; #ifdef WIN32 - platform = "winapplication"; + platform = "winapplication"; #else - platform = "application"; + platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } -//================================================================================= -// function : onSelectionChanged() -// purpose : Called when selection is changed -//================================================================================= -void SMESHGUI_ClippingDlg::onSelectionChanged() -{ - if (SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(mySMESHGUI)) { - const SALOME_ListIO& aList = mySelector->StoredIObjects(); - if (aList.Extent() > 0) { - Handle(SALOME_InteractiveObject) IOS = aList.First(); - myActor = SMESH::FindActorByEntry(IOS->getEntry()); - if (myActor) { - std::for_each(myPlanes.begin(),myPlanes.end(),TSetVisiblity(false)); - myPlanes.clear(); - - vtkIdType anId = 0, anEnd = myActor->GetNumberOfClippingPlanes(); - for ( ; anId < anEnd; anId++) { - if (vtkImplicitFunction* aFunction = myActor->GetClippingPlane(anId)) { - if(OrientedPlane* aPlane = OrientedPlane::SafeDownCast(aFunction)){ - OrientedPlane* anOrientedPlane = OrientedPlane::New(aViewWindow); - SMESH::TVTKPlane aTVTKPlane(anOrientedPlane); - anOrientedPlane->Delete(); - aTVTKPlane->ShallowCopy(aPlane); - myPlanes.push_back(aTVTKPlane); - } - } - } - - std::for_each(myPlanes.begin(),myPlanes.end(), - TSetVisiblity(PreviewCheckBox->isChecked())); - } - } - SMESH::RenderViewWindow(aViewWindow); - } - Sinchronize(); -} - //======================================================================= // function : onSelectPlane() // purpose : //======================================================================= void SMESHGUI_ClippingDlg::onSelectPlane (int theIndex) { - if (!myActor || myPlanes.empty()) + if (myPlanes.empty()) return; - OrientedPlane* aPlane = myPlanes[theIndex].GetPointer(); + SMESH::TPlaneData aPlaneData = myPlanes[theIndex]; + SMESH::OrientedPlane* aPlane = aPlaneData.Plane.GetPointer(); // Orientation SMESH::Orientation anOrientation = aPlane->GetOrientation(); @@ -642,6 +662,11 @@ void SMESHGUI_ClippingDlg::onSelectPlane (int theIndex) break; } myIsSelectPlane = false; + + // Actors + bool anIsBlocked = ActorList->blockSignals( true ); + updateActorList(); + ActorList->blockSignals( anIsBlocked ); } //======================================================================= @@ -650,19 +675,31 @@ void SMESHGUI_ClippingDlg::onSelectPlane (int theIndex) //======================================================================= void SMESHGUI_ClippingDlg::ClickOnNew() { - if (!myActor) - return; + if(myViewWindow){ + SMESH::OrientedPlane* aPlane = SMESH::OrientedPlane::New(myViewWindow); + SMESH::TPlane aTPlane(aPlane); + + SMESH::TActorList anActorList; + VTK::ActorCollectionCopy aCopy( myViewWindow->getRenderer()->GetActors() ); + vtkActorCollection* anAllActors = aCopy.GetActors(); + anAllActors->InitTraversal(); + while( vtkActor* aVTKActor = anAllActors->GetNextActor() ) + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) + anActorList.push_back( anActor ); - if(SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(mySMESHGUI)){ - OrientedPlane* aPlane = OrientedPlane::New(aViewWindow); - SMESH::TVTKPlane aTVTKPlane(aPlane); - myPlanes.push_back(aTVTKPlane); + SMESH::TPlaneData aPlaneData(aTPlane, anActorList); + + myPlanes.push_back(aPlaneData); if (PreviewCheckBox->isChecked()) - aTVTKPlane->myActor->VisibilityOn(); - - Sinchronize(); + aTPlane->myActor->VisibilityOn(); + + bool anIsBlocked = ActorList->blockSignals( true ); + + synchronize(); SetCurrentPlaneParam(); + + ActorList->blockSignals( anIsBlocked ); } } @@ -672,20 +709,105 @@ void SMESHGUI_ClippingDlg::ClickOnNew() //======================================================================= void SMESHGUI_ClippingDlg::ClickOnDelete() { - if (!myActor || myPlanes.empty()) + if (myPlanes.empty()) return; int aPlaneIndex = ComboBoxPlanes->currentIndex(); - SMESH::TPlanes::iterator anIter = myPlanes.begin() + aPlaneIndex; - anIter->GetPointer()->myActor->SetVisibility(false); + SMESH::TPlaneDataVector::iterator anIter = myPlanes.begin() + aPlaneIndex; + SMESH::TPlaneData aPlaneData = *anIter; + aPlaneData.Plane.GetPointer()->myActor->SetVisibility(false); myPlanes.erase(anIter); if(AutoApplyCheckBox->isChecked()) ClickOnApply(); - Sinchronize(); - SMESH::RenderViewWindow(SMESH::GetCurrentVtkView()); + synchronize(); + SMESH::RenderViewWindow( myViewWindow ); +} + +//======================================================================= +// function : updateActorItem() +// purpose : +//======================================================================= +void SMESHGUI_ClippingDlg::updateActorItem( QListWidgetItem* theItem, + bool theUpdateSelectAll, + bool theUpdateClippingPlaneMap ) +{ + // update Select All check box + if( theUpdateSelectAll ) { + int aNbItems = ActorList->count(), aNbChecked = 0; + for( int i = 0; i < aNbItems; i++ ) + if( QListWidgetItem* anItem = ActorList->item( i ) ) + if( anItem->checkState() == Qt::Checked ) + aNbChecked++; + + Qt::CheckState aCheckState = Qt::Unchecked; + if( aNbChecked == aNbItems ) + aCheckState = Qt::Checked; + else if( aNbChecked > 0 ) + aCheckState = Qt::PartiallyChecked; + + bool anIsBlocked = SelectAllCheckBox->blockSignals( true ); + SelectAllCheckBox->setCheckState( aCheckState ); + SelectAllCheckBox->blockSignals( anIsBlocked ); + } + + // update clipping plane map + if( theUpdateClippingPlaneMap ) { + int aCurPlaneIndex = ComboBoxPlanes->currentIndex(); + if( ActorItem* anItem = dynamic_cast( theItem ) ) { + if( SMESH_Actor* anActor = anItem->getActor() ) { + SMESH::TPlaneData& aPlaneData = myPlanes[ aCurPlaneIndex ]; + SMESH::TActorList& anActorList = aPlaneData.ActorList; + bool anIsPushed = false; + SMESH::TActorList::iterator anIter = anActorList.begin(); + for ( ; anIter != anActorList.end(); anIter++ ) { + if( anActor == *anIter ) { + anIsPushed = true; + break; + } + } + if( theItem->checkState() == Qt::Checked && !anIsPushed ) + anActorList.push_back( anActor ); + else if( theItem->checkState() == Qt::Unchecked && anIsPushed ) + anActorList.remove( anActor ); + } + } + } +} + +//======================================================================= +// function : onActorItemChanged() +// purpose : +//======================================================================= +void SMESHGUI_ClippingDlg::onActorItemChanged( QListWidgetItem* theItem ) +{ + updateActorItem( theItem, true, true ); + SetCurrentPlaneParam(); +} + +//======================================================================= +// function : onSelectAll() +// purpose : +//======================================================================= +void SMESHGUI_ClippingDlg::onSelectAll( int theState ) +{ + if( theState == Qt::PartiallyChecked ) { + SelectAllCheckBox->setCheckState( Qt::Checked ); + return; + } + + bool anIsBlocked = ActorList->blockSignals( true ); + for( int i = 0, n = ActorList->count(); i < n; i++ ) { + if( QListWidgetItem* anItem = ActorList->item( i ) ) { + anItem->setCheckState( theState == Qt::Checked ? Qt::Checked : Qt::Unchecked ); + updateActorItem( anItem, false, true ); + } + } + SelectAllCheckBox->setTristate( false ); + ActorList->blockSignals( anIsBlocked ); + SetCurrentPlaneParam(); } //======================================================================= @@ -698,16 +820,16 @@ void SMESHGUI_ClippingDlg::onSelectOrientation (int theItem) return; if (theItem == 0) { - TextLabelRot1->setText(tr("Rotation around X (Y to Z):")); - TextLabelRot2->setText(tr("Rotation around Y (X to Z):")); + TextLabelRot1->setText(tr("ROTATION_AROUND_X_Y2Z")); + TextLabelRot2->setText(tr("ROTATION_AROUND_Y_X2Z")); } else if (theItem == 1) { - TextLabelRot1->setText(tr("Rotation around Y (Z to X):")); - TextLabelRot2->setText(tr("Rotation around Z (Y to X):")); + TextLabelRot1->setText(tr("ROTATION_AROUND_Y_Z2X")); + TextLabelRot2->setText(tr("ROTATION_AROUND_Z_Y2X")); } else if (theItem == 2) { - TextLabelRot1->setText(tr("Rotation around Z (X to Y):")); - TextLabelRot2->setText(tr("Rotation around X (Z to Y):")); + TextLabelRot1->setText(tr("ROTATION_AROUND_Z_X2Y")); + TextLabelRot2->setText(tr("ROTATION_AROUND_X_Z2Y")); } if((QComboBox*)sender() == ComboBoxOrientation) @@ -715,17 +837,17 @@ void SMESHGUI_ClippingDlg::onSelectOrientation (int theItem) } //======================================================================= -// function : Sinchronize() +// function : synchronize() // purpose : //======================================================================= -void SMESHGUI_ClippingDlg::Sinchronize() +void SMESHGUI_ClippingDlg::synchronize() { int aNbPlanes = myPlanes.size(); ComboBoxPlanes->clear(); QString aName; for(int i = 1; i<=aNbPlanes; i++) { - aName = QString(tr("Plane# %1")).arg(i); + aName = QString(tr("PLANE_NUM")).arg(i); ComboBoxPlanes->addItem(aName); } @@ -735,17 +857,22 @@ void SMESHGUI_ClippingDlg::Sinchronize() bool anIsControlsEnable = (aPos >= 0); if (anIsControlsEnable) { onSelectPlane(aPos); + updateActorList(); } else { - ComboBoxPlanes->addItem(tr("No planes")); + ComboBoxPlanes->addItem(tr("NO_PLANES")); + ActorList->clear(); SpinBoxRot1->SetValue(0.0); SpinBoxRot2->SetValue(0.0); SpinBoxDistance->SetValue(0.5); } + ActorList->setEnabled(anIsControlsEnable); + SelectAllCheckBox->setEnabled(anIsControlsEnable); buttonDelete->setEnabled(anIsControlsEnable); - buttonApply->setEnabled(anIsControlsEnable); - PreviewCheckBox->setEnabled(anIsControlsEnable); - AutoApplyCheckBox->setEnabled(anIsControlsEnable); + // the following 3 controls should be enabled + //buttonApply->setEnabled(anIsControlsEnable); + //PreviewCheckBox->setEnabled(anIsControlsEnable); + //AutoApplyCheckBox->setEnabled(anIsControlsEnable); ComboBoxOrientation->setEnabled(anIsControlsEnable); SpinBoxDistance->setEnabled(anIsControlsEnable); SpinBoxRot1->setEnabled(anIsControlsEnable); @@ -773,7 +900,8 @@ void SMESHGUI_ClippingDlg::SetCurrentPlaneParam() int aCurPlaneIndex = ComboBoxPlanes->currentIndex(); - OrientedPlane* aPlane = myPlanes[aCurPlaneIndex].GetPointer(); + SMESH::TPlaneData aPlaneData = myPlanes[aCurPlaneIndex]; + SMESH::OrientedPlane* aPlane = aPlaneData.Plane.GetPointer(); vtkFloatingPointType aNormal[3]; SMESH::Orientation anOrientation; @@ -831,52 +959,69 @@ void SMESHGUI_ClippingDlg::SetCurrentPlaneParam() aPlane->SetOrientation(anOrientation); aPlane->SetDistance(getDistance()); - myActor->SetPlaneParam(aNormal, getDistance(), aPlane); - - vtkDataSet* aDataSet = myActor->GetInput(); - vtkFloatingPointType *aPnt = aDataSet->GetCenter(); - - vtkFloatingPointType* anOrigin = aPlane->GetOrigin(); - vtkFloatingPointType aDel = aDataSet->GetLength()/2.0; - - vtkFloatingPointType aDelta[2][3] = {{aDir[0][0]*aDel, aDir[0][1]*aDel, aDir[0][2]*aDel}, - {aDir[1][0]*aDel, aDir[1][1]*aDel, aDir[1][2]*aDel}}; - vtkFloatingPointType aParam, aPnt0[3], aPnt1[3], aPnt2[3]; - - vtkFloatingPointType aPnt01[3] = {aPnt[0] - aDelta[0][0] - aDelta[1][0], - aPnt[1] - aDelta[0][1] - aDelta[1][1], - aPnt[2] - aDelta[0][2] - aDelta[1][2]}; - vtkFloatingPointType aPnt02[3] = {aPnt01[0] + aNormal[0], - aPnt01[1] + aNormal[1], - aPnt01[2] + aNormal[2]}; - vtkPlane::IntersectWithLine(aPnt01,aPnt02,aNormal,anOrigin,aParam,aPnt0); - - vtkFloatingPointType aPnt11[3] = {aPnt[0] - aDelta[0][0] + aDelta[1][0], - aPnt[1] - aDelta[0][1] + aDelta[1][1], - aPnt[2] - aDelta[0][2] + aDelta[1][2]}; - vtkFloatingPointType aPnt12[3] = {aPnt11[0] + aNormal[0], - aPnt11[1] + aNormal[1], - aPnt11[2] + aNormal[2]}; - vtkPlane::IntersectWithLine(aPnt11,aPnt12,aNormal,anOrigin,aParam,aPnt1); - - vtkFloatingPointType aPnt21[3] = {aPnt[0] + aDelta[0][0] - aDelta[1][0], - aPnt[1] + aDelta[0][1] - aDelta[1][1], - aPnt[2] + aDelta[0][2] - aDelta[1][2]}; - vtkFloatingPointType aPnt22[3] = {aPnt21[0] + aNormal[0], - aPnt21[1] + aNormal[1], - aPnt21[2] + aNormal[2]}; - vtkPlane::IntersectWithLine(aPnt21,aPnt22,aNormal,anOrigin,aParam,aPnt2); - - vtkPlaneSource* aPlaneSource = aPlane->myPlaneSource; - aPlaneSource->SetNormal(aNormal[0],aNormal[1],aNormal[2]); - aPlaneSource->SetOrigin(aPnt0[0],aPnt0[1],aPnt0[2]); - aPlaneSource->SetPoint1(aPnt1[0],aPnt1[1],aPnt1[2]); - aPlaneSource->SetPoint2(aPnt2[0],aPnt2[1],aPnt2[2]); + SMESH::TActorList anActorList = aPlaneData.ActorList; + + vtkFloatingPointType aBounds[6]; + vtkFloatingPointType anOrigin[3]; + bool anIsOk = SMESH::ComputeClippingPlaneParameters( anActorList, + aNormal, + getDistance(), + aBounds, + anOrigin ); + + aPlane->myActor->SetVisibility( anIsOk && PreviewCheckBox->isChecked() ); + + if( anIsOk ) { + aPlane->SetNormal( aNormal ); + aPlane->SetOrigin( anOrigin ); + + vtkFloatingPointType aPnt[3] = { ( aBounds[0] + aBounds[1] ) / 2., + ( aBounds[2] + aBounds[3] ) / 2., + ( aBounds[4] + aBounds[5] ) / 2. }; + + vtkFloatingPointType aDel = pow( pow( aBounds[1] - aBounds[0], 2 ) + + pow( aBounds[3] - aBounds[2], 2 ) + + pow( aBounds[5] - aBounds[4], 2 ), 0.5 ); + + vtkFloatingPointType aDelta[2][3] = {{aDir[0][0]*aDel, aDir[0][1]*aDel, aDir[0][2]*aDel}, + {aDir[1][0]*aDel, aDir[1][1]*aDel, aDir[1][2]*aDel}}; + vtkFloatingPointType aParam, aPnt0[3], aPnt1[3], aPnt2[3]; + + vtkFloatingPointType aPnt01[3] = {aPnt[0] - aDelta[0][0] - aDelta[1][0], + aPnt[1] - aDelta[0][1] - aDelta[1][1], + aPnt[2] - aDelta[0][2] - aDelta[1][2]}; + vtkFloatingPointType aPnt02[3] = {aPnt01[0] + aNormal[0], + aPnt01[1] + aNormal[1], + aPnt01[2] + aNormal[2]}; + vtkPlane::IntersectWithLine(aPnt01,aPnt02,aNormal,anOrigin,aParam,aPnt0); + + vtkFloatingPointType aPnt11[3] = {aPnt[0] - aDelta[0][0] + aDelta[1][0], + aPnt[1] - aDelta[0][1] + aDelta[1][1], + aPnt[2] - aDelta[0][2] + aDelta[1][2]}; + vtkFloatingPointType aPnt12[3] = {aPnt11[0] + aNormal[0], + aPnt11[1] + aNormal[1], + aPnt11[2] + aNormal[2]}; + vtkPlane::IntersectWithLine(aPnt11,aPnt12,aNormal,anOrigin,aParam,aPnt1); + + vtkFloatingPointType aPnt21[3] = {aPnt[0] + aDelta[0][0] - aDelta[1][0], + aPnt[1] + aDelta[0][1] - aDelta[1][1], + aPnt[2] + aDelta[0][2] - aDelta[1][2]}; + vtkFloatingPointType aPnt22[3] = {aPnt21[0] + aNormal[0], + aPnt21[1] + aNormal[1], + aPnt21[2] + aNormal[2]}; + vtkPlane::IntersectWithLine(aPnt21,aPnt22,aNormal,anOrigin,aParam,aPnt2); + + vtkPlaneSource* aPlaneSource = aPlane->myPlaneSource; + aPlaneSource->SetNormal(aNormal[0],aNormal[1],aNormal[2]); + aPlaneSource->SetOrigin(aPnt0[0],aPnt0[1],aPnt0[2]); + aPlaneSource->SetPoint1(aPnt1[0],aPnt1[1],aPnt1[2]); + aPlaneSource->SetPoint2(aPnt2[0],aPnt2[1],aPnt2[2]); + } if(AutoApplyCheckBox->isChecked()) ClickOnApply(); - SMESH::RenderViewWindow(SMESH::GetCurrentVtkView()); + SMESH::RenderViewWindow( myViewWindow ); } //======================================================================= @@ -885,8 +1030,8 @@ void SMESHGUI_ClippingDlg::SetCurrentPlaneParam() //======================================================================= void SMESHGUI_ClippingDlg::OnPreviewToggle (bool theIsToggled) { - std::for_each(myPlanes.begin(),myPlanes.end(),TSetVisiblity(theIsToggled)); - SMESH::RenderViewWindow(SMESH::GetCurrentVtkView()); + std::for_each(myPlanes.begin(),myPlanes.end(),TSetVisibility(theIsToggled)); + SMESH::RenderViewWindow( myViewWindow ); } //================================================================================= @@ -904,3 +1049,124 @@ void SMESHGUI_ClippingDlg::keyPressEvent( QKeyEvent* e ) ClickOnHelp(); } } + +//================================================================================= +// function : initializePlaneData() +// purpose : +//================================================================================= +void SMESHGUI_ClippingDlg::initializePlaneData() +{ + const SMESHGUI_ClippingPlaneInfoMap& aClippingPlaneInfoMap = mySMESHGUI->getClippingPlaneInfoMap(); + SMESHGUI_ClippingPlaneInfoMap::const_iterator anIter1 = aClippingPlaneInfoMap.find( myViewWindow->getViewManager() ); + if( anIter1 != aClippingPlaneInfoMap.end() ) { + const SMESHGUI_ClippingPlaneInfoList& aClippingPlaneInfoList = anIter1->second; + SMESHGUI_ClippingPlaneInfoList::const_iterator anIter2 = aClippingPlaneInfoList.begin(); + for( ; anIter2 != aClippingPlaneInfoList.end(); anIter2++ ) { + const SMESH::ClippingPlaneInfo& aClippingPlaneInfo = *anIter2; + SMESH::TPlane aTPlane( aClippingPlaneInfo.Plane ); + SMESH::TPlaneData aPlaneData( aTPlane, aClippingPlaneInfo.ActorList ); + myPlanes.push_back( aPlaneData ); + } + } + std::for_each( myPlanes.begin(),myPlanes.end(), TSetVisibility( PreviewCheckBox->isChecked() ) ); +} + +//================================================================================= +// function : updateActorList() +// purpose : +//================================================================================= +void SMESHGUI_ClippingDlg::updateActorList() +{ + ActorList->clear(); + + SalomeApp_Study* anAppStudy = SMESHGUI::activeStudy(); + if( !anAppStudy ) + return; + + _PTR(Study) aStudy = anAppStudy->studyDS(); + if( !aStudy ) + return; + + if( !myViewWindow ) + return; + + int aCurPlaneIndex = ComboBoxPlanes->currentIndex(); + const SMESH::TPlaneData& aPlaneData = myPlanes[ aCurPlaneIndex ]; + const SMESH::TActorList& anActorList = aPlaneData.ActorList; + + VTK::ActorCollectionCopy aCopy( myViewWindow->getRenderer()->GetActors() ); + vtkActorCollection* anAllActors = aCopy.GetActors(); + anAllActors->InitTraversal(); + while( vtkActor* aVTKActor = anAllActors->GetNextActor() ) { + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) { + if( anActor->hasIO() ) { + Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); + if( _PTR(SObject) aSObj = aStudy->FindObjectID( anIO->getEntry() ) ) { + bool anIsChecked = false; + SMESH::TActorList::const_iterator anIter = anActorList.begin(); + for ( ; anIter != anActorList.end(); anIter++ ) { + if( vtkActor* aVTKActorRef = *anIter ) { + if( SMESH_Actor* anActorRef = SMESH_Actor::SafeDownCast( aVTKActorRef ) ) { + if( anActorRef == anActor ) { + anIsChecked = true; + break; + } + } + } + } + QString aName = QString( aSObj->GetName().c_str() ); + QListWidgetItem* anItem = new ActorItem( anActor, aName, ActorList ); + anItem->setCheckState( anIsChecked ? Qt::Checked : Qt::Unchecked ); + updateActorItem( anItem, true, false ); + } + } + } + } +} + +//================================================================================= +// function : getCurrentActors() +// purpose : +//================================================================================= +SMESH::TActorList SMESHGUI_ClippingDlg::getCurrentActors() +{ + SMESH::TActorList anActorList; + for( int i = 0, n = ActorList->count(); i < n; i++ ) + if( ActorItem* anItem = dynamic_cast( ActorList->item( i ) ) ) + if( anItem->checkState() == Qt::Checked ) + if( SMESH_Actor* anActor = anItem->getActor() ) + anActorList.push_back( anActor ); + return anActorList; +} + +//================================================================================= +// function : dumpPlaneData() +// purpose : +//================================================================================= +void SMESHGUI_ClippingDlg::dumpPlaneData() const +{ + printf( "----------- Plane Data -----------\n" ); + int anId = 1; + SMESH::TPlaneDataVector::const_iterator anIter1 = myPlanes.begin(); + for ( ; anIter1 != myPlanes.end(); anIter1++, anId++ ) { + SMESH::TPlaneData aPlaneData = *anIter1; + SMESH::TPlane aPlane = aPlaneData.Plane; + vtkFloatingPointType* aNormal = aPlane->GetNormal(); + vtkFloatingPointType* anOrigin = aPlane->GetOrigin(); + printf( "Plane N%d:\n", anId ); + printf( " Normal = ( %f, %f, %f )\n", aNormal[0], aNormal[1], aNormal[2] ); + printf( " Origin = ( %f, %f, %f )\n", anOrigin[0], anOrigin[1], anOrigin[2] ); + + SMESH::TActorList anActorList = aPlaneData.ActorList; + SMESH::TActorList::const_iterator anIter2 = anActorList.begin(); + for ( ; anIter2 != anActorList.end(); anIter2++ ) { + if( vtkActor* aVTKActor = *anIter2 ) { + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) + printf( " - Actor: '%s'\n", anActor->getName() ); + } + else + printf( " - Actor: NULL\n"); + } + } + printf( "----------------------------------\n" ); +} diff --git a/src/SMESHGUI/SMESHGUI_ClippingDlg.h b/src/SMESHGUI/SMESHGUI_ClippingDlg.h index af9829762..70bd03b89 100644 --- a/src/SMESHGUI/SMESHGUI_ClippingDlg.h +++ b/src/SMESHGUI/SMESHGUI_ClippingDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ClippingDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -34,31 +35,91 @@ // Qt includes #include +#include // VTK includes +#include #include // STL includes +#include +#include #include class QLabel; class QPushButton; class QCheckBox; class QComboBox; -class LightApp_SelectionMgr; -class SVTK_Selector; +class QListWidget; +class QListWidgetItem; +class SALOME_Actor; class SMESHGUI; class SMESH_Actor; -class OrientedPlane; class SMESHGUI_SpinBox; +class vtkActor; +class vtkDataSetMapper; +class vtkPlaneSource; namespace SMESH { - typedef vtkSmartPointer TVTKPlane; - typedef std::vector TPlanes; enum Orientation { XY, YZ, ZX }; -}; + class OrientedPlane: public vtkPlane + { + QPointer myViewWindow; + vtkDataSetMapper* myMapper; + + public: + static OrientedPlane *New(); + static OrientedPlane *New(SVTK_ViewWindow* theViewWindow); + vtkTypeMacro (OrientedPlane, vtkPlane); + + SMESH::Orientation myOrientation; + float myDistance; + double myAngle[2]; + + vtkPlaneSource* myPlaneSource; + SALOME_Actor *myActor; + + void SetOrientation (SMESH::Orientation theOrientation) { myOrientation = theOrientation; } + SMESH::Orientation GetOrientation() { return myOrientation; } + + void SetDistance (float theDistance) { myDistance = theDistance; } + float GetDistance() { return myDistance; } + + void ShallowCopy (OrientedPlane* theOrientedPlane); + + protected: + OrientedPlane(SVTK_ViewWindow* theViewWindow); + OrientedPlane(); + + void Init(); + + ~OrientedPlane(); + private: + // Not implemented. + OrientedPlane (const OrientedPlane&); + void operator= (const OrientedPlane&); + }; + + typedef vtkSmartPointer TPlane; + typedef std::list TActorList; + + struct TPlaneData + { + TPlaneData( TPlane thePlane, + TActorList theActorList ) + { + Plane = thePlane; + ActorList = theActorList; + } + TPlane Plane; + TActorList ActorList; + }; + + typedef std::vector TPlaneVector; + typedef std::vector TPlaneDataVector; +}; //================================================================================= // class : SMESHGUI_ClippingDlg @@ -69,7 +130,7 @@ class SMESHGUI_EXPORT SMESHGUI_ClippingDlg : public QDialog Q_OBJECT public: - SMESHGUI_ClippingDlg( SMESHGUI* ); + SMESHGUI_ClippingDlg( SMESHGUI*, SVTK_ViewWindow* ); ~SMESHGUI_ClippingDlg(); double getDistance() const; @@ -77,35 +138,42 @@ public: double getRotation1() const; double getRotation2() const; void setRotation( const double, const double ); - void Sinchronize(); // used in SMESHGUI::restoreVisualParameters() to avoid // declaration of OrientedPlane outside of SMESHGUI_ClippingDlg.cxx - static void AddPlane (SMESH_Actor* theActor, - SVTK_ViewWindow* theViewWindow, - SMESH::Orientation theOrientation, - double theDistance, - vtkFloatingPointType theAngle[2]); - - static void GetPlaneParam (SMESH_Actor* theActor, - int thePlaneIndex, - SMESH::Orientation& theOrientation, - double& theDistance, - vtkFloatingPointType* theAngle); + static SMESH::OrientedPlane* AddPlane (SMESH::TActorList theActorList, + SVTK_ViewWindow* theViewWindow, + SMESH::Orientation theOrientation, + double theDistance, + const vtkFloatingPointType theAngle[2]); protected: void keyPressEvent( QKeyEvent* ); private: - LightApp_SelectionMgr* mySelectionMgr; - SVTK_Selector* mySelector; + void initializePlaneData(); + + void synchronize(); + + void updateActorList(); + SMESH::TActorList getCurrentActors(); + + void updateActorItem( QListWidgetItem* theItem, + bool theUpdateSelectAll, + bool theUpdateClippingPlaneMap ); + + void dumpPlaneData() const; + +private: SMESHGUI* mySMESHGUI; - SMESH_Actor* myActor; - SMESH::TPlanes myPlanes; + SVTK_ViewWindow* myViewWindow; + SMESH::TPlaneDataVector myPlanes; QComboBox* ComboBoxPlanes; QPushButton* buttonNew; QPushButton* buttonDelete; + QListWidget* ActorList; + QCheckBox* SelectAllCheckBox; QLabel* TextLabelOrientation; QComboBox* ComboBoxOrientation; QLabel* TextLabelDistance; @@ -128,9 +196,10 @@ public slots: void onSelectPlane( int ); void ClickOnNew(); void ClickOnDelete(); + void onActorItemChanged( QListWidgetItem* ); + void onSelectAll( int ); void onSelectOrientation( int ); void SetCurrentPlaneParam(); - void onSelectionChanged(); void OnPreviewToggle( bool ); void ClickOnOk(); void ClickOnCancel(); diff --git a/src/SMESHGUI/SMESHGUI_ComputeDlg.cxx b/src/SMESHGUI/SMESHGUI_ComputeDlg.cxx index 147a6a464..0bb2fa5ef 100644 --- a/src/SMESHGUI/SMESHGUI_ComputeDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ComputeDlg.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_ComputeDlg.cxx // Author : Edward AGAPOV, Open CASCADE S.A.S. // SMESH includes @@ -29,8 +27,12 @@ #include "SMESHGUI_GEOMGenUtils.h" #include "SMESHGUI_MeshUtils.h" #include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_MeshInfosBox.h" #include "SMESHGUI_HypothesesUtils.h" #include "SMESHGUI_MeshEditPreview.h" +#include "SMESHGUI_MeshOrderOp.h" +#include "SMESHGUI_MeshOrderDlg.h" + #include "SMESH_ActorUtils.h" #include @@ -83,6 +85,8 @@ #include #include #include +#include +#include // VTK includes #include @@ -96,6 +100,7 @@ #define COLONIZE(str) (QString(str).contains(":") > 0 ? QString(str) : QString(str) + " :" ) +/* OBSOLETE static void addSeparator( QWidget* parent ) { QGridLayout* l = qobject_cast( parent->layout() ); @@ -107,6 +112,7 @@ static void addSeparator( QWidget* parent ) l->addWidget( hline, row, i ); } } +*/ enum TCol { COL_ALGO = 0, COL_SHAPE, COL_ERROR, COL_SHAPEID, COL_PUBLISHED, COL_BAD_MESH, NB_COLUMNS @@ -339,10 +345,13 @@ namespace SMESH CASE2TEXT( COMPERR_EXCEPTION ); CASE2TEXT( COMPERR_MEMORY_PB ); CASE2TEXT( COMPERR_BAD_SHAPE ); + CASE2TEXT( COMPERR_CANCELED ); case SMESH::COMPERR_ALGO_FAILED: if ( strlen(comment) == 0 ) text = QObject::tr("COMPERR_ALGO_FAILED"); break; + case SMESH::COMPERR_WARNING: + return comment ? QString(comment) : QObject::tr("COMPERR_UNKNOWN"); default: text = QString("#%1").arg( -errCode ); } @@ -351,7 +360,7 @@ namespace SMESH } // ----------------------------------------------------------------------- /*! - * \brief Return SO of a subshape + * \brief Return SO of a sub-shape */ _PTR(SObject) getSubShapeSO( int subShapeID, GEOM::GEOM_Object_var aMainShape) { @@ -376,7 +385,7 @@ namespace SMESH } // ----------------------------------------------------------------------- /*! - * \brief Return subshape by ID + * \brief Return sub-shape by ID */ GEOM::GEOM_Object_ptr getSubShape( int subShapeID, GEOM::GEOM_Object_var aMainShape) { @@ -414,7 +423,7 @@ namespace SMESH } // ----------------------------------------------------------------------- /*! - * \brief Return text describing a subshape + * \brief Return text describing a sub-shape */ QString shapeText(int subShapeID, GEOM::GEOM_Object_var aMainShape ) { @@ -452,351 +461,20 @@ namespace SMESH } // namespace SMESH -// ========================================================================================= -/*! - * \brief Box showing mesh info - */ -// ========================================================================================= - -SMESHGUI_MeshInfosBox::SMESHGUI_MeshInfosBox(const bool full, QWidget* theParent) - : QGroupBox( tr("SMESH_MESHINFO_TITLE"), theParent ), myFull( full ) -{ - QGridLayout* l = new QGridLayout(this); - l->setMargin( MARGIN ); - l->setSpacing( SPACING ); - - QFont italic = font(); italic.setItalic(true); - QFont bold = font(); bold.setBold(true); - - QLabel* lab; - int row = 0; - - // title - lab = new QLabel( this ); - lab->setMinimumWidth(100); lab->setFont( italic ); - l->addWidget( lab, row, 0 ); - // -- - lab = new QLabel(tr("SMESH_MESHINFO_ORDER0"), this ); - lab->setMinimumWidth(100); lab->setFont( italic ); - l->addWidget( lab, row, 1 ); - // -- - lab = new QLabel(tr("SMESH_MESHINFO_ORDER1"), this ); - lab->setMinimumWidth(100); lab->setFont( italic ); - l->addWidget( lab, row, 2 ); - // -- - lab = new QLabel(tr("SMESH_MESHINFO_ORDER2"), this ); - lab->setMinimumWidth(100); lab->setFont( italic ); - l->addWidget( lab, row, 3 ); - - if ( myFull ) - { - // nodes - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), this ); - lab->setFont( bold ); - l->addWidget( lab, row, 0 ); - // -- - myNbNode = new QLabel( this ); - l->addWidget( myNbNode, row, 1 ); - - addSeparator(this); // add separator - - // edges - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), this ); - lab->setFont( bold ); - l->addWidget( lab, row, 0 ); - // -- - myNbEdge = new QLabel( this ); - l->addWidget( myNbEdge, row, 1 ); - // -- - myNbLinEdge = new QLabel( this ); - l->addWidget( myNbLinEdge, row, 2 ); - // -- - myNbQuadEdge = new QLabel( this ); - l->addWidget( myNbQuadEdge, row, 3 ); - - addSeparator(this); // add separator - - // faces - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_FACES")), this); - lab->setFont( bold ); - l->addWidget( lab, row, 0 ); - // -- - myNbFace = new QLabel( this ); - l->addWidget( myNbFace, row, 1 ); - // -- - myNbLinFace = new QLabel( this ); - l->addWidget( myNbLinFace, row, 2 ); - // -- - myNbQuadFace = new QLabel( this ); - l->addWidget( myNbQuadFace, row, 3 ); - // -- - row++; // increment row count - // ... triangles - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TRIANGLES")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbTrai = new QLabel( this ); - l->addWidget( myNbTrai, row, 1 ); - // -- - myNbLinTrai = new QLabel( this ); - l->addWidget( myNbLinTrai, row, 2 ); - // -- - myNbQuadTrai = new QLabel( this ); - l->addWidget( myNbQuadTrai, row, 3 ); - // -- - row++; // increment row count - // ... quadrangles - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_QUADRANGLES")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbQuad = new QLabel( this ); - l->addWidget( myNbQuad, row, 1 ); - // -- - myNbLinQuad = new QLabel( this ); - l->addWidget( myNbLinQuad, row, 2 ); - // -- - myNbQuadQuad = new QLabel( this ); - l->addWidget( myNbQuadQuad, row, 3 ); - // -- - row++; // increment row count - // ... poligones - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_POLYGONES")), this ); - l->addWidget( lab, row, 0 ); - myNbPolyg = new QLabel( this ); - l->addWidget( myNbPolyg, row, 1 ); - - addSeparator(this); // add separator - - // volumes - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_VOLUMES")), this); - lab->setFont( bold ); - l->addWidget( lab, row, 0 ); - // -- - myNbVolum = new QLabel( this ); - l->addWidget( myNbVolum, row, 1 ); - // -- - myNbLinVolum = new QLabel( this ); - l->addWidget( myNbLinVolum, row, 2 ); - // -- - myNbQuadVolum = new QLabel( this ); - l->addWidget( myNbQuadVolum, row, 3 ); - // -- - row++; // increment row count - // ... tetras - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TETRAS")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbTetra = new QLabel( this ); - l->addWidget( myNbTetra, row, 1 ); - // -- - myNbLinTetra = new QLabel( this ); - l->addWidget( myNbLinTetra, row, 2 ); - // -- - myNbQuadTetra = new QLabel( this ); - l->addWidget( myNbQuadTetra, row, 3 ); - // -- - row++; // increment row count - // ... hexas - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_HEXAS")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbHexa = new QLabel( this ); - l->addWidget( myNbHexa, row, 1 ); - // -- - myNbLinHexa = new QLabel( this ); - l->addWidget( myNbLinHexa, row, 2 ); - // -- - myNbQuadHexa = new QLabel( this ); - l->addWidget( myNbQuadHexa, row, 3 ); - // -- - row++; // increment row count - // ... pyras - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PYRAS")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbPyra = new QLabel( this ); - l->addWidget( myNbPyra, row, 1 ); - // -- - myNbLinPyra = new QLabel( this ); - l->addWidget( myNbLinPyra, row, 2 ); - // -- - myNbQuadPyra = new QLabel( this ); - l->addWidget( myNbQuadPyra, row, 3 ); - // -- - row++; // increment row count - // ... prisms - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PRISMS")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbPrism = new QLabel( this ); - l->addWidget( myNbPrism, row, 1 ); - // -- - myNbLinPrism = new QLabel( this ); - l->addWidget( myNbLinPrism, row, 2 ); - // -- - myNbQuadPrism = new QLabel( this ); - l->addWidget( myNbQuadPrism, row, 3 ); - // -- - row++; // increment row count - // ... polyedres - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_POLYEDRES")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbPolyh = new QLabel( this ); - l->addWidget( myNbPolyh, row, 1 ); - } - else - { - // nodes - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbNode = new QLabel( this ); - l->addWidget( myNbNode, row, 1 ); - - // edges - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), this ); - l->addWidget( lab, row, 0 ); - // -- - myNbEdge = new QLabel( this ); - l->addWidget( myNbEdge, row, 1 ); - // -- - myNbLinEdge = new QLabel( this ); - l->addWidget( myNbLinEdge, row, 2 ); - // -- - myNbQuadEdge = new QLabel( this ); - l->addWidget( myNbQuadEdge, row, 3 ); - - // faces - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_FACES")), this); - l->addWidget( lab, row, 0 ); - // -- - myNbFace = new QLabel( this ); - l->addWidget( myNbFace, row, 1 ); - // -- - myNbLinFace = new QLabel( this ); - l->addWidget( myNbLinFace, row, 2 ); - // -- - myNbQuadFace = new QLabel( this ); - l->addWidget( myNbQuadFace, row, 3 ); - - // volumes - row = l->rowCount(); // retrieve current row count - // -- - lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_VOLUMES")), this); - l->addWidget( lab, row, 0 ); - // -- - myNbVolum = new QLabel( this ); - l->addWidget( myNbVolum, row, 1 ); - // -- - myNbLinVolum = new QLabel( this ); - l->addWidget( myNbLinVolum, row, 2 ); - // -- - myNbQuadVolum = new QLabel( this ); - l->addWidget( myNbQuadVolum, row, 3 ); - } -} - -// ========================================================================================= -/*! - * \brief Set mesh info - */ -// ========================================================================================= - -void SMESHGUI_MeshInfosBox::SetInfoByMesh(SMESH::SMESH_Mesh_var mesh) -{ - const SMESH::ElementOrder lin = SMESH::ORDER_LINEAR; - int nbTot, nbLin; - - // nodes - myNbNode ->setText( QString("%1").arg( mesh->NbNodes() )); - - // edges - nbTot = mesh->NbEdges(), nbLin = mesh->NbEdgesOfOrder(lin); - myNbEdge ->setText( QString("%1").arg( nbTot )); - myNbLinEdge ->setText( QString("%1").arg( nbLin )); - myNbQuadEdge ->setText( QString("%1").arg( nbTot - nbLin )); - - // faces - nbTot = mesh->NbFaces(), nbLin = mesh->NbFacesOfOrder(lin); - myNbFace ->setText( QString("%1").arg( nbTot )); - myNbLinFace ->setText( QString("%1").arg( nbLin )); - myNbQuadFace ->setText( QString("%1").arg( nbTot - nbLin )); - - // volumes - nbTot = mesh->NbVolumes(), nbLin = mesh->NbVolumesOfOrder(lin); - myNbVolum ->setText( QString("%1").arg( nbTot )); - myNbLinVolum ->setText( QString("%1").arg( nbLin )); - myNbQuadVolum->setText( QString("%1").arg( nbTot - nbLin )); - - if ( myFull ) - { - // triangles - nbTot = mesh->NbTriangles(), nbLin = mesh->NbTrianglesOfOrder(lin); - myNbTrai ->setText( QString("%1").arg( nbTot )); - myNbLinTrai ->setText( QString("%1").arg( nbLin )); - myNbQuadTrai ->setText( QString("%1").arg( nbTot - nbLin )); - // quadrangles - nbTot = mesh->NbQuadrangles(), nbLin = mesh->NbQuadranglesOfOrder(lin); - myNbQuad ->setText( QString("%1").arg( nbTot )); - myNbLinQuad ->setText( QString("%1").arg( nbLin )); - myNbQuadQuad ->setText( QString("%1").arg( nbTot - nbLin )); - // poligones - myNbPolyg ->setText( QString("%1").arg( mesh->NbPolygons() )); - - // tetras - nbTot = mesh->NbTetras(), nbLin = mesh->NbTetrasOfOrder(lin); - myNbTetra ->setText( QString("%1").arg( nbTot )); - myNbLinTetra ->setText( QString("%1").arg( nbLin )); - myNbQuadTetra->setText( QString("%1").arg( nbTot - nbLin )); - // hexas - nbTot = mesh->NbHexas(), nbLin = mesh->NbHexasOfOrder(lin); - myNbHexa ->setText( QString("%1").arg( nbTot )); - myNbLinHexa ->setText( QString("%1").arg( nbLin )); - myNbQuadHexa ->setText( QString("%1").arg( nbTot - nbLin )); - // pyras - nbTot = mesh->NbPyramids(), nbLin = mesh->NbPyramidsOfOrder(lin); - myNbPyra ->setText( QString("%1").arg( nbTot )); - myNbLinPyra ->setText( QString("%1").arg( nbLin )); - myNbQuadPyra ->setText( QString("%1").arg( nbTot - nbLin )); - // prisms - nbTot = mesh->NbPrisms(), nbLin = mesh->NbPrismsOfOrder(lin); - myNbPrism ->setText( QString("%1").arg( nbTot )); - myNbLinPrism ->setText( QString("%1").arg( nbLin )); - myNbQuadPrism->setText( QString("%1").arg( nbTot - nbLin )); - // polyedres - myNbPolyh ->setText( QString("%1").arg( mesh->NbPolyhedrons() )); - } -} - // ========================================================================================= /*! * \brief Dialog to compute a mesh and show computation errors */ //======================================================================= -SMESHGUI_ComputeDlg::SMESHGUI_ComputeDlg( QWidget* parent ) +SMESHGUI_ComputeDlg::SMESHGUI_ComputeDlg( QWidget* parent, bool ForEval ) : SMESHGUI_Dialog( parent, false, true, Close/* | Help*/ ) { QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame()); aDlgLay->setMargin( 0 ); aDlgLay->setSpacing( SPACING ); - QFrame* aMainFrame = createMainFrame (mainFrame()); + QFrame* aMainFrame = createMainFrame(mainFrame(),ForEval); aDlgLay->addWidget(aMainFrame); @@ -818,7 +496,7 @@ SMESHGUI_ComputeDlg::~SMESHGUI_ComputeDlg() // purpose : Create frame containing dialog's fields //======================================================================= -QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent) +QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent, bool ForEval) { QFrame* aFrame = new QFrame(theParent); @@ -827,7 +505,13 @@ QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent) // constructor - QGroupBox* aPixGrp = new QGroupBox(tr("CONSTRUCTOR"), aFrame); + QGroupBox* aPixGrp; + if(ForEval) { + aPixGrp = new QGroupBox(tr("EVAL_DLG"), aFrame); + } + else { + aPixGrp = new QGroupBox(tr("CONSTRUCTOR"), aFrame); + } QButtonGroup* aBtnGrp = new QButtonGroup(this); QHBoxLayout* aPixGrpLayout = new QHBoxLayout(aPixGrp); aPixGrpLayout->setMargin(MARGIN); aPixGrpLayout->setSpacing(SPACING); @@ -854,10 +538,11 @@ QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent) // Computation errors myCompErrorGroup = new QGroupBox(tr("ERRORS"), aFrame); - myTable = new QTableWidget( 1, NB_COLUMNS, myCompErrorGroup); - myShowBtn = new QPushButton(tr("SHOW_SHAPE"), myCompErrorGroup); - myPublishBtn = new QPushButton(tr("PUBLISH_SHAPE"), myCompErrorGroup); - myBadMeshBtn = new QPushButton(tr("SHOW_BAD_MESH"), myCompErrorGroup); + myWarningLabel = new QLabel(QString("%1").arg(tr("COMPUTE_WARNING")), myCompErrorGroup); + myTable = new QTableWidget( 1, NB_COLUMNS, myCompErrorGroup); + myShowBtn = new QPushButton(tr("SHOW_SHAPE"), myCompErrorGroup); + myPublishBtn = new QPushButton(tr("PUBLISH_SHAPE"), myCompErrorGroup); + myBadMeshBtn = new QPushButton(tr("SHOW_BAD_MESH"), myCompErrorGroup); //myTable->setReadOnly( true ); // VSR: check myTable->setEditTriggers( QAbstractItemView::NoEditTriggers ); @@ -879,11 +564,12 @@ QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent) QGridLayout* grpLayout = new QGridLayout(myCompErrorGroup); grpLayout->setSpacing(SPACING); grpLayout->setMargin(MARGIN); - grpLayout->addWidget( myTable, 0, 0, 4, 1 ); - grpLayout->addWidget( myShowBtn, 0, 1 ); - grpLayout->addWidget( myPublishBtn, 1, 1 ); - grpLayout->addWidget( myBadMeshBtn, 2, 1 ); - grpLayout->setRowStretch( 3, 1 ); + grpLayout->addWidget( myWarningLabel, 0, 0 ); + grpLayout->addWidget( myTable, 1, 0, 4, 1 ); + grpLayout->addWidget( myShowBtn, 1, 1 ); + grpLayout->addWidget( myPublishBtn, 2, 1 ); + grpLayout->addWidget( myBadMeshBtn, 3, 1 ); + grpLayout->setRowStretch( 4, 1 ); // Hypothesis definition errors @@ -931,8 +617,7 @@ QFrame* SMESHGUI_ComputeDlg::createMainFrame (QWidget* theParent) //================================================================================ SMESHGUI_BaseComputeOp::SMESHGUI_BaseComputeOp() - : SMESHGUI_Operation(), - myCompDlg( 0 ) + : SMESHGUI_Operation(), myCompDlg( 0 ) { myTShapeDisplayer = new SMESH::TShapeDisplayer(); myBadMeshDisplayer = 0; @@ -941,6 +626,15 @@ SMESHGUI_BaseComputeOp::SMESHGUI_BaseComputeOp() myHelpFileName = "about_meshes_page.html"; // V4 } +SMESH::SMESH_Mesh_ptr SMESHGUI_BaseComputeOp::getMesh() +{ + LightApp_SelectionMgr* Sel = selectionMgr(); + SALOME_ListIO selected; Sel->selectedObjects( selected ); + Handle(SALOME_InteractiveObject) anIO = selected.First(); + SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(anIO); + return myMesh->_is_nil() ? aMesh._retn() : SMESH::SMESH_Mesh::_duplicate( myMesh ); +} + //================================================================================ /*! * \brief Start operation @@ -963,8 +657,8 @@ void SMESHGUI_BaseComputeOp::startOperation() int nbSel = selected.Extent(); if (nbSel != 1) { SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), - tr("SMESH_WRN_NO_AVAILABLE_DATA")); + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_NO_AVAILABLE_DATA")); onCancel(); return; } @@ -973,16 +667,97 @@ void SMESHGUI_BaseComputeOp::startOperation() myMesh = SMESH::GetMeshByIO(myIObject); if (myMesh->_is_nil()) { SUIT_MessageBox::warning(desktop(), - tr("SMESH_WRN_WARNING"), - tr("SMESH_WRN_NO_AVAILABLE_DATA")); + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_NO_AVAILABLE_DATA")); onCancel(); - + return; } myMainShape = myMesh->GetShapeToMesh(); SMESHGUI_Operation::startOperation(); } +//================================================================================ +//================================================================================ + +SMESHGUI_ComputeDlg_QThread::SMESHGUI_ComputeDlg_QThread(SMESH::SMESH_Gen_var gen, + SMESH::SMESH_Mesh_var mesh, + GEOM::GEOM_Object_var mainShape) +{ + myResult = false; + myGen = gen; + myMesh = mesh; + myMainShape = mainShape; +} + +void SMESHGUI_ComputeDlg_QThread::run() +{ + myResult = myGen->Compute(myMesh, myMainShape); +} + +bool SMESHGUI_ComputeDlg_QThread::result() +{ + return myResult; +} + +void SMESHGUI_ComputeDlg_QThread::cancel() +{ + myGen->CancelCompute(myMesh, myMainShape); +} + +//================================================================================ +//================================================================================ + +SMESHGUI_ComputeDlg_QThreadQDialog::SMESHGUI_ComputeDlg_QThreadQDialog(QWidget *parent, + SMESH::SMESH_Gen_var gen, + SMESH::SMESH_Mesh_var mesh, + GEOM::GEOM_Object_var mainShape) + : QDialog(parent), + qthread(gen, mesh, mainShape) +{ + // -- + setWindowTitle(tr("Compute")); + cancelButton = new QPushButton(tr("Cancel")); + cancelButton->setDefault(true); + connect(cancelButton, SIGNAL(clicked()), this, SLOT(onCancel())); + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(cancelButton); + setLayout(layout); + resize(200, 50); + // -- + startTimer(30); // 30 millisecs + qthread.start(); +} + +bool SMESHGUI_ComputeDlg_QThreadQDialog::result() +{ + return qthread.result(); +} + +void SMESHGUI_ComputeDlg_QThreadQDialog::onCancel() +{ + qthread.cancel(); +} + +void SMESHGUI_ComputeDlg_QThreadQDialog::timerEvent(QTimerEvent *event) +{ + if(qthread.isFinished()) + { + close(); + } + event->accept(); +} + +void SMESHGUI_ComputeDlg_QThreadQDialog::closeEvent(QCloseEvent *event) +{ + if(qthread.isRunning()) + { + event->ignore(); + return; + } + event->accept(); +} + //================================================================================ /*! * \brief computeMesh() @@ -1001,9 +776,11 @@ void SMESHGUI_BaseComputeOp::computeMesh() bool computeFailed = true, memoryLack = false; _PTR(SObject) aMeshSObj = SMESH::FindSObject(myMesh); + if ( !aMeshSObj ) // IPAL 21340 + return; bool hasShape = myMesh->HasShapeToMesh(); bool shapeOK = myMainShape->_is_nil() ? !hasShape : hasShape; - if ( shapeOK && aMeshSObj ) + if ( shapeOK ) { myCompDlg->myMeshName->setText( aMeshSObj->GetName().c_str() ); SMESH::SMESH_Gen_var gen = getSMESHGUI()->GetSMESHGen(); @@ -1011,12 +788,26 @@ void SMESHGUI_BaseComputeOp::computeMesh() if ( errors->length() > 0 ) { aHypErrors = SMESH::GetMessageOnAlgoStateErrors( errors.in() ); } + if ( myMesh->HasModificationsToDiscard() && // issue 0020693 + SUIT_MessageBox::question( desktop(), tr( "SMESH_WARNING" ), + tr( "FULL_RECOMPUTE_QUESTION" ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 1, 0 ) == 0 ) + myMesh->Clear(); SUIT_OverrideCursor aWaitCursor; try { #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 OCC_CATCH_SIGNALS; #endif - if (gen->Compute(myMesh, myMainShape)) + //SMESH::UpdateNulData(myIObject, true); + bool res; +#ifdef WITH_SMESH_CANCEL_COMPUTE + SMESHGUI_ComputeDlg_QThreadQDialog qthreaddialog(desktop(), gen, myMesh, myMainShape); + qthreaddialog.exec(); + res = qthreaddialog.result(); +#else + res = gen->Compute(myMesh, myMainShape); +#endif + if (res) computeFailed = false; } catch(const SALOME::SALOME_Exception & S_ex){ @@ -1035,6 +826,10 @@ void SMESHGUI_BaseComputeOp::computeMesh() memoryLack = true; } + if ( !memoryLack && !SMDS_Mesh::CheckMemory(true) ) { // has memory to show dialog boxes? + memoryLack = true; + } + // NPAL16631: if ( !memoryLack ) { SMESH::ModifiedMesh(aMeshSObj, !computeFailed, myMesh->NbNodes() == 0); @@ -1042,32 +837,45 @@ void SMESHGUI_BaseComputeOp::computeMesh() // SHOW MESH // NPAL16631: if ( getSMESHGUI()->automaticUpdate() ) - if ( !memoryLack && getSMESHGUI()->automaticUpdate() ) + SUIT_ResourceMgr* resMgr = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() ); + long newSize = myMesh->NbElements(); + bool limitExceeded; + if ( !memoryLack ) { - try { + if ( getSMESHGUI()->automaticUpdate( newSize, &limitExceeded ) ) + { + try { #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 - OCC_CATCH_SIGNALS; + OCC_CATCH_SIGNALS; #endif - SMESH::Update(myIObject, true); - } - catch (...) { + SMESH::Update(myIObject, true); + } + catch (...) { #ifdef _DEBUG_ - MESSAGE ( "Exception thrown during mesh visualization" ); + MESSAGE ( "Exception thrown during mesh visualization" ); #endif - if ( SMDS_Mesh::CheckMemory(true) ) { // has memory to show warning? - SMESH::OnVisuException(); - } - else { - memoryLack = true; + if ( SMDS_Mesh::CheckMemory(true) ) { // has memory to show warning? + SMESH::OnVisuException(); + } + else { + memoryLack = true; + } } } + else if ( limitExceeded ) + { + long limitSize = resMgr->integerValue( "SMESH", "update_limit", 500000 ); + SUIT_MessageBox::warning( desktop(), + tr( "SMESH_WRN_WARNING" ), + tr( "SMESH_WRN_SIZE_LIMIT_EXCEEDED" ).arg( newSize ).arg( limitSize ) ); + } } LightApp_SelectionMgr *Sel = selectionMgr(); if ( Sel ) { - SALOME_ListIO selected; - selected.Append( myIObject ); - Sel->setSelectedObjects( selected ); + SALOME_ListIO selected; + selected.Append( myIObject ); + Sel->setSelectedObjects( selected ); } } } @@ -1110,10 +918,10 @@ void SMESHGUI_BaseComputeOp::computeMesh() } void SMESHGUI_BaseComputeOp::showComputeResult( const bool theMemoryLack, - const bool theNoCompError, - SMESH::compute_error_array_var& theCompErrors, - const bool theNoHypoError, - const QString& theHypErrors ) + const bool theNoCompError, + SMESH::compute_error_array_var& theCompErrors, + const bool theNoHypoError, + const QString& theHypErrors ) { bool hasShape = myMesh->HasShapeToMesh(); SMESHGUI_ComputeDlg* aCompDlg = computeDlg(); @@ -1129,7 +937,8 @@ void SMESHGUI_BaseComputeOp::showComputeResult( const bool theMemoryLack, } else if ( theNoCompError && theNoHypoError ) { - aCompDlg->myFullInfo->SetInfoByMesh( myMesh ); + SMESH::long_array_var aRes = myMesh->GetMeshInfo(); + aCompDlg->myFullInfo->SetMeshInfo( aRes ); aCompDlg->myFullInfo->show(); aCompDlg->myBriefInfo->hide(); aCompDlg->myHypErrorGroup->hide(); @@ -1137,25 +946,44 @@ void SMESHGUI_BaseComputeOp::showComputeResult( const bool theMemoryLack, } else { - QTableWidget* tbl = aCompDlg->myTable; - aCompDlg->myBriefInfo->SetInfoByMesh( myMesh ); - aCompDlg->myBriefInfo->show(); - aCompDlg->myFullInfo->hide(); + bool onlyWarnings = !theNoCompError; // == valid mesh computed but there are errors reported + for ( int i = 0; i < theCompErrors->length() && onlyWarnings; ++i ) + onlyWarnings = ( theCompErrors[ i ].code == SMESH::COMPERR_WARNING ); + + // full or brief mesh info + SMESH::long_array_var aRes = myMesh->GetMeshInfo(); + if ( onlyWarnings ) { + aCompDlg->myFullInfo->SetMeshInfo( aRes ); + aCompDlg->myFullInfo->show(); + aCompDlg->myBriefInfo->hide(); + } else { + aCompDlg->myBriefInfo->SetMeshInfo( aRes ); + aCompDlg->myBriefInfo->show(); + aCompDlg->myFullInfo->hide(); + } + // pbs of hypo dfinitions if ( theNoHypoError ) { aCompDlg->myHypErrorGroup->hide(); - } - else { + } else { aCompDlg->myHypErrorGroup->show(); aCompDlg->myHypErrorLabel->setText( theHypErrors ); } - if ( theNoCompError ) { + // table of errors + if ( theNoCompError ) + { aCompDlg->myCompErrorGroup->hide(); } - else { + else + { aCompDlg->myCompErrorGroup->show(); + if ( onlyWarnings ) + aCompDlg->myWarningLabel->show(); + else + aCompDlg->myWarningLabel->hide(); + if ( !hasShape ) { aCompDlg->myPublishBtn->hide(); aCompDlg->myShowBtn->hide(); @@ -1166,6 +994,7 @@ void SMESHGUI_BaseComputeOp::showComputeResult( const bool theMemoryLack, } // fill table of errors + QTableWidget* tbl = aCompDlg->myTable; tbl->setRowCount( theCompErrors->length() ); if ( !hasShape ) tbl->hideColumn( COL_SHAPE ); else tbl->showColumn( COL_SHAPE ); @@ -1176,29 +1005,29 @@ void SMESHGUI_BaseComputeOp::showComputeResult( const bool theMemoryLack, { SMESH::ComputeError & err = theCompErrors[ row ]; - QString text = err.algoName.in(); - if ( !tbl->item( row, COL_ALGO ) ) tbl->setItem( row, COL_ALGO, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_ALGO )->setText( text ); + QString text = err.algoName.in(); + if ( !tbl->item( row, COL_ALGO ) ) tbl->setItem( row, COL_ALGO, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_ALGO )->setText( text ); - text = SMESH::errorText( err.code, err.comment.in() ); - if ( !tbl->item( row, COL_ERROR ) ) tbl->setItem( row, COL_ERROR, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_ERROR )->setText( text ); + text = SMESH::errorText( err.code, err.comment.in() ); + if ( !tbl->item( row, COL_ERROR ) ) tbl->setItem( row, COL_ERROR, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_ERROR )->setText( text ); - text = QString("%1").arg( err.subShapeID ); - if ( !tbl->item( row, COL_SHAPEID ) ) tbl->setItem( row, COL_SHAPEID, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_SHAPEID )->setText( text ); + text = QString("%1").arg( err.subShapeID ); + if ( !tbl->item( row, COL_SHAPEID ) ) tbl->setItem( row, COL_SHAPEID, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_SHAPEID )->setText( text ); text = hasShape ? SMESH::shapeText( err.subShapeID, myMainShape ) : QString(""); - if ( !tbl->item( row, COL_SHAPE ) ) tbl->setItem( row, COL_SHAPE, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_SHAPE )->setText( text ); + if ( !tbl->item( row, COL_SHAPE ) ) tbl->setItem( row, COL_SHAPE, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_SHAPE )->setText( text ); text = ( !hasShape || SMESH::getSubShapeSO( err.subShapeID, myMainShape )) ? "PUBLISHED" : ""; - if ( !tbl->item( row, COL_PUBLISHED ) ) tbl->setItem( row, COL_PUBLISHED, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_PUBLISHED )->setText( text ); // if text=="", "PUBLISH" button enabled + if ( !tbl->item( row, COL_PUBLISHED ) ) tbl->setItem( row, COL_PUBLISHED, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_PUBLISHED )->setText( text ); // if text=="", "PUBLISH" button enabled text = err.hasBadMesh ? "hasBadMesh" : ""; - if ( !tbl->item( row, COL_BAD_MESH ) ) tbl->setItem( row, COL_BAD_MESH, new QTableWidgetItem( text ) ); - else tbl->item( row, COL_BAD_MESH )->setText( text ); + if ( !tbl->item( row, COL_BAD_MESH ) ) tbl->setItem( row, COL_BAD_MESH, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_BAD_MESH )->setText( text ); if ( err.hasBadMesh ) hasBadMesh = true; //tbl->item( row, COL_ERROR )->setWordWrap( true ); // VSR: TODO ??? @@ -1244,7 +1073,7 @@ void SMESHGUI_BaseComputeOp::stopOperation() //================================================================================ /*! - * \brief publish selected subshape + * \brief publish selected sub-shape */ //================================================================================ @@ -1420,7 +1249,7 @@ SMESHGUI_ComputeDlg* SMESHGUI_BaseComputeOp::computeDlg() const if ( !myCompDlg ) { SMESHGUI_BaseComputeOp* me = (SMESHGUI_BaseComputeOp*)this; - me->myCompDlg = new SMESHGUI_ComputeDlg( desktop() ); + me->myCompDlg = new SMESHGUI_ComputeDlg( desktop(), false ); // connect signals and slots connect(myCompDlg->myShowBtn, SIGNAL (clicked()), SLOT(onPreviewShape())); connect(myCompDlg->myPublishBtn, SIGNAL (clicked()), SLOT(onPublishShape())); @@ -1433,6 +1262,17 @@ SMESHGUI_ComputeDlg* SMESHGUI_BaseComputeOp::computeDlg() const return myCompDlg; } +//================================================================================ +/*! + * \brief returns from compute mesh result dialog + */ +//================================================================================ + +bool SMESHGUI_BaseComputeOp::onApply() +{ + return true; +} + //================================================================================ /*! * \brief Return a table @@ -1476,18 +1316,26 @@ SMESHGUI_ComputeOp::~SMESHGUI_ComputeOp() void SMESHGUI_ComputeOp::startOperation() { SMESHGUI_BaseComputeOp::startOperation(); + if (myMesh->_is_nil()) + return; computeMesh(); } //================================================================================ /*! - * \brief perform it's intention action: compute mesh + * \brief check the same operations on the same mesh */ //================================================================================ -bool SMESHGUI_ComputeOp::onApply() +bool SMESHGUI_BaseComputeOp::isValid( SUIT_Operation* theOp ) const { - return true; + SMESHGUI_BaseComputeOp* baseOp = dynamic_cast( theOp ); + bool ret = true; + if ( !myMesh->_is_nil() && baseOp ) { + SMESH::SMESH_Mesh_var aMesh = baseOp->getMesh(); + if ( !aMesh->_is_nil() && aMesh->GetId() == myMesh->GetId() ) ret = false; + } + return ret; } //================================================================================ @@ -1511,10 +1359,11 @@ LightApp_Dialog* SMESHGUI_ComputeOp::dlg() const SMESHGUI_PrecomputeOp::SMESHGUI_PrecomputeOp() : SMESHGUI_BaseComputeOp(), myDlg( 0 ), + myOrderMgr( 0 ), myActiveDlg( 0 ), myPreviewDisplayer( 0 ) { - myHelpFileName = "preview_meshes_page.html"; // V4 + myHelpFileName = "constructing_meshes_page.html#preview_mesh_anchor"; } //================================================================================ @@ -1527,6 +1376,8 @@ SMESHGUI_PrecomputeOp::~SMESHGUI_PrecomputeOp() { delete myDlg; myDlg = 0; + delete myOrderMgr; + myOrderMgr = 0; myActiveDlg = 0; if ( myPreviewDisplayer ) delete myPreviewDisplayer; @@ -1559,6 +1410,8 @@ void SMESHGUI_PrecomputeOp::startOperation() // connect signals connect( myDlg, SIGNAL( preview() ), this, SLOT( onPreview() ) ); + connect( myDlg, SIGNAL( dlgOk() ), this, SLOT( onCompute() ) ); + connect( myDlg, SIGNAL( dlgApply() ), this, SLOT( onCompute() ) ); } myActiveDlg = myDlg; @@ -1587,6 +1440,22 @@ void SMESHGUI_PrecomputeOp::startOperation() } SMESHGUI_BaseComputeOp::startOperation(); + if (myMesh->_is_nil()) + return; + + if (myDlg->getPreviewMode() == -1) + { + // nothing to preview + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_NOTHING_PREVIEW")); + onCancel(); + return; + } + + // disconnect slot from preview dialog to have Apply from results of compute operation only + disconnect( myDlg, SIGNAL( dlgOk() ), this, SLOT( onOk() ) ); + disconnect( myDlg, SIGNAL( dlgApply() ), this, SLOT( onApply() ) ); myDlg->show(); } @@ -1611,7 +1480,7 @@ void SMESHGUI_PrecomputeOp::stopOperation() //================================================================================ /*! - * \brief perform it's intention action: reinitialise dialog + * \brief reinitialize dialog after operaiton become active again */ //================================================================================ @@ -1622,16 +1491,57 @@ void SMESHGUI_PrecomputeOp::resumeOperation() SMESHGUI_BaseComputeOp::resumeOperation(); } +//================================================================================ +/*! + * \brief perform it's intention action: reinitialise dialog + */ +//================================================================================ + void SMESHGUI_PrecomputeOp::initDialog() { QList modes; + QMap modeMap; + _PTR(SObject) pMesh = studyDS()->FindObjectID( myIObject->getEntry() ); + getAssignedAlgos( pMesh, modeMap ); + if ( modeMap.contains( SMESH::DIM_3D ) ) + { + if ( modeMap.contains( SMESH::DIM_2D ) ) + modes.append( SMESH::DIM_2D ); + if ( modeMap.contains( SMESH::DIM_1D ) ) + modes.append( SMESH::DIM_1D ); + } + else if ( modeMap.contains( SMESH::DIM_2D ) ) + { + if ( modeMap.contains( SMESH::DIM_1D ) ) + modes.append( SMESH::DIM_1D ); + } + + myOrderMgr = new SMESHGUI_MeshOrderMgr( myDlg->getMeshOrderBox() ); + myOrderMgr->SetMesh( myMesh ); + bool isOrder = myOrderMgr->GetMeshOrder(myPrevOrder); + myDlg->getMeshOrderBox()->setShown(isOrder); + if ( !isOrder ) { + delete myOrderMgr; + myOrderMgr = 0; + } + + myDlg->setPreviewModes( modes ); +} + +//================================================================================ +/*! + * \brief detect asigned mesh algorithms + */ +//================================================================================ + +void SMESHGUI_PrecomputeOp::getAssignedAlgos(_PTR(SObject) theMesh, + QMap& theModeMap) +{ _PTR(SObject) aHypRoot; _PTR(GenericAttribute) anAttr; int aPart = SMESH::Tag_RefOnAppliedAlgorithms; - - _PTR(SObject) pMesh = studyDS()->FindObjectID( myIObject->getEntry() ); - if ( pMesh && pMesh->FindSubObject( aPart, aHypRoot ) ) + if ( theMesh && theMesh->FindSubObject( aPart, aHypRoot ) ) { _PTR(ChildIterator) anIter = SMESH::GetActiveStudyDocument()->NewChildIterator( aHypRoot ); @@ -1649,50 +1559,38 @@ void SMESHGUI_PrecomputeOp::initDialog() CORBA::Object_var aVar = _CAST(SObject,anObj)->GetObject(); if ( CORBA::is_nil( aVar ) ) continue; - - SMESH::SMESH_Algo_var algo = SMESH::SMESH_3D_Algo::_narrow( aVar ); - if ( !algo->_is_nil() ) - { - modeMap[ SMESH::DIM_1D ] = 0; - modeMap[ SMESH::DIM_2D ] = 0; - } - else + + for( int dim = SMESH::DIM_1D; dim <= SMESH::DIM_3D; dim++ ) { - algo = SMESH::SMESH_2D_Algo::_narrow( aVar ); + SMESH::SMESH_Algo_var algo; + switch(dim) { + case SMESH::DIM_1D: algo = SMESH::SMESH_1D_Algo::_narrow( aVar ); break; + case SMESH::DIM_2D: algo = SMESH::SMESH_2D_Algo::_narrow( aVar ); break; + case SMESH::DIM_3D: algo = SMESH::SMESH_3D_Algo::_narrow( aVar ); break; + default: break; + } if ( !algo->_is_nil() ) - modeMap[ SMESH::DIM_2D ] = 0; + theModeMap[ dim ] = 0; } } } } - if ( modeMap.contains( SMESH::DIM_1D ) ) - modes.append( SMESH::DIM_1D ); - if ( modeMap.contains( SMESH::DIM_2D ) ) - modes.append( SMESH::DIM_2D ); - - myDlg->setPreviewModes( modes ); } //================================================================================ /*! - * \brief perform it's intention action: + * \brief perform it's intention action: compute mesh */ //================================================================================ -bool SMESHGUI_PrecomputeOp::onApply() +void SMESHGUI_PrecomputeOp::onCompute() { - QObject* obj = sender(); - if ( obj != myDlg && myActiveDlg == myDlg ) - return true; // just return from error messages - if ( myActiveDlg == myDlg ) - { - myDlg->hide(); - myMapShapeId.clear(); - myActiveDlg = computeDlg(); - computeMesh(); - } - - return true; + myDlg->hide(); + if (myOrderMgr && myOrderMgr->IsOrderChanged()) + myOrderMgr->SetMeshOrder(); + myMapShapeId.clear(); + myActiveDlg = computeDlg(); + computeMesh(); } //================================================================================ @@ -1704,14 +1602,14 @@ bool SMESHGUI_PrecomputeOp::onApply() void SMESHGUI_PrecomputeOp::onCancel() { QObject* curDlg = sender(); - if ( curDlg == computeDlg() ) + if ( curDlg == computeDlg() && myActiveDlg == myDlg ) { - if ( myActiveDlg == myDlg ) // return from error messages - myDlg->show(); - + // return from error messages + myDlg->show(); return; } + bool isRestoreOrder = false; if ( myActiveDlg == myDlg && !myMesh->_is_nil() && myMapShapeId.count() ) { // ask to remove already computed mesh elements @@ -1723,8 +1621,24 @@ void SMESHGUI_PrecomputeOp::onCancel() QMap::const_iterator it = myMapShapeId.constBegin(); for ( ; it != myMapShapeId.constEnd(); ++it ) myMesh->ClearSubMesh( *it ); + isRestoreOrder = true; } } + + // return previous mesh order + if (myOrderMgr && myOrderMgr->IsOrderChanged()) { + if (!isRestoreOrder) + isRestoreOrder = + (SUIT_MessageBox::question( desktop(), tr( "SMESH_WARNING" ), + tr( "SMESH_REJECT_MESH_ORDER" ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 0, 1 ) == 0); + if (isRestoreOrder) + myOrderMgr->SetMeshOrder(myPrevOrder); + } + + delete myOrderMgr; + myOrderMgr = 0; + myMapShapeId.clear(); SMESHGUI_BaseComputeOp::onCancel(); } @@ -1743,6 +1657,11 @@ void SMESHGUI_PrecomputeOp::onPreview() _PTR(SObject) aMeshSObj = SMESH::FindSObject(myMesh); if ( !aMeshSObj ) return; + + // set modified submesh priority if any + if (myOrderMgr && myOrderMgr->IsOrderChanged()) + myOrderMgr->SetMeshOrder(); + // Compute preview of mesh, // i.e. compute mesh till indicated dimension int dim = myDlg->getPreviewMode(); @@ -1778,6 +1697,7 @@ void SMESHGUI_PrecomputeOp::onPreview() SMESH::MeshPreviewStruct_var previewData = gen->Precompute(myMesh, myMainShape, (SMESH::Dimension)dim, aShapesId); + SMESH::MeshPreviewStruct* previewRes = previewData._retn(); if ( previewRes && previewRes->nodesXYZ.length() > 0 ) { @@ -1785,7 +1705,7 @@ void SMESHGUI_PrecomputeOp::onPreview() myPreviewDisplayer->SetData( previewRes ); // append shape indeces with computed mesh entities for ( int i = 0, n = aShapesId->length(); i < n; i++ ) - myMapShapeId[ aShapesId[ i ] ] = 0; + myMapShapeId[ aShapesId[ i ] ] = 0; } else myPreviewDisplayer->SetVisibility(false); @@ -1847,7 +1767,8 @@ void SMESHGUI_PrecomputeOp::onPreview() //================================================================================ SMESHGUI_PrecomputeDlg::SMESHGUI_PrecomputeDlg( QWidget* parent ) - : SMESHGUI_Dialog( parent, false, false, OK | Cancel | Help ) + : SMESHGUI_Dialog( parent, false, false, OK | Cancel | Help ), + myOrderBox(0) { setWindowTitle( tr( "CAPTION" ) ); @@ -1856,6 +1777,9 @@ SMESHGUI_PrecomputeDlg::SMESHGUI_PrecomputeDlg( QWidget* parent ) QVBoxLayout* layout = new QVBoxLayout( main ); + myOrderBox = new SMESHGUI_MeshOrderBox( main ); + layout->addWidget(myOrderBox); + QFrame* frame = new QFrame( main ); layout->setMargin(0); layout->setSpacing(0); layout->addWidget( frame ); @@ -1911,3 +1835,292 @@ int SMESHGUI_PrecomputeDlg::getPreviewMode() const { return myPreviewMode->currentId(); } + +//================================================================================ +/*! + * \brief Returns current preview mesh mode +*/ +//================================================================================ + +SMESHGUI_MeshOrderBox* SMESHGUI_PrecomputeDlg::getMeshOrderBox() const +{ + return myOrderBox; +} + + +//================================================================================ +/*! + * \brief Constructor +*/ +//================================================================================ + +SMESHGUI_EvaluateOp::SMESHGUI_EvaluateOp() + : SMESHGUI_BaseComputeOp() +{ +} + + +//================================================================================ +/*! + * \brief Desctructor +*/ +//================================================================================ + +SMESHGUI_EvaluateOp::~SMESHGUI_EvaluateOp() +{ +} + +//================================================================================ +/*! + * \brief perform it's intention action: compute mesh + */ +//================================================================================ + +void SMESHGUI_EvaluateOp::startOperation() +{ + SMESHGUI_BaseComputeOp::evaluateDlg(); + SMESHGUI_BaseComputeOp::startOperation(); + if (myMesh->_is_nil()) + return; + evaluateMesh(); +} + +//================================================================================ +/*! + * \brief Gets dialog of this operation + * \retval LightApp_Dialog* - pointer to dialog of this operation + */ +//================================================================================ + +LightApp_Dialog* SMESHGUI_EvaluateOp::dlg() const +{ + return evaluateDlg(); +} + +//================================================================================ +/*! + * \brief evaluateMesh() +*/ +//================================================================================ + +void SMESHGUI_BaseComputeOp::evaluateMesh() +{ + // EVALUATE MESH + + SMESH::MemoryReserve aMemoryReserve; + + SMESH::compute_error_array_var aCompErrors; + QString aHypErrors; + + bool evaluateFailed = true, memoryLack = false; + SMESH::long_array_var aRes; + + _PTR(SObject) aMeshSObj = SMESH::FindSObject(myMesh); + if ( !aMeshSObj ) // IPAL21340 + return; + + bool hasShape = myMesh->HasShapeToMesh(); + bool shapeOK = myMainShape->_is_nil() ? !hasShape : hasShape; + if ( shapeOK ) + { + myCompDlg->myMeshName->setText( aMeshSObj->GetName().c_str() ); + SMESH::SMESH_Gen_var gen = getSMESHGUI()->GetSMESHGen(); + SMESH::algo_error_array_var errors = gen->GetAlgoState(myMesh,myMainShape); + if ( errors->length() > 0 ) { + aHypErrors = SMESH::GetMessageOnAlgoStateErrors( errors.in() ); + } + SUIT_OverrideCursor aWaitCursor; + try { +#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 + OCC_CATCH_SIGNALS; +#endif + aRes = gen->Evaluate(myMesh, myMainShape); + } + catch(const SALOME::SALOME_Exception & S_ex){ + memoryLack = true; + } + + try { +#if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 + OCC_CATCH_SIGNALS; +#endif + aCompErrors = gen->GetComputeErrors( myMesh, myMainShape ); + } + catch(const SALOME::SALOME_Exception & S_ex){ + memoryLack = true; + } + } + + if ( memoryLack ) + aMemoryReserve.release(); + + evaluateFailed = ( aCompErrors->length() > 0 ); + myCompDlg->setWindowTitle(tr( evaluateFailed ? "SMESH_WRN_EVALUATE_FAILED" : "SMESH_EVALUATE_SUCCEED")); + + // SHOW ERRORS + + bool noCompError = ( !aCompErrors.operator->() || aCompErrors->length() == 0 ); + bool noHypoError = ( aHypErrors.isEmpty() ); + + //SUIT_ResourceMgr* resMgr = SMESH::GetResourceMgr( SMESHGUI::GetSMESHGUI() ); + //int aNotifyMode = resMgr->integerValue( "SMESH", "show_result_notification" ); + + bool isShowResultDlg = true; + //if( noHypoError ) + //switch( aNotifyMode ) { + //case 0: // show the mesh computation result dialog NEVER + //isShowResultDlg = false; + //commit(); + //break; + //case 1: // show the mesh computation result dialog if there are some errors + //if ( memoryLack || !noHypoError ) + // isShowResultDlg = true; + //else + //{ + // isShowResultDlg = false; + // commit(); + //} + //break; + //default: // show the result dialog after each mesh computation + //isShowResultDlg = true; + //} + + // SHOW RESULTS + if ( isShowResultDlg ) + showEvaluateResult( aRes, memoryLack, noCompError, aCompErrors, + noHypoError, aHypErrors); +} + + +void SMESHGUI_BaseComputeOp::showEvaluateResult(const SMESH::long_array& theRes, + const bool theMemoryLack, + const bool theNoCompError, + SMESH::compute_error_array_var& theCompErrors, + const bool theNoHypoError, + const QString& theHypErrors) +{ + bool hasShape = myMesh->HasShapeToMesh(); + SMESHGUI_ComputeDlg* aCompDlg = evaluateDlg(); + aCompDlg->myMemoryLackGroup->hide(); + + if ( theMemoryLack ) + { + aCompDlg->myMemoryLackGroup->show(); + aCompDlg->myFullInfo->hide(); + aCompDlg->myBriefInfo->hide(); + aCompDlg->myHypErrorGroup->hide(); + aCompDlg->myCompErrorGroup->hide(); + } + else if ( theNoCompError && theNoHypoError ) + { + aCompDlg->myFullInfo->SetMeshInfo( theRes ); + aCompDlg->myFullInfo->show(); + aCompDlg->myBriefInfo->hide(); + aCompDlg->myHypErrorGroup->hide(); + aCompDlg->myCompErrorGroup->hide(); + } + else + { + QTableWidget* tbl = aCompDlg->myTable; + aCompDlg->myBriefInfo->SetMeshInfo( theRes ); + aCompDlg->myBriefInfo->show(); + aCompDlg->myFullInfo->hide(); + + if ( theNoHypoError ) { + aCompDlg->myHypErrorGroup->hide(); + } + else { + aCompDlg->myHypErrorGroup->show(); + aCompDlg->myHypErrorLabel->setText( theHypErrors ); + } + + if ( theNoCompError ) { + aCompDlg->myCompErrorGroup->hide(); + } + else { + aCompDlg->myCompErrorGroup->show(); + + aCompDlg->myPublishBtn->hide(); + aCompDlg->myShowBtn->hide(); + + // fill table of errors + tbl->setRowCount( theCompErrors->length() ); + if ( !hasShape ) tbl->hideColumn( COL_SHAPE ); + else tbl->showColumn( COL_SHAPE ); + tbl->setColumnWidth( COL_ERROR, 200 ); + + bool hasBadMesh = false; + for ( int row = 0; row < theCompErrors->length(); ++row ) + { + SMESH::ComputeError & err = theCompErrors[ row ]; + + QString text = err.algoName.in(); + if ( !tbl->item( row, COL_ALGO ) ) tbl->setItem( row, COL_ALGO, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_ALGO )->setText( text ); + + text = SMESH::errorText( err.code, err.comment.in() ); + if ( !tbl->item( row, COL_ERROR ) ) tbl->setItem( row, COL_ERROR, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_ERROR )->setText( text ); + + text = QString("%1").arg( err.subShapeID ); + if ( !tbl->item( row, COL_SHAPEID ) ) tbl->setItem( row, COL_SHAPEID, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_SHAPEID )->setText( text ); + + text = hasShape ? SMESH::shapeText( err.subShapeID, myMainShape ) : QString(""); + if ( !tbl->item( row, COL_SHAPE ) ) tbl->setItem( row, COL_SHAPE, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_SHAPE )->setText( text ); + + text = ( !hasShape || SMESH::getSubShapeSO( err.subShapeID, myMainShape )) ? "PUBLISHED" : ""; + if ( !tbl->item( row, COL_PUBLISHED ) ) tbl->setItem( row, COL_PUBLISHED, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_PUBLISHED )->setText( text ); // if text=="", "PUBLISH" button enabled + + text = err.hasBadMesh ? "hasBadMesh" : ""; + if ( !tbl->item( row, COL_BAD_MESH ) ) tbl->setItem( row, COL_BAD_MESH, new QTableWidgetItem( text ) ); + else tbl->item( row, COL_BAD_MESH )->setText( text ); + if ( err.hasBadMesh ) hasBadMesh = true; + + //tbl->item( row, COL_ERROR )->setWordWrap( true ); // VSR: TODO ??? + tbl->resizeRowToContents( row ); + } + tbl->resizeColumnToContents( COL_ALGO ); + tbl->resizeColumnToContents( COL_SHAPE ); + + if ( hasBadMesh ) + aCompDlg->myBadMeshBtn->show(); + else + aCompDlg->myBadMeshBtn->hide(); + + tbl->setCurrentCell(0,0); + currentCellChanged(); // to update buttons + } + } + // show dialog and wait, becase Compute can be invoked from Preview operation + //aCompDlg->exec(); // this way it becomes modal - impossible to rotate model in the Viewer + aCompDlg->show(); +} + + +//================================================================================ +/*! + * \brief Gets dialog of evaluate operation + * \retval SMESHGUI_ComputeDlg* - pointer to dialog of this operation + */ +//================================================================================ + +SMESHGUI_ComputeDlg* SMESHGUI_BaseComputeOp::evaluateDlg() const +{ + if ( !myCompDlg ) + { + SMESHGUI_BaseComputeOp* me = (SMESHGUI_BaseComputeOp*)this; + me->myCompDlg = new SMESHGUI_ComputeDlg( desktop(), true ); + // connect signals and slots + connect(myCompDlg->myShowBtn, SIGNAL (clicked()), SLOT(onPreviewShape())); + connect(myCompDlg->myPublishBtn, SIGNAL (clicked()), SLOT(onPublishShape())); + connect(myCompDlg->myBadMeshBtn, SIGNAL (clicked()), SLOT(onShowBadMesh())); + QTableWidget* aTable = me->table(); + connect(aTable, SIGNAL(itemSelectionChanged()), SLOT(currentCellChanged())); + connect(aTable, SIGNAL(currentCellChanged(int,int,int,int)), SLOT(currentCellChanged())); + } + return myCompDlg; +} + diff --git a/src/SMESHGUI/SMESHGUI_ComputeDlg.h b/src/SMESHGUI/SMESHGUI_ComputeDlg.h index 5ec969cc7..49aa2477c 100644 --- a/src/SMESHGUI/SMESHGUI_ComputeDlg.h +++ b/src/SMESHGUI/SMESHGUI_ComputeDlg.h @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_ComputeDlg.h // Author : Edward AGAPOV, Open CASCADE S.A.S. // @@ -36,8 +34,10 @@ // Qt includes #include +#include #include #include +#include // IDL includes #include @@ -50,11 +50,10 @@ class QTableWidget; class QLabel; class QtxComboBox; class SMESHGUI_ComputeDlg; +class SMESHGUI_MeshInfosBox; class SMESHGUI_PrecomputeDlg; class SMESHGUI_MeshEditPreview; -class SMESH::compute_error_array; - namespace SMESH { class TShapeDisplayer; @@ -71,6 +70,8 @@ public: SMESHGUI_BaseComputeOp(); virtual ~SMESHGUI_BaseComputeOp(); + SMESH::SMESH_Mesh_ptr getMesh(); + protected: virtual void startOperation(); virtual void stopOperation(); @@ -78,12 +79,23 @@ protected: SMESHGUI_ComputeDlg* computeDlg() const; void computeMesh(); void showComputeResult( const bool, - const bool, - SMESH::compute_error_array_var&, - const bool, - const QString& ); + const bool, + SMESH::compute_error_array_var&, + const bool, + const QString& ); + SMESHGUI_ComputeDlg* evaluateDlg() const; + void evaluateMesh(); + void showEvaluateResult(const SMESH::long_array& theRes, + const bool, + const bool, + SMESH::compute_error_array_var&, + const bool, + const QString&); + + virtual bool isValid( SUIT_Operation* theOp ) const; protected slots: + virtual bool onApply(); void onPreviewShape(); void onPublishShape(); void onShowBadMesh(); @@ -120,9 +132,10 @@ protected: virtual void startOperation(); protected slots: - virtual bool onApply(); }; +class SMESHGUI_MeshOrderMgr; + /*! * \brief Operation to preview and compute a mesh and show computation errors */ @@ -136,6 +149,12 @@ public: virtual LightApp_Dialog* dlg() const; + /** + * \brief returns map of assigned algorithms modes + */ + static void getAssignedAlgos(_PTR(SObject) theMesh, + QMap& theModeMap); + protected: virtual void startOperation(); virtual void stopOperation(); @@ -144,64 +163,42 @@ protected: virtual void initDialog(); protected slots: - virtual bool onApply(); virtual void onCancel(); private slots: void onPreview(); + void onCompute(); private: + //! private fields QMap< int, int > myMapShapeId; QPointer myActiveDlg; QPointer myDlg; SMESHGUI_MeshEditPreview* myPreviewDisplayer; + //! fields for mesh order + typedef QList TListOfInt; + typedef QList TListOfListOfInt; + TListOfListOfInt myPrevOrder; + SMESHGUI_MeshOrderMgr* myOrderMgr; }; /*! - * \brief Box showing mesh info + * \brief Operation to evaluate a mesh and show result */ - -class SMESHGUI_EXPORT SMESHGUI_MeshInfosBox : public QGroupBox +class SMESHGUI_EXPORT SMESHGUI_EvaluateOp: public SMESHGUI_BaseComputeOp { Q_OBJECT public: - SMESHGUI_MeshInfosBox( const bool, QWidget* ); + SMESHGUI_EvaluateOp(); + virtual ~SMESHGUI_EvaluateOp(); - void SetInfoByMesh( SMESH::SMESH_Mesh_var ); + virtual LightApp_Dialog* dlg() const; -private: - bool myFull; - QLabel* myNbNode; - QLabel* myNbEdge; - QLabel* myNbLinEdge; - QLabel* myNbQuadEdge; - QLabel* myNbTrai; - QLabel* myNbLinTrai; - QLabel* myNbQuadTrai; - QLabel* myNbQuad; - QLabel* myNbLinQuad; - QLabel* myNbQuadQuad; - QLabel* myNbFace; - QLabel* myNbLinFace; - QLabel* myNbQuadFace; - QLabel* myNbPolyg; - QLabel* myNbHexa; - QLabel* myNbLinHexa; - QLabel* myNbQuadHexa; - QLabel* myNbTetra; - QLabel* myNbLinTetra; - QLabel* myNbQuadTetra; - QLabel* myNbPyra; - QLabel* myNbLinPyra; - QLabel* myNbQuadPyra; - QLabel* myNbPrism; - QLabel* myNbLinPrism; - QLabel* myNbQuadPrism; - QLabel* myNbVolum; - QLabel* myNbLinVolum; - QLabel* myNbQuadVolum; - QLabel* myNbPolyh; +protected: + virtual void startOperation(); + +protected slots: }; /*! @@ -213,17 +210,18 @@ class SMESHGUI_EXPORT SMESHGUI_ComputeDlg : public SMESHGUI_Dialog Q_OBJECT public: - SMESHGUI_ComputeDlg( QWidget* ); + SMESHGUI_ComputeDlg( QWidget*, bool ); virtual ~SMESHGUI_ComputeDlg(); protected: - QFrame* createMainFrame( QWidget* ); + QFrame* createMainFrame( QWidget*, bool ); QLabel* myMeshName; QGroupBox* myMemoryLackGroup; QGroupBox* myCompErrorGroup; QGroupBox* myHypErrorGroup; QLabel* myHypErrorLabel; + QLabel* myWarningLabel; QTableWidget* myTable; QPushButton* myShowBtn; QPushButton* myPublishBtn; @@ -236,6 +234,8 @@ protected: friend class SMESHGUI_PrecomputeOp; }; +class SMESHGUI_MeshOrderBox; + /*! * \brief Dialog to preview and compute a mesh and show computation errors */ @@ -250,14 +250,69 @@ public: void setPreviewModes( const QList& ); int getPreviewMode() const; + + SMESHGUI_MeshOrderBox* getMeshOrderBox() const; signals: void preview(); private: + SMESHGUI_MeshOrderBox* myOrderBox; QPushButton* myPreviewBtn; QtxComboBox* myPreviewMode; }; +/*! + * \brief Thread to launch computation + */ + +class SMESHGUI_EXPORT SMESHGUI_ComputeDlg_QThread : public QThread +{ + Q_OBJECT + +public: + SMESHGUI_ComputeDlg_QThread(SMESH::SMESH_Gen_var gen, + SMESH::SMESH_Mesh_var mesh, + GEOM::GEOM_Object_var mainShape); + bool result(); + void cancel(); + +protected: + void run(); + +private: + SMESH::SMESH_Gen_var myGen; + SMESH::SMESH_Mesh_var myMesh; + GEOM::GEOM_Object_var myMainShape; + bool myResult; +}; + +/*! + * \brief Dialog to display Cancel button + */ + +class SMESHGUI_EXPORT SMESHGUI_ComputeDlg_QThreadQDialog : public QDialog +{ + Q_OBJECT + +public: + SMESHGUI_ComputeDlg_QThreadQDialog(QWidget *parent, + SMESH::SMESH_Gen_var gen, + SMESH::SMESH_Mesh_var mesh, + GEOM::GEOM_Object_var mainShape); + bool result(); + +protected: + void timerEvent(QTimerEvent *timer); + void closeEvent(QCloseEvent *event); + +private slots: + void onCancel(); + +private: + SMESHGUI_ComputeDlg_QThread qthread; + QPushButton *cancelButton; + +}; #endif // SMESHGUI_COMPUTEDLG_H diff --git a/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.cxx b/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.cxx index 5db422cdd..b42d728ae 100644 --- a/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ConvToQuadDlg.cxx // Author : Open CASCADE S.A.S. @@ -35,6 +36,7 @@ #include #include #include +#include #define SPACING 6 #define MARGIN 11 @@ -69,6 +71,8 @@ SMESHGUI_ConvToQuadDlg::SMESHGUI_ConvToQuadDlg() myBG->addButton(myRB2, 1); myRB1->setChecked( true ); + myWarning = new QLabel(QString("%1").arg(tr("NON_CONFORM_WARNING")), mainFrame()); + // Fill layout QGridLayout* aLay = new QGridLayout( mainFrame() ); aLay->setMargin( 5 ); @@ -79,6 +83,7 @@ SMESHGUI_ConvToQuadDlg::SMESHGUI_ConvToQuadDlg() aLay->addWidget( objectWg( 0, Control ), 0, 2 ); aLay->addWidget( myMedNdsOnGeom, 1, 0, 1, 3 ); aLay->addWidget( myBGBox, 2, 0, 1, 3 ); + aLay->addWidget( myWarning, 3, 0, 1, 3 ); connect(myBG, SIGNAL( buttonClicked( int ) ), this, SIGNAL( onClicked( int ) ) ); } @@ -112,11 +117,26 @@ int SMESHGUI_ConvToQuadDlg::CurrentRB( ) return myBG->checkedId(); } +void SMESHGUI_ConvToQuadDlg::ShowWarning(bool toShow) +{ + if ( toShow ) + myWarning->show(); + else + myWarning->hide(); +} + +bool SMESHGUI_ConvToQuadDlg::isWarningShown() +{ + return myWarning->isVisible(); +} + void SMESHGUI_ConvToQuadDlg::SetEnabledControls( const bool theCheck ) { - myBGBox->setEnabled( theCheck ); + //myBGBox->setEnabled( theCheck ); + myRB1->setEnabled( theCheck ); + myRB2->setEnabled( theCheck ); myMedNdsOnGeom->setEnabled( theCheck ); - setButtonEnabled( theCheck, QtxDialog::OK | QtxDialog::Apply ); + //setButtonEnabled( theCheck, QtxDialog::OK | QtxDialog::Apply ); } void SMESHGUI_ConvToQuadDlg::SetEnabledRB( const int idx, const bool theCheck ) diff --git a/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.h b/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.h index 53240eae6..660dbc731 100644 --- a/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.h +++ b/src/SMESHGUI/SMESHGUI_ConvToQuadDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ConvToQuadDlg.h // Author : Open CASCADE S.A.S. @@ -35,6 +36,7 @@ class QCheckBox; class QRadioButton; class QButtonGroup; class QGroupBox; +class QLabel; class SMESHGUI_EXPORT SMESHGUI_ConvToQuadDlg : public SMESHGUI_Dialog { @@ -52,6 +54,8 @@ public: void SetEnabledControls( const bool ); void SetEnabledRB( const int, const bool ); int CurrentRB(); //returns the ID of the selected toggle button + void ShowWarning(bool); + bool isWarningShown(); signals: void onClicked( int ); @@ -62,6 +66,7 @@ private: QButtonGroup* myBG; QRadioButton* myRB1; QRadioButton* myRB2; + QLabel* myWarning; }; #endif // SMESHGUI_CONVTOQUADDLG_H diff --git a/src/SMESHGUI/SMESHGUI_ConvToQuadOp.cxx b/src/SMESHGUI/SMESHGUI_ConvToQuadOp.cxx index 962b11abd..a62d0b949 100644 --- a/src/SMESHGUI/SMESHGUI_ConvToQuadOp.cxx +++ b/src/SMESHGUI/SMESHGUI_ConvToQuadOp.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ConvToQuadOp.cxx // Author : Open CASCADE S.A.S. @@ -26,15 +27,18 @@ // #include "SMESHGUI_ConvToQuadOp.h" +#include "SMESHGUI.h" #include "SMESHGUI_ConvToQuadDlg.h" #include "SMESHGUI_Utils.h" +#include "SMDSAbs_ElementType.hxx" -#include +#include "SMESH_TypeFilter.hxx" // SALOME GUI includes -#include -#include #include +#include +#include +#include // IDL includes #include @@ -97,6 +101,7 @@ void SMESHGUI_ConvToQuadOp::startOperation() myDlg->SetMediumNdsOnGeom( false ); myDlg->activateObject( 0 ); + myDlg->ShowWarning( false ); myDlg->show(); selectionDone(); @@ -117,22 +122,26 @@ void SMESHGUI_ConvToQuadOp::selectionDone() SMESHGUI_SelectionOp::selectionDone(); try { - QString anMeshEntry = myDlg->selectedObject( 0 ); - _PTR(SObject) pMesh = studyDS()->FindObjectID( anMeshEntry.toLatin1().data() ); - if ( !pMesh ) return; + QString anObjEntry = myDlg->selectedObject( 0 ); + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + if ( !pObj ) return; - SMESH::SMESH_Mesh_var mesh = - SMESH::SObjectToInterface( pMesh ); + SMESH::SMESH_IDSource_var idSource = + SMESH::SObjectToInterface( pObj ); - if( mesh->_is_nil() ) + myDlg->setButtonEnabled( true, QtxDialog::OK | QtxDialog::Apply ); + if( idSource->_is_nil() ) { myDlg->SetEnabledControls( false ); + myDlg->setButtonEnabled( false, QtxDialog::OK | QtxDialog::Apply ); + return; } - else if( ConsistMesh( mesh ) == SMESHGUI_ConvToQuadOp::Quadratic ) + MeshType meshType = ConsistMesh( idSource ); + if( meshType == SMESHGUI_ConvToQuadOp::Quadratic ) { myDlg->SetEnabledRB( 0, false ); } - else if( ConsistMesh( mesh ) == SMESHGUI_ConvToQuadOp::Linear ) + else if( meshType == SMESHGUI_ConvToQuadOp::Linear ) { myDlg->SetEnabledRB( 1, false ); } @@ -140,6 +149,22 @@ void SMESHGUI_ConvToQuadOp::selectionDone() { myDlg->SetEnabledControls( true ); } + + // show warning on non-conformal result mesh + if ( ! idSource->_is_nil() ) + { + SMESH::SMESH_subMesh_var subMesh = + SMESH::SObjectToInterface( pObj ); + bool toShow = false; + if ( !subMesh->_is_nil() ) + { + SMESH::SMESH_Mesh_var mesh = idSource->GetMesh(); + idSource = SMESH::SMESH_IDSource::_narrow( mesh ); + MeshType fullMeshType = ConsistMesh( idSource ); + toShow = ( fullMeshType != Comp ); + } + myDlg->ShowWarning( toShow ); + } } catch ( const SALOME::SALOME_Exception& S_ex ) { @@ -162,7 +187,7 @@ void SMESHGUI_ConvToQuadOp::selectionDone() SUIT_SelectionFilter* SMESHGUI_ConvToQuadOp::createFilter( const int theId ) const { if ( theId == 0 ) - return new SMESH_TypeFilter( MESH ); + return new SMESH_TypeFilter( MESHorSUBMESH ); else return 0; } @@ -176,48 +201,57 @@ SUIT_SelectionFilter* SMESHGUI_ConvToQuadOp::createFilter( const int theId ) con //================================================================================ bool SMESHGUI_ConvToQuadOp::onApply() { + SUIT_OverrideCursor aWaitCursor; QString aMess; - QString anMeshEntry = myDlg->selectedObject( 0 ); - _PTR(SObject) pMesh = studyDS()->FindObjectID( anMeshEntry.toLatin1().data() ); - if ( !pMesh ) + QString anObjEntry = myDlg->selectedObject( 0 ); + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + if ( !pObj ) { dlg()->show(); SUIT_MessageBox::warning( myDlg, - tr( "SMESH_WRN_WARNING" ), tr("MESH_IS_NOT_SELECTED") ); - + tr( "SMESH_WRN_WARNING" ), tr("MESH_IS_NOT_SELECTED") ); return false; } - SMESH::SMESH_Mesh_var mesh = - SMESH::SObjectToInterface( pMesh ); + SMESH::SMESH_Mesh_var mesh; + SMESH::SMESH_IDSource_var idSource = + SMESH::SObjectToInterface( pObj ); + if( !CORBA::is_nil(idSource) ) + mesh = idSource->GetMesh(); if( CORBA::is_nil(mesh) ) { SUIT_MessageBox::warning( myDlg, - tr( "SMESH_WRN_WARNING" ), tr("REF_IS_NULL") ); - + tr( "SMESH_WRN_WARNING" ), tr("REF_IS_NULL") ); return false; - } + } bool aResult = false; try { SMESH::SMESH_MeshEditor_var aEditor = mesh->GetMeshEditor(); + aResult = true; + SMESH::SMESH_Mesh_var sourceMesh = SMESH::SObjectToInterface( pObj ); if( !myDlg->CurrentRB() ) { bool aParam = true; if( myDlg->IsEnabledCheck() ) - aParam = myDlg->IsMediumNdsOnGeom(); + aParam = myDlg->IsMediumNdsOnGeom(); - aEditor->ConvertToQuadratic( aParam ); - aResult = true; + if ( sourceMesh->_is_nil() ) + aEditor->ConvertToQuadraticObject( aParam, idSource ); + else + aEditor->ConvertToQuadratic( aParam ); } else { - aResult = aEditor->ConvertFromQuadratic(); + if ( sourceMesh->_is_nil() ) + aEditor->ConvertFromQuadraticObject( idSource ); + else + aEditor->ConvertFromQuadratic(); } } catch ( const SALOME::SALOME_Exception& S_ex ) @@ -231,6 +265,7 @@ bool SMESHGUI_ConvToQuadOp::onApply() } if( aResult ) { + SMESHGUI::Modified(); update( UF_ObjBrowser | UF_Model | UF_Viewer ); selectionDone(); } @@ -242,42 +277,43 @@ bool SMESHGUI_ConvToQuadOp::onApply() * Determines, what elements this mesh contains. */ //================================================================================ -SMESHGUI_ConvToQuadOp::MeshType SMESHGUI_ConvToQuadOp::ConsistMesh( const SMESH::SMESH_Mesh_var& mesh) const +SMESHGUI_ConvToQuadOp::MeshType SMESHGUI_ConvToQuadOp::ConsistMesh( const SMESH::SMESH_IDSource_var& idSource) const { - int nbAllElem = 0, nbQEdges =0, nbQFaces =0, nbQVolum = 0; - int nbEdges = 0, nbFaces = 0, nbVolum = 0; - - nbAllElem = (int)mesh->NbElements(); - nbQEdges = (int)mesh->NbEdgesOfOrder(SMESH::ORDER_QUADRATIC); - nbQFaces = (int)mesh->NbFacesOfOrder(SMESH::ORDER_QUADRATIC); - nbQVolum = (int)mesh->NbVolumesOfOrder(SMESH::ORDER_QUADRATIC); - - nbEdges = (int)mesh->NbEdgesOfOrder(SMESH::ORDER_LINEAR); - nbFaces = (int)mesh->NbFacesOfOrder(SMESH::ORDER_LINEAR); - nbVolum = (int)mesh->NbVolumesOfOrder(SMESH::ORDER_LINEAR); - - if( nbAllElem == (nbQEdges+nbQFaces+nbQVolum) ) - return SMESHGUI_ConvToQuadOp::Quadratic; - else if ( nbAllElem == (nbEdges+nbFaces+nbVolum) ) - return SMESHGUI_ConvToQuadOp::Linear; - else - return SMESHGUI_ConvToQuadOp::Comp; + SMESH::long_array_var nbElemOfType = idSource->GetMeshInfo(); + bool hasQuad = ( nbElemOfType[SMDSEntity_Quad_Edge ] || + nbElemOfType[SMDSEntity_Quad_Triangle ] || + nbElemOfType[SMDSEntity_Quad_Quadrangle] || + nbElemOfType[SMDSEntity_Quad_Tetra ] || + nbElemOfType[SMDSEntity_Quad_Hexa ] || + nbElemOfType[SMDSEntity_Quad_Pyramid ] || + nbElemOfType[SMDSEntity_Quad_Penta ] ); + + bool hasLin = ( nbElemOfType[SMDSEntity_Edge ] || + nbElemOfType[SMDSEntity_Triangle ] || + nbElemOfType[SMDSEntity_Quadrangle] || + nbElemOfType[SMDSEntity_Tetra ] || + nbElemOfType[SMDSEntity_Hexa ] || + nbElemOfType[SMDSEntity_Pyramid ] || + nbElemOfType[SMDSEntity_Penta ] ); + + if ( hasQuad && hasLin ) + return Comp; + return hasQuad ? Quadratic : Linear; } - void SMESHGUI_ConvToQuadOp::ConnectRadioButtons( int id ) { - QString anMeshEntry = myDlg->selectedObject( 0 ); - _PTR(SObject) pMesh = studyDS()->FindObjectID( anMeshEntry.toLatin1().data() ); - if ( !pMesh ) return; + QString anObjEntry = myDlg->selectedObject( 0 ); + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + if ( !pObj ) return; - SMESH::SMESH_Mesh_var mesh = - SMESH::SObjectToInterface( pMesh ); + SMESH::SMESH_IDSource_var idSource = + SMESH::SObjectToInterface( pObj ); + SMESH::SMESH_Mesh_var mesh = idSource->GetMesh(); - GEOM::GEOM_Object_var mainGeom; - mainGeom = mesh->GetShapeToMesh(); + bool hasGeom = mesh->HasShapeToMesh(); - if( id || mainGeom->_is_nil() ) + if( id || !hasGeom ) myDlg->SetEnabledCheck( false ); else myDlg->SetEnabledCheck( true ); diff --git a/src/SMESHGUI/SMESHGUI_ConvToQuadOp.h b/src/SMESHGUI/SMESHGUI_ConvToQuadOp.h index ad60ea5ea..9e2cbb129 100644 --- a/src/SMESHGUI/SMESHGUI_ConvToQuadOp.h +++ b/src/SMESHGUI/SMESHGUI_ConvToQuadOp.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ConvToQuadOp.h // Author : Open CASCADE S.A.S. @@ -54,7 +55,7 @@ protected: virtual void startOperation(); virtual void selectionDone(); virtual SUIT_SelectionFilter* createFilter( const int ) const; - MeshType ConsistMesh( const SMESH::SMESH_Mesh_var& ) const; + MeshType ConsistMesh( const SMESH::SMESH_IDSource_var& ) const; protected slots: virtual bool onApply(); diff --git a/src/SMESHGUI/SMESHGUI_CopyMeshDlg.cxx b/src/SMESHGUI/SMESHGUI_CopyMeshDlg.cxx new file mode 100644 index 000000000..7c3d1d62c --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_CopyMeshDlg.cxx @@ -0,0 +1,697 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_CopyMeshDlg.cxx + +#include "SMESHGUI_CopyMeshDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_SpinBox.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_FilterDlg.h" + +#include +#include +#include + +// SALOME GUI includes +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +// SALOME KERNEL includes +#include + +// OCCT includes +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) + +/*! + \class BusyLocker + \brief Simple 'busy state' flag locker. + \internal +*/ + +namespace +{ + class BusyLocker + { + public: + //! Constructor. Sets passed boolean flag to \c true. + BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; } + //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false. + ~BusyLocker() { myBusy = false; } + private: + bool& myBusy; //! External 'busy state' boolean flag + }; +} + +#define SPACING 6 +#define MARGIN 11 + +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + + +//================================================================================ +/*! + * \brief Constructor + */ +//================================================================================ + +SMESHGUI_CopyMeshDlg::SMESHGUI_CopyMeshDlg( SMESHGUI* theModule ) + : QDialog( SMESH::GetDesktop( theModule ) ), + mySMESHGUI( theModule ), + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myFilterDlg(0), + mySelectedObject(SMESH::SMESH_IDSource::_nil()), + myIsApplyAndClose( false ) +{ + QPixmap image (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_COPY_MESH"))); + + setModal(false); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(tr("SMESH_COPY_MESH_TITLE")); + setSizeGripEnabled(true); + + QVBoxLayout* SMESHGUI_CopyMeshDlgLayout = new QVBoxLayout(this); + SMESHGUI_CopyMeshDlgLayout->setSpacing(SPACING); + SMESHGUI_CopyMeshDlgLayout->setMargin(MARGIN); + + /***************************************************************/ + ConstructorsBox = new QGroupBox(tr("SMESH_COPY_MESH_TITLE"), this); + QButtonGroup* GroupConstructors = new QButtonGroup(this); + QHBoxLayout* ConstructorsBoxLayout = new QHBoxLayout(ConstructorsBox); + ConstructorsBoxLayout->setSpacing(SPACING); + ConstructorsBoxLayout->setMargin(MARGIN); + + QRadioButton* RadioButton1= new QRadioButton(ConstructorsBox); + RadioButton1->setIcon(image); + GroupConstructors->addButton(RadioButton1, 0); + + ConstructorsBoxLayout->addWidget(RadioButton1); + RadioButton1->setChecked(true); + GroupConstructors->addButton(RadioButton1, 0); + + /***************************************************************/ + GroupArguments = new QGroupBox(tr("SMESH_ARGUMENTS"), this); + QGridLayout* GroupArgumentsLayout = new QGridLayout(GroupArguments); + GroupArgumentsLayout->setSpacing(SPACING); + GroupArgumentsLayout->setMargin(MARGIN); + + myIdValidator = new SMESHGUI_IdValidator(this); + + // Controls for idSource/elements selection + myTextLabelElements = new QLabel(tr("OBJECT_NAME"), GroupArguments); + myLineEditElements = new QLineEdit(GroupArguments); + myLineEditElements->setValidator(myIdValidator); + myLineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + + // Control for the mesh objects selection + myIdSourceCheck = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); + + // Name of a mesh to create + QLabel* meshNameLabel = new QLabel(tr("NEW_NAME"), GroupArguments); + myMeshNameEdit = new QLineEdit(GroupArguments); + + // CheckBox for copying groups + myCopyGroupsCheck = new QCheckBox(tr("SMESH_MAKE_GROUPS"), GroupArguments); + myCopyGroupsCheck->setChecked(false); + + // CheckBox for keeping ids + myKeepIdsCheck = new QCheckBox(tr("SMESH_KEEP_IDS"), GroupArguments); + myKeepIdsCheck->setChecked(true); + + // layout + GroupArgumentsLayout->addWidget(myTextLabelElements, 0, 0); + GroupArgumentsLayout->addWidget(myLineEditElements, 0, 1, 1, 5); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 6); + GroupArgumentsLayout->addWidget(myIdSourceCheck, 1, 0, 1, 6); + GroupArgumentsLayout->addWidget(meshNameLabel, 2, 0); + GroupArgumentsLayout->addWidget(myMeshNameEdit, 2, 1, 1, 5); + GroupArgumentsLayout->addWidget(myCopyGroupsCheck, 3, 0, 1, 6); + GroupArgumentsLayout->addWidget(myKeepIdsCheck, 4, 0, 1, 6); + + /***************************************************************/ + GroupButtons = new QGroupBox(this); + QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons); + GroupButtonsLayout->setSpacing(SPACING); + GroupButtonsLayout->setMargin(MARGIN); + + buttonOk = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), GroupButtons); + buttonOk->setAutoDefault(true); + buttonOk->setDefault(true); + buttonApply = new QPushButton(tr("SMESH_BUT_APPLY"), GroupButtons); + buttonApply->setAutoDefault(true); + buttonCancel = new QPushButton(tr("SMESH_BUT_CLOSE"), GroupButtons); + buttonCancel->setAutoDefault(true); + buttonHelp = new QPushButton(tr("SMESH_BUT_HELP"), GroupButtons); + buttonHelp->setAutoDefault(true); + + GroupButtonsLayout->addWidget(buttonOk); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addWidget(buttonApply); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addStretch(); + GroupButtonsLayout->addWidget(buttonCancel); + GroupButtonsLayout->addWidget(buttonHelp); + + /***************************************************************/ + SMESHGUI_CopyMeshDlgLayout->addWidget(ConstructorsBox); + SMESHGUI_CopyMeshDlgLayout->addWidget(GroupArguments); + SMESHGUI_CopyMeshDlgLayout->addWidget(GroupButtons); + + /* Initialisations */ + mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); + + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + // Selection filter + myIdSourceFilter = new SMESH_TypeFilter( IDSOURCE ); + + myHelpFileName = "copy_mesh_page.html"; + + Init(); + + /* signals and slots connections */ + connect(buttonOk, SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(ClickOnCancel())); + connect(buttonApply, SIGNAL(clicked()), this, SLOT(ClickOnApply())); + connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); + + connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), + this, SLOT (DeactivateActiveDialog())); + connect(mySelectionMgr, SIGNAL (currentSelectionChanged()), + this, SLOT (SelectionIntoArgument())); + connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()),/* to close dialog if study change */ + this, SLOT (ClickOnCancel())); + + connect(myLineEditElements, SIGNAL(textChanged(const QString&)), + this, SLOT (onTextChange(const QString&))); + connect(myIdSourceCheck, SIGNAL(toggled(bool)), + this, SLOT (onSelectIdSource(bool))); + + SelectionIntoArgument(); +} + +//================================================================================= +// function : ~SMESHGUI_CopyMeshDlg() +// purpose : Destroys the object and frees any allocated resources +//================================================================================= + +SMESHGUI_CopyMeshDlg::~SMESHGUI_CopyMeshDlg() +{ + if ( myFilterDlg ) + { + myFilterDlg->setParent( 0 ); + delete myFilterDlg; myFilterDlg = 0; + } + if ( myIdSourceFilter ) + { + if ( mySelectionMgr ) + mySelectionMgr->removeFilter( myIdSourceFilter ); + delete myIdSourceFilter; myIdSourceFilter=0; + } +} + +//================================================================================= +// function : Init() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::Init (bool ResetControls) +{ + myBusy = false; + + myMeshNameEdit->setText( SMESH::UniqueMeshName("Mesh")); + if ( ResetControls ) + { + myLineEditElements->clear(); + //myElementsId = ""; + myNbOkElements = 0; + + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + + myActor = 0; + myMesh = SMESH::SMESH_Mesh::_nil(); + + myIdSourceCheck->setChecked(true); + myCopyGroupsCheck->setChecked(false); + myKeepIdsCheck->setChecked(false); + + onSelectIdSource( myIdSourceCheck->isChecked() ); + } +} + +//================================================================================= +// function : ClickOnApply() +// purpose : +//================================================================================= + +bool SMESHGUI_CopyMeshDlg::ClickOnApply() +{ + if (mySMESHGUI->isActiveStudyLocked()) + return false; + + if( !isValid() ) + return false; + + QStringList anEntryList; + try + { + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_IDSource_var aPartToCopy; + if ( myIdSourceCheck->isChecked()) + { + aPartToCopy = mySelectedObject; + } + else + { + QStringList aListElementsId = myLineEditElements->text().split(" ", QString::SkipEmptyParts); + SMESH::long_array_var anElementsId = new SMESH::long_array; + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + aPartToCopy = aMeshEditor->MakeIDSource( anElementsId, SMESH::ALL ); + } + QByteArray meshName = myMeshNameEdit->text().toLatin1(); + bool toCopyGroups = ( myCopyGroupsCheck->isChecked() ); + bool toKeepIDs = ( myKeepIdsCheck->isChecked() ); + + SMESH::SMESH_Gen_var gen = SMESHGUI::GetSMESHGen(); + SMESH::SMESH_Mesh_var newMesh = + gen->CopyMesh(aPartToCopy, meshName.constData(), toCopyGroups, toKeepIDs); + if( !newMesh->_is_nil() ) + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( newMesh ) ) + anEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + newMesh->UnRegister(); +#endif + } catch (...) { + } + + mySMESHGUI->updateObjBrowser(true); + SMESHGUI::Modified(); + + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + + Init(false); + mySelectedObject = SMESH::SMESH_IDSource::_nil(); + SelectionIntoArgument(); + + return true; +} + +//================================================================================= +// function : ClickOnOk() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::ClickOnOk() +{ + setIsApplyAndClose( true ); + if( ClickOnApply() ) + ClickOnCancel(); +} + +//================================================================================= +// function : ClickOnCancel() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::ClickOnCancel() +{ + disconnect(mySelectionMgr, 0, this, 0); + if ( mySelectionMgr ) + mySelectionMgr->removeFilter( myIdSourceFilter ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + mySMESHGUI->ResetState(); + reject(); +} + +//================================================================================= +// function : ClickOnHelp() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::ClickOnHelp() +{ + LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + else { + QString platform; +#ifdef WIN32 + platform = "winapplication"; +#else + platform = "application"; +#endif + SUIT_MessageBox::warning(this, tr("WRN_WARNING"), + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); + } +} + +//======================================================================= +// function : onTextChange() +// purpose : +//======================================================================= + +void SMESHGUI_CopyMeshDlg::onTextChange (const QString& theNewText) +{ + QLineEdit* send = (QLineEdit*)sender(); + + if (myBusy) return; + BusyLocker lock( myBusy ); + + //if (send == myLineEditElements) + myNbOkElements = 0; + + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + + // hilight entered elements + SMDS_Mesh* aMesh = 0; + if (myActor) + aMesh = myActor->GetObject()->GetMesh(); + + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); + if (myActor && aMesh) + { + TColStd_MapOfInteger newIndices; + if (send == myLineEditElements) { + for (int i = 0; i < aListId.count(); i++) + if ( const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt())) + { + newIndices.Add(e->GetID()); + } + } + myNbOkElements = newIndices.Extent(); + + Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); + mySelector->AddOrRemoveIndex( anIO, newIndices, false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( anIO, true, true ); + } + else + { + myNbOkElements = aListId.count(); + } + + if (myNbOkElements) { + buttonOk->setEnabled(true); + buttonApply->setEnabled(true); + } +} + +//================================================================================= +// function : SelectionIntoArgument() +// purpose : Called when selection as changed or other case +//================================================================================= + +void SMESHGUI_CopyMeshDlg::SelectionIntoArgument() +{ + if (myBusy) return; + BusyLocker lock( myBusy ); + + // clear + myActor = 0; + QString aString = ""; + + myLineEditElements->setText(aString); + myNbOkElements = 0; + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + myFilterBtn->setEnabled(false); + + // get selected mesh + SALOME_ListIO aList; + mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); + int nbSel = aList.Extent(); + if (nbSel != 1) + return; + + Handle(SALOME_InteractiveObject) IO = aList.First(); + mySelectedObject = SMESH::IObjectToInterface( IO ); + if ( mySelectedObject->_is_nil() ) + return; + + myMesh = SMESH::GetMeshByIO(IO); + if (myMesh->_is_nil()) + return; + + myActor = SMESH::FindActorByEntry(IO->getEntry()); + if (!myActor) + myActor = SMESH::FindActorByObject(myMesh); + + if (myIdSourceCheck->isChecked()) + { + SMESH::GetNameOfSelectedIObjects( mySelectionMgr, aString ); + if ( aString.isEmpty() ) aString = " "; + else aString = aString.trimmed(); // issue 0021327 + } + else + { + SMESH::GetNameOfSelectedElements( mySelector, IO, aString ); + myNbOkElements = aString.size(); + myFilterBtn->setEnabled(true); + } + myLineEditElements->setText( aString ); + bool ok = !aString.isEmpty(); + + buttonOk->setEnabled(ok); + buttonApply->setEnabled(ok); +} + +//======================================================================= +//function : onSelectIdSource +//purpose : +//======================================================================= +void SMESHGUI_CopyMeshDlg::onSelectIdSource (bool toSelectMesh) +{ + if (toSelectMesh) + myTextLabelElements->setText(tr("OBJECT_NAME")); + else + myTextLabelElements->setText(tr("ELEM_IDS")); + + if (toSelectMesh) { + myLineEditElements->clear(); + } + + mySelectionMgr->clearFilters(); + mySelectionMgr->installFilter(myIdSourceFilter); + SMESH::SetPointRepresentation(false); + + if (toSelectMesh) { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + myLineEditElements->setReadOnly(true); + myLineEditElements->setValidator(0); + } + else + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + myLineEditElements->setReadOnly(false); + myLineEditElements->setValidator(myIdValidator); + onTextChange(myLineEditElements->text()); + } + + SelectionIntoArgument(); +} + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= + +bool SMESHGUI_CopyMeshDlg::isValid() +{ + if ( myIdSourceCheck->isChecked() ) + return !mySelectedObject->_is_nil(); + + return myNbOkElements > 0; +} + +//================================================================================= +// function : DeactivateActiveDialog() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::DeactivateActiveDialog() +{ + if (ConstructorsBox->isEnabled()) { + ConstructorsBox->setEnabled(false); + GroupArguments->setEnabled(false); + GroupButtons->setEnabled(false); + mySMESHGUI->ResetState(); + mySMESHGUI->SetActiveDialogBox(0); + if ( mySelectionMgr ) + mySelectionMgr->removeFilter( myIdSourceFilter ); + } +} + +//================================================================================= +// function : ActivateThisDialog() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::ActivateThisDialog() +{ + /* Emit a signal to deactivate the active dialog */ + mySMESHGUI->EmitSignalDeactivateDialog(); + ConstructorsBox->setEnabled(true); + GroupArguments->setEnabled(true); + GroupButtons->setEnabled(true); + + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + onSelectIdSource( myIdSourceCheck->isChecked() ); + + SelectionIntoArgument(); +} + +//================================================================================= +// function : enterEvent() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::enterEvent (QEvent*) +{ + if (!ConstructorsBox->isEnabled()) + ActivateThisDialog(); +} + +//================================================================================= +// function : closeEvent() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::closeEvent (QCloseEvent*) +{ + /* same than click on cancel button */ + ClickOnCancel(); +} + +//======================================================================= +//function : hideEvent +//purpose : caused by ESC key +//======================================================================= +void SMESHGUI_CopyMeshDlg::hideEvent (QHideEvent*) +{ + if (!isMinimized()) + ClickOnCancel(); +} + +//================================================================================= +// function : keyPressEvent() +// purpose : +//================================================================================= +void SMESHGUI_CopyMeshDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( e->isAccepted() ) + return; + + if ( e->key() == Qt::Key_F1 ) { + e->accept(); + ClickOnHelp(); + } +} + +//================================================================================= +// function : setFilters() +// purpose : SLOT. Called when "Filter" button pressed. +//================================================================================= +void SMESHGUI_CopyMeshDlg::setFilters() +{ + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } + if ( !myFilterDlg ) + myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); + + myFilterDlg->SetSelection(); + myFilterDlg->SetMesh( myMesh ); + myFilterDlg->SetSourceWg( myLineEditElements ); + + myFilterDlg->show(); +} + +//================================================================ +// function : setIsApplyAndClose +// Purpose : Set value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +void SMESHGUI_CopyMeshDlg::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +//================================================================ +// function : isApplyAndClose +// Purpose : Get value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +bool SMESHGUI_CopyMeshDlg::isApplyAndClose() const +{ + return myIsApplyAndClose; +} diff --git a/src/SMESHGUI/SMESHGUI_CopyMeshDlg.h b/src/SMESHGUI/SMESHGUI_CopyMeshDlg.h new file mode 100644 index 000000000..b0c3b9338 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_CopyMeshDlg.h @@ -0,0 +1,130 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_CopyMeshDlg.h +// +#ifndef SMESHGUI_CopyMeshDLG_H +#define SMESHGUI_CopyMeshDLG_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +// Qt includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QCheckBox; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; + +class SMESHGUI; +class SMESHGUI_IdValidator; +class SMESHGUI_FilterDlg; +class SMESH_Actor; +class SVTK_Selector; +class LightApp_SelectionMgr; +class SUIT_SelectionFilter; + +//================================================================================= +// class : SMESHGUI_CopyMeshDlg +// purpose : copy some elements or a mesh object into a new mesh +//================================================================================= + +class SMESHGUI_EXPORT SMESHGUI_CopyMeshDlg : public QDialog +{ + Q_OBJECT + +public: + SMESHGUI_CopyMeshDlg( SMESHGUI* ); + ~SMESHGUI_CopyMeshDlg(); + +private: + void Init( bool = true ); + void closeEvent( QCloseEvent* ); + void enterEvent( QEvent* ); /* mouse enter the QWidget */ + void hideEvent( QHideEvent* ); /* ESC key */ + void keyPressEvent( QKeyEvent* ); + int GetConstructorId(); + void setNewMeshName(); + + bool isValid(); + + void setIsApplyAndClose( const bool theFlag ); + bool isApplyAndClose() const; + + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ + SMESHGUI_IdValidator* myIdValidator; + LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ + int myNbOkElements; /* to check when elements are defined */ + + SVTK_Selector* mySelector; + + bool myBusy; + SMESH::SMESH_Mesh_var myMesh; + SMESH_Actor* myActor; + SUIT_SelectionFilter* myIdSourceFilter; + + SMESH::SMESH_IDSource_var mySelectedObject; + + QGroupBox* ConstructorsBox; + QGroupBox* GroupArguments; + QGroupBox* GroupButtons; + + QPushButton* buttonOk; + QPushButton* buttonCancel; + QPushButton* buttonApply; + QPushButton* buttonHelp; + + QLabel* myTextLabelElements; + QLineEdit* myLineEditElements; + QLineEdit* myMeshNameEdit; + QCheckBox* myIdSourceCheck; + QCheckBox* myCopyGroupsCheck; + QCheckBox* myKeepIdsCheck; + + QPushButton* myFilterBtn; + SMESHGUI_FilterDlg* myFilterDlg; + + QString myHelpFileName; + + bool myIsApplyAndClose; + +private slots: + void ClickOnOk(); + void ClickOnCancel(); + bool ClickOnApply(); + void ClickOnHelp(); + void SelectionIntoArgument(); + void DeactivateActiveDialog(); + void ActivateThisDialog(); + void onTextChange( const QString& ); + void onSelectIdSource( bool ); + void setFilters(); +}; + +#endif // SMESHGUI_CopyMeshDLG_H diff --git a/src/SMESHGUI/SMESHGUI_CreatePatternDlg.cxx b/src/SMESHGUI/SMESHGUI_CreatePatternDlg.cxx index ef7f198ee..ac3c71931 100755 --- a/src/SMESHGUI/SMESHGUI_CreatePatternDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_CreatePatternDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_CreatePatternDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. // SMESH includes @@ -193,11 +194,11 @@ QWidget* SMESHGUI_CreatePatternDlg::createMainFrame( QWidget* theParent ) // Connect signals and slots connect( myTypeGrp, SIGNAL( buttonClicked( int ) ), - this, SLOT( onTypeChanged( int ) ) ); + this, SLOT( onTypeChanged( int ) ) ); connect( myProjectChk, SIGNAL( toggled( bool ) ), - this, SLOT( onProject( bool ) ) ); + this, SLOT( onProject( bool ) ) ); connect( aSelBtn, SIGNAL( clicked() ), - this, SLOT( onSelBtnClicked() ) ); + this, SLOT( onSelBtnClicked() ) ); return aMainGrp; } @@ -274,11 +275,11 @@ void SMESHGUI_CreatePatternDlg::Init( const int theType ) // selection and SMESHGUI connect( mySelectionMgr, SIGNAL( currentSelectionChanged() ), - this, SLOT( onSelectionDone() ) ); + this, SLOT( onSelectionDone() ) ); connect( mySMESHGUI, SIGNAL( SignalDeactivateActiveDialog() ), - this, SLOT( onDeactivate() ) ); + this, SLOT( onDeactivate() ) ); connect( mySMESHGUI, SIGNAL( SignalCloseAllDialogs() ), - this, SLOT( onClose() ) ); + this, SLOT( onClose() ) ); mySwitch2d->setEnabled( theType == Type_2d ); mySwitch3d->setEnabled( theType == Type_3d ); @@ -291,7 +292,7 @@ void SMESHGUI_CreatePatternDlg::Init( const int theType ) QApplication::instance()->processEvents(); updateGeometry(); - resize( minimumSize() ); + resize(100,100); activateSelection(); onSelectionDone(); @@ -342,8 +343,8 @@ bool SMESHGUI_CreatePatternDlg::isValid() { if ( myGeomObj->_is_nil() ) { SUIT_MessageBox::information( this, - tr( "SMESH_INSUFFICIENT_DATA" ), - tr( "SMESHGUI_INVALID_PARAMETERS" ) ); + tr( "SMESH_INSUFFICIENT_DATA" ), + tr( "SMESHGUI_INVALID_PARAMETERS" ) ); return false; } return true; @@ -406,13 +407,13 @@ void SMESHGUI_CreatePatternDlg::onSave() if ( aWritten != aLen ) { SUIT_MessageBox::information( this, - tr( "SMESH_ERROR" ), - tr( "ERROR_OF_SAVING" ) ); + tr( "SMESH_ERROR" ), + tr( "ERROR_OF_SAVING" ) ); } else { //SUIT_Application::getDesktop()->setSelectionModes(ActorSelection); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ) ) - aViewWindow->SetSelectionMode( ActorSelection ); + aViewWindow->SetSelectionMode( ActorSelection ); disconnect( mySelectionMgr, 0, this, 0 ); disconnect( mySMESHGUI, 0, this, 0 ); mySMESHGUI->ResetState(); @@ -465,7 +466,7 @@ void SMESHGUI_CreatePatternDlg::onOk() else { //SUIT_Application::getDesktop()->setSelectionModes(ActorSelection); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ) ) - aViewWindow->SetSelectionMode( ActorSelection ); + aViewWindow->SetSelectionMode( ActorSelection ); disconnect( mySelectionMgr, 0, this, 0 ); disconnect( mySMESHGUI, 0, this, 0 ); mySMESHGUI->ResetState(); @@ -512,11 +513,11 @@ void SMESHGUI_CreatePatternDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning( this, - tr( "WRN_WARNING" ), - tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). - arg( app->resourceMgr()->stringValue( "ExternalBrowser", - platform ) ). - arg( myHelpFileName ) ); + tr( "WRN_WARNING" ), + tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). + arg( app->resourceMgr()->stringValue( "ExternalBrowser", + platform ) ). + arg( myHelpFileName ) ); } } @@ -532,7 +533,7 @@ bool SMESHGUI_CreatePatternDlg::loadFromObject( const bool theMess ) if ( myPattern->_is_nil() ) myPattern = SMESH::GetPattern(); - if ( myMesh->_is_nil() && mySubMesh->_is_nil() || myGeomObj->_is_nil() ) + if ( (myMesh->_is_nil() && mySubMesh->_is_nil()) || myGeomObj->_is_nil() ) return false; SMESH::SMESH_Mesh_ptr aMesh = mySubMesh->_is_nil() ? myMesh.in() : mySubMesh->GetFather(); @@ -754,20 +755,20 @@ void SMESHGUI_CreatePatternDlg::activateSelection() if ( myType == Type_2d ) { mySelectionMgr->installFilter( new SMESH_NumberFilter( "SMESH", - TopAbs_SHAPE, - -1, - TopAbs_FACE ) ); + TopAbs_SHAPE, + -1, + TopAbs_FACE ) ); } else { TColStd_MapOfInteger aTypes; aTypes.Add( TopAbs_SHELL ); aTypes.Add( TopAbs_SOLID ); mySelectionMgr->installFilter( new SMESH_NumberFilter( "SMESH", - TopAbs_FACE, - 6, - aTypes, - GEOM::GEOM_Object::_nil(), - true ) ); + TopAbs_FACE, + 6, + aTypes, + GEOM::GEOM_Object::_nil(), + true ) ); } } diff --git a/src/SMESHGUI/SMESHGUI_CreatePatternDlg.h b/src/SMESHGUI/SMESHGUI_CreatePatternDlg.h index 838b609d4..0a03e1061 100755 --- a/src/SMESHGUI/SMESHGUI_CreatePatternDlg.h +++ b/src/SMESHGUI/SMESHGUI_CreatePatternDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_CreatePatternDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. // diff --git a/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.cxx b/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.cxx index db615c8c4..ffc22ecdb 100644 --- a/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_CreatePolyhedralVolumeDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -30,6 +31,7 @@ #include "SMESHGUI_Utils.h" #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_GroupUtils.h" #include "SMESHGUI_IdValidator.h" #include @@ -63,6 +65,7 @@ // Qt includes #include #include +#include #include #include #include @@ -129,9 +132,9 @@ namespace SMESH typedef std::vector TVTKIds; void SetPosition(SMESH_Actor* theActor, - vtkIdType theType, - const TVTKIds& theIds, - bool theReset=true) + vtkIdType theType, + const TVTKIds& theIds, + bool theReset=true) { vtkUnstructuredGrid *aGrid = theActor->GetUnstructuredGrid(); myGrid->SetPoints(aGrid->GetPoints()); @@ -141,14 +144,14 @@ namespace SMESH vtkIdList *anIds = vtkIdList::New(); for (int i = 0, iEnd = theIds.size(); i < iEnd; i++) - anIds->InsertId(i,theIds[i]); + anIds->InsertId(i,theIds[i]); myGrid->InsertNextCell(theType,anIds); if(theIds.size()!=0){ - myGrid->InsertNextCell(theType,anIds); - myGrid->Modified(); + myGrid->InsertNextCell(theType,anIds); + myGrid->Modified(); } - + anIds->Delete(); SetVisibility(true); @@ -167,7 +170,7 @@ namespace SMESH ~TPolySimulation(){ if( myViewWindow ) - myViewWindow->RemoveActor(myPreviewActor); + myViewWindow->RemoveActor(myPreviewActor); myPreviewActor->Delete(); @@ -246,6 +249,21 @@ SMESHGUI_CreatePolyhedralVolumeDlg::SMESHGUI_CreatePolyhedralVolumeDlg( SMESHGUI GroupContentLayout->addWidget( RemoveButton, 3, 3 ); GroupContentLayout->addWidget( Preview, 5, 0, 1, 4 ); + /***************************************************************/ + GroupGroups = new QGroupBox( tr( "SMESH_ADD_TO_GROUP" ), this ); + GroupGroups->setCheckable( true ); + QHBoxLayout* GroupGroupsLayout = new QHBoxLayout(GroupGroups); + GroupGroupsLayout->setSpacing(SPACING); + GroupGroupsLayout->setMargin(MARGIN); + + TextLabel_GroupName = new QLabel( tr( "SMESH_GROUP" ), GroupGroups ); + ComboBox_GroupName = new QComboBox( GroupGroups ); + ComboBox_GroupName->setEditable( true ); + ComboBox_GroupName->setInsertPolicy( QComboBox::NoInsert ); + + GroupGroupsLayout->addWidget( TextLabel_GroupName ); + GroupGroupsLayout->addWidget( ComboBox_GroupName, 1 ); + /***************************************************************/ GroupButtons = new QGroupBox( this ); QHBoxLayout* GroupButtonsLayout = new QHBoxLayout( GroupButtons ); @@ -273,6 +291,7 @@ SMESHGUI_CreatePolyhedralVolumeDlg::SMESHGUI_CreatePolyhedralVolumeDlg( SMESHGUI /***************************************************************/ topLayout->addWidget( ConstructorsBox ); topLayout->addWidget( GroupContent ); + topLayout->addWidget( GroupGroups ); topLayout->addWidget( GroupButtons ); mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); @@ -306,6 +325,9 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::Init() myEditCurrentArgument = LineEditElements; mySMESHGUI->SetActiveDialogBox( (QDialog*)this ); + /* reset "Add to group" control */ + GroupGroups->setChecked( false ); + myNbOkElements = 0; myActor = 0; @@ -321,7 +343,7 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::Init() connect(SelectElementsButton, SIGNAL( clicked() ), SLOT( SetEditCurrentArgument() ) ); connect(LineEditElements, SIGNAL( textChanged(const QString&) ), SLOT(onTextChange(const QString&))); - connect(myFacesByNodes, SIGNAL(selectionChanged()), this, SLOT(onListSelectionChanged())); + connect(myFacesByNodes, SIGNAL(itemSelectionChanged()), this, SLOT(onListSelectionChanged())); connect(AddButton, SIGNAL(clicked()), this, SLOT(onAdd())); connect(RemoveButton, SIGNAL(clicked()), this, SLOT(onRemove())); @@ -358,42 +380,42 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::ConstructorsClicked(int constructorId) { case 0 : { - if ( myActor ){ + if ( myActor ){ myActor->SetPointRepresentation(true); - } + } else SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); - - AddButton->setEnabled(false); - RemoveButton->setEnabled(false); - TextLabelIds->setText( tr( "SMESH_ID_NODES" ) ); - myFacesByNodesLabel->show(); - myFacesByNodes->clear(); - myFacesByNodes->show(); - AddButton->show(); - RemoveButton->show(); - Preview->show(); - break; + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + + AddButton->setEnabled(false); + RemoveButton->setEnabled(false); + TextLabelIds->setText( tr( "SMESH_ID_NODES" ) ); + myFacesByNodesLabel->show(); + myFacesByNodes->clear(); + myFacesByNodes->show(); + AddButton->show(); + RemoveButton->show(); + Preview->show(); + break; } case 1 : { - if( myActor ){ - myActor->SetPointRepresentation(false); - } else { - SMESH::SetPointRepresentation(false); - } - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - - TextLabelIds->setText( tr( "SMESH_ID_FACES" ) ); - myFacesByNodesLabel->hide(); - myFacesByNodes->hide(); - AddButton->hide(); - RemoveButton->hide(); - Preview->show(); - break; + if( myActor ){ + myActor->SetPointRepresentation(false); + } else { + SMESH::SetPointRepresentation(false); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + + TextLabelIds->setText( tr( "SMESH_ID_FACES" ) ); + myFacesByNodesLabel->hide(); + myFacesByNodes->hide(); + AddButton->hide(); + RemoveButton->hide(); + Preview->show(); + break; } } @@ -402,7 +424,7 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::ConstructorsClicked(int constructorId) QApplication::instance()->processEvents(); updateGeometry(); - resize( minimumSize() ); + resize(100,100); } //================================================================================= @@ -420,80 +442,143 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::ClickOnPreview(bool theToggled){ //================================================================================= void SMESHGUI_CreatePolyhedralVolumeDlg::ClickOnApply() { + if( !isValid() ) + return; + if ( myNbOkElements>0 && !mySMESHGUI->isActiveStudyLocked()) { if(checkEditLine(false) == -1) {return;} busy = true; + long anElemId = -1; + + bool addToGroup = GroupGroups->isChecked(); + QString aGroupName; + + SMESH::SMESH_GroupBase_var aGroup; + int idx = 0; + if( addToGroup ) { + aGroupName = ComboBox_GroupName->currentText(); + for ( int i = 1; i < ComboBox_GroupName->count(); i++ ) { + QString aName = ComboBox_GroupName->itemText( i ); + if ( aGroupName == aName && ( i == ComboBox_GroupName->currentIndex() || idx == 0 ) ) + idx = i; + } + if ( idx > 0 && idx < myGroups.count() ) { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( myGroups[idx-1] ); + if ( !aGeomGroup->_is_nil() ) { + int res = SUIT_MessageBox::question( this, tr( "SMESH_WRN_WARNING" ), + tr( "MESH_STANDALONE_GRP_CHOSEN" ).arg( aGroupName ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 0, 1 ); + if ( res == 1 ) return; + } + aGroup = myGroups[idx-1]; + } + } + if (GetConstructorId() == 0) - { - SMESH::long_array_var anIdsOfNodes = new SMESH::long_array; - SMESH::long_array_var aQuantities = new SMESH::long_array; - - aQuantities->length( myFacesByNodes->count() ); - - TColStd_ListOfInteger aNodesIds; - - int aNbQuantities = 0; - for (int i = 0; i < myFacesByNodes->count(); i++ ) { - QStringList anIds = myFacesByNodes->item(i)->text().split( " ", QString::SkipEmptyParts ); - for (QStringList::iterator it = anIds.begin(); it != anIds.end(); ++it) - aNodesIds.Append( (*it).toInt() ); - - aQuantities[aNbQuantities++] = anIds.count(); - } - - anIdsOfNodes->length(aNodesIds.Extent()); - - int aNbIdsOfNodes = 0; - TColStd_ListIteratorOfListOfInteger It; - It.Initialize(aNodesIds); - for( ;It.More();It.Next()) - anIdsOfNodes[aNbIdsOfNodes++] = It.Value(); - - try{ - SUIT_OverrideCursor aWaitCursor; - SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - aMeshEditor->AddPolyhedralVolume(anIdsOfNodes, aQuantities); - }catch(SALOME::SALOME_Exception& exc){ - INFOS("Follow exception was cought:\n\t"<length( myFacesByNodes->count() ); + + TColStd_ListOfInteger aNodesIds; + + int aNbQuantities = 0; + for (int i = 0; i < myFacesByNodes->count(); i++ ) { + QStringList anIds = myFacesByNodes->item(i)->text().split( " ", QString::SkipEmptyParts ); + for (QStringList::iterator it = anIds.begin(); it != anIds.end(); ++it) + aNodesIds.Append( (*it).toInt() ); + + aQuantities[aNbQuantities++] = anIds.count(); + } + + anIdsOfNodes->length(aNodesIds.Extent()); + + int aNbIdsOfNodes = 0; + TColStd_ListIteratorOfListOfInteger It; + It.Initialize(aNodesIds); + for( ;It.More();It.Next()) + anIdsOfNodes[aNbIdsOfNodes++] = It.Value(); + + try{ + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + anElemId = aMeshEditor->AddPolyhedralVolume(anIdsOfNodes, aQuantities); + }catch(SALOME::SALOME_Exception& exc){ + INFOS("Follow exception was cought:\n\t"<text().split( " ", QString::SkipEmptyParts ); - anIdsOfFaces->length(aListId.count()); - for ( int i = 0; i < aListId.count(); i++ ) - anIdsOfFaces[i] = aListId[i].toInt(); - - try{ - SUIT_OverrideCursor aWaitCursor; - SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - aMeshEditor->AddPolyhedralVolumeByFaces(anIdsOfFaces); - }catch(SALOME::SALOME_Exception& exc){ - INFOS("Follow exception was cought:\n\t"<text().split( " ", QString::SkipEmptyParts ); + anIdsOfFaces->length(aListId.count()); + for ( int i = 0; i < aListId.count(); i++ ) + anIdsOfFaces[i] = aListId[i].toInt(); + + try{ + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + anElemId = aMeshEditor->AddPolyhedralVolumeByFaces(anIdsOfFaces); + }catch(SALOME::SALOME_Exception& exc){ + INFOS("Follow exception was cought:\n\t"<_is_nil() ) { + // create new group + aGroupUsed = SMESH::AddGroup( myMesh, SMESH::VOLUME, aGroupName ); + if ( !aGroupUsed->_is_nil() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroupUsed)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + else { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( aGroup ); + if ( !aGeomGroup->_is_nil() ) { + aGroupUsed = myMesh->ConvertToStandalone( aGeomGroup ); + if ( !aGroupUsed->_is_nil() && idx > 0 ) { + myGroups[idx-1] = SMESH::SMESH_GroupBase::_duplicate(aGroupUsed); + SMESHGUI::GetSMESHGUI()->getApp()->updateObjectBrowser(); + } + } + else + aGroupUsed = SMESH::SMESH_Group::_narrow( aGroup ); + } + + if ( !aGroupUsed->_is_nil() ) { + SMESH::long_array_var anIdList = new SMESH::long_array; + anIdList->length( 1 ); + anIdList[0] = anElemId; + aGroupUsed->Add( anIdList.inout() ); + } + } + //SALOME_ListIO aList; //mySelectionMgr->setSelectedObjects( aList ); SMESH::UpdateView(); if( myActor ){ - unsigned int anEntityMode = myActor->GetEntityMode(); - myActor->SetEntityMode(SMESH_Actor::eVolumes | anEntityMode); + unsigned int anEntityMode = myActor->GetEntityMode(); + myActor->SetEntityMode(SMESH_Actor::eVolumes | anEntityMode); } //ConstructorsClicked( GetConstructorId() ); busy = false; + + SMESHGUI::Modified(); } + myFacesByNodes->clear(); } //================================================================================= @@ -507,7 +592,7 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::ClickOnOk() ClickOnCancel(); } - + //================================================================================= // function : ClickOnCancel() // purpose : @@ -543,10 +628,10 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -570,67 +655,67 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::onTextChange(const QString& theNewText) if (GetConstructorId() == 0) { if ( aMesh ) { - TColStd_MapOfInteger newIndices; + TColStd_MapOfInteger newIndices; - QStringList aListId = theNewText.split( " ", QString::SkipEmptyParts ); - for ( int i = 0; i < aListId.count(); i++ ) { - const SMDS_MeshNode * n = aMesh->FindNode( aListId[ i ].toInt() ); - if ( n ) { - newIndices.Add(n->GetID()); - myNbOkElements++; - } - } + QStringList aListId = theNewText.split( " ", QString::SkipEmptyParts ); + for ( int i = 0; i < aListId.count(); i++ ) { + const SMDS_MeshNode * n = aMesh->FindNode( aListId[ i ].toInt() ); + if ( n ) { + newIndices.Add(n->GetID()); + myNbOkElements++; + } + } - mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); + mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( myActor->getIO(), true, true ); - if ( myNbOkElements>0 && aListId.count()>=3) - AddButton->setEnabled(true); - else - AddButton->setEnabled(false); + if ( myNbOkElements>0 && aListId.count()>=3) + AddButton->setEnabled(true); + else + AddButton->setEnabled(false); - displaySimulation(); + displaySimulation(); } } else if (GetConstructorId() == 1) { - myNbOkElements = 0; - buttonOk->setEnabled( false ); - buttonApply->setEnabled( false ); + myNbOkElements = 0; + buttonOk->setEnabled( false ); + buttonApply->setEnabled( false ); - // check entered ids of faces and hilight them - QStringList aListId; - if ( aMesh ) { - TColStd_MapOfInteger newIndices; + // check entered ids of faces and hilight them + QStringList aListId; + if ( aMesh ) { + TColStd_MapOfInteger newIndices; - aListId = theNewText.split( " ", QString::SkipEmptyParts ); - - for ( int i = 0; i < aListId.count(); i++ ) { - const SMDS_MeshElement * e = aMesh->FindElement( aListId[ i ].toInt() ); - if ( e ) { - newIndices.Add(e->GetID()); - myNbOkElements++; - } - } - - mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); + aListId = theNewText.split( " ", QString::SkipEmptyParts ); + + for ( int i = 0; i < aListId.count(); i++ ) { + const SMDS_MeshElement * e = aMesh->FindElement( aListId[ i ].toInt() ); + if ( e ) { + newIndices.Add(e->GetID()); + myNbOkElements++; + } + } + + mySelector->AddOrRemoveIndex( myActor->getIO(), newIndices, false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( myActor->getIO(), true, true ); - if ( myNbOkElements ) { - if (aListId.count()>1){ - buttonOk->setEnabled( true ); - buttonApply->setEnabled( true ); - } - else{ - buttonOk->setEnabled( false ); - buttonApply->setEnabled( false ); - } - if(aListId.count()>1) - displaySimulation(); - } - } + if ( myNbOkElements ) { + if (aListId.count()>1){ + buttonOk->setEnabled( true ); + buttonApply->setEnabled( true ); + } + else{ + buttonOk->setEnabled( false ); + buttonApply->setEnabled( false ); + } + if(aListId.count()>1) + displaySimulation(); + } + } } busy = false; } @@ -663,6 +748,8 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::SelectionIntoArgument() mySimulation->SetVisibility(false); + QString aCurrentEntry = myEntry; + // get selected mesh SALOME_ListIO selected; @@ -672,10 +759,29 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::SelectionIntoArgument() return; } + myEntry = selected.First()->getEntry(); myMesh = SMESH::GetMeshByIO( selected.First() ); if ( myMesh->_is_nil() ) return; + // process groups + if ( !myMesh->_is_nil() && myEntry != aCurrentEntry ) { + myGroups.clear(); + ComboBox_GroupName->clear(); + ComboBox_GroupName->addItem( QString() ); + SMESH::ListOfGroups aListOfGroups = *myMesh->GetGroups(); + for ( int i = 0, n = aListOfGroups.length(); i < n; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i]; + if ( !aGroup->_is_nil() && aGroup->GetType() == SMESH::VOLUME ) { + QString aGroupName( aGroup->GetName() ); + if ( !aGroupName.isEmpty() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + } + } + myActor = SMESH::FindActorByObject(myMesh); if ( !myActor ) return; @@ -749,13 +855,13 @@ int SMESHGUI_CreatePolyhedralVolumeDlg::checkEditLine(bool checkLast) case 0:{ // nodes const SMDS_MeshNode * aNode = aMesh->FindNode( aListId[ i ].toInt() ); if( !aNode ){ - SUIT_MessageBox::warning(this, - tr("SMESH_POLYEDRE_CREATE_ERROR"), - tr("The incorrect indices of nodes!")); - - myEditCurrentArgument->clear(); - myEditCurrentArgument->setText( aString ); - return -1; + SUIT_MessageBox::warning(this, + tr("SMESH_POLYEDRE_CREATE_ERROR"), + tr("The incorrect indices of nodes!")); + + myEditCurrentArgument->clear(); + myEditCurrentArgument->setText( aString ); + return -1; } break; @@ -764,24 +870,24 @@ int SMESHGUI_CreatePolyhedralVolumeDlg::checkEditLine(bool checkLast) bool aElemIsOK = true; const SMDS_MeshElement * aElem = aMesh->FindElement( aListId[ i ].toInt() ); if (!aElem) - { - aElemIsOK = false; - } + { + aElemIsOK = false; + } else - { - SMDSAbs_ElementType aType = aMesh->GetElementType( aElem->GetID(),true ); - if (aType != SMDSAbs_Face){ - aElemIsOK = false; - } - } + { + SMDSAbs_ElementType aType = aMesh->GetElementType( aElem->GetID(),true ); + if (aType != SMDSAbs_Face){ + aElemIsOK = false; + } + } if (!aElemIsOK){ - SUIT_MessageBox::warning(this, - tr("SMESH_POLYEDRE_CREATE_ERROR"), - tr("The incorrect indices of faces!")); - - myEditCurrentArgument->clear(); - myEditCurrentArgument->setText( aString ); - return -1; + SUIT_MessageBox::warning(this, + tr("SMESH_POLYEDRE_CREATE_ERROR"), + tr("The incorrect indices of faces!")); + + myEditCurrentArgument->clear(); + myEditCurrentArgument->setText( aString ); + return -1; } break; } @@ -804,66 +910,66 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::displaySimulation() vtkIdType aType = VTK_CONVEX_POINT_SET; SMDS_Mesh* aMesh = 0; if ( myActor ){ - aMesh = myActor->GetObject()->GetMesh(); + aMesh = myActor->GetObject()->GetMesh(); } if (GetConstructorId() == 0 && aMesh){ - if (!AddButton->isEnabled()){ - mySimulation->ResetGrid(true); - for (int i = 0; i < myFacesByNodes->count(); i++) { - QStringList anIds = myFacesByNodes->item(i)->text().split( " ", QString::SkipEmptyParts ); - SMESH::TPolySimulation::TVTKIds aVTKIds_faces; - for (QStringList::iterator it = anIds.begin(); it != anIds.end(); ++it){ - const SMDS_MeshNode* aNode = aMesh->FindNode( (*it).toInt() ); - if (!aNode) continue; - vtkIdType aId = myActor->GetObject()->GetNodeVTKId( (*it).toInt() ); - aVTKIds.push_back(aId); - aVTKIds_faces.push_back(aId); - } - if(!Preview->isChecked()){ - aType = VTK_POLYGON; - mySimulation->SetPosition(myActor, aType, aVTKIds_faces,false); - } - } - if(myFacesByNodes->count() == 0){ - mySimulation->SetVisibility(false); - } else { - mySimulation->SetVisibility(true); - } - if(Preview->isChecked()){ - mySimulation->SetPosition(myActor, aType, aVTKIds); - } - } else { - // add ids from edit line - QStringList anEditIds = myEditCurrentArgument->text().split( " ", QString::SkipEmptyParts ); - for ( int i = 0; i < anEditIds.count(); i++ ) - aVTKIds.push_back( myActor->GetObject()->GetNodeVTKId( anEditIds[ i ].toInt() )); - aType = VTK_POLYGON; - mySimulation->SetPosition(myActor, aType, aVTKIds); - } + if (!AddButton->isEnabled()){ + mySimulation->ResetGrid(true); + for (int i = 0; i < myFacesByNodes->count(); i++) { + QStringList anIds = myFacesByNodes->item(i)->text().split( " ", QString::SkipEmptyParts ); + SMESH::TPolySimulation::TVTKIds aVTKIds_faces; + for (QStringList::iterator it = anIds.begin(); it != anIds.end(); ++it){ + const SMDS_MeshNode* aNode = aMesh->FindNode( (*it).toInt() ); + if (!aNode) continue; + vtkIdType aId = myActor->GetObject()->GetNodeVTKId( (*it).toInt() ); + aVTKIds.push_back(aId); + aVTKIds_faces.push_back(aId); + } + if(!Preview->isChecked()){ + aType = VTK_POLYGON; + mySimulation->SetPosition(myActor, aType, aVTKIds_faces,false); + } + } + if(myFacesByNodes->count() == 0){ + mySimulation->SetVisibility(false); + } else { + mySimulation->SetVisibility(true); + } + if(Preview->isChecked()){ + mySimulation->SetPosition(myActor, aType, aVTKIds); + } + } else { + // add ids from edit line + QStringList anEditIds = myEditCurrentArgument->text().split( " ", QString::SkipEmptyParts ); + for ( int i = 0; i < anEditIds.count(); i++ ) + aVTKIds.push_back( myActor->GetObject()->GetNodeVTKId( anEditIds[ i ].toInt() )); + aType = VTK_POLYGON; + mySimulation->SetPosition(myActor, aType, aVTKIds); + } }else if(GetConstructorId() == 1 && aMesh){ - QStringList aListId = myEditCurrentArgument->text().split( " ", QString::SkipEmptyParts ); - for ( int i = 0; i < aListId.count(); i++ ) - { - const SMDS_MeshElement * anElem = aMesh->FindElement( aListId[ i ].toInt() ); - if ( !anElem ) continue; - SMDSAbs_ElementType aFaceType = aMesh->GetElementType( anElem->GetID(),true ); - if (aFaceType != SMDSAbs_Face) continue; - - SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); - SMESH::TPolySimulation::TVTKIds aVTKIds_faces; - while( anIter->more() ) - if ( const SMDS_MeshNode* aNode = (SMDS_MeshNode*)anIter->next() ){ - vtkIdType aId = myActor->GetObject()->GetNodeVTKId( aNode->GetID() ); - aVTKIds.push_back(aId); - aVTKIds_faces.push_back(aId); - } - if(!Preview->isChecked()){ - aType = VTK_POLYGON; - mySimulation->SetPosition(myActor, aType, aVTKIds_faces); - } - } - if(Preview->isChecked()) - mySimulation->SetPosition(myActor, aType, aVTKIds); + QStringList aListId = myEditCurrentArgument->text().split( " ", QString::SkipEmptyParts ); + for ( int i = 0; i < aListId.count(); i++ ) + { + const SMDS_MeshElement * anElem = aMesh->FindElement( aListId[ i ].toInt() ); + if ( !anElem ) continue; + SMDSAbs_ElementType aFaceType = aMesh->GetElementType( anElem->GetID(),true ); + if (aFaceType != SMDSAbs_Face) continue; + + SMDS_ElemIteratorPtr anIter = anElem->nodesIterator(); + SMESH::TPolySimulation::TVTKIds aVTKIds_faces; + while( anIter->more() ) + if ( const SMDS_MeshNode* aNode = (SMDS_MeshNode*)anIter->next() ){ + vtkIdType aId = myActor->GetObject()->GetNodeVTKId( aNode->GetID() ); + aVTKIds.push_back(aId); + aVTKIds_faces.push_back(aId); + } + if(!Preview->isChecked()){ + aType = VTK_POLYGON; + mySimulation->SetPosition(myActor, aType, aVTKIds_faces); + } + } + if(Preview->isChecked()) + mySimulation->SetPosition(myActor, aType, aVTKIds); } SMESH::UpdateView(); } @@ -985,8 +1091,8 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::onAdd() myNbOkElements = 1; myEditCurrentArgument->clear(); AddButton->setEnabled(false); - buttonOk->setEnabled( true ); - if(myFacesByNodes->count()>1) buttonApply->setEnabled( true ); + buttonApply->setEnabled( myFacesByNodes->count() > 1 ); + buttonOk->setEnabled( myFacesByNodes->count() > 1 ); } busy = false; onListSelectionChanged(); @@ -1062,3 +1168,16 @@ void SMESHGUI_CreatePolyhedralVolumeDlg::keyPressEvent( QKeyEvent* e ) ClickOnHelp(); } } + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= +bool SMESHGUI_CreatePolyhedralVolumeDlg::isValid() +{ + if( GroupGroups->isChecked() && ComboBox_GroupName->currentText().isEmpty() ) { + SUIT_MessageBox::warning( this, tr( "SMESH_WRN_WARNING" ), tr( "GROUP_NAME_IS_EMPTY" ) ); + return false; + } + return true; +} diff --git a/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.h b/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.h index b46d0c35b..44dd3ab5d 100644 --- a/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.h +++ b/src/SMESHGUI/SMESHGUI_CreatePolyhedralVolumeDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_CreatePolyhedralVolumeDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -37,6 +38,7 @@ #include CORBA_SERVER_HEADER(SMESH_Mesh) class QButtonGroup; +class QComboBox; class QGroupBox; class QListWidget; class QLabel; @@ -67,6 +69,8 @@ public: ~SMESHGUI_CreatePolyhedralVolumeDlg(); private: + typedef QList GrpList; + void Init(); void closeEvent( QCloseEvent* ); void enterEvent( QEvent* ); /* mouse enter the QWidget */ @@ -75,6 +79,8 @@ private: int GetConstructorId(); void displaySimulation(); + bool isValid(); + int checkEditLine( bool = true ); /*! Checking for indices, return 1 if all ok, esle -1*/ SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ @@ -88,12 +94,17 @@ private: SMESH::SMESH_Mesh_var myMesh; SMESH_Actor* myActor; SMESH::TPolySimulation* mySimulation; + QString myEntry; + GrpList myGroups; QGroupBox* ConstructorsBox; QButtonGroup* GroupConstructors; QRadioButton* RadioButton1; QRadioButton* RadioButton2; QCheckBox* Preview; + QGroupBox* GroupGroups; + QLabel* TextLabel_GroupName; + QComboBox* ComboBox_GroupName; QGroupBox* GroupButtons; QPushButton* buttonOk; QPushButton* buttonCancel; diff --git a/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.cxx b/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.cxx index 6e2a106a2..ac5a1c686 100644 --- a/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_DeleteGroupDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. // SMESH includes @@ -189,7 +190,7 @@ bool SMESHGUI_DeleteGroupDlg::isValid() { if (myListBox->count() == 0) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("NO_SELECTED_GROUPS")); + tr("NO_SELECTED_GROUPS")); return false; } @@ -218,6 +219,7 @@ bool SMESHGUI_DeleteGroupDlg::onApply() myListGrp.clear(); mySelectionMgr->clearSelected(); SMESH::UpdateView(); + SMESHGUI::Modified(); mySMESHGUI->updateObjBrowser(true); myBlockSelection = false; @@ -266,10 +268,10 @@ void SMESHGUI_DeleteGroupDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } diff --git a/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.h b/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.h index c9d6bd029..48e418417 100644 --- a/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.h +++ b/src/SMESHGUI/SMESHGUI_DeleteGroupDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_DeleteGroupDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. // diff --git a/src/SMESHGUI/SMESHGUI_Dialog.cxx b/src/SMESHGUI/SMESHGUI_Dialog.cxx index 754221521..958f609ff 100644 --- a/src/SMESHGUI/SMESHGUI_Dialog.cxx +++ b/src/SMESHGUI/SMESHGUI_Dialog.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Dialog.cxx // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_Dialog.h b/src/SMESHGUI/SMESHGUI_Dialog.h index 7da64f71e..374cbe72b 100644 --- a/src/SMESHGUI/SMESHGUI_Dialog.h +++ b/src/SMESHGUI/SMESHGUI_Dialog.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Dialog.h // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. @@ -44,8 +45,8 @@ class SMESHGUI_EXPORT SMESHGUI_Dialog : public LightApp_Dialog Q_OBJECT public: - SMESHGUI_Dialog( QWidget* = 0, const bool = false, const bool = false, - const int = OK | Close | Apply | Help ); + SMESHGUI_Dialog( QWidget* = 0, const bool modal = false, const bool allowResize = false, + const int = OK | Close | Apply | Help ); virtual ~SMESHGUI_Dialog(); virtual void show(); diff --git a/src/SMESHGUI/SMESHGUI_Displayer.cxx b/src/SMESHGUI/SMESHGUI_Displayer.cxx index 613fbb9e4..153c34825 100644 --- a/src/SMESHGUI/SMESHGUI_Displayer.cxx +++ b/src/SMESHGUI/SMESHGUI_Displayer.cxx @@ -1,32 +1,34 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : Displayer for SMESH module // File : SMESHGUI_Displayer.cxx // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // SMESH includes // -#include "SMESHGUI_Displayer.h" +#include "SMESHGUI_Displayer.h" #include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_Utils.h" // SALOME GUI includes #include @@ -35,6 +37,13 @@ #include #include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) +#include CORBA_SERVER_HEADER(SMESH_Mesh) + + SMESHGUI_Displayer::SMESHGUI_Displayer( SalomeApp_Application* app ) : LightApp_Displayer(), myApp( app ) @@ -59,16 +68,16 @@ SALOME_Prs* SMESHGUI_Displayer::buildPresentation( const QString& entry, SALOME_ SUIT_ViewWindow* wnd = vtk_viewer->getViewManager()->getActiveView(); SMESH_Actor* anActor = SMESH::FindActorByEntry( wnd, entry.toLatin1().data() ); if( !anActor ) - anActor = SMESH::CreateActor( study()->studyDS(), entry.toLatin1().data(), true ); + anActor = SMESH::CreateActor( study()->studyDS(), entry.toLatin1().data(), true ); if( anActor ) { - SMESH::DisplayActor( wnd, anActor ); + SMESH::DisplayActor( wnd, anActor ); prs = LightApp_Displayer::buildPresentation( entry.toLatin1().data(), aViewFrame ); } if( prs ) - UpdatePrs( prs ); + UpdatePrs( prs ); else if( anActor ) - SMESH::RemoveActor( vtk_viewer->getViewManager()->getActiveView(), anActor ); + SMESH::RemoveActor( vtk_viewer->getViewManager()->getActiveView(), anActor ); } } @@ -80,7 +89,38 @@ SalomeApp_Study* SMESHGUI_Displayer::study() const return dynamic_cast( myApp->activeStudy() ); } -bool SMESHGUI_Displayer::canBeDisplayed( const QString& /*entry*/, const QString& viewer_type ) const -{ - return viewer_type==SVTK_Viewer::Type(); +bool SMESHGUI_Displayer::canBeDisplayed( const QString& entry, const QString& viewer_type ) const { + bool res = false; + if(viewer_type != SVTK_Viewer::Type()) + return res; + + SalomeApp_Study* study = dynamic_cast( myApp->activeStudy() ); + if( !study ) + return res; + + + _PTR(SObject) obj = study->studyDS()->FindObjectID( (const char*)entry.toLatin1() ); + CORBA::Object_var anObj = SMESH::SObjectToObject( obj ); + + /* + if( !CORBA::is_nil( anObj ) ) { + SMESH::SMESH_Mesh_var mesh = SMESH::SMESH_Mesh::_narrow( anObj ); + if ( ! mesh->_is_nil() ) + res = (mesh->NbNodes() > 0); + + SMESH::SMESH_subMesh_var aSubMeshObj = SMESH::SMESH_subMesh::_narrow( anObj ); + if ( !aSubMeshObj->_is_nil() ) + res = (aSubMeshObj->GetNumberOfNodes(true) > 0); + + SMESH::SMESH_GroupBase_var aGroupObj = SMESH::SMESH_GroupBase::_narrow( anObj ); + if ( !aGroupObj->_is_nil() ) + res = !aGroupObj->IsEmpty(); + }*/ + if( !CORBA::is_nil( anObj ) ) { + if(!SMESH::SMESH_Mesh::_narrow( anObj )->_is_nil() || + !SMESH::SMESH_subMesh::_narrow( anObj )->_is_nil() || + !SMESH::SMESH_GroupBase::_narrow( anObj )->_is_nil()) + res = true; + } + return res; } diff --git a/src/SMESHGUI/SMESHGUI_Displayer.h b/src/SMESHGUI/SMESHGUI_Displayer.h index 61288ee13..6a0920b4a 100644 --- a/src/SMESHGUI/SMESHGUI_Displayer.h +++ b/src/SMESHGUI/SMESHGUI_Displayer.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : Displayer for SMESH module // File : SMESHGUI_Displayer.h // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.cxx b/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.cxx new file mode 100644 index 000000000..e49250afe --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.cxx @@ -0,0 +1,624 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_DuplicateNodesDlg.cxx +// Author : Michael ZORIN, Open CASCADE S.A.S. + +// SMESH includes +#include "SMESHGUI_DuplicateNodesDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" + +#include + +// SALOME GUI includes +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) + +#define SPACING 6 +#define MARGIN 11 + +/*! + \class BusyLocker + \brief Simple 'busy state' flag locker. + \internal +*/ + +class BusyLocker +{ +public: + //! Constructor. Sets passed boolean flag to \c true. + BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; } + //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false. + ~BusyLocker() { myBusy = false; } +private: + bool& myBusy; //! External 'busy state' boolean flag +}; + +/*! + \brief Constructor + \param theModule Mesh module instance +*/ +SMESHGUI_DuplicateNodesDlg::SMESHGUI_DuplicateNodesDlg( SMESHGUI* theModule ) + : QDialog( SMESH::GetDesktop( theModule ) ), + mySMESHGUI( theModule ), + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) +{ + // Dialog attributes + setModal(false); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(tr("SMESH_DUPLICATE_TITLE")); + setSizeGripEnabled(true); + + // Icons for the dialog operation modes and selection button + SUIT_ResourceMgr* aResMgr = SMESH::GetResourceMgr( mySMESHGUI ); + QPixmap iconWithoutElem (aResMgr->loadPixmap("SMESH", tr("ICON_SMESH_DUPLICATE_NODES"))); + QPixmap iconWithElem (aResMgr->loadPixmap("SMESH", tr("ICON_SMESH_DUPLICATE_NODES_WITH_ELEM"))); + QPixmap iconSelect (aResMgr->loadPixmap("SMESH", tr("ICON_SELECT"))); + + // Main layout + QVBoxLayout* aMainLayout = new QVBoxLayout(this); + aMainLayout->setSpacing(SPACING); + aMainLayout->setMargin(MARGIN); + + // Operation modes selector + QGroupBox* aConstructorsBox = new QGroupBox(tr("DUPLICATION_MODE"), this); + myGroupConstructors = new QButtonGroup(this); + QHBoxLayout* aConstructorsBoxLayout = new QHBoxLayout(aConstructorsBox); + aConstructorsBoxLayout->setSpacing(SPACING); + aConstructorsBoxLayout->setMargin(MARGIN); + + QRadioButton* aRadioButton1 = new QRadioButton(aConstructorsBox); + aRadioButton1->setIcon(iconWithoutElem); + QRadioButton* aRadioButton2 = new QRadioButton(aConstructorsBox); + aRadioButton2->setIcon(iconWithElem); + + aConstructorsBoxLayout->addWidget(aRadioButton1); + aConstructorsBoxLayout->addWidget(aRadioButton2); + myGroupConstructors->addButton(aRadioButton1, 0); + myGroupConstructors->addButton(aRadioButton2, 1); + + // Arguments + myGroupArguments = new QGroupBox(this); + QGridLayout* aGroupArgumentsLayout = new QGridLayout(myGroupArguments); + aGroupArgumentsLayout->setSpacing(SPACING); + aGroupArgumentsLayout->setMargin(MARGIN); + + myTextLabel1 = new QLabel(myGroupArguments); + mySelectButton1 = new QPushButton(myGroupArguments); + mySelectButton1->setIcon(iconSelect); + myLineEdit1 = new QLineEdit(myGroupArguments); + myLineEdit1->setReadOnly(true); + + myTextLabel2 = new QLabel(myGroupArguments); + mySelectButton2 = new QPushButton(myGroupArguments); + mySelectButton2->setIcon(iconSelect); + myLineEdit2 = new QLineEdit(myGroupArguments); + myLineEdit2->setReadOnly(true); + + myTextLabel3 = new QLabel(myGroupArguments); + mySelectButton3 = new QPushButton(myGroupArguments); + mySelectButton3->setIcon(iconSelect); + myLineEdit3 = new QLineEdit(myGroupArguments); + myLineEdit3->setReadOnly(true); + + myCheckBoxNewElemGroup = new QCheckBox(tr("CONSTRUCT_NEW_GROUP_ELEMENTS"), myGroupArguments); + myCheckBoxNewNodeGroup = new QCheckBox(tr("CONSTRUCT_NEW_GROUP_NODES"), myGroupArguments); + + aGroupArgumentsLayout->addWidget(myTextLabel1, 0, 0); + aGroupArgumentsLayout->addWidget(mySelectButton1, 0, 1); + aGroupArgumentsLayout->addWidget(myLineEdit1, 0, 2); + aGroupArgumentsLayout->addWidget(myTextLabel2, 1, 0); + aGroupArgumentsLayout->addWidget(mySelectButton2, 1, 1); + aGroupArgumentsLayout->addWidget(myLineEdit2, 1, 2); + aGroupArgumentsLayout->addWidget(myTextLabel3, 2, 0); + aGroupArgumentsLayout->addWidget(mySelectButton3, 2, 1); + aGroupArgumentsLayout->addWidget(myLineEdit3, 2, 2); + aGroupArgumentsLayout->addWidget(myCheckBoxNewElemGroup, 3, 0); + aGroupArgumentsLayout->addWidget(myCheckBoxNewNodeGroup, 4, 0); + aGroupArgumentsLayout->setRowStretch(5, 1); + + // Buttons + QGroupBox* aGroupButtons = new QGroupBox(this); + QHBoxLayout* aGroupButtonsLayout = new QHBoxLayout(aGroupButtons); + aGroupButtonsLayout->setSpacing(SPACING); + aGroupButtonsLayout->setMargin(MARGIN); + + myButtonOk = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aGroupButtons); + myButtonOk->setAutoDefault(true); + myButtonOk->setDefault(true); + myButtonApply = new QPushButton(tr("SMESH_BUT_APPLY"), aGroupButtons); + myButtonApply->setAutoDefault(true); + myButtonClose = new QPushButton(tr("SMESH_BUT_CLOSE"), aGroupButtons); + myButtonClose->setAutoDefault(true); + myButtonHelp = new QPushButton(tr("SMESH_BUT_HELP"), aGroupButtons); + myButtonHelp->setAutoDefault(true); + + aGroupButtonsLayout->addWidget(myButtonOk); + aGroupButtonsLayout->addSpacing(10); + aGroupButtonsLayout->addWidget(myButtonApply); + aGroupButtonsLayout->addSpacing(10); + aGroupButtonsLayout->addStretch(); + aGroupButtonsLayout->addWidget(myButtonClose); + aGroupButtonsLayout->addWidget(myButtonHelp); + + // Add mode selector, arguments and buttons to the main layout + aMainLayout->addWidget(aConstructorsBox); + aMainLayout->addWidget(myGroupArguments); + aMainLayout->addWidget(aGroupButtons); + + // Initialize the dialog + Init(); + + // Help file name + myHelpFileName = "double_nodes_page.html"; + + // Signals and slots connections + connect(myGroupConstructors, SIGNAL(buttonClicked(int)), SLOT(onConstructorsClicked(int))); + + connect(mySelectButton1, SIGNAL (clicked()), this, SLOT(onEditCurrentArgument())); + connect(mySelectButton2, SIGNAL (clicked()), this, SLOT(onEditCurrentArgument())); + connect(mySelectButton3, SIGNAL (clicked()), this, SLOT(onEditCurrentArgument())); + + connect(myButtonOk, SIGNAL(clicked()), this, SLOT(onOk())); + connect(myButtonClose, SIGNAL(clicked()), this, SLOT(onClose())); + connect(myButtonApply, SIGNAL(clicked()), this, SLOT(onApply())); + connect(myButtonHelp, SIGNAL(clicked()), this, SLOT(onHelp())); + + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionChanged())); + + connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(onDeactivate())); + connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(onClose())); +} + +/*! + \brief Destructor +*/ +SMESHGUI_DuplicateNodesDlg::~SMESHGUI_DuplicateNodesDlg() +{ +} + +/*! + \brief Destructor +*/ +void SMESHGUI_DuplicateNodesDlg::Init() +{ + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + myCheckBoxNewElemGroup->setChecked(true); + myCheckBoxNewNodeGroup->setChecked(true); + + // Set initial parameters + myBusy = false; + myCurrentLineEdit = myLineEdit1; + + myGroups1.clear(); + myGroups2.clear(); + myGroups3.clear(); + + // Set selection mode + mySelectionMgr->installFilter(new SMESH_TypeFilter(GROUP)); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + + // Set construction mode + int operationMode = myGroupConstructors->checkedId(); + if (operationMode < 0) { + // The dialog has been just displayed + operationMode = 0; + myGroupConstructors->button(0)->setChecked(true); + } + onConstructorsClicked(operationMode); +} + +/*! + \brief SLOT called to change the dialog operation mode. + \param constructorId id of the radio button in mode selector button group +*/ +void SMESHGUI_DuplicateNodesDlg::onConstructorsClicked (int constructorId) +{ + // Clear all fields + myLineEdit1->clear(); + myLineEdit2->clear(); + myLineEdit3->clear(); + + myGroups1.clear(); + myGroups2.clear(); + myGroups3.clear(); + + // Set the first field as current + myCurrentLineEdit = myLineEdit1; + myCurrentLineEdit->setFocus(); + + switch (constructorId) { + case 0: + { + // Set text to the group of arguments and to the first two labels + myGroupArguments->setTitle(tr("DUPLICATION_WITHOUT_ELEMS")); + myTextLabel1->setText(tr("GROUP_NODES_TO_DUPLICATE")); + myTextLabel2->setText(tr("GROUP_NODES_TO_REPLACE")); + + myCheckBoxNewElemGroup->hide(); + myCheckBoxNewNodeGroup->show(); + + // Hide the third field + myTextLabel3->hide(); + mySelectButton3->hide(); + myLineEdit3->hide(); + + break; + } + case 1: + { + // Set text to the group of arguments and to all the labels + myGroupArguments->setTitle(tr("DUPLICATION_WITH_ELEMS")); + myTextLabel1->setText(tr("GROUP_ELEMS_TO_DUPLICATE")); + myTextLabel2->setText(tr("GROUP_NODES_NOT_DUPLICATE")); + myTextLabel3->setText(tr("GROUP_ELEMS_TO_REPLACE")); + + myCheckBoxNewElemGroup->show(); + myCheckBoxNewNodeGroup->show(); + + // Show the third field + myTextLabel3->show(); + mySelectButton3->show(); + myLineEdit3->show(); + + break; + } + } + + // Process selection + onSelectionChanged(); +} + +/*! + \brief SLOT called to apply changes. +*/ +bool SMESHGUI_DuplicateNodesDlg::onApply() +{ + if ( mySMESHGUI->isActiveStudyLocked() || !isValid() ) + return false; + + BusyLocker lock( myBusy ); + + bool toCreateElemGroup = myCheckBoxNewElemGroup->isChecked(); + bool toCreateNodeGroup = myCheckBoxNewNodeGroup->isChecked(); + int operationMode = myGroupConstructors->checkedId(); + + // Apply changes + bool result = false; + SUIT_OverrideCursor aWaitCursor; + + try { + SMESH::SMESH_Mesh_var aMesh = myGroups1[0]->GetMesh(); + SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); + + if ( operationMode == 0 ) { + SMESH::ListOfGroups_var g1 = new SMESH::ListOfGroups(); + g1->length( myGroups1.count() ); + for ( int i = 0; i < myGroups1.count(); i++ ) + g1[i] = myGroups1[i]; + SMESH::ListOfGroups_var g2 = new SMESH::ListOfGroups(); + g2->length( myGroups2.count() ); + for ( int i = 0; i < myGroups2.count(); i++ ) + g2[i] = myGroups2[i]; + + if ( toCreateNodeGroup ) { + SMESH::SMESH_GroupBase_var aNewGroup = + aMeshEditor->DoubleNodeGroupsNew( g1.in(), g2.in() ); + result = !CORBA::is_nil( aNewGroup ); + } + else { + result = aMeshEditor->DoubleNodeGroups( g1.in(), g2.in() ); + } + } + else { + SMESH::ListOfGroups_var g1 = new SMESH::ListOfGroups(); + g1->length( myGroups1.count() ); + for ( int i = 0; i < myGroups1.count(); i++ ) + g1[i] = myGroups1[i]; + SMESH::ListOfGroups_var g2 = new SMESH::ListOfGroups(); + g2->length( myGroups2.count() ); + for ( int i = 0; i < myGroups2.count(); i++ ) + g2[i] = myGroups2[i]; + SMESH::ListOfGroups_var g3 = new SMESH::ListOfGroups(); + g3->length( myGroups3.count() ); + + for ( int i = 0; i < myGroups3.count(); i++ ) + g3[i] = myGroups3[i]; + if ( toCreateElemGroup || toCreateNodeGroup ) { + SMESH::ListOfGroups_var aNewGroups = + aMeshEditor->DoubleNodeElemGroups2New( g1.in(), g2.in(), g3.in(), + toCreateElemGroup, toCreateNodeGroup ); + result = ( aNewGroups[ !toCreateElemGroup ].in() ); + } + else { + result = aMeshEditor->DoubleNodeElemGroups( g1.in(), g2.in(), g3.in() ); + } + } + } + catch (const SALOME::SALOME_Exception& S_ex) { + SalomeApp_Tools::QtCatchCorbaException(S_ex); + } + catch ( const std::exception& exc ) { + INFOS( "Follow exception was cought:\n\t" << exc.what() ); + } + catch (...){ + INFOS( "Unknown exception was cought !!!" ); + } + + if (!result) { + SUIT_MessageBox::warning(this, + tr("SMESH_WRN_WARNING"), + tr("SMESH_OPERATION_FAILED")); + return false; + } + + // Update GUI + mySelectionMgr->clearSelected(); + SMESH::UpdateView(); + SMESHGUI::Modified(); + mySMESHGUI->updateObjBrowser(true); + + // Reinitialize the dialog + Init(); + + return true; +} + +/*! + \brief SLOT called to apply changes and close the dialog. +*/ +void SMESHGUI_DuplicateNodesDlg::onOk() +{ + if (onApply()) + onClose(); +} + +/*! + \brief SLOT called to close the dialog. +*/ +void SMESHGUI_DuplicateNodesDlg::onClose() +{ + disconnect(mySelectionMgr, 0, this, 0); + disconnect(mySMESHGUI, 0, this, 0); + mySMESHGUI->ResetState(); + mySelectionMgr->clearFilters(); + reject(); +} + +/*! + \brief SLOT called when selection changed. +*/ +void SMESHGUI_DuplicateNodesDlg::onSelectionChanged() +{ + if ( myBusy || !isEnabled() ) return; + + int operationMode = myGroupConstructors->checkedId(); + + SALOME_ListIO aList; + mySelectionMgr->selectedObjects( aList ); + //int aNbSel = aList.Extent(); + + QList aGroups; + + SALOME_ListIteratorOfListIO anIter ( aList ); + bool ok = true; + for ( ; anIter.More() && ok; anIter.Next()) { + SMESH::SMESH_GroupBase_var aGroup = SMESH::IObjectToInterface( anIter.Value() ); + // check group is selected + ok = !CORBA::is_nil( aGroup ); + // check groups of the same mesh are selected + if ( ok ) { + SMESH::SMESH_Mesh_var aMesh1; + if ( !aGroups.isEmpty() ) aMesh1 = aGroups[0]->GetMesh(); + SMESH::SMESH_Mesh_var aMesh2 = aGroup->GetMesh(); + ok = CORBA::is_nil( aMesh1 ) || aMesh1->_is_equivalent( aMesh2 ); + } + // check group of proper type is selected + if ( ok ) { + SMESH::ElementType aGroupType = aGroup->GetType(); + if ( operationMode == 0 ) { + ok = ( myCurrentLineEdit == myLineEdit1 && aGroupType == SMESH::NODE ) || + ( myCurrentLineEdit == myLineEdit2 && aGroupType != SMESH::NODE ); + } + else { + ok = ( myCurrentLineEdit == myLineEdit1 && ( aGroupType == SMESH::EDGE || + aGroupType == SMESH::FACE ) ) || + ( myCurrentLineEdit == myLineEdit2 && aGroupType == SMESH::NODE ) || + ( myCurrentLineEdit == myLineEdit3 && aGroupType != SMESH::NODE ); + } + } + if ( ok ) aGroups << aGroup; + } + + // Clear current field + myCurrentLineEdit->clear(); + + if ( ok && !aGroups.isEmpty() ) { + if ( myCurrentLineEdit == myLineEdit1 ) myGroups1 = aGroups; + else if ( myCurrentLineEdit == myLineEdit2 ) myGroups2 = aGroups; + else if ( myCurrentLineEdit == myLineEdit3 ) myGroups3 = aGroups; + CORBA::String_var name = aGroups[0]->GetName(); + myCurrentLineEdit->setText( aGroups.count() == 1 ? QString(name).trimmed() : + QObject::tr( "SMESH_OBJECTS_SELECTED" ).arg( aGroups.count() ) ); + } + else { + if ( myCurrentLineEdit == myLineEdit1 ) myGroups1.clear(); + else if ( myCurrentLineEdit == myLineEdit2 ) myGroups2.clear(); + else if ( myCurrentLineEdit == myLineEdit3 ) myGroups3.clear(); + myCurrentLineEdit->clear(); + } + + // Enable/disable "Apply and Close" and "Apply" buttons + bool isDataValid = isValid(); + myButtonOk->setEnabled( isDataValid ); + myButtonApply->setEnabled( isDataValid ); +} + +/*! + \brief SLOT called when selection button clicked. +*/ +void SMESHGUI_DuplicateNodesDlg::onEditCurrentArgument() +{ + QPushButton* send = (QPushButton*)sender(); + + // Set current field for edition + if (send == mySelectButton1) { + myCurrentLineEdit = myLineEdit1; + } + else if (send == mySelectButton2) { + myCurrentLineEdit = myLineEdit2; + } + else if (send == mySelectButton3) { + myCurrentLineEdit = myLineEdit3; + } + + myCurrentLineEdit->setFocus(); + onSelectionChanged(); +} + +/*! + \brief Check if the input data is valid. + \return \c true id the data is valid +*/ +bool SMESHGUI_DuplicateNodesDlg::isValid() +{ + return myGroupConstructors->checkedId() == 1 ? + ( !myGroups1.isEmpty() && !myGroups3.isEmpty() ) : + ( !myGroups1.isEmpty() ); +} + + +/*! + \brief SLOT called when dialog shoud be deativated. +*/ +void SMESHGUI_DuplicateNodesDlg::onDeactivate() +{ + if (isEnabled()) { + mySelectionMgr->clearFilters(); + setEnabled(false); + mySMESHGUI->ResetState(); + mySMESHGUI->SetActiveDialogBox(0); + } +} + +/*! + \brief Receive dialog enter events. + Activates the dialog when the mouse cursor enters. + Reimplemented from QWidget class. +*/ +void SMESHGUI_DuplicateNodesDlg::enterEvent (QEvent*) +{ + if ( !isEnabled() ) { + mySMESHGUI->EmitSignalDeactivateDialog(); + setEnabled(true); + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + // Set selection mode + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySelectionMgr->installFilter(new SMESH_TypeFilter (GROUP)); + } +} + +/*! + \brief Receive close events. + Reimplemented from QWidget class. +*/ +void SMESHGUI_DuplicateNodesDlg::closeEvent (QCloseEvent*) +{ + onClose(); +} + +/*! + \brief Receive key press events. + Reimplemented from QWidget class. +*/ +void SMESHGUI_DuplicateNodesDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( e->isAccepted() ) + return; + + if ( e->key() == Qt::Key_F1 ) { + e->accept(); + onHelp(); + } +} + +/*! + \brief Show the dialog help page. +*/ +void SMESHGUI_DuplicateNodesDlg::onHelp() +{ + LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + else { + QString platform; +#ifdef WIN32 + platform = "winapplication"; +#else + platform = "application"; +#endif + SUIT_MessageBox::warning(this, tr("WRN_WARNING"), + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); + } +} diff --git a/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.h b/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.h new file mode 100644 index 000000000..2714ba093 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_DuplicateNodesDlg.h @@ -0,0 +1,120 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_DuplicateNodesDlg.h +// Author : Michael ZORIN, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_DUPLICATENODESDLG_H +#define SMESHGUI_DUPLICATENODESDLG_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +// Qt includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) + +class QButtonGroup; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QCheckBox; + +class LightApp_SelectionMgr; + +class SMESHGUI; + +/*! + \class SMESHGUI_DuplicateNodesDlg + \brief Dialog for duplication of nodes. +*/ +class SMESHGUI_EXPORT SMESHGUI_DuplicateNodesDlg : public QDialog +{ + Q_OBJECT + +public: + SMESHGUI_DuplicateNodesDlg( SMESHGUI* ); + ~SMESHGUI_DuplicateNodesDlg(); + +private: + void Init(); + + bool isValid(); + + void closeEvent( QCloseEvent* ); + void enterEvent( QEvent* ); + void keyPressEvent( QKeyEvent* ); + +private slots: + void onConstructorsClicked( int ); + + void onOk(); + void onClose(); + bool onApply(); + void onHelp(); + + void onEditCurrentArgument(); + void onSelectionChanged(); + + void onDeactivate(); + +private: + QLineEdit* myCurrentLineEdit; + + QButtonGroup* myGroupConstructors; + + QGroupBox* myGroupArguments; + QLabel* myTextLabel1; + QLabel* myTextLabel2; + QLabel* myTextLabel3; + QPushButton* mySelectButton1; + QPushButton* mySelectButton2; + QPushButton* mySelectButton3; + QLineEdit* myLineEdit1; + QLineEdit* myLineEdit2; + QLineEdit* myLineEdit3; + QCheckBox* myCheckBoxNewElemGroup; + QCheckBox* myCheckBoxNewNodeGroup; + + QPushButton* myButtonOk; + QPushButton* myButtonApply; + QPushButton* myButtonClose; + QPushButton* myButtonHelp; + + SMESHGUI* mySMESHGUI; + LightApp_SelectionMgr* mySelectionMgr; + + QList myGroups1; + QList myGroups2; + QList myGroups3; + + bool myBusy; + + QString myHelpFileName; +}; + +#endif // SMESHGUI_DUPLICATENODESDLG_H diff --git a/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.cxx b/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.cxx index a8cffc9e3..4ec84dce9 100644 --- a/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.cxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ExtrusionAlongPathDlg.cxx // Author : Vadim SANDLER, Open CASCADE S.A.S. // SMESH includes -// + #include "SMESHGUI_ExtrusionAlongPathDlg.h" #include "SMESHGUI.h" @@ -33,6 +34,7 @@ #include "SMESHGUI_SpinBox.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" #include #include @@ -108,8 +110,7 @@ private: // purpose : constructor //================================================================================= SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), + : SMESHGUI_PreviewDlg( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), myFilterDlg( 0 ) { @@ -166,8 +167,9 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod ElementsLineEdit = new QLineEdit(GroupArguments); ElementsLineEdit->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + ElementsLineEdit->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Controls for the whole mesh selection MeshCheck = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -186,15 +188,6 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod PathMeshLineEdit = new QLineEdit(PathGrp); PathMeshLineEdit->setReadOnly(true); - // Controls for path shape selection - QLabel* PathShapeLab = new QLabel(tr("SMESH_PATH_SHAPE"), PathGrp); - - SelectPathShapeButton = new QToolButton(PathGrp); - SelectPathShapeButton->setIcon(selectImage); - - PathShapeLineEdit = new QLineEdit(PathGrp); - PathShapeLineEdit->setReadOnly(true); - // Controls for path starting point selection QLabel* StartPointLab = new QLabel(tr("SMESH_PATH_START"), PathGrp); @@ -208,12 +201,9 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod PathGrpLayout->addWidget(PathMeshLab, 0, 0); PathGrpLayout->addWidget(SelectPathMeshButton, 0, 1); PathGrpLayout->addWidget(PathMeshLineEdit, 0, 2); - PathGrpLayout->addWidget(PathShapeLab, 1, 0); - PathGrpLayout->addWidget(SelectPathShapeButton, 1, 1); - PathGrpLayout->addWidget(PathShapeLineEdit, 1, 2); - PathGrpLayout->addWidget(StartPointLab, 2, 0); - PathGrpLayout->addWidget(SelectStartPointButton, 2, 1); - PathGrpLayout->addWidget(StartPointLineEdit, 2, 2); + PathGrpLayout->addWidget(StartPointLab, 1, 0); + PathGrpLayout->addWidget(SelectStartPointButton, 1, 1); + PathGrpLayout->addWidget(StartPointLineEdit, 1, 2); BasePointGrp = new QGroupBox(tr("SMESH_BASE_POINT"), GroupArguments); BasePointGrp->setCheckable(true); @@ -272,16 +262,20 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod MakeGroupsCheck = new QCheckBox(tr("SMESH_MAKE_GROUPS"), GroupArguments); MakeGroupsCheck->setChecked(true); + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + // layouting GroupArgumentsLayout->addWidget(ElementsLab, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(ElementsLineEdit, 0, 2); - GroupArgumentsLayout->addWidget(filterBtn, 0, 3); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 3); GroupArgumentsLayout->addWidget(MeshCheck, 1, 0, 1, 4); GroupArgumentsLayout->addWidget(PathGrp, 2, 0, 1, 4); GroupArgumentsLayout->addWidget(BasePointGrp, 3, 0, 1, 4); GroupArgumentsLayout->addWidget(AnglesGrp, 4, 0, 1, 4); - GroupArgumentsLayout->addWidget(MakeGroupsCheck, 5, 0, 1, 4); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 5, 0, 1, 4); + GroupArgumentsLayout->addWidget(MakeGroupsCheck, 6, 0, 1, 4); /***************************************************************/ // common buttons group box @@ -319,10 +313,10 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod /***************************************************************/ // Initialisations - XSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - YSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - ZSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - AngleSpin->RangeStepAndValidator(-180.0, 180.0, 5.0, 3); + XSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + YSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + ZSpin->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + AngleSpin->RangeStepAndValidator(-180.0, 180.0, 5.0, "angle_precision"); mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); @@ -337,7 +331,8 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); myElementsFilter = new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR); - myPathMeshFilter = new SMESH_TypeFilter (MESH); + //myPathMeshFilter = new SMESH_TypeFilter (MESH); + myPathMeshFilter = new SMESH_TypeFilter(MESHorSUBMESH); myHelpFileName = "extrusion_along_path_page.html"; @@ -357,7 +352,6 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod connect(SelectElementsButton, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); connect(SelectPathMeshButton, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); - connect(SelectPathShapeButton, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); connect(SelectStartPointButton, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); connect(SelectBasePointButton, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); @@ -366,12 +360,23 @@ SMESHGUI_ExtrusionAlongPathDlg::SMESHGUI_ExtrusionAlongPathDlg( SMESHGUI* theMod connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(reject())); connect(ElementsLineEdit, SIGNAL(textChanged(const QString&)), - SLOT(onTextChange(const QString&))); + SLOT(onTextChange(const QString&))); connect(StartPointLineEdit, SIGNAL(textChanged(const QString&)), - SLOT(onTextChange(const QString&))); + SLOT(onTextChange(const QString&))); connect(MeshCheck, SIGNAL(toggled(bool)), SLOT(onSelectMesh())); + connect(XSpin, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(YSpin, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(ZSpin, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(AddAngleButton, SIGNAL(clicked()), this, SLOT(toDisplaySimulation())); + connect(RemoveAngleButton, SIGNAL(clicked()), this, SLOT(toDisplaySimulation())); + connect(LinearAnglesCheck, SIGNAL(toggled(bool)), SLOT(onSelectMesh())); + + + //To Connect preview check box + connectPreviewControl(); + AnglesList->installEventFilter(this); ElementsLineEdit->installEventFilter(this); StartPointLineEdit->installEventFilter(this); @@ -405,12 +410,10 @@ void SMESHGUI_ExtrusionAlongPathDlg::Init (bool ResetControls) myMesh = SMESH::SMESH_Mesh::_nil(); myIDSource = SMESH::SMESH_IDSource::_nil(); myMeshActor = 0; - myPathMesh = SMESH::SMESH_Mesh::_nil(); - myPathShape = GEOM::GEOM_Object::_nil(); + myPath = SMESH::SMESH_IDSource::_nil(); ElementsLineEdit->clear(); PathMeshLineEdit->clear(); - PathShapeLineEdit->clear(); StartPointLineEdit->clear(); if (ResetControls) { @@ -422,6 +425,8 @@ void SMESHGUI_ExtrusionAlongPathDlg::Init (bool ResetControls) MeshCheck->setChecked(false); ConstructorsClicked(0); onSelectMesh(); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); } SetEditCurrentArgument(0); } @@ -436,6 +441,8 @@ void SMESHGUI_ExtrusionAlongPathDlg::ConstructorsClicked (int type) disconnect(mySelectionMgr, 0, this, 0); + hidePreview(); + if (type == 0) GroupArguments->setTitle(tr("EXTRUSION_1D")); else if (type == 1) @@ -454,19 +461,19 @@ void SMESHGUI_ExtrusionAlongPathDlg::ConstructorsClicked (int type) SMESH::SetPointRepresentation(false); if (MeshCheck->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myElementsFilter); } else { if (type == 0) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + } if (type == 1) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + } } } connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); @@ -483,50 +490,21 @@ bool SMESHGUI_ExtrusionAlongPathDlg::ClickOnApply() if (mySMESHGUI->isActiveStudyLocked()) return false; - if (myMesh->_is_nil() || MeshCheck->isChecked() && myIDSource->_is_nil() || - !myMeshActor || myPathMesh->_is_nil() || myPathShape->_is_nil()) + //if (myMesh->_is_nil() || MeshCheck->isChecked() && myIDSource->_is_nil() || + // !myMeshActor || myPathMesh->_is_nil() || myPathShape->_is_nil()) + if ( myMesh->_is_nil() || (MeshCheck->isChecked() && myIDSource->_is_nil()) || + /*!myMeshActor ||*/ myPath->_is_nil() ) return false; if (!isValid()) return false; - SMESH::long_array_var anElementsId = new SMESH::long_array; - - if (!MeshCheck->isChecked()) { - // If "Select whole mesh, submesh or group" check box is off -> - // use only elements of given type selected by user - - SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh(); - if (aMesh) { - QStringList aListElementsId = ElementsLineEdit->text().split(" ", QString::SkipEmptyParts); - anElementsId = new SMESH::long_array; - anElementsId->length(aListElementsId.count()); - bool bOk; - int j = 0; - for (int i = 0; i < aListElementsId.count(); i++) { - long ind = aListElementsId[ i ].toLong(&bOk); - if (bOk) { - const SMDS_MeshElement* e = aMesh->FindElement(ind); - if (e) { - bool typeMatch = Elements1dRB->isChecked() && e->GetType() == SMDSAbs_Edge || - Elements2dRB->isChecked() && e->GetType() == SMDSAbs_Face; - if (typeMatch) - anElementsId[ j++ ] = ind; - } - } - } - anElementsId->length(j); - } - - if (anElementsId->length() <= 0) { - return false; - } - } + SMESH::long_array_var anElementsId = getSelectedElements(); if (StartPointLineEdit->text().trimmed().isEmpty()) { return false; } - + bool bOk; long aNodeStart = StartPointLineEdit->text().toLong(&bOk); if (!bOk) { @@ -534,19 +512,13 @@ bool SMESHGUI_ExtrusionAlongPathDlg::ClickOnApply() } QStringList aParameters; + + //get angles + SMESH::double_array_var anAngles = getAngles(); + + for (int i = 0; i < myAnglesList.count(); i++) + aParameters << AnglesList->item(i)->text(); - // get angles - SMESH::double_array_var anAngles = new SMESH::double_array; - if (AnglesGrp->isChecked()) { - anAngles->length(myAnglesList.count()); - int j = 0; - for (int i = 0; i < myAnglesList.count(); i++) { - double angle = myAnglesList[i]; - anAngles[ j++ ] = angle*PI/180; - aParameters << AnglesList->item(i)->text(); - } - anAngles->length(j); - } // get base point SMESH::PointStruct aBasePoint; @@ -562,88 +534,63 @@ bool SMESHGUI_ExtrusionAlongPathDlg::ClickOnApply() try { SUIT_OverrideCursor wc; - SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - if ( LinearAnglesCheck->isChecked() ) - anAngles = aMeshEditor->LinearAnglesVariation( myPathMesh, myPathShape, anAngles ); + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); SMESH::SMESH_MeshEditor::Extrusion_Error retVal; - if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) { - if( MeshCheck->isChecked() ) { - if( GetConstructorId() == 0 ) - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionAlongPathObject1DMakeGroups(myIDSource, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint, retVal); - else - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionAlongPathObject2DMakeGroups(myIDSource, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint, retVal); - } - else - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionAlongPathMakeGroups(anElementsId, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint, retVal); + + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + + bool NeedGroups = ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ); + SMESH::ElementType ElemType = SMESH::FACE; + if( GetConstructorId() == 0 ) + ElemType = SMESH::EDGE; + if( !MeshCheck->isChecked() ) { + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionAlongPathX(anElementsId, myPath, aNodeStart, AnglesGrp->isChecked(), + anAngles, LinearAnglesCheck->isChecked(), + BasePointGrp->isChecked(), aBasePoint, + NeedGroups, ElemType, retVal); } else { - if( MeshCheck->isChecked() ) { - if( GetConstructorId() == 0 ) - retVal = aMeshEditor->ExtrusionAlongPathObject1D(myIDSource, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint); - else - retVal = aMeshEditor->ExtrusionAlongPathObject2D(myIDSource, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint); - } - else - retVal = aMeshEditor->ExtrusionAlongPath(anElementsId, myPathMesh, - myPathShape, aNodeStart, - AnglesGrp->isChecked(), anAngles, - BasePointGrp->isChecked(), aBasePoint); + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionAlongPathObjX(myIDSource, myPath, aNodeStart, AnglesGrp->isChecked(), + anAngles, LinearAnglesCheck->isChecked(), + BasePointGrp->isChecked(), aBasePoint, + NeedGroups, ElemType, retVal); } - if( retVal == SMESH::SMESH_MeshEditor::EXTR_OK ) - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - //wc.stop(); wc.suspend(); switch (retVal) { case SMESH::SMESH_MeshEditor::EXTR_NO_ELEMENTS: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("NO_ELEMENTS_SELECTED")); + tr("SMESH_ERROR"), + tr("NO_ELEMENTS_SELECTED")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_PATH_NOT_EDGE: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("SELECTED_PATH_IS_NOT_EDGE")); + tr("SMESH_ERROR"), + tr("SELECTED_PATH_IS_NOT_EDGE")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_BAD_PATH_SHAPE: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("BAD_SHAPE_TYPE")); + tr("SMESH_ERROR"), + tr("BAD_SHAPE_TYPE")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_BAD_STARTING_NODE: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("EXTR_BAD_STARTING_NODE")); + tr("SMESH_ERROR"), + tr("EXTR_BAD_STARTING_NODE")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_BAD_ANGLES_NUMBER: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("WRONG_ANGLES_NUMBER")); + tr("SMESH_ERROR"), + tr("WRONG_ANGLES_NUMBER")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_CANT_GET_TANGENT: SUIT_MessageBox::warning(this, - tr("SMESH_ERROR"), - tr("CANT_GET_TANGENT")); + tr("SMESH_ERROR"), + tr("CANT_GET_TANGENT")); return false; break; case SMESH::SMESH_MeshEditor::EXTR_OK: break; @@ -653,7 +600,11 @@ bool SMESHGUI_ExtrusionAlongPathDlg::ClickOnApply() } //mySelectionMgr->clearSelected(); - SMESH::Update( myMeshActor->getIO(), myMeshActor->GetVisibility() ); + if ( myMeshActor ) + SMESH::Update( myMeshActor->getIO(), myMeshActor->GetVisibility() ); + + SMESHGUI::Modified(); + if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) mySMESHGUI->updateObjBrowser(true); // new groups may appear //SMESH::UpdateView(); @@ -689,10 +640,10 @@ void SMESHGUI_ExtrusionAlongPathDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -751,49 +702,50 @@ void SMESHGUI_ExtrusionAlongPathDlg::onTextChange (const QString& theNewText) const Handle(SALOME_InteractiveObject)& anIO = myMeshActor->getIO(); TColStd_MapOfInteger newIndices; for (int i = 0; i < aListId.count(); i++) { - long ind = aListId[ i ].toLong(&bOk); - if (bOk) { - const SMDS_MeshElement* e = aMesh->FindElement(ind); - if (e) { - // check also type of element - bool typeMatch = Elements1dRB->isChecked() && e->GetType() == SMDSAbs_Edge || - Elements2dRB->isChecked() && e->GetType() == SMDSAbs_Face; - if (typeMatch) - newIndices.Add(e->GetID()); - } - } + long ind = aListId[ i ].toLong(&bOk); + if (bOk) { + const SMDS_MeshElement* e = aMesh->FindElement(ind); + if (e) { + // check also type of element + bool typeMatch = (Elements1dRB->isChecked() && e->GetType() == SMDSAbs_Edge) || + (Elements2dRB->isChecked() && e->GetType() == SMDSAbs_Face); + if (typeMatch) + newIndices.Add(e->GetID()); + } + } } mySelector->AddOrRemoveIndex(anIO, newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( anIO, true, true ); + aViewWindow->highlight( anIO, true, true ); } - } else if (send == StartPointLineEdit && + } + else if (send == StartPointLineEdit && myEditCurrentArgument == StartPointLineEdit) { - if (!myPathMesh->_is_nil()) { - SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPathMesh); + if (!myPath->_is_nil()) { + SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPath); SMDS_Mesh* aMesh = 0; if (aPathActor) - aMesh = aPathActor->GetObject()->GetMesh(); + aMesh = aPathActor->GetObject()->GetMesh(); if (aMesh) { - //mySelectionMgr->clearSelected(); - //mySelectionMgr->AddIObject(aPathActor->getIO()); + //mySelectionMgr->clearSelected(); + //mySelectionMgr->AddIObject(aPathActor->getIO()); SALOME_ListIO aList; aList.Append(aPathActor->getIO()); mySelectionMgr->setSelectedObjects(aList, false); - bool bOk; - long ind = theNewText.toLong(&bOk); - if (bOk) { - const SMDS_MeshNode* n = aMesh->FindNode(ind); - if (n) { - //if (!mySelectionMgr->IsIndexSelected(aPathActor->getIO(), n->GetID())) { + bool bOk; + long ind = theNewText.toLong(&bOk); + if (bOk) { + const SMDS_MeshNode* n = aMesh->FindNode(ind); + if (n) { + //if (!mySelectionMgr->IsIndexSelected(aPathActor->getIO(), n->GetID())) { TColStd_MapOfInteger newIndices; - newIndices.Add(n->GetID()); - mySelector->AddOrRemoveIndex( aPathActor->getIO(), newIndices, false ); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( aPathActor->getIO(), true, true ); - } - } + newIndices.Add(n->GetID()); + mySelector->AddOrRemoveIndex( aPathActor->getIO(), newIndices, false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( aPathActor->getIO(), true, true ); + } + } } } } @@ -843,7 +795,7 @@ void SMESHGUI_ExtrusionAlongPathDlg::SelectionIntoArgument() } // find actor myMeshActor = SMESH::FindActorByObject(myMesh); - if (!myMeshActor) + if (!myMeshActor && !MeshCheck->isChecked()) return; if (MeshCheck->isChecked()) { @@ -862,57 +814,37 @@ void SMESHGUI_ExtrusionAlongPathDlg::SelectionIntoArgument() SMESH::GetNameOfSelectedElements(mySelector, IO, aString); ElementsLineEdit->setText(aString); } - } else if (myEditCurrentArgument == PathMeshLineEdit) { + } + else if (myEditCurrentArgument == PathMeshLineEdit) { // we are now selecting path mesh // reset PathMeshLineEdit->clear(); - myPathMesh = SMESH::SMESH_Mesh::_nil(); - PathShapeLineEdit->clear(); - myPathShape = GEOM::GEOM_Object::_nil(); + myPath = SMESH::SMESH_IDSource::_nil(); StartPointLineEdit->clear(); - + // try to get mesh from selection Handle(SALOME_InteractiveObject) IO = aList.First(); - myPathMesh = SMESH::IObjectToInterface(IO); - if(myPathMesh->_is_nil()) + myPath = SMESH::IObjectToInterface(IO); + if( myPath->_is_nil() ) return; - + QString aString; SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aString); PathMeshLineEdit->setText(aString); - } else if (myEditCurrentArgument == PathShapeLineEdit) { - // we are now selecting path mesh - // reset - PathShapeLineEdit->clear(); - myPathShape = GEOM::GEOM_Object::_nil(); - StartPointLineEdit->clear(); - - // return if path mesh is not yet selected - if (myPathMesh->_is_nil()) - return; - - // try to get shape from selection - Handle(SALOME_InteractiveObject) IO = aList.First(); - myPathShape = SMESH::IObjectToInterface(IO); - if (myPathShape->_is_nil()) - return; - - QString aString; - SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aString); - PathShapeLineEdit->setText(aString); - } else if (myEditCurrentArgument == StartPointLineEdit) { + } + else if (myEditCurrentArgument == StartPointLineEdit) { // we are now selecting start point of path // reset StartPointLineEdit->clear(); // return if path mesh or path shape is not yet selected - if (myPathMesh->_is_nil() || myPathShape->_is_nil()) + if( myPath->_is_nil() ) return; // try to get shape from selection Handle(SALOME_InteractiveObject) IO = aList.First(); - SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPathMesh); + SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPath); if ( !aPathActor ) return; @@ -937,10 +869,10 @@ void SMESHGUI_ExtrusionAlongPathDlg::SelectionIntoArgument() TopoDS_Vertex aVertex; if (!aGeomObj->_is_nil()) { if (aGeomObj->IsShape() && GEOMBase::GetShape(aGeomObj, aVertex) && !aVertex.IsNull()) { - gp_Pnt aPnt = BRep_Tool::Pnt(aVertex); - XSpin->SetValue(aPnt.X()); - YSpin->SetValue(aPnt.Y()); - ZSpin->SetValue(aPnt.Z()); + gp_Pnt aPnt = BRep_Tool::Pnt(aVertex); + XSpin->SetValue(aPnt.X()); + YSpin->SetValue(aPnt.Y()); + ZSpin->SetValue(aPnt.Z()); } return; } @@ -972,6 +904,7 @@ void SMESHGUI_ExtrusionAlongPathDlg::SelectionIntoArgument() YSpin->SetValue(n->Y()); ZSpin->SetValue(n->Z()); } + onDisplaySimulation(true); } //================================================================================= @@ -983,7 +916,6 @@ void SMESHGUI_ExtrusionAlongPathDlg::SetEditCurrentArgument() QToolButton* send = (QToolButton*)sender(); if (send != SelectElementsButton && send != SelectPathMeshButton && - send != SelectPathShapeButton && send != SelectStartPointButton && send != SelectBasePointButton) return; @@ -997,7 +929,7 @@ void SMESHGUI_ExtrusionAlongPathDlg::SetEditCurrentArgument() void SMESHGUI_ExtrusionAlongPathDlg::SetEditCurrentArgument (QToolButton* button) { disconnect(mySelectionMgr, 0, this, 0); - mySelectionMgr->clearSelected(); + // mySelectionMgr->clearSelected(); mySelectionMgr->clearFilters(); SMESH::SetPickable(); @@ -1006,19 +938,19 @@ void SMESHGUI_ExtrusionAlongPathDlg::SetEditCurrentArgument (QToolButton* button SMESH::SetPointRepresentation(false); if (MeshCheck->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myElementsFilter); } else { if (Elements1dRB->isChecked()) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + } else if (Elements2dRB->isChecked()) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + } } } else if (button == SelectPathMeshButton) { myEditCurrentArgument = PathMeshLineEdit; @@ -1027,31 +959,16 @@ void SMESHGUI_ExtrusionAlongPathDlg::SetEditCurrentArgument (QToolButton* button aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myPathMeshFilter); } - else if (button == SelectPathShapeButton) { - myEditCurrentArgument = PathShapeLineEdit; - SMESH::SetPointRepresentation(false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); - - if (!myPathMesh->_is_nil()) { - GEOM::GEOM_Object_var aMainShape = myPathMesh->GetShapeToMesh(); - SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPathMesh); - - if (!aMainShape->_is_nil() && aPathActor) - mySelectionMgr->installFilter(new SMESH_NumberFilter ("GEOM", TopAbs_SHAPE, -1, - TopAbs_EDGE, aMainShape)); - //SMESH::SetPickable(aPathActor); - } - } else if (button == SelectStartPointButton) { myEditCurrentArgument = StartPointLineEdit; - if (!myPathMesh->_is_nil()) { - SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPathMesh); + //if (!myPathMesh->_is_nil()) { + if (!myPath->_is_nil()) { + SMESH_Actor* aPathActor = SMESH::FindActorByObject(myPath); if (aPathActor) { - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); - SMESH::SetPickable(aPathActor); + SMESH::SetPointRepresentation(true); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + SMESH::SetPickable(aPathActor); } } } @@ -1136,6 +1053,7 @@ void SMESHGUI_ExtrusionAlongPathDlg::onSelectMesh() ElementsLineEdit->setValidator(toSelectMesh ? 0 : myIdValidator); ElementsLab->setText(toSelectMesh ? tr("SMESH_NAME") : tr("SMESH_ID_ELEMENTS")); ElementsLineEdit->clear(); + myFilterBtn->setEnabled(!toSelectMesh); SetEditCurrentArgument(SelectElementsButton); } @@ -1195,21 +1113,21 @@ bool SMESHGUI_ExtrusionAlongPathDlg::eventFilter (QObject* object, QEvent* event QKeyEvent* ke = (QKeyEvent*)event; if (object == AnglesList) { if (ke->key() == Qt::Key_Delete) - OnAngleRemoved(); + OnAngleRemoved(); } } else if (event->type() == QEvent::FocusIn) { if (object == ElementsLineEdit) { if (myEditCurrentArgument != ElementsLineEdit) - SetEditCurrentArgument(SelectElementsButton); + SetEditCurrentArgument(SelectElementsButton); } else if (object == StartPointLineEdit) { if (myEditCurrentArgument != StartPointLineEdit) - SetEditCurrentArgument(SelectStartPointButton); + SetEditCurrentArgument(SelectStartPointButton); } else if (object == XSpin->editor() || object == YSpin->editor() || object == ZSpin->editor()) { if (myEditCurrentArgument != XSpin) - SetEditCurrentArgument(SelectBasePointButton); + SetEditCurrentArgument(SelectBasePointButton); } } return QDialog::eventFilter(object, event); @@ -1237,6 +1155,12 @@ void SMESHGUI_ExtrusionAlongPathDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_ExtrusionAlongPathDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) { QList types; @@ -1287,10 +1211,162 @@ void SMESHGUI_ExtrusionAlongPathDlg::updateLinearAngles() enableLinear = false; anItem->text().toDouble(&enableLinear); if( !enableLinear ) - break; + break; } } if( !enableLinear ) LinearAnglesCheck->setChecked( false ); LinearAnglesCheck->setEnabled( enableLinear ); } + +//================================================================================= +// function : isValuesValid() +// purpose : Return true in case if values entered into dialog are valid +//================================================================================= +bool SMESHGUI_ExtrusionAlongPathDlg::isValuesValid() { + + if ( (MeshCheck->isChecked() && myIDSource->_is_nil()) || + myMesh->_is_nil() || + myPath->_is_nil() ) + return false; + + if(!MeshCheck->isChecked()) { + QStringList aListElementsId = ElementsLineEdit->text().split(" ", QString::SkipEmptyParts); + if(aListElementsId.count() <= 0) + return false; + } + + bool bOk; + StartPointLineEdit->text().toLong(&bOk); + if (!bOk) { + return false; + } + + return true; +} + + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_ExtrusionAlongPathDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + if(isValid() && isValuesValid()) { + + //Get selected elements: + SMESH::long_array_var anElementsId = getSelectedElements(); + + // get angles + SMESH::double_array_var anAngles = getAngles(); + + // get base point + SMESH::PointStruct aBasePoint; + if (BasePointGrp->isChecked()) { + aBasePoint.x = XSpin->GetValue(); + aBasePoint.y = YSpin->GetValue(); + aBasePoint.z = ZSpin->GetValue(); + } + bool bOk; + long aNodeStart = StartPointLineEdit->text().toLong(&bOk); + if (bOk) { + + try { + SUIT_OverrideCursor wc; + + SMESH::SMESH_MeshEditor::Extrusion_Error retVal; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + bool NeedGroups = false; + SMESH::ElementType ElemType = SMESH::FACE; + if( GetConstructorId() == 0 ) + ElemType = SMESH::EDGE; + if( !MeshCheck->isChecked() ) { + aMeshEditor->ExtrusionAlongPathX(anElementsId, myPath, aNodeStart, AnglesGrp->isChecked(), + anAngles, LinearAnglesCheck->isChecked(), + BasePointGrp->isChecked(), aBasePoint, + NeedGroups, ElemType, retVal); + } + else { + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionAlongPathObjX(myIDSource, myPath, aNodeStart, AnglesGrp->isChecked(), + anAngles, LinearAnglesCheck->isChecked(), + BasePointGrp->isChecked(), aBasePoint, + NeedGroups, ElemType, retVal); + } + + wc.suspend(); + if( retVal == SMESH::SMESH_MeshEditor::EXTR_OK ) { + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + } else { + hidePreview(); + } + + } catch (...) { + hidePreview(); + } + } else { + hidePreview(); + } + + } else { + hidePreview(); + } + } else { + hidePreview(); + } +} + + +//================================================================================= +// function : getSelectedElements +// purpose : return list of the selected elements +//================================================================================= +SMESH::long_array_var SMESHGUI_ExtrusionAlongPathDlg::getSelectedElements() { + + // If "Select whole mesh, submesh or group" check box is off -> + // use only elements of given type selected by user + SMESH::long_array_var anElementsId = new SMESH::long_array; + if (!MeshCheck->isChecked()) { + + SMDS_Mesh* aMesh; + if ( myMeshActor ) + aMesh = myMeshActor->GetObject()->GetMesh(); + + if (aMesh) { + QStringList aListElementsId = ElementsLineEdit->text().split(" ", QString::SkipEmptyParts); + anElementsId = new SMESH::long_array; + anElementsId->length(aListElementsId.count()); + bool bOk; + int j = 0; + for (int i = 0; i < aListElementsId.count(); i++) { + long ind = aListElementsId[ i ].toLong(&bOk); + if (bOk) { + const SMDS_MeshElement* e = aMesh->FindElement(ind); + if (e) { + bool typeMatch = (Elements1dRB->isChecked() && e->GetType() == SMDSAbs_Edge) || + (Elements2dRB->isChecked() && e->GetType() == SMDSAbs_Face); + if (typeMatch) + anElementsId[ j++ ] = ind; + } + } + } + anElementsId->length(j); + } + } + return anElementsId; +} + +SMESH::double_array_var SMESHGUI_ExtrusionAlongPathDlg::getAngles() { + SMESH::double_array_var anAngles = new SMESH::double_array; + if (AnglesGrp->isChecked()) { + anAngles->length(myAnglesList.count()); + int j = 0; + for (int i = 0; i < myAnglesList.count(); i++) { + double angle = myAnglesList[i]; + anAngles[ j++ ] = angle*M_PI/180.; + } + anAngles->length(j); + } + return anAngles; +} diff --git a/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.h b/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.h index 4e1cbcb9c..b02aeb7a2 100644 --- a/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.h +++ b/src/SMESHGUI/SMESHGUI_ExtrusionAlongPathDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ExtrusionAlongPathDlg.h // Author : Vadim SANDLER, Open CASCADE S.A.S. @@ -28,9 +29,7 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include +#include "SMESHGUI_PreviewDlg.h" // IDL includes #include @@ -59,7 +58,7 @@ class SUIT_SelectionFilter; // class : SMESHGUI_ExtrusionAlongPathDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_ExtrusionAlongPathDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_ExtrusionAlongPathDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -80,10 +79,13 @@ private: void SetEditCurrentArgument( QToolButton* ); bool isValid(); + bool isValuesValid(); + + SMESH::long_array_var getSelectedElements(); + SMESH::double_array_var getAngles(); void updateLinearAngles(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ SVTK_Selector* mySelector; @@ -94,8 +96,9 @@ private: SMESH::SMESH_Mesh_var myMesh; SMESH_Actor* myMeshActor; SMESH::SMESH_IDSource_var myIDSource; - SMESH::SMESH_Mesh_var myPathMesh; - GEOM::GEOM_Object_var myPathShape; + //SMESH::SMESH_Mesh_var myPathMesh; + SMESH::SMESH_IDSource_var myPath; + //GEOM::GEOM_Object_var myPathShape; SUIT_SelectionFilter* myElementsFilter; SUIT_SelectionFilter* myPathMeshFilter; int myType; @@ -115,8 +118,8 @@ private: QGroupBox* PathGrp; QToolButton* SelectPathMeshButton; QLineEdit* PathMeshLineEdit; - QToolButton* SelectPathShapeButton; - QLineEdit* PathShapeLineEdit; + //QToolButton* SelectPathShapeButton; + //QLineEdit* PathShapeLineEdit; QToolButton* SelectStartPointButton; QLineEdit* StartPointLineEdit; QCheckBox* LinearAnglesCheck; @@ -140,10 +143,12 @@ private: QString myHelpFileName; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; protected slots: void reject(); + virtual void onDisplaySimulation( bool ); private slots: void ConstructorsClicked( int ); diff --git a/src/SMESHGUI/SMESHGUI_ExtrusionDlg.cxx b/src/SMESHGUI/SMESHGUI_ExtrusionDlg.cxx index 0626e4575..c75af4f45 100644 --- a/src/SMESHGUI/SMESHGUI_ExtrusionDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ExtrusionDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ExtrusionDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -33,6 +34,7 @@ #include "SMESHGUI_SpinBox.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" #include #include @@ -87,8 +89,7 @@ // purpose : constructor //================================================================================= SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), + : SMESHGUI_PreviewDlg( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), myFilterDlg( 0 ), mySelectedObject(SMESH::SMESH_IDSource::_nil()) @@ -96,6 +97,7 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) QPixmap image0 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_EDGE"))); QPixmap image1 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_TRIANGLE"))); QPixmap image2 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT"))); + QPixmap image3 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_NODE"))); setModal( false ); setAttribute( Qt::WA_DeleteOnClose, true ); @@ -113,16 +115,20 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) ConstructorsBoxLayout->setSpacing(SPACING); ConstructorsBoxLayout->setMargin(MARGIN); + RadioButton0= new QRadioButton(ConstructorsBox); + RadioButton0->setIcon(image3); RadioButton1= new QRadioButton(ConstructorsBox); RadioButton1->setIcon(image0); RadioButton2= new QRadioButton(ConstructorsBox); RadioButton2->setIcon(image1); + ConstructorsBoxLayout->addWidget(RadioButton0); ConstructorsBoxLayout->addWidget(RadioButton1); ConstructorsBoxLayout->addWidget(RadioButton2); - GroupConstructors->addButton(RadioButton1, 0); - GroupConstructors->addButton(RadioButton2, 1); + GroupConstructors->addButton(RadioButton0, 0); + GroupConstructors->addButton(RadioButton1, 1); + GroupConstructors->addButton(RadioButton2, 2); /***************************************************************/ GroupButtons = new QGroupBox(this); @@ -149,7 +155,7 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) GroupButtonsLayout->addWidget(buttonHelp); /***************************************************************/ - GroupArguments = new QGroupBox(tr("EXTRUSION_1D"), this); + GroupArguments = new QGroupBox(tr("EXTRUSION_0D"), this); QGridLayout* GroupArgumentsLayout = new QGridLayout(GroupArguments); GroupArgumentsLayout->setSpacing(SPACING); GroupArgumentsLayout->setMargin(MARGIN); @@ -164,16 +170,21 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); + RadioButton3 = new QRadioButton(GroupArguments); + RadioButton3->setText( tr("SMESH_EXTRUSION_TO_DISTANCE") ); + RadioButton4 = new QRadioButton(GroupArguments); + RadioButton4->setText( tr("SMESH_EXTRUSION_ALONG_VECTOR") ); + //Control for the Distance selection TextLabelDistance = new QLabel(tr("SMESH_DISTANCE"), GroupArguments); - TextLabelVector = new QLabel(tr("SMESH_VECTOR"), GroupArguments); TextLabelDx = new QLabel(tr("SMESH_X"), GroupArguments); SpinBox_Dx = new SMESHGUI_SpinBox(GroupArguments); @@ -185,6 +196,8 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) // Controls for vector selection + TextLabelVector = new QLabel(tr("SMESH_VECTOR"), GroupArguments); + SelectVectorButton = new QPushButton(GroupArguments); SelectVectorButton->setIcon(image2); @@ -197,6 +210,9 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) TextLabelVz = new QLabel(tr("SMESH_DZ"), GroupArguments); SpinBox_Vz = new SMESHGUI_SpinBox(GroupArguments); + TextLabelDist = new QLabel(tr("SMESH_DISTANCE"), GroupArguments); + SpinBox_VDist = new SMESHGUI_SpinBox(GroupArguments); + // Controls for nb. steps defining TextLabelNbSteps = new QLabel(tr("SMESH_NUMBEROFSTEPS"), GroupArguments); SpinBox_NbSteps = new SalomeApp_IntSpinBox(GroupArguments); @@ -204,29 +220,39 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) // CheckBox for groups generation MakeGroupsCheck = new QCheckBox(tr("SMESH_MAKE_GROUPS"), GroupArguments); + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2, 1, 5); - GroupArgumentsLayout->addWidget(filterBtn, 0, 7); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 7); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 8); - GroupArgumentsLayout->addWidget(TextLabelDistance, 2, 0); - GroupArgumentsLayout->addWidget(TextLabelDx, 2, 2); - GroupArgumentsLayout->addWidget(SpinBox_Dx, 2, 3); - GroupArgumentsLayout->addWidget(TextLabelDy, 2, 4); - GroupArgumentsLayout->addWidget(SpinBox_Dy, 2, 5); - GroupArgumentsLayout->addWidget(TextLabelDz, 2, 6); - GroupArgumentsLayout->addWidget(SpinBox_Dz, 2, 7); - GroupArgumentsLayout->addWidget(TextLabelVector, 3, 0); - GroupArgumentsLayout->addWidget(SelectVectorButton, 3, 1); - GroupArgumentsLayout->addWidget(TextLabelVx, 3, 2); - GroupArgumentsLayout->addWidget(SpinBox_Vx, 3, 3); - GroupArgumentsLayout->addWidget(TextLabelVy, 3, 4); - GroupArgumentsLayout->addWidget(SpinBox_Vy, 3, 5); - GroupArgumentsLayout->addWidget(TextLabelVz, 3, 6); - GroupArgumentsLayout->addWidget(SpinBox_Vz, 3, 7); - GroupArgumentsLayout->addWidget(TextLabelNbSteps, 4, 0); - GroupArgumentsLayout->addWidget(SpinBox_NbSteps, 4, 2, 1, 6); - GroupArgumentsLayout->addWidget(MakeGroupsCheck, 5, 0, 1, 8); + GroupArgumentsLayout->addWidget(RadioButton3, 2, 1, 1, 3); + GroupArgumentsLayout->addWidget(RadioButton4, 2, 5, 1, 3); + GroupArgumentsLayout->addWidget(TextLabelDistance, 3, 0); + GroupArgumentsLayout->addWidget(TextLabelDx, 3, 2); + GroupArgumentsLayout->addWidget(SpinBox_Dx, 3, 3); + GroupArgumentsLayout->addWidget(TextLabelDy, 3, 4); + GroupArgumentsLayout->addWidget(SpinBox_Dy, 3, 5); + GroupArgumentsLayout->addWidget(TextLabelDz, 3, 6); + GroupArgumentsLayout->addWidget(SpinBox_Dz, 3, 7); + GroupArgumentsLayout->addWidget(TextLabelVector, 4, 0); + GroupArgumentsLayout->addWidget(SelectVectorButton, 4, 1); + GroupArgumentsLayout->addWidget(TextLabelVx, 4, 2); + GroupArgumentsLayout->addWidget(SpinBox_Vx, 4, 3); + GroupArgumentsLayout->addWidget(TextLabelVy, 4, 4); + GroupArgumentsLayout->addWidget(SpinBox_Vy, 4, 5); + GroupArgumentsLayout->addWidget(TextLabelVz, 4, 6); + GroupArgumentsLayout->addWidget(SpinBox_Vz, 4, 7); + GroupArgumentsLayout->addWidget(TextLabelDist, 5, 0); + GroupArgumentsLayout->addWidget(SpinBox_VDist, 5, 3); + GroupArgumentsLayout->addWidget(TextLabelNbSteps, 6, 0, 1, 3); + GroupArgumentsLayout->addWidget(SpinBox_NbSteps, 6, 3); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 7, 0, 1, 8); + GroupArgumentsLayout->addWidget(MakeGroupsCheck, 8, 0, 1, 8); + GroupArgumentsLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 8, 0); + /***************************************************************/ SMESHGUI_ExtrusionDlgLayout->addWidget(ConstructorsBox); @@ -234,17 +260,19 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) SMESHGUI_ExtrusionDlgLayout->addWidget(GroupButtons); /* Initialisations */ - SpinBox_Vx->RangeStepAndValidator(-1, 1, 0.01, 3); - SpinBox_Vy->RangeStepAndValidator(-1, 1, 0.01, 3); - SpinBox_Vz->RangeStepAndValidator(-1, 1, 0.01, 3); + SpinBox_Vx->RangeStepAndValidator(COORD_MIN, COORD_MAX, 0.01, "length_precision"); + SpinBox_Vy->RangeStepAndValidator(COORD_MIN, COORD_MAX, 0.01, "length_precision"); + SpinBox_Vz->RangeStepAndValidator(COORD_MIN, COORD_MAX, 0.01, "length_precision"); - SpinBox_Dx->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Dy->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Dz->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); + SpinBox_Dx->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Dy->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Dz->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); SpinBox_NbSteps->setRange(1, 999999); + SpinBox_VDist->RangeStepAndValidator(0, COORD_MAX, 10.0, "length_precision"); - RadioButton1->setChecked(true); + RadioButton0->setChecked(true); + RadioButton3->setChecked(true); MakeGroupsCheck->setChecked(true); mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); @@ -273,6 +301,9 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) connect(buttonApply, SIGNAL(clicked()), this, SLOT(ClickOnApply())); connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); + connect(RadioButton3, SIGNAL(clicked()), this, SLOT(ClickOnRadio())); + connect(RadioButton4, SIGNAL(clicked()), this, SLOT(ClickOnRadio())); + // to update state of the Ok & Apply buttons connect(SpinBox_Vx, SIGNAL(valueChanged(double)), SLOT(CheckIsEnable())); connect(SpinBox_Vy, SIGNAL(valueChanged(double)), SLOT(CheckIsEnable())); @@ -291,9 +322,22 @@ SMESHGUI_ExtrusionDlg::SMESHGUI_ExtrusionDlg (SMESHGUI* theModule) connect(LineEditElements, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); connect(CheckBoxMesh, SIGNAL(toggled(bool)), SLOT(onSelectMesh(bool))); + connect(SpinBox_Dx, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Dy, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Dz, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Vx, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Vy, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Vz, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_VDist, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_NbSteps, SIGNAL(valueChanged(int)), this, SLOT(toDisplaySimulation())); + + //To Connect preview check box + connectPreviewControl(); + /***************************************************************/ ConstructorsClicked(0); + ClickOnRadio(); SelectionIntoArgument(); } @@ -316,6 +360,7 @@ SMESHGUI_ExtrusionDlg::~SMESHGUI_ExtrusionDlg() void SMESHGUI_ExtrusionDlg::Init (bool ResetControls) { myBusy = false; + myIDs.clear(); LineEditElements->clear(); myNbOkElements = 0; @@ -325,6 +370,7 @@ void SMESHGUI_ExtrusionDlg::Init (bool ResetControls) if (ResetControls) { SpinBox_NbSteps->setValue(1); + SpinBox_VDist->setValue(10); SpinBox_Dx->SetValue(0); SpinBox_Dy->SetValue(0); SpinBox_Dz->SetValue(0); @@ -334,6 +380,8 @@ void SMESHGUI_ExtrusionDlg::Init (bool ResetControls) CheckBoxMesh->setChecked(false); onSelectMesh(false); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); } CheckIsEnable(); @@ -344,19 +392,33 @@ void SMESHGUI_ExtrusionDlg::Init (bool ResetControls) // purpose : Check whether the Ok and Apply buttons should be enabled or not //================================================================================= void SMESHGUI_ExtrusionDlg::CheckIsEnable() -{ - - double aX = SpinBox_Vx->GetValue()*SpinBox_Dx->GetValue(); - double aY = SpinBox_Vy->GetValue()*SpinBox_Dy->GetValue(); - double aZ = SpinBox_Vz->GetValue()*SpinBox_Dz->GetValue(); - double aModule = sqrt(aX*aX + aY*aY + aZ*aZ); - - bool anIsEnable = myNbOkElements > 0 && aModule > 1.0E-38; +{ + bool anIsEnable = myNbOkElements > 0 && isValuesValid(); buttonOk->setEnabled(anIsEnable); buttonApply->setEnabled(anIsEnable); } +//================================================================================= +// function : isValuesValid() +// purpose : Return true in case if values entered into dialog are valid +//================================================================================= +bool SMESHGUI_ExtrusionDlg::isValuesValid() { + double aX, aY, aZ, aModule = 0; + if ( RadioButton3->isChecked() ) { + aX = SpinBox_Dx->GetValue(); + aY = SpinBox_Dy->GetValue(); + aZ = SpinBox_Dz->GetValue(); + aModule = sqrt(aX*aX + aY*aY + aZ*aZ); + } else if ( RadioButton4->isChecked() ) { + aX = SpinBox_Vx->GetValue(); + aY = SpinBox_Vy->GetValue(); + aZ = SpinBox_Vz->GetValue(); + aModule = sqrt(aX*aX + aY*aY + aZ*aZ); + } + return aModule > 1.0E-38; +} + //================================================================================= // function : ConstructorsClicked() // purpose : Radio button management @@ -365,25 +427,45 @@ void SMESHGUI_ExtrusionDlg::ConstructorsClicked (int constructorId) { disconnect(mySelectionMgr, 0, this, 0); + hidePreview(); + + TextLabelElements->setText(tr( constructorId ? "SMESH_ID_ELEMENTS" : "SMESH_ID_NODES")); + switch (constructorId) { case 0: { - GroupArguments->setTitle(tr("EXTRUSION_1D")); + GroupArguments->setTitle(tr("EXTRUSION_0D")); if (!CheckBoxMesh->isChecked()) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } + { + LineEditElements->clear(); + myIDs.clear(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + } break; } case 1: + { + GroupArguments->setTitle(tr("EXTRUSION_1D")); + if (!CheckBoxMesh->isChecked()) + { + LineEditElements->clear(); + myIDs.clear(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + } + break; + } + case 2: { GroupArguments->setTitle(tr("EXTRUSION_2D")); if (!CheckBoxMesh->isChecked()) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + { + LineEditElements->clear(); + myIDs.clear(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + } break; } } @@ -397,6 +479,58 @@ void SMESHGUI_ExtrusionDlg::ConstructorsClicked (int constructorId) connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); } +//================================================================================= +// function : ConstructorsClicked() +// purpose : Radio button management +//================================================================================= +void SMESHGUI_ExtrusionDlg::ClickOnRadio() +{ + if ( RadioButton3->isChecked() ) { + TextLabelDistance->show(); + TextLabelDx->show(); + SpinBox_Dx->show(); + TextLabelDy->show(); + SpinBox_Dy->show(); + TextLabelDz->show(); + SpinBox_Dz->show(); + + TextLabelVector->hide(); + TextLabelVx->hide(); + SpinBox_Vx->hide(); + TextLabelVy->hide(); + SpinBox_Vy->hide(); + TextLabelVz->hide(); + SpinBox_Vz->hide(); + TextLabelDist->hide(); + SpinBox_VDist->hide(); + SelectVectorButton->hide(); + } else if ( RadioButton4->isChecked() ) { + TextLabelDistance->hide(); + TextLabelDx->hide(); + SpinBox_Dx->hide(); + TextLabelDy->hide(); + SpinBox_Dy->hide(); + TextLabelDz->hide(); + SpinBox_Dz->hide(); + + TextLabelVector->show(); + TextLabelVx->show(); + SpinBox_Vx->show(); + TextLabelVy->show(); + SpinBox_Vy->show(); + TextLabelVz->show(); + SpinBox_Vz->show(); + TextLabelDist->show(); + SpinBox_VDist->show(); + SelectVectorButton->show(); + } + onDisplaySimulation(true); + // AdjustSize + qApp->processEvents(); + updateGeometry(); + resize( minimumSizeHint() ); +} + //================================================================================= // function : ClickOnApply() // purpose : Called when user presses button @@ -410,66 +544,104 @@ bool SMESHGUI_ExtrusionDlg::ClickOnApply() return false; if (myNbOkElements) { - - gp_XYZ aNormale(SpinBox_Vx->GetValue(), - SpinBox_Vy->GetValue(), - SpinBox_Vz->GetValue()); - - aNormale /= aNormale.Modulus(); - + SMESH::DirStruct aVector; - aVector.PS.x = SpinBox_Dx->GetValue()*aNormale.X(); - aVector.PS.y = SpinBox_Dy->GetValue()*aNormale.Y(); - aVector.PS.z = SpinBox_Dz->GetValue()*aNormale.Z(); + getExtrusionVector(aVector); + + QStringList aParameters; + if ( RadioButton3->isChecked() ) { + aParameters << SpinBox_Dx->text(); + aParameters << SpinBox_Dy->text(); + aParameters << SpinBox_Dz->text(); + } else if ( RadioButton4->isChecked() ) { + // only 3 coords in a python dump command :( + // aParameters << SpinBox_Vx->text(); + // aParameters << SpinBox_Vy->text(); + // aParameters << SpinBox_Vz->text(); + // aParameters << SpinBox_VDist->text(); + } long aNbSteps = (long)SpinBox_NbSteps->value(); - QStringList aParameters; - aParameters << SpinBox_Dx->text(); - aParameters << SpinBox_Dy->text(); - aParameters << SpinBox_Dz->text(); aParameters << SpinBox_NbSteps->text(); try { SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) { - if( CheckBoxMesh->isChecked() ) { - if( GetConstructorId() == 0 ) - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionSweepObject1DMakeGroups(mySelectedObject, aVector, aNbSteps); - else - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionSweepObject2DMakeGroups(mySelectedObject, aVector, aNbSteps); - } - else - SMESH::ListOfGroups_var groups = - aMeshEditor->ExtrusionSweepMakeGroups(myElementsId.inout(), aVector, aNbSteps); + if( CheckBoxMesh->isChecked() ) + switch (GetConstructorId() ) { + case 0: + { + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionSweepObject0DMakeGroups(mySelectedObject, aVector, aNbSteps); + break; + } + case 1: + { + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionSweepObject1DMakeGroups(mySelectedObject, aVector, aNbSteps); + break; + } + case 2: + { + SMESH::ListOfGroups_var groups = + aMeshEditor->ExtrusionSweepObject2DMakeGroups(mySelectedObject, aVector, aNbSteps); + break; + } + } + else + { + SMESH::ListOfGroups_var groups; + if (GetConstructorId() == 0) + groups = aMeshEditor->ExtrusionSweepMakeGroups0D(myElementsId.inout(), aVector, aNbSteps); + else + groups = aMeshEditor->ExtrusionSweepMakeGroups(myElementsId.inout(), aVector, aNbSteps); + } + } else { - if( CheckBoxMesh->isChecked() ) { - if( GetConstructorId() == 0 ) - aMeshEditor->ExtrusionSweepObject1D(mySelectedObject, aVector, aNbSteps); - else - aMeshEditor->ExtrusionSweepObject2D(mySelectedObject, aVector, aNbSteps); - } - else - aMeshEditor->ExtrusionSweep(myElementsId.inout(), aVector, aNbSteps); + if( CheckBoxMesh->isChecked() ) + switch( GetConstructorId() ) { + case 0: + { + aMeshEditor->ExtrusionSweepObject0D(mySelectedObject, aVector, aNbSteps); + break; + } + case 1: + { + aMeshEditor->ExtrusionSweepObject1D(mySelectedObject, aVector, aNbSteps); + break; + } + case 2: + { + aMeshEditor->ExtrusionSweepObject2D(mySelectedObject, aVector, aNbSteps); + break; + } + } + else + if (GetConstructorId() == 0) + aMeshEditor->ExtrusionSweep0D(myElementsId.inout(), aVector, aNbSteps); + else + aMeshEditor->ExtrusionSweep(myElementsId.inout(), aVector, aNbSteps); } - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - } catch (...) { } - SMESH::UpdateView(); + SMESH::Update(myIO, SMESH::eDisplay); if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) mySMESHGUI->updateObjBrowser(true); // new groups may appear Init(false); ConstructorsClicked(GetConstructorId()); + mySelectionMgr->clearSelected(); mySelectedObject = SMESH::SMESH_IDSource::_nil(); SelectionIntoArgument(); + + SMESHGUI::Modified(); } return true; } @@ -490,17 +662,6 @@ void SMESHGUI_ExtrusionDlg::ClickOnOk() //================================================================================= void SMESHGUI_ExtrusionDlg::ClickOnCancel() { - disconnect(mySelectionMgr, 0, this, 0); - mySelectionMgr->clearFilters(); - //mySelectionMgr->clearSelected(); - if (SMESH::GetCurrentVtkView()) { - SMESH::RemoveFilters(); // PAL6938 -- clean all mesh entity filters - SMESH::SetPointRepresentation(false); - SMESH::SetPickable(); - } - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); - mySMESHGUI->ResetState(); reject(); } @@ -521,10 +682,10 @@ void SMESHGUI_ExtrusionDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -553,9 +714,28 @@ void SMESHGUI_ExtrusionDlg::onTextChange (const QString& theNewText) if (send == LineEditElements) { SMDS_Mesh* aMesh = myActor ? myActor->GetObject()->GetMesh() : 0; - SMESH::ElementType SMESHType = GetConstructorId() ? SMESH::FACE : SMESH::EDGE; - SMDSAbs_ElementType SMDSType = GetConstructorId() ? SMDSAbs_Face: SMDSAbs_Edge; - + SMESH::ElementType SMESHType; + SMDSAbs_ElementType SMDSType; + switch (GetConstructorId()) { + case 0: + { + SMESHType = SMESH::NODE; + SMDSType = SMDSAbs_Node; + break; + } + case 1: + { + SMESHType = SMESH::EDGE; + SMDSType = SMDSAbs_Edge; + break; + } + case 2: + { + SMESHType = SMESH::FACE; + SMDSType = SMDSAbs_Face; + break; + } + } myElementsId = new SMESH::long_array; myElementsId->length( aListId.count() ); TColStd_MapOfInteger newIndices; @@ -564,7 +744,11 @@ void SMESHGUI_ExtrusionDlg::onTextChange (const QString& theNewText) bool validId = false; if ( id > 0 ) { if ( aMesh ) { - const SMDS_MeshElement * e = aMesh->FindElement( id ); + const SMDS_MeshElement * e; + if (SMDSType == SMDSAbs_Node) + e = aMesh->FindNode( id ); + else + e = aMesh->FindElement( id ); validId = ( e && e->GetType() == SMDSType ); } else { validId = ( myMesh->GetElementType( id, true ) == SMESHType ); @@ -576,12 +760,14 @@ void SMESHGUI_ExtrusionDlg::onTextChange (const QString& theNewText) myElementsId->length( myNbOkElements = newIndices.Extent() ); mySelector->AddOrRemoveIndex(myIO, newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myIO, true, true ); + aViewWindow->highlight( myIO, true, true ); } } CheckIsEnable(); + onDisplaySimulation(true); + myBusy = false; } @@ -598,8 +784,11 @@ void SMESHGUI_ExtrusionDlg::SelectionIntoArgument() return; // clear - myActor = 0; - myIO.Nullify(); + if(myEditCurrentArgument != (QWidget*)SpinBox_Vx) { + myActor = 0; + Handle(SALOME_InteractiveObject) resIO = myIO; + myIO.Nullify(); + } QString aString = ""; // set busy flag @@ -617,11 +806,14 @@ void SMESHGUI_ExtrusionDlg::SelectionIntoArgument() return; Handle(SALOME_InteractiveObject) IO = aList.First(); - myMesh = SMESH::GetMeshByIO(IO); - if (myMesh->_is_nil()) - return; - myIO = IO; - myActor = SMESH::FindActorByObject(myMesh); + + if(myEditCurrentArgument != (QWidget*)SpinBox_Vx) { + myMesh = SMESH::GetMeshByIO(IO); + if (myMesh->_is_nil()) + return; + myIO = IO; + myActor = SMESH::FindActorByObject(myMesh); + } if (myEditCurrentArgument == (QWidget*)LineEditElements) { int aNbElements = 0; @@ -648,7 +840,7 @@ void SMESHGUI_ExtrusionDlg::SelectionIntoArgument() aNbElements = aMapIndex.Extent(); if (aNbElements < 1) - return; + return; myElementsId = new SMESH::long_array; myElementsId->length( aNbElements ); @@ -667,7 +859,9 @@ void SMESHGUI_ExtrusionDlg::SelectionIntoArgument() TColStd_IndexedMapOfInteger aMapIndex; mySelector->GetIndex(IO,aMapIndex); int aNbElements = aMapIndex.Extent(); - SMDS_Mesh* aMesh = myActor ? myActor->GetObject()->GetMesh() : 0; + SMESH::SMESH_Mesh_var aMesh_var = SMESH::GetMeshByIO(IO); + SMESH_Actor* anActor = SMESH::FindActorByObject(aMesh_var); + SMDS_Mesh* aMesh = anActor ? anActor->GetObject()->GetMesh() : 0; if(aNbElements != 1 || !aMesh) return; @@ -684,6 +878,8 @@ void SMESHGUI_ExtrusionDlg::SelectionIntoArgument() } + onDisplaySimulation(true); + // OK CheckIsEnable(); } @@ -704,20 +900,30 @@ void SMESHGUI_ExtrusionDlg::SetEditCurrentArgument() myEditCurrentArgument = (QWidget*)LineEditElements; if (CheckBoxMesh->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { int aConstructorId = GetConstructorId(); - if (aConstructorId == 0) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } - else if (aConstructorId == 1) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + switch(aConstructorId) { + case 0: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + break; + } + case 1: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + break; + } + case 2: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + break; + } + } } } else if (send == SelectVectorButton){ @@ -774,17 +980,47 @@ void SMESHGUI_ExtrusionDlg::enterEvent (QEvent*) ActivateThisDialog(); } +//================================================================================= +// function : closeEvent() +// purpose : +//================================================================================= +void SMESHGUI_ExtrusionDlg::closeEvent( QCloseEvent* ) +{ + /* same than click on cancel button */ + disconnect(mySelectionMgr, 0, this, 0); + mySelectionMgr->clearFilters(); + //mySelectionMgr->clearSelected(); + if (SMESH::GetCurrentVtkView()) { + SMESH::RemoveFilters(); // PAL6938 -- clean all mesh entity filters + SMESH::SetPointRepresentation(false); + SMESH::SetPickable(); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySMESHGUI->ResetState(); +} + +void SMESHGUI_ExtrusionDlg::reject() +{ + QDialog::reject(); + close(); +} + //================================================================================= // function : onSelectMesh() // purpose : //================================================================================= void SMESHGUI_ExtrusionDlg::onSelectMesh (bool toSelectMesh) { - if (toSelectMesh) + if (toSelectMesh) { + myIDs = LineEditElements->text(); TextLabelElements->setText(tr("SMESH_NAME")); + } else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); + if (myEditCurrentArgument != LineEditElements) { LineEditElements->clear(); return; @@ -800,23 +1036,35 @@ void SMESHGUI_ExtrusionDlg::onSelectMesh (bool toSelectMesh) LineEditElements->setValidator(0); } else { int aConstructorId = GetConstructorId(); - if (aConstructorId == 0) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } - else if (aConstructorId == 0) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } - + switch(aConstructorId) { + case 0: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + break; + } + case 1: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + break; + } + case 2: + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + break; + } + } LineEditElements->setReadOnly(false); LineEditElements->setValidator(myIdValidator); onTextChange(LineEditElements->text()); } SelectionIntoArgument(); + + if (!toSelectMesh) + LineEditElements->setText( myIDs ); } //================================================================================= @@ -850,14 +1098,38 @@ void SMESHGUI_ExtrusionDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_ExtrusionDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) { QList types; + types.append( SMESH::NODE ); types.append( SMESH::EDGE ); types.append( SMESH::FACE ); myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, types ); } - myFilterDlg->Init( GetConstructorId() ? SMESH::FACE : SMESH::EDGE ); + switch( GetConstructorId() ){ + case 0: + { + myFilterDlg->Init( SMESH::NODE ); + break; + } + case 1: + { + myFilterDlg->Init( SMESH::EDGE ); + break; + } + case 2: + { + myFilterDlg->Init( SMESH::FACE ); + break; + } + } + myFilterDlg->SetSelection(); myFilterDlg->SetMesh( myMesh ); @@ -874,9 +1146,16 @@ bool SMESHGUI_ExtrusionDlg::isValid() { QString msg; bool ok = true; - ok = SpinBox_Dx->isValid( msg, true ) && ok; - ok = SpinBox_Dy->isValid( msg, true ) && ok; - ok = SpinBox_Dz->isValid( msg, true ) && ok; + if ( RadioButton3->isChecked() ) { + ok = SpinBox_Dx->isValid( msg, true ) && ok; + ok = SpinBox_Dy->isValid( msg, true ) && ok; + ok = SpinBox_Dz->isValid( msg, true ) && ok; + } else if ( RadioButton4->isChecked() ) { + ok = SpinBox_Vx->isValid( msg, true ) && ok; + ok = SpinBox_Vy->isValid( msg, true ) && ok; + ok = SpinBox_Vz->isValid( msg, true ) && ok; + ok = SpinBox_VDist->isValid( msg, true ) && ok; + } ok = SpinBox_NbSteps->isValid( msg, true ) && ok; if( !ok ) { @@ -888,3 +1167,82 @@ bool SMESHGUI_ExtrusionDlg::isValid() } return true; } + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_ExtrusionDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + if (myNbOkElements && isValid() && isValuesValid()) { + //Get input vector + SMESH::DirStruct aVector; + getExtrusionVector(aVector); + + //Get Number of the steps + long aNbSteps = (long)SpinBox_NbSteps->value(); + + try { + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + if( CheckBoxMesh->isChecked() ) { + switch (GetConstructorId()) { + case 0: + { + aMeshEditor->ExtrusionSweepObject0D(mySelectedObject, aVector, aNbSteps); + break; + } + case 1: + { + aMeshEditor->ExtrusionSweepObject1D(mySelectedObject, aVector, aNbSteps); + break; + } + case 2: + { + aMeshEditor->ExtrusionSweepObject2D(mySelectedObject, aVector, aNbSteps); + break; + } + } + } + else + if(GetConstructorId() == 0) + aMeshEditor->ExtrusionSweep0D(myElementsId.inout(), aVector, aNbSteps); + else + aMeshEditor->ExtrusionSweep(myElementsId.inout(), aVector, aNbSteps); + + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + } catch (...) { + hidePreview(); + } + } else { + hidePreview(); + } + } else { + hidePreview(); + } +} + +//================================================================================= +// function : getExtrusionVector() +// purpose : get direction of the extrusion +//================================================================================= +void SMESHGUI_ExtrusionDlg::getExtrusionVector(SMESH::DirStruct& aVector) { + if ( RadioButton3->isChecked() ) { + aVector.PS.x = SpinBox_Dx->GetValue(); + aVector.PS.y = SpinBox_Dy->GetValue(); + aVector.PS.z = SpinBox_Dz->GetValue(); + } else if ( RadioButton4->isChecked() ) { + gp_XYZ aNormale(SpinBox_Vx->GetValue(), + SpinBox_Vy->GetValue(), + SpinBox_Vz->GetValue()); + + + aNormale /= aNormale.Modulus(); + double aVDist = (double)SpinBox_VDist->value(); + + aVector.PS.x = aNormale.X()*aVDist; + aVector.PS.y = aNormale.Y()*aVDist; + aVector.PS.z = aNormale.Z()*aVDist; + } +} diff --git a/src/SMESHGUI/SMESHGUI_ExtrusionDlg.h b/src/SMESHGUI/SMESHGUI_ExtrusionDlg.h index 99468eaa7..dd0b741a0 100644 --- a/src/SMESHGUI/SMESHGUI_ExtrusionDlg.h +++ b/src/SMESHGUI/SMESHGUI_ExtrusionDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_ExtrusionDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -28,13 +29,11 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_PreviewDlg.h" // SALOME GUI includes #include -// Qt includes -#include - // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Mesh) @@ -61,7 +60,7 @@ class SalomeApp_IntSpinBox; // class : SMESHGUI_ExtrusionDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_ExtrusionDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_ExtrusionDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -69,15 +68,19 @@ public: SMESHGUI_ExtrusionDlg( SMESHGUI* ); ~SMESHGUI_ExtrusionDlg(); + void reject(); + private: void Init( bool = true ); void enterEvent( QEvent* ); /* mouse enter the QWidget */ + void closeEvent( QCloseEvent* ); void keyPressEvent( QKeyEvent* ); int GetConstructorId(); - + void getExtrusionVector(SMESH::DirStruct& aVector); + bool isValid(); + bool isValuesValid(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ QWidget* myEditCurrentArgument; /* Current argument editor */ @@ -96,10 +99,14 @@ private: // widgets QGroupBox* ConstructorsBox; QButtonGroup* GroupConstructors; + QRadioButton* RadioButton0; QRadioButton* RadioButton1; QRadioButton* RadioButton2; + QRadioButton* RadioButton3; + QRadioButton* RadioButton4; QGroupBox* GroupArguments; + QGroupBox* GroupDimensions; QLabel* TextLabelElements; QPushButton* SelectElementsButton; QLineEdit* LineEditElements; @@ -119,6 +126,8 @@ private: SMESHGUI_SpinBox* SpinBox_Vy; QLabel* TextLabelVz; SMESHGUI_SpinBox* SpinBox_Vz; + QLabel* TextLabelDist; + SMESHGUI_SpinBox* SpinBox_VDist; QLabel* TextLabelNbSteps; SalomeApp_IntSpinBox* SpinBox_NbSteps; QCheckBox* MakeGroupsCheck; @@ -130,8 +139,13 @@ private: QPushButton* buttonHelp; QString myHelpFileName; + QString myIDs; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; + +protected slots: + virtual void onDisplaySimulation( bool ); private slots: void ConstructorsClicked( int ); @@ -140,6 +154,7 @@ private slots: bool ClickOnApply(); void ClickOnCancel(); void ClickOnHelp(); + void ClickOnRadio(); void SetEditCurrentArgument(); void SelectionIntoArgument(); void DeactivateActiveDialog(); diff --git a/src/SMESHGUI/SMESHGUI_FileInfoDlg.cxx b/src/SMESHGUI/SMESHGUI_FileInfoDlg.cxx index b6f6f0e48..78446f12b 100644 --- a/src/SMESHGUI/SMESHGUI_FileInfoDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_FileInfoDlg.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FileInfoDlg.cxx // Author : Alexandre SOLOVYOV, Open CASCADE S.A.S. (alexander.solovyov@opencascade.com) diff --git a/src/SMESHGUI/SMESHGUI_FileInfoDlg.h b/src/SMESHGUI/SMESHGUI_FileInfoDlg.h index 0303acb61..dee3e0764 100644 --- a/src/SMESHGUI/SMESHGUI_FileInfoDlg.h +++ b/src/SMESHGUI/SMESHGUI_FileInfoDlg.h @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FileInfoDlg.h // Author : Alexandre SOLOVYOV, Open CASCADE S.A.S. (alexander.solovyov@opencascade.com) diff --git a/src/SMESHGUI/SMESHGUI_FileValidator.cxx b/src/SMESHGUI/SMESHGUI_FileValidator.cxx new file mode 100755 index 000000000..e56a4951e --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FileValidator.cxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_FileValidator.cxx +// Author : Oleg UVAROV +// SMESH includes +// +#include "SMESHGUI_FileValidator.h" + +// SALOME GUI includes +#include +#include + +// Qt includes +#include + +//======================================================================= +//function : SMESHGUI_FileValidator +//purpose : +//======================================================================= +SMESHGUI_FileValidator::SMESHGUI_FileValidator( QWidget* parent ) +: SUIT_FileValidator( parent ), + myIsOverwrite( true ) +{ +} + +//======================================================================= +//function : canSave +//purpose : +//======================================================================= +bool SMESHGUI_FileValidator::canSave( const QString& fileName, bool checkPermission ) +{ + if ( QFile::exists( fileName ) ) { + if ( parent() ) { + int anAnswer = SUIT_MessageBox::question( parent(), QObject::tr( "SMESH_WRN_WARNING" ), + QObject::tr( "SMESH_FILE_EXISTS" ).arg( fileName ), + QObject::tr( "SMESH_BUT_OVERWRITE" ), + QObject::tr( "SMESH_BUT_ADD" ), + QObject::tr( "SMESH_BUT_CANCEL" ), 0, 2 ); + if( anAnswer == 2 ) + return false; + myIsOverwrite = anAnswer == 0; + } + + // copied from SUIT_FileValidator + if ( checkPermission && !QFileInfo( fileName ).isWritable() ) { + if ( parent() ) + SUIT_MessageBox::critical( parent(), QObject::tr( "SMESH_ERROR" ), + QObject::tr( "NO_PERMISSION" ).arg( fileName ) ); + return false; + } + } + else { + return SUIT_FileValidator::canSave( fileName, checkPermission ); + } + return true; +} diff --git a/src/SMESHGUI/SMESHGUI_FileValidator.h b/src/SMESHGUI/SMESHGUI_FileValidator.h new file mode 100755 index 000000000..0f6f0129e --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FileValidator.h @@ -0,0 +1,46 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_FileValidator.h +// Author : Oleg UVAROV +// +#ifndef SMESHGUI_FILEVALIDATOR_H +#define SMESHGUI_FILEVALIDATOR_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +// SALOME GUI includes +#include + +class SMESHGUI_EXPORT SMESHGUI_FileValidator : public SUIT_FileValidator +{ +public: + SMESHGUI_FileValidator( QWidget* = 0 ); + + virtual bool canSave( const QString&, bool = true ); + + bool isOverwrite() const { return myIsOverwrite; } + +private: + bool myIsOverwrite; +}; + +#endif // SMESHGUI_FILEVALIDATOR_H diff --git a/src/SMESHGUI/SMESHGUI_Filter.cxx b/src/SMESHGUI/SMESHGUI_Filter.cxx index e6d12feec..c011b9606 100755 --- a/src/SMESHGUI/SMESHGUI_Filter.cxx +++ b/src/SMESHGUI/SMESHGUI_Filter.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESHGUI_Filter : Filters for VTK viewer // File : SMESHGUI_Filter.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -106,8 +107,8 @@ bool SMESHGUI_PredicateFilter::IsObjValid( const int theObjId ) const if ( myActor == 0 || myPred->_is_nil() ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -210,8 +211,8 @@ bool SMESHGUI_QuadrangleFilter::IsValid( const int theCellId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -230,8 +231,8 @@ bool SMESHGUI_QuadrangleFilter::IsObjValid( const int theObjId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -289,8 +290,8 @@ bool SMESHGUI_TriangleFilter::IsValid( const int theCellId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -309,8 +310,8 @@ bool SMESHGUI_TriangleFilter::IsObjValid( const int theObjId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -367,8 +368,8 @@ bool SMESHGUI_FacesFilter::IsValid( const int theCellId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -386,8 +387,8 @@ bool SMESHGUI_FacesFilter::IsObjValid( const int theObjId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -441,11 +442,11 @@ SMESHGUI_VolumesFilter::~SMESHGUI_VolumesFilter() //======================================================================= bool SMESHGUI_VolumesFilter::IsValid( const int theCellId ) const { - if ( myActor == 0 ) + if ( myActor == 0 || theCellId < 1 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); @@ -463,8 +464,8 @@ bool SMESHGUI_VolumesFilter::IsObjValid( const int theObjId ) const if ( myActor == 0 ) return false; - SMESH_Actor* anActor = ( SMESH_Actor* )myActor; - if ( anActor->GetObject() == 0 ) + SMESH_Actor* anActor = dynamic_cast< SMESH_Actor* >( myActor ); + if ( !anActor || anActor->GetObject() == 0 ) return false; SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); diff --git a/src/SMESHGUI/SMESHGUI_Filter.h b/src/SMESHGUI/SMESHGUI_Filter.h index 6da648be9..ed048df67 100755 --- a/src/SMESHGUI/SMESHGUI_Filter.h +++ b/src/SMESHGUI/SMESHGUI_Filter.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESHGUI_Filter : Filters for VTK viewer // File : SMESHGUI_Filter.h // Author : Sergey LITONIN, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_FilterDlg.cxx b/src/SMESHGUI/SMESHGUI_FilterDlg.cxx index 0b55d8b71..ac756a16c 100755 --- a/src/SMESHGUI/SMESHGUI_FilterDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_FilterDlg.cxx @@ -1,29 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. -// SMESH includes // + +// SMESH includes #include "SMESHGUI_FilterDlg.h" #include "SMESHGUI.h" @@ -32,6 +34,7 @@ #include "SMESHGUI_Filter.h" #include "SMESHGUI_FilterUtils.h" #include "SMESHGUI_FilterLibraryDlg.h" +#include "SMESHGUI_SpinBox.h" #include #include @@ -53,6 +56,8 @@ #include #include #include +#include +#include #include #include @@ -159,30 +164,32 @@ public: virtual void SetString(const int, const QString&); void SetEditable(const int, const bool); void SetEditable(const bool); + void SetPrecision(const int, const char* = 0); private: - QMap< int, QLineEdit* > myLineEdits; + QMap< int, QWidget* > myWidgets; }; SMESHGUI_FilterTable::AdditionalWidget::AdditionalWidget (QWidget* theParent) : QWidget(theParent) { QLabel* aLabel = new QLabel(tr("SMESH_TOLERANCE"), this); - myLineEdits[ Tolerance ] = new QLineEdit(this); - QDoubleValidator* aValidator = new QDoubleValidator(myLineEdits[ Tolerance ]); - aValidator->setBottom(0); - myLineEdits[ Tolerance ]->setValidator(aValidator); + + SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox(this); + sb->setAcceptNames( false ); // No Notebook variables allowed + sb->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + sb->RangeStepAndValidator( 0., 1.e20, 0.1, "len_tol_precision" ); + myWidgets[ Tolerance ] = sb; QHBoxLayout* aLay = new QHBoxLayout(this); aLay->setSpacing(SPACING); aLay->setMargin(0); aLay->addWidget(aLabel); - aLay->addWidget(myLineEdits[ Tolerance ]); + aLay->addWidget(myWidgets[ Tolerance ]); aLay->addStretch(); - QString aText = QString("%1").arg(Precision::Confusion()); - myLineEdits[ Tolerance ]->setText(aText); + SetDouble( Tolerance, Precision::Confusion() ); } SMESHGUI_FilterTable::AdditionalWidget::~AdditionalWidget() @@ -204,13 +211,18 @@ bool SMESHGUI_FilterTable::AdditionalWidget::IsValid (const bool theMsg) const QList aParams = GetParameters(); QList::const_iterator anIter; for (anIter = aParams.begin(); anIter != aParams.end(); ++anIter) { - const QLineEdit* aWg = myLineEdits[ *anIter ]; - int p = 0; - QString aText = aWg->text(); - if (aWg->isEnabled() && aWg->validator()->validate(aText, p) != QValidator::Acceptable) { - if (theMsg) - SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("SMESHGUI_INVALID_PARAMETERS")); + if ( !myWidgets.contains( *anIter ) ) continue; + bool valid = true; + if ( qobject_cast( myWidgets[ *anIter ] ) ) { + valid = qobject_cast( myWidgets[ *anIter ] )->hasAcceptableInput(); + } + else if ( qobject_cast( myWidgets[ *anIter ] ) ) { + QString foo; + valid = qobject_cast( myWidgets[ *anIter ] )->isValid( foo, false ); + } + if (!valid && theMsg) { + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), + tr("SMESHGUI_INVALID_PARAMETERS")); return false; } } @@ -220,41 +232,78 @@ bool SMESHGUI_FilterTable::AdditionalWidget::IsValid (const bool theMsg) const double SMESHGUI_FilterTable::AdditionalWidget::GetDouble (const int theId) const { - return myLineEdits.contains(theId) ? myLineEdits[ theId ]->text().toDouble() : 0; + double retval = 0; + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = qobject_cast( myWidgets[ theId ] )->text().toDouble(); + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = qobject_cast( myWidgets[ theId ] )->GetValue(); + } + return retval; } int SMESHGUI_FilterTable::AdditionalWidget::GetInteger (const int theId) const { - return myLineEdits.contains(theId) ? myLineEdits[ theId ]->text().toInt() : 0; + int retval = 0; + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = qobject_cast( myWidgets[ theId ] )->text().toInt(); + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = (int)( qobject_cast( myWidgets[ theId ] )->GetValue() ); + } + return retval; } QString SMESHGUI_FilterTable::AdditionalWidget::GetString (const int theId) const { - return myLineEdits.contains(theId) ? myLineEdits[ theId ]->text() : QString(""); + QString retval = ""; + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = qobject_cast( myWidgets[ theId ] )->text(); + if ( qobject_cast( myWidgets[ theId ] ) ) + retval = QString::number( qobject_cast( myWidgets[ theId ] )->GetValue() ); + } + return retval; } void SMESHGUI_FilterTable::AdditionalWidget::SetDouble (const int theId, const double theVal) { - if (myLineEdits.contains(theId)) - myLineEdits[ theId ]->setText(QString("%1").arg(theVal)); + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->setText( QString::number( theVal ) ); + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->SetValue( theVal ); + } } void SMESHGUI_FilterTable::AdditionalWidget::SetInteger (const int theId, const int theVal) { - if (myLineEdits.contains(theId)) - myLineEdits[ theId ]->setText(QString("%1").arg(theVal)); + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->setText( QString::number( theVal ) ); + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->SetValue( (double)theVal ); + } } void SMESHGUI_FilterTable::AdditionalWidget::SetString (const int theId, const QString& theVal) { - if (myLineEdits.contains(theId)) - myLineEdits[ theId ]->setText(theVal); + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->setText( theVal ); + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->SetValue( theVal.toDouble() ); + } } void SMESHGUI_FilterTable::AdditionalWidget::SetEditable (const int theId, const bool isEditable) { - if (myLineEdits.contains(theId)) - myLineEdits[ theId ]->setReadOnly(!isEditable); + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->setReadOnly( !isEditable ); + if ( qobject_cast( myWidgets[ theId ] ) ) + qobject_cast( myWidgets[ theId ] )->setReadOnly( !isEditable ); + } } void SMESHGUI_FilterTable::AdditionalWidget::SetEditable (const bool isEditable) @@ -265,6 +314,19 @@ void SMESHGUI_FilterTable::AdditionalWidget::SetEditable (const bool isEditable) SetEditable( *anIter, isEditable ); } +void SMESHGUI_FilterTable::AdditionalWidget::SetPrecision(const int theId, const char* precision) +{ + if ( myWidgets.contains( theId ) ) { + if ( qobject_cast( myWidgets[ theId ] ) ) { + SMESHGUI_SpinBox* sb = qobject_cast( myWidgets[ theId ] ); + double val = sb->GetValue(); + double min = pow(10.0, -(sb->decimals())); + sb->RangeStepAndValidator( 0., 1.e20, 0.1, precision ? precision : "len_tol_precision" ); + sb->SetValue( qMax( val, min ) ); + } + } +} + /* Class : SMESHGUI_FilterTable::ComboItem Description : Combo table item. Identificator corresponding to string may be assigned @@ -411,6 +473,115 @@ bool SMESHGUI_FilterTable::CheckItem::checked() const return checkState() == Qt::Checked; } +/* + Class : SMESHGUI_FilterTable::IntSpinItem + Description : Integer spin table item. +*/ + +class SMESHGUI_FilterTable::IntSpinItem : public QTableWidgetItem +{ +public: + static int Type(); + + IntSpinItem( const int theValue ); + + int value() const; + void setValue( const int theValue ); + + void clear(); +}; + +int SMESHGUI_FilterTable::IntSpinItem::Type() +{ + return QTableWidgetItem::UserType + 3; +} + +SMESHGUI_FilterTable::IntSpinItem::IntSpinItem( const int theValue ) + : QTableWidgetItem( Type() ) +{ + setValue( theValue ); +} + +int SMESHGUI_FilterTable::IntSpinItem::value() const +{ + bool ok = false; + int value = data( Qt::UserRole ).toInt( &ok ); + return ok ? value : 0; +} + +void SMESHGUI_FilterTable::IntSpinItem::setValue( const int theValue ) +{ + setData( Qt::UserRole, theValue ); + setText( QString::number( theValue ) ); +} + +void SMESHGUI_FilterTable::IntSpinItem::clear() +{ + setText( "" ); +} + +/* + Class : SMESHGUI_FilterTable::DoubleSpinItem + Description : Double spin table item. +*/ + +class SMESHGUI_FilterTable::DoubleSpinItem : public QTableWidgetItem +{ +public: + static int Type(); + + DoubleSpinItem( const double theValue ); + + double value() const; + void setValue( const double theValue ); + + int precision() const; + void setPrecision( const int thePrecision ); + + void clear(); +}; + +int SMESHGUI_FilterTable::DoubleSpinItem::Type() +{ + return QTableWidgetItem::UserType + 4; +} + +SMESHGUI_FilterTable::DoubleSpinItem::DoubleSpinItem( const double theValue ) + : QTableWidgetItem( Type() ) +{ + setValue( theValue ); +} + +double SMESHGUI_FilterTable::DoubleSpinItem::value() const +{ + bool ok = false; + double value = data( Qt::UserRole ).toDouble( &ok ); + return ok ? value : 0; +} + +void SMESHGUI_FilterTable::DoubleSpinItem::setValue( const double theValue ) +{ + setData( Qt::UserRole, theValue ); + setText( QString::number( theValue ) ); +} + +int SMESHGUI_FilterTable::DoubleSpinItem::precision() const +{ + bool ok = false; + int precision = data( Qt::UserRole + 1 ).toInt( &ok ); + return ok ? precision : 0; +} + +void SMESHGUI_FilterTable::DoubleSpinItem::setPrecision( const int thePrecision ) +{ + setData( Qt::UserRole + 1, thePrecision ); +} + +void SMESHGUI_FilterTable::DoubleSpinItem::clear() +{ + setText( "" ); +} + /* Class : SMESHGUI_FilterTable::ComboDelegate Description : Table used by this widget @@ -423,13 +594,13 @@ public: ~ComboDelegate(); QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, - const QModelIndex& ) const; + const QModelIndex& ) const; void setEditorData( QWidget*, const QModelIndex& ) const; void setModelData( QWidget*, QAbstractItemModel*, const QModelIndex& ) const; void updateEditorGeometry( QWidget*, const QStyleOptionViewItem&, - const QModelIndex& ) const; + const QModelIndex& ) const; private: QTableWidget* myTable; }; @@ -445,47 +616,86 @@ SMESHGUI_FilterTable::ComboDelegate::~ComboDelegate() } QWidget* SMESHGUI_FilterTable::ComboDelegate::createEditor( QWidget* parent, - const QStyleOptionViewItem& option, - const QModelIndex& index ) const -{ - QStringList l = index.data( Qt::UserRole ).toStringList(); - if ( !l.isEmpty() ) { - QComboBox* cb = new QComboBox( parent ); - cb->setFrame( false ); - cb->addItems( l ); - return cb; + const QStyleOptionViewItem& option, + const QModelIndex& index ) const +{ + QVariant aData = index.data( Qt::UserRole ); + QVariant::Type aDataType = aData.type(); + if( aDataType == QVariant::StringList ) { + QStringList l = aData.toStringList(); + if ( !l.isEmpty() ) { + QComboBox* cb = new QComboBox( parent ); + cb->setFrame( false ); + cb->addItems( l ); + return cb; + } + } + else if( aDataType == QVariant::Int ) { + bool ok = false; + int aValue = aData.toInt( &ok ); + if ( ok ) { + SalomeApp_IntSpinBox* intSpin = new SalomeApp_IntSpinBox( 0, 1000, 1, parent, false, true ); + intSpin->setFrame( false ); + intSpin->setValue( aValue ); + return intSpin; + } + } + else if( aDataType == QVariant::Double ) { + bool ok = false; + double aValue = aData.toDouble( &ok ); + if ( ok ) { + int aPrecision = index.data( Qt::UserRole + 1 ).toInt( &ok ); + if ( !ok ) + aPrecision = 0; + + SalomeApp_DoubleSpinBox* dblSpin = new SalomeApp_DoubleSpinBox( -1.e20, 1.e20, 1, aPrecision, 20, parent, false, true ); + dblSpin->setFrame( false ); + dblSpin->setValue( aValue ); + return dblSpin; + } } return QItemDelegate::createEditor( parent, option, index ); } void SMESHGUI_FilterTable::ComboDelegate::setEditorData( QWidget* editor, - const QModelIndex& index ) const + const QModelIndex& index ) const { - QString value = index.model()->data( index, Qt::DisplayRole ).toString(); - QComboBox* cb = dynamic_cast( editor ); + QVariant data = index.model()->data( index, Qt::DisplayRole ); + QString value = data.toString(); bool bOk = false; - if ( cb ) { + if ( QComboBox* cb = dynamic_cast( editor ) ) { int i = cb->findText( value ); if ( i >= 0 ) { cb->setCurrentIndex( i ); bOk = true; } } + else if ( SalomeApp_DoubleSpinBox* dblSpin = dynamic_cast( editor ) ) { + if( data.type() == QVariant::Double ) { + double valueDouble = data.toDouble( &bOk ); + if( bOk ) + dblSpin->setValue( valueDouble ); + } + } if ( !bOk ) QItemDelegate::setEditorData( editor, index ); } void SMESHGUI_FilterTable::ComboDelegate::setModelData( QWidget* editor, - QAbstractItemModel* model, - const QModelIndex& index) const -{ - QComboBox* cb = dynamic_cast( editor ); - if ( cb ) model->setData( index, cb->currentText(), Qt::DisplayRole ); + QAbstractItemModel* model, + const QModelIndex& index) const +{ + if( QComboBox* cb = dynamic_cast( editor ) ) + model->setData( index, cb->currentText(), Qt::DisplayRole ); + else if( SalomeApp_IntSpinBox* intSpin = dynamic_cast( editor ) ) + model->setData( index, intSpin->value(), Qt::DisplayRole ); + else if( SalomeApp_DoubleSpinBox* dblSpin = dynamic_cast( editor ) ) + model->setData( index, dblSpin->value(), Qt::DisplayRole ); else QItemDelegate::setModelData( editor, model, index ); } void SMESHGUI_FilterTable::ComboDelegate::updateEditorGeometry( QWidget* editor, - const QStyleOptionViewItem& option, - const QModelIndex& index ) const + const QStyleOptionViewItem& option, + const QModelIndex& index ) const { editor->setGeometry( option.rect ); } @@ -597,8 +807,8 @@ bool SMESHGUI_FilterTable::Table::isEditable (int row, int col) const void SMESHGUI_FilterTable::Table::setReadOnly( bool on ) { setEditTriggers( on ? - QAbstractItemView::NoEditTriggers : - QAbstractItemView::AllEditTriggers ); + QAbstractItemView::NoEditTriggers : + QAbstractItemView::AllEditTriggers ); } bool SMESHGUI_FilterTable::Table::isReadOnly() const @@ -909,13 +1119,16 @@ bool SMESHGUI_FilterTable::IsValid (const bool theMess, const int theEntityType) QtxColorButton* clrBtn = qobject_cast(aTable->cellWidget(i, 2)); if (clrBtn && !clrBtn->color().isValid()) errMsg = tr( "GROUPCOLOR_ERROR" ); - } else if (aCriterion == SMESH::FT_RangeOfIds || - aCriterion == SMESH::FT_BelongToGeom || - aCriterion == SMESH::FT_BelongToPlane || - aCriterion == SMESH::FT_BelongToCylinder || - aCriterion == SMESH::FT_BelongToGenSurface || - aCriterion == SMESH::FT_ElemGeomType || - aCriterion == SMESH::FT_LyingOnGeom) { + } + else if (aCriterion == SMESH::FT_RangeOfIds || + aCriterion == SMESH::FT_BelongToGeom || + aCriterion == SMESH::FT_BelongToPlane || + aCriterion == SMESH::FT_BelongToCylinder || + aCriterion == SMESH::FT_BelongToGenSurface || + aCriterion == SMESH::FT_ElemGeomType || + aCriterion == SMESH::FT_CoplanarFaces || + aCriterion == SMESH::FT_LyingOnGeom) + { if (aTable->text(i, 2).isEmpty()) errMsg = tr( "ERROR" ); } @@ -929,8 +1142,8 @@ bool SMESHGUI_FilterTable::IsValid (const bool theMess, const int theEntityType) if (!aRes && aTable->isEditable(i, 2)) errMsg = tr( "ERROR" ); else if (aType == SMESH::EDGE && - GetCriterionType(i, aType) == SMESH::FT_MultiConnection && - aThreshold == 1) + GetCriterionType(i, aType) == SMESH::FT_MultiConnection && + aThreshold == 1) errMsg = tr( "MULTIEDGES_ERROR" ); } @@ -1029,12 +1242,14 @@ void SMESHGUI_FilterTable::GetCriterion (const int theRow, } else if ( aCriterionType == SMESH::FT_ElemGeomType ) theCriterion.Threshold = (double)((ComboItem*)aTable->item(theRow, 2))->value(); + else if ( aCriterionType == SMESH::FT_CoplanarFaces ) + theCriterion.ThresholdID = aTable->text(theRow, 2).toLatin1().constData(); else if ( aCriterionType != SMESH::FT_RangeOfIds && aCriterionType != SMESH::FT_BelongToGeom && - aCriterionType != SMESH::FT_BelongToPlane && - aCriterionType != SMESH::FT_BelongToCylinder && - aCriterionType != SMESH::FT_BelongToGenSurface && - aCriterionType != SMESH::FT_LyingOnGeom) + aCriterionType != SMESH::FT_BelongToPlane && + aCriterionType != SMESH::FT_BelongToCylinder && + aCriterionType != SMESH::FT_BelongToGenSurface && + aCriterionType != SMESH::FT_LyingOnGeom ) { theCriterion.Compare = ((ComboItem*)aTable->item(theRow, 1))->value(); theCriterion.Threshold = aTable->item(theRow, 2)->text().toDouble(); @@ -1068,7 +1283,10 @@ void SMESHGUI_FilterTable::SetCriterion (const int theRow, ((ComboItem*)aTable->item(theRow, 0))->setValue(theCriterion.Type); onCriterionChanged(theRow, 0, aType); - ((ComboItem*)aTable->item(theRow, 1))->setValue(theCriterion.Compare); + if ( theCriterion.Compare == SMESH::FT_Undefined ) + ((ComboItem*)aTable->item(theRow, 1))->setValue( SMESH::FT_EqualTo ); + else + ((ComboItem*)aTable->item(theRow, 1))->setValue(theCriterion.Compare); ((CheckItem*)aTable->item(theRow, 3))->setChecked(theCriterion.UnaryOp == SMESH::FT_LogicalNOT); if (theCriterion.BinaryOp != SMESH::FT_Undefined) @@ -1100,19 +1318,30 @@ void SMESHGUI_FilterTable::SetCriterion (const int theRow, ComboItem* typeBox = (ComboItem*)aTable->item(theRow, 2); typeBox->setValue( (int)(theCriterion.Threshold + 0.5) ); } + else if (theCriterion.Type == SMESH::FT_CoplanarFaces ) + { + aTable->item( theRow, 2 )->setText( QString( theCriterion.ThresholdID ) ); + } else if (theCriterion.Type != SMESH::FT_RangeOfIds && - theCriterion.Type != SMESH::FT_BelongToGeom && - theCriterion.Type != SMESH::FT_BelongToPlane && - theCriterion.Type != SMESH::FT_BelongToCylinder && - theCriterion.Type != SMESH::FT_BelongToGenSurface && - theCriterion.Type != SMESH::FT_LyingOnGeom && - theCriterion.Type != SMESH::FT_FreeBorders && - theCriterion.Type != SMESH::FT_FreeEdges && - theCriterion.Type != SMESH::FT_FreeNodes && - theCriterion.Type != SMESH::FT_FreeFaces && - theCriterion.Type != SMESH::FT_BadOrientedVolume && - theCriterion.Type != SMESH::FT_LinearOrQuadratic) + theCriterion.Type != SMESH::FT_BelongToGeom && + theCriterion.Type != SMESH::FT_BelongToPlane && + theCriterion.Type != SMESH::FT_BelongToCylinder && + theCriterion.Type != SMESH::FT_BelongToGenSurface && + theCriterion.Type != SMESH::FT_LyingOnGeom && + theCriterion.Type != SMESH::FT_CoplanarFaces && + theCriterion.Type != SMESH::FT_FreeBorders && + theCriterion.Type != SMESH::FT_FreeEdges && + theCriterion.Type != SMESH::FT_FreeNodes && + theCriterion.Type != SMESH::FT_FreeFaces && + theCriterion.Type != SMESH::FT_BadOrientedVolume && + theCriterion.Type != SMESH::FT_BareBorderFace && + theCriterion.Type != SMESH::FT_BareBorderVolume && + theCriterion.Type != SMESH::FT_OverConstrainedFace && + theCriterion.Type != SMESH::FT_OverConstrainedVolume && + theCriterion.Type != SMESH::FT_LinearOrQuadratic) + { aTable->item( theRow, 2 )->setText(QString("%1").arg(theCriterion.Threshold, 0, 'g', 15)); + } else { aTable->item( theRow, 2 )->setText(QString(theCriterion.ThresholdStr)); @@ -1121,21 +1350,25 @@ void SMESHGUI_FilterTable::SetCriterion (const int theRow, } if (theCriterion.Compare == SMESH::FT_EqualTo || - theCriterion.Type == SMESH::FT_BelongToPlane || - theCriterion.Type == SMESH::FT_BelongToCylinder || - theCriterion.Type == SMESH::FT_BelongToGenSurface) + theCriterion.Type == SMESH::FT_BelongToPlane || + theCriterion.Type == SMESH::FT_BelongToCylinder || + theCriterion.Type == SMESH::FT_BelongToGenSurface || + theCriterion.Type == SMESH::FT_BelongToGeom || + theCriterion.Type == SMESH::FT_LyingOnGeom || + theCriterion.Type == SMESH::FT_CoplanarFaces || + theCriterion.Type == SMESH::FT_EqualNodes) { QTableWidgetItem* anItem = aTable->item(theRow, 0); if (!myAddWidgets.contains(anItem)) { myAddWidgets[ anItem ] = new AdditionalWidget(myWgStack); + myAddWidgets[ anItem ]->SetPrecision( AdditionalWidget::Tolerance, getPrecision( theCriterion.Type ) ); myWgStack->addWidget(myAddWidgets[ anItem ]); } myAddWidgets[ anItem ]->SetDouble(AdditionalWidget::Tolerance, theCriterion.Tolerance); } emit CriterionChanged(theRow, aType); - } //======================================================================= @@ -1263,14 +1496,18 @@ void SMESHGUI_FilterTable::updateAdditionalWidget() ComboItem* anItem = ((ComboItem*)aTable->item(aRow, 0)); int aCriterion = GetCriterionType(aRow); - bool toEnable = ((ComboItem*)aTable->item(aRow, 1))->value() == SMESH::FT_EqualTo && - aCriterion != SMESH::FT_BelongToGeom && - aCriterion != SMESH::FT_LyingOnGeom && - aCriterion != SMESH::FT_RangeOfIds && - aCriterion != SMESH::FT_FreeEdges && - aCriterion != SMESH::FT_FreeFaces && - aCriterion != SMESH::FT_BadOrientedVolume; - + bool toEnable = ((((ComboItem*)aTable->item(aRow, 1))->value() == SMESH::FT_EqualTo && + aCriterion != SMESH::FT_RangeOfIds && + aCriterion != SMESH::FT_FreeEdges && + aCriterion != SMESH::FT_FreeFaces && + aCriterion != SMESH::FT_BadOrientedVolume && + aCriterion != SMESH::FT_BareBorderFace && + aCriterion != SMESH::FT_BareBorderVolume && + aCriterion != SMESH::FT_OverConstrainedFace && + aCriterion != SMESH::FT_OverConstrainedVolume) + || + aCriterion == SMESH::FT_CoplanarFaces); + if (!myAddWidgets.contains(anItem)) { myAddWidgets[ anItem ] = new AdditionalWidget(myWgStack); @@ -1278,9 +1515,45 @@ void SMESHGUI_FilterTable::updateAdditionalWidget() } myWgStack->setCurrentWidget(myAddWidgets[ anItem ]); + myAddWidgets[ anItem ]->SetPrecision( AdditionalWidget::Tolerance, getPrecision( aCriterion ) ); myWgStack->setEnabled(toEnable); } +const char* SMESHGUI_FilterTable::getPrecision( const int aType ) +{ + const char* retval = 0; + switch ( aType ) { + case SMESH::FT_AspectRatio: + case SMESH::FT_AspectRatio3D: + case SMESH::FT_Taper: + retval = "parametric_precision"; break; + case SMESH::FT_Warping: + case SMESH::FT_MinimumAngle: + case SMESH::FT_Skew: + case SMESH::FT_CoplanarFaces: + retval = "angle_precision"; break; + case SMESH::FT_Area: + retval = "area_precision"; break; + case SMESH::FT_BelongToGeom: + case SMESH::FT_BelongToPlane: + case SMESH::FT_BelongToCylinder: + case SMESH::FT_BelongToGenSurface: + case SMESH::FT_LyingOnGeom: + case SMESH::FT_EqualNodes: + retval = "len_tol_precision"; break; + case SMESH::FT_Length: + case SMESH::FT_Length2D: + case SMESH::FT_MaxElementLength2D: + case SMESH::FT_MaxElementLength3D: + retval = "length_precision"; break; + case SMESH::FT_Volume3D: + retval = "vol_precision"; break; + default: + break; + } + return retval; +} + //======================================================================= // name : SMESHGUI_FilterTable::removeAdditionalWidget // Purpose : Remove widgets containing additional parameters from widget @@ -1353,8 +1626,17 @@ static QList geomTypes( const int theType ) typeIds.append( SMESH::Geom_PYRAMID ); typeIds.append( SMESH::Geom_HEXA ); typeIds.append( SMESH::Geom_PENTA ); + typeIds.append( SMESH::Geom_HEXAGONAL_PRISM ); typeIds.append( SMESH::Geom_POLYHEDRA ); } + if ( theType == SMESH::ALL || theType == SMESH::ELEM0D ) + { + typeIds.append( SMESH::Geom_POINT ); + } + if ( theType == SMESH::ALL || theType == SMESH::BALL ) + { + typeIds.append( SMESH::Geom_BALL ); + } return typeIds; } @@ -1370,10 +1652,56 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con int aCriterionType = GetCriterionType(row); QtxColorButton* clrBtn = qobject_cast(aTable->cellWidget(row, 2)); - bool isComboItem = aTable->item(row, 2)->type() == ComboItem::Type(); + int aComboType = ComboItem::Type(); + int aIntSpinType = IntSpinItem::Type(); + int aDoubleSpinType = DoubleSpinItem::Type(); + QTableWidgetItem* aTableItem = aTable->item(row, 2); + bool isComboItem = false; + bool isIntSpinItem = false; + bool isDoubleSpinItem = false; + if (aTableItem) { + int aTableType = aTable->item(row, 2)->type(); + isComboItem = ( aTableType == aComboType ); + isIntSpinItem = ( aTableType == aIntSpinType ); + isDoubleSpinItem = ( aTableType == aDoubleSpinType ); + } + bool anIsDoubleCriterion = + aCriterionType == SMESH::FT_AspectRatio || + aCriterionType == SMESH::FT_AspectRatio3D || + aCriterionType == SMESH::FT_Taper || + aCriterionType == SMESH::FT_Warping || + aCriterionType == SMESH::FT_MinimumAngle || + aCriterionType == SMESH::FT_Skew || + aCriterionType == SMESH::FT_Area || + aCriterionType == SMESH::FT_Length || + aCriterionType == SMESH::FT_Length2D || + aCriterionType == SMESH::FT_MaxElementLength2D || + aCriterionType == SMESH::FT_MaxElementLength3D || + aCriterionType == SMESH::FT_Volume3D; + + int aPrecision = 0; + if ( anIsDoubleCriterion ) { + const char* aPrecisionType = getPrecision( aCriterionType ); + SUIT_ResourceMgr* aResourceMgr = SMESH::GetResourceMgr( mySMESHGUI ); + if( aPrecisionType && aResourceMgr ) + aPrecision = aResourceMgr->integerValue( "SMESH", aPrecisionType, aPrecision ); + } + + // if the precision is to be changed we should remove the existing + // spin item and create another one with new precision + bool anIsPrecisionChanged = false; + if ( anIsDoubleCriterion && isDoubleSpinItem ) { + if ( DoubleSpinItem* aDoubleSpinItem = dynamic_cast( aTable->item( row, 2 ) ) ) { + anIsPrecisionChanged = aDoubleSpinItem->precision() != aPrecision; + } + } + if ( (aCriterionType != SMESH::FT_GroupColor && clrBtn) || - (aCriterionType != SMESH::FT_ElemGeomType && isComboItem) ) + (aCriterionType != SMESH::FT_ElemGeomType && isComboItem) || + (aCriterionType != SMESH::FT_MultiConnection && isIntSpinItem) || + (!anIsDoubleCriterion && isDoubleSpinItem) || + anIsPrecisionChanged ) { bool isSignalsBlocked = aTable->signalsBlocked(); aTable->blockSignals( true ); @@ -1382,13 +1710,16 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con aTable->blockSignals( isSignalsBlocked ); } if ( (aCriterionType == SMESH::FT_GroupColor && !clrBtn) || - (aCriterionType == SMESH::FT_ElemGeomType && !isComboItem) ) + (aCriterionType == SMESH::FT_ElemGeomType && !isComboItem) || + (aCriterionType == SMESH::FT_MultiConnection && !isIntSpinItem) || + (anIsDoubleCriterion && !isDoubleSpinItem) || + anIsPrecisionChanged ) { bool isSignalsBlocked = aTable->signalsBlocked(); aTable->blockSignals( true ); if ( aCriterionType == SMESH::FT_GroupColor ) aTable->setCellWidget( row, 2, new QtxColorButton( aTable ) ); - else { + else if ( aCriterionType == SMESH::FT_ElemGeomType ) { QList typeIds = geomTypes( aType ); QMap typeNames; QList::const_iterator anIter = typeIds.begin(); @@ -1400,17 +1731,36 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con ComboItem* typeBox = new ComboItem( typeNames ); aTable->setItem( row, 2, typeBox ); } + else if ( aCriterionType == SMESH::FT_MultiConnection ) { + IntSpinItem* intSpin = new IntSpinItem( 0 ); + aTable->setItem( row, 2, intSpin ); + } + else if ( anIsDoubleCriterion ) { + DoubleSpinItem* dblSpin = new DoubleSpinItem( 0 ); + dblSpin->setPrecision( aPrecision ); + aTable->setItem( row, 2, dblSpin ); + } aTable->blockSignals( isSignalsBlocked ); } - if (aType == SMESH::NODE && aCriterionType == SMESH::FT_FreeNodes || - aType == SMESH::EDGE && aCriterionType == SMESH::FT_FreeBorders || - aType == SMESH::FACE && (aCriterionType == SMESH::FT_FreeEdges || - aCriterionType == SMESH::FT_FreeFaces) || - aType == SMESH::VOLUME && aCriterionType == SMESH::FT_BadOrientedVolume || + if ((aType == SMESH::NODE && (aCriterionType == SMESH::FT_FreeNodes || + aCriterionType == SMESH::FT_EqualNodes )) || + (aType == SMESH::EDGE && (aCriterionType == SMESH::FT_FreeBorders || + aCriterionType == SMESH::FT_EqualEdges )) || + (aType == SMESH::FACE && (aCriterionType == SMESH::FT_BareBorderFace || + aCriterionType == SMESH::FT_OverConstrainedFace || + aCriterionType == SMESH::FT_FreeEdges || + aCriterionType == SMESH::FT_FreeFaces || + aCriterionType == SMESH::FT_EqualFaces)) || + (aType == SMESH::VOLUME && (aCriterionType == SMESH::FT_BadOrientedVolume || + aCriterionType == SMESH::FT_OverConstrainedVolume || + aCriterionType == SMESH::FT_BareBorderVolume || + aCriterionType == SMESH::FT_EqualVolumes )) || aCriterionType == SMESH::FT_LinearOrQuadratic || aCriterionType == SMESH::FT_GroupColor || - aCriterionType == SMESH::FT_ElemGeomType) + aCriterionType == SMESH::FT_ElemGeomType || + aCriterionType == SMESH::FT_CoplanarFaces + ) { bool isSignalsBlocked = aTable->signalsBlocked(); aTable->blockSignals( true ); @@ -1418,8 +1768,10 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con if (aCompareItem->count() > 0) aCompareItem->clear(); aTable->setEditable(false, row, 1); + aTable->item(row, 2)->setText( QString("") ); aTable->setEditable(aCriterionType == SMESH::FT_GroupColor || - aCriterionType == SMESH::FT_ElemGeomType, row, 2); + aCriterionType == SMESH::FT_ElemGeomType || + aCriterionType == SMESH::FT_CoplanarFaces, row, 2); aTable->blockSignals( isSignalsBlocked ); } else if (aCriterionType == SMESH::FT_RangeOfIds || @@ -1445,19 +1797,21 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con } else { - if (aCompareItem->count() != 3) + if (aCompareItem && aCompareItem->count() != 3) { aCompareItem->setItems(getCompare()); } - QString aText = aTable->text(row, 2); - bool isOk = false; - aText.toDouble(&isOk); - aTable->item( row, 2 )->setText(isOk ? aText : QString("")); - if (!aTable->isEditable(row, 1)) - aTable->setEditable(true, row, 1); - if (!aTable->isEditable(row, 2)) - aTable->setEditable(true, row, 2); + if (aTable->item( row, 2 )) { + QString aText = aTable->text(row, 2); + bool isOk = false; + aText.toDouble(&isOk); + aTable->item( row, 2 )->setText(isOk ? aText : QString("")); + if (!aTable->isEditable(row, 1)) + aTable->setEditable(true, row, 1); + if (!aTable->isEditable(row, 2)) + aTable->setEditable(true, row, 2); + } } updateAdditionalWidget(); @@ -1472,7 +1826,8 @@ void SMESHGUI_FilterTable::onCriterionChanged (const int row, const int col, con //======================================================================= void SMESHGUI_FilterTable::onCriterionChanged (int row, int col) { - onCriterionChanged(row, col, -1); + if( col == 0 ) + onCriterionChanged(row, col, -1); } //======================================================================= @@ -1615,6 +1970,7 @@ const QMap& SMESHGUI_FilterTable::getSupportedTypes() const if (aTypes.isEmpty()) { aTypes[ SMESH::NODE ] = tr("NODES"); + aTypes[ SMESH::BALL ] = tr("BALLS"); aTypes[ SMESH::EDGE ] = tr("EDGES"); aTypes[ SMESH::FACE ] = tr("FACES"); aTypes[ SMESH::VOLUME ] = tr("VOLUMES"); @@ -1643,6 +1999,7 @@ const QMap& SMESHGUI_FilterTable::getCriteria (const int theType) aCriteria[ SMESH::FT_LyingOnGeom ] = tr("LYING_ON_GEOM"); aCriteria[ SMESH::FT_FreeNodes ] = tr("FREE_NODES"); aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); + aCriteria[ SMESH::FT_EqualNodes ] = tr("EQUAL_NODE"); } return aCriteria; } @@ -1663,6 +2020,7 @@ const QMap& SMESHGUI_FilterTable::getCriteria (const int theType) aCriteria[ SMESH::FT_LinearOrQuadratic ] = tr("LINEAR"); aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); aCriteria[ SMESH::FT_ElemGeomType ] = tr("GEOM_TYPE"); + aCriteria[ SMESH::FT_EqualEdges ] = tr("EQUAL_EDGE"); } return aCriteria; } @@ -1677,6 +2035,7 @@ const QMap& SMESHGUI_FilterTable::getCriteria (const int theType) aCriteria[ SMESH::FT_Taper ] = tr("TAPER"); aCriteria[ SMESH::FT_Skew ] = tr("SKEW"); aCriteria[ SMESH::FT_Area ] = tr("AREA"); + aCriteria[ SMESH::FT_MaxElementLength2D ] = tr("MAX_ELEMENT_LENGTH_2D"); aCriteria[ SMESH::FT_FreeEdges ] = tr("FREE_EDGES"); aCriteria[ SMESH::FT_RangeOfIds ] = tr("RANGE_OF_IDS"); aCriteria[ SMESH::FT_BelongToGeom ] = tr("BELONG_TO_GEOM"); @@ -1687,9 +2046,13 @@ const QMap& SMESHGUI_FilterTable::getCriteria (const int theType) aCriteria[ SMESH::FT_Length2D ] = tr("LENGTH2D"); aCriteria[ SMESH::FT_MultiConnection2D ] = tr("MULTI2D_BORDERS"); aCriteria[ SMESH::FT_FreeFaces ] = tr("FREE_FACES"); + aCriteria[ SMESH::FT_BareBorderFace ] = tr("BARE_BORDER_FACE"); + aCriteria[ SMESH::FT_OverConstrainedFace] = tr("OVER_CONSTRAINED_FACE"); aCriteria[ SMESH::FT_LinearOrQuadratic ] = tr("LINEAR"); aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); aCriteria[ SMESH::FT_ElemGeomType ] = tr("GEOM_TYPE"); + aCriteria[ SMESH::FT_CoplanarFaces ] = tr("COPLANAR_FACES"); + aCriteria[ SMESH::FT_EqualFaces ] = tr("EQUAL_FACE"); } return aCriteria; } @@ -1698,15 +2061,48 @@ const QMap& SMESHGUI_FilterTable::getCriteria (const int theType) static QMap aCriteria; if (aCriteria.isEmpty()) { - aCriteria[ SMESH::FT_AspectRatio3D ] = tr("ASPECT_RATIO_3D"); - aCriteria[ SMESH::FT_RangeOfIds ] = tr("RANGE_OF_IDS"); - aCriteria[ SMESH::FT_BelongToGeom ] = tr("BELONG_TO_GEOM"); - aCriteria[ SMESH::FT_LyingOnGeom ] = tr("LYING_ON_GEOM"); - aCriteria[ SMESH::FT_BadOrientedVolume ] = tr("BAD_ORIENTED_VOLUME"); - aCriteria[ SMESH::FT_Volume3D ] = tr("VOLUME_3D"); - aCriteria[ SMESH::FT_LinearOrQuadratic ] = tr("LINEAR"); - aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); - aCriteria[ SMESH::FT_ElemGeomType ] = tr("GEOM_TYPE"); + aCriteria[ SMESH::FT_AspectRatio3D ] = tr("ASPECT_RATIO_3D"); + aCriteria[ SMESH::FT_RangeOfIds ] = tr("RANGE_OF_IDS"); + aCriteria[ SMESH::FT_BelongToGeom ] = tr("BELONG_TO_GEOM"); + aCriteria[ SMESH::FT_LyingOnGeom ] = tr("LYING_ON_GEOM"); + aCriteria[ SMESH::FT_BadOrientedVolume ] = tr("BAD_ORIENTED_VOLUME"); + aCriteria[ SMESH::FT_BareBorderVolume ] = tr("BARE_BORDER_VOLUME"); + aCriteria[ SMESH::FT_OverConstrainedVolume] = tr("OVER_CONSTRAINED_VOLUME"); + aCriteria[ SMESH::FT_Volume3D ] = tr("VOLUME_3D"); + aCriteria[ SMESH::FT_MaxElementLength3D ] = tr("MAX_ELEMENT_LENGTH_3D"); + aCriteria[ SMESH::FT_LinearOrQuadratic ] = tr("LINEAR"); + aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); + aCriteria[ SMESH::FT_ElemGeomType ] = tr("GEOM_TYPE"); + aCriteria[ SMESH::FT_EqualVolumes ] = tr("EQUAL_VOLUME"); + } + return aCriteria; + } + else if (theType == SMESH::ELEM0D) + { + static QMap aCriteria; + if (aCriteria.isEmpty()) + { + aCriteria[ SMESH::FT_RangeOfIds ] = tr("RANGE_OF_IDS"); + aCriteria[ SMESH::FT_BelongToGeom ] = tr("BELONG_TO_GEOM"); + aCriteria[ SMESH::FT_BelongToPlane ] = tr("BELONG_TO_PLANE"); + aCriteria[ SMESH::FT_BelongToCylinder ] = tr("BELONG_TO_CYLINDER"); + aCriteria[ SMESH::FT_BelongToGenSurface ] = tr("BELONG_TO_GENSURFACE"); + aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); + } + return aCriteria; + } + else if (theType == SMESH::BALL) + { + static QMap aCriteria; + if (aCriteria.isEmpty()) + { + aCriteria[ SMESH::FT_BallDiameter ] = tr("BALL_DIAMETER"); + aCriteria[ SMESH::FT_RangeOfIds ] = tr("RANGE_OF_IDS"); + aCriteria[ SMESH::FT_BelongToGeom ] = tr("BELONG_TO_GEOM"); + aCriteria[ SMESH::FT_BelongToPlane ] = tr("BELONG_TO_PLANE"); + aCriteria[ SMESH::FT_BelongToCylinder ] = tr("BELONG_TO_CYLINDER"); + aCriteria[ SMESH::FT_BelongToGenSurface ] = tr("BELONG_TO_GENSURFACE"); + aCriteria[ SMESH::FT_GroupColor ] = tr("GROUP_COLOR"); } return aCriteria; } @@ -1773,7 +2169,7 @@ SMESHGUI_FilterTable::Table* SMESHGUI_FilterTable::createTable (QWidget* thePar } static int aLenCr = qAbs( aMaxLenCr - - aMetrics.width(tr("CRITERION"))) / aMetrics.width(' ') + 5; + aMetrics.width(tr("CRITERION"))) / aMetrics.width(' ') + 5; QString aCrStr; aCrStr.fill(' ', aLenCr); @@ -1803,10 +2199,10 @@ SMESHGUI_FilterTable::Table* SMESHGUI_FilterTable::createTable (QWidget* thePar aTable->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); connect(aTable, SIGNAL(cellChanged(int, int)), - this, SLOT(onCriterionChanged(int, int))); + this, SLOT(onCriterionChanged(int, int))); connect(aTable, SIGNAL(currentCellChanged(int, int, int, int)), - this, SLOT(onCurrentChanged(int, int))); + this, SLOT(onCurrentChanged(int, int))); return aTable; } @@ -1836,15 +2232,15 @@ void SMESHGUI_FilterTable::SetEditable (const bool isEditable) Table* aTable = anIter.value(); for (int i = 0, n = aTable->rowCount(); i < n; i++) for (int j = 0, m = aTable->columnCount(); j < m; j++) - { - QTableWidgetItem* anItem = aTable->item(i, j); - if ( dynamic_cast( anItem ) ) { - Qt::ItemFlags f = anItem->flags(); - if (!isEditable) f = f & ~Qt::ItemIsUserCheckable; - else f = f | Qt::ItemIsUserCheckable; - anItem->setFlags( f ); - } - } + { + QTableWidgetItem* anItem = aTable->item(i, j); + if ( dynamic_cast( anItem ) ) { + Qt::ItemFlags f = anItem->flags(); + if (!isEditable) f = f & ~Qt::ItemIsUserCheckable; + else f = f | Qt::ItemIsUserCheckable; + anItem->setFlags( f ); + } + } //end of IPAL19974 if (isEditable) @@ -2017,8 +2413,8 @@ bool SMESHGUI_FilterTable::GetThreshold (const int theRow, // Purpose : Set text and internal value in cell of ID value //======================================================================= void SMESHGUI_FilterTable::SetID( const int theRow, - const QString& theText, - const int theEntityType ) + const QString& theText, + const int theEntityType ) { Table* aTable = myTables[ theEntityType == -1 ? GetType() : theEntityType ]; aTable->item( theRow, 5 )->setText( theText ); @@ -2029,8 +2425,8 @@ void SMESHGUI_FilterTable::SetID( const int theRow, // Purpose : Get text and internal value from cell of ID value //======================================================================= bool SMESHGUI_FilterTable::GetID( const int theRow, - QString& theText, - const int theEntityType ) + QString& theText, + const int theEntityType ) { Table* aTable = myTables[ theEntityType == -1 ? GetType() : theEntityType ]; QTableWidgetItem* anItem = aTable->item( theRow, 5 ); @@ -2057,7 +2453,8 @@ SMESHGUI_FilterDlg::SMESHGUI_FilterDlg( SMESHGUI* theModule, const QList& theTypes ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myInitSourceWgOnApply( true ) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) mySelector = aViewWindow->GetSelector(); @@ -2073,7 +2470,8 @@ SMESHGUI_FilterDlg::SMESHGUI_FilterDlg( SMESHGUI* theModule, const int theType ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myInitSourceWgOnApply( true ) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) mySelector = aViewWindow->GetSelector(); @@ -2180,7 +2578,7 @@ QWidget* SMESHGUI_FilterDlg::createSourceGroup (QWidget* theParent) mySourceGrp->addButton(aSelBtn, Selection); mySourceGrp->addButton(aDlgBtn, Dialog); - aSelBtn->setChecked(true); + aMeshBtn->setChecked(true); return aBox; } @@ -2191,19 +2589,15 @@ QWidget* SMESHGUI_FilterDlg::createSourceGroup (QWidget* theParent) //======================================================================= void SMESHGUI_FilterDlg::updateMainButtons() { + myButtons[ BTN_Close ]->show(); if (myTypes.count() == 1) { - myButtons[ BTN_Cancel ]->show(); myButtons[ BTN_Apply ]->hide(); - myButtons[ BTN_Close ]->hide(); } else { - myButtons[ BTN_Cancel ]->hide(); myButtons[ BTN_Apply ]->show(); - myButtons[ BTN_Close ]->show(); } - // updateGeometry(); } @@ -2220,7 +2614,6 @@ QWidget* SMESHGUI_FilterDlg::createButtonFrame (QWidget* theParent) myButtons[ BTN_OK ] = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aGrp); myButtons[ BTN_Apply ] = new QPushButton(tr("SMESH_BUT_APPLY"), aGrp); - myButtons[ BTN_Cancel ] = new QPushButton(tr("SMESH_BUT_CANCEL"), aGrp); myButtons[ BTN_Close ] = new QPushButton(tr("SMESH_BUT_CLOSE"), aGrp); myButtons[ BTN_Help ] = new QPushButton(tr("SMESH_BUT_HELP"), aGrp); @@ -2229,12 +2622,10 @@ QWidget* SMESHGUI_FilterDlg::createButtonFrame (QWidget* theParent) aLay->addWidget(myButtons[ BTN_Apply ]); aLay->addSpacing(10); aLay->addStretch(); - aLay->addWidget(myButtons[ BTN_Cancel ]); aLay->addWidget(myButtons[ BTN_Close ]); aLay->addWidget(myButtons[ BTN_Help ]); connect(myButtons[ BTN_OK ], SIGNAL(clicked()), SLOT(onOk())); - connect(myButtons[ BTN_Cancel ], SIGNAL(clicked()), SLOT(onClose())); connect(myButtons[ BTN_Close ], SIGNAL(clicked()), SLOT(onClose())); connect(myButtons[ BTN_Apply ], SIGNAL(clicked()), SLOT(onApply())); connect(myButtons[ BTN_Help ], SIGNAL(clicked()), SLOT(onHelp())); @@ -2325,8 +2716,8 @@ void SMESHGUI_FilterDlg::Init (const QList& theTypes) mySetInViewer->setChecked(true); mySourceGrp->button(myApplyToState.contains(theTypes.first()) ? - myApplyToState[ theTypes.first() ] : - Selection)->setChecked(true); + myApplyToState[ theTypes.first() ] : + Mesh)->setChecked(true); } //======================================================================= @@ -2400,10 +2791,10 @@ void SMESHGUI_FilterDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -2506,31 +2897,18 @@ void SMESHGUI_FilterDlg::setIdsToWg (QWidget* theWg, const QList& theIds) if (theWg == 0) return; + QStringList aStrList; + foreach(int id, theIds) + aStrList << QString::number(id); + if (theWg->inherits("QListWidget")) { - QListWidget* aListBox = qobject_cast( theWg ); - aListBox->clear(); - - QStringList aStrList; - QList::const_iterator anIter; - for (anIter = theIds.begin(); anIter != theIds.end(); ++anIter) - aStrList.append(QString("%1").arg(*anIter)); - - aListBox->addItems(aStrList); + qobject_cast(theWg)->clear(); + qobject_cast(theWg)->addItems(aStrList); } else if (theWg->inherits("QLineEdit")) { - QLineEdit* aLineEdit = qobject_cast( theWg ); - QString aStr; - QList::const_iterator anIter; - - for (anIter = theIds.begin(); anIter != theIds.end(); ++ anIter) - aStr += QString("%1 ").arg(*anIter); - - if (!aStr.isEmpty()) - aStr.remove(aStr.length() - 1, 1); - - aLineEdit->setText(aStr); + qobject_cast( theWg )->setText(aStrList.join(" ")); } } @@ -2550,7 +2928,8 @@ bool SMESHGUI_FilterDlg::isValid() const aType == SMESH::FT_BelongToPlane || aType == SMESH::FT_BelongToCylinder || aType == SMESH::FT_BelongToGenSurface || - aType == SMESH::FT_LyingOnGeom) { + aType == SMESH::FT_LyingOnGeom) + { QString aName; myTable->GetThreshold(i, aName); @@ -2558,7 +2937,7 @@ bool SMESHGUI_FilterDlg::isValid() const SMESH::GetActiveStudyDocument()->FindObjectByName(aName.toLatin1().constData(), "GEOM"); if (aList.size() == 0) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("BAD_SHAPE_NAME").arg(aName)); + tr("BAD_SHAPE_NAME").arg(aName)); return false; } @@ -2574,31 +2953,54 @@ bool SMESHGUI_FilterDlg::isValid() const aFace.IsNull() || aFace.ShapeType() != TopAbs_FACE) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("SHAPE_IS_NOT_A_FACE").arg(aName)); + tr("SHAPE_IS_NOT_A_FACE").arg(aName)); return false; } Handle(Geom_Surface) aSurf = BRep_Tool::Surface(TopoDS::Face(aFace)); if (aSurf.IsNull()) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("SHAPE_IS_NOT_A_FACE").arg(aName)); + tr("SHAPE_IS_NOT_A_FACE").arg(aName)); return false; } if (aType == SMESH::FT_BelongToPlane && !aSurf->IsKind(STANDARD_TYPE(Geom_Plane))) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("SHAPE_IS_NOT_A_PLANE").arg(aName)); + tr("SHAPE_IS_NOT_A_PLANE").arg(aName)); return false; } if (aType == SMESH::FT_BelongToCylinder && !aSurf->IsKind(STANDARD_TYPE(Geom_CylindricalSurface))) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("SHAPE_IS_NOT_A_CYLINDER").arg(aName)); + tr("SHAPE_IS_NOT_A_CYLINDER").arg(aName)); return false; } } } } + else if (aType == SMESH::FT_CoplanarFaces) + { + QString faceID; + myTable->GetThreshold(i, faceID); + if ( faceID.isEmpty() ) + { + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), + tr("FACE_ID_NOT_SELECTED")); + return false; + } + if ( myMesh->_is_nil() ) + { + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), + tr("MESH_IS_NOT_SELECTED")); + return false; + } + if ( myMesh->GetElementType( faceID.toLong(), /*iselem=*/true) != SMESH::FACE ) + { + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), + tr("NOT_FACE_ID").arg(faceID)); + return false; + } + } } return true; @@ -2609,18 +3011,24 @@ bool SMESHGUI_FilterDlg::isValid() const // Purpose : Set widget of parent dialog containing idsto be filtered if // user select corresponding source radio button //======================================================================= -void SMESHGUI_FilterDlg::SetSourceWg (QWidget* theWg) +void SMESHGUI_FilterDlg::SetSourceWg (QWidget* theWg, + const bool initOnApply) { mySourceWg = theWg; + myInitSourceWgOnApply = initOnApply; } //======================================================================= -// name : SMESHGUI_FilterDlg::SetGroupIds +// name : SMESHGUI_FilterDlg::SetMesh // Purpose : Set mesh //======================================================================= void SMESHGUI_FilterDlg::SetMesh (SMESH::SMESH_Mesh_var theMesh) { - myMesh = theMesh; + if ( !theMesh->_is_nil() ) { + myMesh = theMesh; + if ( !myFilter[ myTable->GetType() ]->_is_nil()) + myFilter[ myTable->GetType() ]->SetMesh( theMesh ); + } const bool isEnable = !(myMesh->_is_nil()); myButtons[BTN_OK]->setEnabled(isEnable); myButtons[BTN_Apply]->setEnabled(isEnable); @@ -2637,7 +3045,7 @@ void SMESHGUI_FilterDlg::SetSelection() if (mySelectionMgr) { myIObjects.Clear(); - const SALOME_ListIO& anObjs = mySelector->StoredIObjects(); + const SALOME_ListIO& anObjs = mySelector->StoredIObjects(); SALOME_ListIteratorOfListIO anIter (anObjs); for ( ; anIter.More(); anIter.Next()) { TColStd_IndexedMapOfInteger aMap; @@ -2674,9 +3082,11 @@ bool SMESHGUI_FilterDlg::onApply() if (!myFilter[ aCurrType ]->GetPredicate()->_is_nil()) { QList aResultIds; filterSource(aCurrType, aResultIds); + // select in viewer selectInViewer(aCurrType, aResultIds); } + myInsertState[ aCurrType ] = mySetInViewer->isChecked(); myApplyToState[ aCurrType ] = mySourceGrp->checkedId(); } @@ -2725,6 +3135,36 @@ bool SMESHGUI_FilterDlg::createFilter (const int theType) return true; } +//================================================================================ +/*! + * \brief Return the current filter + */ +//================================================================================ + +SMESH::Filter_var SMESHGUI_FilterDlg::GetFilter() const +{ + SMESH::Filter_var filter; + try { + int aCurrType = myTable->GetType(); + filter = myFilter[ aCurrType ]; + } + catch(...) + { + } + return filter._retn(); +} + +//================================================================================ +/*! + * \brief Sets a filter to the table + */ +//================================================================================ + +void SMESHGUI_FilterDlg::SetFilter(SMESH::Filter_var filter, int type) +{ + myFilter[ type ] = filter; +} + //======================================================================= // name : SMESHGUI_FilterDlg::insertFilterInViewer // Purpose : Insert filter in viewer @@ -2787,15 +3227,15 @@ void SMESHGUI_FilterDlg::filterSource (const int theType, // filter ids SMESH::Predicate_ptr aPred = myFilter[ theType ]->GetPredicate(); - aPred->SetMesh(myMesh); + myFilter[ theType ]->SetMesh(myMesh); QList::const_iterator anIter; for (anIter = aDialogIds.begin(); anIter != aDialogIds.end(); ++ anIter) if (aPred->IsSatisfy(*anIter)) theResIds.append(*anIter); - - // set ids to the dialog - setIdsToWg(mySourceWg, theResIds); } + // set ids to the dialog + if (myInitSourceWgOnApply || aSourceId == Dialog) + setIdsToWg(mySourceWg, theResIds); } //======================================================================= @@ -2860,7 +3300,7 @@ void SMESHGUI_FilterDlg::filterSelectionSource (const int theType, // Filter entities SMESH::Predicate_ptr aPred = myFilter[ theType ]->GetPredicate(); - aPred->SetMesh(myMesh); + myFilter[ theType ]->SetMesh(myMesh); TColStd_MapIteratorOfMapOfInteger aResIter(aToBeFiltered); for ( ; aResIter.More(); aResIter.Next()) if (aPred->IsSatisfy(aResIter.Key())) @@ -2966,24 +3406,33 @@ void SMESHGUI_FilterDlg::onSelectionDone() } } - int aCriterionType = myTable->GetCriterionType(aRow); - if (aList.Extent() != 1 || - !myTable->CurrentCell(aRow, aCol) || - aCriterionType != SMESH::FT_BelongToGeom && - aCriterionType != SMESH::FT_BelongToPlane && - aCriterionType != SMESH::FT_BelongToCylinder && - aCriterionType != SMESH::FT_BelongToGenSurface && - aCriterionType != SMESH::FT_LyingOnGeom) + QList types; + types << SMESH::FT_BelongToGeom << SMESH::FT_BelongToPlane + << SMESH::FT_BelongToCylinder << SMESH::FT_BelongToGenSurface + << SMESH::FT_LyingOnGeom << SMESH::FT_CoplanarFaces; + if (aList.Extent() != 1 || !myTable->CurrentCell(aRow, aCol) || + !types.contains(myTable->GetCriterionType(aRow))) return; - Handle(SALOME_InteractiveObject) anIO = aList.First(); - GEOM::GEOM_Object_var anObj = SMESH::IObjectToInterface(anIO); - if (!anObj->_is_nil()) + if ( myTable->GetCriterionType(aRow) == SMESH::FT_CoplanarFaces ) + { + QString aString; + int nbElems = SMESH::GetNameOfSelectedElements(mySelector,//myViewWindow->GetSelector(), + aList.First(), aString); + if (nbElems == 1) + myTable->SetThreshold(aRow, aString); + } + else + { + Handle(SALOME_InteractiveObject) anIO = aList.First(); + GEOM::GEOM_Object_var anObj = SMESH::IObjectToInterface(anIO); + if (!anObj->_is_nil()) { myTable->SetThreshold(aRow, GEOMBase::GetName(anObj)); //myTable->SetID( aRow, GEOMBase::GetIORFromObject(anObj)); myTable->SetID(aRow, anIO->getEntry()); } + } } @@ -3014,9 +3463,9 @@ void SMESHGUI_FilterDlg::updateSelection() if (mySelectionMgr == 0) return; - TColStd_MapOfInteger allTypes; - for( int i=0; i<10; i++ ) - allTypes.Add( i ); +// TColStd_MapOfInteger allTypes; +// for( int i=0; i<10; i++ ) +// allTypes.Add( i ); SalomeApp_Study* aStudy = dynamic_cast( mySMESHGUI->application()->activeStudy() ); if( !aStudy ) return; @@ -3025,15 +3474,16 @@ void SMESHGUI_FilterDlg::updateSelection() mySelectionMgr->clearFilters(); int aRow, aCol; - + + bool isCurrentCell = myTable->CurrentCell(aRow, aCol); int aCriterionType = myTable->GetCriterionType(aRow); - if (myTable->CurrentCell(aRow, aCol) && + if ( isCurrentCell && (aCriterionType == SMESH::FT_BelongToGeom || aCriterionType == SMESH::FT_BelongToPlane || aCriterionType == SMESH::FT_BelongToCylinder || aCriterionType == SMESH::FT_BelongToGenSurface || - aCriterionType == SMESH::FT_LyingOnGeom)) { - + aCriterionType == SMESH::FT_LyingOnGeom)) + { if (aCriterionType == SMESH::FT_BelongToGeom || aCriterionType == SMESH::FT_BelongToGenSurface || aCriterionType == SMESH::FT_LyingOnGeom) { @@ -3048,9 +3498,12 @@ void SMESHGUI_FilterDlg::updateSelection() } myIsSelectionChanged = true; - } else { + } + else + { if (myIsSelectionChanged) { - mySelectionMgr->installFilter( new GEOM_TypeFilter( aStudy, -1 ) ); // This filter deactivates selection + // mySelectionMgr->installFilter( new GEOM_TypeFilter( aStudy, -1 ) ); // This filter deactivates selection + // Impossible to select any object in the OB on the second opening of FilterDlg } } } diff --git a/src/SMESHGUI/SMESHGUI_FilterDlg.h b/src/SMESHGUI/SMESHGUI_FilterDlg.h index ed1c95e9d..ace63067b 100755 --- a/src/SMESHGUI/SMESHGUI_FilterDlg.h +++ b/src/SMESHGUI/SMESHGUI_FilterDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -71,6 +72,8 @@ class SMESHGUI_EXPORT SMESHGUI_FilterTable : public QWidget class Table; class ComboItem; + class IntSpinItem; + class DoubleSpinItem; class CheckItem; class AdditionalWidget; class ComboDelegate; @@ -123,12 +126,12 @@ public: const int = -1 ); void SetID( const int, - const QString&, - const int = -1 ); + const QString&, + const int = -1 ); bool GetID( const int, - QString&, - const int = -1 ); + QString&, + const int = -1 ); void Update(); @@ -167,6 +170,7 @@ private: void updateBtnState(); void removeAdditionalWidget( QTableWidget*, const int ); void updateAdditionalWidget(); + const char* getPrecision( const int ); const QMap& getSupportedTypes() const; @@ -211,7 +215,7 @@ class SMESHGUI_FilterDlg : public QDialog enum { Mesh, Selection, Dialog, None }; // Buttons - enum { BTN_OK, BTN_Cancel, BTN_Apply, BTN_Close, BTN_Help }; + enum { BTN_OK, BTN_Apply, BTN_Close, BTN_Help }; public: SMESHGUI_FilterDlg( SMESHGUI*, const QList& ); @@ -223,10 +227,13 @@ public: void SetSelection(); void SetMesh (SMESH::SMESH_Mesh_var); - void SetSourceWg( QWidget* ); + void SetSourceWg( QWidget*, const bool initOnApply = true ); static SMESH::Filter::Criterion createCriterion(); + SMESH::Filter_var GetFilter() const; + void SetFilter(SMESH::Filter_var filter, int type); + signals: void Accepted(); @@ -285,6 +292,7 @@ private: LightApp_SelectionMgr* mySelectionMgr; SVTK_Selector* mySelector; SMESH::SMESH_Mesh_var myMesh; + bool myInitSourceWgOnApply; QWidget* mySourceWg; SALOME_DataMapOfIOMapOfInteger myIObjects; diff --git a/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.cxx b/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.cxx index 47c9e912c..c17dde1fa 100644 --- a/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterLibraryDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -226,7 +227,7 @@ QWidget* SMESHGUI_FilterLibraryDlg::createMainFrame (QWidget* theParent) connect(myOpenBtn, SIGNAL(clicked()), this, SLOT(onBrowse())); connect(myListBox, SIGNAL(itemSelectionChanged()), - this, SLOT(onFilterChanged())); + this, SLOT(onFilterChanged())); connect(myAddBtn, SIGNAL(clicked()), this, SLOT(onAddBtnPressed())); connect(myDeleteBtn, SIGNAL(clicked()), this, SLOT(onDeleteBtnPressed())); @@ -256,7 +257,6 @@ QWidget* SMESHGUI_FilterLibraryDlg::createButtonFrame (QWidget* theParent) myButtons[ BTN_OK ] = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aGrp); myButtons[ BTN_Apply ] = new QPushButton(tr("SMESH_BUT_APPLY"), aGrp); - myButtons[ BTN_Cancel ] = new QPushButton(tr("SMESH_BUT_CANCEL"), aGrp); myButtons[ BTN_Close ] = new QPushButton(tr("SMESH_BUT_CLOSE"), aGrp); myButtons[ BTN_Help ] = new QPushButton(tr("SMESH_BUT_HELP"), aGrp); @@ -265,12 +265,10 @@ QWidget* SMESHGUI_FilterLibraryDlg::createButtonFrame (QWidget* theParent) aLay->addWidget(myButtons[ BTN_Apply ]); aLay->addSpacing(10); aLay->addStretch(); - aLay->addWidget(myButtons[ BTN_Cancel ]); aLay->addWidget(myButtons[ BTN_Close ]); aLay->addWidget(myButtons[ BTN_Help ]); connect(myButtons[ BTN_OK ], SIGNAL(clicked()), SLOT(onOk())); - connect(myButtons[ BTN_Cancel ], SIGNAL(clicked()), SLOT(onClose())); connect(myButtons[ BTN_Close ], SIGNAL(clicked()), SLOT(onClose())); connect(myButtons[ BTN_Apply ], SIGNAL(clicked()), SLOT(onApply())); connect(myButtons[ BTN_Help ], SIGNAL(clicked()), SLOT(onHelp())); @@ -290,14 +288,11 @@ QWidget* SMESHGUI_FilterLibraryDlg::createButtonFrame (QWidget* theParent) //======================================================================= void SMESHGUI_FilterLibraryDlg::updateMainButtons() { + myButtons[ BTN_Close ]->show(); if (myTypes.count() == 1) { - myButtons[ BTN_Cancel ]->show(); myButtons[ BTN_Apply ]->hide(); - myButtons[ BTN_Close ]->hide(); } else { - myButtons[ BTN_Cancel ]->hide(); myButtons[ BTN_Apply ]->show(); - myButtons[ BTN_Close ]->show(); } } @@ -423,7 +418,7 @@ bool SMESHGUI_FilterLibraryDlg::onApply() if (myLibrary->_is_nil()) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_WRN_WARNING"), - tr("LIBRARY_IS_NOT_LOADED")); + tr("LIBRARY_IS_NOT_LOADED")); return false; } @@ -437,11 +432,11 @@ bool SMESHGUI_FilterLibraryDlg::onApply() } else if (myMode == EDIT || myMode == ADD_TO) { SMESH::Filter_var aFilter = createFilter(); if (!myListBox->selectedItems().empty() && - !myLibrary->Replace(myCurrFilterName.toLatin1().constData(), - myName->text().toLatin1().constData(), - aFilter.in())) { + !myLibrary->Replace(myCurrFilterName.toLatin1().constData(), + myName->text().toLatin1().constData(), + aFilter.in())) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("ERROR_OF_EDITING")); + tr("ERROR_OF_EDITING")); aResult = false; } else @@ -457,7 +452,7 @@ bool SMESHGUI_FilterLibraryDlg::onApply() delete aFileName; } else if (myMode != COPY_FROM) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("ERROR_OF_SAVING")); + tr("ERROR_OF_SAVING")); } else { } @@ -507,10 +502,10 @@ void SMESHGUI_FilterLibraryDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -674,7 +669,7 @@ void SMESHGUI_FilterLibraryDlg::processNewLibrary() if (myLibrary->_is_nil()) { if (myMode == COPY_FROM) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("ERROR_LOAD")); + tr("ERROR_LOAD")); return; } else { myLibrary = aFilterMgr->CreateLibrary(); @@ -730,7 +725,7 @@ bool SMESHGUI_FilterLibraryDlg::isNameValid(const bool theMess) const if (aCurrName.isEmpty()) { if (theMess) SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("EMPTY_FILTER_NAME")); + tr("EMPTY_FILTER_NAME")); return false; } @@ -739,7 +734,7 @@ bool SMESHGUI_FilterLibraryDlg::isNameValid(const bool theMess) const if (aNames[ f ] == aCurrName && aNames[ f ] != myCurrFilterName) { if (theMess) SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INSUFFICIENT_DATA"), - tr("ERROR_FILTER_NAME")); + tr("ERROR_FILTER_NAME")); return false; } } @@ -787,7 +782,7 @@ bool SMESHGUI_FilterLibraryDlg::isPermissionValid(const bool theIsExistingOnly) if (!isWritable) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_WRN_WARNING"), - tr("NO_PERMISSION")); + tr("NO_PERMISSION")); return false; } @@ -831,8 +826,8 @@ void SMESHGUI_FilterLibraryDlg::onFilterChanged() SMESH::Filter_var aFilter = createFilter(); myLibrary->Replace(myCurrFilterName.toLatin1().constData(), - myName->text().toLatin1().constData(), - aFilter); + myName->text().toLatin1().constData(), + aFilter); } // Fill table with filter parameters @@ -938,8 +933,8 @@ void SMESHGUI_FilterLibraryDlg::onAddBtnPressed() SMESH::Filter_var aFilter = createFilter(); myLibrary->Replace(myCurrFilterName.toLatin1().constData(), - myName->text().toLatin1().constData(), - aFilter); + myName->text().toLatin1().constData(), + aFilter); } myTable->Clear(myTable->GetType()); @@ -955,7 +950,7 @@ void SMESHGUI_FilterLibraryDlg::addFilterToLib (const QString& theName) { if (myLibrary->_is_nil()) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_WRN_WARNING"), - tr("LIBRARY_IS_NOT_LOADED")); + tr("LIBRARY_IS_NOT_LOADED")); return; } @@ -979,7 +974,7 @@ void SMESHGUI_FilterLibraryDlg::addFilterToLib (const QString& theName) if (!aResult) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("ERROR_OF_ADDING")); + tr("ERROR_OF_ADDING")); } updateList(); @@ -989,7 +984,7 @@ void SMESHGUI_FilterLibraryDlg::addFilterToLib (const QString& theName) if (theName != aName) SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_WARNING"), - tr("ASSIGN_NEW_NAME").arg(theName).arg(aName)); + tr("ASSIGN_NEW_NAME").arg(theName).arg(aName)); } //======================================================================= @@ -1089,7 +1084,7 @@ void SMESHGUI_FilterLibraryDlg::onDeleteBtnPressed() { if (myLibrary->_is_nil()) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_WRN_WARNING"), - tr("LIBRARY_IS_NOT_LOADED")); + tr("LIBRARY_IS_NOT_LOADED")); return; } @@ -1097,7 +1092,7 @@ void SMESHGUI_FilterLibraryDlg::onDeleteBtnPressed() if (anIndex == -1 || !myLibrary->Delete(myCurrFilterName.toLatin1().constData())) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("ERROR_OF_DELETING")); + tr("ERROR_OF_DELETING")); } else { myCurrFilterName = ""; myCurrFilter = -1; @@ -1188,8 +1183,8 @@ void SMESHGUI_FilterLibraryDlg::onNeedValidation() { SMESH::Filter_var aFilter = createFilter(myTable->GetType()); myLibrary->Replace(myCurrFilterName.toLatin1().constData(), - myName->text().toLatin1().constData(), - aFilter); + myName->text().toLatin1().constData(), + aFilter); } } } diff --git a/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.h b/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.h index eb9fe6134..5b5af4fd0 100644 --- a/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.h +++ b/src/SMESHGUI/SMESHGUI_FilterLibraryDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterLibraryDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -55,7 +56,7 @@ class SMESHGUI_EXPORT SMESHGUI_FilterLibraryDlg : public QDialog Q_OBJECT // Buttons - enum { BTN_OK, BTN_Cancel, BTN_Apply, BTN_Close, BTN_Help }; + enum { BTN_OK, BTN_Apply, BTN_Close, BTN_Help }; class Dialog; diff --git a/src/SMESHGUI/SMESHGUI_FilterUtils.cxx b/src/SMESHGUI/SMESHGUI_FilterUtils.cxx index d7ce02e58..44ca767f1 100644 --- a/src/SMESHGUI/SMESHGUI_FilterUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_FilterUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterUtils.cxx // Author : Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_FilterUtils.h b/src/SMESHGUI/SMESHGUI_FilterUtils.h index aa4cfd0be..dd3103d35 100644 --- a/src/SMESHGUI/SMESHGUI_FilterUtils.h +++ b/src/SMESHGUI/SMESHGUI_FilterUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_FilterUtils.h // Author : Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.cxx b/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.cxx new file mode 100644 index 000000000..89515cad6 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.cxx @@ -0,0 +1,553 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_FindElemByPointDlg.cxx +// Author : Edward AGAPOV, Open CASCADE S.A.S. +// + +// SMESH includes +#include "SMESHGUI_FindElemByPointDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_SpinBox.h" +#include "SMESHGUI_MeshEditPreview.h" + +#include "SMESH_Actor.h" +#include "SMESH_ActorUtils.h" +#include "SMESH_TypeFilter.hxx" +#include "SMESH_LogicalFilter.hxx" + +// SALOME GUI includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VTK includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) + +#define SPACING 6 +#define MARGIN 11 + +//======================================================================= +/*! + * \brief Dialog to find elements by a point coordinates + */ +//======================================================================= + +SMESHGUI_FindElemByPointDlg::SMESHGUI_FindElemByPointDlg() + : SMESHGUI_Dialog( 0, false, true, OK | Help ) +{ + setWindowTitle(tr("CAPTION")); + + QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame()); + aDlgLay->setMargin(0); + aDlgLay->setSpacing(SPACING); + + QWidget* aMainFrame = createMainFrame (mainFrame()); + + aDlgLay->addWidget(aMainFrame); + aDlgLay->setStretchFactor(aMainFrame, 1); +} + +//================================================================================ +/*! + * \brief Set types of elements available for search + */ +//================================================================================ + +void SMESHGUI_FindElemByPointDlg::setTypes(SMESH::array_of_ElementType_var & types) +{ + myElemTypeCombo->blockSignals(true); + myElemTypeCombo->clear(); + int nbTypes = 0, hasNodes = 0; + for ( int i = 0; i < types->length(); ++i ) + { + switch ( types[i] ) { + case SMESH::NODE: + myElemTypeCombo->addItem( tr( "MEN_NODE" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::NODE )); + hasNodes = 1; + break; + case SMESH::EDGE: + myElemTypeCombo->addItem( tr( "MEN_EDGE" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::EDGE )); + break; + case SMESH::FACE: + myElemTypeCombo->addItem( tr( "MEN_FACE" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::FACE )); + break; + case SMESH::VOLUME: + myElemTypeCombo->addItem( tr( "MEN_VOLUME_3D" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::VOLUME )); + break; + case SMESH::ELEM0D: + myElemTypeCombo->addItem( tr( "MEN_ELEM0D" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::ELEM0D )); + break; + case SMESH::BALL: + myElemTypeCombo->addItem( tr( "MEN_BALL" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::BALL )); + break; + default:; + } + } + if ( nbTypes - hasNodes > 1 ) + { + myElemTypeCombo->addItem( tr( "MEN_ALL" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::ALL )); + } + if ( !hasNodes && nbTypes > 0 ) + { + myElemTypeCombo->addItem( tr( "MEN_NODE" )); + myElemTypeCombo->setId( nbTypes++, int( SMESH::NODE )); + } + myElemTypeCombo->blockSignals(false); +} + +//======================================================================= +// function : createMainFrame() +// purpose : Create frame containing dialog's input fields +//======================================================================= +QWidget* SMESHGUI_FindElemByPointDlg::createMainFrame (QWidget* theParent) +{ + QWidget* aFrame = new QWidget(theParent); + + //mesh name + + QGroupBox* aMeshGrp = new QGroupBox(tr("MESH_GROUP"), aFrame); + QHBoxLayout* aMeshGrpLayout = new QHBoxLayout(aMeshGrp); + aMeshGrpLayout->setMargin(MARGIN); + aMeshGrpLayout->setSpacing(SPACING); + + myMeshName = new QLineEdit(aMeshGrp); + aMeshGrpLayout->addWidget(myMeshName); + + // coordinates + + QGroupBox* aCoordGrp = new QGroupBox(tr("SMESH_COORDINATES"), aFrame); + QHBoxLayout* aCoordGrpLayout = new QHBoxLayout(aCoordGrp); + aCoordGrpLayout->setMargin(MARGIN); + aCoordGrpLayout->setSpacing(SPACING); + + QLabel* aXLabel = new QLabel(tr("SMESH_X"), aCoordGrp); + myX = new SMESHGUI_SpinBox(aCoordGrp); + + QLabel* aYLabel = new QLabel(tr("SMESH_Y"), aCoordGrp); + myY = new SMESHGUI_SpinBox(aCoordGrp); + + QLabel* aZLabel = new QLabel(tr("SMESH_Z"), aCoordGrp); + myZ = new SMESHGUI_SpinBox(aCoordGrp); + + myX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myX->SetValue(0); + myY->SetValue(0); + myZ->SetValue(0); + + aCoordGrpLayout->addWidget(aXLabel); + aCoordGrpLayout->addWidget(myX); + aCoordGrpLayout->addWidget(aYLabel); + aCoordGrpLayout->addWidget(myY); + aCoordGrpLayout->addWidget(aZLabel); + aCoordGrpLayout->addWidget(myZ); + + // Elements + + QGroupBox* elementGrp = new QGroupBox(tr("Elements"), aFrame); + QGridLayout* elementGrpLayout = new QGridLayout(elementGrp); + elementGrpLayout->setSpacing(SPACING); + elementGrpLayout->setMargin(MARGIN); + + + myFindBtn = new QPushButton(elementGrp); + myFindBtn->setText(tr("Find")); + //myFindBtn->setCheckable(false); + + myElemTypeCombo = new QtxComboBox(elementGrp); + + myFoundList = new QListWidget(elementGrp); + + elementGrpLayout->addWidget( myFindBtn, 0, 0 ); + elementGrpLayout->addWidget( myElemTypeCombo, 0, 1 ); + elementGrpLayout->addWidget( myFoundList, 1, 0, 2, 2 ); + + + QVBoxLayout* aLay = new QVBoxLayout(aFrame); + aLay->setMargin( 0 ); + aLay->setSpacing( SPACING ); + aLay->addWidget(aMeshGrp); + aLay->addWidget(aCoordGrp); + aLay->addWidget(elementGrp); + + // OK instead of "Apply and Close" + if ( QAbstractButton* but = button(OK) ) + but->setText( tr("SMESH_BUT_OK")); + + return aFrame; +} + +//================================================================================ +/*! + * \brief Constructor +*/ +//================================================================================ + +SMESHGUI_FindElemByPointOp::SMESHGUI_FindElemByPointOp() + :SMESHGUI_SelectionOp() +{ + mySimulation = 0; + myDlg = new SMESHGUI_FindElemByPointDlg; + myHelpFileName = "find_element_by_point_page.html"; + + QList filters; + filters.append( new SMESH_TypeFilter( MESH ) ); + filters.append( new SMESH_TypeFilter( GROUP ) ); + myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); + + myPreview = new SMESH::MeshPreviewStruct(); + + myPreview->nodesXYZ.length(1); + myPreview->nodesXYZ[0].x = myDlg->myX->GetValue(); + myPreview->nodesXYZ[0].y = myDlg->myY->GetValue(); + myPreview->nodesXYZ[0].z = myDlg->myZ->GetValue(); + + myPreview->elementTypes.length(1); + myPreview->elementTypes[0].SMDS_ElementType = SMESH::NODE; + myPreview->elementTypes[0].isPoly = false; + myPreview->elementTypes[0].nbNodesInElement = 1; + + myPreview->elementConnectivities.length(1); + myPreview->elementConnectivities[0] = 0; + + // connect signals and slots + connect(myDlg->myFindBtn, SIGNAL(clicked()), this, SLOT(onFind())); + connect(myDlg->myX, SIGNAL(valueChanged(double)), this, SLOT(redisplayPreview())); + connect(myDlg->myY, SIGNAL(valueChanged(double)), this, SLOT(redisplayPreview())); + connect(myDlg->myZ, SIGNAL(valueChanged(double)), this, SLOT(redisplayPreview())); + connect(myDlg->myFoundList, SIGNAL(itemSelectionChanged()), this, SLOT(onElemSelected())); + connect(myDlg->myElemTypeCombo,SIGNAL(currentIndexChanged(int)),this, SLOT(onElemTypeChange(int))); + connect(myDlg, SIGNAL(rejectedDlg()), this, SLOT(onRejectedDlg())); +} + +//======================================================================= +// function : startOperation() +// purpose : Init dialog fields, connect signals and slots, show dialog +//======================================================================= +void SMESHGUI_FindElemByPointOp::startOperation() +{ + // init simulation with a current View + if ( mySimulation ) delete mySimulation; + mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( getSMESHGUI() )); + vtkProperty* aProp = vtkProperty::New(); + aProp->SetRepresentationToWireframe(); + aProp->SetColor(250, 0, 250); + aProp->SetPointSize(5); + aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1); + mySimulation->GetActor()->SetProperty(aProp); + aProp->Delete(); + + SMESHGUI_SelectionOp::startOperation(); + myDlg->show(); + redisplayPreview(); + + onSelectionDone(); // init myMeshOrPart +} + +//================================================================================ +/*! + * \brief Stops operation + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::stopOperation() +{ + if ( mySimulation ) + { + mySimulation->SetVisibility(false); + delete mySimulation; + mySimulation = 0; + } + selectionMgr()->removeFilter( myFilter ); + SMESHGUI_SelectionOp::stopOperation(); +} + +//================================================================================ +/*! + * \brief hilight found selected elements + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::onElemSelected() +{ + if ( !myMeshIO.IsNull() ) + { + Selection_Mode selMode = + myDlg->myElemTypeCombo->currentId() == int(SMESH::NODE) ? NodeSelection : CellSelection; + if ( selectionMode() != selMode ) + setSelectionMode( selMode ); + + QList ids = myDlg->myFoundList->selectedItems(); + QList::iterator id = ids.begin(); + TColStd_MapOfInteger idMap; + for ( ; id != ids.end(); ++id ) + idMap.Add( (*id)->text().toInt() ); + + addOrRemoveIndex( myMeshIO, idMap, false ); + + SALOME_ListIO aList; + aList.Append(myMeshIO); + selectionMgr()->setSelectedObjects(aList,false); + } +} + +//================================================================================ +/*! + * \brief Set selection mode according to element type + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::onElemTypeChange(int index) +{ + Selection_Mode newMode = (index == 1) ? NodeSelection : CellSelection; + if ( selectionMode() != newMode ) + { + selectionMgr()->clearFilters(); + setSelectionMode( newMode ); + } + myDlg->myFoundList->clear(); +} + +//================================================================================ +/*! + * \brief Method needed for internal cuisine + */ +//================================================================================ + +void SMESHGUI_FindElemByPointDlg::reject() +{ + emit rejectedDlg(); + QtxDialog::reject(); +} + +//================================================================================ +/*! + * \brief Method needed for internal cuisine + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::onRejectedDlg() +{ + myMeshIO.Nullify(); +} + +//================================================================================ +/*! + * \brief perform it's intention action: find elements + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::onFind() +{ + if ( myMeshOrPart->_is_nil() ) + return; + + try { + SUIT_OverrideCursor wc; + + SMESH::SMESH_Mesh_var aMesh = myMeshOrPart->GetMesh(); + if ( aMesh->_is_nil() ) + return; + SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); + if (aMeshEditor->_is_nil()) + return; + + SMESH::long_array_var foundIds; + if ( aMesh->_is_equivalent( myMeshOrPart ) ) + foundIds = + aMeshEditor->FindElementsByPoint( myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue(), + SMESH::ElementType( myDlg->myElemTypeCombo->currentId())); + else + foundIds = + aMeshEditor->FindAmongElementsByPoint( myMeshOrPart, + myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue(), + SMESH::ElementType( myDlg->myElemTypeCombo->currentId())); + myDlg->myFoundList->clear(); + for ( int i = 0; i < foundIds->length(); ++i ) + myDlg->myFoundList->addItem( QString::number( foundIds[i] )); + + if ( foundIds->length() > 0 ) + myDlg->myFoundList->setCurrentRow(0); + } + catch (const SALOME::SALOME_Exception& S_ex) { + SalomeApp_Tools::QtCatchCorbaException(S_ex); + } + catch (...) { + } +} + +//================================================================================ +/*! + * \brief Method needed for internal cuisine + */ +//================================================================================ + +bool SMESHGUI_FindElemByPointOp::onApply() +{ + onRejectedDlg(); + return true; +} + +//================================================================================ +/*! + * \brief SLOT called when selection changed + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::onSelectionDone() +{ + if ( !myDlg->isVisible() || !myDlg->isEnabled() ) + return; + + QString oldMeshEntry, newMeshEntry; + if ( !myMeshIO.IsNull() && myMeshIO->hasEntry() ) + oldMeshEntry = myMeshIO->getEntry(); + + try { + SALOME_ListIO aList; + selectionMgr()->selectedObjects(aList, SVTK_Viewer::Type()); + if (aList.Extent() == 1 && aList.First()->hasEntry()) + { + Handle(SALOME_InteractiveObject) anIO = aList.First(); + _PTR(SObject) pObj = studyDS()->FindObjectID(anIO->getEntry()); + CORBA::Object_var anObj = SMESH::IObjectToObject( anIO ); + newMeshEntry = anIO->getEntry(); + SMESH::SMESH_IDSource_var aMeshOrPart = SMESH::SMESH_IDSource::_narrow(anObj); + if ( pObj && !aMeshOrPart->_is_nil() && oldMeshEntry != newMeshEntry ) + { + myMeshOrPart = aMeshOrPart; + myMeshIO.Nullify(); + myMeshIO = anIO; + std::string name = pObj->GetName(); + myDlg->myMeshName->setText(""); + myDlg->myMeshName->setText( QString( name.c_str() ).trimmed() ); + SMESH::array_of_ElementType_var types = myMeshOrPart->GetTypes(); + myDlg->setTypes( types ); + } + } + } + catch (...) { + } + + if ( oldMeshEntry != newMeshEntry || newMeshEntry.isEmpty() ) + myDlg->myFoundList->clear(); + + myDlg->myFindBtn->setEnabled( !myMeshIO.IsNull() ); +} + +//================================================================================ +/*! + * \brief show point by coordinates + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::redisplayPreview() +{ + myDlg->myFoundList->clear(); + + myPreview->nodesXYZ[0].x = myDlg->myX->GetValue(); + myPreview->nodesXYZ[0].y = myDlg->myY->GetValue(); + myPreview->nodesXYZ[0].z = myDlg->myZ->GetValue(); + + mySimulation->SetData(&myPreview.in()); +} + +//================================================================================ +/*! + * \brief install filter on meshes + */ +//================================================================================ + +void SMESHGUI_FindElemByPointOp::activateSelection() +{ + selectionMgr()->clearFilters(); + selectionMgr()->installFilter( myFilter ); +} + +//================================================================================ +/*! + * \brief Destructor +*/ +//================================================================================ + +SMESHGUI_FindElemByPointOp::~SMESHGUI_FindElemByPointOp() +{ + if ( myDlg ) { delete myDlg; myDlg = 0; } + if ( mySimulation ) { delete mySimulation; mySimulation = 0; } + if ( myFilter ) + { + QList sub = ((SMESH_LogicalFilter*)myFilter)->getFilters(); + delete sub.front(); + delete sub.back(); + delete myFilter; + myFilter = 0; + } +} + +//================================================================================ +/*! + * \brief Gets dialog of this operation + * \retval LightApp_Dialog* - pointer to dialog of this operation + */ +//================================================================================ + +LightApp_Dialog* SMESHGUI_FindElemByPointOp::dlg() const +{ + return myDlg; +} + diff --git a/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.h b/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.h new file mode 100644 index 000000000..09990407e --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_FindElemByPointDlg.h @@ -0,0 +1,120 @@ +// Copyright (C) 2007-2012 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 +// + +#ifndef SMESHGUI_FindElemByPointDLG_H +#define SMESHGUI_FindElemByPointDLG_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +#include "SMESHGUI_Dialog.h" +#include "SMESHGUI_SelectionOp.h" + +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QLineEdit; +class QPushButton; +class QListWidget; +class QtxComboBox; +class SMESHGUI_SpinBox; +class SMESHGUI_MeshEditPreview; +class SMESHGUI_FindElemByPointDlg; + +/*! + * \brief Operation to find elements by a point coordinates + */ +class SMESHGUI_EXPORT SMESHGUI_FindElemByPointOp: public SMESHGUI_SelectionOp +{ + Q_OBJECT + +public: + SMESHGUI_FindElemByPointOp(); + virtual ~SMESHGUI_FindElemByPointOp(); + + virtual LightApp_Dialog* dlg() const; + +protected: + + virtual void startOperation(); + virtual void stopOperation(); + + virtual void activateSelection(); + +protected slots: + + virtual void onFind(); + virtual bool onApply(); + +private slots: + + void onRejectedDlg(); + void onSelectionDone(); + void onElemSelected(); + void onElemTypeChange(int); + void redisplayPreview(); + +private: + SMESHGUI_FindElemByPointDlg* myDlg; + + SUIT_SelectionFilter* myFilter; + SMESHGUI_MeshEditPreview* mySimulation; // to show point coordinates + SMESH::SMESH_IDSource_var myMeshOrPart; + SMESH::MeshPreviewStruct_var myPreview; + Handle(SALOME_InteractiveObject) myMeshIO; +}; + +/*! + * \brief Dialog to find elements by a point coordinates + */ + +class SMESHGUI_EXPORT SMESHGUI_FindElemByPointDlg : public SMESHGUI_Dialog +{ + Q_OBJECT + +public: + SMESHGUI_FindElemByPointDlg(); + + void setTypes(SMESH::array_of_ElementType_var & types); + +private: + QWidget* createMainFrame( QWidget* ); + + QLineEdit* myMeshName; + SMESHGUI_SpinBox* myX; + SMESHGUI_SpinBox* myY; + SMESHGUI_SpinBox* myZ; + QtxComboBox* myElemTypeCombo; + QPushButton* myFindBtn; + QListWidget* myFoundList; + + QString myHelpFileName; + + friend class SMESHGUI_FindElemByPointOp; + +signals: + void rejectedDlg(); + +protected slots: + virtual void reject(); + +private slots: +//void ButtonToggled( bool ); +}; + +#endif // SMESHGUI_FindElemByPointDLG_H diff --git a/src/SMESHGUI/SMESHGUI_GEOMGenUtils.cxx b/src/SMESHGUI/SMESHGUI_GEOMGenUtils.cxx index 33907aec0..ead9da3c0 100644 --- a/src/SMESHGUI/SMESHGUI_GEOMGenUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_GEOMGenUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GEOMGenUtils.cxx // Author : Open CASCADE S.A.S. @@ -51,7 +52,8 @@ namespace SMESH return aGEOMGen; } - GEOM::GEOM_Object_var GetShapeOnMeshOrSubMesh(_PTR(SObject) theMeshOrSubmesh) + GEOM::GEOM_Object_var GetShapeOnMeshOrSubMesh(_PTR(SObject) theMeshOrSubmesh, + bool* isMesh) { SALOMEDS_SObject* aMeshOrSubmesh = _CAST(SObject,theMeshOrSubmesh); if(aMeshOrSubmesh) { @@ -60,11 +62,17 @@ namespace SMESH SMESH::SMESH_Mesh_var aMesh = SObjectToInterface( theMeshOrSubmesh ); if ( !aMesh->_is_nil() ) + { + if ( isMesh ) *isMesh = true; return aMesh->GetShapeToMesh(); + } SMESH::SMESH_subMesh_var aSubmesh = SObjectToInterface( theMeshOrSubmesh ); if ( !aSubmesh->_is_nil() ) - return aSubmesh->GetSubShape(); + { + if ( isMesh ) *isMesh = false; + return aSubmesh->GetSubShape(); + } } } return GEOM::GEOM_Object::_nil(); @@ -89,7 +97,7 @@ namespace SMESH SALOMEDS_SObject* aRefSO = _CAST(SObject,aRefSOClient); aMeshShape = GEOM::GEOM_Object::_narrow(aRefSO->GetObject()); } else { - SALOMEDS_SObject* aSO = _CAST(SObject,aSObject); + SALOMEDS_SObject* aSO = _CAST(SObject,aSObject); aMeshShape = GEOM::GEOM_Object::_narrow(aSO->GetObject()); } diff --git a/src/SMESHGUI/SMESHGUI_GEOMGenUtils.h b/src/SMESHGUI/SMESHGUI_GEOMGenUtils.h index 8461a6432..807c2576a 100644 --- a/src/SMESHGUI/SMESHGUI_GEOMGenUtils.h +++ b/src/SMESHGUI/SMESHGUI_GEOMGenUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GEOMGenUtils.h // Author : Open CASCADE S.A.S. @@ -42,7 +43,7 @@ namespace SMESH { SMESHGUI_EXPORT GEOM::GEOM_Gen_var GetGEOMGen(); - SMESHGUI_EXPORT GEOM::GEOM_Object_var GetShapeOnMeshOrSubMesh( _PTR(SObject) ); + SMESHGUI_EXPORT GEOM::GEOM_Object_var GetShapeOnMeshOrSubMesh( _PTR(SObject), bool* isMesh=0 ); SMESHGUI_EXPORT GEOM::GEOM_Object_ptr GetGeom( _PTR(SObject) ); diff --git a/src/SMESHGUI/SMESHGUI_GroupDlg.cxx b/src/SMESHGUI/SMESHGUI_GroupDlg.cxx index 352215c95..10995de19 100644 --- a/src/SMESHGUI/SMESHGUI_GroupDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_GroupDlg.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_GroupDlg.cxx -// Author : Natalia KOPNOVA, Open CASCADE S.A.S. -// SMESH includes + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_GroupDlg.cxx +// Author : Natalia KOPNOVA, Open CASCADE S.A.S. +// SMESH includes // #include "SMESHGUI_GroupDlg.h" @@ -38,6 +39,7 @@ #include #include #include +#include // SALOME GEOM includes #include @@ -61,6 +63,8 @@ #include +#include + // SALOME KERNEL includes #include @@ -96,19 +100,34 @@ #define SPACING 6 #define MARGIN 11 +enum grpSelectionMode { + grpNoSelection = -1, + grpNodeSelection = 0, + grpBallSelection = 1, + grpEdgeSelection = 2, + grpFaceSelection = 3, + grpVolumeSelection = 4, + grpSubMeshSelection = 5, + grpGroupSelection = 6, + grpMeshSelection = 7, + grpGeomSelection = 8, + grpAllSelection = 9, +}; + //================================================================================= // function : SMESHGUI_GroupDlg() // purpose : //================================================================================= SMESHGUI_GroupDlg::SMESHGUI_GroupDlg( SMESHGUI* theModule, - SMESH::SMESH_Mesh_ptr theMesh ) + SMESH::SMESH_Mesh_ptr theMesh ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), mySelector( SMESH::GetViewWindow( theModule )->GetSelector() ), myIsBusy( false ), myNameChanged( false ), - myActor( 0 ) + myIsApplyAndClose( false ), + myNbChangesOfContents(0) { initDialog( true ); if ( !theMesh->_is_nil() ) @@ -127,14 +146,15 @@ SMESHGUI_GroupDlg::SMESHGUI_GroupDlg( SMESHGUI* theModule, // purpose : //================================================================================= SMESHGUI_GroupDlg::SMESHGUI_GroupDlg( SMESHGUI* theModule, - SMESH::SMESH_GroupBase_ptr theGroup, + SMESH::SMESH_GroupBase_ptr theGroup, const bool theIsConvert ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), mySelector( SMESH::GetViewWindow( theModule )->GetSelector() ), myIsBusy( false ), - myNameChanged( false ) + myNameChanged( false ), + myNbChangesOfContents(0) // just not to use uninitialized variable { initDialog( false ); if ( !theGroup->_is_nil() ) @@ -145,7 +165,7 @@ SMESHGUI_GroupDlg::SMESHGUI_GroupDlg( SMESHGUI* theModule, mySelectGroup->setEnabled( false ); myCurrentLineEdit = myMeshGroupLine; - setSelectionMode( 5 ); + setSelectionMode( grpGroupSelection ); } } @@ -194,6 +214,7 @@ void SMESHGUI_GroupDlg::initDialog( bool create) QStringList types; types.append( tr( "MESH_NODE" ) ); + types.append( tr( "SMESH_BALL_ELEM" ) ); types.append( tr( "SMESH_EDGE" ) ); types.append( tr( "SMESH_FACE" ) ); types.append( tr( "SMESH_VOLUME" ) ); @@ -221,10 +242,13 @@ void SMESHGUI_GroupDlg::initDialog( bool create) QRadioButton* rb1 = new QRadioButton( tr( "SMESH_GROUP_STANDALONE" ), aGrpTypeBox ); QRadioButton* rb2 = new QRadioButton( tr( "SMESH_GROUP_GEOMETRY" ), aGrpTypeBox ); + QRadioButton* rb3 = new QRadioButton( tr( "SMESH_GROUP_FILTER" ), aGrpTypeBox ); myGrpTypeGroup->addButton( rb1, 0 ); myGrpTypeGroup->addButton( rb2, 1 ); + myGrpTypeGroup->addButton( rb3, 2 ); aGrpTypeBoxLayout->addWidget( rb1 ); aGrpTypeBoxLayout->addWidget( rb2 ); + aGrpTypeBoxLayout->addWidget( rb3 ); aGrpTypeBox->setEnabled( create ); myGrpTypeId = -1; @@ -232,66 +256,72 @@ void SMESHGUI_GroupDlg::initDialog( bool create) myWGStack = new QStackedWidget( this ); QWidget* wg1 = new QWidget( myWGStack ); QWidget* wg2 = new QWidget( myWGStack ); + QWidget* wg3 = new QWidget( myWGStack ); /***************************************************************/ - QGroupBox* aContentBox = new QGroupBox( tr( "SMESH_CONTENT" ), wg1 ); + QGroupBox* aContentBox = new QGroupBox( tr( "SMESH_CONTENT" ), wg1 ); QGridLayout* aContentBoxLayout = new QGridLayout( aContentBox ); aContentBoxLayout->setMargin( MARGIN ); aContentBoxLayout->setSpacing( SPACING ); - QLabel* aLabel = new QLabel( tr( "SMESH_ID_ELEMENTS" ), aContentBox ); - myElements = new QListWidget( aContentBox ); + mySelectAll = new QCheckBox( tr( "SELECT_ALL" ), aContentBox ); + myAllowElemsModif = new QCheckBox( tr( "ALLOW_ELEM_LIST_MODIF" ), aContentBox ); + + myElementsLab = new QLabel( tr( "SMESH_ID_ELEMENTS" ), aContentBox ); + myElements = new QListWidget( aContentBox ); myElements->setSelectionMode( QListWidget::ExtendedSelection ); - myFilter = new QPushButton( tr( "SMESH_BUT_FILTER" ), aContentBox ); - QPushButton* aAddBtn = new QPushButton( tr( "SMESH_BUT_ADD" ), aContentBox ); - QPushButton* aRemoveBtn = new QPushButton( tr( "SMESH_BUT_REMOVE" ), aContentBox ); - QPushButton* aSortBtn = new QPushButton( tr( "SMESH_BUT_SORT" ), aContentBox ); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), aContentBox ); + myAddBtn = new QPushButton( tr( "SMESH_BUT_ADD" ), aContentBox ); + myRemoveBtn = new QPushButton( tr( "SMESH_BUT_REMOVE" ), aContentBox ); + mySortBtn = new QPushButton( tr( "SMESH_BUT_SORT" ), aContentBox ); - aContentBoxLayout->addWidget( aLabel, 0, 0 ); - aContentBoxLayout->addWidget( myElements, 1, 0, 6, 1 ); - aContentBoxLayout->addWidget( myFilter, 1, 1 ); - aContentBoxLayout->addWidget( aAddBtn, 3, 1 ); - aContentBoxLayout->addWidget( aRemoveBtn, 4, 1 ); - aContentBoxLayout->addWidget( aSortBtn, 6, 1 ); + aContentBoxLayout->addWidget( mySelectAll, 0, 0 ); + aContentBoxLayout->addWidget( myAllowElemsModif, 1, 0 ); + aContentBoxLayout->addWidget( myFilterBtn, 1, 1 ); + aContentBoxLayout->addWidget( myElementsLab, 2, 0 ); + aContentBoxLayout->addWidget( myElements, 3, 0, 6, 1 ); + aContentBoxLayout->addWidget( myAddBtn, 3, 1 ); + aContentBoxLayout->addWidget( myRemoveBtn, 4, 1 ); + aContentBoxLayout->addWidget( mySortBtn, 8, 1 ); aContentBoxLayout->setColumnStretch( 0, 1 ); - aContentBoxLayout->setRowStretch( 2, 1 ); - aContentBoxLayout->setRowStretch( 5, 1 ); + aContentBoxLayout->setRowStretch( 3, 1 ); + aContentBoxLayout->setRowStretch( 6, 1 ); /***************************************************************/ - QGroupBox* aSelectBox = new QGroupBox( tr( "SMESH_SELECT_FROM" ), wg1 ); - QGridLayout* aSelectBoxLayout = new QGridLayout( aSelectBox ); - aSelectBoxLayout->setMargin( MARGIN ); - aSelectBoxLayout->setSpacing( SPACING ); + mySelectBox = new QGroupBox( tr( "SMESH_SELECT_FROM" ), wg1 ); + QGridLayout* mySelectBoxLayout = new QGridLayout( mySelectBox ); + mySelectBoxLayout->setMargin( MARGIN ); + mySelectBoxLayout->setSpacing( SPACING ); - mySelectSubMesh = new QCheckBox( tr( "SMESH_SUBMESH" ), aSelectBox ); - mySubMeshBtn = new QPushButton( aSelectBox ); + mySelectSubMesh = new QCheckBox( tr( "SMESH_SUBMESH" ), mySelectBox ); + mySubMeshBtn = new QPushButton( mySelectBox ); mySubMeshBtn->setIcon( image0 ); - mySubMeshLine = new QLineEdit( aSelectBox ); + mySubMeshLine = new QLineEdit( mySelectBox ); mySubMeshLine->setReadOnly( true ); onSelectSubMesh( false ); - mySelectGroup = new QCheckBox( tr( "SMESH_GROUP" ), aSelectBox ); - myGroupBtn = new QPushButton( aSelectBox ); + mySelectGroup = new QCheckBox( tr( "SMESH_GROUP" ), mySelectBox ); + myGroupBtn = new QPushButton( mySelectBox ); myGroupBtn->setIcon( image0 ); - myGroupLine = new QLineEdit( aSelectBox ); + myGroupLine = new QLineEdit( mySelectBox ); myGroupLine->setReadOnly( true ); onSelectGroup( false ); - aSelectBoxLayout->addWidget( mySelectSubMesh, 0, 0 ); - aSelectBoxLayout->addWidget( mySubMeshBtn, 0, 1 ); - aSelectBoxLayout->addWidget( mySubMeshLine, 0, 2 ); - aSelectBoxLayout->addWidget( mySelectGroup, 1, 0 ); - aSelectBoxLayout->addWidget( myGroupBtn, 1, 1 ); - aSelectBoxLayout->addWidget( myGroupLine, 1, 2 ); + mySelectBoxLayout->addWidget( mySelectSubMesh, 0, 0 ); + mySelectBoxLayout->addWidget( mySubMeshBtn, 0, 1 ); + mySelectBoxLayout->addWidget( mySubMeshLine, 0, 2 ); + mySelectBoxLayout->addWidget( mySelectGroup, 1, 0 ); + mySelectBoxLayout->addWidget( myGroupBtn, 1, 1 ); + mySelectBoxLayout->addWidget( myGroupLine, 1, 2 ); /***************************************************************/ QVBoxLayout* wg1Layout = new QVBoxLayout( wg1 ); wg1Layout->setMargin( 0 ); wg1Layout->setSpacing( SPACING ); wg1Layout->addWidget( aContentBox ); - wg1Layout->addWidget( aSelectBox ); + wg1Layout->addWidget( mySelectBox ); wg1Layout->setStretchFactor( aContentBox, 10 ); /***************************************************************/ @@ -309,15 +339,24 @@ void SMESHGUI_GroupDlg::initDialog( bool create) /***************************************************************/ QGridLayout* wg2Layout = new QGridLayout( wg2 ); wg2Layout->setMargin( 0 ); - wg1Layout->setSpacing( SPACING ); + wg2Layout->setSpacing( SPACING ); wg2Layout->addWidget( geomObject, 0, 0 ); wg2Layout->addWidget( myGeomGroupBtn, 0, 1 ); wg2Layout->addWidget( myGeomGroupLine,0, 2 ); wg2Layout->setRowStretch( 1, 5 ); + /***************************************************************/ + QPushButton * aFilter2 = new QPushButton( tr( "SMESH_BUT_FILTER" ), wg3 ); + QGridLayout* wg3Layout = new QGridLayout( wg3 ); + wg3Layout->setMargin( 0 ); + wg3Layout->setSpacing( SPACING ); + wg3Layout->addWidget( aFilter2, 0, 0 ); + wg3Layout->setRowStretch( 1, 5 ); + /***************************************************************/ myWGStack->insertWidget( 0, wg1 ); myWGStack->insertWidget( 1, wg2 ); + myWGStack->insertWidget( 2, wg3 ); /***************************************************************/ QGroupBox* aColorBox = new QGroupBox(tr( "SMESH_SET_COLOR" ), this); @@ -328,7 +367,7 @@ void SMESHGUI_GroupDlg::initDialog( bool create) QLabel* aColorLab = new QLabel(tr( "SMESH_CHECK_COLOR" ), aColorBox ); myColorBtn = new QtxColorButton(aColorBox); myColorBtn->setSizePolicy( QSizePolicy::MinimumExpanding, - myColorBtn->sizePolicy().verticalPolicy() ); + myColorBtn->sizePolicy().verticalPolicy() ); aColorBoxLayout->addWidget(aColorLab); aColorBoxLayout->addWidget(myColorBtn); @@ -372,45 +411,53 @@ void SMESHGUI_GroupDlg::initDialog( bool create) aMainLayout->addWidget(aButtons, 6, 0, 1, 3); /* signals and slots connections */ - connect(myMeshGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); - connect(myGrpTypeGroup, SIGNAL(buttonClicked(int)), this, SLOT(onGrpTypeChanged(int))); - connect(myTypeGroup, SIGNAL(buttonClicked(int)), this, SLOT(onTypeChanged(int))); + connect(myMeshGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); + connect(myGrpTypeGroup, SIGNAL(buttonClicked(int)), this, SLOT(onGrpTypeChanged(int))); + connect(myTypeGroup, SIGNAL(buttonClicked(int)), this, SLOT(onTypeChanged(int))); - connect(myName, SIGNAL(textChanged(const QString&)), this, SLOT(onNameChanged(const QString&))); - connect(myElements, SIGNAL(itemSelectionChanged()), this, SLOT(onListSelectionChanged())); + connect(myName, SIGNAL(textChanged(const QString&)), this, SLOT(onNameChanged(const QString&))); + connect(myElements, SIGNAL(itemSelectionChanged()), this, SLOT(onListSelectionChanged())); - connect(myFilter, SIGNAL(clicked()), this, SLOT(setFilters())); - connect(aAddBtn, SIGNAL(clicked()), this, SLOT(onAdd())); - connect(aRemoveBtn, SIGNAL(clicked()), this, SLOT(onRemove())); - connect(aSortBtn, SIGNAL(clicked()), this, SLOT(onSort())); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + connect(aFilter2, SIGNAL(clicked()), this, SLOT(setFilters())); + connect(mySelectAll, SIGNAL(toggled(bool)), this, SLOT(onSelectAll())); + connect(myAllowElemsModif,SIGNAL(toggled(bool)), this, SLOT(onSelectAll())); + connect(myAddBtn, SIGNAL(clicked()), this, SLOT(onAdd())); + connect(myRemoveBtn, SIGNAL(clicked()), this, SLOT(onRemove())); + connect(mySortBtn, SIGNAL(clicked()), this, SLOT(onSort())); connect(mySelectSubMesh, SIGNAL(toggled(bool)), this, SLOT(onSelectSubMesh(bool))); connect(mySelectGroup, SIGNAL(toggled(bool)), this, SLOT(onSelectGroup(bool))); - connect(mySubMeshBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); - connect(myGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); + connect(mySubMeshBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); + connect(myGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); connect(myGeomGroupBtn, SIGNAL(toggled(bool)), this, SLOT(onGeomSelectionButton(bool))); - connect(myColorBtn, SIGNAL(changed( QColor )), this, SLOT(onColorChanged( QColor ))); + connect(myColorBtn, SIGNAL(changed( QColor )), this, SLOT(onColorChanged( QColor ))); - connect(myOKBtn, SIGNAL(clicked()), this, SLOT(onOK())); - connect(myApplyBtn, SIGNAL(clicked()), this, SLOT(onApply())); - connect(myCloseBtn, SIGNAL(clicked()), this, SLOT(onClose())); - connect(myHelpBtn, SIGNAL(clicked()), this, SLOT(onHelp())); + connect(myOKBtn, SIGNAL(clicked()), this, SLOT(onOK())); + connect(myApplyBtn, SIGNAL(clicked()), this, SLOT(onApply())); + connect(myCloseBtn, SIGNAL(clicked()), this, SLOT(onClose())); + connect(myHelpBtn, SIGNAL(clicked()), this, SLOT(onHelp())); /* Init selection */ mySMESHGUI->SetActiveDialogBox(this); mySMESHGUI->SetState(800); - mySelectionMode = -1; + mySelectionMode = grpNoSelection; myMeshFilter = new SMESH_TypeFilter(MESH); - mySubMeshFilter = new SMESH_TypeFilter(SUBMESH); - myGroupFilter = new SMESH_TypeFilter(GROUP); + mySubMeshFilter = new SMESH_LogicalFilter(QList(), + SMESH_LogicalFilter::LO_OR, + /*takeOwnership=*/true); + myGroupFilter = new SMESH_LogicalFilter(QList(), + SMESH_LogicalFilter::LO_OR, + /*takeOwnership=*/true); SalomeApp_Study* aStudy = dynamic_cast( mySMESHGUI->application()->activeStudy() ); myGeomFilter = new GEOM_SelectionFilter( aStudy, true ); connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), this, SLOT(onDeactivate())); connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(onClose())); connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onObjectSelectionChanged())); + connect(mySMESHGUI, SIGNAL(SignalVisibilityChanged()), this, SLOT(onVisibilityChanged())); rb1->setChecked(true); // VSR !!! onGrpTypeChanged(0); // VSR!!! @@ -418,8 +465,7 @@ void SMESHGUI_GroupDlg::initDialog( bool create) if (myMesh->_is_nil() ) myTypeGroup->button(0)->setChecked(true); - updateButtons(); - //myName->setText(GetDefaultName(tr( "SMESH_GROUP" ))); + onSelectAll(); //updateButtons(); } //================================================================================= @@ -433,6 +479,10 @@ SMESHGUI_GroupDlg::~SMESHGUI_GroupDlg() myFilterDlg->setParent( 0 ); delete myFilterDlg; } + if ( myMeshFilter ) delete myMeshFilter; + if ( mySubMeshFilter ) delete mySubMeshFilter; + if ( myGroupFilter ) delete myGroupFilter; + if ( myGeomFilter ) delete myGeomFilter; } //================================================================================= @@ -485,6 +535,7 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_Mesh_ptr theMesh) setShowEntityMode(); myGroup = SMESH::SMESH_Group::_nil(); myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); + myGroupOnFilter = SMESH::SMESH_GroupOnFilter::_nil(); // NPAL19389: create a group with a selection in another group // set actor of myMesh, if it is visible, else try @@ -513,7 +564,7 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_Mesh_ptr theMesh) // purpose : //================================================================================= void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, - const bool theIsConvert) + const bool theIsConvert) { restoreShowEntityMode(); myMesh = theGroup->GetMesh(); @@ -532,17 +583,20 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, int aType = 0; switch(theGroup->GetType()) { - case SMESH::NODE: aType= 0; break; - case SMESH::EDGE: aType = 1; break; - case SMESH::FACE: aType = 2; break; - case SMESH::VOLUME: aType = 3; break; + case SMESH::NODE: aType = 0; break; + case SMESH::BALL: aType = 1; break; + case SMESH::EDGE: aType = 2; break; + case SMESH::FACE: aType = 3; break; + case SMESH::VOLUME: aType = 4; break; } myTypeGroup->button(aType)->setChecked(true); - myGroup = SMESH::SMESH_Group::_narrow( theGroup ); - myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow( theGroup ); + myGroup = SMESH::SMESH_Group::_narrow( theGroup ); + myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow( theGroup ); + myGroupOnFilter = SMESH::SMESH_GroupOnFilter::_narrow( theGroup ); + myFilter = SMESH::Filter::_nil(); - if (myGroup->_is_nil() && myGroupOnGeom->_is_nil()) + if (myGroup->_is_nil() && myGroupOnGeom->_is_nil() && myGroupOnFilter->_is_nil() ) return; // NPAL19389: create a group with a selection in another group @@ -550,21 +604,24 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, // actor of theGroup, if it is visible, else try // any visible actor of group or submesh of myMesh // commented, because an attempt to set selection on not displayed cells leads to error - //SetAppropriateActor(); - myActor = SMESH::FindActorByObject(myMesh); - if ( !myActor ) - myActor = SMESH::FindActorByObject(theGroup); - SMESH::SetPickable(myActor); + SetAppropriateActor(); - int grpType = (!myGroup->_is_nil() ? 0 : (theIsConvert ? 0 : 1)); + /* SMESH_Actor* anActor = SMESH::FindActorByObject(myMesh); + if ( !anActor ) + anActor = SMESH::FindActorByObject(theGroup); + SMESH::SetPickable(anActor);*/ + + int grpType = (!myGroup->_is_nil() ? 0 : (theIsConvert ? 0 : myGroupOnGeom->_is_nil() ? 2 : 1)); myGrpTypeGroup->button(grpType)->setChecked(true); onGrpTypeChanged(grpType); - if ( grpType == 0 ) { + myTypeId = aType; + if ( grpType == 0 ) { // standalone group myCurrentLineEdit = 0; myElements->clear(); + myAllowElemsModif->setChecked( true ); + setSelectionMode(aType); - myTypeId = aType; setShowEntityMode(); // depends on myTypeId @@ -579,7 +636,7 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, myElements->selectAll(); } } - else + else if ( grpType == 1 ) // group on geom { QString aShapeName( "" ); _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); @@ -587,15 +644,30 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, if (!aGroupShape->_is_nil()) { _PTR(SObject) aGroupShapeSO = aStudy->FindObjectID(aGroupShape->GetStudyEntry()); - aShapeName = aGroupShapeSO->GetName().c_str(); + if ( aGroupShapeSO ) + aShapeName = aGroupShapeSO->GetName().c_str(); } myGeomGroupLine->setText( aShapeName ); + } + else // group on filter + { + myFilter = myGroupOnFilter->GetFilter(); + if ( !myFilter->_is_nil() ) { + SMESH::Predicate_var perdicate = myFilter->GetPredicate(); + if ( perdicate->_is_nil() ) + myFilter = SMESH::Filter::_nil(); + } + } + + if ( grpType != 0 ) + { myNameChanged = true; myName->blockSignals(true); - myName->setText( "Group On " + aShapeName); + myName->setText(theGroup->GetName()); myName->blockSignals(false); } - updateButtons(); + + onSelectAll(); //updateButtons(); } //================================================================================= @@ -605,14 +677,33 @@ void SMESHGUI_GroupDlg::init (SMESH::SMESH_GroupBase_ptr theGroup, void SMESHGUI_GroupDlg::updateButtons() { bool enable = !myName->text().trimmed().isEmpty(); - - if (myGrpTypeId == 0) { - enable = enable && myElements->count() > 0; - enable = enable && (!myGroup->_is_nil() || !myMesh->_is_nil()); - } - else if (myGrpTypeId == 1) { - if (CORBA::is_nil(myGroupOnGeom)) { // creation mode - enable = enable && myGeomObjects->length() > 0 && !myMesh->_is_nil(); + if ( enable ) + { + if (myGrpTypeId == 0) { // standalone + if ( !mySelectAll->isChecked() ) + { + if ( myAllowElemsModif->isChecked() ) + { + enable = ( myElements->count() > 0 ); + } + else if ((enable = !myFilter->_is_nil() )) + { + SMESH::array_of_ElementType_var types = myFilter->GetTypes(); + enable = types->length(); + } + } + enable = enable && (!myGroup->_is_nil() || !myMesh->_is_nil()); + } + else if (myGrpTypeId == 1) // on geom + { + if (CORBA::is_nil(myGroupOnGeom)) // creation mode + enable = ( myGeomObjects->length() > 0 && !myMesh->_is_nil() ); + } + else if (myGrpTypeId == 2) // on filter + { + if (( enable = !myFilter->_is_nil() )) + if (CORBA::is_nil(myGroupOnFilter) ) // creation mode + enable = !myMesh->_is_nil(); } } @@ -639,9 +730,13 @@ void SMESHGUI_GroupDlg::onTypeChanged (int id) { if (myTypeId != id) { myElements->clear(); - if (myCurrentLineEdit == 0) - setSelectionMode(id); myTypeId = id; + if ( myGrpTypeId == 0 && myCurrentLineEdit == 0) + setSelectionMode(id); + else + setSelectionMode( mySelectionMode++ ); // update according to mySelectionMode + + onObjectSelectionChanged(); setShowEntityMode(); } } @@ -653,13 +748,14 @@ void SMESHGUI_GroupDlg::onTypeChanged (int id) void SMESHGUI_GroupDlg::onGrpTypeChanged (int id) { if (myGrpTypeId != id) { + myGrpTypeId = id; myWGStack->setCurrentIndex( id ); myName->blockSignals(true); myName->setText(myOldName); myName->blockSignals(false); - onSelectGeomGroup(id == 1); + onSelectGeomGroup(id != 0); } - myGrpTypeId = id; + updateButtons(); } //================================================================================= @@ -680,48 +776,95 @@ void SMESHGUI_GroupDlg::setSelectionMode (int theMode) // PAL7314 if (myMesh->_is_nil()) return; + SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ); + bool isSelectAll = mySelectAll->isChecked() || !myAllowElemsModif->isChecked() || myGrpTypeId != 0; if (mySelectionMode != theMode) { // [PAL10408] mySelectionMgr->clearSelected(); mySelectionMgr->clearFilters(); - if (myActor) - myActor->SetPointRepresentation(false); + SMESH::RemoveFilters(); + + if (myActorsList.count() > 0) + for (QListIterator it( myActorsList ); it.hasNext(); ) + it.next()->SetPointRepresentation(false); else SMESH::SetPointRepresentation(false); - if (theMode < 4) { - switch (theMode) { - case 0: - if (myActor) - myActor->SetPointRepresentation(true); - else - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); - break; - case 1: - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - break; - case 2: - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - break; - default: - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(VolumeSelection); + + switch (theMode) { + case grpNodeSelection: + if ( myGrpTypeId == 0 ) // standalone + { + if (myActorsList.count() > 0) + for (QListIterator it( myActorsList ); it.hasNext(); ) + it.next()->SetPointRepresentation(true); + else + SMESH::SetPointRepresentation(true); + } + if ( aViewWindow ) aViewWindow->SetSelectionMode(isSelectAll ? ActorSelection : NodeSelection); + break; + case grpEdgeSelection: + if ( aViewWindow ) aViewWindow->SetSelectionMode(isSelectAll ? ActorSelection : EdgeSelection); + break; + case grpBallSelection: + //if ( aViewWindow ) aViewWindow->SetSelectionMode(isSelectAll ? ActorSelection : BallSelection); + break; + case grpFaceSelection: + if ( aViewWindow ) aViewWindow->SetSelectionMode(isSelectAll ? ActorSelection : FaceSelection); + break; + case grpVolumeSelection: + if ( aViewWindow ) aViewWindow->SetSelectionMode(isSelectAll ? ActorSelection : VolumeSelection); + break; + case grpSubMeshSelection: { + + SMESH_TypeFilter* f = 0; + switch (myTypeId) { + case grpNodeSelection: f = new SMESH_TypeFilter(SUBMESH); break; + case grpEdgeSelection: f = new SMESH_TypeFilter(SUBMESH_EDGE); break; + case grpFaceSelection: f = new SMESH_TypeFilter(SUBMESH_FACE); break; + case grpVolumeSelection: f = new SMESH_TypeFilter(SUBMESH_SOLID); break; + default: f = new SMESH_TypeFilter(SUBMESH); + } + QList filtList; + filtList.append( f ); + filtList.append( new SMESH_TypeFilter(SUBMESH_COMPOUND)); + mySubMeshFilter->setFilters( filtList ); + + mySelectionMgr->installFilter( mySubMeshFilter ); + + if ( aViewWindow ) aViewWindow->SetSelectionMode(ActorSelection); + break; + } + case grpGroupSelection: { + + SMESH_TypeFilter* f = 0; + switch (myTypeId) { + case grpNodeSelection: f = new SMESH_TypeFilter(GROUP_NODE); break; + case grpBallSelection: f = new SMESH_TypeFilter(GROUP_BALL); break; + case grpEdgeSelection: f = new SMESH_TypeFilter(GROUP_EDGE); break; + case grpFaceSelection: f = new SMESH_TypeFilter(GROUP_FACE); break; + case grpVolumeSelection: f = new SMESH_TypeFilter(GROUP_VOLUME); break; + default: f = new SMESH_TypeFilter(GROUP); } - } else { - if (theMode == 4) - mySelectionMgr->installFilter(mySubMeshFilter); - else if (theMode == 5) - mySelectionMgr->installFilter(myGroupFilter); - else if (theMode == 6) - mySelectionMgr->installFilter(myMeshFilter); - else if (theMode == 7) - mySelectionMgr->installFilter(myGeomFilter); - - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + QList filtList; + filtList.append( f ); + myGroupFilter->setFilters( filtList ); + + mySelectionMgr->installFilter(myGroupFilter); + if ( aViewWindow ) aViewWindow->SetSelectionMode(ActorSelection); + break; + } + case grpMeshSelection: + mySelectionMgr->installFilter(myMeshFilter); + if ( aViewWindow ) aViewWindow->SetSelectionMode(ActorSelection); + break; + case grpGeomSelection: + mySelectionMgr->installFilter(myGeomFilter); + if ( aViewWindow ) aViewWindow->SetSelectionMode(ActorSelection); + break; + default: + if ( aViewWindow ) aViewWindow->SetSelectionMode(ActorSelection); + break; } + if ( aViewWindow ) aViewWindow->Repaint(); mySelectionMode = theMode; } } @@ -738,20 +881,40 @@ bool SMESHGUI_GroupDlg::onApply() if (myName->text().trimmed().isEmpty()) return false; - if (myGrpTypeId == 0) { // on mesh elements - if (!myElements->count()) + SMESH::ElementType aType = SMESH::ALL; + switch (myTypeId) { + case grpNodeSelection: aType = SMESH::NODE; break; + case grpBallSelection: aType = SMESH::BALL; break; + case grpEdgeSelection: aType = SMESH::EDGE; break; + case grpFaceSelection: aType = SMESH::FACE; break; + case grpVolumeSelection: aType = SMESH::VOLUME; break; + } + + bool anIsOk = false; + QStringList anEntryList; + + SMESH::SMESH_GroupBase_var resultGroup; + bool isCreation; + + if (myGrpTypeId == 0) // standalone + { + if (!mySelectAll->isChecked() && !myElements->count() && myAllowElemsModif->isChecked()) return false; mySelectionMgr->clearSelected(); if (myGroup->_is_nil()) { // creation or conversion // check if group on geometry is not null - if (!CORBA::is_nil(myGroupOnGeom)) { + if (!myGroupOnGeom->_is_nil() || !myGroupOnFilter->_is_nil()) { if (myMesh->_is_nil()) return false; - myGroup = myMesh->ConvertToStandalone( myGroupOnGeom ); - // nullify pointer, because object become dead + if ( myGroupOnGeom->_is_nil() ) + myGroup = myMesh->ConvertToStandalone( myGroupOnFilter ); + else + myGroup = myMesh->ConvertToStandalone( myGroupOnGeom ); + myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); + myGroupOnFilter = SMESH::SMESH_GroupOnFilter::_nil(); } } @@ -759,107 +922,98 @@ bool SMESHGUI_GroupDlg::onApply() if (myMesh->_is_nil()) return false; - SMESH::ElementType aType = SMESH::ALL; - switch (myTypeId) { - case 0: aType = SMESH::NODE; break; - case 1: aType = SMESH::EDGE; break; - case 2: aType = SMESH::FACE; break; - case 3: aType = SMESH::VOLUME; break; - } - - SMESH::long_array_var anIdList = new SMESH::long_array; - int i, k = myElements->count(); - anIdList->length(k); - for (i = 0; i < k; i++) { - anIdList[i] = myElements->item(i)->text().toInt(); - } - myGroup = SMESH::AddGroup(myMesh, aType, myName->text()); - myGroup->Add(anIdList.inout()); - SALOMEDS::Color aColor = getGroupColor(); - myGroup->SetColor(aColor); + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroup ); + isCreation = true; - _PTR(SObject) aMeshGroupSO = SMESH::FindSObject(myGroup); + if ( mySelectAll->isChecked() ) { + // select all + myGroup->AddFrom(myMesh.in()); + } + else { + // select manually - //SMESH::setFileName ( aMeshGroupSO, QString::number(myColorSpinBox->value()) ); - SMESH::setFileType ( aMeshGroupSO, "COULEURGROUP" ); + if ( !myFilter->_is_nil() && + ( myNbChangesOfContents == 1 || !myAllowElemsModif->isChecked())) + { + myGroup->AddFrom( myFilter ); + } + else + { + SMESH::long_array_var anIdList = new SMESH::long_array; + int i, k = myElements->count(); + anIdList->length(k); + for (i = 0; i < k; i++) { + anIdList[i] = myElements->item(i)->text().toInt(); + } + myGroup->Add(anIdList.inout()); + } + } - /* init for next operation */ - myName->setText( "" ); - myElements->clear(); - myGroup = SMESH::SMESH_Group::_nil(); } else { // edition - myGroup->SetName(myName->text().toLatin1().data()); - - SALOMEDS::Color aColor = getGroupColor(); - myGroup->SetColor(aColor); - - _PTR(SObject) aMeshGroupSO = SMESH::FindSObject(myGroup); - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(aMeshGroupSO->GetID().c_str())) - anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B ); - QList aAddList; + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroup ); + isCreation = false; - int i, total = myElements->count(); - for (i = 0; i < total; i++) { - int anId = myElements->item(i)->text().toInt(); - int idx = myIdList.indexOf(anId); - if ( idx == -1 ) - aAddList.append(anId); - else - myIdList.removeAt(idx); + if ( mySelectAll->isChecked() ) { + // select all + myGroup->Clear(); + myGroup->AddFrom(myMesh.in()); } - if (!aAddList.empty()) { - SMESH::long_array_var anIdList = new SMESH::long_array; - int added = aAddList.count(); - anIdList->length(added); - for (i = 0; i < added; i++) - anIdList[i] = aAddList[i]; - myGroup->Add(anIdList.inout()); - } - if (!myIdList.empty()) { - SMESH::long_array_var anIdList = new SMESH::long_array; - int removed = myIdList.count(); - anIdList->length(removed); - for (i = 0; i < removed; i++) - anIdList[i] = myIdList[i]; - myGroup->Remove(anIdList.inout()); - } - /* init for next operation */ - myIdList.clear(); - for (i = 0; i < total; i++) { - myIdList.append(myElements->item(i)->text().toInt()); + else { + QList aAddList; + + int i, total = myElements->count(); + for (i = 0; i < total; i++) { + int anId = myElements->item(i)->text().toInt(); + int idx = myIdList.indexOf(anId); + if ( idx == -1 ) + aAddList.append(anId); + else + myIdList.removeAt(idx); + } + if (!aAddList.empty()) { + SMESH::long_array_var anIdList = new SMESH::long_array; + int added = aAddList.count(); + anIdList->length(added); + for (i = 0; i < added; i++) + anIdList[i] = aAddList[i]; + myGroup->Add(anIdList.inout()); + } + if (!myIdList.empty()) { + SMESH::long_array_var anIdList = new SMESH::long_array; + int removed = myIdList.count(); + anIdList->length(removed); + for (i = 0; i < removed; i++) + anIdList[i] = myIdList[i]; + myGroup->Remove(anIdList.inout()); + } + /* init for next operation */ + myIdList.clear(); + for (i = 0; i < total; i++) { + myIdList.append(myElements->item(i)->text().toInt()); + } } } - mySMESHGUI->updateObjBrowser(true); - SMESH::UpdateView(); // asv: fix of BUG PAL5515 - mySelectionMgr->clearSelected(); - return true; + anIsOk = true; } - else if (myGrpTypeId == 1) { // on geom object + else if (myGrpTypeId == 1) // on geom object + { if (CORBA::is_nil(myGroupOnGeom)) { // creation if (myMesh->_is_nil() || !myGeomObjects->length()) return false; - SMESH::ElementType aType = SMESH::ALL; - switch (myTypeId) { - case 0: aType = SMESH::NODE; break; - case 1: aType = SMESH::EDGE; break; - case 2: aType = SMESH::FACE; break; - case 3: aType = SMESH::VOLUME; break; - } - _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); GEOM::GEOM_IGroupOperations_var aGroupOp = - SMESH::GetGEOMGen()->GetIGroupOperations(aStudy->StudyId()); + SMESH::GetGEOMGen()->GetIGroupOperations(aStudy->StudyId()); if (myGeomObjects->length() == 1) { - myGroupOnGeom = myMesh->CreateGroupFromGEOM(aType, - myName->text().toLatin1().data(), - myGeomObjects[0]); + myGroupOnGeom = myMesh->CreateGroupFromGEOM(aType, + myName->text().toLatin1().data(), + myGeomObjects[0]); } else { SMESH::SMESH_Gen_var aSMESHGen = SMESHGUI::GetSMESHGen(); @@ -901,43 +1055,101 @@ bool SMESHGUI_GroupDlg::onApply() aNewGeomGroupName += myName->text(); SALOMEDS::SObject_var aNewGroupSO = geomGen->AddInStudy(aSMESHGen->GetCurrentStudy(), aGroupVar, - aNewGeomGroupName.toLatin1().data(), aMeshShape); + aNewGeomGroupName.toLatin1().data(), aMeshShape); } myGroupOnGeom = myMesh->CreateGroupFromGEOM(aType, - myName->text().toLatin1().data(), - aGroupVar); + myName->text().toLatin1().data(), + aGroupVar); } + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroupOnGeom ); + isCreation = true; + + } + else { // edition + + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroupOnGeom ); + isCreation = false; + } + anIsOk = true; + } + if (myGrpTypeId == 2) // group on filter + { + if ( myFilter->_is_nil() ) return false; - SALOMEDS::Color aColor = getGroupColor(); - myGroupOnGeom->SetColor(aColor); + if (CORBA::is_nil(myGroupOnFilter)) { // creation + if (myMesh->_is_nil()) + return false; - _PTR(SObject) aMeshGroupSO = SMESH::FindSObject(myGroupOnGeom); + myGroupOnFilter = myMesh->CreateGroupFromFilter(aType, + myName->text().toLatin1().data(), + myFilter); - //SMESH::setFileName ( aMeshGroupSO, QString::number(myColorSpinBox->value()) ); - SMESH::setFileType ( aMeshGroupSO,"COULEURGROUP" ); + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroupOnFilter ); + isCreation = true; + } + else + { + myGroupOnFilter->SetFilter( myFilter ); - /* init for next operation */ - myName->setText( "" ); - myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); + resultGroup = SMESH::SMESH_GroupBase::_narrow( myGroupOnFilter ); + isCreation = false; } - else { // edition - myGroupOnGeom->SetName(myName->text().toLatin1().data()); + anIsOk = true; + } + + if( anIsOk ) + { + SALOMEDS::Color aColor = getGroupColor(); + resultGroup->SetColor(aColor); - SALOMEDS::Color aColor = getGroupColor(); - myGroupOnGeom->SetColor(aColor); + _PTR(SObject) aMeshGroupSO = SMESH::FindSObject( resultGroup ); + if( aMeshGroupSO ) + anEntryList.append( aMeshGroupSO->GetID().c_str() ); - _PTR(SObject) aMeshGroupSO = SMESH::FindSObject(myGroupOnGeom); - if(SMESH_Actor *anActor = SMESH::FindActorByEntry(aMeshGroupSO->GetID().c_str())) - anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B ); - } + if ( isCreation ) + { + SMESH::setFileType ( aMeshGroupSO, "COULEURGROUP" ); + /* init for the next operation */ + myName->setText( "" ); + myElements->clear(); + myGroup = SMESH::SMESH_Group::_nil(); + myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); + myGroupOnFilter = SMESH::SMESH_GroupOnFilter::_nil(); + myFilter = SMESH::Filter::_nil(); + } + else + { + resultGroup->SetName(myName->text().toLatin1().data()); + + if ( aMeshGroupSO ) + if(SMESH_Actor *anActor = SMESH::FindActorByEntry(aMeshGroupSO->GetID().c_str())) { + anActor->setName(myName->text().toLatin1().data()); + switch ( myTypeId ) { + case grpNodeSelection: anActor->SetNodeColor( aColor.R, aColor.G, aColor.B ); break; + case grpBallSelection: anActor->SetBallColor( aColor.R, aColor.G, aColor.B ); break; + case grpEdgeSelection: anActor->SetEdgeColor( aColor.R, aColor.G, aColor.B ); break; + case grpFaceSelection: + case grpVolumeSelection: + default: + QColor c; + int delta; + SMESH::GetColor("SMESH", "fill_color", c , delta, "0,170,255|-100"); + anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B, delta ); break; + } + } + } + SMESHGUI::Modified(); mySMESHGUI->updateObjBrowser(true); + SMESH::UpdateView(); // asv: fix of BUG PAL5515 mySelectionMgr->clearSelected(); - return true; - } - return false; + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + myObjectToSelect = anApp->browseObjects( anEntryList, isApplyAndClose() ); + } + return anIsOk; } //================================================================================= @@ -946,8 +1158,10 @@ bool SMESHGUI_GroupDlg::onApply() //================================================================================= void SMESHGUI_GroupDlg::onOK() { + setIsApplyAndClose( true ); if ( onApply() ) onClose(); + setIsApplyAndClose( false ); } //================================================================================= @@ -956,9 +1170,9 @@ void SMESHGUI_GroupDlg::onOK() //================================================================================= void SMESHGUI_GroupDlg::onListSelectionChanged() { - // MESSAGE( "SMESHGUI_GroupDlg::onListSelectionChanged(); myActor = " << myActor); - if( myIsBusy || !myActor) return; - myIsBusy = true; + //MESSAGE( "SMESHGUI_GroupDlg::onListSelectionChanged(); myActorsList.count() = " << myActorsList.count()); + if( myIsBusy || myActorsList.count() == 0 ) return; + myIsBusy = true; if (myCurrentLineEdit == 0) { mySelectionMgr->clearSelected(); @@ -966,9 +1180,9 @@ void SMESHGUI_GroupDlg::onListSelectionChanged() QList selItems = myElements->selectedItems(); QListWidgetItem* anItem; foreach(anItem, selItems) aIndexes.Add(anItem->text().toInt()); - mySelector->AddOrRemoveIndex(myActor->getIO(), aIndexes, false); + mySelector->AddOrRemoveIndex(myActorsList.first()->getIO(), aIndexes, false); SALOME_ListIO aList; - aList.Append(myActor->getIO()); + aList.Append(myActorsList.first()->getIO()); mySelectionMgr->setSelectedObjects(aList,false); } myIsBusy = false; @@ -1010,7 +1224,7 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() myGeomObjects->length(0); if (myGeomGroupBtn->isChecked()) - myGeomGroupBtn->setChecked(false); + myGeomGroupBtn->setChecked(false); if (!myCreate) myName->setText( "" ); @@ -1029,16 +1243,20 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() Handle(SALOME_InteractiveObject) IO = aList.First(); if (myCreate) { - restoreShowEntityMode(); - myMesh = SMESH::IObjectToInterface(IO); + restoreShowEntityMode(); + myMesh = SMESH::IObjectToInterface(IO); setShowEntityMode(); updateGeomPopup(); if (myMesh->_is_nil()) - { + { updateButtons(); - myIsBusy = false; - return; - } + myIsBusy = false; + return; + } + + if ( myFilterDlg && !myMesh->_is_nil()){ + myFilterDlg->SetMesh( myMesh ); + } myGroup = SMESH::SMESH_Group::_nil(); // NPAL19389: create a group with a selection in another group @@ -1059,15 +1277,14 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() else { SMESH::SMESH_GroupBase_var aGroup = SMESH::IObjectToInterface(IO); if (aGroup->_is_nil()) - { - myIsBusy = false; + { + myIsBusy = false; return; - } + } myIsBusy = false; - myCurrentLineEdit = 0; - myGroup = SMESH::SMESH_Group::_nil(); - myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); + myGroup = SMESH::SMESH_Group::_nil(); + myGroupOnGeom = SMESH::SMESH_GroupOnGeom::_nil(); init(aGroup); myIsBusy = true; @@ -1081,13 +1298,13 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() if (myGrpTypeId == 0) { - if (myTypeId == -1) - onTypeChanged(0); - else - { - myElements->clear(); - setSelectionMode(myTypeId); - } + if (myTypeId == -1) + onTypeChanged(0); + else + { + myElements->clear(); + setSelectionMode(myTypeId); + } } myIsBusy = false; @@ -1103,26 +1320,24 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() if (aNbSel == 0 || !aMeshSO) { - myGeomObjects->length(0); - updateButtons(); - myIsBusy = false; - return; + myGeomObjects->length(0); + updateButtons(); + myIsBusy = false; + return; } myGeomObjects->length(aNbSel); GEOM::GEOM_Object_var aGeomGroup; - Standard_Boolean testResult; int i = 0; SALOME_ListIteratorOfListIO anIt (aList); for (; anIt.More(); anIt.Next()) { - testResult = Standard_False; - aGeomGroup = GEOMBase::ConvertIOinGEOMObject(anIt.Value(), testResult); + aGeomGroup = GEOMBase::ConvertIOinGEOMObject(anIt.Value()); // Check if the object is a geometry group - if (!testResult || CORBA::is_nil(aGeomGroup)) + if (CORBA::is_nil(aGeomGroup)) continue; // Check if group constructed on the same shape as a mesh or on its child @@ -1137,21 +1352,17 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() else aGroupMainShape = GEOM::GEOM_Object::_duplicate(aGeomGroup); _PTR(SObject) aGroupMainShapeSO = - //aStudy->FindObjectIOR(aStudy->ConvertObjectToIOR(aGroupMainShape)); aStudy->FindObjectID(aGroupMainShape->GetStudyEntry()); _PTR(SObject) anObj, aRef; bool isRefOrSubShape = false; if (aMeshSO->FindSubObject(1, anObj) && anObj->ReferencedObject(aRef)) { - //if (strcmp(aRef->GetID(), aGroupMainShapeSO->GetID()) == 0) { if (aRef->GetID() == aGroupMainShapeSO->GetID()) { isRefOrSubShape = true; } else { _PTR(SObject) aFather = aGroupMainShapeSO->GetFather(); _PTR(SComponent) aComponent = aGroupMainShapeSO->GetFatherComponent(); - //while (!isRefOrSubShape && strcmp(aFather->GetID(), aComponent->GetID()) != 0) { while (!isRefOrSubShape && aFather->GetID() != aComponent->GetID()) { - //if (strcmp(aRef->GetID(), aFather->GetID()) == 0) if (aRef->GetID() == aFather->GetID()) isRefOrSubShape = true; else @@ -1165,10 +1376,10 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() myGeomObjects->length(i); if ( i == 0 ) - { - myIsBusy = false; - return; - } + { + myIsBusy = false; + return; + } aNbSel = i; } @@ -1202,13 +1413,12 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() } else // !myCurrentLineEdit: local selection of nodes or elements { - if (aNbSel == 1 && myActor && myActor->hasIO()) + if (aNbSel == 1 && myActorsList.count() > 0 ) { -#ifdef ENABLE_SWITCH_ACTOR_DURING_ELEMENTS_SELECTION // NPAL19389: create a group with a selection in another group // Switch myActor to the newly selected one, if the last // is visible and belongs to group or submesh of myMesh - Handle(SALOME_InteractiveObject) curIO = myActor->getIO(); + /* Handle(SALOME_InteractiveObject) curIO = myActor->getIO(); Handle(SALOME_InteractiveObject) selIO = aList.First(); if (curIO->hasEntry() && selIO->hasEntry()) { const char* selEntry = selIO->getEntry(); @@ -1235,54 +1445,101 @@ void SMESHGUI_GroupDlg::onObjectSelectionChanged() } } } - } + }*/ // NPAL19389 END -#endif // ENABLE_SWITCH_ACTOR_DURING_ELEMENTS_SELECTION QString aListStr = ""; int aNbItems = 0; if (myTypeId == 0) { - aNbItems = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), aListStr); + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + QString tmpStr; + aNbItems += SMESH::GetNameOfSelectedNodes(mySelector, it.next()->getIO(), tmpStr); + aListStr += tmpStr; + } } else { - aNbItems = SMESH::GetNameOfSelectedElements(mySelector, myActor->getIO(), aListStr); + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + QString tmpStr; + aNbItems += SMESH::GetNameOfSelectedElements(mySelector, it.next()->getIO(), tmpStr); + aListStr += tmpStr; + } } if (aNbItems > 0) { - QListWidgetItem* anItem; - QList listItemsToSel; - QStringList anElements = aListStr.split( " ", QString::SkipEmptyParts); - for (QStringList::iterator it = anElements.begin(); it != anElements.end(); ++it) { - QList found = myElements->findItems(*it, Qt::MatchExactly); - foreach(anItem, found) - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } - bool blocked = myElements->signalsBlocked(); - myElements->blockSignals(true); - foreach(anItem, listItemsToSel) anItem->setSelected(true); - myElements->blockSignals(blocked); - onListSelectionChanged(); - listItemsToSel.clear(); + QListWidgetItem* anItem; + QList listItemsToSel; + QStringList anElements = aListStr.split( " ", QString::SkipEmptyParts); + for (QStringList::iterator it = anElements.begin(); it != anElements.end(); ++it) { + QList found = myElements->findItems(*it, Qt::MatchExactly); + foreach(anItem, found) + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + bool blocked = myElements->signalsBlocked(); + myElements->blockSignals(true); + foreach(anItem, listItemsToSel) anItem->setSelected(true); + myElements->blockSignals(blocked); + onListSelectionChanged(); + listItemsToSel.clear(); } } } - - if (!myActor) { - if (!myGroup->_is_nil()) - myActor = SMESH::FindActorByObject(myGroup); - else if(!myGroupOnGeom->_is_nil()) - myActor = SMESH::FindActorByObject(myGroupOnGeom); - else - myActor = SMESH::FindActorByObject(myMesh); + + if (myActorsList.count() == 0) { + if (!myGroup->_is_nil()) { + SMESH_Actor* anActor = SMESH::FindActorByObject(myGroup); + if ( anActor ) + myActorsList.append( anActor ); + } + else if(!myGroupOnGeom->_is_nil()) { + SMESH_Actor* anActor = SMESH::FindActorByObject(myGroupOnGeom); + if ( anActor ) + myActorsList.append( anActor ); + } + else { + SMESH_Actor* anActor = SMESH::FindActorByObject( myMesh ); + if ( anActor ) + myActorsList.append( anActor ); + } } // somehow, if we display the mesh, while selecting from another actor, // the mesh becomes pickable, and there is no way to select any element - if (myActor) - SMESH::SetPickable(myActor); + if (myActorsList.count() > 0) { + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + SMESH_Actor* anActor = it.next(); + if ( IsActorVisible(anActor) ) + anActor->SetPickable(true); + } + } myIsBusy = false; } +//================================================================================= +// function : onSelectAll() +// purpose : Called when "Select all" is checked +//================================================================================= +void SMESHGUI_GroupDlg::onSelectAll() +{ + bool noElemsModif = ( mySelectAll->isChecked() || !myAllowElemsModif->isChecked() ); + + myElementsLab->setEnabled( !noElemsModif ); + myElements->setEnabled ( !noElemsModif ); + myFilterBtn->setEnabled ( !mySelectAll->isChecked() ); + myAddBtn->setEnabled ( !noElemsModif ); + myRemoveBtn->setEnabled ( !noElemsModif ); + mySortBtn->setEnabled ( !noElemsModif ); + mySelectBox->setEnabled ( !noElemsModif ); + myAllowElemsModif->setEnabled( !mySelectAll->isChecked() ); + + int selMode = mySelectionMode; + mySelectionMode = grpNoSelection; + setSelectionMode( selMode ); + updateButtons(); +} + //================================================================================= // function : onSelectSubMesh() // purpose : Called when selection in 3D view or ObjectBrowser is changed @@ -1297,7 +1554,7 @@ void SMESHGUI_GroupDlg::onSelectSubMesh(bool on) //VSR: mySelectGeomGroup->setChecked(false); //VSR: } myCurrentLineEdit = mySubMeshLine; - setSelectionMode(4); + setSelectionMode(grpSubMeshSelection); } else { mySubMeshLine->setText( "" ); @@ -1321,7 +1578,7 @@ void SMESHGUI_GroupDlg::onSelectGroup(bool on) mySelectSubMesh->setChecked(false); } myCurrentLineEdit = myGroupLine; - setSelectionMode(5); + setSelectionMode(grpGroupSelection); } else { myGroupLine->setText( "" ); @@ -1336,7 +1593,7 @@ void SMESHGUI_GroupDlg::onSelectGroup(bool on) //================================================================================= // function : (onSelectGeomGroup) -// purpose : Called when selection in 3D view or ObjectBrowser is changed +// purpose : Called when group type changed. on == "on group" or "on filter" //================================================================================= void SMESHGUI_GroupDlg::onSelectGeomGroup(bool on) { @@ -1347,9 +1604,14 @@ void SMESHGUI_GroupDlg::onSelectGeomGroup(bool on) else if (mySelectGroup->isChecked()) { mySelectGroup->setChecked(false); } - myCurrentLineEdit = myGeomGroupLine; - updateGeomPopup(); - setSelectionMode(8); + if ( myGrpTypeId == 1 ) { // on group + myCurrentLineEdit = myGeomGroupLine; + updateGeomPopup(); + } + else { // on filter + myCurrentLineEdit = 0; + } + setSelectionMode(grpAllSelection); } else { myGeomGroupBtn->setChecked(false); @@ -1357,11 +1619,10 @@ void SMESHGUI_GroupDlg::onSelectGeomGroup(bool on) myGeomGroupLine->setText( "" ); myCurrentLineEdit = 0; if (myTypeId != -1) - setSelectionMode(myTypeId); + setSelectionMode( myTypeId ); } } - //================================================================================= // function : setCurrentSelection() // purpose : @@ -1371,11 +1632,14 @@ void SMESHGUI_GroupDlg::setCurrentSelection() QPushButton* send = (QPushButton*)sender(); myCurrentLineEdit = 0; if (send == myMeshGroupBtn) { - myCurrentLineEdit = myMeshGroupLine; + disconnect(myMeshGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); + mySelectionMgr->clearSelected(); if (myCreate) - setSelectionMode(6); + setSelectionMode(grpMeshSelection); else - setSelectionMode(5); + setSelectionMode(grpGroupSelection); + connect(myMeshGroupBtn, SIGNAL(clicked()), this, SLOT(setCurrentSelection())); + myCurrentLineEdit = myMeshGroupLine; onObjectSelectionChanged(); } else if (send == mySubMeshBtn) { @@ -1395,14 +1659,22 @@ void SMESHGUI_GroupDlg::setCurrentSelection() //================================================================================= void SMESHGUI_GroupDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } + SMESH::ElementType aType = SMESH::ALL; switch ( myTypeId ) { - case 0 : aType = SMESH::NODE; break; - case 1 : aType = SMESH::EDGE; break; - case 2 : aType = SMESH::FACE; break; - case 3 : aType = SMESH::VOLUME; break; - default: return; + case grpNodeSelection: aType = SMESH::NODE; break; + case grpBallSelection: aType = SMESH::BALL; break; + case grpEdgeSelection: aType = SMESH::EDGE; break; + case grpFaceSelection: aType = SMESH::FACE; break; + case grpVolumeSelection: aType = SMESH::VOLUME; break; + default: return; } if ( myFilterDlg == 0 ) @@ -1413,9 +1685,15 @@ void SMESHGUI_GroupDlg::setFilters() else myFilterDlg->Init( aType ); + if ( !myGroupOnFilter->_is_nil() ) + { + myFilterDlg->SetFilter( myFilter, aType ); + myFilterDlg->Init( aType ); + } + myFilterDlg->SetSelection(); myFilterDlg->SetMesh( myMesh ); - myFilterDlg->SetSourceWg( myElements ); + myFilterDlg->SetSourceWg( myElements, false ); myFilterDlg->show(); } @@ -1433,6 +1711,28 @@ void SMESHGUI_GroupDlg::onFilterAccepted() mySelectSubMesh->setChecked( false ); mySelectGroup->setChecked( false ); } + // get a filter from myFilterDlg + myFilter = myFilterDlg->GetFilter(); + if ( !myFilter->_is_nil() ) { + SMESH::Predicate_var perdicate = myFilter->GetPredicate(); + if ( perdicate->_is_nil() ) + myFilter = SMESH::Filter::_nil(); + } + // set mesh to myFilter + if ( !myFilter->_is_nil() ) { + SMESH::SMESH_Mesh_var mesh = myMesh; + if ( mesh->_is_nil() ) { + if ( !myGroup->_is_nil() ) + mesh = myGroup->GetMesh(); + else if ( !myGroupOnGeom->_is_nil() ) + mesh = myGroupOnGeom->GetMesh(); + else if ( !myGroupOnFilter->_is_nil() ) + mesh = myGroupOnFilter->GetMesh(); + } + myFilter->SetMesh( mesh ); + } + + updateButtons(); } //================================================================================= @@ -1446,25 +1746,30 @@ void SMESHGUI_GroupDlg::onAdd() int aNbSel = aList.Extent(); - if (aNbSel == 0 || !myActor || myMesh->_is_nil()) return; + if (aNbSel == 0 || myActorsList.count() == 0 || myMesh->_is_nil()) return; myIsBusy = true; + int sizeBefore = myElements->count(); SMESH::ElementType aType = SMESH::ALL; switch(myTypeId) { - case 0: + case grpNodeSelection: aType = SMESH::NODE; mySelector->SetSelectionMode(NodeSelection); break; - case 1: + case grpBallSelection: + aType = SMESH::BALL; + //mySelector->SetSelectionMode(BallSelection); + break; + case grpEdgeSelection: aType = SMESH::EDGE; mySelector->SetSelectionMode(EdgeSelection); break; - case 2: + case grpFaceSelection: aType = SMESH::FACE; mySelector->SetSelectionMode(FaceSelection); break; - case 3: + case grpVolumeSelection: aType = SMESH::VOLUME; mySelector->SetSelectionMode(VolumeSelection); break; @@ -1480,26 +1785,36 @@ void SMESHGUI_GroupDlg::onAdd() QString aListStr = ""; int aNbItems = 0; if (myTypeId == 0) { - aNbItems = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), aListStr); + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + QString tmpStr; + aNbItems += SMESH::GetNameOfSelectedNodes(mySelector, it.next()->getIO(), tmpStr); + aListStr += tmpStr; + } } else { - aNbItems = SMESH::GetNameOfSelectedElements(mySelector, myActor->getIO(), aListStr); + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + QString tmpStr; + aNbItems += SMESH::GetNameOfSelectedElements(mySelector, it.next()->getIO(), tmpStr); + aListStr += tmpStr; + } } if (aNbItems > 0) { QStringList anElements = aListStr.split( " ", QString::SkipEmptyParts); for (QStringList::iterator it = anElements.begin(); it != anElements.end(); ++it) { - QList found = myElements->findItems(*it, Qt::MatchExactly); - if (found.count() == 0) { - anItem = new QListWidgetItem(*it); - myElements->addItem(anItem); - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } - else { - foreach(anItem, found) - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } + QList found = myElements->findItems(*it, Qt::MatchExactly); + if (found.count() == 0) { + anItem = new QListWidgetItem(*it); + myElements->addItem(anItem); + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + else { + foreach(anItem, found) + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } } bool blocked = myElements->signalsBlocked(); myElements->blockSignals(true); @@ -1519,8 +1834,8 @@ void SMESHGUI_GroupDlg::onAdd() SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(anIt.Value()); if (!aSubMesh->_is_nil()) { - // check if mesh is the same - if (aSubMesh->GetFather()->GetId() == myMesh->GetId()) { + // check if mesh is the same + if (aSubMesh->GetFather()->GetId() == myMesh->GetId()) { try { SMESH::long_array_var anElements = aSubMesh->GetElementsByType(aType); int k = anElements->length(); @@ -1530,21 +1845,21 @@ void SMESHGUI_GroupDlg::onAdd() if (found.count() == 0) { anItem = new QListWidgetItem(aText); myElements->addItem(anItem); - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + else { + foreach(anItem, found) + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); } - else { - foreach(anItem, found) - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } } - bool blocked = myElements->signalsBlocked(); - myElements->blockSignals(true); - foreach(anItem, listItemsToSel) anItem->setSelected(true); - myElements->blockSignals(blocked); - onListSelectionChanged(); - listItemsToSel.clear(); + bool blocked = myElements->signalsBlocked(); + myElements->blockSignals(true); + foreach(anItem, listItemsToSel) anItem->setSelected(true); + myElements->blockSignals(blocked); + onListSelectionChanged(); + listItemsToSel.clear(); } catch (const SALOME::SALOME_Exception& ex) { SalomeApp_Tools::QtCatchCorbaException(ex); @@ -1566,32 +1881,32 @@ void SMESHGUI_GroupDlg::onAdd() SMESH::SMESH_GroupBase_var aGroup = SMESH::IObjectToInterface(anIt.Value()); if (!aGroup->_is_nil()) { - // check if mesh is the same - if (aGroup->GetType() == aType && aGroup->GetMesh()->GetId() == myMesh->GetId()) { - SMESH::long_array_var anElements = aGroup->GetListOfID(); - int k = anElements->length(); - for (int i = 0; i < k; i++) { - QString aText = QString::number(anElements[i]); - QList found = myElements->findItems(aText, Qt::MatchExactly); - if (found.count() == 0) { - anItem = new QListWidgetItem(aText); - myElements->addItem(anItem); - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } - else { - foreach(anItem, found) - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } - } - bool blocked = myElements->signalsBlocked(); - myElements->blockSignals(true); - foreach(anItem, listItemsToSel) anItem->setSelected(true); - myElements->blockSignals(blocked); - onListSelectionChanged(); - listItemsToSel.clear(); - } + // check if mesh is the same + if (aGroup->GetType() == aType && aGroup->GetMesh()->GetId() == myMesh->GetId()) { + SMESH::long_array_var anElements = aGroup->GetListOfID(); + int k = anElements->length(); + for (int i = 0; i < k; i++) { + QString aText = QString::number(anElements[i]); + QList found = myElements->findItems(aText, Qt::MatchExactly); + if (found.count() == 0) { + anItem = new QListWidgetItem(aText); + myElements->addItem(anItem); + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + else { + foreach(anItem, found) + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + } + bool blocked = myElements->signalsBlocked(); + myElements->blockSignals(true); + foreach(anItem, listItemsToSel) anItem->setSelected(true); + myElements->blockSignals(blocked); + onListSelectionChanged(); + listItemsToSel.clear(); + } } } mySelectGroup->setChecked(false); @@ -1605,10 +1920,10 @@ void SMESHGUI_GroupDlg::onAdd() SMESH::ElementType aGroupType = SMESH::ALL; switch(aGroupOp->GetType(myGeomObjects[0])) { - case 7: aGroupType = SMESH::NODE; break; - case 6: aGroupType = SMESH::EDGE; break; - case 4: aGroupType = SMESH::FACE; break; - case 2: aGroupType = SMESH::VOLUME; break; + case TopAbs_VERTEX: aGroupType = SMESH::NODE; break; + case TopAbs_EDGE: aGroupType = SMESH::EDGE; break; + case TopAbs_FACE: aGroupType = SMESH::FACE; break; + case TopAbs_SOLID: aGroupType = SMESH::VOLUME; break; default: myIsBusy = false; return; } @@ -1619,7 +1934,7 @@ void SMESHGUI_GroupDlg::onAdd() // Construct filter SMESH::FilterManager_var aFilterMgr = SMESH::GetFilterManager(); SMESH::Filter_var aFilter = aFilterMgr->CreateFilter(); - SMESH::BelongToGeom_var aBelongToGeom = aFilterMgr->CreateBelongToGeom();; + SMESH::BelongToGeom_var aBelongToGeom = aFilterMgr->CreateBelongToGeom(); aBelongToGeom->SetGeom(myGeomObjects[0]); aBelongToGeom->SetShapeName(aGroupSO->GetName().c_str()); aBelongToGeom->SetElementType(aType); @@ -1629,19 +1944,19 @@ void SMESHGUI_GroupDlg::onAdd() int k = anElements->length(); for (int i = 0; i < k; i++) { - QString aText = QString::number(anElements[i]); - QList found = myElements->findItems(aText, Qt::MatchExactly); - if (found.count() == 0) { - anItem = new QListWidgetItem(aText); - myElements->addItem(anItem); - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } - else { - foreach(anItem, found) - if (!anItem->isSelected()) - listItemsToSel.push_back(anItem); - } + QString aText = QString::number(anElements[i]); + QList found = myElements->findItems(aText, Qt::MatchExactly); + if (found.count() == 0) { + anItem = new QListWidgetItem(aText); + myElements->addItem(anItem); + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } + else { + foreach(anItem, found) + if (!anItem->isSelected()) + listItemsToSel.push_back(anItem); + } } bool blocked = myElements->signalsBlocked(); myElements->blockSignals(true); @@ -1656,6 +1971,8 @@ void SMESHGUI_GroupDlg::onAdd() onListSelectionChanged(); } myIsBusy = false; + if ( sizeBefore < myElements->count() ) + ++myNbChangesOfContents; // mySelectionMgr->clearSelected(); updateButtons(); } @@ -1667,6 +1984,8 @@ void SMESHGUI_GroupDlg::onAdd() void SMESHGUI_GroupDlg::onRemove() { myIsBusy = true; + int sizeBefore = myElements->count(); + if (myCurrentLineEdit == 0) { QList selItems = myElements->selectedItems(); QListWidgetItem* item; @@ -1681,10 +2000,11 @@ void SMESHGUI_GroupDlg::onRemove() SMESH::ElementType aType = SMESH::ALL; switch(myTypeId) { - case 0: aType = SMESH::NODE; break; - case 1: aType = SMESH::EDGE; break; - case 2: aType = SMESH::FACE; break; - case 3: aType = SMESH::VOLUME; break; + case grpNodeSelection: aType = SMESH::NODE; break; + case grpBallSelection: aType = SMESH::BALL; break; + case grpEdgeSelection: aType = SMESH::EDGE; break; + case grpFaceSelection: aType = SMESH::FACE; break; + case grpVolumeSelection: aType = SMESH::VOLUME; break; } if (myCurrentLineEdit == mySubMeshLine) { @@ -1694,42 +2014,42 @@ void SMESHGUI_GroupDlg::onRemove() SALOME_ListIteratorOfListIO anIt (aList); for ( ; anIt.More(); anIt.Next()) { - SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(anIt.Value()); - if (!aSubMesh->_is_nil()) { - // check if mesh is the same - if (aSubMesh->GetFather()->GetId() == myMesh->GetId()) { - if (aType == SMESH::NODE) { - try { - SMESH::long_array_var anElements = aSubMesh->GetNodesId(); - int k = anElements->length(); - for (int i = 0; i < k; i++) { - QList found = - myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); - QListWidgetItem* anItem; - foreach(anItem, found) delete anItem; - } - } - catch (const SALOME::SALOME_Exception& ex) { - SalomeApp_Tools::QtCatchCorbaException(ex); - } - } - else { - try { - SMESH::long_array_var anElements = aSubMesh->GetElementsId(); - int k = anElements->length(); - for (int i = 0; i < k; i++) { - QList found = - myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); - QListWidgetItem* anItem; - foreach(anItem, found) delete anItem; - } - } - catch (const SALOME::SALOME_Exception& ex) { - SalomeApp_Tools::QtCatchCorbaException(ex); - } - } - } - } + SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(anIt.Value()); + if (!aSubMesh->_is_nil()) { + // check if mesh is the same + if (aSubMesh->GetFather()->GetId() == myMesh->GetId()) { + if (aType == SMESH::NODE) { + try { + SMESH::long_array_var anElements = aSubMesh->GetNodesId(); + int k = anElements->length(); + for (int i = 0; i < k; i++) { + QList found = + myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); + QListWidgetItem* anItem; + foreach(anItem, found) delete anItem; + } + } + catch (const SALOME::SALOME_Exception& ex) { + SalomeApp_Tools::QtCatchCorbaException(ex); + } + } + else { + try { + SMESH::long_array_var anElements = aSubMesh->GetElementsId(); + int k = anElements->length(); + for (int i = 0; i < k; i++) { + QList found = + myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); + QListWidgetItem* anItem; + foreach(anItem, found) delete anItem; + } + } + catch (const SALOME::SALOME_Exception& ex) { + SalomeApp_Tools::QtCatchCorbaException(ex); + } + } + } + } } } else if (myCurrentLineEdit == myGroupLine) { @@ -1740,24 +2060,26 @@ void SMESHGUI_GroupDlg::onRemove() SALOME_ListIteratorOfListIO anIt (aList); for ( ; anIt.More(); anIt.Next()) { - SMESH::SMESH_Group_var aGroup = SMESH::IObjectToInterface(anIt.Value()); - if (aRes && !aGroup->_is_nil()) { - // check if mesh is the same - if (aGroup->GetType() == aType && aGroup->GetMesh()->GetId() == myMesh->GetId()) { - SMESH::long_array_var anElements = aGroup->GetListOfID(); - int k = anElements->length(); - for (int i = 0; i < k; i++) { - QList found = - myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); - QListWidgetItem* anItem; - foreach(anItem, found) delete anItem; - } - } - } + SMESH::SMESH_Group_var aGroup = SMESH::IObjectToInterface(anIt.Value()); + if (aRes && !aGroup->_is_nil()) { + // check if mesh is the same + if (aGroup->GetType() == aType && aGroup->GetMesh()->GetId() == myMesh->GetId()) { + SMESH::long_array_var anElements = aGroup->GetListOfID(); + int k = anElements->length(); + for (int i = 0; i < k; i++) { + QList found = + myElements->findItems(QString::number(anElements[i]), Qt::MatchExactly); + QListWidgetItem* anItem; + foreach(anItem, found) delete anItem; + } + } + } } } } myIsBusy = false; + if ( sizeBefore > myElements->count() ) + myNbChangesOfContents += 2; // it's used to detect that "Add" was only once updateButtons(); } @@ -1781,7 +2103,7 @@ void SMESHGUI_GroupDlg::onSort() int id = myElements->item(i)->text().toInt(); anArray[i] = id; if (myElements->item(i)->isSelected()) - aSelected.append(id); + aSelected.append(id); } // sort & update list std::sort(anArray.begin(), anArray.end()); @@ -1793,7 +2115,7 @@ void SMESHGUI_GroupDlg::onSort() anItem = new QListWidgetItem(QString::number(anArray[i])); myElements->addItem(anItem); if (aSelected.contains(anArray[i])) - listItemsToSel.push_back(anItem); + listItemsToSel.push_back(anItem); } bool blocked = myElements->signalsBlocked(); myElements->blockSignals(true); @@ -1813,6 +2135,15 @@ void SMESHGUI_GroupDlg::closeEvent (QCloseEvent*) onClose(); } +//================================================================================= +// function : onVisibilityChanged() +// purpose : +//================================================================================= +void SMESHGUI_GroupDlg::onVisibilityChanged() +{ + SetAppropriateActor(); +} + //================================================================================= // function : SMESHGUI_GroupDlg::onClose // purpose : SLOT called when "Close" button pressed. Close dialog @@ -1826,7 +2157,13 @@ void SMESHGUI_GroupDlg::onClose() restoreShowEntityMode(); } - mySelectionMgr->clearSelected(); + if( isApplyAndClose() && !myObjectToSelect.isEmpty() ) { + SUIT_DataOwnerPtrList aList; + aList.append( new LightApp_DataOwner( myObjectToSelect ) ); + mySelectionMgr->setSelected( aList ); + } + else + mySelectionMgr->clearSelected(); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->clearFilters(); @@ -1852,10 +2189,10 @@ void SMESHGUI_GroupDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr( "WRN_WARNING" ), - tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). - arg(app->resourceMgr()->stringValue( "ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). + arg(app->resourceMgr()->stringValue( "ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -1878,7 +2215,7 @@ void SMESHGUI_GroupDlg::enterEvent (QEvent*) if (!isEnabled()) { mySMESHGUI->EmitSignalDeactivateDialog(); setEnabled(true); - mySelectionMode = -1; + mySelectionMode = grpNoSelection; setSelectionMode(myTypeId); //mySMESHGUI->SetActiveDialogBox((QDialog*)this); mySMESHGUI->SetActiveDialogBox(this); @@ -1956,12 +2293,12 @@ void SMESHGUI_GroupDlg::onGeomSelectionButton(bool isBtnOn) myCurrentLineEdit = myGeomGroupLine; QAction* a = myGeomPopup->exec( QCursor::pos() ); if (!a || myActions[a] == DIRECT_GEOM_INDEX) - setSelectionMode(7); + setSelectionMode(grpGeomSelection); } else if (!isBtnOn) { myCurrentLineEdit = 0; - setSelectionMode(8); + setSelectionMode(grpAllSelection); } } @@ -1974,19 +2311,19 @@ void SMESHGUI_GroupDlg::onGeomPopup( QAction* a ) int index = myActions[a]; if ( index == GEOM_BY_MESH_INDEX ) { - mySelectionMode = -1; + mySelectionMode = grpNoSelection; if ( !myShapeByMeshOp ) { - myShapeByMeshOp = new SMESHGUI_ShapeByMeshOp(true); - connect(myShapeByMeshOp, SIGNAL(committed(SUIT_Operation*)), - SLOT(onPublishShapeByMeshDlg(SUIT_Operation*))); - connect(myShapeByMeshOp, SIGNAL(aborted(SUIT_Operation*)), - SLOT(onCloseShapeByMeshDlg(SUIT_Operation*))); + myShapeByMeshOp = new SMESHGUI_ShapeByMeshOp(true); + connect(myShapeByMeshOp, SIGNAL(committed(SUIT_Operation*)), + SLOT(onPublishShapeByMeshDlg(SUIT_Operation*))); + connect(myShapeByMeshOp, SIGNAL(aborted(SUIT_Operation*)), + SLOT(onCloseShapeByMeshDlg(SUIT_Operation*))); } // set mesh object to SMESHGUI_ShapeByMeshOp and start it if ( !myMesh->_is_nil() ) { - myIsBusy = true; + myIsBusy = true; hide(); // stop processing selection - myIsBusy = false; + myIsBusy = false; myShapeByMeshOp->setModule( mySMESHGUI ); myShapeByMeshOp->setStudy( 0 ); // it's really necessary myShapeByMeshOp->SetMesh( myMesh ); @@ -2013,12 +2350,12 @@ void SMESHGUI_GroupDlg::onPublishShapeByMeshDlg(SUIT_Operation* op) QString ID = aGeomVar->GetStudyEntry(); _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); if ( _PTR(SObject) aGeomSO = aStudy->FindObjectID( ID.toLatin1().data() )) { - SALOME_ListIO anIOList; - Handle(SALOME_InteractiveObject) anIO = new SALOME_InteractiveObject - ( aGeomSO->GetID().c_str(), "SMESH", aGeomSO->GetName().c_str() ); - anIOList.Append( anIO ); - mySelectionMgr->setSelectedObjects( anIOList, false ); - onObjectSelectionChanged(); + SALOME_ListIO anIOList; + Handle(SALOME_InteractiveObject) anIO = new SALOME_InteractiveObject + ( aGeomSO->GetID().c_str(), "SMESH", aGeomSO->GetName().c_str() ); + anIOList.Append( anIO ); + mySelectionMgr->setSelectedObjects( anIOList, false ); + onObjectSelectionChanged(); } } } @@ -2035,7 +2372,7 @@ void SMESHGUI_GroupDlg::onCloseShapeByMeshDlg(SUIT_Operation* op) if ( myShapeByMeshOp == op ) { show(); - setSelectionMode(7); + setSelectionMode(grpGeomSelection); } } @@ -2046,8 +2383,8 @@ void SMESHGUI_GroupDlg::onCloseShapeByMeshDlg(SUIT_Operation* op) void SMESHGUI_GroupDlg::setGroupColor( const SALOMEDS::Color& theColor ) { QColor aQColor( (int)( theColor.R * 255.0 ), - (int)( theColor.G * 255.0 ), - (int)( theColor.B * 255.0 ) ); + (int)( theColor.G * 255.0 ), + (int)( theColor.B * 255.0 ) ); setGroupQColor( aQColor ); } @@ -2118,8 +2455,8 @@ void SMESHGUI_GroupDlg::setDefaultGroupColor() SALOMEDS::Color aColor = SMESHGUI::getUniqueColor( aReservedColors ); aQColor.setRgb( (int)( aColor.R * 255.0 ), - (int)( aColor.G * 255.0 ), - (int)( aColor.B * 255.0 ) ); + (int)( aColor.G * 255.0 ), + (int)( aColor.B * 255.0 ) ); } @@ -2135,82 +2472,84 @@ void SMESHGUI_GroupDlg::setDefaultGroupColor() bool SMESHGUI_GroupDlg::SetAppropriateActor() { bool isActor = false; + myActorsList.clear(); if (myMesh->_is_nil()) return false; SVTK_ViewWindow* aViewWindow = SMESH::GetCurrentVtkView(); - // try mesh actor - myActor = SMESH::FindActorByObject(myMesh); - if (myActor && myActor->hasIO()) - { - isActor = true; - if (aViewWindow && !aViewWindow->isVisible(myActor->getIO())) - isActor = false; - } - - // try current group actor - if (!isActor) { - if (!myGroup->_is_nil()) { - myActor = SMESH::FindActorByObject(myGroup); - if (myActor && myActor->hasIO()) - { - isActor = true; - if (aViewWindow && !aViewWindow->isVisible(myActor->getIO())) - isActor = false; + if (myGeomGroupBtn->isChecked()) { // try current group on geometry actor + if (!isActor) { + if (!myGroupOnGeom->_is_nil()) { + SMESH_Actor* anActor = SMESH::FindActorByObject(myGroupOnGeom); + if (anActor && anActor->hasIO()) + { + isActor = true; + if (aViewWindow && !aViewWindow->isVisible(anActor->getIO())) + isActor = false; + else + myActorsList.append(anActor); + } } } - } - - // try current group on geometry actor - if (!isActor) { - if (!myGroupOnGeom->_is_nil()) { - myActor = SMESH::FindActorByObject(myGroupOnGeom); - if (myActor && myActor->hasIO()) - { - isActor = true; - if (aViewWindow && !aViewWindow->isVisible(myActor->getIO())) - isActor = false; - } + } else { + // try mesh actor + SMESH_Actor* anActor = SMESH::FindActorByObject(myMesh); + if (anActor && anActor->hasIO()) { + isActor = true; + if (aViewWindow && !aViewWindow->isVisible(anActor->getIO())) + isActor = false; + else + myActorsList.append(anActor); } - } - - // try any visible actor of group or submesh of current mesh - if (!isActor && aViewWindow) { - // mesh entry - _PTR(SObject) aSObject = SMESH::FindSObject(myMesh); - if (aSObject) { - CORBA::String_var meshEntry = aSObject->GetID().c_str(); - int len = strlen(meshEntry); - - // iterate on all actors in current view window, search for - // any visible actor, that belongs to group or submesh of current mesh - vtkActorCollection *aCollection = aViewWindow->getRenderer()->GetActors(); - aCollection->InitTraversal(); - for (vtkActor *anAct = aCollection->GetNextActor(); - anAct && !isActor; - anAct = aCollection->GetNextActor()) - { - SMESH_Actor *anActor = dynamic_cast(anAct); - if (anActor && anActor->hasIO()) { - Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); - if (aViewWindow->isVisible(anIO)) { - if (anIO->hasEntry() && strncmp(anIO->getEntry(), meshEntry, len) == 0) { - myActor = anActor; - isActor = true; + + // try group actor + if (!isActor && !myGroup->_is_nil()) { + SMESH_Actor* anActor = SMESH::FindActorByObject(myGroup); + if (anActor && anActor->hasIO()) + myActorsList.append(anActor); + } + + // try any visible actor of group or submesh of current mesh + if (aViewWindow) { + // mesh entry + _PTR(SObject) aSObject = SMESH::FindSObject(myMesh); + if (aSObject) { + CORBA::String_var meshEntry = aSObject->GetID().c_str(); + int len = strlen(meshEntry); + + // iterate on all actors in current view window, search for + // any visible actor, that belongs to group or submesh of current mesh + VTK::ActorCollectionCopy aCopy(aViewWindow->getRenderer()->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); + int nbItems = aCollection->GetNumberOfItems(); + for (int i=0; i(aCollection->GetItemAsObject(i)); + if (anActor && anActor->hasIO()) { + Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); + if (aViewWindow->isVisible(anIO)) { + if (anIO->hasEntry() && strncmp(anIO->getEntry(), meshEntry, len) == 0 && !myActorsList.contains(anActor) ) + myActorsList.append(anActor); + } } } - } } } } - - if (isActor) - SMESH::SetPickable(myActor); - - return isActor; + + if (myActorsList.count() > 0) { + QListIterator it( myActorsList ); + while ( it.hasNext() ) { + SMESH_Actor* anActor = it.next(); + if ( IsActorVisible(anActor) ) + anActor->SetPickable(true); + } + } + + return ( isActor || (myActorsList.count() > 0) ); } - + //======================================================================= //function : setShowEntityMode //purpose : make shown only entity corresponding to my type @@ -2222,10 +2561,11 @@ void SMESHGUI_GroupDlg::setShowEntityMode() if (!myStoredShownEntity) myStoredShownEntity = actor->GetEntityMode(); switch ( myTypeId ) { - case 0: restoreShowEntityMode(); break; - case 1: actor->SetEntityMode( SMESH_Actor::eEdges ); break; - case 2: actor->SetEntityMode( SMESH_Actor::eFaces ); break; - case 3: actor->SetEntityMode( SMESH_Actor::eVolumes ); break; + case grpNodeSelection: restoreShowEntityMode(); break; + case grpBallSelection: actor->SetEntityMode( SMESH_Actor::eBallElem ); break; + case grpEdgeSelection: actor->SetEntityMode( SMESH_Actor::eEdges ); break; + case grpFaceSelection: actor->SetEntityMode( SMESH_Actor::eFaces ); break; + case grpVolumeSelection: actor->SetEntityMode( SMESH_Actor::eVolumes ); break; } } } @@ -2244,3 +2584,35 @@ void SMESHGUI_GroupDlg::restoreShowEntityMode() } myStoredShownEntity = 0; } + +//======================================================================= +//function : IsActorVisible +//purpose : return visibility of the actor +//======================================================================= +bool SMESHGUI_GroupDlg::IsActorVisible( SMESH_Actor* theActor ) +{ + SVTK_ViewWindow* aViewWindow = SMESH::GetCurrentVtkView(); + if (theActor && aViewWindow) + return aViewWindow->isVisible(theActor->getIO()); + return false; +} + +//================================================================ +//function : setIsApplyAndClose +//purpose : Set value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +void SMESHGUI_GroupDlg::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +//================================================================ +//function : isApplyAndClose +//purpose : Get value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +bool SMESHGUI_GroupDlg::isApplyAndClose() const +{ + return myIsApplyAndClose; +} diff --git a/src/SMESHGUI/SMESHGUI_GroupDlg.h b/src/SMESHGUI/SMESHGUI_GroupDlg.h index 39a417887..2592f4dc4 100644 --- a/src/SMESHGUI/SMESHGUI_GroupDlg.h +++ b/src/SMESHGUI/SMESHGUI_GroupDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupDlg.h // Author : Natalia KOPNOVA, Open CASCADE S.A.S. @@ -38,7 +39,10 @@ #include #include CORBA_SERVER_HEADER(SMESH_Mesh) #include CORBA_SERVER_HEADER(SMESH_Group) +#include CORBA_SERVER_HEADER(SMESH_Filter) +class QGroupBox; +class QLabel; class QLineEdit; class QButtonGroup; class QListWidget; @@ -57,6 +61,7 @@ class SUIT_Operation; class SVTK_Selector; class SUIT_SelectionFilter; class LightApp_SelectionMgr; +class SMESH_LogicalFilter; //================================================================================= // class : SMESHGUI_GroupDlg @@ -68,9 +73,9 @@ class SMESHGUI_EXPORT SMESHGUI_GroupDlg : public QDialog public: SMESHGUI_GroupDlg( SMESHGUI*, - SMESH::SMESH_Mesh_ptr = SMESH::SMESH_Mesh::_nil() ); + SMESH::SMESH_Mesh_ptr = SMESH::SMESH_Mesh::_nil() ); SMESHGUI_GroupDlg( SMESHGUI*, - SMESH::SMESH_GroupBase_ptr, + SMESH::SMESH_GroupBase_ptr, const bool theIsConvert = false ); ~SMESHGUI_GroupDlg(); @@ -90,10 +95,12 @@ private slots: bool onApply(); void onHelp(); void onDeactivate(); + void onVisibilityChanged(); void onListSelectionChanged(); void onObjectSelectionChanged(); + void onSelectAll(); void onSelectSubMesh( bool ); void onSelectGroup( bool ); void onSelectGeomGroup( bool ); @@ -126,6 +133,8 @@ private: bool SetAppropriateActor(); void setShowEntityMode(); void restoreShowEntityMode(); + + bool IsActorVisible( SMESH_Actor* ); void setGroupColor( const SALOMEDS::Color& ); SALOMEDS::Color getGroupColor() const; @@ -134,10 +143,14 @@ private: QColor getGroupQColor() const; void setDefaultGroupColor(); - + + void setIsApplyAndClose( const bool theFlag ); + bool isApplyAndClose() const; + + private: + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ - SMESH_Actor* myActor; /* Current mesh actor */ int myGrpTypeId; /* Current group type id : standalone or group on geometry */ int myTypeId; /* Current type id = radio button id */ int myStoredShownEntity; /* Store ShowEntity mode of myMesh */ @@ -154,13 +167,19 @@ private: QButtonGroup* myGrpTypeGroup; QStackedWidget* myWGStack; + QCheckBox* mySelectAll; + QCheckBox* myAllowElemsModif; + QLabel* myElementsLab; QListWidget* myElements; - QPushButton* myFilter; + QPushButton* myFilterBtn; + QPushButton* myAddBtn; + QPushButton* myRemoveBtn; + QPushButton* mySortBtn; + QGroupBox* mySelectBox; QCheckBox* mySelectSubMesh; QPushButton* mySubMeshBtn; QLineEdit* mySubMeshLine; - QCheckBox* mySelectGroup; QPushButton* myGroupBtn; QLineEdit* myGroupLine; @@ -180,8 +199,11 @@ private: SMESHGUI_ShapeByMeshOp* myShapeByMeshOp; SMESH::SMESH_Mesh_var myMesh; + QList myActorsList; SMESH::SMESH_Group_var myGroup; SMESH::SMESH_GroupOnGeom_var myGroupOnGeom; + SMESH::SMESH_GroupOnFilter_var myGroupOnFilter; + SMESH::Filter_var myFilter; QList myIdList; GEOM::ListOfGO_var myGeomObjects; @@ -190,8 +212,8 @@ private: //Handle(SMESH_TypeFilter) mySubMeshFilter; //Handle(SMESH_TypeFilter) myGroupFilter; SUIT_SelectionFilter* myMeshFilter; - SUIT_SelectionFilter* mySubMeshFilter; - SUIT_SelectionFilter* myGroupFilter; + SMESH_LogicalFilter* mySubMeshFilter; + SMESH_LogicalFilter* myGroupFilter; SUIT_SelectionFilter* myGeomFilter; SMESHGUI_FilterDlg* myFilterDlg; @@ -203,6 +225,10 @@ private: QMap myActions; bool myNameChanged; //added by skl for IPAL19574 + int myNbChangesOfContents; // nb add's and remove's + + QString myObjectToSelect; + bool myIsApplyAndClose; }; #endif // SMESHGUI_GROUPDLG_H diff --git a/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.cxx b/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.cxx index c85a269df..2bfdd1bc9 100644 --- a/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupOnShapeDlg.cxx // Created : Wed Sep 17 18:36:51 2008 @@ -29,6 +27,7 @@ #include "SMESHGUI_GroupOnShapeDlg.h" #include "SMESH_TypeFilter.hxx" +#include "SMESHGUI.h" #include "SMESHGUI_Utils.h" #include "SMESHGUI_GEOMGenUtils.h" @@ -37,6 +36,7 @@ #include #include +#include #include #include @@ -178,7 +178,7 @@ SMESHGUI_GroupOnShapeOp::SMESHGUI_GroupOnShapeOp() : SMESHGUI_SelectionOp(ActorSelection), myDlg( 0 ) { - myHelpFileName = "creating_groups_page.html"; + myHelpFileName = "create_groups_from_geometry_page.html"; } SMESHGUI_GroupOnShapeOp::~SMESHGUI_GroupOnShapeOp() @@ -302,7 +302,7 @@ bool SMESHGUI_GroupOnShapeOp::onApply() if ( !aStudy ) return false; // mesh - _PTR(SObject) meshSO = aStudy->FindObjectID( myMeshID.toLatin1().data() ); + _PTR(SObject) meshSO = aStudy->FindObjectID( myMeshID.toLatin1().data() ); SMESH::SMESH_Mesh_var mesh = SMESH::SObjectToInterface( meshSO ); if ( mesh->_is_nil() ) return false; @@ -319,6 +319,7 @@ bool SMESHGUI_GroupOnShapeOp::onApply() // create groups SMESH::SMESH_GroupOnGeom_var group; + QStringList anEntryList; for ( int isNode = 0; isNode < 2; ++isNode ) // elems and then nodes { QStringList::iterator geomID = isNode ? myNodeGeoIDs.begin() : myElemGeoIDs.begin(); @@ -347,11 +348,30 @@ bool SMESHGUI_GroupOnShapeOp::onApply() //printf( "apply() %s %s\n", (*geomID).latin1(), name.latin1() ); group = mesh->CreateGroupFromGEOM( elemType, name.toLatin1().data(), geom ); + if( !group->_is_nil() ) + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( group ) ) + anEntryList.append( aSObject->GetID().c_str() ); } } update( UF_ObjBrowser | UF_Model ); - init(); + SMESHGUI::Modified(); + + // Re-init controls to create the next group + myElemGeoIDs.clear(); + myNodeGeoIDs.clear(); + removeCustomFilters(); + myDlg->myNodeGeomList->clear(); + myDlg->myElemGeomList->clear(); + myDlg->myElemGeomBtn->setChecked(false); + myDlg->myNodeGeomBtn->setChecked(false); + myDlg->updateButtons(); + + update( UF_ObjBrowser | UF_Model ); + + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); return !group->_is_nil(); } @@ -431,6 +451,7 @@ void SMESHGUI_GroupOnShapeOp::selectionDone() myDlg->myElemGeomBtn->setEnabled( nbSelected == 1 ); myDlg->myNodeGeomList->clear(); myDlg->myNodeGeomBtn->setEnabled( nbSelected == 1 ); + myDlg->myElemGeomBtn->click(); return; } diff --git a/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.h b/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.h index 0009e5996..8e5478974 100644 --- a/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.h +++ b/src/SMESHGUI/SMESHGUI_GroupOnShapeDlg.h @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupOnShapeDlg.h // Author : Edard AGAPOV diff --git a/src/SMESHGUI/SMESHGUI_GroupOpDlg.cxx b/src/SMESHGUI/SMESHGUI_GroupOpDlg.cxx index 44cdfdf94..8588a3c56 100644 --- a/src/SMESHGUI/SMESHGUI_GroupOpDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_GroupOpDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupOpDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -44,6 +45,9 @@ #include #include +// SALOME KERNEL includes +#include + // Qt includes #include #include @@ -74,7 +78,8 @@ SMESHGUI_GroupOpDlg::SMESHGUI_GroupOpDlg( SMESHGUI* theModule ) : QDialog( SMESH::GetDesktop( theModule ) ), mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myIsApplyAndClose( false ) { setModal(false); @@ -131,7 +136,7 @@ QWidget* SMESHGUI_GroupOpDlg::createMainFrame( QWidget* theParent ) QLabel* aColorLab = new QLabel(tr( "SMESH_CHECK_COLOR" ), aColorBox ); myColorBtn = new QtxColorButton(aColorBox); myColorBtn->setSizePolicy( QSizePolicy::MinimumExpanding, - myColorBtn->sizePolicy().verticalPolicy() ); + myColorBtn->sizePolicy().verticalPolicy() ); aColorBoxLayout->addWidget(aColorLab); aColorBoxLayout->addWidget(myColorBtn); @@ -242,7 +247,7 @@ bool SMESHGUI_GroupOpDlg::isValid( const QList& theL if ( theListGrp.isEmpty() ) { SUIT_MessageBox::information( this, tr("SMESH_INSUFFICIENT_DATA"), - tr("INCORRECT_ARGUMENTS") ); + tr("INCORRECT_ARGUMENTS") ); return false; } @@ -289,14 +294,14 @@ bool SMESHGUI_GroupOpDlg::isValid( const QList& theL if ( aMeshId == -1 ) { SUIT_MessageBox::information(this, tr("SMESH_INSUFFICIENT_DATA"), - tr("DIFF_MESHES")); + tr("DIFF_MESHES")); return false; } if ( aGrpType == -1 ) { SUIT_MessageBox::information(this, tr("SMESH_INSUFFICIENT_DATA"), - tr("DIFF_TYPES")); + tr("DIFF_TYPES")); return false; } @@ -308,8 +313,10 @@ bool SMESHGUI_GroupOpDlg::isValid( const QList& theL */ void SMESHGUI_GroupOpDlg::onOk() { + setIsApplyAndClose( true ); if ( onApply() ) onClose(); + setIsApplyAndClose( false ); } /*! @@ -343,10 +350,10 @@ void SMESHGUI_GroupOpDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -524,6 +531,28 @@ bool SMESHGUI_GroupOpDlg::onApply() return false; } +/*! + \brief Set value of the flag indicating that the dialog is + accepted by Apply & Close button + \param theFlag value of the flag + \sa isApplyAndClose() +*/ +void SMESHGUI_GroupOpDlg::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +/*! + \brief Get value of the flag indicating that the dialog is + accepted by Apply & Close button + \return value of the flag + \sa setApplyAndClose() +*/ +bool SMESHGUI_GroupOpDlg::isApplyAndClose() const +{ + return myIsApplyAndClose; +} + // === === === === === === === === === === === === === === === === === === === === === /*! @@ -585,6 +614,7 @@ bool SMESHGUI_UnionGroupsDlg::onApply() QString aName = getName(); bool aRes = false; + QStringList anEntryList; try { SMESH::ListOfGroups_var aList = convert( myGroups ); @@ -593,6 +623,8 @@ bool SMESHGUI_UnionGroupsDlg::onApply() if ( !CORBA::is_nil( aNewGrp ) ) { aNewGrp->SetColor( getColor() ); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aNewGrp ) ) + anEntryList.append( aSObject->GetID().c_str() ); aRes = true; } } @@ -603,14 +635,18 @@ bool SMESHGUI_UnionGroupsDlg::onApply() if ( aRes ) { + SMESHGUI::Modified(); getSMESHGUI()->updateObjBrowser(true); reset(); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); return true; } else { SUIT_MessageBox::critical(this, tr("SMESH_ERROR"), - tr("SMESH_OPERATION_FAILED")); + tr("SMESH_OPERATION_FAILED")); return false; } } @@ -687,6 +723,7 @@ bool SMESHGUI_IntersectGroupsDlg::onApply() QString aName = getName(); bool aRes = false; + QStringList anEntryList; try { SMESH::ListOfGroups_var aList = convert( myGroups ); @@ -695,6 +732,8 @@ bool SMESHGUI_IntersectGroupsDlg::onApply() if ( !CORBA::is_nil( aNewGrp ) ) { aNewGrp->SetColor( getColor() ); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aNewGrp ) ) + anEntryList.append( aSObject->GetID().c_str() ); aRes = true; } } @@ -705,14 +744,18 @@ bool SMESHGUI_IntersectGroupsDlg::onApply() if ( aRes ) { + SMESHGUI::Modified(); getSMESHGUI()->updateObjBrowser(true); reset(); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); return true; } else { SUIT_MessageBox::critical(this, tr("SMESH_ERROR"), - tr("SMESH_OPERATION_FAILED")); + tr("SMESH_OPERATION_FAILED")); return false; } } @@ -852,6 +895,7 @@ bool SMESHGUI_CutGroupsDlg::onApply() QString aName = getName(); bool aRes = false; + QStringList anEntryList; try { SMESH::ListOfGroups_var aList1 = convert( myGroups1 ); @@ -861,6 +905,8 @@ bool SMESHGUI_CutGroupsDlg::onApply() if ( !CORBA::is_nil( aNewGrp ) ) { aNewGrp->SetColor( getColor() ); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aNewGrp ) ) + anEntryList.append( aSObject->GetID().c_str() ); aRes = true; } } @@ -871,14 +917,18 @@ bool SMESHGUI_CutGroupsDlg::onApply() if ( aRes ) { + SMESHGUI::Modified(); getSMESHGUI()->updateObjBrowser(true); reset(); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); return true; } else { SUIT_MessageBox::critical(this, tr("SMESH_ERROR"), - tr("SMESH_OPERATION_FAILED")); + tr("SMESH_OPERATION_FAILED")); return false; } } @@ -913,7 +963,7 @@ SMESHGUI_DimGroupDlg::SMESHGUI_DimGroupDlg( SMESHGUI* theModule ) : SMESHGUI_GroupOpDlg( theModule ) { setWindowTitle( tr( "CREATE_GROUP_OF_UNDERLYING_ELEMS" ) ); - setHelpFileName( "creating_groups_page.html#gui_create_dim_group" ); + setHelpFileName( "group_of_underlying_elements_page.html" ); QGroupBox* anArgGrp = getArgGrp(); @@ -1003,6 +1053,7 @@ bool SMESHGUI_DimGroupDlg::onApply() QString aName = getName(); bool aRes = false; + QStringList anEntryList; try { SMESH::ListOfGroups_var aList = convert( myGroups ); @@ -1012,6 +1063,8 @@ bool SMESHGUI_DimGroupDlg::onApply() if ( !CORBA::is_nil( aNewGrp ) ) { aNewGrp->SetColor( getColor() ); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( aNewGrp ) ) + anEntryList.append( aSObject->GetID().c_str() ); aRes = true; } } @@ -1022,14 +1075,18 @@ bool SMESHGUI_DimGroupDlg::onApply() if ( aRes ) { + SMESHGUI::Modified(); getSMESHGUI()->updateObjBrowser(true); reset(); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); return true; } else { SUIT_MessageBox::critical(this, tr("SMESH_ERROR"), - tr("SMESH_OPERATION_FAILED")); + tr("SMESH_OPERATION_FAILED")); return false; } } diff --git a/src/SMESHGUI/SMESHGUI_GroupOpDlg.h b/src/SMESHGUI/SMESHGUI_GroupOpDlg.h index fda94dd01..ec3b4c2f5 100644 --- a/src/SMESHGUI/SMESHGUI_GroupOpDlg.h +++ b/src/SMESHGUI/SMESHGUI_GroupOpDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupOpDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -87,6 +88,9 @@ protected: SALOMEDS::Color getColor() const; + void setIsApplyAndClose( const bool theFlag ); + bool isApplyAndClose() const; + private: void closeEvent( QCloseEvent* ); void enterEvent( QEvent* ); @@ -118,6 +122,8 @@ private: SVTK_Selector* mySelector; QString myHelpFileName; + + bool myIsApplyAndClose; }; /* diff --git a/src/SMESHGUI/SMESHGUI_GroupUtils.cxx b/src/SMESHGUI/SMESHGUI_GroupUtils.cxx index e645257e2..093426cfd 100644 --- a/src/SMESHGUI/SMESHGUI_GroupUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_GroupUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupUtils.cxx // Author : Open CASCADE S.A.S. @@ -35,13 +36,13 @@ namespace SMESH { SMESH::SMESH_Group_var AddGroup( SMESH::SMESH_Mesh_ptr theMesh, - SMESH::ElementType theType, - const QString& theGroupName ) + SMESH::ElementType theType, + const QString& theGroupName ) { SMESH::SMESH_Group_var aGroup; try { if ( !theMesh->_is_nil() ) - aGroup = theMesh->CreateGroup( theType, theGroupName.toLatin1().data() ); + aGroup = theMesh->CreateGroup( theType, theGroupName.toLatin1().data() ); } catch( const SALOME::SALOME_Exception& S_ex ) { SalomeApp_Tools::QtCatchCorbaException( S_ex ); diff --git a/src/SMESHGUI/SMESHGUI_GroupUtils.h b/src/SMESHGUI/SMESHGUI_GroupUtils.h index e0673a783..1ede5161b 100644 --- a/src/SMESHGUI/SMESHGUI_GroupUtils.h +++ b/src/SMESHGUI/SMESHGUI_GroupUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_GroupUtils.h // Author : Open CASCADE S.A.S. @@ -40,8 +41,8 @@ namespace SMESH { SMESHGUI_EXPORT SMESH::SMESH_Group_var AddGroup( SMESH::SMESH_Mesh_ptr, - SMESH::ElementType, - const QString& ); + SMESH::ElementType, + const QString& ); } #endif // SMESHGUI_GROUPUTILS_H diff --git a/src/SMESHGUI/SMESHGUI_Hypotheses.cxx b/src/SMESHGUI/SMESHGUI_Hypotheses.cxx index 8ca61f49f..3d0baf890 100644 --- a/src/SMESHGUI/SMESHGUI_Hypotheses.cxx +++ b/src/SMESHGUI/SMESHGUI_Hypotheses.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_Hypotheses.cxx -// Author : Julia DOROVSKIKH, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_Hypotheses.cxx +// Author : Julia DOROVSKIKH, Open CASCADE S.A.S. +// SMESH includes + #include "SMESHGUI_Hypotheses.h" #include "SMESHGUI.h" @@ -48,13 +47,16 @@ #include #include #include -#include #define SPACING 6 #define MARGIN 11 +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + SMESHGUI_GenericHypothesisCreator::SMESHGUI_GenericHypothesisCreator( const QString& theHypType ) - : myHypType( theHypType ), myIsCreate( false ), myDlg( 0 ), myEventLoop( 0 ) + : myHypType( theHypType ), myIsCreate( false ), myDlg( 0 ) { } @@ -69,46 +71,45 @@ void SMESHGUI_GenericHypothesisCreator::setInitParamsHypothesis(SMESH::SMESH_Hyp } void SMESHGUI_GenericHypothesisCreator::create( SMESH::SMESH_Hypothesis_ptr initParamsHyp, - const QString& theHypName, - QWidget* parent) + const QString& theHypName, + QWidget* parent, QObject* obj, const QString& slot ) { MESSAGE( "Creation of hypothesis with initial params" ); setInitParamsHypothesis( initParamsHyp ); - create( false, theHypName, parent ); + create( false, theHypName, parent, obj, slot ); } void SMESHGUI_GenericHypothesisCreator::create( bool isAlgo, - const QString& theHypName, - QWidget* theParent ) + const QString& theHypName, + QWidget* theParent, QObject* obj, const QString& slot ) { MESSAGE( "Creation of hypothesis" ); myIsCreate = true; // Create hypothesis/algorithm - if (isAlgo) - SMESH::CreateHypothesis( hypType(), theHypName, isAlgo ); - else - { - SMESH::SMESH_Hypothesis_var aHypothesis = + if (isAlgo) { + SMESH::SMESH_Hypothesis_var anAlgo = + SMESH::CreateHypothesis( hypType(), theHypName, isAlgo ); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(anAlgo)) + anAlgo->UnRegister(); +#endif + } + else { + SMESH::SMESH_Hypothesis_var aHypothesis = SMESH::CreateHypothesis( hypType(), theHypName, false ); - if( !editHypothesis( aHypothesis.in(), theHypName, theParent ) ) - { //remove just created hypothesis - _PTR(SObject) aHypSObject = SMESH::FindSObject( aHypothesis.in() ); - _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); - if( aStudy && !aStudy->GetProperties()->IsLocked() ) - { - _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); - aBuilder->RemoveObjectWithChildren( aHypSObject ); - } - } + editHypothesis( aHypothesis.in(), theHypName, theParent, obj, slot ); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(aHypothesis)) + aHypothesis->UnRegister(); +#endif } - SMESHGUI::GetSMESHGUI()->updateObjBrowser( true, 0 ); } void SMESHGUI_GenericHypothesisCreator::edit( SMESH::SMESH_Hypothesis_ptr theHypothesis, - const QString& theHypName, - QWidget* theParent ) + const QString& theHypName, + QWidget* theParent, QObject* obj, const QString& slot ) { if( CORBA::is_nil( theHypothesis ) ) return; @@ -117,37 +118,25 @@ void SMESHGUI_GenericHypothesisCreator::edit( SMESH::SMESH_Hypothesis_ptr theHyp myIsCreate = false; - if( !editHypothesis( theHypothesis, theHypName, theParent ) ) - return; - - SMESH::SObjectList listSOmesh = SMESH::GetMeshesUsingAlgoOrHypothesis( theHypothesis ); - if( listSOmesh.size() > 0 ) - for( int i = 0; i < listSOmesh.size(); i++ ) - { - _PTR(SObject) submSO = listSOmesh[i]; - SMESH::SMESH_Mesh_var aMesh = SMESH::SObjectToInterface( submSO ); - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SObjectToInterface( submSO ); - if( !aSubMesh->_is_nil() ) - aMesh = aSubMesh->GetFather(); - _PTR(SObject) meshSO = SMESH::FindSObject( aMesh ); - SMESH::ModifiedMesh( meshSO, false, aMesh->NbNodes()==0); - } - SMESHGUI::GetSMESHGUI()->updateObjBrowser( true, 0 ); + editHypothesis( theHypothesis, theHypName, theParent, obj, slot ); } -bool SMESHGUI_GenericHypothesisCreator::editHypothesis( SMESH::SMESH_Hypothesis_ptr h, - const QString& theHypName, - QWidget* theParent ) +void SMESHGUI_GenericHypothesisCreator::editHypothesis( SMESH::SMESH_Hypothesis_ptr h, + const QString& theHypName, + QWidget* theParent, + QObject* obj, const QString& slot ) { - if( CORBA::is_nil( h ) ) - return false; - - bool res = true; myHypName = theHypName; myHypo = SMESH::SMESH_Hypothesis::_duplicate( h ); +#ifdef WITHGENERICOBJ + myHypo->Register(); +#endif SMESHGUI_HypothesisDlg* Dlg = new SMESHGUI_HypothesisDlg( this, theParent ); connect( Dlg, SIGNAL( finished( int ) ), this, SLOT( onDialogFinished( int ) ) ); + connect( this, SIGNAL( finished( int ) ), obj, slot.toLatin1().constData() ); + connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalCloseAllDialogs() ), Dlg, SLOT( reject() )); + myDlg = Dlg; QFrame* fr = buildFrame(); if( fr ) @@ -159,25 +148,14 @@ bool SMESHGUI_GenericHypothesisCreator::editHypothesis( SMESH::SMESH_Hypothesis_ Dlg->setType( type() ); retrieveParams(); Dlg->show(); - if ( !myEventLoop ) - myEventLoop = new QEventLoop( this ); - myEventLoop->exec(); // make myDlg not modal - res = myDlg->result(); - if( res ) { - QString paramValues = storeParams(); - if ( !paramValues.isEmpty() ) { - if ( _PTR(SObject) SHyp = SMESH::FindSObject( myHypo )) - SMESH::SetValue( SHyp, paramValues ); - } - } + Dlg->resize( Dlg->minimumSizeHint() ); + } + else { + emit finished( QDialog::Accepted ); + delete myDlg; } - delete Dlg; myDlg = 0; - changeWidgets().clear(); - myHypo = SMESH::SMESH_Hypothesis::_nil(); - myInitParamsHypo = SMESH::SMESH_Hypothesis::_nil(); - return res; } - + QFrame* SMESHGUI_GenericHypothesisCreator::buildStdFrame() { if( CORBA::is_nil( hypothesis() ) ) @@ -206,13 +184,13 @@ QFrame* SMESHGUI_GenericHypothesisCreator::buildStdFrame() GroupC1Layout->addWidget( lab, i, 0 ); QWidget* w = getCustomWidget( *anIt, GroupC1, i ); - if ( !w ) + if ( !w ) switch( (*anIt).myValue.type() ) { case QVariant::Int: { SalomeApp_IntSpinBox* sb = new SalomeApp_IntSpinBox( GroupC1 ); - sb->setObjectName( (*anIt).myName ); + sb->setObjectName( (*anIt).myName ); attuneStdWidget( sb, i ); sb->setValue( (*anIt).myValue.toInt() ); connect( sb, SIGNAL( valueChanged( int ) ), this, SLOT( onValueChanged() ) ); @@ -222,7 +200,7 @@ QFrame* SMESHGUI_GenericHypothesisCreator::buildStdFrame() case QVariant::Double: { SalomeApp_DoubleSpinBox* sb = new SMESHGUI_SpinBox( GroupC1 ); - sb->setObjectName( (*anIt).myName ); + sb->setObjectName( (*anIt).myName ); attuneStdWidget( sb, i ); sb->setValue( (*anIt).myValue.toDouble() ); connect( sb, SIGNAL( valueChanged( double ) ), this, SLOT( onValueChanged() ) ); @@ -243,7 +221,7 @@ QFrame* SMESHGUI_GenericHypothesisCreator::buildStdFrame() w = sb; } else if(aStudy->IsReal(aVar.toLatin1().constData())){ - SalomeApp_DoubleSpinBox* sb = new SalomeApp_DoubleSpinBox( GroupC1 ); + SalomeApp_DoubleSpinBox* sb = new SMESHGUI_SpinBox( GroupC1 ); sb->setObjectName( (*anIt).myName ); attuneStdWidget( sb, i ); sb->setText( aVar ); @@ -282,10 +260,59 @@ void SMESHGUI_GenericHypothesisCreator::valueChanged( QWidget* ) { } -void SMESHGUI_GenericHypothesisCreator::onDialogFinished( int /*result*/ ) +void SMESHGUI_GenericHypothesisCreator::onDialogFinished( int result ) { - if ( myEventLoop ) - myEventLoop->exit(); + bool res = result==QDialog::Accepted; + if( res ) + { + /*QString paramValues = */storeParams(); + // No longer needed since NoteBook appears and "Value" OB field shows names of variable +// if ( !paramValues.isEmpty() ) { +// if ( _PTR(SObject) SHyp = SMESH::FindSObject( myHypo )) +// SMESH::SetValue( SHyp, paramValues ); +// } + } + + changeWidgets().clear(); + + if( myIsCreate && !res ) + { + //remove just created hypothesis + _PTR(SObject) aHypSObject = SMESH::FindSObject( myHypo ); + _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); + if( aStudy && !aStudy->GetProperties()->IsLocked() ) + { + _PTR(StudyBuilder) aBuilder = aStudy->NewBuilder(); + aBuilder->RemoveObjectWithChildren( aHypSObject ); + } + } + else if( !myIsCreate && res ) + { + SMESH::SObjectList listSOmesh = SMESH::GetMeshesUsingAlgoOrHypothesis( myHypo ); + if( listSOmesh.size() > 0 ) + for( int i = 0; i < listSOmesh.size(); i++ ) + { + _PTR(SObject) submSO = listSOmesh[i]; + SMESH::SMESH_Mesh_var aMesh = SMESH::SObjectToInterface( submSO ); + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SObjectToInterface( submSO ); + if( !aSubMesh->_is_nil() ) + aMesh = aSubMesh->GetFather(); + _PTR(SObject) meshSO = SMESH::FindSObject( aMesh ); + SMESH::ModifiedMesh( meshSO, false, aMesh->NbNodes()==0); + } + } + SMESHGUI::GetSMESHGUI()->updateObjBrowser( true, 0 ); +#ifdef WITHGENERICOBJ + myHypo->UnRegister(); +#endif + myHypo = SMESH::SMESH_Hypothesis::_nil(); + myInitParamsHypo = SMESH::SMESH_Hypothesis::_nil(); + + disconnect( myDlg, SIGNAL( finished( int ) ), this, SLOT( onDialogFinished( int ) ) ); + myDlg->close(); + //delete myDlg; since WA_DeleteOnClose==true + myDlg = 0; + emit finished( result ); } bool SMESHGUI_GenericHypothesisCreator::stdParams( ListOfStdParams& ) const @@ -305,48 +332,52 @@ bool SMESHGUI_GenericHypothesisCreator::getStdParamFromDlg( ListOfStdParams& par { SalomeApp_IntSpinBox* sb = ( SalomeApp_IntSpinBox* )( *anIt ); item.myValue = sb->value(); + item.myText = sb->text(); params.append( item ); } - else if( (*anIt)->inherits( "SalomeApp_DoubleSpinBox" ) ) { SalomeApp_DoubleSpinBox* sb = ( SalomeApp_DoubleSpinBox* )( *anIt ); item.myValue = sb->value(); + item.myText = sb->text(); params.append( item ); } - else if( (*anIt)->inherits( "QLineEdit" ) ) { QLineEdit* line = ( QLineEdit* )( *anIt ); - item.myValue = line->text(); + item.myValue = item.myText = line->text(); params.append( item ); } - else if ( getParamFromCustomWidget( item, *anIt )) { params.append( item ); } - else res = false; } return res; } +QString SMESHGUI_GenericHypothesisCreator::getVariableName(const char* methodName) const +{ + SMESH::SMESH_Hypothesis_var h = hypothesis(); + if ( !h->_is_nil() ) + { + CORBA::String_var aVaribaleName = h->GetVarParameter( methodName ); + return QString( aVaribaleName.in() ); + } + return QString(); +} QStringList SMESHGUI_GenericHypothesisCreator::getVariablesFromDlg() const { QStringList aResult; ListOfWidgets::const_iterator anIt = widgets().begin(), aLast = widgets().end(); for( ; anIt!=aLast; anIt++ ) { - if( (*anIt)->inherits( "SalomeApp_IntSpinBox" ) ) { - SalomeApp_IntSpinBox* sb = ( SalomeApp_IntSpinBox* )( *anIt ); + if( (*anIt)->inherits( "QAbstractSpinBox" ) ) { + QAbstractSpinBox* sb = ( QAbstractSpinBox* )( *anIt ); aResult.append(sb->text()); - } - else if( (*anIt)->inherits( "QtxDoubleSpinBox" ) ) { - QtxDoubleSpinBox* sb = ( QtxDoubleSpinBox* )( *anIt ); - aResult.append(sb->text()); - } + } } return aResult; } @@ -386,6 +417,11 @@ SMESH::SMESH_Hypothesis_var SMESHGUI_GenericHypothesisCreator::hypothesis() cons return myHypo; } +void SMESHGUI_GenericHypothesisCreator::setShapeEntry( const QString& theEntry ) +{ + myShapeEntry = theEntry; +} + //================================================================================ /*! * \brief Return hypothesis containing initial parameters @@ -430,7 +466,7 @@ SMESHGUI_GenericHypothesisCreator::ListOfWidgets& SMESHGUI_GenericHypothesisCrea } QtxDialog* SMESHGUI_GenericHypothesisCreator:: dlg() const -{ +{ return myDlg; } @@ -495,11 +531,15 @@ void SMESHGUI_GenericHypothesisCreator::onReject() QString SMESHGUI_GenericHypothesisCreator::helpPage() const { QString aHypType = hypType(); - QString aHelpFileName; + QString aHelpFileName = ""; if ( aHypType == "LocalLength" ) aHelpFileName = "a1d_meshing_hypo_page.html#average_length_anchor"; + else if ( aHypType == "MaxLength" ) + aHelpFileName = "a1d_meshing_hypo_page.html#max_length_anchor"; else if ( aHypType == "Arithmetic1D") aHelpFileName = "a1d_meshing_hypo_page.html#arithmetic_1d_anchor"; + else if ( aHypType == "FixedPoints1D") + aHelpFileName = "a1d_meshing_hypo_page.html#fixed_points_1d_anchor"; else if ( aHypType == "MaxElementArea") aHelpFileName = "a2d_meshing_hypo_page.html#max_element_area_anchor"; else if ( aHypType == "MaxElementVolume") @@ -520,23 +560,29 @@ QString SMESHGUI_GenericHypothesisCreator::helpPage() const aHelpFileName = "projection_algos_page.html"; else if ( aHypType == "NumberOfLayers") aHelpFileName = "radial_prism_algo_page.html"; + else if ( aHypType == "NumberOfLayers2D") + aHelpFileName = "radial_quadrangle_1D2D_algo_page.html"; else if ( aHypType == "LayerDistribution") aHelpFileName = "radial_prism_algo_page.html"; + else if ( aHypType == "LayerDistribution2D") + aHelpFileName = "radial_quadrangle_1D2D_algo_page.html"; else if ( aHypType == "SegmentLengthAroundVertex") - aHelpFileName = "segments_around_vertex_algo.html"; - else - aHelpFileName = ""; + aHelpFileName = "segments_around_vertex_algo_page.html"; + else if ( aHypType == "QuadrangleParams") + aHelpFileName = "a2d_meshing_hypo_page.html#hypo_quad_params_anchor"; + else if ( aHypType == "ViscousLayers") + aHelpFileName = "additional_hypo_page.html#viscous_layers_anchor"; + else if ( aHypType == "ImportSource1D" || aHypType == "ImportSource2D") + aHelpFileName = "import_algos_page.html"; return aHelpFileName; } - - - SMESHGUI_HypothesisDlg::SMESHGUI_HypothesisDlg( SMESHGUI_GenericHypothesisCreator* creator, QWidget* parent ) : QtxDialog( parent, false, true ), myCreator( creator ) { - setMinimumSize( 300, height() ); + setAttribute(Qt::WA_DeleteOnClose, true); + // setMinimumSize( 300, height() ); // setFixedSize( 300, height() ); QVBoxLayout* topLayout = new QVBoxLayout( mainFrame() ); topLayout->setMargin( 0 ); @@ -546,7 +592,7 @@ SMESHGUI_HypothesisDlg::SMESHGUI_HypothesisDlg( SMESHGUI_GenericHypothesisCreato QHBoxLayout* titLay = new QHBoxLayout( titFrame ); titLay->setMargin( 0 ); titLay->setSpacing( SPACING ); - + myIconLabel = new QLabel( titFrame ); myIconLabel->setScaledContents( false ); myIconLabel->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); @@ -567,6 +613,7 @@ SMESHGUI_HypothesisDlg::SMESHGUI_HypothesisDlg( SMESHGUI_GenericHypothesisCreato SMESHGUI_HypothesisDlg::~SMESHGUI_HypothesisDlg() { + delete myCreator; } void SMESHGUI_HypothesisDlg::setCustomFrame( QFrame* f ) @@ -602,8 +649,17 @@ void SMESHGUI_HypothesisDlg::onHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); if (app) { - SMESHGUI* aSMESHGUI = dynamic_cast( app->activeModule() ); - app->onHelpContextModule(aSMESHGUI ? app->moduleName(aSMESHGUI->moduleName()) : QString(""), myHelpFileName); + QString name = "SMESH"; + if(myCreator) { + QVariant pluginName = myCreator->property( PLUGIN_NAME ); + if( pluginName.isValid() ) { + QString rootDir = pluginName.toString() + "PLUGIN_ROOT_DIR"; + QString varValue = QString( getenv(rootDir.toLatin1().constData())); + if(!varValue.isEmpty()) + name = pluginName.toString() + "PLUGIN"; + } + } + app->onHelpContextModule(name, myHelpFileName); } else { QString platform; @@ -613,16 +669,16 @@ void SMESHGUI_HypothesisDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } void SMESHGUI_HypothesisDlg::setHIcon( const QPixmap& p ) { - myIconLabel->setPixmap( p ); + myIconLabel->setPixmap( p ); } void SMESHGUI_HypothesisDlg::setType( const QString& t ) @@ -631,18 +687,18 @@ void SMESHGUI_HypothesisDlg::setType( const QString& t ) } HypothesisData::HypothesisData( const QString& theTypeName, - const QString& thePluginName, - const QString& theServerLibName, - const QString& theClientLibName, - const QString& theLabel, - const QString& theIconId, - const QList& theDim, - const bool theIsAux, - const QStringList& theNeededHypos, - const QStringList& theOptionalHypos, - const QStringList& theInputTypes, - const QStringList& theOutputTypes, - const bool theIsNeedGeometry, + const QString& thePluginName, + const QString& theServerLibName, + const QString& theClientLibName, + const QString& theLabel, + const QString& theIconId, + const QList& theDim, + const bool theIsAux, + const QStringList& theNeededHypos, + const QStringList& theOptionalHypos, + const QStringList& theInputTypes, + const QStringList& theOutputTypes, + const bool theIsNeedGeometry, const bool supportSub) : TypeName( theTypeName ), PluginName( thePluginName ), @@ -652,7 +708,7 @@ HypothesisData::HypothesisData( const QString& theTypeName, IconId( theIconId ), Dim( theDim ), IsAux( theIsAux ), - NeededHypos( theNeededHypos ), + NeededHypos( theNeededHypos ), OptionalHypos( theOptionalHypos ), InputTypes( theInputTypes ), OutputTypes( theOutputTypes ), @@ -661,16 +717,96 @@ HypothesisData::HypothesisData( const QString& theTypeName, { } -HypothesesSet::HypothesesSet( const QString& theSetName ) - : HypoSetName( theSetName ) +HypothesesSet::HypothesesSet( const QString& theSetName ) + : myHypoSetName( theSetName ), + myIsAlgo( false ), + myIsCustom( false ) { } HypothesesSet::HypothesesSet( const QString& theSetName, - const QStringList& theHypoList, - const QStringList& theAlgoList ) - : HypoSetName( theSetName ), - HypoList( theHypoList ), - AlgoList( theAlgoList ) + const QStringList& theHypoList, + const QStringList& theAlgoList ) + : myHypoSetName( theSetName ), + myHypoList( theHypoList ), + myAlgoList( theAlgoList ), + myIsAlgo( false ), + myIsCustom( false ) +{ +} + +QStringList* HypothesesSet::list(bool is_algo) const +{ + return const_cast( &( is_algo ? myAlgoList : myHypoList ) ); +} + +QStringList* HypothesesSet::list() const +{ + return list( myIsAlgo ); +} + +QString HypothesesSet::name() const +{ + return myHypoSetName; +} + +void HypothesesSet::set( bool isAlgo, const QStringList& lst ) +{ + *list(isAlgo) = lst; +} + +int HypothesesSet::count( bool isAlgo ) const +{ + return list(isAlgo)->count(); +} + +bool HypothesesSet::isAlgo() const +{ + return myIsAlgo; +} + +void HypothesesSet::init( bool isAlgo ) { + myIsAlgo = isAlgo; + myIndex = -1; +} + +bool HypothesesSet::more() const +{ + return myIndex < list()->count(); +} + +void HypothesesSet::next() +{ + myIndex++; +} + +QString HypothesesSet::current() const +{ + return list()->at(myIndex); +} + +void HypothesesSet::setIsCustom( bool isCustom ) +{ + myIsCustom = isCustom; +} + +bool HypothesesSet::getIsCustom() const +{ + return myIsCustom; +} + +int HypothesesSet::maxDim() const +{ + HypothesesSet * thisSet = (HypothesesSet*) this; + int dim = -1; + for ( int isAlgo = 0; isAlgo < 2; ++isAlgo ) + { + thisSet->init( isAlgo ); + while ( thisSet->next(), thisSet->more() ) + if ( HypothesisData* hypData = SMESH::GetHypothesisData( thisSet->current() )) + for ( int i = 0; i < hypData->Dim.count(); ++i ) + dim = qMax( dim, hypData->Dim[i] ); + } + return dim; } diff --git a/src/SMESHGUI/SMESHGUI_Hypotheses.h b/src/SMESHGUI/SMESHGUI_Hypotheses.h index 6cf6c524d..c15484b84 100644 --- a/src/SMESHGUI/SMESHGUI_Hypotheses.h +++ b/src/SMESHGUI/SMESHGUI_Hypotheses.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Hypotheses.h // Author : Julia DOROVSKIKH, Open CASCADE S.A.S. @@ -28,6 +29,7 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_HypothesesUtils.h" // Qt includes #include @@ -51,10 +53,9 @@ public: virtual ~SMESHGUI_GenericHypothesisCreator(); void create( SMESH::SMESH_Hypothesis_ptr, - const QString&, QWidget* ); - void create( bool, const QString&, QWidget* ); - void edit( SMESH::SMESH_Hypothesis_ptr, - const QString&, QWidget* ); + const QString&, QWidget*, QObject*, const QString& ); + void create( bool, const QString&, QWidget*, QObject*, const QString& ); + void edit( SMESH::SMESH_Hypothesis_ptr, const QString&, QWidget*, QObject*, const QString& ); void setInitParamsHypothesis(SMESH::SMESH_Hypothesis_ptr); virtual bool checkParams( QString& ) const; @@ -64,6 +65,15 @@ public: QString hypType() const; QString hypName() const; bool isCreation() const; + + QString getShapeEntry() const { return myShapeEntry; } + void setShapeEntry( const QString& theEntry ); + + QString getMainShapeEntry() const { return myMainShapeEntry; } + void setMainShapeEntry( const QString& theEntry ) { myMainShapeEntry = theEntry; } + +signals: + void finished( int ); protected: struct StdParam @@ -71,9 +81,11 @@ protected: QString myName; QVariant myValue; bool isVariable; + QString myText; StdParam(){ isVariable = false; } + const char* text() const { return myText.toLatin1().constData(); } }; typedef QList ListOfStdParams; @@ -85,6 +97,7 @@ protected: const ListOfWidgets& widgets() const; ListOfWidgets& changeWidgets(); QtxDialog* dlg() const; + QString getVariableName(const char* methodName) const; virtual QFrame* buildFrame() = 0; QFrame* buildStdFrame(); @@ -96,7 +109,7 @@ protected: static QString stdParamValues( const ListOfStdParams& ); virtual void attuneStdWidget( QWidget*, const int ) const; virtual QWidget* getCustomWidget( const StdParam&, - QWidget*, const int ) const; + QWidget*, const int ) const; virtual bool getParamFromCustomWidget( StdParam&, QWidget* ) const; virtual void valueChanged( QWidget* ); virtual QString caption() const; @@ -110,8 +123,7 @@ private slots: virtual void onDialogFinished( int ); private: - bool editHypothesis( SMESH::SMESH_Hypothesis_ptr, - const QString&, QWidget* ); + void editHypothesis( SMESH::SMESH_Hypothesis_ptr, const QString&, QWidget*, QObject* obj, const QString& ); private: SMESH::SMESH_Hypothesis_var myHypo, myInitParamsHypo; @@ -120,7 +132,8 @@ private: ListOfWidgets myParamWidgets; bool myIsCreate; QtxDialog* myDlg; - QEventLoop* myEventLoop; + QString myShapeEntry; + QString myMainShapeEntry; }; class SMESHGUI_HypothesisDlg : public QtxDialog @@ -131,20 +144,19 @@ public: SMESHGUI_HypothesisDlg( SMESHGUI_GenericHypothesisCreator*, QWidget* ); virtual ~SMESHGUI_HypothesisDlg(); - void setHIcon( const QPixmap& ); - void setCustomFrame( QFrame* ); - void setType( const QString& ); + void setHIcon( const QPixmap& ); + void setCustomFrame( QFrame* ); + void setType( const QString& ); protected slots: - virtual void accept(); - virtual void reject(); - void onHelp(); + virtual void accept(); + virtual void reject(); + void onHelp(); private: - SMESHGUI_GenericHypothesisCreator* myCreator; - QLabel* myIconLabel; - QLabel* myTypeLabel; - QString myHelpFileName; + SMESHGUI_GenericHypothesisCreator* myCreator; + QLabel *myIconLabel, *myTypeLabel; + QString myHelpFileName; }; /*! @@ -158,7 +170,7 @@ public: const QList&, const bool, const QStringList&, const QStringList&, const QStringList&, const QStringList&, - const bool=true, const bool supportSub=false ); + const bool=true, const bool supportSub=false ); QString TypeName; //!< hypothesis type name QString PluginName; //!< plugin name @@ -189,8 +201,32 @@ public: HypothesesSet( const QString& ); HypothesesSet( const QString&, const QStringList&, const QStringList& ); - QString HypoSetName; - QStringList HypoList, AlgoList; + QString name() const; + void set( bool, const QStringList& ); + int count( bool ) const; + + void setIsCustom( bool ); + bool getIsCustom() const; + int maxDim() const; + + bool isAlgo() const; + + //this method sets internal index to -1, thus before any data access it is necessary to call next() + void init( bool ); + + bool more() const; + void next(); + QString current() const; + +private: + QStringList* list(bool) const; + QStringList* list() const; + +private: + QString myHypoSetName; + QStringList myHypoList, myAlgoList; + bool myIsAlgo, myIsCustom; + int myIndex; }; #endif // SMESHGUI_HYPOTHESES_H diff --git a/src/SMESHGUI/SMESHGUI_HypothesesUtils.cxx b/src/SMESHGUI/SMESHGUI_HypothesesUtils.cxx index 5f3500230..d7e3e6ce6 100644 --- a/src/SMESHGUI/SMESHGUI_HypothesesUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_HypothesesUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_HypothesesUtils.cxx // Author : Julia DOROVSKIKH, Open CASCADE S.A.S. @@ -49,8 +50,10 @@ // Qt includes #include +#include //#include + // Other includes #ifdef WNT #include @@ -78,52 +81,87 @@ static int MYDEBUG = 0; namespace SMESH { - typedef QMap THypothesisDataMap; + typedef IMap THypothesisDataMap; THypothesisDataMap myHypothesesMap; THypothesisDataMap myAlgorithmsMap; - - typedef QMap THypCreatorMap; - THypCreatorMap myHypCreatorMap; + + // BUG 0020378 + //typedef QMap THypCreatorMap; + //THypCreatorMap myHypCreatorMap; QList myListOfHypothesesSets; void processHypothesisStatus(const int theHypStatus, - SMESH::SMESH_Hypothesis_ptr theHyp, - const bool theIsAddition) + SMESH::SMESH_Hypothesis_ptr theHyp, + const bool theIsAddition) { if (theHypStatus > SMESH::HYP_OK) { // get Hyp name QString aHypName ("NULL Hypothesis"); if (!CORBA::is_nil(theHyp)) { - _PTR(SObject) Shyp = SMESH::FindSObject(theHyp); - if (Shyp) - // name in study - aHypName = Shyp->GetName().c_str(); - else - // label in xml file - aHypName = GetHypothesisData(theHyp->GetName())->Label; + _PTR(SObject) Shyp = SMESH::FindSObject(theHyp); + if (Shyp) + // name in study + aHypName = Shyp->GetName().c_str(); + else + // label in xml file + aHypName = GetHypothesisData(theHyp->GetName())->Label; } // message bool isFatal = (theHypStatus >= SMESH::HYP_UNKNOWN_FATAL); QString aMsg; if (theIsAddition) - aMsg = (isFatal ? "SMESH_CANT_ADD_HYP" : "SMESH_ADD_HYP_WRN"); + aMsg = (isFatal ? "SMESH_CANT_ADD_HYP" : "SMESH_ADD_HYP_WRN"); else - aMsg = (isFatal ? "SMESH_CANT_RM_HYP" : "SMESH_RM_HYP_WRN"); + aMsg = (isFatal ? "SMESH_CANT_RM_HYP" : "SMESH_RM_HYP_WRN"); aMsg = QObject::tr(aMsg.toLatin1().data()).arg(aHypName) + - QObject::tr(QString("SMESH_HYP_%1").arg(theHypStatus).toLatin1().data()); + QObject::tr(QString("SMESH_HYP_%1").arg(theHypStatus).toLatin1().data()); if ( theHypStatus == SMESH::HYP_HIDDEN_ALGO ) // PAL18501 aMsg = aMsg.arg( GetHypothesisData(theHyp->GetName())->Dim[0] ); SUIT_MessageBox::warning(SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - aMsg); + QObject::tr("SMESH_WRN_WARNING"), + aMsg); } } + //================================================================================ + /*! + * \brief Prepends dimension and appends '[custom]' to the name of hypothesis set + */ + //================================================================================ + + static QString mangledHypoSetName(HypothesesSet* hypSet) + { + QString name = hypSet->name(); + + // prepend 'xD: ' + int dim = hypSet->maxDim(); + if ( dim > -1 ) + name = QString("%1D: %2").arg(dim).arg(name); + + // custom + if ( hypSet->getIsCustom() ) + name = QString("%1 [custom]").arg(name); + + return name; + } + + //================================================================================ + /*! + * \brief Removes dimension and '[custom]' from the name of hypothesis set + */ + //================================================================================ + + static QString demangledHypoSetName(QString name) + { + name.remove(QRegExp("[0-3]D: ")); + name.remove(" [custom]"); + return name; + } void InitAvailableHypotheses() { @@ -137,72 +175,93 @@ namespace SMESH QString HypsXml; char* cenv = getenv("SMESH_MeshersList"); if (cenv) - HypsXml.sprintf("%s", cenv); + HypsXml.sprintf("%s", cenv); QStringList HypsXmlList = HypsXml.split(":", QString::SkipEmptyParts); if (HypsXmlList.count() == 0) { - SUIT_MessageBox::critical(SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("MESHERS_FILE_NO_VARIABLE")); - return; + SUIT_MessageBox::critical(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("MESHERS_FILE_NO_VARIABLE")); + return; } - - // loop on files in HypsXml - QString aNoAccessFiles; + // get full names of xml files from HypsXmlList + QStringList xmlFiles; + xmlFiles.append( QDir::home().filePath("CustomMeshers.xml")); // may be inexistent for (int i = 0; i < HypsXmlList.count(); i++) { - QString HypsXml = HypsXmlList[ i ]; + QString HypsXml = HypsXmlList[ i ]; - // Find full path to the resource XML file - QString xmlFile = resMgr->path("resources", "SMESH", HypsXml + ".xml"); + // Find full path to the resource XML file + QString xmlFile = resMgr->path("resources", "SMESH", HypsXml + ".xml"); if ( xmlFile.isEmpty() ) // try PLUGIN resources xmlFile = resMgr->path("resources", HypsXml, HypsXml + ".xml"); - - QFile file (xmlFile); - if (file.exists() && file.open(QIODevice::ReadOnly)) { - file.close(); - - SMESHGUI_XmlHandler* aXmlHandler = new SMESHGUI_XmlHandler(); - ASSERT(aXmlHandler); - - QXmlInputSource source (&file); - QXmlSimpleReader reader; - reader.setContentHandler(aXmlHandler); - reader.setErrorHandler(aXmlHandler); - bool ok = reader.parse(source); - file.close(); - if (ok) { - myHypothesesMap.unite( aXmlHandler->myHypothesesMap ); - myAlgorithmsMap.unite( aXmlHandler->myAlgorithmsMap ); - QList::iterator it, pos = myListOfHypothesesSets.begin(); - for ( it = aXmlHandler->myListOfHypothesesSets.begin(); - it != aXmlHandler->myListOfHypothesesSets.end(); - ++it ) { - myListOfHypothesesSets.insert( pos, *it ); - } - } - else { - SUIT_MessageBox::critical(SMESHGUI::desktop(), - QObject::tr("INF_PARSE_ERROR"), - QObject::tr(aXmlHandler->errorProtocol().toLatin1().data())); - } - } - else { - if (aNoAccessFiles.isEmpty()) - aNoAccessFiles = xmlFile; - else - aNoAccessFiles += ", " + xmlFile; - } - } // end loop + if ( !xmlFile.isEmpty() ) + xmlFiles.append( xmlFile ); + } + + // loop on xmlFiles + QString aNoAccessFiles; + for (int i = 0; i < xmlFiles.count(); i++) + { + QString xmlFile = xmlFiles[ i ]; + QFile file (xmlFile); + if (file.exists() && file.open(QIODevice::ReadOnly)) + { + file.close(); + + SMESHGUI_XmlHandler* aXmlHandler = new SMESHGUI_XmlHandler(); + ASSERT(aXmlHandler); + + QXmlInputSource source (&file); + QXmlSimpleReader reader; + reader.setContentHandler(aXmlHandler); + reader.setErrorHandler(aXmlHandler); + bool ok = reader.parse(source); + file.close(); + if (ok) { + + THypothesisDataMap::ConstIterator it1 = aXmlHandler->myHypothesesMap.begin(); + + for( ;it1 != aXmlHandler->myHypothesesMap.end(); it1++) + myHypothesesMap.insert( it1.key(), it1.value() ); + + + it1 = aXmlHandler->myAlgorithmsMap.begin(); + for( ;it1 != aXmlHandler->myAlgorithmsMap.end(); it1++) + myAlgorithmsMap.insert( it1.key(), it1.value() ); + + QList::iterator it, pos = myListOfHypothesesSets.begin(); + for ( it = aXmlHandler->myListOfHypothesesSets.begin(); + it != aXmlHandler->myListOfHypothesesSets.end(); + ++it ) + { + (*it)->setIsCustom( i == 0 ); + myListOfHypothesesSets.insert( pos, *it ); + } + } + else { + SUIT_MessageBox::critical(SMESHGUI::desktop(), + QObject::tr("INF_PARSE_ERROR"), + QObject::tr(aXmlHandler->errorProtocol().toLatin1().data())); + } + delete aXmlHandler; + } + else if ( i > 0 ) { // 1st is ~/CustomMeshers.xml + if (aNoAccessFiles.isEmpty()) + aNoAccessFiles = xmlFile; + else + aNoAccessFiles += ", " + xmlFile; + } + } // end loop on xmlFiles if (!aNoAccessFiles.isEmpty()) { - QString aMess = QObject::tr("MESHERS_FILE_CANT_OPEN") + " " + aNoAccessFiles + "\n"; - aMess += QObject::tr("MESHERS_FILE_CHECK_VARIABLE"); - wc.suspend(); - SUIT_MessageBox::warning(SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - aMess); - wc.resume(); + QString aMess = QObject::tr("MESHERS_FILE_CANT_OPEN") + " " + aNoAccessFiles + "\n"; + aMess += QObject::tr("MESHERS_FILE_CHECK_VARIABLE"); + wc.suspend(); + SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + aMess); + wc.resume(); } } } @@ -220,24 +279,25 @@ namespace SMESH bool checkGeometry = ( !isNeedGeometry && isAlgo ); // fill list of hypotheses/algorithms THypothesisDataMap& pMap = isAlgo ? myAlgorithmsMap : myHypothesesMap; - THypothesisDataMap::iterator anIter; + THypothesisDataMap::ConstIterator anIter; for ( anIter = pMap.begin(); anIter != pMap.end(); anIter++ ) { HypothesisData* aData = anIter.value(); + if(!aData || aData->Label.isEmpty()) continue; if ( ( theDim < 0 || aData->Dim.contains( theDim ) ) && aData->IsAux == isAux) { - if (checkGeometry) { - if (aData->IsNeedGeometry == isNeedGeometry) - aHypList.append(anIter.key()); - } - else { - aHypList.append(anIter.key()); - } + if (checkGeometry) { + if (aData->IsNeedGeometry == isNeedGeometry) + aHypList.append(anIter.key()); + } + else { + aHypList.append(anIter.key()); + } } } return aHypList; } - QStringList GetHypothesesSets() + QStringList GetHypothesesSets(int maxDim) { QStringList aSetNameList; @@ -246,26 +306,36 @@ namespace SMESH QList::iterator hypoSet; for ( hypoSet = myListOfHypothesesSets.begin(); - hypoSet != myListOfHypothesesSets.end(); - ++hypoSet ) { + hypoSet != myListOfHypothesesSets.end(); + ++hypoSet ) { HypothesesSet* aSet = *hypoSet; - if ( aSet && aSet->AlgoList.count() ) { - aSetNameList.append( aSet->HypoSetName ); + if ( aSet && + ( aSet->count( true ) || aSet->count( false )) && + aSet->maxDim() <= maxDim) + { + aSetNameList.append( mangledHypoSetName( aSet )); } } + aSetNameList.sort(); + + // reverse order of aSetNameList + QStringList reversedNames; + for ( int i = 0; i < aSetNameList.count(); ++i ) + reversedNames.prepend( aSetNameList[i] ); - return aSetNameList; + return reversedNames; } HypothesesSet* GetHypothesesSet(const QString& theSetName) { + QString name = demangledHypoSetName( theSetName ); QList::iterator hypoSet; for ( hypoSet = myListOfHypothesesSets.begin(); - hypoSet != myListOfHypothesesSets.end(); - ++hypoSet ) { + hypoSet != myListOfHypothesesSets.end(); + ++hypoSet ) { HypothesesSet* aSet = *hypoSet; - if ( aSet && aSet->HypoSetName == theSetName ) - return aSet; + if ( aSet && aSet->name() == name ) + return aSet; } return 0; } @@ -277,10 +347,10 @@ namespace SMESH // Init list of available hypotheses, if needed InitAvailableHypotheses(); - if (myHypothesesMap.find(aHypType) != myHypothesesMap.end()) { + if (myHypothesesMap.contains(aHypType)) { aHypData = myHypothesesMap[aHypType]; } - else if (myAlgorithmsMap.find(aHypType) != myAlgorithmsMap.end()) { + else if (myAlgorithmsMap.contains(aHypType)) { aHypData = myAlgorithmsMap[aHypType]; } return aHypData; @@ -326,10 +396,12 @@ namespace SMESH SMESHGUI_GenericHypothesisCreator* aCreator = 0; // check, if creator for this hypothesis type already exists - if (myHypCreatorMap.find(aHypType) != myHypCreatorMap.end()) { - aCreator = myHypCreatorMap[aHypType]; - } - else { + // BUG 0020378 + //if (myHypCreatorMap.find(aHypType) != myHypCreatorMap.end()) { + // aCreator = myHypCreatorMap[aHypType]; + //} + //else + { // 1. Init list of available hypotheses, if needed InitAvailableHypotheses(); @@ -337,52 +409,60 @@ namespace SMESH HypothesisData* aHypData = GetHypothesisData(aHypType); if (!aHypData) return aCreator; + QString aClientLibName = aHypData->ClientLibName; QString aServerLibName = aHypData->ServerLibName; // 3. Load Client Plugin Library try { - // load plugin library - if(MYDEBUG) MESSAGE("Loading client meshers plugin library ..."); - LibHandle libHandle = LoadLib( aClientLibName.toLatin1().data() ); - if (!libHandle) { - // report any error, if occured - if ( MYDEBUG ) { + // load plugin library + if(MYDEBUG) MESSAGE("Loading client meshers plugin library ..."); + LibHandle libHandle = LoadLib( aClientLibName.toLatin1().data() ); + if (!libHandle) { + // report any error, if occured + if ( MYDEBUG ) { #ifdef WIN32 - const char* anError = "Can't load client meshers plugin library"; + const char* anError = "Can't load client meshers plugin library"; #else - const char* anError = dlerror(); + const char* anError = dlerror(); #endif - MESSAGE(anError); - } - } - else { - // get method, returning hypothesis creator - if(MYDEBUG) MESSAGE("Find GetHypothesisCreator() method ..."); - typedef SMESHGUI_GenericHypothesisCreator* (*GetHypothesisCreator) \ - ( const QString& ); - GetHypothesisCreator procHandle = - (GetHypothesisCreator)GetProc(libHandle, "GetHypothesisCreator"); - if (!procHandle) { - if(MYDEBUG) MESSAGE("bad hypothesis client plugin library"); - UnLoadLib(libHandle); - } - else { - // get hypothesis creator - if(MYDEBUG) MESSAGE("Get Hypothesis Creator for " << aHypType.toLatin1().data()); - aCreator = procHandle( aHypType ); - if (!aCreator) { - if(MYDEBUG) MESSAGE("no such a hypothesis in this plugin"); - } - else { - // map hypothesis creator to a hypothesis name - myHypCreatorMap[aHypType] = aCreator; - } - } - } + MESSAGE(anError); + } + } + else { + // get method, returning hypothesis creator + if(MYDEBUG) MESSAGE("Find GetHypothesisCreator() method ..."); + typedef SMESHGUI_GenericHypothesisCreator* (*GetHypothesisCreator) \ + ( const QString& ); + GetHypothesisCreator procHandle = + (GetHypothesisCreator)GetProc(libHandle, "GetHypothesisCreator"); + if (!procHandle) { + if(MYDEBUG) MESSAGE("bad hypothesis client plugin library"); + UnLoadLib(libHandle); + } + else { + // get hypothesis creator + if(MYDEBUG) MESSAGE("Get Hypothesis Creator for " << aHypType.toLatin1().data()); + aCreator = procHandle( aHypType ); + if (!aCreator) { + if(MYDEBUG) MESSAGE("no such a hypothesis in this plugin"); + } + else { + // map hypothesis creator to a hypothesis name + // BUG 0020378 + //myHypCreatorMap[aHypType] = aCreator; + + //rnv : This dynamic property of the QObject stores the name of the plugin. + // It is used to obtain plugin root dir environment variable + // in the SMESHGUI_HypothesisDlg class. Plugin root dir environment + // variable is used to display documentation. + aCreator->setProperty(PLUGIN_NAME,aHypData->PluginName); + } + } + } } catch (const SALOME::SALOME_Exception& S_ex) { - SalomeApp_Tools::QtCatchCorbaException(S_ex); + SalomeApp_Tools::QtCatchCorbaException(S_ex); } } @@ -391,25 +471,26 @@ namespace SMESH SMESH::SMESH_Hypothesis_ptr CreateHypothesis(const QString& aHypType, - const QString& aHypName, - const bool isAlgo) + const QString& aHypName, + const bool isAlgo) { if(MYDEBUG) MESSAGE("Create " << aHypType.toLatin1().data() << - " with name " << aHypName.toLatin1().data()); + " with name " << aHypName.toLatin1().data()); HypothesisData* aHypData = GetHypothesisData(aHypType); QString aServLib = aHypData->ServerLibName; try { SMESH::SMESH_Hypothesis_var aHypothesis; aHypothesis = SMESHGUI::GetSMESHGen()->CreateHypothesis(aHypType.toLatin1().data(), - aServLib.toLatin1().data()); + aServLib.toLatin1().data()); if (!aHypothesis->_is_nil()) { - _PTR(SObject) aHypSObject = SMESH::FindSObject(aHypothesis.in()); - if (aHypSObject) { - if (!aHypName.isEmpty()) - SMESH::SetName(aHypSObject, aHypName); - SMESHGUI::GetSMESHGUI()->updateObjBrowser(); - return aHypothesis._retn(); - } + _PTR(SObject) aHypSObject = SMESH::FindSObject(aHypothesis.in()); + if (aHypSObject) { + if (!aHypName.isEmpty()) + SMESH::SetName(aHypSObject, aHypName); + SMESHGUI::Modified(); + SMESHGUI::GetSMESHGUI()->updateObjBrowser(); + return aHypothesis._retn(); + } } } catch (const SALOME::SALOME_Exception & S_ex) { SalomeApp_Tools::QtCatchCorbaException(S_ex); @@ -429,23 +510,23 @@ namespace SMESH _PTR(SObject) SM = SMESH::FindSObject(aMesh); GEOM::GEOM_Object_var aShapeObject = SMESH::GetShapeOnMeshOrSubMesh(SM); try { - res = aMesh->AddHypothesis(aShapeObject, aHyp); - if (res < SMESH::HYP_UNKNOWN_FATAL) { - _PTR(SObject) aSH = SMESH::FindSObject(aHyp); - if (SM && aSH) { - SMESH::ModifiedMesh(SM, false, aMesh->NbNodes()==0); - } - } - if (res > SMESH::HYP_OK) { - wc.suspend(); - processHypothesisStatus(res, aHyp, true); - wc.resume(); - } + res = aMesh->AddHypothesis(aShapeObject, aHyp); + if (res < SMESH::HYP_UNKNOWN_FATAL) { + _PTR(SObject) aSH = SMESH::FindSObject(aHyp); + if (SM && aSH) { + SMESH::ModifiedMesh(SM, false, aMesh->NbNodes()==0); + } + } + if (res > SMESH::HYP_OK) { + wc.suspend(); + processHypothesisStatus(res, aHyp, true); + wc.resume(); + } } catch(const SALOME::SALOME_Exception& S_ex) { - wc.suspend(); - SalomeApp_Tools::QtCatchCorbaException(S_ex); - res = SMESH::HYP_UNKNOWN_FATAL; + wc.suspend(); + SalomeApp_Tools::QtCatchCorbaException(S_ex); + res = SMESH::HYP_UNKNOWN_FATAL; } } return res < SMESH::HYP_UNKNOWN_FATAL; @@ -460,33 +541,33 @@ namespace SMESH if (!aSubMesh->_is_nil() && ! aHyp->_is_nil()) { try { - SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); - _PTR(SObject) SsubM = SMESH::FindSObject(aSubMesh); - GEOM::GEOM_Object_var aShapeObject = SMESH::GetShapeOnMeshOrSubMesh(SsubM); - if (!aMesh->_is_nil() && SsubM && !aShapeObject->_is_nil()) { - res = aMesh->AddHypothesis(aShapeObject, aHyp); - if (res < SMESH::HYP_UNKNOWN_FATAL) { + SMESH::SMESH_Mesh_var aMesh = aSubMesh->GetFather(); + _PTR(SObject) SsubM = SMESH::FindSObject(aSubMesh); + GEOM::GEOM_Object_var aShapeObject = SMESH::GetShapeOnMeshOrSubMesh(SsubM); + if (!aMesh->_is_nil() && SsubM && !aShapeObject->_is_nil()) { + res = aMesh->AddHypothesis(aShapeObject, aHyp); + if (res < SMESH::HYP_UNKNOWN_FATAL) { _PTR(SObject) meshSO = SMESH::FindSObject(aMesh); if (meshSO) SMESH::ModifiedMesh(meshSO, false, aMesh->NbNodes()==0); - } - if (res > SMESH::HYP_OK) { - wc.suspend(); - processHypothesisStatus(res, aHyp, true); - wc.resume(); - } - } - else { - SCRUTE(aHyp->_is_nil()); - SCRUTE(aMesh->_is_nil()); - SCRUTE(!SsubM); - SCRUTE(aShapeObject->_is_nil()); - } + } + if (res > SMESH::HYP_OK) { + wc.suspend(); + processHypothesisStatus(res, aHyp, true); + wc.resume(); + } + } + else { + SCRUTE(aHyp->_is_nil()); + SCRUTE(aMesh->_is_nil()); + SCRUTE(!SsubM); + SCRUTE(aShapeObject->_is_nil()); + } } catch(const SALOME::SALOME_Exception& S_ex) { - wc.suspend(); - SalomeApp_Tools::QtCatchCorbaException(S_ex); - res = SMESH::HYP_UNKNOWN_FATAL; + wc.suspend(); + SalomeApp_Tools::QtCatchCorbaException(S_ex); + res = SMESH::HYP_UNKNOWN_FATAL; } } else { @@ -505,28 +586,28 @@ namespace SMESH _PTR(Study) aStudy = GetActiveStudyDocument(); _PTR(SObject) aHypObj = aStudy->FindObjectID( IObject->getEntry() ); if( aHypObj ) - { - _PTR(SObject) MorSM = SMESH::GetMeshOrSubmesh( aHypObj ); - _PTR(SObject) aRealHypo; - if( aHypObj->ReferencedObject( aRealHypo ) ) - { - SMESH_Hypothesis_var hypo = SMESH_Hypothesis::_narrow( SObjectToObject( aRealHypo ) ); - RemoveHypothesisOrAlgorithmOnMesh( MorSM, hypo ); - } - else - { - SMESH_Hypothesis_var hypo = SMESH_Hypothesis::_narrow( SObjectToObject( aHypObj ) ); - SObjectList meshList = GetMeshesUsingAlgoOrHypothesis( hypo ); - for( int i = 0; i < meshList.size(); i++ ) - RemoveHypothesisOrAlgorithmOnMesh( meshList[ i ], hypo ); - } - } + { + _PTR(SObject) MorSM = SMESH::GetMeshOrSubmesh( aHypObj ); + _PTR(SObject) aRealHypo; + if( aHypObj->ReferencedObject( aRealHypo ) ) + { + SMESH_Hypothesis_var hypo = SMESH_Hypothesis::_narrow( SObjectToObject( aRealHypo ) ); + RemoveHypothesisOrAlgorithmOnMesh( MorSM, hypo ); + } + else + { + SMESH_Hypothesis_var hypo = SMESH_Hypothesis::_narrow( SObjectToObject( aHypObj ) ); + SObjectList meshList = GetMeshesUsingAlgoOrHypothesis( hypo ); + for( int i = 0; i < meshList.size(); i++ ) + RemoveHypothesisOrAlgorithmOnMesh( meshList[ i ], hypo ); + } + } } catch(const SALOME::SALOME_Exception& S_ex) { - wc.suspend(); - SalomeApp_Tools::QtCatchCorbaException(S_ex); - res = SMESH::HYP_UNKNOWN_FATAL; + wc.suspend(); + SalomeApp_Tools::QtCatchCorbaException(S_ex); + res = SMESH::HYP_UNKNOWN_FATAL; } return res < SMESH::HYP_UNKNOWN_FATAL; } @@ -560,7 +641,7 @@ namespace SMESH } else if(!aMesh->HasShapeToMesh()){ res = aMesh->RemoveHypothesis(aShapeObject, anHyp); - if (res < SMESH::HYP_UNKNOWN_FATAL) { + if (res < SMESH::HYP_UNKNOWN_FATAL) { _PTR(SObject) meshSO = SMESH::FindSObject(aMesh); if (meshSO) SMESH::ModifiedMesh(meshSO, false, aMesh->NbNodes()==0); @@ -573,9 +654,9 @@ namespace SMESH } } } catch(const SALOME::SALOME_Exception& S_ex) { - wc.suspend(); - SalomeApp_Tools::QtCatchCorbaException(S_ex); - res = SMESH::HYP_UNKNOWN_FATAL; + wc.suspend(); + SalomeApp_Tools::QtCatchCorbaException(S_ex); + res = SMESH::HYP_UNKNOWN_FATAL; } } return res < SMESH::HYP_UNKNOWN_FATAL; @@ -590,25 +671,25 @@ namespace SMESH if (!AlgoOrHyp->_is_nil()) { _PTR(SObject) SO_Hypothesis = SMESH::FindSObject(AlgoOrHyp); if (SO_Hypothesis) { - SObjectList listSO = - SMESHGUI::activeStudy()->studyDS()->FindDependances(SO_Hypothesis); - - if(MYDEBUG) MESSAGE("SMESHGUI::GetMeshesUsingAlgoOrHypothesis(): dependency number ="<GetFather(); - if (aFather) { - _PTR(SObject) SOfatherFather = aFather->GetFather(); - if (SOfatherFather) { - if(MYDEBUG) MESSAGE("SMESHGUI::GetMeshesUsingAlgoOrHypothesis(): dependency added to list"); - index++; - listSOmesh.resize(index); - listSOmesh[index - 1] = SOfatherFather; - } - } - } - } + SObjectList listSO = + SMESHGUI::activeStudy()->studyDS()->FindDependances(SO_Hypothesis); + + if(MYDEBUG) MESSAGE("SMESHGUI::GetMeshesUsingAlgoOrHypothesis(): dependency number ="<GetFather(); + if (aFather) { + _PTR(SObject) SOfatherFather = aFather->GetFather(); + if (SOfatherFather) { + if(MYDEBUG) MESSAGE("SMESHGUI::GetMeshesUsingAlgoOrHypothesis(): dependency added to list"); + index++; + listSOmesh.resize(index); + listSOmesh[index - 1] = SOfatherFather; + } + } + } + } } } if (MYDEBUG) MESSAGE("SMESHGUI::GetMeshesUsingAlgoOrHypothesis(): completed"); diff --git a/src/SMESHGUI/SMESHGUI_HypothesesUtils.h b/src/SMESHGUI/SMESHGUI_HypothesesUtils.h index 18813f4c8..c1e485313 100644 --- a/src/SMESHGUI/SMESHGUI_HypothesesUtils.h +++ b/src/SMESHGUI/SMESHGUI_HypothesesUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_HypothesesUtils.h // Author : Julia DOROVSKIKH, Open CASCADE S.A.S. @@ -47,12 +48,18 @@ // STL includes #include +// boost includes +#include + class HypothesisData; class HypothesesSet; class SMESHGUI_GenericHypothesisCreator; class SALOMEDSClient_SObject; class algo_error_array; + +#define PLUGIN_NAME "PLUGIN_NAME" + namespace SMESH { SMESHGUI_EXPORT @@ -62,9 +69,9 @@ namespace SMESH QStringList GetAvailableHypotheses( const bool, const int = -1, const bool = false, - const bool = true); + const bool = true); SMESHGUI_EXPORT - QStringList GetHypothesesSets(); + QStringList GetHypothesesSets( int maxDim ); SMESHGUI_EXPORT HypothesesSet* GetHypothesesSet( const QString& ); @@ -74,20 +81,20 @@ namespace SMESH SMESHGUI_EXPORT bool IsAvailableHypothesis( const HypothesisData*, - const QString&, - bool& ); + const QString&, + bool& ); SMESHGUI_EXPORT bool IsCompatibleAlgorithm( const HypothesisData*, - const HypothesisData* ); + const HypothesisData* ); SMESHGUI_EXPORT SMESHGUI_GenericHypothesisCreator* GetHypothesisCreator( const QString& ); SMESHGUI_EXPORT SMESH::SMESH_Hypothesis_ptr CreateHypothesis( const QString&, - const QString&, - const bool = false); + const QString&, + const bool = false ); SMESHGUI_EXPORT bool AddHypothesisOnMesh( SMESH::SMESH_Mesh_ptr, SMESH::SMESH_Hypothesis_ptr ); @@ -100,7 +107,7 @@ namespace SMESH SMESHGUI_EXPORT bool RemoveHypothesisOrAlgorithmOnMesh( _PTR(SObject), - SMESH::SMESH_Hypothesis_ptr ); + SMESH::SMESH_Hypothesis_ptr ); typedef std::vector<_PTR(SObject)> SObjectList; SObjectList GetMeshesUsingAlgoOrHypothesis( SMESH::SMESH_Hypothesis_ptr ); diff --git a/src/SMESHGUI/SMESHGUI_IdValidator.h b/src/SMESHGUI/SMESHGUI_IdValidator.h index 66dcd6322..b6b06db0b 100644 --- a/src/SMESHGUI/SMESHGUI_IdValidator.h +++ b/src/SMESHGUI/SMESHGUI_IdValidator.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_IdValidator.h // Author : Edward AGAPOV, Open CASCADE S.A.S. @@ -47,15 +48,15 @@ public: // truncate extra ids int ind = 0, nbId = 0; while ( ind < input.length() ) { - if ( input.at( ind ) != ' ' ) { - if ( ++nbId > myMaxNbId ) { - input.truncate( ind ); - break; - } - ind = input.indexOf( ' ', ind ); - if ( ind < 0 ) break; - } - ind++; + if ( input.at( ind ) != ' ' ) { + if ( ++nbId > myMaxNbId ) { + input.truncate( ind ); + break; + } + ind = input.indexOf( ' ', ind ); + if ( ind < 0 ) break; + } + ind++; } } if ( pos > input.length() ) diff --git a/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.cxx b/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.cxx new file mode 100644 index 000000000..8cdc5b0ec --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.cxx @@ -0,0 +1,529 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_Make2DFrom3D.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#include "SMESHGUI_Make2DFrom3DOp.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESH_TypeFilter.hxx" +#include "SMESH_LogicalFilter.hxx" +#include "SMESHGUI_VTKUtils.h" +#include "SMESH_Actor.h" + +// SALOME GUI includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SPACING 6 +#define MARGIN 11 + +/*! + \class SMESHGUI_Make2DFrom3DDlg + \brief Copy Mesh dialog box +*/ + +SMESHGUI_Make2DFrom3DDlg::SMESHGUI_Make2DFrom3DDlg( QWidget* parent ) + : SMESHGUI_Dialog( parent, false, true, OK | Apply | Close | Help ) +{ + // title + setWindowTitle( tr("CAPTION") ); + + // mode + QGroupBox* aModeGrp = new QGroupBox( tr( "MODE" ), mainFrame() ); + QHBoxLayout* aModeGrpLayout = new QHBoxLayout( aModeGrp ); + aModeGrpLayout->setMargin( MARGIN ); + aModeGrpLayout->setSpacing( SPACING ); + my2dFrom3dRB = new QRadioButton( tr( "2D_FROM_3D" ), aModeGrp ); + my1dFrom2dRB = new QRadioButton( tr( "1D_FROM_2D" ), aModeGrp ); + //my1dFrom3dRB = new QRadioButton( tr( "1D_FROM_3D" ), aModeGrp ); + aModeGrpLayout->addWidget( my2dFrom3dRB ); + aModeGrpLayout->addWidget( my1dFrom2dRB ); + //aModeGrpLayout->addWidget( my1dFrom3dRB ); + +// // Groups of mesh faces +// setObjectPixmap( "SMESH", tr( "ICON_SELECT" ) ); +// createObject( tr( "Groups" ), mainFrame(), Groups ); +// setNameIndication( Groups, ListOfNames ); +// objectWg( Groups, Btn )->hide(); + + // Mesh or Groups + //setObjectPixmap( "SMESH", tr( "ICON_SELECT" ) ); + createObject( tr( "Groups" ), mainFrame(), MeshOrGroups ); + setNameIndication( MeshOrGroups, ListOfNames ); + objectWg( MeshOrGroups, Btn )->hide(); + + // target + QGroupBox* aTargetGrp = new QGroupBox( tr( "TARGET" ), mainFrame() ); + QGridLayout* aTargetGrpLayout = new QGridLayout( aTargetGrp ); + aTargetGrpLayout->setMargin( MARGIN ); + aTargetGrpLayout->setSpacing( SPACING ); + myThisMeshRB = new QRadioButton( tr( "THIS_MESH" ), aTargetGrp ); + myNewMeshRB = new QRadioButton( tr( "NEW_MESH" ), aTargetGrp ); + myMeshName = new QLineEdit( aTargetGrp ); + myCopyCheck = new QCheckBox( tr( "COPY_SRC" ), aTargetGrp ); + aTargetGrpLayout->addWidget( myThisMeshRB, 0, 0 ); + aTargetGrpLayout->addWidget( myNewMeshRB, 1, 0 ); + aTargetGrpLayout->addWidget( myMeshName, 1, 1 ); + aTargetGrpLayout->addWidget( myCopyCheck, 2, 0 ); + myGroupCheck = new QCheckBox( tr( "CREATE_GROUP" ), mainFrame() ); + myGroupName = new QLineEdit( mainFrame() ); + + // layout + QGridLayout* aDlgLay = new QGridLayout( mainFrame() ); + aDlgLay->setMargin( 0 ); + aDlgLay->setSpacing( SPACING ); + aDlgLay->addWidget( aModeGrp, 0, 0, 1, 3 ); + aDlgLay->addWidget( objectWg( MeshOrGroups, Label ), 1, 0 ); + aDlgLay->addWidget( objectWg( MeshOrGroups, Control ), 1, 1 ); + aDlgLay->addWidget( aTargetGrp, 2, 0, 1, 3 ); + aDlgLay->addWidget( myGroupCheck, 3, 0 ); + aDlgLay->addWidget( myGroupName, 3, 1, 1, 2 ); + + // connect signals + connect( myThisMeshRB, SIGNAL( clicked() ), this, SLOT( onTargetChanged() ) ); + connect( myNewMeshRB, SIGNAL( clicked() ), this, SLOT( onTargetChanged() ) ); + connect( myGroupCheck, SIGNAL( clicked() ), this, SLOT( onGroupChecked() ) ); + + // init dlg + my2dFrom3dRB->setChecked( true ); + myThisMeshRB->setChecked( true ); + onTargetChanged(); + onGroupChecked(); +} + +SMESHGUI_Make2DFrom3DDlg::~SMESHGUI_Make2DFrom3DDlg() +{ +} + +SMESH::Bnd_Dimension SMESHGUI_Make2DFrom3DDlg::mode() const +{ + if ( my2dFrom3dRB->isChecked() ) + return SMESH::BND_2DFROM3D; + else if ( my1dFrom2dRB->isChecked() ) + return SMESH::BND_1DFROM2D; + else + return SMESH::BND_1DFROM3D; +} + +bool SMESHGUI_Make2DFrom3DDlg::needNewMesh() const +{ + return myNewMeshRB->isChecked(); +} + +QString SMESHGUI_Make2DFrom3DDlg::getNewMeshName() const +{ + return myMeshName->text().trimmed(); +} + +void SMESHGUI_Make2DFrom3DDlg::setNewMeshName( const QString& name ) +{ + myMeshName->setText( name ); +} + +void SMESHGUI_Make2DFrom3DDlg::setNewMeshEnabled( bool enable ) +{ + if ( !enable ) + myThisMeshRB->setChecked( true ); + + myNewMeshRB->setEnabled( enable ); + + onTargetChanged(); +} + +bool SMESHGUI_Make2DFrom3DDlg::getNewMeshEnabled() const +{ + return myNewMeshRB->isEnabled(); +} + +bool SMESHGUI_Make2DFrom3DDlg::needGroup() const +{ + return myGroupCheck->isChecked(); +} + +QString SMESHGUI_Make2DFrom3DDlg::getGroupName() const +{ + return myGroupName->text().trimmed(); +} + +void SMESHGUI_Make2DFrom3DDlg::setGroupName( const QString& name ) +{ + myGroupName->setText( name ); +} + +bool SMESHGUI_Make2DFrom3DDlg::copySource() const +{ + return myCopyCheck->isChecked(); +} + +void SMESHGUI_Make2DFrom3DDlg::onTargetChanged() +{ + myMeshName->setEnabled( myNewMeshRB->isChecked() ); + myCopyCheck->setEnabled( myNewMeshRB->isChecked() ); +} + +void SMESHGUI_Make2DFrom3DDlg::onGroupChecked() +{ + myGroupName->setEnabled( myGroupCheck->isChecked() ); +} + +/*! + \class SMESHGUI_Make2DFrom3DOp + \brief Copy Mesh operation class +*/ + +SMESHGUI_Make2DFrom3DOp::SMESHGUI_Make2DFrom3DOp() + : SMESHGUI_SelectionOp(), + myMeshFilter(MESH), + myGroupFilter(GROUP) +{ +} + +SMESHGUI_Make2DFrom3DOp::~SMESHGUI_Make2DFrom3DOp() +{ + if ( myDlg ) + delete myDlg; +} + +LightApp_Dialog* SMESHGUI_Make2DFrom3DOp::dlg() const +{ + return myDlg; +} + +void SMESHGUI_Make2DFrom3DOp::startOperation() +{ + if( !myDlg ) + myDlg = new SMESHGUI_Make2DFrom3DDlg( desktop() ); + + myHelpFileName = "make_2dmesh_from_3d_page.html"; + + SMESHGUI_SelectionOp::startOperation(); + + myDlg->setNewMeshName( SMESH::UniqueName( "Mesh_1" ) ); + myDlg->setGroupName( SMESH::UniqueName( "Group" ) ); + myDlg->show(); + + connect( myDlg->my2dFrom3dRB, SIGNAL( toggled(bool) ), this, SLOT( onModeChanged() ) ); + connect( myDlg->my1dFrom2dRB, SIGNAL( toggled(bool) ), this, SLOT( onModeChanged() ) ); + //connect( myDlg->my1dFrom3dRB, SIGNAL( toggled(bool) ), this, SLOT( onModeChanged() ) ); + + //onModeChanged(); + + myDlg->activateObject( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups ); + selectionDone(); +} + +//================================================================================ +/*! + * \brief Set filter corresponding to dimension + */ +//================================================================================ + +void SMESHGUI_Make2DFrom3DOp::onModeChanged() +{ +// QRadioButton* b = dynamic_cast( sender()); +// if ( b && !b->isChecked() ) +// return; + +// // enable "2D groups" field +// bool enableGroups = ( myDlg->mode() == SMESH::BND_1DFROM3D ); +// myDlg->setObjectEnabled( SMESHGUI_Make2DFrom3DDlg::Groups, enableGroups ); +// ((QToolButton*) myDlg->objectWg( SMESHGUI_Make2DFrom3DDlg::Groups, +// SMESHGUI_Make2DFrom3DDlg::Btn ))->setChecked( enableGroups ); + +// // install filter +// int id = enableGroups ? SMESHGUI_Make2DFrom3DDlg::Groups : SMESHGUI_Make2DFrom3DDlg::Mesh; +// onDeactivateObject( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups ); +// onActivateObject ( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups ); +// selectionDone(); +} + +void SMESHGUI_Make2DFrom3DOp::selectionDone() +{ + myDlg->clearSelection( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups ); + mySrcMesh = SMESH::SMESH_Mesh::_nil(); + + if ( !dlg() ) return; + + + if ( dlg()->isVisible() ) { + try { + QStringList names, ids; + LightApp_Dialog::TypesList types; + selected( names, types, ids ); + for ( int i = 0; i < names.count(); ++i ) + names[i] = names[i].trimmed(); + myDlg->selectObject( names, types, ids ); + + // enable/desable "new mesh" button + bool isMesh = true; + for ( int i = 0; i < ids.count() && isMesh; ++i ) + { + _PTR(SObject) sobj = + SMESHGUI::activeStudy()->studyDS()->FindObjectID( ids[i].toLatin1().constData() ); + mySrcMesh = SMESH::SObjectToInterface( sobj ); + isMesh = !mySrcMesh->_is_nil(); + } + myDlg->setNewMeshEnabled( isMesh ); + } + catch ( const SALOME::SALOME_Exception& S_ex ) { + SalomeApp_Tools::QtCatchCorbaException( S_ex ); + } + catch ( ... ) { + } + } +} + +SUIT_SelectionFilter* SMESHGUI_Make2DFrom3DOp::createFilter( const int theId ) const +{ + SMESHGUI_Make2DFrom3DOp* me = (SMESHGUI_Make2DFrom3DOp*) this; + + QList subFilters; + subFilters.append( & me->myMeshFilter ); + subFilters.append( & me->myGroupFilter ); + + SUIT_SelectionFilter* f = new SMESH_LogicalFilter( subFilters, SMESH_LogicalFilter::LO_OR ); + return f; +} + +bool SMESHGUI_Make2DFrom3DOp::isValid( QString& msg ) const +{ + if ( !dlg() ) return false; + + // check if a mesh is selected + if ( !myDlg->hasSelection( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups )) + { + msg = tr( "SMESH_ERR_NO_INPUT_MESH" ); + return false; + } + QStringList entries; + dlg()->selectedObject( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups, entries ); + const bool isMeshSelected = ( !mySrcMesh->_is_nil() ); + if ( isMeshSelected ) + { + // only one mesh is allowed + if ( entries.size() > 1 ) { + msg = tr( "SMESH_TOO_MANY_MESHES" ); + return false; + } + } + else + { + // check if only groups are selected + for ( int i = 0; i < entries.count(); ++i ) + { + SMESH::SMESH_GroupBase_var grp; + if ( _PTR(SObject) sobj = SMESHGUI::activeStudy()->studyDS()->FindObjectID( entries[i].toLatin1().constData() )) + grp = SMESH::SObjectToInterface( sobj ); + if ( grp->_is_nil() ) { + msg = tr( "SMESH_NOT_ONLY_GROUPS" ); + return false; + } + } + } + // check if the selected objects contains elements of required type + bool hasFaces = false, hasVolumes = false; + SMESH::Bnd_Dimension mode = myDlg->mode(); + for ( int i = 0; i < entries.count(); ++i ) + { + SMESH::SMESH_IDSource_var idSource; + if ( _PTR(SObject) sobj = SMESHGUI::activeStudy()->studyDS()->FindObjectID( entries[i].toLatin1().constData() )) + idSource = SMESH::SObjectToInterface( sobj ); + if ( !idSource->_is_nil() ) { + SMESH::array_of_ElementType_var types = idSource->GetTypes(); + for ( int j = 0; j < types->length(); ++j ) + if ( types[j] == SMESH::VOLUME ) + hasVolumes = true; + else if ( types[j] == SMESH::FACE ) + hasFaces = true; + } + } + if ( mode == SMESH::BND_2DFROM3D && !hasVolumes ) { + msg = tr( "SMESH_ERR_NO_3D_ELEMENTS" ); + return false; + } + else if ( mode == SMESH::BND_1DFROM2D && !hasFaces ) { + msg = tr( "SMESH_ERR_NO_2D_ELEMENTS" ); + return false; + } + + // check if new mesh name is specified + if ( myDlg->needNewMesh() && myDlg->getNewMeshName().isEmpty() ) { + msg = tr( "SMESH_ERR_MESH_NAME_NOT_SPECIFIED" ); + return false; + } + + // check if group name is specified + if ( myDlg->needGroup() && myDlg->getGroupName().isEmpty() ) { + msg = tr( "SMESH_ERR_GRP_NAME_NOT_SPECIFIED" ); + return false; + } + + return true; +} + +bool SMESHGUI_Make2DFrom3DOp::compute2DMesh( QStringList& theEntryList ) +{ + SUIT_OverrideCursor wc; + + bool ok = false; + try { + SMESH::Bnd_Dimension mode = myDlg->mode(); + QString meshName = myDlg->needNewMesh() ? myDlg->getNewMeshName() : QString(); + QString groupName = myDlg->needGroup() ? myDlg->getGroupName() : QString(); + bool copyAll = myDlg->copySource(); + + QStringList entries; + dlg()->selectedObject( SMESHGUI_Make2DFrom3DDlg::MeshOrGroups, entries ); + SMESH::ListOfIDSources_var groups = new SMESH::ListOfIDSources; + QString wrongGroups = ""; + + if ( mySrcMesh->_is_nil() ) // get selected groups, find groups of wrong type + { + int nbGroups = 0; + int goodType = ( mode == SMESH::BND_2DFROM3D ? SMESH::VOLUME : SMESH::FACE ); + groups->length( entries.count() ); + for ( int i = 0; i < entries.count(); ++i ) + { + _PTR(SObject) sobj = + SMESHGUI::activeStudy()->studyDS()->FindObjectID( entries[i].toLatin1().constData() ); + SMESH::SMESH_IDSource_var grp = SMESH::SObjectToInterface( sobj ); + SMESH::array_of_ElementType_var types = grp->GetTypes(); + if ( types->length() < 1 || types[0] != goodType ) + { + if ( !wrongGroups.isEmpty() ) + wrongGroups += ", "; + wrongGroups += sobj->GetName().c_str(); + } + else + { + groups[ nbGroups++ ] = grp; + } + } + groups->length( nbGroups ); + mySrcMesh = groups[0]->GetMesh(); + } + + if ( !CORBA::is_nil( mySrcMesh ) ) { + SMESH::SMESH_MeshEditor_var aMeshEditor = mySrcMesh->GetMeshEditor(); + SMESH::SMESH_Group_var newGrp; + SMESH::SMESH_Mesh_var newMesh; + CORBA::Long nbAdded = aMeshEditor->MakeBoundaryElements( mode, + groupName.toLatin1().constData(), + meshName.toLatin1().constData(), + copyAll, + groups, + newMesh.out(), + newGrp.out() ); + QString msg = tr("NB_ADDED").arg( nbAdded ); + if ( !wrongGroups.isEmpty() ) + msg += ".\n" + tr("WRONG_GROUPS").arg( wrongGroups ); + SUIT_MessageBox::information( myDlg, tr("SMESH_INFORMATION"), msg); + + if ( !newMesh->_is_nil() ) { + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( newMesh ) ) + theEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + newMesh->UnRegister(); +#endif + } + if ( !newGrp->_is_nil() ) { +#ifdef WITHGENERICOBJ + newGrp->UnRegister(); +#endif + } + ok = true; + + for ( int i = 0; i < entries.count(); ++i ) + if ( SMESH_Actor* actor = SMESH::FindActorByEntry( entries[i].toLatin1().constData() )) + SMESH::Update(actor->getIO(),actor->GetVisibility()); + SMESH::RepaintCurrentView(); + } + } + catch ( ... ) { + } + return ok; +} + +bool SMESHGUI_Make2DFrom3DOp::onApply() +{ + if ( isStudyLocked() ) + return false; + + QString msg; + if ( !isValid( msg ) ) { + dlg()->show(); + if ( msg != "" ) + SUIT_MessageBox::warning( myDlg, tr( "SMESH_ERROR" ), msg ); + return false; + } + + QStringList anEntryList; + bool res = false; + try { + res = compute2DMesh( anEntryList ); + } + catch ( const SALOME::SALOME_Exception& S_ex ) { + SalomeApp_Tools::QtCatchCorbaException( S_ex ); + } + catch ( ... ) { + } + + if ( res ) { + SMESHGUI::Modified(); + update( UF_ObjBrowser | UF_Model ); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + myDlg->setNewMeshName( SMESH::UniqueName( "Mesh_1" ) ); + myDlg->setGroupName( SMESH::UniqueName( "Group" ) ); + } + else { + SUIT_MessageBox::warning( myDlg, tr( "SMESH_ERROR" ), tr( "SMESH_OPERATION_FAILED" ) ); + } + + return res; +} diff --git a/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.h b/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.h new file mode 100644 index 000000000..79ea60c42 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_Make2DFrom3DOp.h @@ -0,0 +1,121 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_Make2DFrom3D.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#ifndef SMESHGUI_Make2DFrom3DOp_H +#define SMESHGUI_Make2DFrom3DOp_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_Dialog.h" +#include "SMESHGUI_SelectionOp.h" +#include "SMESH_TypeFilter.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QCheckBox; +class QLineEdit; +class QRadioButton; +class SMESHGUI_Make2DFrom3DOp; + +/*! + * \brief Dialog to show result mesh statistic + */ + +class SMESHGUI_EXPORT SMESHGUI_Make2DFrom3DDlg : public SMESHGUI_Dialog +{ + Q_OBJECT + +public: + enum { MeshOrGroups }; + + SMESHGUI_Make2DFrom3DDlg( QWidget* ); + virtual ~SMESHGUI_Make2DFrom3DDlg(); + + SMESH::Bnd_Dimension mode() const; + + bool needNewMesh() const; + QString getNewMeshName() const; + void setNewMeshName( const QString& ); + void setNewMeshEnabled( bool ); + bool getNewMeshEnabled() const; + + bool needGroup() const; + QString getGroupName() const; + void setGroupName( const QString& ); + + bool copySource() const; + +private slots: + void onTargetChanged(); + void onGroupChecked(); + +private: + QRadioButton* my2dFrom3dRB; + QRadioButton* my1dFrom2dRB; + QRadioButton* my1dFrom3dRB; + QRadioButton* myThisMeshRB; + QRadioButton* myNewMeshRB; + QLineEdit* myMeshName; + QCheckBox* myCopyCheck; + QCheckBox* myGroupCheck; + QLineEdit* myGroupName; + + friend class SMESHGUI_Make2DFrom3DOp; +}; + +/*! + * \brief Operation to compute 2D mesh on 3D + */ + +class SMESHGUI_EXPORT SMESHGUI_Make2DFrom3DOp : public SMESHGUI_SelectionOp +{ + Q_OBJECT + +public: + SMESHGUI_Make2DFrom3DOp(); + virtual ~SMESHGUI_Make2DFrom3DOp(); + + virtual LightApp_Dialog* dlg() const; + +protected: + virtual void startOperation(); + virtual void selectionDone(); + virtual SUIT_SelectionFilter* createFilter( const int ) const; + bool isValid( QString& ) const; + +protected slots: + virtual bool onApply(); + void onModeChanged(); + +private: + bool compute2DMesh( QStringList& ); + +private: + SMESH::SMESH_Mesh_var mySrcMesh; + QPointer myDlg; + + SMESH_TypeFilter myMeshFilter; + SMESH_TypeFilter myGroupFilter; +}; + +#endif // SMESHGUI_Make2DFrom3DOp_H diff --git a/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx b/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx index ac654fdd6..090a385cc 100644 --- a/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_MakeNodeAtPointDlg.cxx // Author : Edward AGAPOV, Open CASCADE S.A.S. // SMESH includes @@ -46,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +93,7 @@ SMESHGUI_MakeNodeAtPointDlg::SMESHGUI_MakeNodeAtPointDlg() setWindowTitle(tr("CAPTION")); QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame()); - aDlgLay->setMargin(MARGIN);; + aDlgLay->setMargin(0); aDlgLay->setSpacing(SPACING); QWidget* aMainFrame = createMainFrame (mainFrame()); @@ -115,7 +117,7 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) // constructor - QGroupBox* aPixGrp = new QGroupBox(tr("MESH_PASS_THROUGH_POINT"), aFrame); + QGroupBox* aPixGrp = new QGroupBox(tr("MOVE_NODE"), aFrame); QButtonGroup* aBtnGrp = new QButtonGroup(this); QHBoxLayout* aPixGrpLayout = new QHBoxLayout(aPixGrp); aPixGrpLayout->setMargin(MARGIN); @@ -129,7 +131,7 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) // coordinates - QGroupBox* aCoordGrp = new QGroupBox(tr("SMESH_COORDINATES"), aFrame); + QGroupBox* aCoordGrp = new QGroupBox(tr("DESTINATION"), aFrame); QHBoxLayout* aCoordGrpLayout = new QHBoxLayout(aCoordGrp); aCoordGrpLayout->setMargin(MARGIN); aCoordGrpLayout->setSpacing(SPACING); @@ -147,9 +149,9 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) QLabel* aZLabel = new QLabel(tr("SMESH_Z"), aCoordGrp); myZ = new SMESHGUI_SpinBox(aCoordGrp); - myX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - myY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - myZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); + myX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); aCoordGrpLayout->addWidget(myCoordBtn); aCoordGrpLayout->addWidget(aXLabel); @@ -158,19 +160,9 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) aCoordGrpLayout->addWidget(myY); aCoordGrpLayout->addWidget(aZLabel); aCoordGrpLayout->addWidget(myZ); - - // Method selection - - QGroupBox* aMethodGrp = new QGroupBox(tr("METHOD"), aFrame); - QHBoxLayout* aMethodGrpLayout = new QHBoxLayout(aMethodGrp); - aMethodGrpLayout->setMargin(MARGIN); - aMethodGrpLayout->setSpacing(SPACING); - - myMoveRBtn = new QRadioButton(tr("MOVE_EXISTING_METHOD"), aMethodGrp); - myCreateRBtn = new QRadioButton(tr("CREATE_NEW_METHOD"), aMethodGrp); - - aMethodGrpLayout->addWidget(myMoveRBtn); - aMethodGrpLayout->addWidget(myCreateRBtn); + aCoordGrpLayout->setStretchFactor(myX, 1); + aCoordGrpLayout->setStretchFactor(myY, 1); + aCoordGrpLayout->setStretchFactor(myZ, 1); // node ID @@ -182,6 +174,65 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) myIdBtn->setCheckable(true); myId = new QLineEdit(myNodeToMoveGrp); myId->setValidator(new SMESHGUI_IdValidator(this, 1)); + + QWidget* aCoordWidget = new QWidget(myNodeToMoveGrp); + + QLabel* aCurrentXLabel = new QLabel(tr("SMESH_X"), aCoordWidget); + myCurrentX = new SMESHGUI_SpinBox(aCoordWidget); + myCurrentX->setButtonSymbols(QAbstractSpinBox::NoButtons); + myCurrentX->setReadOnly(true); + + QLabel* aCurrentYLabel = new QLabel(tr("SMESH_Y"), aCoordWidget); + myCurrentY = new SMESHGUI_SpinBox(aCoordWidget); + myCurrentY->setButtonSymbols(QAbstractSpinBox::NoButtons); + myCurrentY->setReadOnly(true); + + QLabel* aCurrentZLabel = new QLabel(tr("SMESH_Z"), aCoordWidget); + myCurrentZ = new SMESHGUI_SpinBox(aCoordWidget); + myCurrentZ->setButtonSymbols(QAbstractSpinBox::NoButtons); + myCurrentZ->setReadOnly(true); + + QLabel* aDXLabel = new QLabel(tr("SMESH_DX"), aCoordWidget); + myDX = new SMESHGUI_SpinBox(aCoordWidget); + myDX->setButtonSymbols(QAbstractSpinBox::NoButtons); + myDX->setReadOnly(true); + + QLabel* aDYLabel = new QLabel(tr("SMESH_DY"), aCoordWidget); + myDY = new SMESHGUI_SpinBox(aCoordWidget); + myDY->setButtonSymbols(QAbstractSpinBox::NoButtons); + myDY->setReadOnly(true); + + QLabel* aDZLabel = new QLabel(tr("SMESH_DZ"), aCoordWidget); + myDZ = new SMESHGUI_SpinBox(aCoordWidget); + myDZ->setButtonSymbols(QAbstractSpinBox::NoButtons); + myDZ->setReadOnly(true); + + myCurrentX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myCurrentY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myCurrentZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myDX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myDY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myDZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + + QGridLayout* aCoordLayout = new QGridLayout(aCoordWidget); + aCoordLayout->setMargin(0); + aCoordLayout->setSpacing(SPACING); + aCoordLayout->addWidget(aCurrentXLabel, 0, 0); + aCoordLayout->addWidget(myCurrentX, 0, 1); + aCoordLayout->addWidget(aCurrentYLabel, 0, 2); + aCoordLayout->addWidget(myCurrentY, 0, 3); + aCoordLayout->addWidget(aCurrentZLabel, 0, 4); + aCoordLayout->addWidget(myCurrentZ, 0, 5); + aCoordLayout->addWidget(aDXLabel, 1, 0); + aCoordLayout->addWidget(myDX, 1, 1); + aCoordLayout->addWidget(aDYLabel, 1, 2); + aCoordLayout->addWidget(myDY, 1, 3); + aCoordLayout->addWidget(aDZLabel, 1, 4); + aCoordLayout->addWidget(myDZ, 1, 5); + aCoordLayout->setColumnStretch(1, 1); + aCoordLayout->setColumnStretch(3, 1); + aCoordLayout->setColumnStretch(5, 1); + myAutoSearchChkBox = new QCheckBox( tr("AUTO_SEARCH"), myNodeToMoveGrp); myPreviewChkBox = new QCheckBox( tr("PREVIEW"), myNodeToMoveGrp); @@ -192,23 +243,20 @@ QWidget* SMESHGUI_MakeNodeAtPointDlg::createMainFrame (QWidget* theParent) myNodeToMoveGrpLayout->addWidget( idLabel, 0, 0 ); myNodeToMoveGrpLayout->addWidget( myIdBtn, 0, 1 ); myNodeToMoveGrpLayout->addWidget( myId, 0, 2 ); - myNodeToMoveGrpLayout->addWidget( myAutoSearchChkBox, 1, 0, 1, 3 ); - myNodeToMoveGrpLayout->addWidget( myPreviewChkBox, 2, 0, 1, 3 ); + myNodeToMoveGrpLayout->addWidget( aCoordWidget, 1, 0, 1, 3 ); + myNodeToMoveGrpLayout->addWidget( myAutoSearchChkBox, 2, 0, 1, 3 ); + myNodeToMoveGrpLayout->addWidget( myPreviewChkBox, 3, 0, 1, 3 ); QVBoxLayout* aLay = new QVBoxLayout(aFrame); aLay->addWidget(aPixGrp); aLay->addWidget(aCoordGrp); - aLay->addWidget(aMethodGrp); aLay->addWidget(myNodeToMoveGrp); connect(myCoordBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool))); - connect(myMoveRBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool))); - connect(myCreateRBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool))); connect(myIdBtn, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool))); connect(myAutoSearchChkBox, SIGNAL (toggled(bool)), this, SLOT(ButtonToggled(bool))); - myMoveRBtn->setChecked(true); - myIdBtn->setDown(true); + myIdBtn->setChecked(true); myAutoSearchChkBox->setChecked(true); return aFrame; @@ -228,30 +276,27 @@ void SMESHGUI_MakeNodeAtPointDlg::ButtonToggled (bool on) if ( aSender == myCoordBtn ) // button to set coord by node selection { if ( myIdBtn->isEnabled() ) - myIdBtn->setDown( !on ); + myIdBtn->setChecked( !on ); } else if ( aSender == myIdBtn ) // button to select a node to move { - myCoordBtn->setDown( !on ); - } - else if ( aSender == myMoveRBtn ) // move node method - { - myNodeToMoveGrp->setEnabled( true ); - } - else if ( aSender == myCreateRBtn ) // create node method - { - myNodeToMoveGrp->setEnabled( false ); - myCoordBtn->setDown( true ); + myCoordBtn->setChecked( !on ); } } if ( aSender == myAutoSearchChkBox ) // automatic node search { if ( on ) { + myCurrentX->SetValue(0); + myCurrentY->SetValue(0); + myCurrentZ->SetValue(0); + myDX->SetValue(0); + myDY->SetValue(0); + myDZ->SetValue(0); myId->setText(""); myId->setReadOnly ( true ); - myIdBtn->setDown( false ); + myIdBtn->setChecked( false ); myIdBtn->setEnabled( false ); - myCoordBtn->setDown( true ); + myCoordBtn->setChecked( true ); } else { myId->setReadOnly ( false ); @@ -280,8 +325,10 @@ SMESHGUI_MakeNodeAtPointOp::SMESHGUI_MakeNodeAtPointOp() connect(myDlg->myId,SIGNAL (textChanged(const QString&)),SLOT(redisplayPreview())); connect(myDlg->myPreviewChkBox, SIGNAL (toggled(bool)),SLOT(redisplayPreview())); connect(myDlg->myAutoSearchChkBox,SIGNAL (toggled(bool)),SLOT(redisplayPreview())); - connect(myDlg->myMoveRBtn, SIGNAL (toggled(bool)),SLOT(redisplayPreview())); - connect(myDlg->myCreateRBtn, SIGNAL (toggled(bool)),SLOT(redisplayPreview())); + + // IPAL22913: TC6.5.0: selected in "Move node" dialog box node is not highlighted + // note: this slot seems to be lost together with removed obsolete SMESHGUI_MoveNodesDlg class + connect(myDlg->myId,SIGNAL (textChanged(const QString&)),SLOT(onTextChange(const QString&))); } //======================================================================= @@ -320,13 +367,18 @@ void SMESHGUI_MakeNodeAtPointOp::startOperation() myDlg->myX->SetValue(0); myDlg->myY->SetValue(0); myDlg->myZ->SetValue(0); + myDlg->myCurrentX->SetValue(0); + myDlg->myCurrentY->SetValue(0); + myDlg->myCurrentZ->SetValue(0); + myDlg->myDX->SetValue(0); + myDlg->myDY->SetValue(0); + myDlg->myDZ->SetValue(0); myDlg->myId->setText(""); myDlg->show(); onSelectionDone(); // init myMeshActor if ( myMeshActor ) { -// myMeshOldDisplayMode = myMeshActor->GetRepresentation(); // myMeshActor->SetRepresentation( VTK_WIREFRAME ); myMeshActor->SetPointRepresentation(true); SMESH::RepaintCurrentView(); @@ -345,7 +397,6 @@ void SMESHGUI_MakeNodeAtPointOp::stopOperation() myNoPreview = true; mySimulation->SetVisibility(false); if ( myMeshActor ) { -// myMeshActor->SetRepresentation( myMeshOldDisplayMode ); myMeshActor->SetPointRepresentation(false); SMESH::RepaintCurrentView(); myMeshActor = 0; @@ -367,7 +418,7 @@ bool SMESHGUI_MakeNodeAtPointOp::onApply() if ( !myMeshActor ) { SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), - tr("INVALID_MESH") ); + tr("INVALID_MESH") ); dlg()->show(); return false; } @@ -376,46 +427,49 @@ bool SMESHGUI_MakeNodeAtPointOp::onApply() if ( !isValid( msg ) ) { // node id is invalid if( !msg.isEmpty() ) SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), - tr("INVALID_ID") ); + tr("INVALID_ID") ); dlg()->show(); return false; } + QStringList aParameters; + aParameters << myDlg->myX->text(); + aParameters << myDlg->myY->text(); + aParameters << myDlg->myZ->text(); try { SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO()); if (aMesh->_is_nil()) { SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_ERROR"), - tr("SMESHG_NO_MESH") ); + tr("SMESHG_NO_MESH") ); return true; } SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); if (aMeshEditor->_is_nil()) return true; - int aResult = 0; - if ( myDlg->myCreateRBtn->isDown() ) - { - aResult = aMeshEditor->AddNode(myDlg->myX->GetValue(), - myDlg->myY->GetValue(), - myDlg->myZ->GetValue()); - } - else - { - int anId = myDlg->myId->text().toInt(); - aResult = aMeshEditor->MoveClosestNodeToPoint(myDlg->myX->GetValue(), - myDlg->myY->GetValue(), - myDlg->myZ->GetValue(), - anId); - } + aMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + + bool ok; + int anId = myDlg->myId->text().toInt( &ok ); + if( !ok || anId < 1 ) + anId = aMeshEditor->FindNodeClosestTo(myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue()); + + int aResult = aMeshEditor->MoveNode(anId, + myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue() ); + if (aResult) { - QStringList aParameters; - aParameters << myDlg->myX->text(); - aParameters << myDlg->myY->text(); - aParameters << myDlg->myZ->text(); - aMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - + myDlg->myCurrentX->SetValue(0); + myDlg->myCurrentY->SetValue(0); + myDlg->myCurrentZ->SetValue(0); + myDlg->myDX->SetValue(0); + myDlg->myDY->SetValue(0); + myDlg->myDZ->SetValue(0); myDlg->myId->setText(""); SALOME_ListIO aList; @@ -423,6 +477,7 @@ bool SMESHGUI_MakeNodeAtPointOp::onApply() aList.Append(myMeshActor->getIO()); selectionMgr()->setSelectedObjects(aList,false); SMESH::UpdateView(); + SMESHGUI::Modified(); } } catch (const SALOME::SALOME_Exception& S_ex) { @@ -444,7 +499,6 @@ bool SMESHGUI_MakeNodeAtPointOp::isValid( QString& msg ) { bool ok = true; if ( myMeshActor && - myDlg->myMoveRBtn->isDown() && !myDlg->myAutoSearchChkBox->isChecked() ) { ok = false; @@ -482,7 +536,7 @@ void SMESHGUI_MakeNodeAtPointOp::onSelectionDone() SMESH_Actor* aMeshActor = SMESH::FindActorByEntry(anIO->getEntry()); if (!aMeshActor) { // coord by geom - if ( myDlg->myCoordBtn->isDown() ) { + if ( myDlg->myCoordBtn->isChecked() ) { GEOM::GEOM_Object_var geom = SMESH::IObjectToInterface(anIO); if ( !geom->_is_nil() ) { TopoDS_Vertex aShape; @@ -510,19 +564,34 @@ void SMESHGUI_MakeNodeAtPointOp::onSelectionDone() if (SMDS_Mesh* aMesh = aMeshActor->GetObject()->GetMesh()) { if (const SMDS_MeshNode* aNode = aMesh->FindNode(aString.toInt())) { myNoPreview = true; - if ( myDlg->myCoordBtn->isDown() ) { // set coord + if ( myDlg->myCoordBtn->isChecked() ) { // set coord myDlg->myX->SetValue(aNode->X()); myDlg->myY->SetValue(aNode->Y()); myDlg->myZ->SetValue(aNode->Z()); myNoPreview = false; redisplayPreview(); } - else if ( myDlg->myIdBtn->isDown() && + else if ( myDlg->myIdBtn->isChecked() && myDlg->myIdBtn->isEnabled() ) { // set node to move myDlg->myId->setText(aString); myNoPreview = false; redisplayPreview(); } + + if (const SMDS_MeshNode* aCurrentNode = aMesh->FindNode(myDlg->myId->text().toInt())) { + double x = aCurrentNode->X(); + double y = aCurrentNode->Y(); + double z = aCurrentNode->Z(); + double dx = myDlg->myX->GetValue() - x; + double dy = myDlg->myY->GetValue() - y; + double dz = myDlg->myZ->GetValue() - z; + myDlg->myCurrentX->SetValue(x); + myDlg->myCurrentY->SetValue(y); + myDlg->myCurrentZ->SetValue(z); + myDlg->myDX->SetValue(dx); + myDlg->myDY->SetValue(dy); + myDlg->myDZ->SetValue(dz); + } } } } @@ -545,15 +614,22 @@ void SMESHGUI_MakeNodeAtPointOp::redisplayPreview() SMESH::MeshPreviewStruct_var aMeshPreviewStruct; bool moveShown = false; - if ( myDlg->myMoveRBtn->isDown() && // Move method - myMeshActor) + if ( myMeshActor) { const bool autoSearch = myDlg->myAutoSearchChkBox->isChecked(); const bool preview = myDlg->myPreviewChkBox->isChecked(); if ( autoSearch ) + { + myDlg->myCurrentX->SetValue(0); + myDlg->myCurrentY->SetValue(0); + myDlg->myCurrentZ->SetValue(0); + myDlg->myDX->SetValue(0); + myDlg->myDY->SetValue(0); + myDlg->myDZ->SetValue(0); myDlg->myId->setText(""); + } QString msg; - if ( preview && ( autoSearch || isValid( msg ) )) + if ( autoSearch || isValid( msg ) ) { try { SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO()); @@ -563,11 +639,19 @@ void SMESHGUI_MakeNodeAtPointOp::redisplayPreview() { SUIT_OverrideCursor aWaitCursor; + int anId = 0; + if ( autoSearch ) + anId = aPreviewer->FindNodeClosestTo(myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue()); + else + anId = myDlg->myId->text().toInt(); + // find id and/or just compute preview - int anId = aPreviewer->MoveClosestNodeToPoint(myDlg->myX->GetValue(), - myDlg->myY->GetValue(), - myDlg->myZ->GetValue(), - myDlg->myId->text().toInt()); + aPreviewer->MoveNode(anId, + myDlg->myX->GetValue(), + myDlg->myY->GetValue(), + myDlg->myZ->GetValue()); if ( autoSearch ) { // set found id QString idTxt("%1"); if ( anId > 0 ) @@ -576,6 +660,24 @@ void SMESHGUI_MakeNodeAtPointOp::redisplayPreview() idTxt = ""; myDlg->myId->setText( idTxt ); } + + SMESH::double_array* aXYZ = aMesh->GetNodeXYZ( anId ); + if( aXYZ && aXYZ->length() >= 3 ) + { + double x = aXYZ->operator[](0); + double y = aXYZ->operator[](1); + double z = aXYZ->operator[](2); + double dx = myDlg->myX->GetValue() - x; + double dy = myDlg->myY->GetValue() - y; + double dz = myDlg->myZ->GetValue() - z; + myDlg->myCurrentX->SetValue(x); + myDlg->myCurrentY->SetValue(y); + myDlg->myCurrentZ->SetValue(z); + myDlg->myDX->SetValue(dx); + myDlg->myDY->SetValue(dy); + myDlg->myDZ->SetValue(dz); + } + if ( preview ) { // fill preview data aMeshPreviewStruct = aPreviewer->GetPreviewData(); moveShown = ( anId > 0 ); @@ -618,6 +720,35 @@ void SMESHGUI_MakeNodeAtPointOp::redisplayPreview() myNoPreview = false; } +//================================================================================ +/*! + * \brief SLOT called when the node id is manually changed + */ +//================================================================================ + +void SMESHGUI_MakeNodeAtPointOp::onTextChange( const QString& theText ) +{ + if( myMeshActor ) + { + if( SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh() ) + { + Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO(); + SALOME_ListIO aList; + aList.Append( anIO ); + selectionMgr()->setSelectedObjects( aList, false ); + + if( const SMDS_MeshNode* aNode = aMesh->FindNode( theText.toInt() ) ) + { + TColStd_MapOfInteger aListInd; + aListInd.Add( aNode->GetID() ); + selector()->AddOrRemoveIndex( anIO, aListInd, false ); + if( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( SMESHGUI::GetSMESHGUI() ) ) + aViewWindow->highlight( anIO, true, true ); + } + } + } +} + //================================================================================ /*! * \brief Activate Node selection diff --git a/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h b/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h index 6df8ba722..11bf1189c 100644 --- a/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h +++ b/src/SMESHGUI/SMESHGUI_MakeNodeAtPointDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_MakeNodeAtPointDlg.h // Author : Edward AGAPOV, Open CASCADE S.A.S. // @@ -35,7 +36,6 @@ class QGroupBox; class QLineEdit; class QPushButton; class QCheckBox; -class QRadioButton; class SMESHGUI_SpinBox; class SMESHGUI_MeshEditPreview; class SMESHGUI_MakeNodeAtPointDlg; @@ -68,6 +68,7 @@ protected slots: private slots: void onSelectionDone(); void redisplayPreview(); + void onTextChange( const QString& ); private: SMESHGUI_MakeNodeAtPointDlg* myDlg; @@ -97,11 +98,15 @@ private: SMESHGUI_SpinBox* myX; SMESHGUI_SpinBox* myY; SMESHGUI_SpinBox* myZ; - QRadioButton* myMoveRBtn; - QRadioButton* myCreateRBtn; QGroupBox* myNodeToMoveGrp; QPushButton* myIdBtn; QLineEdit* myId; + SMESHGUI_SpinBox* myCurrentX; + SMESHGUI_SpinBox* myCurrentY; + SMESHGUI_SpinBox* myCurrentZ; + SMESHGUI_SpinBox* myDX; + SMESHGUI_SpinBox* myDY; + SMESHGUI_SpinBox* myDZ; QCheckBox* myAutoSearchChkBox; QCheckBox* myPreviewChkBox; diff --git a/src/SMESHGUI/SMESHGUI_Measurements.cxx b/src/SMESHGUI/SMESHGUI_Measurements.cxx new file mode 100644 index 000000000..4e94217ed --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_Measurements.cxx @@ -0,0 +1,1231 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_Measurements.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#include "SMESHGUI_Measurements.h" + +#include "SMESH_Actor.h" +#include "SMESHGUI.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) +#include CORBA_SERVER_HEADER(SMESH_Measurements) + +const int SPACING = 6; // layout spacing +const int MARGIN = 9; // layout margin +const int MAX_NB_FOR_EDITOR = 40; // max nb of items in the ID list editor field + +// Uncomment as soon as elements are supported by Min Distance operation +//#define MINDIST_ENABLE_ELEMENT + +// Uncomment as soon as objects are supported by Min Distance operation +//#define MINDIST_ENABLE_OBJECT + +/*! + \class SMESHGUI_MinDistance + \brief Minimum distance measurement widget. + + Widget to calculate minimum distance between two objects. +*/ + +/*! + \brief Constructor. + \param parent parent widget +*/ +SMESHGUI_MinDistance::SMESHGUI_MinDistance( QWidget* parent ) +: QWidget( parent ), myCurrentTgt( FirstTgt ), myFirstActor( 0 ), mySecondActor( 0 ), myPreview( 0 ) +{ + QGroupBox* aFirstTgtGrp = new QGroupBox( tr( "FIRST_TARGET" ), this ); + QRadioButton* aFNode = new QRadioButton( tr( "NODE" ), aFirstTgtGrp ); + QRadioButton* aFElem = new QRadioButton( tr( "ELEMENT" ), aFirstTgtGrp ); + QRadioButton* aFObject = new QRadioButton( tr( "OBJECT" ), aFirstTgtGrp ); + myFirstTgt = new QLineEdit( aFirstTgtGrp ); + + QGridLayout* fl = new QGridLayout( aFirstTgtGrp ); + fl->setMargin( MARGIN ); + fl->setSpacing( SPACING ); + fl->addWidget( aFNode, 0, 0 ); + fl->addWidget( aFElem, 0, 1 ); + fl->addWidget( aFObject, 0, 2 ); + fl->addWidget( myFirstTgt, 1, 0, 1, 3 ); + + myFirst = new QButtonGroup( this ); + myFirst->addButton( aFNode, NodeTgt ); + myFirst->addButton( aFElem, ElementTgt ); + myFirst->addButton( aFObject, ObjectTgt ); + + QGroupBox* aSecondTgtGrp = new QGroupBox( tr( "SECOND_TARGET" ), this ); + QRadioButton* aSOrigin = new QRadioButton( tr( "ORIGIN" ), aSecondTgtGrp ); + QRadioButton* aSNode = new QRadioButton( tr( "NODE" ), aSecondTgtGrp ); + QRadioButton* aSElem = new QRadioButton( tr( "ELEMENT" ), aSecondTgtGrp ); + QRadioButton* aSObject = new QRadioButton( tr( "OBJECT" ), aSecondTgtGrp ); + mySecondTgt = new QLineEdit( aSecondTgtGrp ); + + QGridLayout* sl = new QGridLayout( aSecondTgtGrp ); + sl->setMargin( MARGIN ); + sl->setSpacing( SPACING ); + sl->addWidget( aSOrigin, 0, 0 ); + sl->addWidget( aSNode, 0, 1 ); + sl->addWidget( aSElem, 0, 2 ); + sl->addWidget( aSObject, 0, 3 ); + sl->addWidget( mySecondTgt, 1, 0, 1, 4 ); + + mySecond = new QButtonGroup( this ); + mySecond->addButton( aSOrigin, OriginTgt ); + mySecond->addButton( aSNode, NodeTgt ); + mySecond->addButton( aSElem, ElementTgt ); + mySecond->addButton( aSObject, ObjectTgt ); + + QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this ); + + QGroupBox* aResults = new QGroupBox( tr( "RESULT" ), this ); + QLabel* aDxLab = new QLabel( "dX", aResults ); + myDX = new QLineEdit( aResults ); + QLabel* aDyLab = new QLabel( "dY", aResults ); + myDY = new QLineEdit( aResults ); + QLabel* aDzLab = new QLabel( "dZ", aResults ); + myDZ = new QLineEdit( aResults ); + QLabel* aDistLab = new QLabel( tr( "DISTANCE" ), aResults ); + myDistance = new QLineEdit( aResults ); + + QGridLayout* rl = new QGridLayout( aResults ); + rl->setMargin( MARGIN ); + rl->setSpacing( SPACING ); + rl->addWidget( aDxLab, 0, 0 ); + rl->addWidget( myDX, 0, 1 ); + rl->addWidget( aDyLab, 1, 0 ); + rl->addWidget( myDY, 1, 1 ); + rl->addWidget( aDzLab, 2, 0 ); + rl->addWidget( myDZ, 2, 1 ); + rl->addWidget( aDistLab, 0, 2 ); + rl->addWidget( myDistance, 0, 3 ); + + QGridLayout* l = new QGridLayout( this ); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + + l->addWidget( aFirstTgtGrp, 0, 0, 1, 2 ); + l->addWidget( aSecondTgtGrp, 1, 0, 1, 2 ); + l->addWidget( aCompute, 2, 0 ); + l->addWidget( aResults, 3, 0, 1, 2 ); + l->setColumnStretch( 1, 5 ); + l->setRowStretch( 4, 5 ); + + aFNode->setChecked( true ); + aSOrigin->setChecked( true ); +#ifndef MINDIST_ENABLE_ELEMENT + aFElem->setEnabled( false ); // NOT AVAILABLE YET + aSElem->setEnabled( false ); // NOT AVAILABLE YET +#endif +#ifndef MINDIST_ENABLE_OBJECT + aFObject->setEnabled( false ); // NOT AVAILABLE YET + aSObject->setEnabled( false ); // NOT AVAILABLE YET +#endif + myDX->setReadOnly( true ); + myDY->setReadOnly( true ); + myDZ->setReadOnly( true ); + myDistance->setReadOnly( true ); + + myValidator = new SMESHGUI_IdValidator( this, 1 ); + + myFirstTgt->installEventFilter( this ); + mySecondTgt->installEventFilter( this ); + + connect( myFirst, SIGNAL( buttonClicked( int ) ), this, SLOT( firstChanged() ) ); + connect( mySecond, SIGNAL( buttonClicked( int ) ), this, SLOT( secondChanged() ) ); + connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ) ); + connect( myFirstTgt, SIGNAL( textEdited( QString ) ), this, SLOT( firstEdited() ) ); + connect( mySecondTgt, SIGNAL( textEdited( QString ) ), this, SLOT( secondEdited() ) ); + + QList filters; + filters.append( new SMESH_TypeFilter( MESHorSUBMESH ) ); + filters.append( new SMESH_TypeFilter( GROUP ) ); + myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); + + mySecondTgt->setEnabled( mySecond->checkedId() != OriginTgt ); + clear(); + + //setTarget( FirstTgt ); +} + +/*! + \brief Destructor +*/ +SMESHGUI_MinDistance::~SMESHGUI_MinDistance() +{ + erasePreview(); + if ( myPreview ) + myPreview->Delete(); +} + +/*! + \brief Event filter + \param o object + \param o event + \return \c true if event is filtered or \c false otherwise +*/ +bool SMESHGUI_MinDistance::eventFilter( QObject* o, QEvent* e ) +{ + if ( e->type() == QEvent::FocusIn ) { + if ( o == myFirstTgt ) + setTarget( FirstTgt ); + else if ( o == mySecondTgt ) + setTarget( SecondTgt ); + } + return QWidget::eventFilter( o, e ); +} + +/*! + \brief Setup selection mode depending on the current widget state +*/ +void SMESHGUI_MinDistance::updateSelection() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + + disconnect( selMgr, 0, this, 0 ); + selMgr->clearFilters(); + + bool nodeMode = ( myCurrentTgt == FirstTgt && myFirst->checkedId() == NodeTgt ) || + ( myCurrentTgt == SecondTgt && mySecond->checkedId() == NodeTgt ); + bool elemMode = ( myCurrentTgt == FirstTgt && myFirst->checkedId() == ElementTgt ) || + ( myCurrentTgt == SecondTgt && mySecond->checkedId() == ElementTgt ); + bool objMode = ( myCurrentTgt == FirstTgt && myFirst->checkedId() == ObjectTgt ) || + ( myCurrentTgt == SecondTgt && mySecond->checkedId() == ObjectTgt ) || + ( myCurrentTgt == NoTgt ); + + if ( nodeMode ) { + SMESH::SetPointRepresentation( true ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( NodeSelection ); + } + else if ( elemMode ) { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( CellSelection ); + } + else if ( objMode ) { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( ActorSelection ); + selMgr->installFilter( myFilter ); + } + + connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) ); + + if ( myCurrentTgt == FirstTgt ) + firstEdited(); + else if ( myCurrentTgt == SecondTgt ) + secondEdited(); + + //selectionChanged(); +} + +/*! + \brief Deactivate widget +*/ +void SMESHGUI_MinDistance::deactivate() +{ + disconnect( SMESHGUI::selectionMgr(), 0, this, 0 ); +} + +/*! + \brief Set current target for selection + \param target new target ID +*/ +void SMESHGUI_MinDistance::setTarget( int target ) +{ + if ( myCurrentTgt != target ) { + myCurrentTgt = target; + updateSelection(); + } +} + +/*! + \brief Erase preview actor +*/ +void SMESHGUI_MinDistance::erasePreview() +{ + SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(); + if ( aViewWindow && myPreview ) { + aViewWindow->RemoveActor( myPreview ); + aViewWindow->Repaint(); + } +} + +/*! + \brief Display preview actor +*/ +void SMESHGUI_MinDistance::displayPreview() +{ + SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(); + if ( aViewWindow && myPreview ) { + aViewWindow->AddActor( myPreview ); + aViewWindow->Repaint(); + } +} + +/*! + \brief Create preview actor + \param x1 X coordinate of first point + \param y1 X coordinate of first point + \param z1 Y coordinate of first point + \param x2 Y coordinate of second point + \param y2 Z coordinate of second point + \param z2 Z coordinate of second point +*/ +void SMESHGUI_MinDistance::createPreview( double x1, double y1, double z1, double x2, double y2, double z2 ) +{ + if ( myPreview ) + myPreview->Delete(); + + vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New(); + // create points + vtkPoints* aPoints = vtkPoints::New(); + aPoints->SetNumberOfPoints( 2 ); + aPoints->SetPoint( 0, x1, y1, z1 ); + aPoints->SetPoint( 1, x2, y2, z2 ); + aGrid->SetPoints( aPoints ); + aPoints->Delete(); + // create cells + vtkIdList* anIdList = vtkIdList::New(); + anIdList->SetNumberOfIds( 2 ); + vtkCellArray* aCells = vtkCellArray::New(); + aCells->Allocate( 2, 0); + vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New(); + aCellTypesArray->SetNumberOfComponents( 1 ); + aCellTypesArray->Allocate( 1 ); + anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 1 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->Delete(); + VTKViewer_CellLocationsArray* aCellLocationsArray = VTKViewer_CellLocationsArray::New(); + aCellLocationsArray->SetNumberOfComponents( 1 ); + aCellLocationsArray->SetNumberOfTuples( 1 ); + aCells->InitTraversal(); + for( vtkIdType idType = 0, *pts, npts; aCells->GetNextCell( npts, pts ); idType++ ) + aCellLocationsArray->SetValue( idType, aCells->GetTraversalLocation( npts ) ); + aGrid->SetCells( aCellTypesArray, aCellLocationsArray, aCells ); + aCellLocationsArray->Delete(); + aCellTypesArray->Delete(); + aCells->Delete(); + // create actor + vtkDataSetMapper* aMapper = vtkDataSetMapper::New(); + aMapper->SetInput( aGrid ); + aGrid->Delete(); + myPreview = SALOME_Actor::New(); + myPreview->PickableOff(); + myPreview->SetMapper( aMapper ); + aMapper->Delete(); + vtkProperty* aProp = vtkProperty::New(); + aProp->SetRepresentationToWireframe(); + aProp->SetColor( 250, 0, 250 ); + aProp->SetPointSize( 5 ); + aProp->SetLineWidth( 3 ); + myPreview->SetProperty( aProp ); + aProp->Delete(); +} + +/*! + \brief Called when selection is changed +*/ +void SMESHGUI_MinDistance::selectionChanged() +{ + SUIT_OverrideCursor wc; + + SALOME_ListIO selected; + SMESHGUI::selectionMgr()->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) IO = selected.First(); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + if ( myCurrentTgt == FirstTgt ) { + myFirstSrc = obj; + myFirstActor = SMESH::FindActorByEntry( IO->getEntry() ); + if ( myFirst->checkedId() == ObjectTgt ) { + QString aName; + SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName ); + myFirstTgt->setText( aName ); + } + else { + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + QString ID; + int nb = 0; + if ( myFirstActor && selector ) { + nb = myFirst->checkedId() == NodeTgt ? + SMESH::GetNameOfSelectedElements( selector, IO, ID ) : + SMESH::GetNameOfSelectedNodes( selector, IO, ID ); + } + if ( nb == 1 ) + myFirstTgt->setText( ID.trimmed() ); + else + myFirstTgt->clear(); + } + } + else if ( myCurrentTgt == SecondTgt ) { + mySecondSrc = obj; + mySecondActor = SMESH::FindActorByEntry( IO->getEntry() ); + if ( mySecond->checkedId() == ObjectTgt ) { + QString aName; + SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName ); + mySecondTgt->setText( aName ); + } + else { + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + QString ID; + int nb = 0; + if ( mySecondActor && selector ) { + nb = mySecond->checkedId() == NodeTgt ? + SMESH::GetNameOfSelectedElements( selector, IO, ID ) : + SMESH::GetNameOfSelectedNodes( selector, IO, ID ); + } + if ( nb == 1 ) + mySecondTgt->setText( ID.trimmed() ); + else + mySecondTgt->clear(); + } + } + } + } + clear(); +} + +/*! + \brief Called when first target mode is changed by the user +*/ +void SMESHGUI_MinDistance::firstChanged() +{ + myFirstSrc = SMESH::SMESH_IDSource::_nil(); + myFirstTgt->clear(); + myFirstTgt->setReadOnly( myFirst->checkedId() == ObjectTgt ); + myFirstTgt->setValidator( myFirst->checkedId() == ObjectTgt ? 0 : myValidator ); + setTarget( FirstTgt ); + updateSelection(); + clear(); +} + +/*! + \brief Called when second target mode is changed by the user +*/ +void SMESHGUI_MinDistance::secondChanged() +{ + mySecondSrc = SMESH::SMESH_IDSource::_nil(); + mySecondTgt->setEnabled( mySecond->checkedId() != OriginTgt ); + mySecondTgt->setReadOnly( mySecond->checkedId() == ObjectTgt ); + mySecondTgt->setValidator( mySecond->checkedId() == ObjectTgt ? 0 : myValidator ); + mySecondTgt->clear(); + setTarget( mySecond->checkedId() != OriginTgt ? SecondTgt : NoTgt ); + updateSelection(); + clear(); +} + +/*! + \brief Called when first target is edited by the user +*/ +void SMESHGUI_MinDistance::firstEdited() +{ + setTarget( FirstTgt ); + if ( sender() == myFirstTgt ) + clear(); + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + if ( myFirstActor && selector ) { + Handle(SALOME_InteractiveObject) IO = myFirstActor->getIO(); + if ( myFirst->checkedId() == NodeTgt || myFirst->checkedId() == ElementTgt ) { + TColStd_MapOfInteger ID; + ID.Add( myFirstTgt->text().toLong() ); + selector->AddOrRemoveIndex( IO, ID, false ); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->highlight( IO, true, true ); + } +} + +/*! + \brief Called when second target is edited by the user +*/ +void SMESHGUI_MinDistance::secondEdited() +{ + setTarget( SecondTgt ); + if ( sender() == mySecondTgt ) + clear(); + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + if ( mySecondActor && selector ) { + Handle(SALOME_InteractiveObject) IO = mySecondActor->getIO(); + if ( mySecond->checkedId() == NodeTgt || mySecond->checkedId() == ElementTgt ) { + TColStd_MapOfInteger ID; + ID.Add( mySecondTgt->text().toLong() ); + selector->AddOrRemoveIndex( IO, ID, false ); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->highlight( IO, true, true ); + } +} + +/*! + \brief Compute the minimum distance between targets +*/ +void SMESHGUI_MinDistance::compute() +{ + SUIT_OverrideCursor wc; + SMESH::SMESH_IDSource_var s1; + SMESH::SMESH_IDSource_var s2; + bool isOrigin = mySecond->checkedId() == OriginTgt; + + // process first target + if ( !CORBA::is_nil( myFirstSrc ) ) { + if ( myFirst->checkedId() == NodeTgt || myFirst->checkedId() == ElementTgt ) { + SMESH::SMESH_Mesh_var m = myFirstSrc->GetMesh(); + long id = myFirstTgt->text().toLong(); + if ( !CORBA::is_nil( m ) && id ) { + SMESH::long_array_var ids = new SMESH::long_array(); + ids->length( 1 ); + ids[0] = id; + SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor(); + s1 = me->MakeIDSource( ids.in(), myFirst->checkedId() == NodeTgt ? SMESH::NODE : SMESH::FACE ); + } + } + else { + s1 = myFirstSrc; + } + } + + // process second target + if ( !CORBA::is_nil( mySecondSrc ) ) { + if ( mySecond->checkedId() == NodeTgt || mySecond->checkedId() == ElementTgt ) { + SMESH::SMESH_Mesh_var m = mySecondSrc->GetMesh(); + long id = mySecondTgt->text().toLong(); + if ( !CORBA::is_nil( m ) && id ) { + SMESH::long_array_var ids = new SMESH::long_array(); + ids->length( 1 ); + ids[0] = id; + SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor(); + s2 = me->MakeIDSource( ids.in(), mySecond->checkedId() == NodeTgt ? SMESH::NODE : SMESH::FACE ); + } + } + else { + s2 = mySecondSrc; + } + } + + if ( !CORBA::is_nil( s1 ) && ( !CORBA::is_nil( s2 ) || isOrigin ) ) { + // compute min distance + int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 ); + SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements(); + SMESH::Measure result = measure->MinDistance( s1.in(), s2.in() ); + measure->UnRegister(); + myDX->setText( QString::number( result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDY->setText( QString::number( result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDZ->setText( QString::number( result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDistance->setText( QString::number( result.value, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // update preview actor + erasePreview(); + double x1, y1, z1, x2, y2, z2; + SMESH::double_array_var coord = s1->GetMesh()->GetNodeXYZ( result.node1 ); + x1 = coord[0]; y1 = coord[1]; z1 = coord[2]; + if ( isOrigin ) { + x2 = y2 = z2 = 0.; + } + else { + coord = s2->GetMesh()->GetNodeXYZ( result.node2 ); + x2 = coord[0]; y2 = coord[1]; z2 = coord[2]; + } + createPreview( x1, y1, z1, x2, y2, z2 ); + displayPreview(); + } + else { + clear(); + } +} + +/*! + \brief Reset the widget to the initial state (nullify result fields) +*/ +void SMESHGUI_MinDistance::clear() +{ + myDX->clear(); + myDY->clear(); + myDZ->clear(); + myDistance->clear(); + erasePreview(); +} + +/*! + \class SMESHGUI_BoundingBox + \brief Bounding box measurement widget. + + Widget to calculate bounding box of the selected object(s). +*/ + +/*! + \brief Constructor. + \param parent parent widget +*/ +SMESHGUI_BoundingBox::SMESHGUI_BoundingBox( QWidget* parent ) +: QWidget( parent ), myActor( 0 ), myPreview( 0 ) +{ + QGroupBox* aSourceGrp = new QGroupBox( tr( "SOURCE" ), this ); + QRadioButton* aObjects = new QRadioButton( tr( "OBJECTS" ), aSourceGrp ); + QRadioButton* aNodes = new QRadioButton( tr( "NODES" ), aSourceGrp ); + QRadioButton* aElements = new QRadioButton( tr( "ELEMENTS" ), aSourceGrp ); + mySource = new QLineEdit( aSourceGrp ); + + QGridLayout* fl = new QGridLayout( aSourceGrp ); + fl->setMargin( MARGIN ); + fl->setSpacing( SPACING ); + fl->addWidget( aObjects, 0, 0 ); + fl->addWidget( aNodes, 0, 1 ); + fl->addWidget( aElements, 0, 2 ); + fl->addWidget( mySource, 1, 0, 1, 3 ); + + mySourceMode = new QButtonGroup( this ); + mySourceMode->addButton( aObjects, ObjectsSrc ); + mySourceMode->addButton( aNodes, NodesSrc ); + mySourceMode->addButton( aElements, ElementsSrc ); + + QPushButton* aCompute = new QPushButton( tr( "COMPUTE" ), this ); + + QGroupBox* aResults = new QGroupBox( tr( "RESULT" ), this ); + QLabel* aXminLab = new QLabel( "Xmin", aResults ); + myXmin = new QLineEdit( aResults ); + QLabel* aXmaxLab = new QLabel( "Xmax", aResults ); + myXmax = new QLineEdit( aResults ); + QLabel* aDxLab = new QLabel( "dX", aResults ); + myDX = new QLineEdit( aResults ); + QLabel* aYminLab = new QLabel( "Ymin", aResults ); + myYmin = new QLineEdit( aResults ); + QLabel* aYmaxLab = new QLabel( "Ymax", aResults ); + myYmax = new QLineEdit( aResults ); + QLabel* aDyLab = new QLabel( "dY", aResults ); + myDY = new QLineEdit( aResults ); + QLabel* aZminLab = new QLabel( "Zmin", aResults ); + myZmin = new QLineEdit( aResults ); + QLabel* aZmaxLab = new QLabel( "Zmax", aResults ); + myZmax = new QLineEdit( aResults ); + QLabel* aDzLab = new QLabel( "dZ", aResults ); + myDZ = new QLineEdit( aResults ); + + QGridLayout* rl = new QGridLayout( aResults ); + rl->setMargin( MARGIN ); + rl->setSpacing( SPACING ); + rl->addWidget( aXminLab, 0, 0 ); + rl->addWidget( myXmin, 0, 1 ); + rl->addWidget( aXmaxLab, 0, 2 ); + rl->addWidget( myXmax, 0, 3 ); + rl->addWidget( aDxLab, 0, 4 ); + rl->addWidget( myDX, 0, 5 ); + rl->addWidget( aYminLab, 1, 0 ); + rl->addWidget( myYmin, 1, 1 ); + rl->addWidget( aYmaxLab, 1, 2 ); + rl->addWidget( myYmax, 1, 3 ); + rl->addWidget( aDyLab, 1, 4 ); + rl->addWidget( myDY, 1, 5 ); + rl->addWidget( aZminLab, 2, 0 ); + rl->addWidget( myZmin, 2, 1 ); + rl->addWidget( aZmaxLab, 2, 2 ); + rl->addWidget( myZmax, 2, 3 ); + rl->addWidget( aDzLab, 2, 4 ); + rl->addWidget( myDZ, 2, 5 ); + + QGridLayout* l = new QGridLayout( this ); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + + l->addWidget( aSourceGrp, 0, 0, 1, 2 ); + l->addWidget( aCompute, 1, 0 ); + l->addWidget( aResults, 2, 0, 1, 2 ); + l->setColumnStretch( 1, 5 ); + l->setRowStretch( 3, 5 ); + + aObjects->setChecked( true ); + myXmin->setReadOnly( true ); + myXmax->setReadOnly( true ); + myDX->setReadOnly( true ); + myYmin->setReadOnly( true ); + myYmax->setReadOnly( true ); + myDY->setReadOnly( true ); + myZmin->setReadOnly( true ); + myZmax->setReadOnly( true ); + myDZ->setReadOnly( true ); + + myValidator = new SMESHGUI_IdValidator( this ); + + connect( mySourceMode, SIGNAL( buttonClicked( int ) ), this, SLOT( sourceChanged() ) ); + connect( aCompute, SIGNAL( clicked() ), this, SLOT( compute() ) ); + connect( mySource, SIGNAL( textEdited( QString ) ), this, SLOT( sourceEdited() ) ); + + QList filters; + filters.append( new SMESH_TypeFilter( MESHorSUBMESH ) ); + filters.append( new SMESH_TypeFilter( GROUP ) ); + myFilter = new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); + + clear(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_BoundingBox::~SMESHGUI_BoundingBox() +{ + erasePreview(); + if ( myPreview ) + myPreview->Delete(); +} + +/*! + \brief Setup selection mode depending on the current widget state +*/ +void SMESHGUI_BoundingBox::updateSelection() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + + disconnect( selMgr, 0, this, 0 ); + selMgr->clearFilters(); + + bool nodeMode = mySourceMode->checkedId() == NodesSrc; + bool elemMode = mySourceMode->checkedId() == ElementsSrc; + bool objMode = mySourceMode->checkedId() == ObjectsSrc; + + if ( nodeMode ) { + SMESH::SetPointRepresentation( true ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( NodeSelection ); + } + else if ( elemMode ) { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( CellSelection ); + } + else if ( objMode ) { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( ActorSelection ); + selMgr->installFilter( myFilter ); + } + + connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( selectionChanged() ) ); + + sourceEdited(); + + //selectionChanged(); +} + +/*! + \brief Deactivate widget +*/ +void SMESHGUI_BoundingBox::deactivate() +{ + disconnect( SMESHGUI::selectionMgr(), 0, this, 0 ); +} + +/*! + \brief Erase preview actor +*/ +void SMESHGUI_BoundingBox::erasePreview() +{ + SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(); + if ( aViewWindow && myPreview ) { + aViewWindow->RemoveActor( myPreview ); + aViewWindow->Repaint(); + } +} + +/*! + \brief Display preview actor +*/ +void SMESHGUI_BoundingBox::displayPreview() +{ + SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow(); + if ( aViewWindow && myPreview ) { + aViewWindow->AddActor( myPreview ); + aViewWindow->Repaint(); + } +} + +/*! + \brief Create preview actor + \param minX min X coordinate of bounding box + \param maxX max X coordinate of bounding box + \param minY min Y coordinate of bounding box + \param maxY max Y coordinate of bounding box + \param minZ min Z coordinate of bounding box + \param maxZ max Z coordinate of bounding box +*/ +void SMESHGUI_BoundingBox::createPreview( double minX, double maxX, double minY, double maxY, double minZ, double maxZ ) +{ + if ( myPreview ) + myPreview->Delete(); + + vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New(); + // create points + vtkPoints* aPoints = vtkPoints::New(); + aPoints->SetNumberOfPoints( 8 ); + aPoints->SetPoint( 0, minX, minY, minZ ); + aPoints->SetPoint( 1, maxX, minY, minZ ); + aPoints->SetPoint( 2, minX, maxY, minZ ); + aPoints->SetPoint( 3, maxX, maxY, minZ ); + aPoints->SetPoint( 4, minX, minY, maxZ ); + aPoints->SetPoint( 5, maxX, minY, maxZ ); + aPoints->SetPoint( 6, minX, maxY, maxZ ); + aPoints->SetPoint( 7, maxX, maxY, maxZ ); + aGrid->SetPoints( aPoints ); + aPoints->Delete(); + // create cells + // connectivity: 0-1 0-4 0-2 1-5 1-3 2-6 2-3 3-7 4-6 4-5 5-7 6-7 + vtkIdList* anIdList = vtkIdList::New(); + anIdList->SetNumberOfIds( 2 ); + vtkCellArray* aCells = vtkCellArray::New(); + aCells->Allocate( 2*12, 0); + vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New(); + aCellTypesArray->SetNumberOfComponents( 1 ); + aCellTypesArray->Allocate( 12 ); + anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 1 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 4 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 0 ); anIdList->SetId( 1, 2 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 1 ); anIdList->SetId( 1, 5 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 1 ); anIdList->SetId( 1, 3 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 2 ); anIdList->SetId( 1, 6 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 2 ); anIdList->SetId( 1, 3 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 3 ); anIdList->SetId( 1, 7 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 4 ); anIdList->SetId( 1, 6 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 4 ); anIdList->SetId( 1, 5 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 5 ); anIdList->SetId( 1, 7 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->SetId( 0, 6 ); anIdList->SetId( 1, 7 ); + aCells->InsertNextCell( anIdList ); + aCellTypesArray->InsertNextValue( VTK_LINE ); + anIdList->Delete(); + VTKViewer_CellLocationsArray* aCellLocationsArray = VTKViewer_CellLocationsArray::New(); + aCellLocationsArray->SetNumberOfComponents( 1 ); + aCellLocationsArray->SetNumberOfTuples( 12 ); + aCells->InitTraversal(); + for( vtkIdType idType = 0, *pts, npts; aCells->GetNextCell( npts, pts ); idType++ ) + aCellLocationsArray->SetValue( idType, aCells->GetTraversalLocation( npts ) ); + aGrid->SetCells( aCellTypesArray, aCellLocationsArray, aCells ); + aCellLocationsArray->Delete(); + aCellTypesArray->Delete(); + aCells->Delete(); + // create actor + vtkDataSetMapper* aMapper = vtkDataSetMapper::New(); + aMapper->SetInput( aGrid ); + aGrid->Delete(); + myPreview = SALOME_Actor::New(); + myPreview->PickableOff(); + myPreview->SetMapper( aMapper ); + aMapper->Delete(); + vtkProperty* aProp = vtkProperty::New(); + aProp->SetRepresentationToWireframe(); + aProp->SetColor( 250, 0, 250 ); + aProp->SetPointSize( 5 ); + aProp->SetLineWidth( 3 ); + myPreview->SetProperty( aProp ); + aProp->Delete(); +} + +/*! + \brief Called when selection is changed +*/ +void SMESHGUI_BoundingBox::selectionChanged() +{ + SUIT_OverrideCursor wc; + + SALOME_ListIO selected; + SMESHGUI::selectionMgr()->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) IO = selected.First(); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + mySrc.clear(); + mySrc.append( obj ); + myActor = SMESH::FindActorByEntry( IO->getEntry() ); + if ( mySourceMode->checkedId() == ObjectsSrc ) { + QString aName; + SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName ); + mySource->setText( aName ); + } + else { + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + QString ID; + int nb = 0; + if ( myActor && selector ) { + nb = mySourceMode->checkedId() == NodesSrc ? + SMESH::GetNameOfSelectedElements( selector, IO, ID ) : + SMESH::GetNameOfSelectedNodes( selector, IO, ID ); + } + if ( nb > 0 ) { + myIDs = ID.trimmed(); + if ( nb < MAX_NB_FOR_EDITOR ) { + mySource->setReadOnly( false ); + if ( mySource->validator() != myValidator ) + mySource->setValidator( myValidator ); + mySource->setText( ID.trimmed() ); + } + else { + mySource->setReadOnly( true ); + mySource->setValidator( 0 ); + mySource->setText( tr( "SELECTED_NB_OBJ" ).arg( nb ) + .arg( mySourceMode->checkedId() == NodesSrc ? tr( "NB_NODES" ) : tr( "NB_ELEMENTS") ) ); + } + } + else { + myIDs = ""; + mySource->clear(); + mySource->setReadOnly( false ); + mySource->setValidator( myValidator ); + } + } + } + } + else if ( selected.Extent() > 1 ) { + myIDs = ""; + SALOME_ListIteratorOfListIO It( selected ); + mySrc.clear(); + myActor = 0; + if ( mySourceMode->checkedId() == ObjectsSrc ) { + for( ; It.More(); It.Next()){ + Handle(SALOME_InteractiveObject) IO = It.Value(); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + mySrc.append( obj ); + } + } + QString aName; + SMESH::GetNameOfSelectedIObjects( SMESHGUI::selectionMgr(), aName ); + mySource->setText( aName ); + } + else { + mySource->clear(); + } + } + clear(); +} + +/*! + \brief Called when source mode is changed by the user +*/ +void SMESHGUI_BoundingBox::sourceChanged() +{ + myIDs = ""; + mySource->clear(); + mySource->setReadOnly( mySourceMode->checkedId() == ObjectsSrc ); + mySource->setValidator( mySourceMode->checkedId() == ObjectsSrc ? 0 : myValidator ); + updateSelection(); + clear(); +} + +/*! + \brief Called when source mode is edited by the user +*/ +void SMESHGUI_BoundingBox::sourceEdited() +{ + if ( sender() == mySource ) + clear(); + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + if ( myActor && selector ) { + Handle(SALOME_InteractiveObject) IO = myActor->getIO(); + if ( mySourceMode->checkedId() == NodesSrc || mySourceMode->checkedId() == ElementsSrc ) { + TColStd_MapOfInteger ID; + if ( !mySource->isReadOnly() ) + myIDs = mySource->text(); + QStringList ids = myIDs.split( " ", QString::SkipEmptyParts ); + foreach ( QString id, ids ) + ID.Add( id.trimmed().toLong() ); + selector->AddOrRemoveIndex( IO, ID, false ); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->highlight( IO, true, true ); + } +} + +/*! + \brief Calculate bounding box of the selected object(s) +*/ +void SMESHGUI_BoundingBox::compute() +{ + SUIT_OverrideCursor wc; + SMESH::ListOfIDSources_var srcList = new SMESH::ListOfIDSources(); + if ( mySourceMode->checkedId() == NodesSrc || mySourceMode->checkedId() == ElementsSrc ) { + if ( mySrc.count() > 0 && !CORBA::is_nil( mySrc[0] ) ) { + SMESH::SMESH_Mesh_var m = mySrc[0]->GetMesh(); + QStringList ids = myIDs.split( " ", QString::SkipEmptyParts ); + if ( !CORBA::is_nil( m ) && ids.count() > 0 ) { + SMESH::long_array_var ids_in = new SMESH::long_array(); + ids_in->length( ids.count() ); + for( int i = 0; i < ids.count(); i++ ) + ids_in[i] = ids[i].trimmed().toLong(); + SMESH::SMESH_MeshEditor_var me = m->GetMeshEditor(); + SMESH::SMESH_IDSource_var s = me->MakeIDSource( ids_in.in(), mySourceMode->checkedId() == NodesSrc ? SMESH::NODE : SMESH::FACE ); + srcList->length( 1 ); + srcList[0] = s; + } + } + } + else { + srcList->length( mySrc.count() ); + for( int i = 0; i < mySrc.count(); i++ ) + srcList[i] = mySrc[i]; + } + if ( srcList->length() > 0 ) { + // compute bounding box + int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 ); + SMESH::Measurements_var measure = SMESHGUI::GetSMESHGen()->CreateMeasurements(); + SMESH::Measure result = measure->BoundingBox( srcList.in() ); + measure->UnRegister(); + myXmin->setText( QString::number( result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myXmax->setText( QString::number( result.maxX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDX->setText( QString::number( result.maxX-result.minX, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myYmin->setText( QString::number( result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myYmax->setText( QString::number( result.maxY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDY->setText( QString::number( result.maxY-result.minY, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myZmin->setText( QString::number( result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myZmax->setText( QString::number( result.maxZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + myDZ->setText( QString::number( result.maxZ-result.minZ, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // update preview actor + erasePreview(); + createPreview( result.minX, result.maxX, result.minY, result.maxY, result.minZ, result.maxZ ); + displayPreview(); + } + else { + clear(); + } +} + +/*! + \brief Reset the widget to the initial state (nullify result fields) +*/ +void SMESHGUI_BoundingBox::clear() +{ + myXmin->clear(); + myXmax->clear(); + myDX->clear(); + myYmin->clear(); + myYmax->clear(); + myDY->clear(); + myZmin->clear(); + myZmax->clear(); + myDZ->clear(); + erasePreview(); +} + +/*! + \class SMESHGUI_MeshInfoDlg + \brief Centralized dialog box for the measurements +*/ + +/*! + \brief Constructor + \param parent parent widget + \param page specifies the dialog page to be shown at the start-up +*/ +SMESHGUI_MeasureDlg::SMESHGUI_MeasureDlg( QWidget* parent, int page ) +: QDialog( parent ) +{ + setModal( false ); + setAttribute( Qt::WA_DeleteOnClose, true ); + setWindowTitle( tr( "MEASUREMENTS" ) ); + setSizeGripEnabled( true ); + + SUIT_ResourceMgr* resMgr = SMESHGUI::resourceMgr(); + + myTabWidget = new QTabWidget( this ); + + // min distance + + myMinDist = new SMESHGUI_MinDistance( myTabWidget ); + myTabWidget->addTab( myMinDist, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_MIN_DIST" ) ), tr( "MIN_DIST" ) ); + + // bounding box + + myBndBox = new SMESHGUI_BoundingBox( myTabWidget ); + myTabWidget->addTab( myBndBox, resMgr->loadPixmap( "SMESH", tr( "ICON_MEASURE_BND_BOX" ) ), tr( "BND_BOX" ) ); + + // buttons + QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this ); + okBtn->setAutoDefault( true ); + okBtn->setDefault( true ); + okBtn->setFocus(); + QPushButton* helpBtn = new QPushButton( tr( "SMESH_BUT_HELP" ), this ); + helpBtn->setAutoDefault( true ); + + QHBoxLayout* btnLayout = new QHBoxLayout; + btnLayout->setSpacing( SPACING ); + btnLayout->setMargin( 0 ); + btnLayout->addWidget( okBtn ); + btnLayout->addStretch( 10 ); + btnLayout->addWidget( helpBtn ); + + QVBoxLayout* l = new QVBoxLayout ( this ); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + l->addWidget( myTabWidget ); + l->addStretch(); + l->addLayout( btnLayout ); + + myTabWidget->setCurrentIndex( qMax( (int)MinDistance, qMin( (int)BoundingBox, page ) ) ); + + connect( okBtn, SIGNAL( clicked() ), this, SLOT( reject() ) ); + connect( helpBtn, SIGNAL( clicked() ), this, SLOT( help() ) ); + connect( myTabWidget, SIGNAL( currentChanged( int ) ), this, SLOT( updateSelection() ) ); + connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalDeactivateActiveDialog() ), this, SLOT( deactivate() ) ); + connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalCloseAllDialogs() ), this, SLOT( reject() ) ); + + updateSelection(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_MeasureDlg::~SMESHGUI_MeasureDlg() +{ +} + +/*! + \brief Perform clean-up actions on the dialog box closing. +*/ +void SMESHGUI_MeasureDlg::reject() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + selMgr->clearFilters(); + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( ActorSelection ); + QDialog::reject(); +} + +/*! + \brief Process keyboard event + \param e key press event +*/ +void SMESHGUI_MeasureDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( !e->isAccepted() && e->key() == Qt::Key_F1 ) { + e->accept(); + help(); + } +} + +/*! + \brief Reactivate dialog box, when mouse pointer goes into it. +*/ +void SMESHGUI_MeasureDlg::enterEvent( QEvent* ) +{ + activate(); +} + +/*! + \brief Setup selection mode depending on the current dialog box state. +*/ +void SMESHGUI_MeasureDlg::updateSelection() +{ + if ( myTabWidget->currentIndex() == MinDistance ) + myMinDist->updateSelection(); + else if ( myTabWidget->currentIndex() == BoundingBox ) + myBndBox->updateSelection(); + +} + +/*! + \brief Show help page +*/ +void SMESHGUI_MeasureDlg::help() +{ + SMESH::ShowHelpFile( myTabWidget->currentIndex() == MinDistance ? + "measurements_page.html#min_distance_anchor" : + "measurements_page.html#bounding_box_anchor" ); +} + +/*! + \brief Activate dialog box +*/ +void SMESHGUI_MeasureDlg::activate() +{ + SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog(); + SMESHGUI::GetSMESHGUI()->SetActiveDialogBox( this ); + myTabWidget->setEnabled( true ); + updateSelection(); +} + +/*! + \brief Deactivate dialog box +*/ +void SMESHGUI_MeasureDlg::deactivate() +{ + myMinDist->deactivate(); + myBndBox->deactivate(); + myTabWidget->setEnabled( false ); + disconnect( SMESHGUI::selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) ); +} diff --git a/src/SMESHGUI/SMESHGUI_Measurements.h b/src/SMESHGUI/SMESHGUI_Measurements.h new file mode 100644 index 000000000..3b5079fcb --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_Measurements.h @@ -0,0 +1,172 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_Measurements.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#ifndef SMESHGUI_MEASUREMENTS_H +#define SMESHGUI_MEASUREMENTS_H + +#include "SMESH_SMESHGUI.hxx" + +#include + +class QButtonGroup; +class QLineEdit; +class QTabWidget; +class SUIT_SelectionFilter; +class SALOME_Actor; +class SMESH_Actor; +class SMESHGUI_IdValidator; + +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class SMESHGUI_EXPORT SMESHGUI_MinDistance : public QWidget +{ + Q_OBJECT; + + enum { NoTgt, FirstTgt, SecondTgt }; + enum { OriginTgt, NodeTgt, ElementTgt, ObjectTgt }; + +public: + SMESHGUI_MinDistance( QWidget* = 0 ); + ~SMESHGUI_MinDistance(); + + bool eventFilter( QObject*, QEvent* ); + void updateSelection(); + void deactivate(); + +private: + void setTarget( int ); + void erasePreview(); + void displayPreview(); + void createPreview( double, double, double, double, double, double ); + +private slots: + void selectionChanged(); + void firstChanged(); + void secondChanged(); + void firstEdited(); + void secondEdited(); + void compute(); + void clear(); + +private: + QButtonGroup* myFirst; + QButtonGroup* mySecond; + QLineEdit* myFirstTgt; + QLineEdit* mySecondTgt; + QLineEdit* myDX; + QLineEdit* myDY; + QLineEdit* myDZ; + QLineEdit* myDistance; + int myCurrentTgt; + SMESH::SMESH_IDSource_var myFirstSrc; + SMESH::SMESH_IDSource_var mySecondSrc; + SMESH_Actor* myFirstActor; + SMESH_Actor* mySecondActor; + SMESHGUI_IdValidator* myValidator; + SUIT_SelectionFilter* myFilter; + SALOME_Actor* myPreview; +}; + +class SMESHGUI_EXPORT SMESHGUI_BoundingBox : public QWidget +{ + Q_OBJECT; + + enum { ObjectsSrc, NodesSrc, ElementsSrc }; + +public: + SMESHGUI_BoundingBox( QWidget* = 0 ); + ~SMESHGUI_BoundingBox(); + + void updateSelection(); + void deactivate(); + +private: + void erasePreview(); + void displayPreview(); + void createPreview( double, double, double, double, double, double ); + +private slots: + void selectionChanged(); + void sourceChanged(); + void sourceEdited(); + void compute(); + void clear(); + +private: + typedef QList SourceList; + QButtonGroup* mySourceMode; + QLineEdit* mySource; + QLineEdit* myXmin; + QLineEdit* myXmax; + QLineEdit* myDX; + QLineEdit* myYmin; + QLineEdit* myYmax; + QLineEdit* myDY; + QLineEdit* myZmin; + QLineEdit* myZmax; + QLineEdit* myDZ; + SourceList mySrc; + SMESH_Actor* myActor; + SMESHGUI_IdValidator* myValidator; + QString myIDs; + SUIT_SelectionFilter* myFilter; + SALOME_Actor* myPreview; +}; + +class SMESHGUI_EXPORT SMESHGUI_MeasureDlg : public QDialog +{ + Q_OBJECT; + + enum { NodeMode, ElemMode }; + +public: + //! Measurement type + enum { + MinDistance, //!< minimum distance + BoundingBox //!< bounding box + }; + + SMESHGUI_MeasureDlg( QWidget* = 0, int = MinDistance ); + ~SMESHGUI_MeasureDlg(); + + void reject(); + +protected: + void keyPressEvent( QKeyEvent* ); + void enterEvent( QEvent* ); + +private slots: + void help(); + void updateSelection(); + void activate(); + void deactivate(); + +private: + QTabWidget* myTabWidget; + SMESHGUI_MinDistance* myMinDist; + SMESHGUI_BoundingBox* myBndBox; +}; + +#endif // SMESHGUI_MEASUREMENTS_H diff --git a/src/SMESHGUI/SMESHGUI_EditMeshDlg.cxx b/src/SMESHGUI/SMESHGUI_MergeDlg.cxx similarity index 69% rename from src/SMESHGUI/SMESHGUI_EditMeshDlg.cxx rename to src/SMESHGUI/SMESHGUI_MergeDlg.cxx index c8aa3710d..7446b54b2 100644 --- a/src/SMESHGUI/SMESHGUI_EditMeshDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MergeDlg.cxx @@ -1,30 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_EditMeshDlg.cxx +// File : SMESHGUI_MergeDlg.cxx // Author : Open CASCADE S.A.S. // SMESH includes // -#include "SMESHGUI_EditMeshDlg.h" +#include "SMESHGUI_MergeDlg.h" #include "SMESHGUI.h" #include "SMESHGUI_Utils.h" @@ -73,6 +74,10 @@ #include #include #include +#include +#if !defined(VTK_XVERSION) +#define VTK_XVERSION (VTK_MAJOR_VERSION<<16)+(VTK_MINOR_VERSION<<8)+(VTK_BUILD_VERSION) +#endif // Qt includes #include @@ -145,7 +150,9 @@ namespace SMESH myPtsLabeledDataMapper = vtkLabeledDataMapper::New(); myPtsLabeledDataMapper->SetInput(myPtsSelectVisiblePoints->GetOutput()); +#if (VTK_XVERSION < 0x050200) myPtsLabeledDataMapper->SetLabelFormat("%g"); +#endif myPtsLabeledDataMapper->SetLabelModeToLabelScalars(); vtkTextProperty* aPtsTextProp = vtkTextProperty::New(); @@ -169,7 +176,7 @@ namespace SMESH } void SetPointsData ( SMDS_Mesh* theMesh, - TColStd_MapOfInteger & theNodesIdMap ) + TColStd_MapOfInteger & theNodesIdMap ) { vtkPoints* aPoints = vtkPoints::New(); aPoints->SetNumberOfPoints(theNodesIdMap.Extent()); @@ -177,9 +184,9 @@ namespace SMESH TColStd_MapIteratorOfMapOfInteger idIter( theNodesIdMap ); for( int i = 0; idIter.More(); idIter.Next(), i++ ) { - const SMDS_MeshNode* aNode = theMesh->FindNode(idIter.Key()); - aPoints->SetPoint( i, aNode->X(), aNode->Y(), aNode->Z() ); - myIDs.push_back(idIter.Key()); + const SMDS_MeshNode* aNode = theMesh->FindNode(idIter.Key()); + aPoints->SetPoint( i, aNode->X(), aNode->Y(), aNode->Z() ); + myIDs.push_back(idIter.Key()); } myIdGrid->SetPoints(aPoints); @@ -190,7 +197,7 @@ namespace SMESH } void SetElemsData( TColStd_MapOfInteger & theElemsIdMap, - std::list & aGrCentersXYZ ) + std::list & aGrCentersXYZ ) { vtkPoints* aPoints = vtkPoints::New(); aPoints->SetNumberOfPoints(theElemsIdMap.Extent()); @@ -198,14 +205,14 @@ namespace SMESH TColStd_MapIteratorOfMapOfInteger idIter( theElemsIdMap ); for( ; idIter.More(); idIter.Next() ) { - myIDs.push_back(idIter.Key()); + myIDs.push_back(idIter.Key()); } gp_XYZ aXYZ; std::list::iterator coordIt = aGrCentersXYZ.begin(); for( int i = 0; coordIt != aGrCentersXYZ.end(); coordIt++, i++ ) { - aXYZ = *coordIt; - aPoints->SetPoint( i, aXYZ.X(), aXYZ.Y(), aXYZ.Z() ); + aXYZ = *coordIt; + aPoints->SetPoint( i, aXYZ.X(), aXYZ.Y(), aXYZ.Z() ); } myIdGrid->SetPoints(aPoints); aPoints->Delete(); @@ -234,20 +241,20 @@ namespace SMESH myIsPointsLabeled = theIsPointsLabeled && myIdGrid->GetNumberOfPoints(); if ( myIsPointsLabeled ) { - myPointsNumDataSet->ShallowCopy(myIdGrid); - vtkDataSet *aDataSet = myPointsNumDataSet; - int aNbElem = myIDs.size(); - vtkIntArray *anArray = vtkIntArray::New(); - anArray->SetNumberOfValues( aNbElem ); - for ( int i = 0; i < aNbElem; i++ ) - anArray->SetValue( i, myIDs[i] ); - aDataSet->GetPointData()->SetScalars( anArray ); - anArray->Delete(); - myPtsMaskPoints->SetInput( aDataSet ); - myPointLabels->SetVisibility( theIsActorVisible ); + myPointsNumDataSet->ShallowCopy(myIdGrid); + vtkDataSet *aDataSet = myPointsNumDataSet; + int aNbElem = myIDs.size(); + vtkIntArray *anArray = vtkIntArray::New(); + anArray->SetNumberOfValues( aNbElem ); + for ( int i = 0; i < aNbElem; i++ ) + anArray->SetValue( i, myIDs[i] ); + aDataSet->GetPointData()->SetScalars( anArray ); + anArray->Delete(); + myPtsMaskPoints->SetInput( aDataSet ); + myPointLabels->SetVisibility( theIsActorVisible ); } else { - myPointLabels->SetVisibility( false ); + myPointLabels->SetVisibility( false ); } } @@ -270,7 +277,7 @@ namespace SMESH //myPtsSelectVisiblePoints->UnRegisterAllOutputs(); //vtk 5.0 porting myPtsSelectVisiblePoints->Delete(); - //myPtsMaskPoints->UnRegisterAllOutputs(); //vtk 5.0 porting + //myPtsMaskPoints->UnRegisterAllOutputs(); //vtk 5.0 porting myPtsMaskPoints->Delete(); myPointLabels->Delete(); @@ -282,8 +289,8 @@ namespace SMESH static const char * IconFirst[] = { "18 10 2 1", -" g None", -". g #000000", +" g None", +". g #000000", " . . ", " .. .. .. ", " .. ... ... ", @@ -296,11 +303,10 @@ static const char * IconFirst[] = { " . . "}; //================================================================================= -// class : SMESHGUI_EditMeshDlg() +// class : SMESHGUI_MergeDlg() // purpose : //================================================================================= -SMESHGUI_EditMeshDlg::SMESHGUI_EditMeshDlg (SMESHGUI* theModule, - int theAction) +SMESHGUI_MergeDlg::SMESHGUI_MergeDlg (SMESHGUI* theModule, int theAction) : QDialog(SMESH::GetDesktop(theModule)), mySMESHGUI(theModule), mySelectionMgr(SMESH::GetSelectionMgr(theModule)), @@ -327,9 +333,9 @@ SMESHGUI_EditMeshDlg::SMESHGUI_EditMeshDlg (SMESHGUI* theModule, /***************************************************************/ GroupConstructors = new QGroupBox(myAction == 1 ? - tr("SMESH_MERGE_ELEMENTS") : - tr("SMESH_MERGE_NODES"), - this); + tr("SMESH_MERGE_ELEMENTS") : + tr("SMESH_MERGE_NODES"), + this); QButtonGroup* ButtonGroup = new QButtonGroup(this); QHBoxLayout* GroupConstructorsLayout = new QHBoxLayout(GroupConstructors); @@ -359,48 +365,88 @@ SMESHGUI_EditMeshDlg::SMESHGUI_EditMeshDlg (SMESHGUI* theModule, GroupMeshLayout->addWidget(SelectMeshButton); GroupMeshLayout->addWidget(LineEditMesh); + /***************************************************************/ + // Controls for switch dialog behaviour + + TypeBox = new QGroupBox( tr( "SMESH_MODE" ), this ); + GroupType = new QButtonGroup( this ); + QHBoxLayout* aTypeBoxLayout = new QHBoxLayout( TypeBox ); + aTypeBoxLayout->setMargin( MARGIN ); + aTypeBoxLayout->setSpacing( SPACING ); + + QRadioButton* rb1 = new QRadioButton( tr( "SMESH_AUTOMATIC" ), TypeBox ); + QRadioButton* rb2 = new QRadioButton( tr( "SMESH_MANUAL" ), TypeBox ); + GroupType->addButton( rb1, 0 ); + GroupType->addButton( rb2, 1 ); + aTypeBoxLayout->addWidget( rb1 ); + aTypeBoxLayout->addWidget( rb2 ); + + myTypeId = 0; + /***************************************************************/ // Controls for coincident elements detecting GroupCoincident = new QGroupBox(myAction == 1 ? - tr("COINCIDENT_ELEMENTS") : - tr("COINCIDENT_NODES"), - this); + tr("COINCIDENT_ELEMENTS") : + tr("COINCIDENT_NODES"), + this); + + QVBoxLayout* aCoincidentLayout = new QVBoxLayout(GroupCoincident); + aCoincidentLayout->setSpacing(SPACING); + aCoincidentLayout->setMargin(MARGIN); - QGridLayout* GroupCoincidentLayout = new QGridLayout(GroupCoincident); - GroupCoincidentLayout->setSpacing(SPACING); - GroupCoincidentLayout->setMargin(MARGIN); - if (myAction == 0) { // case merge nodes - TextLabelTolerance = new QLabel(tr("SMESH_TOLERANCE"), GroupCoincident); - SpinBoxTolerance = new SMESHGUI_SpinBox(GroupCoincident); + QWidget* foo = new QWidget(GroupCoincident); + TextLabelTolerance = new QLabel(tr("SMESH_TOLERANCE"), foo); + SpinBoxTolerance = new SMESHGUI_SpinBox(foo); SpinBoxTolerance->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); - GroupCoincidentLayout->addWidget(TextLabelTolerance, 0, 0); - GroupCoincidentLayout->addWidget(SpinBoxTolerance, 0, 1); + GroupExclude = new QGroupBox(tr("EXCLUDE_GROUPS"), foo); + GroupExclude->setCheckable( true ); + GroupExclude->setChecked( false ); + ListExclude = new QListWidget( GroupExclude ); + QVBoxLayout* GroupExcludeLayout = new QVBoxLayout(GroupExclude); + GroupExcludeLayout->setSpacing(SPACING); + GroupExcludeLayout->setMargin(MARGIN); + GroupExcludeLayout->addWidget(ListExclude); + + QGridLayout* fooLayout = new QGridLayout( foo ); + fooLayout->setSpacing(SPACING); + fooLayout->setMargin(0); + fooLayout->addWidget(TextLabelTolerance, 0, 0 ); + fooLayout->addWidget(SpinBoxTolerance, 0, 1 ); + fooLayout->addWidget(GroupExclude, 1, 0, 1, 2 ); + aCoincidentLayout->addWidget(foo); } else { TextLabelTolerance = 0; SpinBoxTolerance = 0; + GroupExclude = 0; + ListExclude = 0; } - int row = GroupCoincidentLayout->rowCount(); + GroupCoincidentWidget = new QWidget(GroupCoincident); + QGridLayout* GroupCoincidentLayout = new QGridLayout(GroupCoincidentWidget); + GroupCoincidentLayout->setSpacing(SPACING); + GroupCoincidentLayout->setMargin(0); - ListCoincident = new QListWidget(GroupCoincident); + ListCoincident = new QListWidget(GroupCoincidentWidget); ListCoincident->setSelectionMode(QListWidget::ExtendedSelection); - DetectButton = new QPushButton(tr("DETECT"), GroupCoincident); - AddGroupButton = new QPushButton(tr("SMESH_BUT_ADD"), GroupCoincident); - RemoveGroupButton = new QPushButton(tr("SMESH_BUT_REMOVE"), GroupCoincident); + DetectButton = new QPushButton(tr("DETECT"), GroupCoincidentWidget); + AddGroupButton = new QPushButton(tr("SMESH_BUT_ADD"), GroupCoincidentWidget); + RemoveGroupButton = new QPushButton(tr("SMESH_BUT_REMOVE"), GroupCoincidentWidget); - SelectAllCB = new QCheckBox(tr("SELECT_ALL"), GroupCoincident); + SelectAllCB = new QCheckBox(tr("SELECT_ALL"), GroupCoincidentWidget); - GroupCoincidentLayout->addWidget(ListCoincident, row, 0, 4, 2); - GroupCoincidentLayout->addWidget(DetectButton, row, 2); - GroupCoincidentLayout->addWidget(AddGroupButton, row+2, 2); - GroupCoincidentLayout->addWidget(RemoveGroupButton, row+3, 2); - GroupCoincidentLayout->addWidget(SelectAllCB, row+4, 0, 1, 3); - GroupCoincidentLayout->setRowMinimumHeight(row+1, 10); - GroupCoincidentLayout->setRowStretch(row+1, 5); + GroupCoincidentLayout->addWidget(ListCoincident, 0, 0, 4, 2); + GroupCoincidentLayout->addWidget(DetectButton, 0, 2); + GroupCoincidentLayout->addWidget(AddGroupButton, 2, 2); + GroupCoincidentLayout->addWidget(RemoveGroupButton, 3, 2); + GroupCoincidentLayout->addWidget(SelectAllCB, 4, 0, 1, 3); + GroupCoincidentLayout->setRowMinimumHeight(1, 10); + GroupCoincidentLayout->setRowStretch(1, 5); + + aCoincidentLayout->addWidget(GroupCoincidentWidget); /***************************************************************/ // Controls for editing the selected group @@ -455,18 +501,26 @@ SMESHGUI_EditMeshDlg::SMESHGUI_EditMeshDlg (SMESHGUI* theModule, /***************************************************************/ DlgLayout->addWidget(GroupConstructors); DlgLayout->addWidget(GroupMesh); + DlgLayout->addWidget(TypeBox); DlgLayout->addWidget(GroupCoincident); DlgLayout->addWidget(GroupEdit); DlgLayout->addWidget(GroupButtons); + GroupCoincidentWidget->setVisible( myAction != 0 ); + GroupCoincident->setVisible( myAction == 0 ); + //if GroupExclude->setVisible( myAction == 0 ); + GroupEdit->hide(); + + this->resize(10,10); + Init(); // Initialisations } //================================================================================= -// function : ~SMESHGUI_EditMeshDlg() +// function : ~SMESHGUI_MergeDlg() // purpose : Destroys the object and frees any allocated resources //================================================================================= -SMESHGUI_EditMeshDlg::~SMESHGUI_EditMeshDlg() +SMESHGUI_MergeDlg::~SMESHGUI_MergeDlg() { delete myIdPreview; } @@ -475,15 +529,17 @@ SMESHGUI_EditMeshDlg::~SMESHGUI_EditMeshDlg() // function : Init() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::Init() +void SMESHGUI_MergeDlg::Init() { if (myAction == 0) { - SpinBoxTolerance->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, 5); + SpinBoxTolerance->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, "len_tol_precision"); SpinBoxTolerance->SetValue(1e-05); } RadioButton->setChecked(true); + GroupType->button(0)->setChecked(true); + myEditCurrentArgument = (QWidget*)LineEditMesh; myActor = 0; @@ -494,17 +550,6 @@ void SMESHGUI_EditMeshDlg::Init() mySMESHGUI->SetActiveDialogBox((QDialog*)this); myIsBusy = false; - // Costruction of the logical filter - SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (MESHorSUBMESH); - SMESH_TypeFilter* aSmeshGroupFilter = new SMESH_TypeFilter (GROUP); - - QList aListOfFilters; - if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter); - if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); - - myMeshOrSubMeshOrGroupFilter = - new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR); - /* signals and slots connections */ connect(buttonOk, SIGNAL(clicked()), this, SLOT(ClickOnOk())); connect(buttonCancel, SIGNAL(clicked()), this, SLOT(ClickOnCancel())); @@ -521,28 +566,31 @@ void SMESHGUI_EditMeshDlg::Init() connect(AddElemButton, SIGNAL (clicked()), this, SLOT(onAddElement())); connect(RemoveElemButton, SIGNAL (clicked()), this, SLOT(onRemoveElement())); connect(SetFirstButton, SIGNAL( clicked() ), this, SLOT( onSetFirst() ) ); + connect(GroupType, SIGNAL(buttonClicked(int)), this, SLOT(onTypeChanged(int))); connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); /* to close dialog if study change */ connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); - SetFirstButton->setEnabled(false); - buttonOk->setEnabled(false); - buttonApply->setEnabled(false); - // Init Mesh field from selection SelectionIntoArgument(); - myHelpFileName = "merging_elements_page.html"; + // Update Buttons + updateControls(); + + if (myAction == 0) + myHelpFileName = "merging_nodes_page.html"; + else + myHelpFileName = "merging_elements_page.html"; } //================================================================================= // function : FindGravityCenter() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::FindGravityCenter(TColStd_MapOfInteger & theElemsIdMap, - std::list< gp_XYZ > & theGrCentersXYZ) +void SMESHGUI_MergeDlg::FindGravityCenter(TColStd_MapOfInteger & theElemsIdMap, + std::list< gp_XYZ > & theGrCentersXYZ) { if (!myActor) return; @@ -576,18 +624,33 @@ void SMESHGUI_EditMeshDlg::FindGravityCenter(TColStd_MapOfInteger & theElemsIdMa // function : ClickOnApply() // purpose : //================================================================================= -bool SMESHGUI_EditMeshDlg::ClickOnApply() +bool SMESHGUI_MergeDlg::ClickOnApply() { if (mySMESHGUI->isActiveStudyLocked() || myMesh->_is_nil()) return false; try { + if (myTypeId == 0) + onDetect(); + SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); SMESH::long_array_var anIds = new SMESH::long_array; SMESH::array_of_long_array_var aGroupsOfElements = new SMESH::array_of_long_array; + if ( ListCoincident->count() == 0) { + if (myAction == 0) + SUIT_MessageBox::warning(this, + tr("SMESH_WARNING"), + tr("SMESH_NO_NODES_DETECTED")); + else + SUIT_MessageBox::warning(this, + tr("SMESH_WARNING"), + tr("SMESH_NO_ELEMENTS_DETECTED")); + return false; + } + aGroupsOfElements->length(ListCoincident->count()); int anArrayNum = 0; @@ -606,12 +669,24 @@ bool SMESHGUI_EditMeshDlg::ClickOnApply() else aMeshEditor->MergeElements (aGroupsOfElements.inout()); + if ( myTypeId == 0 ) { + if (myAction ==0) + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"), + tr("SMESH_MERGED_NODES").arg(QString::number(ListCoincident->count()).toLatin1().data())); + else + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"), + tr("SMESH_MERGED_ELEMENTS").arg(QString::number(ListCoincident->count()).toLatin1().data())); + } + + } catch(...) { } + ListCoincident->clear(); + SMESH::UpdateView(); - - onDetect(); + SMESHGUI::Modified(); + return true; } @@ -619,7 +694,7 @@ bool SMESHGUI_EditMeshDlg::ClickOnApply() // function : ClickOnOk() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::ClickOnOk() +void SMESHGUI_MergeDlg::ClickOnOk() { if (ClickOnApply()) ClickOnCancel(); @@ -629,16 +704,20 @@ void SMESHGUI_EditMeshDlg::ClickOnOk() // function : ClickOnCancel() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::ClickOnCancel() +void SMESHGUI_MergeDlg::ClickOnCancel() { myIdPreview->SetPointsLabeled(false); + SMESH::SetPointRepresentation(false); + disconnect(mySelectionMgr, 0, this, 0); + disconnect(mySMESHGUI, 0, this, 0); + mySMESHGUI->ResetState(); + mySelectionMgr->clearFilters(); //mySelectionMgr->clearSelected(); - SMESH::SetPointRepresentation(false); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->SetSelectionMode(ActorSelection); - disconnect(mySelectionMgr, 0, this, 0); - mySMESHGUI->ResetState(); + reject(); } @@ -646,7 +725,7 @@ void SMESHGUI_EditMeshDlg::ClickOnCancel() // function : ClickOnHelp() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::ClickOnHelp() +void SMESHGUI_MergeDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); if (app) @@ -659,10 +738,10 @@ void SMESHGUI_EditMeshDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -670,7 +749,7 @@ void SMESHGUI_EditMeshDlg::ClickOnHelp() // function : onEditGroup() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onEditGroup() +void SMESHGUI_MergeDlg::onEditGroup() { QList selItems = ListCoincident->selectedItems(); if ( selItems.count() != 1 ) { @@ -692,11 +771,11 @@ void SMESHGUI_EditMeshDlg::onEditGroup() // function : updateControls() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::updateControls() +void SMESHGUI_MergeDlg::updateControls() { if (ListEdit->count() == 0) SetFirstButton->setEnabled(false); - bool enable = !(myMesh->_is_nil()) && ListCoincident->count(); + bool enable = !(myMesh->_is_nil()) && (ListCoincident->count() || (myTypeId == 0)); buttonOk->setEnabled(enable); buttonApply->setEnabled(enable); } @@ -705,7 +784,7 @@ void SMESHGUI_EditMeshDlg::updateControls() // function : onDetect() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onDetect() +void SMESHGUI_MergeDlg::onDetect() { if ( myMesh->_is_nil() || LineEditMesh->text().isEmpty() ) return; @@ -718,19 +797,27 @@ void SMESHGUI_EditMeshDlg::onDetect() ListEdit->clear(); SMESH::array_of_long_array_var aGroupsArray; + SMESH::ListOfIDSources_var aExcludeGroups = new SMESH::ListOfIDSources; + + SMESH::SMESH_IDSource_var src; + if ( mySubMeshOrGroup->_is_nil() ) src = SMESH::SMESH_IDSource::_duplicate( myMesh ); + else src = SMESH::SMESH_IDSource::_duplicate( mySubMeshOrGroup ); switch (myAction) { case 0 : - if(!mySubMeshOrGroup->_is_nil()) - aMeshEditor->FindCoincidentNodesOnPart(mySubMeshOrGroup, SpinBoxTolerance->GetValue(), aGroupsArray); - else - aMeshEditor->FindCoincidentNodes(SpinBoxTolerance->GetValue(), aGroupsArray); + for ( int i = 0; GroupExclude->isChecked() && i < ListExclude->count(); i++ ) { + if ( ListExclude->item( i )->checkState() == Qt::Checked ) { + aExcludeGroups->length( aExcludeGroups->length()+1 ); + aExcludeGroups[ aExcludeGroups->length()-1 ] = SMESH::SMESH_IDSource::_duplicate( myGroups[i] ); + } + } + aMeshEditor->FindCoincidentNodesOnPartBut(src.in(), + SpinBoxTolerance->GetValue(), + aGroupsArray.out(), + aExcludeGroups.in()); break; case 1 : - if(!mySubMeshOrGroup->_is_nil()) - aMeshEditor->FindEqualElements(mySubMeshOrGroup, aGroupsArray); - else - aMeshEditor->FindEqualElements(myMesh, aGroupsArray); + aMeshEditor->FindEqualElements(src.in(), aGroupsArray.out()); break; } @@ -739,7 +826,7 @@ void SMESHGUI_EditMeshDlg::onDetect() QStringList anIDs; for (int j = 0; j < aGroup.length(); j++) - anIDs.append(QString::number(aGroup[j])); + anIDs.append(QString::number(aGroup[j])); ListCoincident->addItem(anIDs.join(" ")); } @@ -754,7 +841,7 @@ void SMESHGUI_EditMeshDlg::onDetect() // function : onSelectGroup() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onSelectGroup() +void SMESHGUI_MergeDlg::onSelectGroup() { if (myIsBusy || !myActor) return; @@ -803,7 +890,7 @@ void SMESHGUI_EditMeshDlg::onSelectGroup() // function : onSelectAll() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onSelectAll (bool isToggled) +void SMESHGUI_MergeDlg::onSelectAll (bool isToggled) { if ( isToggled ) ListCoincident->selectAll(); @@ -815,7 +902,7 @@ void SMESHGUI_EditMeshDlg::onSelectAll (bool isToggled) // function : onSelectElementFromGroup() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onSelectElementFromGroup() +void SMESHGUI_MergeDlg::onSelectElementFromGroup() { if (myIsBusy || !myActor) return; @@ -850,13 +937,17 @@ void SMESHGUI_EditMeshDlg::onSelectElementFromGroup() // function : onAddGroup() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onAddGroup() +void SMESHGUI_MergeDlg::onAddGroup() { if ( myMesh->_is_nil() || LineEditMesh->text().isEmpty() ) return; QString anIDs = ""; - SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs); + int aNbElements = 0; + aNbElements = SMESH::GetNameOfSelectedNodes(mySelector, myActor->getIO(), anIDs); + + if (aNbElements < 1) + return; ListCoincident->clearSelection(); ListCoincident->addItem(anIDs); @@ -878,7 +969,7 @@ void SMESHGUI_EditMeshDlg::onAddGroup() // function : onRemoveGroup() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onRemoveGroup() +void SMESHGUI_MergeDlg::onRemoveGroup() { if (myEditCurrentArgument != (QWidget*)ListCoincident) return; @@ -900,7 +991,7 @@ void SMESHGUI_EditMeshDlg::onRemoveGroup() // function : onAddElement() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onAddElement() +void SMESHGUI_MergeDlg::onAddElement() { if (!myActor) return; @@ -936,7 +1027,7 @@ void SMESHGUI_EditMeshDlg::onAddElement() // function : onRemoveElement() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onRemoveElement() +void SMESHGUI_MergeDlg::onRemoveElement() { if (myEditCurrentArgument != (QWidget*)ListCoincident) return; @@ -956,7 +1047,7 @@ void SMESHGUI_EditMeshDlg::onRemoveElement() // function : onSetFirst() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::onSetFirst() +void SMESHGUI_MergeDlg::onSetFirst() { if (myEditCurrentArgument != (QWidget*)ListCoincident) return; @@ -978,7 +1069,7 @@ void SMESHGUI_EditMeshDlg::onSetFirst() // function : SetEditCurrentArgument() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::SetEditCurrentArgument() +void SMESHGUI_MergeDlg::SetEditCurrentArgument() { QPushButton* send = (QPushButton*)sender(); @@ -991,7 +1082,8 @@ void SMESHGUI_EditMeshDlg::SetEditCurrentArgument() SMESH::SetPointRepresentation(false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->SetSelectionMode(ActorSelection); - mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); + if (myTypeId == 1) + mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } myEditCurrentArgument->setFocus(); @@ -1003,7 +1095,7 @@ void SMESHGUI_EditMeshDlg::SetEditCurrentArgument() // function : SelectionIntoArgument() // purpose : Called when selection as changed or other case //================================================================================= -void SMESHGUI_EditMeshDlg::SelectionIntoArgument() +void SMESHGUI_MergeDlg::SelectionIntoArgument() { if (myEditCurrentArgument == (QWidget*)LineEditMesh) { QString aString = ""; @@ -1012,43 +1104,74 @@ void SMESHGUI_EditMeshDlg::SelectionIntoArgument() ListCoincident->clear(); ListEdit->clear(); myActor = 0; + QString aCurrentEntry = myEntry; int nbSel = SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aString); - if (nbSel != 1) + if (nbSel != 1) { + myIdPreview->SetPointsLabeled(false); + SMESH::SetPointRepresentation(false); + mySelectionMgr->clearFilters(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); return; + } SALOME_ListIO aList; - mySelectionMgr->selectedObjects(aList, SVTK_Viewer::Type()); + mySelectionMgr->selectedObjects(aList); Handle(SALOME_InteractiveObject) IO = aList.First(); + myEntry = IO->getEntry(); myMesh = SMESH::GetMeshByIO(IO); if (myMesh->_is_nil()) return; + + LineEditMesh->setText(aString); myActor = SMESH::FindActorByEntry(IO->getEntry()); if (!myActor) myActor = SMESH::FindActorByObject(myMesh); - if(!myActor) - return; - mySubMeshOrGroup = SMESH::SMESH_IDSource::_nil(); - - if ((!SMESH::IObjectToInterface(IO)->_is_nil() || //SUBMESH OR GROUP - !SMESH::IObjectToInterface(IO)->_is_nil()) && - !SMESH::IObjectToInterface(IO)->_is_nil()) - mySubMeshOrGroup = SMESH::IObjectToInterface(IO); - - LineEditMesh->setText(aString); + if ( myActor && myTypeId ==1 ) { + mySubMeshOrGroup = SMESH::SMESH_IDSource::_nil(); + mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); + + if ((!SMESH::IObjectToInterface(IO)->_is_nil() || //SUBMESH OR GROUP + !SMESH::IObjectToInterface(IO)->_is_nil()) && + !SMESH::IObjectToInterface(IO)->_is_nil()) + mySubMeshOrGroup = SMESH::IObjectToInterface(IO); + + if (myAction == 0) { + SMESH::SetPointRepresentation(true); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + } + else + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(CellSelection); + } - if (myAction == 0) { - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); + // process groups + if ( myAction == 0 && !myMesh->_is_nil() && myEntry != aCurrentEntry ) { + myGroups.clear(); + ListExclude->clear(); + SMESH::ListOfGroups_var aListOfGroups = myMesh->GetGroups(); + for( int i = 0, n = aListOfGroups->length(); i < n; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i]; + if ( !aGroup->_is_nil() ) { // && aGroup->GetType() == SMESH::NODE + QString aGroupName( aGroup->GetName() ); + if ( !aGroupName.isEmpty() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup)); + QListWidgetItem* item = new QListWidgetItem( aGroupName ); + item->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable ); + item->setCheckState( Qt::Unchecked ); + ListExclude->addItem( item ); + } + } + } } - else - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + + updateControls(); } } @@ -1056,10 +1179,11 @@ void SMESHGUI_EditMeshDlg::SelectionIntoArgument() // function : DeactivateActiveDialog() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::DeactivateActiveDialog() +void SMESHGUI_MergeDlg::DeactivateActiveDialog() { if (GroupConstructors->isEnabled()) { GroupConstructors->setEnabled(false); + TypeBox->setEnabled(false); GroupMesh->setEnabled(false); GroupCoincident->setEnabled(false); GroupEdit->setEnabled(false); @@ -1076,11 +1200,12 @@ void SMESHGUI_EditMeshDlg::DeactivateActiveDialog() // function : ActivateThisDialog() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::ActivateThisDialog() +void SMESHGUI_MergeDlg::ActivateThisDialog() { /* Emit a signal to deactivate the active dialog */ mySMESHGUI->EmitSignalDeactivateDialog(); GroupConstructors->setEnabled(true); + TypeBox->setEnabled(true); GroupMesh->setEnabled(true); GroupCoincident->setEnabled(true); GroupEdit->setEnabled(true); @@ -1095,7 +1220,7 @@ void SMESHGUI_EditMeshDlg::ActivateThisDialog() // function : enterEvent() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::enterEvent(QEvent*) +void SMESHGUI_MergeDlg::enterEvent(QEvent*) { if (!GroupConstructors->isEnabled()) ActivateThisDialog(); @@ -1105,7 +1230,7 @@ void SMESHGUI_EditMeshDlg::enterEvent(QEvent*) // function : closeEvent() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::closeEvent(QCloseEvent*) +void SMESHGUI_MergeDlg::closeEvent(QCloseEvent*) { /* same than click on cancel button */ ClickOnCancel(); @@ -1115,7 +1240,7 @@ void SMESHGUI_EditMeshDlg::closeEvent(QCloseEvent*) //function : hideEvent //purpose : caused by ESC key //======================================================================= -void SMESHGUI_EditMeshDlg::hideEvent (QHideEvent *) +void SMESHGUI_MergeDlg::hideEvent (QHideEvent *) { if (!isMinimized()) ClickOnCancel(); @@ -1125,7 +1250,7 @@ void SMESHGUI_EditMeshDlg::hideEvent (QHideEvent *) // function : keyPressEvent() // purpose : //================================================================================= -void SMESHGUI_EditMeshDlg::keyPressEvent( QKeyEvent* e) +void SMESHGUI_MergeDlg::keyPressEvent( QKeyEvent* e) { QDialog::keyPressEvent( e ); if ( e->isAccepted() ) @@ -1136,3 +1261,65 @@ void SMESHGUI_EditMeshDlg::keyPressEvent( QKeyEvent* e) ClickOnHelp(); } } + +//================================================================================= +// function : onTypeChanged() +// purpose : the type radio button management +//================================================================================= +void SMESHGUI_MergeDlg::onTypeChanged (int id) +{ + if (myTypeId == id) + return; + + myTypeId = id; + switch (id) + { + case 0: // automatic + myIdPreview->SetPointsLabeled(false); + SMESH::SetPointRepresentation(false); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySelectionMgr->clearFilters(); + if (myAction == 0) + GroupCoincidentWidget->hide(); + else + GroupCoincident->hide(); + GroupEdit->hide(); + break; + + case 1: // manual + SMESH::UpdateView(); + + // Costruction of the logical filter + SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (MESHorSUBMESH); + SMESH_TypeFilter* aSmeshGroupFilter = new SMESH_TypeFilter (GROUP); + + QList aListOfFilters; + if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter); + if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); + + myMeshOrSubMeshOrGroupFilter = + new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR); + + if (myAction == 0) { + GroupCoincidentWidget->show(); + SMESH::SetPointRepresentation(true); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(NodeSelection); + } + else { + GroupCoincident->show(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(CellSelection); + } + GroupEdit->show(); + break; + } + updateControls(); + + qApp->processEvents(); + updateGeometry(); + resize(10,10); + + SelectionIntoArgument(); +} diff --git a/src/SMESHGUI/SMESHGUI_EditMeshDlg.h b/src/SMESHGUI/SMESHGUI_MergeDlg.h similarity index 69% rename from src/SMESHGUI/SMESHGUI_EditMeshDlg.h rename to src/SMESHGUI/SMESHGUI_MergeDlg.h index 3b96bede4..dd9458d61 100644 --- a/src/SMESHGUI/SMESHGUI_EditMeshDlg.h +++ b/src/SMESHGUI/SMESHGUI_MergeDlg.h @@ -1,30 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_EditMeshDlg.h +// File : SMESHGUI_MergeDlg.h // Author : Open CASCADE S.A.S. // -#ifndef SMESHGUI_EDITMESHDLG_H -#define SMESHGUI_EDITMESHDLG_H +#ifndef SMESHGUI_MergeDlg_H +#define SMESHGUI_MergeDlg_H // SMESH includes #include "SMESH_SMESHGUI.hxx" @@ -49,6 +50,7 @@ class QPushButton; class QRadioButton; class QCheckBox; class QListWidget; +class QButtonGroup; class SMESHGUI; class SMESHGUI_SpinBox; class SMESH_Actor; @@ -63,16 +65,16 @@ namespace SMESH } //================================================================================= -// class : SMESHGUI_EditMeshDlg +// class : SMESHGUI_MergeDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_EditMeshDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_MergeDlg : public QDialog { Q_OBJECT; public: - SMESHGUI_EditMeshDlg( SMESHGUI*, int ); - ~SMESHGUI_EditMeshDlg(); + SMESHGUI_MergeDlg( SMESHGUI*, int ); + ~SMESHGUI_MergeDlg(); private: void Init(); @@ -83,10 +85,12 @@ private: void onEditGroup(); void FindGravityCenter( TColStd_MapOfInteger&, - std::list& ); + std::list& ); // add the centers of gravity of ElemsIdMap elements to the GrCentersXYZ list private: + typedef QList GrpList; + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ SVTK_Selector* mySelector; @@ -102,6 +106,7 @@ private: int myAction; bool myIsBusy; + int myTypeId; // Widgets QGroupBox* GroupConstructors; @@ -119,6 +124,7 @@ private: QLineEdit* LineEditMesh; QGroupBox* GroupCoincident; + QWidget* GroupCoincidentWidget; QLabel* TextLabelTolerance; SMESHGUI_SpinBox* SpinBoxTolerance; QPushButton* DetectButton; @@ -132,9 +138,18 @@ private: QPushButton* AddElemButton; QPushButton* RemoveElemButton; QPushButton* SetFirstButton; + + QGroupBox* GroupExclude; + QListWidget* ListExclude; + + QGroupBox* TypeBox; + QButtonGroup* GroupType; QString myHelpFileName; + QString myEntry; + GrpList myGroups; + private slots: void ClickOnOk(); void ClickOnCancel(); @@ -154,6 +169,7 @@ private: void SelectionIntoArgument(); void DeactivateActiveDialog(); void ActivateThisDialog(); + void onTypeChanged(int); }; -#endif // SMESHGUI_EDITMESHDLG_H +#endif // SMESHGUI_MergeDlg_H diff --git a/src/SMESHGUI/SMESHGUI_MeshDlg.cxx b/src/SMESHGUI/SMESHGUI_MeshDlg.cxx index b877f7fc0..beafb18b2 100644 --- a/src/SMESHGUI/SMESHGUI_MeshDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -112,9 +113,9 @@ SMESHGUI_MeshTab::SMESHGUI_MeshTab( QWidget* theParent ) // Connect signals and slots for ( int i = MainHyp; i <= AddHyp; i++ ) { - connect( myCreateHyp[ i ], SIGNAL( clicked() ), SLOT( onCreateHyp() ) ); - connect( myEditHyp[ i ], SIGNAL( clicked() ), SLOT( onEditHyp() ) ); - connect( myHyp[ i ], SIGNAL( activated( int ) ), SLOT( onHyp( int ) ) ); + connect( myCreateHyp[ i ], SIGNAL( clicked() ) , SLOT( onCreateHyp() ) ); + connect( myEditHyp[ i ] , SIGNAL( clicked() ) , SLOT( onEditHyp() ) ); + connect( myHyp[ i ] , SIGNAL( activated( int ) ), SLOT( onHyp( int ) ) ); } connect( myHyp[ Algo ], SIGNAL( activated( int ) ), SLOT( onHyp( int ) ) ); @@ -381,7 +382,7 @@ SMESHGUI_MeshDlg::SMESHGUI_MeshDlg( const bool theToCreate, const bool theIsMesh myHypoSetButton->setText( tr( "HYPOTHESES_SETS" ) ); myHypoSetButton->setEnabled( false ); myHypoSetButton->setSizePolicy( QSizePolicy::MinimumExpanding, - myHypoSetButton->sizePolicy().verticalPolicy() ); + myHypoSetButton->sizePolicy().verticalPolicy() ); // Fill layout QGridLayout* aLay = new QGridLayout( mainFrame() ); @@ -478,9 +479,13 @@ void SMESHGUI_MeshDlg::setMaxHypoDim( const int maxDim ) const int DIM = maxDim; for ( int dim = Dim0D; dim <= Dim3D; ++dim ) { bool enable = ( dim <= DIM ); - if ( !enable ) + if ( !enable ) { myTabs[ dim ]->reset(); - myTabWg->setTabEnabled( myTabWg->indexOf( myTabs[ dim ] ), enable ); + disableTab( dim ); + } + else { + enableTab( dim ); + } } // deselect desabled tab if ( !myTabWg->isTabEnabled( myTabWg->currentIndex() ) ) @@ -542,14 +547,14 @@ void SMESHGUI_MeshDlg::setGeomPopupEnabled( const bool enable ) myGeomPopup->addAction( tr("DIRECT_GEOM_SELECTION") )->setData( DIRECT_GEOM_INDEX ); myGeomPopup->addAction( tr("GEOM_BY_MESH_ELEM_SELECTION") )->setData( GEOM_BY_MESH_INDEX ); connect( myGeomPopup, SIGNAL( triggered( QAction* ) ), SLOT( onGeomPopup( QAction* ) ) ); - connect( selBtn, SIGNAL( toggled(bool) ), this, SLOT( onGeomSelectionButton(bool) )); + connect( selBtn, SIGNAL( toggled(bool) ), this, SLOT( onGeomSelectionButton(bool) )); } } else { disconnect( selBtn, SIGNAL( toggled(bool) ), this, SLOT( onGeomSelectionButton(bool) )); if ( myGeomPopup ) { - delete myGeomPopup; - myGeomPopup = 0; + delete myGeomPopup; + myGeomPopup = 0; } } } @@ -564,6 +569,7 @@ void SMESHGUI_MeshDlg::setGeomPopupEnabled( const bool enable ) //================================================================================ void SMESHGUI_MeshDlg::disableTab(const int theTabId) { myTabWg->setTabEnabled( myTabWg->indexOf( myTabs[ theTabId ] ), false ); + if ( theTabId == Dim3D ) myHypoSetButton->setEnabled( false ); } //================================================================================ @@ -574,6 +580,20 @@ void SMESHGUI_MeshDlg::disableTab(const int theTabId) { //================================================================================ void SMESHGUI_MeshDlg::enableTab(const int theTabId) { myTabWg->setTabEnabled( myTabWg->indexOf( myTabs[ theTabId ] ), true ); + if ( theTabId == Dim3D ) { + QMenu* aHypoSetPopup = myHypoSetButton->menu(); + myHypoSetButton->setEnabled( aHypoSetPopup && !aHypoSetPopup->actions().isEmpty() ); + } +} + +//================================================================================ +/*! + * \brief Check if tab enabled + * \param int - tab ID + */ +//================================================================================ +bool SMESHGUI_MeshDlg::isTabEnabled(const int theTabId) const { + return myTabWg->isTabEnabled( myTabWg->indexOf( myTabs[ theTabId ] ) ); } void SMESHGUI_MeshDlg::onGeomSelectionButton(bool isBtnOn) @@ -586,3 +606,12 @@ void SMESHGUI_MeshDlg::onGeomPopup( QAction* a ) { emit geomSelectionByMesh( a->data().toInt() == GEOM_BY_MESH_INDEX ); } + +int SMESHGUI_MeshDlg::getActiveObject() +{ + for (int i = 0; i < 3; ++i ) + if ( isObjectShown( i ) && + (( QToolButton* )objectWg( i, Btn ))->isChecked()) + return i; + return -1; +} diff --git a/src/SMESHGUI/SMESHGUI_MeshDlg.h b/src/SMESHGUI/SMESHGUI_MeshDlg.h index 34ee3a565..212449ff9 100644 --- a/src/SMESHGUI/SMESHGUI_MeshDlg.h +++ b/src/SMESHGUI/SMESHGUI_MeshDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -71,7 +72,8 @@ public: void setGeomPopupEnabled( const bool ); void disableTab(const int); void enableTab(const int); - + bool isTabEnabled(const int) const; + int getActiveObject(); signals: void hypoSet( const QString& ); diff --git a/src/SMESHGUI/SMESHGUI_MeshEditPreview.cxx b/src/SMESHGUI/SMESHGUI_MeshEditPreview.cxx index 8bed7191f..ac71ddbc4 100644 --- a/src/SMESHGUI/SMESHGUI_MeshEditPreview.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshEditPreview.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshEditPreview.cxx // Author : Open CASCADE S.A.S. @@ -72,6 +73,11 @@ SMESHGUI_MeshEditPreview::SMESHGUI_MeshEditPreview(SVTK_ViewWindow* theViewWindo myPreviewActor->VisibilityOn(); myPreviewActor->PickableOff(); + vtkFloatingPointType aFactor,aUnits; + myPreviewActor->SetResolveCoincidentTopology(true); + myPreviewActor->GetPolygonOffsetParameters(aFactor,aUnits); + myPreviewActor->SetPolygonOffsetParameters(aFactor,0.2*aUnits); + vtkFloatingPointType anRGB[3]; SMESH::GetColor( "SMESH", "selection_element_color", anRGB[0], anRGB[1], anRGB[2], QColor( 0, 170, 255 ) ); SetColor( anRGB[0], anRGB[1], anRGB[2] ); @@ -122,6 +128,7 @@ vtkIdType getCellType( const SMDSAbs_ElementType theType, else if ( theNbNodes == 4 ) return VTK_QUAD; else if ( theNbNodes == 6 ) return VTK_QUADRATIC_TRIANGLE; else if ( theNbNodes == 8 ) return VTK_QUADRATIC_QUAD; + else if ( theNbNodes == 9 ) return VTK_BIQUADRATIC_QUAD; else return VTK_EMPTY_CELL; case SMDSAbs_Volume: @@ -130,18 +137,11 @@ vtkIdType getCellType( const SMDSAbs_ElementType theType, else if ( theNbNodes == 5 ) return VTK_PYRAMID; else if ( theNbNodes == 6 ) return VTK_WEDGE; else if ( theNbNodes == 8 ) return VTK_HEXAHEDRON; - else if ( theNbNodes == 10 ) { - return VTK_QUADRATIC_TETRA; - } - else if ( theNbNodes == 20 ) { - return VTK_QUADRATIC_HEXAHEDRON; - } - else if ( theNbNodes==15 ) { - return VTK_QUADRATIC_WEDGE; - } - else if ( theNbNodes==13 ) { - return VTK_CONVEX_POINT_SET; - } + else if ( theNbNodes == 10 ) return VTK_QUADRATIC_TETRA; + else if ( theNbNodes == 20 ) return VTK_QUADRATIC_HEXAHEDRON; + else if ( theNbNodes == 27 ) return VTK_TRIQUADRATIC_HEXAHEDRON; + else if ( theNbNodes == 15 ) return VTK_QUADRATIC_WEDGE; + else if ( theNbNodes == 13 ) return VTK_QUADRATIC_PYRAMID;//VTK_CONVEX_POINT_SET; else return VTK_EMPTY_CELL; default: return VTK_EMPTY_CELL; diff --git a/src/SMESHGUI/SMESHGUI_MeshEditPreview.h b/src/SMESHGUI/SMESHGUI_MeshEditPreview.h index 1b5fb3c9a..4a0f840fe 100644 --- a/src/SMESHGUI/SMESHGUI_MeshEditPreview.h +++ b/src/SMESHGUI/SMESHGUI_MeshEditPreview.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshEditPreview.h // Author : Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_MeshInfo.cxx b/src/SMESHGUI/SMESHGUI_MeshInfo.cxx new file mode 100644 index 000000000..25ee42a9a --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshInfo.cxx @@ -0,0 +1,2175 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_MeshInfo.cxx +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#include "SMESHGUI_MeshInfo.h" + +#include "SMESH_Actor.h" +#include "SMESHGUI.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMDSAbs_ElementType.hxx" +#include "SMDS_Mesh.hxx" +#include "SMDS_BallElement.hxx" +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utilities.h" + +#include +#include CORBA_SERVER_HEADER(GEOM_Gen) + +const int SPACING = 6; +const int MARGIN = 9; +const int MAXITEMS = 10; +const int GROUPS_ID = 100; +const int SUBMESHES_ID = 200; + +/*! + \class ExtraWidget + \internal +*/ + +class ExtraWidget : public QWidget +{ +public: + ExtraWidget( QWidget*, bool = false ); + ~ExtraWidget(); + + void updateControls( int, int, int = MAXITEMS ); + +public: + QLabel* current; + QPushButton* prev; + QPushButton* next; + bool brief; +}; + +ExtraWidget::ExtraWidget( QWidget* parent, bool b ) : QWidget( parent ), brief( b ) +{ + current = new QLabel( this ); + current->setAlignment( Qt::AlignRight | Qt::AlignVCenter ); + prev = new QPushButton( tr( "<<" ), this ); + next = new QPushButton( tr( ">>" ), this ); + QHBoxLayout* hbl = new QHBoxLayout( this ); + hbl->setContentsMargins( 0, SPACING, 0, 0 ); + hbl->setSpacing( SPACING ); + hbl->addStretch(); + hbl->addWidget( current ); + hbl->addWidget( prev ); + hbl->addWidget( next ); +} + +ExtraWidget::~ExtraWidget() +{ +} + +void ExtraWidget::updateControls( int total, int index, int blockSize ) +{ + setVisible( total > blockSize ); + QString format = brief ? QString( "%1-%2 / %3" ) : SMESHGUI_MeshInfoDlg::tr( "X_FROM_Y_ITEMS_SHOWN" ); + current->setText( format.arg( index*blockSize+1 ).arg( qMin( index*blockSize+blockSize, total ) ).arg( total ) ); + prev->setEnabled( index > 0 ); + next->setEnabled( (index+1)*blockSize < total ); +} + +/*! + \class SMESHGUI_MeshInfo + \brief Base mesh information widget + + Displays the base information about mesh object: mesh, sub-mesh, group or arbitrary ID source. +*/ + +/*! + \brief Constructor. + \param parent parent widget +*/ +SMESHGUI_MeshInfo::SMESHGUI_MeshInfo( QWidget* parent ) + : QFrame( parent ), myWidgets( iElementsEnd ) +{ + setFrameStyle( StyledPanel | Sunken ); + + QGridLayout* l = new QGridLayout( this ); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + + int index = 0; + + // object + QLabel* aNameLab = new QLabel( tr( "NAME_LAB" ), this ); + QLabel* aName = createField(); + aName->setMinimumWidth( 150 ); + QLabel* aObjLab = new QLabel( tr( "OBJECT_LAB" ), this ); + QLabel* aObj = createField(); + aObj->setMinimumWidth( 150 ); + myWidgets[ index++ ] << aNameLab << aName; + myWidgets[ index++ ] << aObjLab << aObj; + + // nodes + QWidget* aNodesLine = createLine(); + QLabel* aNodesLab = new QLabel( tr( "NODES_LAB" ), this ); + QLabel* aNodes = createField(); + myWidgets[ index++ ] << aNodesLine; + myWidgets[ index++ ] << aNodesLab << aNodes; + + // elements + QWidget* aElemLine = createLine(); + QLabel* aElemLab = new QLabel( tr( "ELEMENTS_LAB" ), this ); + QLabel* aElemTotal = new QLabel( tr( "TOTAL_LAB" ), this ); + QLabel* aElemLin = new QLabel( tr( "LINEAR_LAB" ), this ); + QLabel* aElemQuad = new QLabel( tr( "QUADRATIC_LAB" ), this ); + myWidgets[ index++ ] << aElemLine; + myWidgets[ index++ ] << aElemLab << aElemTotal << aElemLin << aElemQuad; + + // ... 0D elements + QWidget* a0DLine = createLine(); + QLabel* a0DLab = new QLabel( tr( "0D_LAB" ), this ); + QLabel* a0DTotal = createField(); + myWidgets[ index++ ] << a0DLine; + myWidgets[ index++ ] << a0DLab << a0DTotal; + + // ... Ball elements + QWidget* aBallLine = createLine(); + QLabel* aBallLab = new QLabel( tr( "BALL_LAB" ), this ); + QLabel* aBallTotal = createField(); + myWidgets[ index++ ] << aBallLine; + myWidgets[ index++ ] << aBallLab << aBallTotal; + + // ... 1D elements + QWidget* a1DLine = createLine(); + QLabel* a1DLab = new QLabel( tr( "1D_LAB" ), this ); + QLabel* a1DTotal = createField(); + QLabel* a1DLin = createField(); + QLabel* a1DQuad = createField(); + myWidgets[ index++ ] << a1DLine; + myWidgets[ index++ ] << a1DLab << a1DTotal << a1DLin << a1DQuad; + + // ... 2D elements + QWidget* a2DLine = createLine(); + QLabel* a2DLab = new QLabel( tr( "2D_LAB" ), this ); + QLabel* a2DTotal = createField(); + QLabel* a2DLin = createField(); + QLabel* a2DQuad = createField(); + QLabel* a2DTriLab = new QLabel( tr( "TRIANGLES_LAB" ), this ); + QLabel* a2DTriTotal = createField(); + QLabel* a2DTriLin = createField(); + QLabel* a2DTriQuad = createField(); + QLabel* a2DQuaLab = new QLabel( tr( "QUADRANGLES_LAB" ), this ); + QLabel* a2DQuaTotal = createField(); + QLabel* a2DQuaLin = createField(); + QLabel* a2DQuaQuad = createField(); + QLabel* a2DPolLab = new QLabel( tr( "POLYGONS_LAB" ), this ); + QLabel* a2DPolTotal = createField(); + myWidgets[ index++ ] << a2DLine; + myWidgets[ index++ ] << a2DLab << a2DTotal << a2DLin << a2DQuad; + myWidgets[ index++ ] << a2DTriLab << a2DTriTotal << a2DTriLin << a2DTriQuad; + myWidgets[ index++ ] << a2DQuaLab << a2DQuaTotal << a2DQuaLin << a2DQuaQuad; + myWidgets[ index++ ] << a2DPolLab << a2DPolTotal; + + // ... 3D elements + QWidget* a3DLine = createLine(); + QLabel* a3DLab = new QLabel( tr( "3D_LAB" ), this ); + QLabel* a3DTotal = createField(); + QLabel* a3DLin = createField(); + QLabel* a3DQuad = createField(); + QLabel* a3DTetLab = new QLabel( tr( "TETRAHEDRONS_LAB" ), this ); + QLabel* a3DTetTotal = createField(); + QLabel* a3DTetLin = createField(); + QLabel* a3DTetQuad = createField(); + QLabel* a3DHexLab = new QLabel( tr( "HEXAHEDONRS_LAB" ), this ); + QLabel* a3DHexTotal = createField(); + QLabel* a3DHexLin = createField(); + QLabel* a3DHexQuad = createField(); + QLabel* a3DPyrLab = new QLabel( tr( "PYRAMIDS_LAB" ), this ); + QLabel* a3DPyrTotal = createField(); + QLabel* a3DPyrLin = createField(); + QLabel* a3DPyrQuad = createField(); + QLabel* a3DPriLab = new QLabel( tr( "PRISMS_LAB" ), this ); + QLabel* a3DPriTotal = createField(); + QLabel* a3DPriLin = createField(); + QLabel* a3DPriQuad = createField(); + QLabel* a3DHexPriLab = new QLabel( tr( "HEX_PRISMS_LAB" ), this ); + QLabel* a3DHexPriTotal = createField(); + QLabel* a3DPolLab = new QLabel( tr( "POLYHEDRONS_LAB" ), this ); + QLabel* a3DPolTotal = createField(); + myWidgets[ index++ ] << a3DLine; + myWidgets[ index++ ] << a3DLab << a3DTotal << a3DLin << a3DQuad; + myWidgets[ index++ ] << a3DTetLab << a3DTetTotal << a3DTetLin << a3DTetQuad; + myWidgets[ index++ ] << a3DHexLab << a3DHexTotal << a3DHexLin << a3DHexQuad; + myWidgets[ index++ ] << a3DPyrLab << a3DPyrTotal << a3DPyrLin << a3DPyrQuad; + myWidgets[ index++ ] << a3DPriLab << a3DPriTotal << a3DPriLin << a3DPriQuad; + myWidgets[ index++ ] << a3DHexPriLab << a3DHexPriTotal; + myWidgets[ index++ ] << a3DPolLab << a3DPolTotal; + + myLoadBtn = new QPushButton( tr( "BUT_LOAD_MESH" ), this ); + myLoadBtn->setAutoDefault( true ); + connect( myLoadBtn, SIGNAL( clicked() ), this, SLOT( loadMesh() ) ); + + setFontAttributes( aNameLab, Bold ); + setFontAttributes( aObjLab, Bold ); + setFontAttributes( aNodesLab, Bold ); + setFontAttributes( aElemLab, Bold ); + setFontAttributes( aElemTotal, Italic ); + setFontAttributes( aElemLin, Italic ); + setFontAttributes( aElemQuad, Italic ); + setFontAttributes( a0DLab, Bold ); + setFontAttributes( aBallLab, Bold ); + setFontAttributes( a1DLab, Bold ); + setFontAttributes( a2DLab, Bold ); + setFontAttributes( a3DLab, Bold ); + + l->addWidget( aNameLab, 0, 0 ); + l->addWidget( aName, 0, 1, 1, 3 ); + l->addWidget( aObjLab, 1, 0 ); + l->addWidget( aObj, 1, 1, 1, 3 ); + l->addWidget( aNodesLine, 2, 0, 1, 4 ); + l->addWidget( aNodesLab, 3, 0 ); + l->addWidget( aNodes, 3, 1 ); + l->addWidget( aElemLine, 4, 0, 1, 4 ); + l->addWidget( aElemLab, 5, 0 ); + l->addWidget( aElemTotal, 5, 1 ); + l->addWidget( aElemLin, 5, 2 ); + l->addWidget( aElemQuad, 5, 3 ); + l->addWidget( a0DLine, 6, 1, 1, 3 ); + l->addWidget( a0DLab, 7, 0 ); + l->addWidget( a0DTotal, 7, 1 ); + l->addWidget( aBallLine, 8, 1, 1, 3 ); + l->addWidget( aBallLab, 9, 0 ); + l->addWidget( aBallTotal, 9, 1 ); + l->addWidget( a1DLine, 10, 1, 1, 3 ); + l->addWidget( a1DLab, 11, 0 ); + l->addWidget( a1DTotal, 11, 1 ); + l->addWidget( a1DLin, 11, 2 ); + l->addWidget( a1DQuad, 11, 3 ); + l->addWidget( a2DLine, 12, 1, 1, 3 ); + l->addWidget( a2DLab, 13, 0 ); + l->addWidget( a2DTotal, 13, 1 ); + l->addWidget( a2DLin, 13, 2 ); + l->addWidget( a2DQuad, 13, 3 ); + l->addWidget( a2DTriLab, 14, 0 ); + l->addWidget( a2DTriTotal, 14, 1 ); + l->addWidget( a2DTriLin, 14, 2 ); + l->addWidget( a2DTriQuad, 14, 3 ); + l->addWidget( a2DQuaLab, 15, 0 ); + l->addWidget( a2DQuaTotal, 15, 1 ); + l->addWidget( a2DQuaLin, 15, 2 ); + l->addWidget( a2DQuaQuad, 15, 3 ); + l->addWidget( a2DPolLab, 16, 0 ); + l->addWidget( a2DPolTotal, 16, 1 ); + l->addWidget( a3DLine, 17, 1, 1, 3 ); + l->addWidget( a3DLab, 18, 0 ); + l->addWidget( a3DTotal, 18, 1 ); + l->addWidget( a3DLin, 18, 2 ); + l->addWidget( a3DQuad, 18, 3 ); + l->addWidget( a3DTetLab, 19, 0 ); + l->addWidget( a3DTetTotal, 19, 1 ); + l->addWidget( a3DTetLin, 19, 2 ); + l->addWidget( a3DTetQuad, 19, 3 ); + l->addWidget( a3DHexLab, 20, 0 ); + l->addWidget( a3DHexTotal, 20, 1 ); + l->addWidget( a3DHexLin, 20, 2 ); + l->addWidget( a3DHexQuad, 20, 3 ); + l->addWidget( a3DPyrLab, 21, 0 ); + l->addWidget( a3DPyrTotal, 21, 1 ); + l->addWidget( a3DPyrLin, 21, 2 ); + l->addWidget( a3DPyrQuad, 21, 3 ); + l->addWidget( a3DPriLab, 22, 0 ); + l->addWidget( a3DPriTotal, 22, 1 ); + l->addWidget( a3DPriLin, 22, 2 ); + l->addWidget( a3DPriQuad, 22, 3 ); + l->addWidget( a3DHexPriLab, 23, 0 ); + l->addWidget( a3DHexPriTotal, 23, 1 ); + l->addWidget( a3DPolLab, 24, 0 ); + l->addWidget( a3DPolTotal, 24, 1 ); + l->addWidget( myLoadBtn, 25, 1, 1, 3 ); + l->setColumnStretch( 0, 0 ); + l->setColumnStretch( 1, 5 ); + l->setColumnStretch( 2, 5 ); + l->setColumnStretch( 3, 5 ); + l->setRowStretch( 23, 5 ); + + clear(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_MeshInfo::~SMESHGUI_MeshInfo() +{ +} + +/*! + \brief Show information on the mesh object. + \param obj object being processed (mesh, sub-mesh, group, ID source) +*/ +void SMESHGUI_MeshInfo::showInfo( SMESH::SMESH_IDSource_ptr obj ) +{ + clear(); + if ( !CORBA::is_nil( obj ) ) { + _PTR(SObject) sobj = SMESH::ObjectToSObject( obj ); + if ( sobj ) + myWidgets[iName][iSingle]->setProperty( "text", sobj->GetName().c_str() ); + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( obj ); + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( obj ); + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( obj ); + if ( !aMesh->_is_nil() ) { + myWidgets[iObject][iSingle]->setProperty( "text", tr( "OBJECT_MESH" ) ); + } + else if ( !aSubMesh->_is_nil() ) { + myWidgets[iObject][iSingle]->setProperty( "text", tr( "OBJECT_SUBMESH" ) ); + } + else if ( !aGroup->_is_nil() ) { + QString objType; + switch( aGroup->GetType() ) { + case SMESH::NODE: + objType = tr( "OBJECT_GROUP_NODES" ); + break; + case SMESH::EDGE: + objType = tr( "OBJECT_GROUP_EDGES" ); + break; + case SMESH::FACE: + objType = tr( "OBJECT_GROUP_FACES" ); + break; + case SMESH::VOLUME: + objType = tr( "OBJECT_GROUP_VOLUMES" ); + break; + case SMESH::ELEM0D: + objType = tr( "OBJECT_GROUP_0DELEMS" ); + break; + case SMESH::BALL: + objType = tr( "OBJECT_GROUP_BALLS" ); + break; + default: + objType = tr( "OBJECT_GROUP" ); + break; + } + myWidgets[iObject][iSingle]->setProperty( "text", objType ); + } + SMESH::long_array_var info = obj->GetMeshInfo(); + myWidgets[iNodes][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_Node] ) ); + myWidgets[i0D][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_0D] ) ); + myWidgets[iBalls][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_Ball] ) ); + long nbEdges = info[SMDSEntity_Edge] + info[SMDSEntity_Quad_Edge]; + myWidgets[i1D][iTotal] ->setProperty( "text", QString::number( nbEdges ) ); + myWidgets[i1D][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Edge] ) ); + myWidgets[i1D][iQuadratic]->setProperty( "text", QString::number( info[SMDSEntity_Quad_Edge] ) ); + long nbTriangles = info[SMDSEntity_Triangle] + info[SMDSEntity_Quad_Triangle]; + long nbQuadrangles = info[SMDSEntity_Quadrangle] + info[SMDSEntity_Quad_Quadrangle] + info[SMDSEntity_BiQuad_Quadrangle]; + long nb2DLinear = info[SMDSEntity_Triangle] + info[SMDSEntity_Quadrangle] + info[SMDSEntity_Polygon]; + long nb2DQuadratic = info[SMDSEntity_Quad_Triangle] + info[SMDSEntity_Quad_Quadrangle] + info[SMDSEntity_BiQuad_Quadrangle]; + myWidgets[i2D][iTotal] ->setProperty( "text", QString::number( nb2DLinear + nb2DQuadratic ) ); + myWidgets[i2D][iLinear] ->setProperty( "text", QString::number( nb2DLinear ) ); + myWidgets[i2D][iQuadratic] ->setProperty( "text", QString::number( nb2DQuadratic ) ); + myWidgets[i2DTriangles][iTotal] ->setProperty( "text", QString::number( nbTriangles ) ); + myWidgets[i2DTriangles][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Triangle] ) ); + myWidgets[i2DTriangles][iQuadratic] ->setProperty( "text", QString::number( info[SMDSEntity_Quad_Triangle] ) ); + myWidgets[i2DQuadrangles][iTotal] ->setProperty( "text", QString::number( nbQuadrangles ) ); + myWidgets[i2DQuadrangles][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Quadrangle] ) ); + myWidgets[i2DQuadrangles][iQuadratic]->setProperty( "text", QString::number( info[SMDSEntity_Quad_Quadrangle] + info[SMDSEntity_BiQuad_Quadrangle] )); + myWidgets[i2DPolygons][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_Polygon] ) ); + long nbTetrahedrons = info[SMDSEntity_Tetra] + info[SMDSEntity_Quad_Tetra]; + long nbHexahedrons = info[SMDSEntity_Hexa] + info[SMDSEntity_Quad_Hexa] + info[SMDSEntity_TriQuad_Hexa]; + long nbPyramids = info[SMDSEntity_Pyramid] + info[SMDSEntity_Quad_Pyramid]; + long nbPrisms = info[SMDSEntity_Penta] + info[SMDSEntity_Quad_Penta]; + long nb3DLinear = info[SMDSEntity_Tetra] + info[SMDSEntity_Hexa] + info[SMDSEntity_Pyramid] + info[SMDSEntity_Penta] + info[SMDSEntity_Polyhedra] + info[SMDSEntity_Hexagonal_Prism]; + long nb3DQuadratic = info[SMDSEntity_Quad_Tetra] + info[SMDSEntity_Quad_Hexa] + info[SMDSEntity_TriQuad_Hexa] + info[SMDSEntity_Quad_Pyramid] + info[SMDSEntity_Quad_Penta]; + myWidgets[i3D][iTotal] ->setProperty( "text", QString::number( nb3DLinear + nb3DQuadratic ) ); + myWidgets[i3D][iLinear] ->setProperty( "text", QString::number( nb3DLinear ) ); + myWidgets[i3D][iQuadratic] ->setProperty( "text", QString::number( nb3DQuadratic ) ); + myWidgets[i3DTetrahedrons][iTotal] ->setProperty( "text", QString::number( nbTetrahedrons ) ); + myWidgets[i3DTetrahedrons][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Tetra] ) ); + myWidgets[i3DTetrahedrons][iQuadratic]->setProperty( "text", QString::number( info[SMDSEntity_Quad_Tetra] ) ); + myWidgets[i3DHexahedrons][iTotal] ->setProperty( "text", QString::number( nbHexahedrons ) ); + myWidgets[i3DHexahedrons][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Hexa] ) ); + myWidgets[i3DHexahedrons][iQuadratic] ->setProperty( "text", QString::number( info[SMDSEntity_Quad_Hexa] + info[SMDSEntity_TriQuad_Hexa] ) ); + myWidgets[i3DPyramids][iTotal] ->setProperty( "text", QString::number( nbPyramids ) ); + myWidgets[i3DPyramids][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Pyramid] ) ); + myWidgets[i3DPyramids][iQuadratic] ->setProperty( "text", QString::number( info[SMDSEntity_Quad_Pyramid] ) ); + myWidgets[i3DPrisms][iTotal] ->setProperty( "text", QString::number( nbPrisms ) ); + myWidgets[i3DPrisms][iLinear] ->setProperty( "text", QString::number( info[SMDSEntity_Penta] ) ); + myWidgets[i3DPrisms][iQuadratic] ->setProperty( "text", QString::number( info[SMDSEntity_Quad_Penta] ) ); + myWidgets[i3DHexaPrisms][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_Hexagonal_Prism] ) ); + myWidgets[i3DPolyhedrons][iTotal] ->setProperty( "text", QString::number( info[SMDSEntity_Polyhedra] ) ); + + // before full loading from study file, type of elements in a sub-mesh can't be defined + // in some cases + bool infoOK = obj->IsMeshInfoCorrect(); + myLoadBtn->setVisible( !infoOK ); + if ( !infoOK ) + { + // two options: + // 1. Type of 2D or 3D elements is unknown but their nb is OK (for a sub-mesh) + // 2. No info at all (for a group on geom or filter) + bool hasAnyInfo = false; + for ( size_t i = 0; i < info->length() && !hasAnyInfo; ++i ) + hasAnyInfo = info[i]; + if ( hasAnyInfo ) // believe it is a sub-mesh + { + if ( nb2DLinear + nb2DQuadratic > 0 ) + { + myWidgets[i2D][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2D][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iTotal] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iTotal] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iQuadratic]->setProperty( "text", "?" ); + myWidgets[i2DPolygons][iTotal] ->setProperty( "text", "?" ); + } + else if ( nb3DLinear + nb3DQuadratic > 0 ) + { + myWidgets[i3D][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3D][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iQuadratic]->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DHexaPrisms][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPolyhedrons][iTotal] ->setProperty( "text", "?" ); + } + } + else + { + myWidgets[iNodes][iTotal] ->setProperty( "text", "?" ); + myWidgets[i0D][iTotal] ->setProperty( "text", "?" ); + myWidgets[iBalls][iTotal] ->setProperty( "text", "?" ); + myWidgets[i1D][iTotal] ->setProperty( "text", "?" ); + myWidgets[i1D][iLinear] ->setProperty( "text", "?" ); + myWidgets[i1D][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2D][iTotal] ->setProperty( "text", "?" ); + myWidgets[i2D][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2D][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iTotal] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2DTriangles][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iTotal] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iLinear] ->setProperty( "text", "?" ); + myWidgets[i2DQuadrangles][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i2DPolygons][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3D][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3D][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3D][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DTetrahedrons][iQuadratic]->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DHexahedrons][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DPyramids][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iLinear] ->setProperty( "text", "?" ); + myWidgets[i3DPrisms][iQuadratic] ->setProperty( "text", "?" ); + myWidgets[i3DHexaPrisms][iTotal] ->setProperty( "text", "?" ); + myWidgets[i3DPolyhedrons][iTotal] ->setProperty( "text", "?" ); + } + } + } +} + +/*! + \brief Load mesh from a study file +*/ +void SMESHGUI_MeshInfo::loadMesh() +{ + SUIT_OverrideCursor wc; + + SALOME_ListIO selected; + SMESHGUI::selectionMgr()->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) IO = selected.First(); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + SMESH::SMESH_Mesh_var mesh = obj->GetMesh(); + if ( !mesh->_is_nil() ) + { + mesh->Load(); + showInfo( obj ); + } + } + } +} + +/*! + \brief Reset the widget to the initial state (nullify all fields). +*/ +void SMESHGUI_MeshInfo::clear() +{ + myWidgets[iName][iSingle] ->setProperty( "text", QString() ); + myWidgets[iObject][iSingle] ->setProperty( "text", QString() ); + myWidgets[iNodes][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i0D][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[iBalls][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i1D][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i1D][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i1D][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2D][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2D][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2D][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DTriangles][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DTriangles][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DTriangles][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DQuadrangles][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DQuadrangles][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DQuadrangles][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i2DPolygons][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3D][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3D][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3D][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DTetrahedrons][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DTetrahedrons][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DTetrahedrons][iQuadratic]->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DHexahedrons][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DHexahedrons][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DHexahedrons][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPyramids][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPyramids][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPyramids][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPrisms][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPrisms][iLinear] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPrisms][iQuadratic] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DHexaPrisms][iTotal] ->setProperty( "text", QString::number( 0 ) ); + myWidgets[i3DPolyhedrons][iTotal] ->setProperty( "text", QString::number( 0 ) ); +} + +/*! + \brief Create info field + \return new info field +*/ +QLabel* SMESHGUI_MeshInfo::createField() +{ + QLabel* lab = new QLabel( this ); + lab->setFrameStyle( StyledPanel | Sunken ); + lab->setAlignment( Qt::AlignCenter ); + lab->setAutoFillBackground( true ); + QPalette pal = lab->palette(); + pal.setColor( QPalette::Window, QApplication::palette().color( QPalette::Active, QPalette::Base ) ); + lab->setPalette( pal ); + lab->setMinimumWidth( 70 ); + return lab; +} + +/*! + \brief Create horizontal rule. + \return new line object +*/ +QWidget* SMESHGUI_MeshInfo::createLine() +{ + QFrame* line = new QFrame( this ); + line->setFrameStyle( HLine | Sunken ); + return line; +} + +/*! + \brief Change widget font attributes (bold, italic, ...). + \param w widget + \param attr font attributes (XORed flags) + \param val value to be set to attributes +*/ +void SMESHGUI_MeshInfo::setFontAttributes( QWidget* w, int attr, bool val ) +{ + if ( w && attr ) { + QFont f = w->font(); + if ( attr & Bold ) f.setBold( val ); + if ( attr & Italic ) f.setItalic( val ); + w->setFont( f ); + } +} + +/*! + \brief Show/hide group(s) of fields. + \param start beginning of the block + \param end end of the block + \param on visibility flag +*/ +void SMESHGUI_MeshInfo::setFieldsVisible( int start, int end, bool on ) +{ + start = qMax( 0, start ); + end = qMin( end, (int)iElementsEnd ); + for ( int i = start; i < end; i++ ) { + wlist wl = myWidgets[i]; + foreach ( QWidget* w, wl ) w->setVisible( on ); + } +} + +/*! + \class SMESHGUI_ElemInfo + \brief Base class for the mesh element information widget. +*/ + +/*! + \brief Constructor + \param parent parent widget +*/ +SMESHGUI_ElemInfo::SMESHGUI_ElemInfo( QWidget* parent ) +: QWidget( parent ), myActor( 0 ), myIsElement( -1 ) +{ + myFrame = new QWidget( this ); + myExtra = new ExtraWidget( this ); + QVBoxLayout* vbl = new QVBoxLayout( this ); + vbl->setMargin( 0 ); + vbl->setSpacing( 0 ); + vbl->addWidget( myFrame ); + vbl->addWidget( myExtra ); + connect( myExtra->prev, SIGNAL( clicked() ), this, SLOT( showPrevious() ) ); + connect( myExtra->next, SIGNAL( clicked() ), this, SLOT( showNext() ) ); + clear(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_ElemInfo::~SMESHGUI_ElemInfo() +{ +} + +/*! + \brief Set mesh data source (actor) + \param actor mesh object actor +*/ +void SMESHGUI_ElemInfo::setSource( SMESH_Actor* actor ) +{ + if ( myActor != actor ) { + myActor = actor; + myIsElement = -1; + clear(); + } +} + +/*! + \brief Show mesh element information + \param id mesh node / element ID + \param isElem show mesh element information if \c true or mesh node information if \c false +*/ +void SMESHGUI_ElemInfo::showInfo( long id, bool isElem ) +{ + QSet ids; + ids << id; + showInfo( ids, isElem ); +} + +/*! + \brief Show mesh element information + \param ids mesh nodes / elements identifiers + \param isElem show mesh element information if \c true or mesh node information if \c false +*/ +void SMESHGUI_ElemInfo::showInfo( QSet ids, bool isElem ) +{ + QList newIds = ids.toList(); + qSort( newIds ); + if ( myIDs == newIds && myIsElement == isElem ) return; + + myIDs = newIds; + myIsElement = isElem; + myIndex = 0; + updateControls(); + information( myIDs.mid( myIndex*MAXITEMS, MAXITEMS ) ); +} + +/*! + \brief Clear mesh element information widget +*/ +void SMESHGUI_ElemInfo::clear() +{ + myIDs.clear(); + myIndex = 0; + clearInternal(); + updateControls(); +} + +/*! + \brief Get central area widget + \return central widget +*/ +QWidget* SMESHGUI_ElemInfo::frame() const +{ + return myFrame; +} + +/*! + \brief Get actor + \return actor being used +*/ +SMESH_Actor* SMESHGUI_ElemInfo::actor() const +{ + return myActor; +} + +/*! + \brief Get current info mode. + \return \c true if mesh element information is shown or \c false if node information is shown +*/ +bool SMESHGUI_ElemInfo::isElements() const +{ + return myIsElement; +} + +/*! + \fn void SMESHGUI_ElemInfo::information( const QList& ids ) + \brief Show information on the specified nodes / elements + + This function is to be redefined in sub-classes. + + \param ids nodes / elements identifiers information is to be shown on +*/ + +/*! + \brief Internal clean-up (reset widget) +*/ +void SMESHGUI_ElemInfo::clearInternal() +{ +} + +/*! + \brief Get node connectivity + \param node mesh node + \return node connectivity map +*/ +SMESHGUI_ElemInfo::Connectivity SMESHGUI_ElemInfo::nodeConnectivity( const SMDS_MeshNode* node ) +{ + Connectivity elmap; + if ( node ) { + SMDS_ElemIteratorPtr it = node->GetInverseElementIterator(); + while ( it && it->more() ) { + const SMDS_MeshElement* ne = it->next(); + elmap[ ne->GetType() ] << ne->GetID(); + } + } + return elmap; +} + +/*! + \brief Format connectivity data to string representation + \param connectivity connetivity map + \param type element type + \return string representation of the connectivity +*/ +QString SMESHGUI_ElemInfo::formatConnectivity( Connectivity connectivity, int type ) +{ + QStringList str; + if ( connectivity.contains( type ) ) { + QList elements = connectivity[ type ]; + qSort( elements ); + foreach( int id, elements ) + str << QString::number( id ); + } + return str.join( " " ); +} + +/*! + \brief Calculate gravity center of the mesh element + \param element mesh element +*/ +SMESHGUI_ElemInfo::XYZ SMESHGUI_ElemInfo::gravityCenter( const SMDS_MeshElement* element ) +{ + XYZ xyz; + if ( element ) { + SMDS_ElemIteratorPtr nodeIt = element->nodesIterator(); + while ( nodeIt->more() ) { + const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + xyz.add( node->X(), node->Y(), node->Z() ); + } + xyz.divide( element->NbNodes() ); + } + return xyz; +} + +/*! + \brief This slot is called from "Show Previous" button click. + Shows information on the previous group of the items. +*/ +void SMESHGUI_ElemInfo::showPrevious() +{ + myIndex = qMax( 0, myIndex-1 ); + updateControls(); + information( myIDs.mid( myIndex*MAXITEMS, MAXITEMS ) ); +} + +/*! + \brief This slot is called from "Show Next" button click. + Shows information on the next group of the items. +*/ +void SMESHGUI_ElemInfo::showNext() +{ + myIndex = qMin( myIndex+1, myIDs.count() / MAXITEMS ); + updateControls(); + information( myIDs.mid( myIndex*MAXITEMS, MAXITEMS ) ); +} + +/*! + \brief Update widgets state +*/ +void SMESHGUI_ElemInfo::updateControls() +{ + myExtra->updateControls( myIDs.count(), myIndex ); +} + +/*! + \class SMESHGUI_SimpleElemInfo + \brief Represents mesh element information in the simple text area. +*/ + +/*! + \brief Constructor + \param parent parent widget +*/ +SMESHGUI_SimpleElemInfo::SMESHGUI_SimpleElemInfo( QWidget* parent ) +: SMESHGUI_ElemInfo( parent ) +{ + myInfo = new QTextBrowser( frame() ); + QVBoxLayout* l = new QVBoxLayout( frame() ); + l->setMargin( 0 ); + l->addWidget( myInfo ); +} + +/*! + \brief Show mesh element information + \param ids mesh nodes / elements identifiers +*/ +void SMESHGUI_SimpleElemInfo::information( const QList& ids ) +{ + clearInternal(); + + if ( actor() ) { + int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 ); + foreach ( long id, ids ) { + if ( !isElements() ) { + // + // show node info + // + const SMDS_MeshNode* node = actor()->GetObject()->GetMesh()->FindNode( id ); + if ( !node ) return; + + // node ID + myInfo->append( QString( "%1 #%2" ).arg( tr( "NODE" ) ).arg( id ) ); + // separator + myInfo->append( "" ); + // coordinates + myInfo->append( QString( "%1: (%2, %3, %4)" ).arg( tr( "COORDINATES" ) ). + arg( node->X(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ). + arg( node->Y(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ). + arg( node->Z(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // separator + myInfo->append( "" ); + // connectivity + Connectivity connectivity = nodeConnectivity( node ); + if ( !connectivity.isEmpty() ) { + myInfo->append( QString( "%1:" ).arg( tr( "CONNECTIVITY" ) ) ); + QString con = formatConnectivity( connectivity, SMDSAbs_0DElement ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "0D_ELEMENTS" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Edge ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "EDGES" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Ball ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "BALL_ELEMENTS" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Face ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "FACES" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Volume ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "VOLUMES" ) ).arg( con ) ); + } + else { + myInfo->append( QString( "%1" ).arg( tr( "FREE_NODE" ) ).arg( id ) ); + } + } + else { + // + // show element info + // + const SMDS_MeshElement* e = actor()->GetObject()->GetMesh()->FindElement( id ); + if ( !e ) return; + + // element ID && type + QString stype; + switch( e->GetType() ) { + case SMDSAbs_0DElement: + stype = tr( "0D ELEMENT" ); break; + case SMDSAbs_Ball: + stype = tr( "BALL" ); break; + case SMDSAbs_Edge: + stype = tr( "EDGE" ); break; + case SMDSAbs_Face: + stype = tr( "FACE" ); break; + case SMDSAbs_Volume: + stype = tr( "VOLUME" ); break; + default: + break; + } + if ( stype.isEmpty() ) return; + myInfo->append( QString( "%1 #%2" ).arg( stype ).arg( id ) ); + // separator + myInfo->append( "" ); + // geometry type + QString gtype; + switch( e->GetEntityType() ) { + case SMDSEntity_Triangle: + case SMDSEntity_Quad_Triangle: + gtype = tr( "TRIANGLE" ); break; + case SMDSEntity_Quadrangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + gtype = tr( "QUADRANGLE" ); break; + case SMDSEntity_Polygon: + case SMDSEntity_Quad_Polygon: + gtype = tr( "POLYGON" ); break; + case SMDSEntity_Tetra: + case SMDSEntity_Quad_Tetra: + gtype = tr( "TETRAHEDRON" ); break; + case SMDSEntity_Pyramid: + case SMDSEntity_Quad_Pyramid: + gtype = tr( "PYRAMID" ); break; + case SMDSEntity_Hexa: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + gtype = tr( "HEXAHEDRON" ); break; + case SMDSEntity_Penta: + case SMDSEntity_Quad_Penta: + gtype = tr( "PRISM" ); break; + case SMDSEntity_Hexagonal_Prism: + gtype = tr( "HEX_PRISM" ); break; + case SMDSEntity_Polyhedra: + case SMDSEntity_Quad_Polyhedra: + gtype = tr( "POLYHEDRON" ); break; + default: + break; + } + if ( !gtype.isEmpty() ) + myInfo->append( QString( "%1: %2" ).arg( tr( "TYPE" ) ).arg( gtype ) ); + // quadratic flag and gravity center (any element except 0D) + if ( e->GetEntityType() > SMDSEntity_0D && e->GetEntityType() < SMDSEntity_Ball ) { + // quadratic flag + myInfo->append( QString( "%1? %2" ).arg( tr( "QUADRATIC" ) ).arg( e->IsQuadratic() ? tr( "YES" ) : tr( "NO" ) ) ); + // separator + myInfo->append( "" ); + // gravity center + XYZ gc = gravityCenter( e ); + myInfo->append( QString( "%1: (%2, %3, %4)" ).arg( tr( "GRAVITY_CENTER" ) ).arg( gc.x() ).arg( gc.y() ).arg( gc.z() ) ); + } + if ( const SMDS_BallElement* ball = dynamic_cast( e )) { + // ball diameter + myInfo->append( QString( "%1: %2" ).arg( tr( "BALL_DIAMETER" ) ).arg( ball->GetDiameter() )); + } + // separator + myInfo->append( "" ); + // connectivity + SMDS_ElemIteratorPtr nodeIt = e->nodesIterator(); + for ( int idx = 1; nodeIt->more(); idx++ ) { + const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + // node number and ID + myInfo->append( QString( "%1 %2/%3 - #%4" ).arg( tr( "NODE" ) ).arg( idx ).arg( e->NbNodes() ).arg( node->GetID() ) ); + // node coordinates + myInfo->append( QString( "%1: (%2, %3, %4)" ).arg( tr( "COORDINATES" ) ). + arg( node->X(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ). + arg( node->Y(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ). + arg( node->Z(), 0, precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // node connectivity + Connectivity connectivity = nodeConnectivity( node ); + if ( !connectivity.isEmpty() ) { + myInfo->append( QString( "%1:" ).arg( tr( "CONNECTIVITY" ) ) ); + QString con = formatConnectivity( connectivity, SMDSAbs_0DElement ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "0D_ELEMENTS" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Edge ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "EDGES" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Face ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "FACES" ) ).arg( con ) ); + con = formatConnectivity( connectivity, SMDSAbs_Volume ); + if ( !con.isEmpty() ) + myInfo->append( QString( "- %1: %2" ).arg( tr( "VOLUMES" ) ).arg( con ) ); + } + else { + myInfo->append( QString( "%1" ).arg( tr( "FREE_NODE" ) ).arg( id ) ); + } + } + } + // separator + if ( ids.count() > 1 ) { + myInfo->append( "" ); + myInfo->append( "------" ); + myInfo->append( "" ); + } + } + } +} + +/*! + \brief Internal clean-up (reset widget) +*/ +void SMESHGUI_SimpleElemInfo::clearInternal() +{ + myInfo->clear(); +} + +/*! + \class SMESHGUI_TreeElemInfo::ItemDelegate + \brief Item delegate for tree mesh info widget + \internal +*/ +class SMESHGUI_TreeElemInfo::ItemDelegate : public QItemDelegate +{ +public: + ItemDelegate( QObject* ); + QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const; +}; + +/*! + \brief Constructor + \internal +*/ +SMESHGUI_TreeElemInfo::ItemDelegate::ItemDelegate( QObject* parent ) : QItemDelegate( parent ) +{ +} + +/*! + \brief Create item editor widget + \internal +*/ +QWidget* SMESHGUI_TreeElemInfo::ItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QWidget* w = index.column() == 0 ? 0: QItemDelegate::createEditor( parent, option, index ); + if ( qobject_cast( w ) ) qobject_cast( w )->setReadOnly( true ); + return w; +} + +/*! + \class SMESHGUI_TreeElemInfo + \brief Represents mesh element information in the tree-like form. +*/ + +/*! + \brief Constructor + \param parent parent widget +*/ +SMESHGUI_TreeElemInfo::SMESHGUI_TreeElemInfo( QWidget* parent ) +: SMESHGUI_ElemInfo( parent ) +{ + myInfo = new QTreeWidget( frame() ); + myInfo->setColumnCount( 2 ); + myInfo->setHeaderLabels( QStringList() << tr( "PROPERTY" ) << tr( "VALUE" ) ); + myInfo->header()->setStretchLastSection( true ); + myInfo->header()->setResizeMode( 0, QHeaderView::ResizeToContents ); + myInfo->setItemDelegate( new ItemDelegate( myInfo ) ); + QVBoxLayout* l = new QVBoxLayout( frame() ); + l->setMargin( 0 ); + l->addWidget( myInfo ); +} + +/*! + \brief Show mesh element information + \param ids mesh nodes / elements identifiers +*/ +void SMESHGUI_TreeElemInfo::information( const QList& ids ) +{ + clearInternal(); + + if ( actor() ) { + int precision = SMESHGUI::resourceMgr()->integerValue( "SMESH", "length_precision", 6 ); + foreach ( long id, ids ) { + if ( !isElements() ) { + // + // show node info + // + const SMDS_MeshElement* e = actor()->GetObject()->GetMesh()->FindNode( id ); + if ( !e ) return; + const SMDS_MeshNode* node = static_cast( e ); + + // node ID + QTreeWidgetItem* nodeItem = createItem( 0, Bold | All ); + nodeItem->setText( 0, tr( "NODE" ) ); + nodeItem->setText( 1, QString( "#%1" ).arg( id ) ); + // coordinates + QTreeWidgetItem* coordItem = createItem( nodeItem, Bold ); + coordItem->setText( 0, tr( "COORDINATES" ) ); + QTreeWidgetItem* xItem = createItem( coordItem ); + xItem->setText( 0, "X" ); + xItem->setText( 1, QString::number( node->X(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* yItem = createItem( coordItem ); + yItem->setText( 0, "Y" ); + yItem->setText( 1, QString::number( node->Y(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* zItem = createItem( coordItem ); + zItem->setText( 0, "Z" ); + zItem->setText( 1, QString::number( node->Z(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // connectivity + QTreeWidgetItem* conItem = createItem( nodeItem, Bold ); + conItem->setText( 0, tr( "CONNECTIVITY" ) ); + Connectivity connectivity = nodeConnectivity( node ); + if ( !connectivity.isEmpty() ) { + QString con = formatConnectivity( connectivity, SMDSAbs_0DElement ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( conItem ); + i->setText( 0, tr( "0D_ELEMENTS" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Ball ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( conItem ); + i->setText( 0, tr( "BALL_ELEMENTS" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Edge ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( conItem ); + i->setText( 0, tr( "EDGES" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Face ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( conItem ); + i->setText( 0, tr( "FACES" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Volume ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( conItem ); + i->setText( 0, tr( "VOLUMES" ) ); + i->setText( 1, con ); + } + } + else { + conItem->setText( 1, tr( "FREE_NODE" ) ); + } + // node position + int shapeID = node->getshapeId(); + if ( shapeID > 0 ) + { + SMDS_PositionPtr pos = node->GetPosition(); + SMDS_TypeOfPosition posType = pos->GetTypeOfPosition(); + QString shapeType; + double u,v; + switch ( posType ) { + case SMDS_TOP_EDGE: shapeType = tr( "EDGE" ); + u = static_cast( pos )->GetUParameter(); + break; + case SMDS_TOP_FACE: shapeType = tr( "FACE" ); + u = static_cast( pos )->GetUParameter(); + v = static_cast( pos )->GetVParameter(); + break; + case SMDS_TOP_VERTEX: shapeType = tr( "VERTEX" ); break; + default: shapeType = tr( "SOLID" ); + } + QTreeWidgetItem* posItem = createItem( nodeItem, Bold ); + posItem->setText( 0, tr("NODE_POSITION") ); + posItem->setText( 1, (shapeType + " #%1").arg( shapeID )); + if ( posType == SMDS_TOP_EDGE || posType == SMDS_TOP_FACE ) { + QTreeWidgetItem* uItem = createItem( posItem ); + uItem->setText( 0, tr("U_POSITION") ); + uItem->setText( 1, QString::number( u, precision > 0 ? 'f' : 'g', qAbs( precision ))); + if ( posType == SMDS_TOP_FACE ) { + QTreeWidgetItem* vItem = createItem( posItem ); + vItem->setText( 0, tr("V_POSITION") ); + vItem->setText( 1, QString::number( v, precision > 0 ? 'f' : 'g', qAbs( precision ))); + } + } + } + } + else { + // + // show element info + // + const SMDS_MeshElement* e = actor()->GetObject()->GetMesh()->FindElement( id ); + if ( !e ) return; + + // element ID && type + QString stype; + switch( e->GetType() ) { + case SMDSAbs_0DElement: + stype = tr( "0D ELEMENT" ); break; + case SMDSAbs_Ball: + stype = tr( "BALL" ); break; + case SMDSAbs_Edge: + stype = tr( "EDGE" ); break; + case SMDSAbs_Face: + stype = tr( "FACE" ); break; + case SMDSAbs_Volume: + stype = tr( "VOLUME" ); break; + default: + break; + } + if ( stype.isEmpty() ) return; + QTreeWidgetItem* elemItem = createItem( 0, Bold | All ); + elemItem->setText( 0, stype ); + elemItem->setText( 1, QString( "#%1" ).arg( id ) ); + // geometry type + QString gtype; + switch( e->GetEntityType() ) { + case SMDSEntity_Triangle: + case SMDSEntity_Quad_Triangle: + gtype = tr( "TRIANGLE" ); break; + case SMDSEntity_Quadrangle: + case SMDSEntity_Quad_Quadrangle: + case SMDSEntity_BiQuad_Quadrangle: + gtype = tr( "QUADRANGLE" ); break; + case SMDSEntity_Polygon: + case SMDSEntity_Quad_Polygon: + gtype = tr( "POLYGON" ); break; + case SMDSEntity_Tetra: + case SMDSEntity_Quad_Tetra: + gtype = tr( "TETRAHEDRON" ); break; + case SMDSEntity_Pyramid: + case SMDSEntity_Quad_Pyramid: + gtype = tr( "PYRAMID" ); break; + case SMDSEntity_Hexa: + case SMDSEntity_Quad_Hexa: + case SMDSEntity_TriQuad_Hexa: + gtype = tr( "HEXAHEDRON" ); break; + case SMDSEntity_Penta: + case SMDSEntity_Quad_Penta: + gtype = tr( "PRISM" ); break; + case SMDSEntity_Hexagonal_Prism: + gtype = tr( "HEX_PRISM" ); break; + case SMDSEntity_Polyhedra: + case SMDSEntity_Quad_Polyhedra: + gtype = tr( "POLYHEDRON" ); break; + default: + break; + } + if ( !gtype.isEmpty() ) { + QTreeWidgetItem* typeItem = createItem( elemItem, Bold ); + typeItem->setText( 0, tr( "TYPE" ) ); + typeItem->setText( 1, gtype ); + } + // quadratic flag and gravity center (any element except 0D) + if ( e->GetEntityType() > SMDSEntity_0D && e->GetEntityType() < SMDSEntity_Ball ) { + // quadratic flag + QTreeWidgetItem* quadItem = createItem( elemItem, Bold ); + quadItem->setText( 0, tr( "QUADRATIC" ) ); + quadItem->setText( 1, e->IsQuadratic() ? tr( "YES" ) : tr( "NO" ) ); + // gravity center + XYZ gc = gravityCenter( e ); + QTreeWidgetItem* gcItem = createItem( elemItem, Bold ); + gcItem->setText( 0, tr( "GRAVITY_CENTER" ) ); + QTreeWidgetItem* xItem = createItem( gcItem ); + xItem->setText( 0, "X" ); + xItem->setText( 1, QString::number( gc.x(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* yItem = createItem( gcItem ); + yItem->setText( 0, "Y" ); + yItem->setText( 1, QString::number( gc.y(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* zItem = createItem( gcItem ); + zItem->setText( 0, "Z" ); + zItem->setText( 1, QString::number( gc.z(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + } + if ( const SMDS_BallElement* ball = dynamic_cast( e )) { + // ball diameter + QTreeWidgetItem* diamItem = createItem( elemItem, Bold ); + diamItem->setText( 0, tr( "BALL_DIAMETER" ) ); + diamItem->setText( 1, QString( "%1" ).arg( ball->GetDiameter() )); + } + // connectivity + QTreeWidgetItem* conItem = createItem( elemItem, Bold ); + conItem->setText( 0, tr( "CONNECTIVITY" ) ); + SMDS_ElemIteratorPtr nodeIt = e->nodesIterator(); + for ( int idx = 1; nodeIt->more(); idx++ ) { + const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + // node number and ID + QTreeWidgetItem* nodeItem = createItem( conItem, Bold ); + nodeItem->setText( 0, QString( "%1 %2 / %3" ).arg( tr( "NODE" ) ).arg( idx ).arg( e->NbNodes() ) ); + nodeItem->setText( 1, QString( "#%1" ).arg( node->GetID() ) ); + nodeItem->setExpanded( false ); + // node coordinates + QTreeWidgetItem* coordItem = createItem( nodeItem ); + coordItem->setText( 0, tr( "COORDINATES" ) ); + QTreeWidgetItem* xItem = createItem( coordItem ); + xItem->setText( 0, "X" ); + xItem->setText( 1, QString::number( node->X(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* yItem = createItem( coordItem ); + yItem->setText( 0, "Y" ); + yItem->setText( 1, QString::number( node->Y(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + QTreeWidgetItem* zItem = createItem( coordItem ); + zItem->setText( 0, "Z" ); + zItem->setText( 1, QString::number( node->Z(), precision > 0 ? 'f' : 'g', qAbs( precision ) ) ); + // node connectivity + QTreeWidgetItem* nconItem = createItem( nodeItem ); + nconItem->setText( 0, tr( "CONNECTIVITY" ) ); + Connectivity connectivity = nodeConnectivity( node ); + if ( !connectivity.isEmpty() ) { + QString con = formatConnectivity( connectivity, SMDSAbs_0DElement ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( nconItem ); + i->setText( 0, tr( "0D_ELEMENTS" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Edge ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( nconItem ); + i->setText( 0, tr( "EDGES" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Ball ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( nconItem ); + i->setText( 0, tr( "BALL_ELEMENTS" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Face ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( nconItem ); + i->setText( 0, tr( "FACES" ) ); + i->setText( 1, con ); + } + con = formatConnectivity( connectivity, SMDSAbs_Volume ); + if ( !con.isEmpty() ) { + QTreeWidgetItem* i = createItem( nconItem ); + i->setText( 0, tr( "VOLUMES" ) ); + i->setText( 1, con ); + } + } + } + } + } + } +} + +/*! + \brief Internal clean-up (reset widget) +*/ +void SMESHGUI_TreeElemInfo::clearInternal() +{ + myInfo->clear(); + myInfo->repaint(); +} + +/*! + \brief Create new tree item. + \param parent parent tree widget item + \param flags item flag + \return new tree widget item +*/ +QTreeWidgetItem* SMESHGUI_TreeElemInfo::createItem( QTreeWidgetItem* parent, int flags ) +{ + QTreeWidgetItem* item; + if ( parent ) + item = new QTreeWidgetItem( parent ); + else + item = new QTreeWidgetItem( myInfo ); + + item->setFlags( item->flags() | Qt::ItemIsEditable ); + + QFont f = item->font( 0 ); + f.setBold( true ); + for ( int i = 0; i < myInfo->columnCount(); i++ ) { + if ( ( flags & Bold ) && ( i == 0 || flags & All ) ) + item->setFont( i, f ); + } + + item->setExpanded( true ); + return item; +} + +/*! + \class GrpComputor + \brief Mesh information computer + \internal + + The class is created for different computation operation. Currently it is used + to compute number of underlying nodes for the groups. +*/ + +/*! + \brief Contructor +*/ +GrpComputor::GrpComputor( SMESH::SMESH_GroupBase_ptr grp, QTreeWidgetItem* item, QObject* parent ) + : QObject( parent ), myItem( item ) +{ + myGroup = SMESH::SMESH_GroupBase::_narrow( grp ); +} + +/*! + \brief Compute function +*/ +void GrpComputor::compute() +{ + if ( !CORBA::is_nil( myGroup ) && myItem ) { + QTreeWidgetItem* item = myItem; + myItem = 0; + int nbNodes = myGroup->GetNumberOfNodes(); + item->treeWidget()->removeItemWidget( item, 1 ); + item->setText( 1, QString::number( nbNodes )); + } +} + +/*! + \class SMESHGUI_AddInfo + \brief The wigdet shows additional information on the mesh object. +*/ + +/*! + \brief Constructor + \param parent parent widget +*/ +SMESHGUI_AddInfo::SMESHGUI_AddInfo( QWidget* parent ) +: QTreeWidget( parent ) +{ + setColumnCount( 2 ); + header()->setStretchLastSection( true ); + header()->setResizeMode( 0, QHeaderView::ResizeToContents ); + header()->hide(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_AddInfo::~SMESHGUI_AddInfo() +{ +} + +/*! + \brief Show additional information on the selected object + \param obj object being processed (mesh, sub-mesh, group, ID source) +*/ +void SMESHGUI_AddInfo::showInfo( SMESH::SMESH_IDSource_ptr obj ) +{ + setProperty( "group_index", 0 ); + setProperty( "submesh_index", 0 ); + myComputors.clear(); + clear(); + + if ( CORBA::is_nil( obj ) ) return; + + _PTR(SObject) sobj = SMESH::ObjectToSObject( obj ); + if ( !sobj ) return; + + // name + QTreeWidgetItem* nameItem = createItem( 0, Bold | All ); + nameItem->setText( 0, tr( "NAME" ) ); + nameItem->setText( 1, sobj->GetName().c_str() ); + + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( obj ); + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow( obj ); + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( obj ); + + if ( !aMesh->_is_nil() ) + meshInfo( aMesh, nameItem ); + else if ( !aSubMesh->_is_nil() ) + subMeshInfo( aSubMesh, nameItem ); + else if ( !aGroup->_is_nil() ) + groupInfo( aGroup.in(), nameItem ); +} + +/*! + \brief Create new tree item. + \param parent parent tree widget item + \param flags item flag + \return new tree widget item +*/ +QTreeWidgetItem* SMESHGUI_AddInfo::createItem( QTreeWidgetItem* parent, int flags ) +{ + QTreeWidgetItem* item; + + if ( parent ) + item = new QTreeWidgetItem( parent ); + else + item = new QTreeWidgetItem( this ); + + //item->setFlags( item->flags() | Qt::ItemIsEditable ); + + QFont f = item->font( 0 ); + f.setBold( true ); + for ( int i = 0; i < columnCount(); i++ ) { + if ( ( flags & Bold ) && ( i == 0 || flags & All ) ) + item->setFont( i, f ); + } + + item->setExpanded( true ); + return item; +} + +/*! + \brief Show mesh info + \param mesh mesh object + \param parent parent tree item +*/ +void SMESHGUI_AddInfo::meshInfo( SMESH::SMESH_Mesh_ptr mesh, QTreeWidgetItem* parent ) +{ + // type + GEOM::GEOM_Object_var shape = mesh->GetShapeToMesh(); + SALOME_MED::MedFileInfo* inf = mesh->GetMEDFileInfo(); + QTreeWidgetItem* typeItem = createItem( parent, Bold ); + typeItem->setText( 0, tr( "TYPE" ) ); + if ( !CORBA::is_nil( shape ) ) { + typeItem->setText( 1, tr( "MESH_ON_GEOMETRY" ) ); + _PTR(SObject) sobj = SMESH::ObjectToSObject( shape ); + if ( sobj ) { + QTreeWidgetItem* gobjItem = createItem( typeItem ); + gobjItem->setText( 0, tr( "GEOM_OBJECT" ) ); + gobjItem->setText( 1, sobj->GetName().c_str() ); + } + } + else if ( strlen( (char*)inf->fileName ) > 0 ) { + typeItem->setText( 1, tr( "MESH_FROM_FILE" ) ); + QTreeWidgetItem* fileItem = createItem( typeItem ); + fileItem->setText( 0, tr( "FILE_NAME" ) ); + fileItem->setText( 1, (char*)inf->fileName ); + } + else { + typeItem->setText( 1, tr( "STANDALONE_MESH" ) ); + } + + // groups + myGroups = mesh->GetGroups(); + showGroups(); + + // sub-meshes + mySubMeshes = mesh->GetSubMeshes(); + showSubMeshes(); +} + +/*! + \brief Show sub-mesh info + \param subMesh sub-mesh object + \param parent parent tree item +*/ +void SMESHGUI_AddInfo::subMeshInfo( SMESH::SMESH_subMesh_ptr subMesh, QTreeWidgetItem* parent ) +{ + bool isShort = parent->parent() != 0; + + if ( !isShort ) { + // parent mesh + _PTR(SObject) sobj = SMESH::ObjectToSObject( subMesh->GetFather() ); + if ( sobj ) { + QTreeWidgetItem* nameItem = createItem( parent, Bold ); + nameItem->setText( 0, tr( "PARENT_MESH" ) ); + nameItem->setText( 1, sobj->GetName().c_str() ); + } + } + + // shape + GEOM::GEOM_Object_var gobj = subMesh->GetSubShape(); + _PTR(SObject) sobj = SMESH::ObjectToSObject( gobj ); + if ( sobj ) { + QTreeWidgetItem* gobjItem = createItem( parent, Bold ); + gobjItem->setText( 0, tr( "GEOM_OBJECT" ) ); + gobjItem->setText( 1, sobj->GetName().c_str() ); + } +} + +/*! + \brief Show group info + \param grp mesh group object + \param parent parent tree item +*/ +void SMESHGUI_AddInfo::groupInfo( SMESH::SMESH_GroupBase_ptr grp, QTreeWidgetItem* parent ) +{ + bool isShort = parent->parent() != 0; + + SMESH::SMESH_Group_var aStdGroup = SMESH::SMESH_Group::_narrow( grp ); + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( grp ); + SMESH::SMESH_GroupOnFilter_var aFltGroup = SMESH::SMESH_GroupOnFilter::_narrow( grp ); + + if ( !isShort ) { + // parent mesh + _PTR(SObject) sobj = SMESH::ObjectToSObject( grp->GetMesh() ); + if ( sobj ) { + QTreeWidgetItem* nameItem = createItem( parent, Bold ); + nameItem->setText( 0, tr( "PARENT_MESH" ) ); + nameItem->setText( 1, sobj->GetName().c_str() ); + } + } + + // type : group on geometry, standalone group, group on filter + QTreeWidgetItem* typeItem = createItem( parent, Bold ); + typeItem->setText( 0, tr( "TYPE" ) ); + if ( !CORBA::is_nil( aStdGroup ) ) { + typeItem->setText( 1, tr( "STANDALONE_GROUP" ) ); + } + else if ( !CORBA::is_nil( aGeomGroup ) ) { + typeItem->setText( 1, tr( "GROUP_ON_GEOMETRY" ) ); + GEOM::GEOM_Object_var gobj = aGeomGroup->GetShape(); + _PTR(SObject) sobj = SMESH::ObjectToSObject( gobj ); + if ( sobj ) { + QTreeWidgetItem* gobjItem = createItem( typeItem ); + gobjItem->setText( 0, tr( "GEOM_OBJECT" ) ); + gobjItem->setText( 1, sobj->GetName().c_str() ); + } + } + else if ( !CORBA::is_nil( aFltGroup ) ) { + typeItem->setText( 1, tr( "GROUP_ON_FILTER" ) ); + } + + if ( !isShort ) { + // entity type + QString etype = tr( "UNKNOWN" ); + switch( grp->GetType() ) { + case SMESH::NODE: + etype = tr( "NODE" ); + break; + case SMESH::EDGE: + etype = tr( "EDGE" ); + break; + case SMESH::FACE: + etype = tr( "FACE" ); + break; + case SMESH::VOLUME: + etype = tr( "VOLUME" ); + break; + case SMESH::ELEM0D: + etype = tr( "0DELEM" ); + break; + case SMESH::BALL: + etype = tr( "BALL" ); + break; + default: + break; + } + QTreeWidgetItem* etypeItem = createItem( parent, Bold ); + etypeItem->setText( 0, tr( "ENTITY_TYPE" ) ); + etypeItem->setText( 1, etype ); + } + + // size + QTreeWidgetItem* sizeItem = createItem( parent, Bold ); + sizeItem->setText( 0, tr( "SIZE" ) ); + sizeItem->setText( 1, QString::number( grp->Size() ) ); + + // color + SALOMEDS::Color color = grp->GetColor(); + QTreeWidgetItem* colorItem = createItem( parent, Bold ); + colorItem->setText( 0, tr( "COLOR" ) ); + colorItem->setBackground( 1, QBrush( QColor( color.R*255., color.G*255., color.B*255.) ) ); + + // nb of underlying nodes + if ( grp->GetType() != SMESH::NODE) { + QTreeWidgetItem* nodesItem = createItem( parent, Bold ); + nodesItem->setText( 0, tr( "NB_NODES" ) ); + int nbNodesLimit = SMESHGUI::resourceMgr()->integerValue( "SMESH", "info_groups_nodes_limit", 100000 ); + SMESH::SMESH_Mesh_var mesh = grp->GetMesh(); + bool meshLoaded = mesh->IsLoaded(); + bool toShowNodes = ( grp->IsNodeInfoAvailable() || nbNodesLimit <= 0 || grp->Size() <= nbNodesLimit ); + if ( toShowNodes && meshLoaded ) { + // already calculated and up-to-date + nodesItem->setText( 1, QString::number( grp->GetNumberOfNodes() ) ); + } + else { + QPushButton* btn = new QPushButton( tr( meshLoaded ? "COMPUTE" : "LOAD"), this ); + setItemWidget( nodesItem, 1, btn ); + GrpComputor* comp = new GrpComputor( grp, nodesItem, this ); + connect( btn, SIGNAL( clicked() ), comp, SLOT( compute() ) ); + myComputors.append( comp ); + if ( !meshLoaded ) + connect( btn, SIGNAL( clicked() ), this, SLOT( changeLoadToCompute() ) ); + } + } +} + +void SMESHGUI_AddInfo::showGroups() +{ + myComputors.clear(); + + QTreeWidgetItem* parent = topLevelItemCount() > 0 ? topLevelItem( 0 ) : 0; // parent should be first top level item + if ( !parent ) return; + + int idx = property( "group_index" ).toInt(); + + QTreeWidgetItem* itemGroups = 0; + for ( int i = 0; i < parent->childCount() && !itemGroups; i++ ) { + if ( parent->child( i )->data( 0, Qt::UserRole ).toInt() == GROUPS_ID ) { + itemGroups = parent->child( i ); + ExtraWidget* extra = dynamic_cast( itemWidget( itemGroups, 1 ) ); + if ( extra ) + extra->updateControls( myGroups->length(), idx ); + while ( itemGroups->childCount() ) delete itemGroups->child( 0 ); // clear child items + } + } + + QMap grpItems; + for ( int i = idx*MAXITEMS ; i < qMin( (idx+1)*MAXITEMS, (int)myGroups->length() ); i++ ) { + SMESH::SMESH_GroupBase_var grp = myGroups[i]; + if ( CORBA::is_nil( grp ) ) continue; + _PTR(SObject) grpSObj = SMESH::ObjectToSObject( grp ); + if ( !grpSObj ) continue; + + int grpType = grp->GetType(); + + if ( !itemGroups ) { + // create top-level groups container item + itemGroups = createItem( parent, Bold | All ); + itemGroups->setText( 0, tr( "GROUPS" ) ); + itemGroups->setData( 0, Qt::UserRole, GROUPS_ID ); + + // total number of groups > 10, show extra widgets for info browsing + if ( myGroups->length() > MAXITEMS ) { + ExtraWidget* extra = new ExtraWidget( this, true ); + connect( extra->prev, SIGNAL( clicked() ), this, SLOT( showPreviousGroups() ) ); + connect( extra->next, SIGNAL( clicked() ), this, SLOT( showNextGroups() ) ); + setItemWidget( itemGroups, 1, extra ); + extra->updateControls( myGroups->length(), idx ); + } + } + + if ( grpItems.find( grpType ) == grpItems.end() ) { + grpItems[ grpType ] = createItem( itemGroups, Bold | All ); + grpItems[ grpType ]->setText( 0, tr( QString( "GROUPS_%1" ).arg( grpType ).toLatin1().constData() ) ); + itemGroups->insertChild( grpType-1, grpItems[ grpType ] ); + } + + // group name + QTreeWidgetItem* grpNameItem = createItem( grpItems[ grpType ] ); + grpNameItem->setText( 0, QString( grpSObj->GetName().c_str() ).trimmed() ); // name is trimmed + + // group info + groupInfo( grp.in(), grpNameItem ); + } +} + +void SMESHGUI_AddInfo::showSubMeshes() +{ + QTreeWidgetItem* parent = topLevelItemCount() > 0 ? topLevelItem( 0 ) : 0; // parent should be first top level item + if ( !parent ) return; + + int idx = property( "submesh_index" ).toInt(); + + QTreeWidgetItem* itemSubMeshes = 0; + for ( int i = 0; i < parent->childCount() && !itemSubMeshes; i++ ) { + if ( parent->child( i )->data( 0, Qt::UserRole ).toInt() == SUBMESHES_ID ) { + itemSubMeshes = parent->child( i ); + ExtraWidget* extra = dynamic_cast( itemWidget( itemSubMeshes, 1 ) ); + if ( extra ) + extra->updateControls( mySubMeshes->length(), idx ); + while ( itemSubMeshes->childCount() ) delete itemSubMeshes->child( 0 ); // clear child items + } + } + + QMap smItems; + for ( int i = idx*MAXITEMS ; i < qMin( (idx+1)*MAXITEMS, (int)mySubMeshes->length() ); i++ ) { + SMESH::SMESH_subMesh_var sm = mySubMeshes[i]; + if ( CORBA::is_nil( sm ) ) continue; + _PTR(SObject) smSObj = SMESH::ObjectToSObject( sm ); + if ( !smSObj ) continue; + + GEOM::GEOM_Object_var gobj = sm->GetSubShape(); + if ( CORBA::is_nil(gobj ) ) continue; + + int smType = gobj->GetShapeType(); + if ( smType == GEOM::COMPSOLID ) smType = GEOM::COMPOUND; + + if ( !itemSubMeshes ) { + itemSubMeshes = createItem( parent, Bold | All ); + itemSubMeshes->setText( 0, tr( "SUBMESHES" ) ); + itemSubMeshes->setData( 0, Qt::UserRole, SUBMESHES_ID ); + + // total number of sub-meshes > 10, show extra widgets for info browsing + if ( mySubMeshes->length() > MAXITEMS ) { + ExtraWidget* extra = new ExtraWidget( this, true ); + connect( extra->prev, SIGNAL( clicked() ), this, SLOT( showPreviousSubMeshes() ) ); + connect( extra->next, SIGNAL( clicked() ), this, SLOT( showNextSubMeshes() ) ); + setItemWidget( itemSubMeshes, 1, extra ); + extra->updateControls( mySubMeshes->length(), idx ); + } + } + + if ( smItems.find( smType ) == smItems.end() ) { + smItems[ smType ] = createItem( itemSubMeshes, Bold | All ); + smItems[ smType ]->setText( 0, tr( QString( "SUBMESHES_%1" ).arg( smType ).toLatin1().constData() ) ); + itemSubMeshes->insertChild( smType, smItems[ smType ] ); + } + + // submesh name + QTreeWidgetItem* smNameItem = createItem( smItems[ smType ] ); + smNameItem->setText( 0, QString( smSObj->GetName().c_str() ).trimmed() ); // name is trimmed + + // submesh info + subMeshInfo( sm.in(), smNameItem ); + } +} + +/*! + * \brief Change button label of "nb underlying node" group from "Load" to "Compute" + */ +void SMESHGUI_AddInfo::changeLoadToCompute() +{ + for ( int i = 0; i < myComputors.count(); ++i ) + { + if ( QTreeWidgetItem* item = myComputors[i]->getItem() ) + { + if ( QPushButton* btn = qobject_cast( itemWidget ( item, 1 ) ) ) + btn->setText( tr("COMPUTE") ); + } + } +} + +void SMESHGUI_AddInfo::showPreviousGroups() +{ + int idx = property( "group_index" ).toInt(); + setProperty( "group_index", idx-1 ); + showGroups(); +} + +void SMESHGUI_AddInfo::showNextGroups() +{ + int idx = property( "group_index" ).toInt(); + setProperty( "group_index", idx+1 ); + showGroups(); +} + +void SMESHGUI_AddInfo::showPreviousSubMeshes() +{ + int idx = property( "submesh_index" ).toInt(); + setProperty( "submesh_index", idx-1 ); + showSubMeshes(); +} + +void SMESHGUI_AddInfo::showNextSubMeshes() +{ + int idx = property( "submesh_index" ).toInt(); + setProperty( "submesh_index", idx+1 ); + showSubMeshes(); +} + +/*! + \class SMESHGUI_MeshInfoDlg + \brief Mesh information dialog box +*/ + +/*! + \brief Constructor + \param parent parent widget + \param page specifies the dialog page to be shown at the start-up +*/ +SMESHGUI_MeshInfoDlg::SMESHGUI_MeshInfoDlg( QWidget* parent, int page ) +: QDialog( parent ), myActor( 0 ) +{ + setModal( false ); + setAttribute( Qt::WA_DeleteOnClose, true ); + setWindowTitle( tr( "MESH_INFO" ) ); + setSizeGripEnabled( true ); + + myTabWidget = new QTabWidget( this ); + + // base info + + myBaseInfo = new SMESHGUI_MeshInfo( myTabWidget ); + myTabWidget->addTab( myBaseInfo, tr( "BASE_INFO" ) ); + + // elem info + + QWidget* w = new QWidget( myTabWidget ); + + myMode = new QButtonGroup( this ); + myMode->addButton( new QRadioButton( tr( "NODE_MODE" ), w ), NodeMode ); + myMode->addButton( new QRadioButton( tr( "ELEM_MODE" ), w ), ElemMode ); + myMode->button( NodeMode )->setChecked( true ); + myID = new QLineEdit( w ); + myID->setValidator( new SMESHGUI_IdValidator( this ) ); + + int mode = SMESHGUI::resourceMgr()->integerValue( "SMESH", "mesh_elem_info", 1 ); + mode = qMin( 1, qMax( 0, mode ) ); + + if ( mode == 0 ) + myElemInfo = new SMESHGUI_SimpleElemInfo( w ); + else + myElemInfo = new SMESHGUI_TreeElemInfo( w ); + + QGridLayout* elemLayout = new QGridLayout( w ); + elemLayout->setMargin( MARGIN ); + elemLayout->setSpacing( SPACING ); + elemLayout->addWidget( myMode->button( NodeMode ), 0, 0 ); + elemLayout->addWidget( myMode->button( ElemMode ), 0, 1 ); + elemLayout->addWidget( myID, 0, 2 ); + elemLayout->addWidget( myElemInfo, 1, 0, 1, 3 ); + + myTabWidget->addTab( w, tr( "ELEM_INFO" ) ); + + // additional info + + myAddInfo = new SMESHGUI_AddInfo( myTabWidget ); + myTabWidget->addTab( myAddInfo, tr( "ADDITIONAL_INFO" ) ); + + // buttons + + QPushButton* okBtn = new QPushButton( tr( "SMESH_BUT_OK" ), this ); + okBtn->setAutoDefault( true ); + okBtn->setDefault( true ); + okBtn->setFocus(); + QPushButton* helpBtn = new QPushButton( tr( "SMESH_BUT_HELP" ), this ); + helpBtn->setAutoDefault( true ); + + QHBoxLayout* btnLayout = new QHBoxLayout; + btnLayout->setSpacing( SPACING ); + btnLayout->setMargin( 0 ); + + btnLayout->addWidget( okBtn ); + btnLayout->addStretch( 10 ); + btnLayout->addWidget( helpBtn ); + + QVBoxLayout* l = new QVBoxLayout ( this ); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + l->addWidget( myTabWidget ); + l->addLayout( btnLayout ); + + myTabWidget->setCurrentIndex( qMax( (int)BaseInfo, qMin( (int)ElemInfo, page ) ) ); + + connect( okBtn, SIGNAL( clicked() ), this, SLOT( reject() ) ); + connect( helpBtn, SIGNAL( clicked() ), this, SLOT( help() ) ); + connect( myTabWidget, SIGNAL( currentChanged( int ) ), this, SLOT( updateSelection() ) ); + connect( myMode, SIGNAL( buttonClicked( int ) ), this, SLOT( modeChanged() ) ); + connect( myID, SIGNAL( textEdited( QString ) ), this, SLOT( idChanged() ) ); + connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalDeactivateActiveDialog() ), this, SLOT( deactivate() ) ); + connect( SMESHGUI::GetSMESHGUI(), SIGNAL( SignalCloseAllDialogs() ), this, SLOT( reject() ) ); + + updateSelection(); +} + +/*! + \brief Destructor +*/ +SMESHGUI_MeshInfoDlg::~SMESHGUI_MeshInfoDlg() +{ +} + +/*! + \brief Show mesh information + \param IO interactive object +*/ +void SMESHGUI_MeshInfoDlg::showInfo( const Handle(SALOME_InteractiveObject)& IO ) +{ + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( IO ); + if ( !CORBA::is_nil( obj ) ) { + myBaseInfo->showInfo( obj ); + myAddInfo->showInfo( obj ); + + myActor = SMESH::FindActorByEntry( IO->getEntry() ); + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + QString ID; + int nb = 0; + if ( myActor && selector ) { + nb = myMode->checkedId() == NodeMode ? + SMESH::GetNameOfSelectedElements( selector, IO, ID ) : + SMESH::GetNameOfSelectedNodes( selector, IO, ID ); + } + myElemInfo->setSource( myActor ) ; + if ( nb > 0 ) { + myID->setText( ID.trimmed() ); + QSet ids; + QStringList idTxt = ID.split( " ", QString::SkipEmptyParts ); + foreach ( ID, idTxt ) + ids << ID.trimmed().toLong(); + myElemInfo->showInfo( ids, myMode->checkedId() == ElemMode ); + } + else { + myID->clear(); + myElemInfo->clear(); + } + } +} + +/*! + \brief Perform clean-up actions on the dialog box closing. +*/ +void SMESHGUI_MeshInfoDlg::reject() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + selMgr->clearFilters(); + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( ActorSelection ); + QDialog::reject(); +} + +/*! + \brief Process keyboard event + \param e key press event +*/ +void SMESHGUI_MeshInfoDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( !e->isAccepted() && e->key() == Qt::Key_F1 ) { + e->accept(); + help(); + } +} + +/*! + \brief Reactivate dialog box, when mouse pointer goes into it. +*/ +void SMESHGUI_MeshInfoDlg::enterEvent( QEvent* ) +{ + //activate(); +} + +/*! + \brief Setup selection mode depending on the current dialog box state. +*/ +void SMESHGUI_MeshInfoDlg::updateSelection() +{ + LightApp_SelectionMgr* selMgr = SMESHGUI::selectionMgr(); + + disconnect( selMgr, 0, this, 0 ); + selMgr->clearFilters(); + + if ( myTabWidget->currentIndex() == BaseInfo || myTabWidget->currentIndex() == AddInfo ) { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( ActorSelection ); + } + else { + if ( myMode->checkedId() == NodeMode ) { + SMESH::SetPointRepresentation( true ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( NodeSelection ); + } + else { + SMESH::SetPointRepresentation( false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->SetSelectionMode( CellSelection ); + } + } + + QString oldID = myID->text().trimmed(); + SMESH_Actor* oldActor = myActor; + myID->clear(); + + connect( selMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) ); + updateInfo(); + + if ( oldActor == myActor && myActor && !oldID.isEmpty() ) { + myID->setText( oldID ); + idChanged(); + } +} + +/*! + \brief Show help page +*/ +void SMESHGUI_MeshInfoDlg::help() +{ + SMESH::ShowHelpFile( ( myTabWidget->currentIndex() == BaseInfo || myTabWidget->currentIndex() == AddInfo ) ? + "mesh_infos_page.html#advanced_mesh_infos_anchor" : + "mesh_infos_page.html#mesh_element_info_anchor" ); +} + +/*! + \brief Show mesh information +*/ +void SMESHGUI_MeshInfoDlg::updateInfo() +{ + SUIT_OverrideCursor wc; + + SALOME_ListIO selected; + SMESHGUI::selectionMgr()->selectedObjects( selected ); + + if ( selected.Extent() == 1 ) { + Handle(SALOME_InteractiveObject) IO = selected.First(); + showInfo( IO ); + } +// else { +// myBaseInfo->clear(); +// myElemInfo->clear(); +// myAddInfo->clear(); +// } +} + +/*! + \brief Activate dialog box +*/ +void SMESHGUI_MeshInfoDlg::activate() +{ + SMESHGUI::GetSMESHGUI()->EmitSignalDeactivateDialog(); + SMESHGUI::GetSMESHGUI()->SetActiveDialogBox( this ); + myTabWidget->setEnabled( true ); + updateSelection(); +} + +/*! + \brief Deactivate dialog box +*/ +void SMESHGUI_MeshInfoDlg::deactivate() +{ + myTabWidget->setEnabled( false ); + disconnect( SMESHGUI::selectionMgr(), SIGNAL( currentSelectionChanged() ), this, SLOT( updateInfo() ) ); +} + +/*! + \brief Called when users switches between node / element modes. +*/ +void SMESHGUI_MeshInfoDlg::modeChanged() +{ + myID->clear(); + updateSelection(); +} + +/*! + \brief Caled when users prints mesh element ID in the corresponding field. +*/ +void SMESHGUI_MeshInfoDlg::idChanged() +{ + SVTK_Selector* selector = SMESH::GetViewWindow()->GetSelector(); + if ( myActor && selector ) { + Handle(SALOME_InteractiveObject) IO = myActor->getIO(); + TColStd_MapOfInteger ID; + QSet ids; + QStringList idTxt = myID->text().split( " ", QString::SkipEmptyParts ); + foreach ( QString tid, idTxt ) { + long id = tid.trimmed().toLong(); + const SMDS_MeshElement* e = myMode->checkedId() == ElemMode ? + myActor->GetObject()->GetMesh()->FindElement( id ) : + myActor->GetObject()->GetMesh()->FindNode( id ); + if ( e ) { + ID.Add( id ); + ids << id; + } + } + selector->AddOrRemoveIndex( IO, ID, false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow() ) + aViewWindow->highlight( IO, true, true ); + myElemInfo->showInfo( ids, myMode->checkedId() == ElemMode ); + } +} diff --git a/src/SMESHGUI/SMESHGUI_MeshInfo.h b/src/SMESHGUI/SMESHGUI_MeshInfo.h new file mode 100644 index 000000000..a2fa39930 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshInfo.h @@ -0,0 +1,305 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_MeshInfo.h +// Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com) + +#ifndef SMESHGUI_MESHINFO_H +#define SMESHGUI_MESHINFO_H + +#include "SMESH_SMESHGUI.hxx" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_Group) + +class QButtonGroup; +class QLabel; +class QLineEdit; +class QPushButton; +class QTabWidget; +class QTextBrowser; +class SMESH_Actor; +class SMDS_MeshNode; +class SMDS_MeshElement; + +class ExtraWidget; + +class SMESHGUI_EXPORT SMESHGUI_MeshInfo : public QFrame +{ + Q_OBJECT; + + enum { + iName, + iObject, + iNodesStart, + iNodes, + iNodesEnd, + iElementsStart = iNodesEnd, + iElements, + i0DStart, + i0D, + i0DEnd, + iBallsStart = i0DEnd, + iBalls, + iBallsEnd, + i1DStart = iBallsEnd, + i1D, + i1DEnd, + i2DStart = i1DEnd, + i2D, + i2DTriangles, + i2DQuadrangles, + i2DPolygons, + i2DEnd, + i3DStart = i2DEnd, + i3D, + i3DTetrahedrons, + i3DHexahedrons, + i3DPyramids, + i3DPrisms, + i3DHexaPrisms, + i3DPolyhedrons, + i3DEnd, + iElementsEnd = i3DEnd + }; + + enum { + iSingle = 1, + iTotal = iSingle, + iLinear, + iQuadratic + }; + + typedef QList wlist; + typedef QVector iwlist; + +public: + SMESHGUI_MeshInfo( QWidget* = 0 ); + ~SMESHGUI_MeshInfo(); + + void showInfo( SMESH::SMESH_IDSource_ptr ); + void clear(); + +private: + enum { Bold = 0x01, Italic = 0x02 }; + + QLabel* createField(); + QWidget* createLine(); + void setFontAttributes( QWidget*, int, bool = true ); + void setFieldsVisible( int, int, bool ); + +private slots: + void loadMesh(); + +private: + iwlist myWidgets; + QPushButton* myLoadBtn; +}; + +class SMESHGUI_EXPORT SMESHGUI_ElemInfo : public QWidget +{ + Q_OBJECT; + +public: + SMESHGUI_ElemInfo( QWidget* = 0 ); + ~SMESHGUI_ElemInfo(); + + void setSource( SMESH_Actor* ); + void showInfo( long, bool ); + void showInfo( QSet, bool ); + void clear(); + +protected: + struct XYZ + { + double myX, myY, myZ; + XYZ() { myX = myY = myZ = 0.0; } + void add( double x, double y, double z ) { myX += x; myY += y; myZ += z; } + void divide( double a ) { if ( a != 0.) { myX /= a; myY /= a; myZ /= a; } } + double x() const { return myX; } + double y() const { return myY; } + double z() const { return myZ; } + }; + typedef QMap< int, QList > Connectivity; + + QWidget* frame() const; + SMESH_Actor* actor() const; + bool isElements() const; + + virtual void information( const QList& ) = 0; + virtual void clearInternal(); + + Connectivity nodeConnectivity( const SMDS_MeshNode* ); + QString formatConnectivity( Connectivity, int ); + XYZ gravityCenter( const SMDS_MeshElement* ); + +private slots: + void showPrevious(); + void showNext(); + void updateControls(); + +private: + SMESH_Actor* myActor; + QList myIDs; + int myIsElement; + QWidget* myFrame; + ExtraWidget* myExtra; + int myIndex; +}; + +class SMESHGUI_EXPORT SMESHGUI_SimpleElemInfo : public SMESHGUI_ElemInfo +{ +public: + SMESHGUI_SimpleElemInfo( QWidget* = 0 ); + +protected: + void information( const QList& ); + void clearInternal(); + +private: + QTextBrowser* myInfo; +}; + +class SMESHGUI_EXPORT SMESHGUI_TreeElemInfo : public SMESHGUI_ElemInfo +{ + class ItemDelegate; + + enum { Bold = 0x01, All = 0x80 }; + +public: + SMESHGUI_TreeElemInfo( QWidget* = 0 ); + +protected: + void information( const QList& ); + void clearInternal(); + +private: + QTreeWidgetItem* createItem( QTreeWidgetItem* = 0, int = 0 ); + +private: + QTreeWidget* myInfo; +}; + +class GrpComputor: public QObject +{ + Q_OBJECT; + +public: + GrpComputor( SMESH::SMESH_GroupBase_ptr, QTreeWidgetItem*, QObject* ); + QTreeWidgetItem* getItem() { return myItem; } + +public slots: + void compute(); + +private: + SMESH::SMESH_GroupBase_var myGroup; + QTreeWidgetItem* myItem; +}; + +class SMESHGUI_EXPORT SMESHGUI_AddInfo : public QTreeWidget +{ + Q_OBJECT; + + enum { Bold = 0x01, All = 0x80 }; + +public: + SMESHGUI_AddInfo( QWidget* = 0 ); + ~SMESHGUI_AddInfo(); + + void showInfo( SMESH::SMESH_IDSource_ptr ); + // void clear(); + +private slots: + void changeLoadToCompute(); + void showPreviousGroups(); + void showNextGroups(); + void showPreviousSubMeshes(); + void showNextSubMeshes(); + +private: + QTreeWidgetItem* createItem( QTreeWidgetItem* = 0, int = 0 ); + void meshInfo( SMESH::SMESH_Mesh_ptr, QTreeWidgetItem* ); + void subMeshInfo( SMESH::SMESH_subMesh_ptr, QTreeWidgetItem* ); + void groupInfo( SMESH::SMESH_GroupBase_ptr, QTreeWidgetItem* ); + + void showGroups(); + void showSubMeshes(); + +private: + QList myComputors; + SMESH::ListOfGroups_var myGroups; + SMESH::submesh_array_var mySubMeshes; +}; + +class SMESHGUI_EXPORT SMESHGUI_MeshInfoDlg : public QDialog +{ + Q_OBJECT; + + enum { NodeMode, ElemMode }; + +public: + //! Information type + enum { + BaseInfo, //!< base mesh information + ElemInfo, //!< mesh element information + AddInfo //!< additional information + }; + + SMESHGUI_MeshInfoDlg( QWidget* = 0, int = BaseInfo ); + ~SMESHGUI_MeshInfoDlg(); + + void showInfo( const Handle(SALOME_InteractiveObject)& ); + void reject(); + +protected: + void keyPressEvent( QKeyEvent* ); + void enterEvent( QEvent* ); + +private slots: + void help(); + void updateSelection(); + void updateInfo(); + void activate(); + void deactivate(); + void modeChanged(); + void idChanged(); + +private: + QTabWidget* myTabWidget; + SMESHGUI_MeshInfo* myBaseInfo; + QButtonGroup* myMode; + QLineEdit* myID; + SMESHGUI_ElemInfo* myElemInfo; + SMESHGUI_AddInfo* myAddInfo; + SMESH_Actor* myActor; +}; + +#endif // SMESHGUI_MESHINFO_H diff --git a/src/SMESHGUI/SMESHGUI_MeshInfosBox.cxx b/src/SMESHGUI/SMESHGUI_MeshInfosBox.cxx new file mode 100644 index 000000000..17f653938 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshInfosBox.cxx @@ -0,0 +1,475 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshInfosBox.cxx +// Author : Edward AGAPOV, Open CASCADE S.A.S. +// SMESH includes +// +#include "SMESHGUI_MeshInfosBox.h" + +#include "SMDSAbs_ElementType.hxx" + +// Qt includes +#include +#include +#include + +#define SPACING 6 +#define MARGIN 11 + +#define COLONIZE(str) (QString(str).contains(":") > 0 ? QString(str) : QString(str) + " :" ) + +static void addSeparator( QWidget* parent ) +{ + QGridLayout* l = qobject_cast( parent->layout() ); + int row = l->rowCount(); + int cols = l->columnCount(); + for ( int i = 0; i < cols; i++ ) { + QFrame* hline = new QFrame( parent ); + hline->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + l->addWidget( hline, row, i ); + } +} + +enum TCol { + COL_ALGO = 0, COL_SHAPE, COL_ERROR, COL_SHAPEID, COL_PUBLISHED, COL_BAD_MESH, NB_COLUMNS +}; + +// ========================================================================================= +/*! + * \brief Box showing mesh info + */ +// ========================================================================================= + +SMESHGUI_MeshInfosBox::SMESHGUI_MeshInfosBox(const bool full, QWidget* theParent) +: QGroupBox( tr("SMESH_MESHINFO_TITLE"), theParent ), myFull( full ), + myNbNode(0), + my0DElem(0), + myBall(0), + myNbEdge(0), myNbLinEdge(0), myNbQuadEdge(0), + myNbTrai(0), myNbLinTrai(0), myNbQuadTrai(0), + myNbQuad(0), myNbLinQuad(0), myNbQuadQuad(0), + myNbFace(0), myNbLinFace(0), myNbQuadFace(0), + myNbPolyg(0), + myNbHexa(0), myNbLinHexa(0), myNbQuadHexa(0), + myNbTetra(0),myNbLinTetra(0),myNbQuadTetra(0), + myNbPyra(0), myNbLinPyra(0), myNbQuadPyra(0), + myNbPrism(0),myNbLinPrism(0), myNbQuadPrism(0), + myNbVolum(0), myNbLinVolum(0), myNbQuadVolum(0), + myNbHexaPrism(0), + myNbPolyh(0) +{ + QGridLayout* l = new QGridLayout(this); + l->setMargin( MARGIN ); + l->setSpacing( SPACING ); + + QFont italic = font(); italic.setItalic(true); + QFont bold = font(); bold.setBold(true); + + QLabel* lab; + int row = 0; + + // title + lab = new QLabel( this ); + lab->setMinimumWidth(100); lab->setFont( italic ); + l->addWidget( lab, row, 0 ); + // -- + lab = new QLabel(tr("SMESH_MESHINFO_ORDER0"), this ); + lab->setMinimumWidth(100); lab->setFont( italic ); + l->addWidget( lab, row, 1 ); + // -- + lab = new QLabel(tr("SMESH_MESHINFO_ORDER1"), this ); + lab->setMinimumWidth(100); lab->setFont( italic ); + l->addWidget( lab, row, 2 ); + // -- + lab = new QLabel(tr("SMESH_MESHINFO_ORDER2"), this ); + lab->setMinimumWidth(100); lab->setFont( italic ); + l->addWidget( lab, row, 3 ); + + if ( myFull ) + { + // nodes + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), this ); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + myNbNode = new QLabel( this ); + l->addWidget( myNbNode, row, 1 ); + + addSeparator(this); // add separator + + // 0D elements + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_0DELEMS")), this ); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + my0DElem = new QLabel( this ); + l->addWidget( my0DElem, row, 1 ); + + addSeparator(this); // add separator + + // balls + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_BALLS")), this ); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + myBall = new QLabel( this ); + l->addWidget( myBall, row, 1 ); + + addSeparator(this); // add separator + + // edges + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), this ); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + myNbEdge = new QLabel( this ); + l->addWidget( myNbEdge, row, 1 ); + // -- + myNbLinEdge = new QLabel( this ); + l->addWidget( myNbLinEdge, row, 2 ); + // -- + myNbQuadEdge = new QLabel( this ); + l->addWidget( myNbQuadEdge, row, 3 ); + + addSeparator(this); // add separator + + // faces + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_FACES")), this); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + myNbFace = new QLabel( this ); + l->addWidget( myNbFace, row, 1 ); + // -- + myNbLinFace = new QLabel( this ); + l->addWidget( myNbLinFace, row, 2 ); + // -- + myNbQuadFace = new QLabel( this ); + l->addWidget( myNbQuadFace, row, 3 ); + // -- + row++; // increment row count + // ... triangles + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TRIANGLES")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbTrai = new QLabel( this ); + l->addWidget( myNbTrai, row, 1 ); + // -- + myNbLinTrai = new QLabel( this ); + l->addWidget( myNbLinTrai, row, 2 ); + // -- + myNbQuadTrai = new QLabel( this ); + l->addWidget( myNbQuadTrai, row, 3 ); + // -- + row++; // increment row count + // ... quadrangles + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_QUADRANGLES")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbQuad = new QLabel( this ); + l->addWidget( myNbQuad, row, 1 ); + // -- + myNbLinQuad = new QLabel( this ); + l->addWidget( myNbLinQuad, row, 2 ); + // -- + myNbQuadQuad = new QLabel( this ); + l->addWidget( myNbQuadQuad, row, 3 ); + // -- + row++; // increment row count + // ... poligones + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_POLYGONES")), this ); + l->addWidget( lab, row, 0 ); + myNbPolyg = new QLabel( this ); + l->addWidget( myNbPolyg, row, 1 ); + + addSeparator(this); // add separator + + // volumes + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_VOLUMES")), this); + lab->setFont( bold ); + l->addWidget( lab, row, 0 ); + // -- + myNbVolum = new QLabel( this ); + l->addWidget( myNbVolum, row, 1 ); + // -- + myNbLinVolum = new QLabel( this ); + l->addWidget( myNbLinVolum, row, 2 ); + // -- + myNbQuadVolum = new QLabel( this ); + l->addWidget( myNbQuadVolum, row, 3 ); + // -- + row++; // increment row count + // ... tetras + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TETRAS")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbTetra = new QLabel( this ); + l->addWidget( myNbTetra, row, 1 ); + // -- + myNbLinTetra = new QLabel( this ); + l->addWidget( myNbLinTetra, row, 2 ); + // -- + myNbQuadTetra = new QLabel( this ); + l->addWidget( myNbQuadTetra, row, 3 ); + // -- + row++; // increment row count + // ... hexas + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_HEXAS")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbHexa = new QLabel( this ); + l->addWidget( myNbHexa, row, 1 ); + // -- + myNbLinHexa = new QLabel( this ); + l->addWidget( myNbLinHexa, row, 2 ); + // -- + myNbQuadHexa = new QLabel( this ); + l->addWidget( myNbQuadHexa, row, 3 ); + // -- + row++; // increment row count + // ... pyras + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PYRAS")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbPyra = new QLabel( this ); + l->addWidget( myNbPyra, row, 1 ); + // -- + myNbLinPyra = new QLabel( this ); + l->addWidget( myNbLinPyra, row, 2 ); + // -- + myNbQuadPyra = new QLabel( this ); + l->addWidget( myNbQuadPyra, row, 3 ); + // -- + row++; // increment row count + // ... prisms + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PRISMS")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbPrism = new QLabel( this ); + l->addWidget( myNbPrism, row, 1 ); + // -- + myNbLinPrism = new QLabel( this ); + l->addWidget( myNbLinPrism, row, 2 ); + // -- + myNbQuadPrism = new QLabel( this ); + l->addWidget( myNbQuadPrism, row, 3 ); + // -- + row++; // increment row count + // ... hexa prisms + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_HEXAPRISM")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbHexaPrism = new QLabel( this ); + l->addWidget( myNbHexaPrism, row, 1 ); + // -- + row++; // increment row count + // ... polyedres + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_POLYEDRES")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbPolyh = new QLabel( this ); + l->addWidget( myNbPolyh, row, 1 ); + } + else + { + // nodes + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbNode = new QLabel( this ); + l->addWidget( myNbNode, row, 1 ); + + // 0D elements + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_0DELEMS")), this ); + l->addWidget( lab, row, 0 ); + // -- + my0DElem = new QLabel( this ); + l->addWidget( my0DElem, row, 1 ); + + addSeparator(this); // add separator + + // edges + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), this ); + l->addWidget( lab, row, 0 ); + // -- + myNbEdge = new QLabel( this ); + l->addWidget( myNbEdge, row, 1 ); + // -- + myNbLinEdge = new QLabel( this ); + l->addWidget( myNbLinEdge, row, 2 ); + // -- + myNbQuadEdge = new QLabel( this ); + l->addWidget( myNbQuadEdge, row, 3 ); + + // faces + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_FACES")), this); + l->addWidget( lab, row, 0 ); + // -- + myNbFace = new QLabel( this ); + l->addWidget( myNbFace, row, 1 ); + // -- + myNbLinFace = new QLabel( this ); + l->addWidget( myNbLinFace, row, 2 ); + // -- + myNbQuadFace = new QLabel( this ); + l->addWidget( myNbQuadFace, row, 3 ); + + // volumes + row = l->rowCount(); // retrieve current row count + // -- + lab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_VOLUMES")), this); + l->addWidget( lab, row, 0 ); + // -- + myNbVolum = new QLabel( this ); + l->addWidget( myNbVolum, row, 1 ); + // -- + myNbLinVolum = new QLabel( this ); + l->addWidget( myNbLinVolum, row, 2 ); + // -- + myNbQuadVolum = new QLabel( this ); + l->addWidget( myNbQuadVolum, row, 3 ); + } +} + +// ========================================================================================= +/*! + * \brief Set mesh info + */ +// ========================================================================================= + +void SMESHGUI_MeshInfosBox::SetMeshInfo(const SMESH::long_array& theInfo) +{ + // nodes + myNbNode ->setText( QString("%1").arg( theInfo[SMDSEntity_Node] )); + + //0D elements + my0DElem ->setText( QString("%1").arg( theInfo[SMDSEntity_0D] )); + + //balls + myBall ->setText( QString("%1").arg( theInfo[SMDSEntity_Ball] )); + + // edges + myNbEdge ->setText( QString("%1").arg( theInfo[SMDSEntity_Edge] + + theInfo[SMDSEntity_Quad_Edge] )); + myNbLinEdge ->setText( QString("%1").arg( theInfo[SMDSEntity_Edge] )); + myNbQuadEdge ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Edge] )); + + // faces + myNbFace ->setText( QString("%1").arg( theInfo[SMDSEntity_Triangle] + + theInfo[SMDSEntity_Quad_Triangle] + + theInfo[SMDSEntity_Quadrangle] + + theInfo[SMDSEntity_Quad_Quadrangle] + + theInfo[SMDSEntity_BiQuad_Quadrangle] + + theInfo[SMDSEntity_Polygon] )); + myNbLinFace ->setText( QString("%1").arg( theInfo[SMDSEntity_Triangle] + + theInfo[SMDSEntity_Quadrangle] + + theInfo[SMDSEntity_Polygon] )); + myNbQuadFace ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Triangle] + + theInfo[SMDSEntity_Quad_Quadrangle] + + theInfo[SMDSEntity_BiQuad_Quadrangle] )); + + // volumes + myNbVolum ->setText( QString("%1").arg( theInfo[SMDSEntity_Tetra] + + theInfo[SMDSEntity_Quad_Tetra] + + theInfo[SMDSEntity_Pyramid] + + theInfo[SMDSEntity_Quad_Pyramid] + + theInfo[SMDSEntity_Hexa] + + theInfo[SMDSEntity_Quad_Hexa] + + theInfo[SMDSEntity_TriQuad_Hexa] + + theInfo[SMDSEntity_Penta] + + theInfo[SMDSEntity_Quad_Penta] + + theInfo[SMDSEntity_Hexagonal_Prism] + + theInfo[SMDSEntity_Polyhedra] )); + myNbLinVolum ->setText( QString("%1").arg( theInfo[SMDSEntity_Tetra] + + theInfo[SMDSEntity_Pyramid] + + theInfo[SMDSEntity_Hexa] + + theInfo[SMDSEntity_Penta] + + theInfo[SMDSEntity_Polyhedra] )); + myNbQuadVolum->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Tetra] + + theInfo[SMDSEntity_Quad_Pyramid] + + theInfo[SMDSEntity_Quad_Hexa] + + theInfo[SMDSEntity_TriQuad_Hexa] + + theInfo[SMDSEntity_Quad_Penta] )); + + if ( myFull ) + { + // triangles + myNbTrai ->setText( QString("%1").arg( theInfo[SMDSEntity_Triangle] + + theInfo[SMDSEntity_Quad_Triangle] )); + myNbLinTrai ->setText( QString("%1").arg( theInfo[SMDSEntity_Triangle] )); + myNbQuadTrai ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Triangle] )); + // quadrangles + myNbQuad ->setText( QString("%1").arg( theInfo[SMDSEntity_Quadrangle] + + theInfo[SMDSEntity_Quad_Quadrangle] + + theInfo[SMDSEntity_BiQuad_Quadrangle] )); + myNbLinQuad ->setText( QString("%1").arg( theInfo[SMDSEntity_Quadrangle] )); + myNbQuadQuad ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Quadrangle] + + theInfo[SMDSEntity_BiQuad_Quadrangle])); + // poligones + myNbPolyg ->setText( QString("%1").arg( theInfo[SMDSEntity_Polygon] )); + + // tetras + myNbTetra ->setText( QString("%1").arg( theInfo[SMDSEntity_Tetra] + + theInfo[SMDSEntity_Quad_Tetra] )); + myNbLinTetra ->setText( QString("%1").arg( theInfo[SMDSEntity_Tetra] )); + myNbQuadTetra->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Tetra] )); + // hexas + myNbHexa ->setText( QString("%1").arg( theInfo[SMDSEntity_Hexa] + + theInfo[SMDSEntity_TriQuad_Hexa], + theInfo[SMDSEntity_Quad_Hexa] )); + myNbLinHexa ->setText( QString("%1").arg( theInfo[SMDSEntity_Hexa] )); + myNbQuadHexa ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Hexa] + + theInfo[SMDSEntity_TriQuad_Hexa] )); + // pyras + myNbPyra ->setText( QString("%1").arg( theInfo[SMDSEntity_Pyramid] + + theInfo[SMDSEntity_Quad_Pyramid] )); + myNbLinPyra ->setText( QString("%1").arg( theInfo[SMDSEntity_Pyramid] )); + myNbQuadPyra ->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Pyramid] )); + // prisms + myNbPrism ->setText( QString("%1").arg( theInfo[SMDSEntity_Penta] + + theInfo[SMDSEntity_Quad_Penta] )); + myNbLinPrism ->setText( QString("%1").arg( theInfo[SMDSEntity_Penta] )); + myNbQuadPrism->setText( QString("%1").arg( theInfo[SMDSEntity_Quad_Penta] )); + // octahedra + myNbHexaPrism->setText( QString("%1").arg( theInfo[ SMDSEntity_Hexagonal_Prism ])); + // polyedres + myNbPolyh ->setText( QString("%1").arg( theInfo[SMDSEntity_Polyhedra] )); + } +} diff --git a/src/SMESHGUI/SMESHGUI_MeshInfosBox.h b/src/SMESHGUI/SMESHGUI_MeshInfosBox.h new file mode 100644 index 000000000..a90a37b29 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshInfosBox.h @@ -0,0 +1,88 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshInfosBox.h +// Author : Edward AGAPOV, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_MeshInfosBox_H +#define SMESHGUI_MeshInfosBox_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +// Qt includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QLabel; + +/*! + * \brief Box showing mesh info + */ + +class SMESHGUI_EXPORT SMESHGUI_MeshInfosBox : public QGroupBox +{ + Q_OBJECT + +public: + SMESHGUI_MeshInfosBox( const bool, QWidget* ); + + void SetMeshInfo( const SMESH::long_array& theInfo ); + +private: + bool myFull; + QLabel* myNbNode; + QLabel* my0DElem; + QLabel* myBall; + QLabel* myNbEdge; + QLabel* myNbLinEdge; + QLabel* myNbQuadEdge; + QLabel* myNbTrai; + QLabel* myNbLinTrai; + QLabel* myNbQuadTrai; + QLabel* myNbQuad; + QLabel* myNbLinQuad; + QLabel* myNbQuadQuad; + QLabel* myNbFace; + QLabel* myNbLinFace; + QLabel* myNbQuadFace; + QLabel* myNbPolyg; + QLabel* myNbHexa; + QLabel* myNbLinHexa; + QLabel* myNbQuadHexa; + QLabel* myNbTetra; + QLabel* myNbLinTetra; + QLabel* myNbQuadTetra; + QLabel* myNbPyra; + QLabel* myNbLinPyra; + QLabel* myNbQuadPyra; + QLabel* myNbPrism; + QLabel* myNbLinPrism; + QLabel* myNbQuadPrism; + QLabel* myNbVolum; + QLabel* myNbLinVolum; + QLabel* myNbQuadVolum; + QLabel* myNbHexaPrism; + QLabel* myNbPolyh; +}; + +#endif // SMESHGUI_MeshInfosBox_H diff --git a/src/SMESHGUI/SMESHGUI_MeshInfosDlg.cxx b/src/SMESHGUI/SMESHGUI_MeshInfosDlg.cxx deleted file mode 100644 index 005183023..000000000 --- a/src/SMESHGUI/SMESHGUI_MeshInfosDlg.cxx +++ /dev/null @@ -1,645 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_MeshInfosDlg.cxx -// Author : Nicolas BARBEROU -// SMESH includes -// -#include "SMESHGUI_MeshInfosDlg.h" - -#include "SMESHGUI.h" -#include "SMESHGUI_Utils.h" - -// SALOME GUI includes -#include -#include -#include -#include -#include - -#include -#include -#include - -// SALOME KERNEL includes -#include - -// Qt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// IDL includes -#include -#include CORBA_SERVER_HEADER(SMESH_Mesh) -#include CORBA_SERVER_HEADER(SMESH_Group) - -#define COLONIZE(str) (QString(str).contains(":") > 0 ? QString(str) : QString(str) + " :" ) -#define SPACING 6 -#define MARGIN 11 - -//================================================================================= -// function : SMESHGUI_MeshInfosDlg() -// purpose : Constructor -//================================================================================= -SMESHGUI_MeshInfosDlg::SMESHGUI_MeshInfosDlg(SMESHGUI* theModule): - QDialog(SMESH::GetDesktop(theModule)), - mySelectionMgr(SMESH::GetSelectionMgr(theModule)), - mySMESHGUI(theModule) -{ - setModal( false ); - setAttribute( Qt::WA_DeleteOnClose, true ); - setWindowTitle(tr("SMESH_MESHINFO_TITLE")); - setSizeGripEnabled(true); - - myStartSelection = true; - myIsActiveWindow = true; - - QVBoxLayout* aTopLayout = new QVBoxLayout(this); - aTopLayout->setSpacing(SPACING); aTopLayout->setMargin(MARGIN); - - // select button & label - QPixmap image0(SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH",tr("ICON_SELECT"))); - mySelectBtn = new QPushButton(this); - mySelectBtn->setIcon(image0); - mySelectBtn->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - - mySelectLab = new QLabel(this); - mySelectLab->setAlignment(Qt::AlignCenter); - QFont fnt = mySelectLab->font(); fnt.setBold(true); - mySelectLab->setFont(fnt); - - QHBoxLayout* aSelectLayout = new QHBoxLayout; - aSelectLayout->setMargin(0); aSelectLayout->setSpacing(0); - aSelectLayout->addWidget(mySelectBtn); - aSelectLayout->addWidget(mySelectLab); - - // top widget stack - myWGStack = new QStackedWidget(this); - - // no valid selection - QWidget* myBadWidget = new QWidget(myWGStack); - QVBoxLayout* aBadLayout = new QVBoxLayout(myBadWidget); - QLabel* myBadLab = new QLabel(tr("SMESH_BAD_SELECTION"), myBadWidget); - myBadLab->setAlignment(Qt::AlignCenter); - myBadLab->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); - aBadLayout->addWidget(myBadLab); - myWGStack->addWidget(myBadWidget); - - // mesh - myMeshWidget = new QWidget(myWGStack); - QGridLayout* aMeshLayout = new QGridLayout(myMeshWidget); - aMeshLayout->setSpacing(SPACING); aMeshLayout->setMargin(0); - myWGStack->addWidget(myMeshWidget); - - // --> name - QLabel* myMeshNameLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NAME")), myMeshWidget); - myMeshName = new QLabel(myMeshWidget); - myMeshName->setMinimumWidth(100); - QFrame* line1 = new QFrame(myMeshWidget); - line1->setFrameStyle(QFrame::HLine | QFrame::Sunken); - - // --> nodes - QLabel* myMeshNbNodesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), myMeshWidget); - myMeshNbNodes = new QLabel(myMeshWidget); - myMeshNbNodes->setMinimumWidth(100); - - // --> header with orders - QLabel* myMeshOrder0Lab = new QLabel(tr("SMESH_MESHINFO_ORDER0"), myMeshWidget); - QLabel* myMeshOrder1Lab = new QLabel(tr("SMESH_MESHINFO_ORDER1"), myMeshWidget); - QLabel* myMeshOrder2Lab = new QLabel(tr("SMESH_MESHINFO_ORDER2"), myMeshWidget); - QFont fnti = myMeshOrder0Lab->font(); fnti.setItalic(true); - myMeshOrder0Lab->setFont(fnti); - myMeshOrder1Lab->setFont(fnti); - myMeshOrder2Lab->setFont(fnti); - - // --> edges - QLabel* myMeshNbEdgesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), myMeshWidget); - myMeshNbEdges = new QLabel(myMeshWidget); - myMeshNbEdges->setMinimumWidth(100); - myMeshNbEdges1 = new QLabel(myMeshWidget); - myMeshNbEdges1->setMinimumWidth(100); - myMeshNbEdges2 = new QLabel(myMeshWidget); - myMeshNbEdges2->setMinimumWidth(100); - - // --> faces - myMeshFacesGroup = new QGroupBox(tr("SMESH_MESHINFO_FACES"), myMeshWidget); - QGridLayout* myMeshFacesGroupLayout = new QGridLayout(myMeshFacesGroup); - myMeshFacesGroupLayout->setSpacing(SPACING); myMeshFacesGroupLayout->setMargin(MARGIN); - - // --> faces --> total - QLabel* myMeshNbFacesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TOTAL")), myMeshFacesGroup); - myMeshNbFacesLab->setFont(fnt); - myMeshNbFaces = new QLabel(myMeshFacesGroup); - myMeshNbFaces->setMinimumWidth(100); - myMeshNbFaces->setFont(fnt); - myMeshNbFaces1 = new QLabel(myMeshFacesGroup); - myMeshNbFaces1->setMinimumWidth(100); - myMeshNbFaces1->setFont(fnt); - myMeshNbFaces2 = new QLabel(myMeshFacesGroup); - myMeshNbFaces2->setMinimumWidth(100); - myMeshNbFaces2->setFont(fnt); - - // --> faces --> triangles - QLabel* myMeshNbTrianglesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TRIANGLES")), myMeshFacesGroup); - myMeshNbTriangles = new QLabel(myMeshFacesGroup); - myMeshNbTriangles->setMinimumWidth(100); - myMeshNbTriangles1 = new QLabel(myMeshFacesGroup); - myMeshNbTriangles1->setMinimumWidth(100); - myMeshNbTriangles2 = new QLabel(myMeshFacesGroup); - myMeshNbTriangles2->setMinimumWidth(100); - - // --> faces --> quadrangles - QLabel* myMeshNbQuadranglesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_QUADRANGLES")), myMeshFacesGroup); - myMeshNbQuadrangles = new QLabel(myMeshFacesGroup); - myMeshNbQuadrangles->setMinimumWidth(100); - myMeshNbQuadrangles1 = new QLabel(myMeshFacesGroup); - myMeshNbQuadrangles1->setMinimumWidth(100); - myMeshNbQuadrangles2 = new QLabel(myMeshFacesGroup); - myMeshNbQuadrangles2->setMinimumWidth(100); - - // --> faces --> polygons - QLabel* myMeshNbPolygonesLab = new QLabel( COLONIZE( tr( "SMESH_MESHINFO_POLYGONES" ) ), myMeshFacesGroup ); - myMeshNbPolygones = new QLabel( myMeshFacesGroup ); - myMeshNbPolygones->setMinimumWidth( 100 ); - - myMeshFacesGroupLayout->addWidget(myMeshNbFacesLab, 0, 0); - myMeshFacesGroupLayout->addWidget(myMeshNbFaces, 0, 1); - myMeshFacesGroupLayout->addWidget(myMeshNbFaces1, 0, 2); - myMeshFacesGroupLayout->addWidget(myMeshNbFaces2, 0, 3); - myMeshFacesGroupLayout->addWidget(myMeshNbTrianglesLab, 1, 0); - myMeshFacesGroupLayout->addWidget(myMeshNbTriangles, 1, 1); - myMeshFacesGroupLayout->addWidget(myMeshNbTriangles1, 1, 2); - myMeshFacesGroupLayout->addWidget(myMeshNbTriangles2, 1, 3); - myMeshFacesGroupLayout->addWidget(myMeshNbQuadranglesLab, 2, 0); - myMeshFacesGroupLayout->addWidget(myMeshNbQuadrangles, 2, 1); - myMeshFacesGroupLayout->addWidget(myMeshNbQuadrangles1, 2, 2); - myMeshFacesGroupLayout->addWidget(myMeshNbQuadrangles2, 2, 3); - myMeshFacesGroupLayout->addWidget(myMeshNbPolygonesLab, 3, 0); - myMeshFacesGroupLayout->addWidget(myMeshNbPolygones, 3, 1); - - // --> volumes - myMeshVolumesGroup = new QGroupBox(tr("SMESH_MESHINFO_VOLUMES"), myMeshWidget); - QGridLayout* myMeshVolumesGroupLayout = new QGridLayout(myMeshVolumesGroup); - myMeshVolumesGroupLayout->setSpacing(SPACING); myMeshVolumesGroupLayout->setMargin(MARGIN); - - // --> volumes --> total - QLabel* myMeshNbVolumesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TOTAL")), myMeshVolumesGroup); - myMeshNbVolumesLab->setFont(fnt); - myMeshNbVolumes = new QLabel(myMeshVolumesGroup); - myMeshNbVolumes->setMinimumWidth(100); - myMeshNbVolumes->setFont(fnt); - myMeshNbVolumes1 = new QLabel(myMeshVolumesGroup); - myMeshNbVolumes1->setMinimumWidth(100); - myMeshNbVolumes1->setFont(fnt); - myMeshNbVolumes2 = new QLabel(myMeshVolumesGroup); - myMeshNbVolumes2->setMinimumWidth(100); - myMeshNbVolumes2->setFont(fnt); - - // --> volumes --> tetrahedrons - QLabel* myMeshNbTetraLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TETRAS")), myMeshVolumesGroup); - myMeshNbTetra = new QLabel(myMeshVolumesGroup); - myMeshNbTetra->setMinimumWidth(100); - myMeshNbTetra1 = new QLabel(myMeshVolumesGroup); - myMeshNbTetra1->setMinimumWidth(100); - myMeshNbTetra2 = new QLabel(myMeshVolumesGroup); - myMeshNbTetra2->setMinimumWidth(100); - - // --> volumes --> hexahedrons - QLabel* myMeshNbHexaLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_HEXAS")), myMeshVolumesGroup); - myMeshNbHexa = new QLabel(myMeshVolumesGroup); - myMeshNbHexa->setMinimumWidth(100); - myMeshNbHexa1 = new QLabel(myMeshVolumesGroup); - myMeshNbHexa1->setMinimumWidth(100); - myMeshNbHexa2 = new QLabel(myMeshVolumesGroup); - myMeshNbHexa2->setMinimumWidth(100); - - // --> volumes --> prisms - QLabel* myMeshNbPrismLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PRISMS")), myMeshVolumesGroup); - myMeshNbPrism = new QLabel(myMeshVolumesGroup); - myMeshNbPrism->setMinimumWidth(100); - myMeshNbPrism1 = new QLabel(myMeshVolumesGroup); - myMeshNbPrism1->setMinimumWidth(100); - myMeshNbPrism2 = new QLabel(myMeshVolumesGroup); - myMeshNbPrism2->setMinimumWidth(100); - - // --> volumes --> pyramids - QLabel* myMeshNbPyraLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_PYRAS")), myMeshVolumesGroup); - myMeshNbPyra = new QLabel(myMeshVolumesGroup); - myMeshNbPyra->setMinimumWidth(100); - myMeshNbPyra1 = new QLabel(myMeshVolumesGroup); - myMeshNbPyra1->setMinimumWidth(100); - myMeshNbPyra2 = new QLabel(myMeshVolumesGroup); - myMeshNbPyra2->setMinimumWidth(100); - - // --> volumes --> polyherones - QLabel* myMeshNbPolyhedronesLab = new QLabel( COLONIZE( tr( "SMESH_MESHINFO_POLYEDRES" ) ), myMeshVolumesGroup ); - myMeshNbPolyhedrones = new QLabel( myMeshVolumesGroup ); - myMeshNbPolyhedrones->setMinimumWidth( 100 ); - - myMeshVolumesGroupLayout->addWidget(myMeshNbVolumesLab, 0, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbVolumes, 0, 1); - myMeshVolumesGroupLayout->addWidget(myMeshNbVolumes1, 0, 2); - myMeshVolumesGroupLayout->addWidget(myMeshNbVolumes2, 0, 3); - myMeshVolumesGroupLayout->addWidget(myMeshNbTetraLab, 1, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbTetra, 1, 1); - myMeshVolumesGroupLayout->addWidget(myMeshNbTetra1, 1, 2); - myMeshVolumesGroupLayout->addWidget(myMeshNbTetra2, 1, 3); - myMeshVolumesGroupLayout->addWidget(myMeshNbHexaLab, 2, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbHexa, 2, 1); - myMeshVolumesGroupLayout->addWidget(myMeshNbHexa1, 2, 2); - myMeshVolumesGroupLayout->addWidget(myMeshNbHexa2, 2, 3); - myMeshVolumesGroupLayout->addWidget(myMeshNbPrismLab, 3, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbPrism, 3, 1); - myMeshVolumesGroupLayout->addWidget(myMeshNbPrism1, 3, 2); - myMeshVolumesGroupLayout->addWidget(myMeshNbPrism2, 3, 3); - myMeshVolumesGroupLayout->addWidget(myMeshNbPyraLab, 4, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbPyra, 4, 1); - myMeshVolumesGroupLayout->addWidget(myMeshNbPyra1, 4, 2); - myMeshVolumesGroupLayout->addWidget(myMeshNbPyra2, 4, 3); - myMeshVolumesGroupLayout->addWidget(myMeshNbPolyhedronesLab, 5, 0); - myMeshVolumesGroupLayout->addWidget(myMeshNbPolyhedrones, 5, 1); - - aMeshLayout->addWidget(myMeshNameLab, 0, 0); - aMeshLayout->addWidget(myMeshName, 0, 1); - aMeshLayout->addWidget(line1, 1, 0, 1, 2); - aMeshLayout->addWidget(myMeshNbNodesLab, 2, 0); - aMeshLayout->addWidget(myMeshNbNodes, 2, 1); - aMeshLayout->addWidget(myMeshOrder0Lab, 3, 1); - aMeshLayout->addWidget(myMeshOrder1Lab, 3, 2); - aMeshLayout->addWidget(myMeshOrder2Lab, 3, 3); - aMeshLayout->addWidget(myMeshNbEdgesLab, 4, 0); - aMeshLayout->addWidget(myMeshNbEdges, 4, 1); - aMeshLayout->addWidget(myMeshNbEdges1, 4, 2); - aMeshLayout->addWidget(myMeshNbEdges2, 4, 3); - aMeshLayout->addWidget(myMeshFacesGroup, 5, 0, 1, 4); - aMeshLayout->addWidget(myMeshVolumesGroup, 6, 0, 1, 4); - aMeshLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 7, 0); - - // submesh - mySubMeshWidget = new QWidget(myWGStack); - QGridLayout* aSubMeshLayout = new QGridLayout(mySubMeshWidget); - aSubMeshLayout->setSpacing(SPACING); aSubMeshLayout->setMargin(0); - myWGStack->addWidget(mySubMeshWidget); - - // --> name - QLabel* mySubMeshNameLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NAME")), mySubMeshWidget); - mySubMeshName = new QLabel(mySubMeshWidget); - mySubMeshName->setMinimumWidth(100); - QFrame* line2 = new QFrame(mySubMeshWidget); - line2->setFrameStyle(QFrame::HLine | QFrame::Sunken); - - // --> nodes - QLabel* mySubMeshNbNodesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NODES")), mySubMeshWidget); - mySubMeshNbNodes = new QLabel(mySubMeshWidget); - mySubMeshNbNodes->setMinimumWidth(100); - - // --> elements - mySubMeshElementsGroup = new QGroupBox(tr("SMESH_MESHINFO_ELEMENTS"), mySubMeshWidget); - QGridLayout* mySubMeshElementsGroupLayout = new QGridLayout(mySubMeshElementsGroup); - mySubMeshElementsGroupLayout->setSpacing(SPACING); mySubMeshElementsGroupLayout->setMargin(MARGIN); - - // --> elements --> total - QLabel* mySubMeshNbElementsLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TOTAL")), mySubMeshElementsGroup); - mySubMeshNbElementsLab->setFont(fnt); - mySubMeshNbElements = new QLabel(mySubMeshElementsGroup); - mySubMeshNbElements->setMinimumWidth(100); - mySubMeshNbElements->setFont(fnt); - - // --> elements --> edges - QLabel* mySubMeshNbEdgesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_EDGES")), mySubMeshElementsGroup); - mySubMeshNbEdges = new QLabel(mySubMeshElementsGroup); - mySubMeshNbEdges->setMinimumWidth(100); - - // --> elements --> faces - QLabel* mySubMeshNbFacesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_FACES")), mySubMeshElementsGroup); - mySubMeshNbFaces = new QLabel(mySubMeshElementsGroup); - mySubMeshNbFaces->setMinimumWidth(100); - - // --> elements --> volumes - QLabel* mySubMeshNbVolumesLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_VOLUMES")), mySubMeshElementsGroup); - mySubMeshNbVolumes = new QLabel(mySubMeshElementsGroup); - mySubMeshNbVolumes->setMinimumWidth(100); - - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbElementsLab, 0, 0); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbElements, 0, 1); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbEdgesLab, 1, 0); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbEdges, 1, 1); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbFacesLab, 2, 0); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbFaces, 2, 1); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbVolumesLab, 3, 0); - mySubMeshElementsGroupLayout->addWidget(mySubMeshNbVolumes, 3, 1); - - aSubMeshLayout->addWidget(mySubMeshNameLab, 0, 0); - aSubMeshLayout->addWidget(mySubMeshName, 0, 1); - aSubMeshLayout->addWidget(line2, 1, 0, 1, 2); - aSubMeshLayout->addWidget(mySubMeshNbNodesLab, 2, 0); - aSubMeshLayout->addWidget(mySubMeshNbNodes, 2, 1); - aSubMeshLayout->addWidget(mySubMeshElementsGroup, 3, 0, 1, 2); - aSubMeshLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 4, 0); - - // group - myGroupWidget = new QWidget(myWGStack); - QGridLayout* myGroupWidgetLayout = new QGridLayout(myGroupWidget); - myGroupWidgetLayout->setSpacing(SPACING); myGroupWidgetLayout->setMargin(0); - myWGStack->addWidget(myGroupWidget); - - // --> name - QLabel* myGroupNameLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_NAME")), myGroupWidget); - myGroupName = new QLabel(myGroupWidget); - myGroupName->setMinimumWidth(100); - QFrame* line3 = new QFrame(myGroupWidget); - line3->setFrameStyle(QFrame::HLine | QFrame::Sunken); - - // --> type - QLabel* myGroupTypeLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_TYPE")), myGroupWidget); - myGroupType = new QLabel(myGroupWidget); - myGroupType->setMinimumWidth(100); - - // --> number of entities - QLabel* myGroupNbLab = new QLabel(COLONIZE(tr("SMESH_MESHINFO_ENTITIES")), myGroupWidget); - myGroupNb = new QLabel(myGroupWidget); - myGroupNb->setMinimumWidth(100); - - myGroupWidgetLayout->addWidget(myGroupNameLab, 0, 0); - myGroupWidgetLayout->addWidget(myGroupName, 0, 1); - myGroupWidgetLayout->addWidget(line3, 1, 0, 1, 2); - myGroupWidgetLayout->addWidget(myGroupTypeLab, 2, 0); - myGroupWidgetLayout->addWidget(myGroupType, 2, 1); - myGroupWidgetLayout->addWidget(myGroupNbLab, 3, 0); - myGroupWidgetLayout->addWidget(myGroupNb, 3, 1); - myGroupWidgetLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 4, 0); - - // buttons - myButtonsGroup = new QGroupBox(this); - QHBoxLayout* myButtonsGroupLayout = new QHBoxLayout(myButtonsGroup); - myButtonsGroupLayout->setSpacing(SPACING); myButtonsGroupLayout->setMargin(MARGIN); - - // buttons --> OK and Help buttons - myOkBtn = new QPushButton(tr("SMESH_BUT_OK" ), myButtonsGroup); - myOkBtn->setAutoDefault(true); myOkBtn->setDefault(true); - myHelpBtn = new QPushButton(tr("SMESH_BUT_HELP" ), myButtonsGroup); - myHelpBtn->setAutoDefault(true); - - myButtonsGroupLayout->addWidget(myOkBtn); - myButtonsGroupLayout->addSpacing(10); - myButtonsGroupLayout->addStretch(); - myButtonsGroupLayout->addWidget(myHelpBtn); - - aTopLayout->addLayout(aSelectLayout); - aTopLayout->addWidget(myWGStack); - aTopLayout->addWidget(myButtonsGroup); - - mySMESHGUI->SetActiveDialogBox(this); - - // connect signals - connect(myOkBtn, SIGNAL(clicked()), this, SLOT(close())); - connect( myHelpBtn, SIGNAL(clicked()), this, SLOT(onHelp())); - connect(mySelectBtn, SIGNAL(clicked()), this, SLOT(onStartSelection())); - connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(close())); - connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onSelectionChanged())); - - // init dialog with current selection - onSelectionChanged(); - - myHelpFileName = "mesh_infos_page.html#advanced_mesh_infos_anchor"; -} - -//================================================================================= -// function : ~SMESHGUI_MeshInfosDlg() -// purpose : Destructor -//================================================================================= -SMESHGUI_MeshInfosDlg::~SMESHGUI_MeshInfosDlg() -{ -} - -//================================================================================= -// function : DumpMeshInfos() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::DumpMeshInfos() -{ - SUIT_OverrideCursor wc; - - SALOME_ListIO aList; - mySelectionMgr->selectedObjects(aList); - - int nbSel = aList.Extent(); - if (nbSel == 1) { - myStartSelection = false; - mySelectLab->setText(""); - Handle(SALOME_InteractiveObject) IObject = aList.First(); - _PTR(SObject) aSO = SMESH::GetActiveStudyDocument()->FindObjectID(IObject->getEntry()); - if (aSO) { - //CORBA::Object_var anObject = aSO->GetObject(); - CORBA::Object_var anObject = SMESH::SObjectToObject(aSO); - if (!CORBA::is_nil(anObject)) { - SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow(anObject); - if (!aMesh->_is_nil()) { - myWGStack->setCurrentWidget(myMeshWidget); - setWindowTitle(tr("SMESH_MESHINFO_TITLE") + " [" + tr("SMESH_OBJECT_MESH") + "]"); - myMeshName->setText(aSO->GetName().c_str()); - myMeshNbNodes->setNum((int)aMesh->NbNodes()); - myMeshNbEdges->setNum((int)aMesh->NbEdges()); - myMeshNbEdges1->setNum((int)aMesh->NbEdgesOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbEdges2->setNum((int)aMesh->NbEdgesOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbFaces->setNum((int)aMesh->NbFaces()); - myMeshNbFaces1->setNum((int)aMesh->NbFacesOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbFaces2->setNum((int)aMesh->NbFacesOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbTriangles->setNum((int)aMesh->NbTriangles()); - myMeshNbTriangles1->setNum((int)aMesh->NbTrianglesOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbTriangles2->setNum((int)aMesh->NbTrianglesOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbQuadrangles->setNum((int)aMesh->NbQuadrangles()); - myMeshNbQuadrangles1->setNum((int)aMesh->NbQuadranglesOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbQuadrangles2->setNum((int)aMesh->NbQuadranglesOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbPolygones->setNum( (int)aMesh->NbPolygons() ); - myMeshNbVolumes->setNum((int)aMesh->NbVolumes()); - myMeshNbVolumes1->setNum((int)aMesh->NbVolumesOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbVolumes2->setNum((int)aMesh->NbVolumesOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbTetra->setNum((int)aMesh->NbTetras()); - myMeshNbTetra1->setNum((int)aMesh->NbTetrasOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbTetra2->setNum((int)aMesh->NbTetrasOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbHexa->setNum((int)aMesh->NbHexas()); - myMeshNbHexa1->setNum((int)aMesh->NbHexasOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbHexa2->setNum((int)aMesh->NbHexasOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbPrism->setNum((int)aMesh->NbPrisms()); - myMeshNbPrism1->setNum((int)aMesh->NbPrismsOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbPrism2->setNum((int)aMesh->NbPrismsOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbPyra->setNum((int)aMesh->NbPyramids()); - myMeshNbPyra1->setNum((int)aMesh->NbPyramidsOfOrder(SMESH::ORDER_LINEAR)); - myMeshNbPyra2->setNum((int)aMesh->NbPyramidsOfOrder(SMESH::ORDER_QUADRATIC)); - myMeshNbPolyhedrones->setNum( (int)aMesh->NbPolyhedrons() ); - return; - } - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow(anObject); - if (!aSubMesh->_is_nil()) { - myWGStack->setCurrentWidget(mySubMeshWidget); - setWindowTitle(tr("SMESH_MESHINFO_TITLE") + " [" + tr("SMESH_SUBMESH") + "]"); - mySubMeshName->setText(aSO->GetName().c_str()); - mySubMeshNbNodes->setNum((int)aSubMesh->GetNumberOfNodes(true)); - mySubMeshNbElements->setNum((int)aSubMesh->GetNumberOfElements()); - mySubMeshNbEdges->setNum((int)(aSubMesh->GetElementsByType(SMESH::EDGE)->length())); - mySubMeshNbFaces->setNum((int)(aSubMesh->GetElementsByType(SMESH::FACE)->length())); - mySubMeshNbVolumes->setNum((int)(aSubMesh->GetElementsByType(SMESH::VOLUME)->length())); - return; - } - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObject); - if (!aGroup->_is_nil()) { - myWGStack->setCurrentWidget(myGroupWidget); - setWindowTitle(tr("SMESH_MESHINFO_TITLE") + " [" + tr("SMESH_GROUP") + "]"); - myGroupName->setText(aSO->GetName().c_str()); - int aType = aGroup->GetType(); - QString strType; - switch (aType) { - case SMESH::NODE: - strType = "SMESH_MESHINFO_NODES"; break; - case SMESH::EDGE: - strType = "SMESH_MESHINFO_EDGES"; break; - case SMESH::FACE: - strType = "SMESH_MESHINFO_FACES"; break; - case SMESH::VOLUME: - strType = "SMESH_MESHINFO_VOLUMES"; break; - default: - strType = "SMESH_MESHINFO_ALL_TYPES"; break; - } - - myGroupType->setText(tr(strType.toLatin1().data())); - myGroupNb->setNum((int)aGroup->Size()); - return; - } - } - } - } - myWGStack->setCurrentIndex(0); - setWindowTitle(tr("SMESH_MESHINFO_TITLE")); -} - -//================================================================================= -// function : SelectionIntoArgument() -// purpose : Called when selection has changed -//================================================================================= -void SMESHGUI_MeshInfosDlg::onSelectionChanged() -{ - if (myStartSelection) - DumpMeshInfos(); -} - -//================================================================================= -// function : closeEvent() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::closeEvent(QCloseEvent* e) -{ - mySMESHGUI->ResetState(); - QDialog::closeEvent(e); -} - -//================================================================================= -// function : windowActivationChange() -// purpose : called when window is activated/deactivated -//================================================================================= -void SMESHGUI_MeshInfosDlg::windowActivationChange(bool oldActive) -{ - QDialog::windowActivationChange(oldActive); - if (isActiveWindow() && myIsActiveWindow != isActiveWindow()) - ActivateThisDialog(); - myIsActiveWindow = isActiveWindow(); -} - -//================================================================================= -// function : DeactivateActiveDialog() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::DeactivateActiveDialog() -{ - disconnect(mySelectionMgr, 0, this, 0); -} - -//================================================================================= -// function : ActivateThisDialog() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::ActivateThisDialog() -{ - /* Emit a signal to deactivate any active dialog */ - mySMESHGUI->EmitSignalDeactivateDialog(); - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onSelectionChanged())); -} - -//================================================================================= -// function : onStartSelection() -// purpose : starts selection -//================================================================================= -void SMESHGUI_MeshInfosDlg::onStartSelection() -{ - myStartSelection = true; - onSelectionChanged(); - myStartSelection = true; - mySelectLab->setText(tr("INF_SELECT_OBJECT")); -} - -//================================================================================= -// function : onHelp() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::onHelp() -{ - LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); - else { - QString platform; -#ifdef WIN32 - platform = "winapplication"; -#else - platform = "application"; -#endif - SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); - } -} - -//================================================================================= -// function : keyPressEvent() -// purpose : -//================================================================================= -void SMESHGUI_MeshInfosDlg::keyPressEvent( QKeyEvent* e ) -{ - QDialog::keyPressEvent( e ); - if ( e->isAccepted() ) - return; - - if ( e->key() == Qt::Key_F1 ) { - e->accept(); - onHelp(); - } -} diff --git a/src/SMESHGUI/SMESHGUI_MeshInfosDlg.h b/src/SMESHGUI/SMESHGUI_MeshInfosDlg.h deleted file mode 100644 index b2b84c9e6..000000000 --- a/src/SMESHGUI/SMESHGUI_MeshInfosDlg.h +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_MeshInfosDlg.h -// Author : Nicolas BARBEROU -// -#ifndef SMESHGUI_MESHINFOSDLG_H -#define SMESHGUI_MESHINFOSDLG_H - -// SMESH includes -#include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include - -class QGroupBox; -class QLabel; -class QPushButton; -class QStackedWidget; - -class LightApp_SelectionMgr; -class SMESHGUI; - -class SMESHGUI_EXPORT SMESHGUI_MeshInfosDlg : public QDialog -{ - Q_OBJECT - -public: - SMESHGUI_MeshInfosDlg( SMESHGUI* ); - ~SMESHGUI_MeshInfosDlg(); - -protected: - void closeEvent( QCloseEvent* ); - void keyPressEvent( QKeyEvent* ); - void windowActivationChange( bool ); - void DumpMeshInfos(); - -private slots: - void onSelectionChanged(); - void DeactivateActiveDialog(); - void ActivateThisDialog(); - void onStartSelection(); - void onHelp(); - -private: - SMESHGUI* mySMESHGUI; - LightApp_SelectionMgr* mySelectionMgr; - bool myStartSelection; - bool myIsActiveWindow; - - QPushButton* mySelectBtn; - QLabel* mySelectLab; - - QStackedWidget* myWGStack; - - QWidget* myMeshWidget; - QLabel* myMeshName; - QLabel* myMeshNbNodes; - QLabel* myMeshNbEdges; - QLabel* myMeshNbEdges1; - QLabel* myMeshNbEdges2; - QGroupBox* myMeshFacesGroup; - QLabel* myMeshNbFaces; - QLabel* myMeshNbFaces1; - QLabel* myMeshNbFaces2; - QLabel* myMeshNbTriangles; - QLabel* myMeshNbTriangles1; - QLabel* myMeshNbTriangles2; - QLabel* myMeshNbQuadrangles; - QLabel* myMeshNbQuadrangles1; - QLabel* myMeshNbQuadrangles2; - QLabel* myMeshNbPolygones; - QGroupBox* myMeshVolumesGroup; - QLabel* myMeshNbVolumes; - QLabel* myMeshNbVolumes1; - QLabel* myMeshNbVolumes2; - QLabel* myMeshNbTetra; - QLabel* myMeshNbTetra1; - QLabel* myMeshNbTetra2; - QLabel* myMeshNbHexa; - QLabel* myMeshNbHexa1; - QLabel* myMeshNbHexa2; - QLabel* myMeshNbPyra; - QLabel* myMeshNbPyra1; - QLabel* myMeshNbPyra2; - QLabel* myMeshNbPrism; - QLabel* myMeshNbPrism1; - QLabel* myMeshNbPrism2; - QLabel* myMeshNbPolyhedrones; - - QWidget* mySubMeshWidget; - QLabel* mySubMeshName; - QLabel* mySubMeshNbNodes; - QGroupBox* mySubMeshElementsGroup; - QLabel* mySubMeshNbElements; - QLabel* mySubMeshNbEdges; - QLabel* mySubMeshNbFaces; - QLabel* mySubMeshNbVolumes; - - QWidget* myGroupWidget; - QLabel* myGroupName; - QLabel* myGroupType; - QLabel* myGroupNb; - - QGroupBox* myButtonsGroup; - QPushButton* myOkBtn; - QPushButton* myHelpBtn; - - QString myHelpFileName; -}; - -#endif // SMESHGUI_MESHINFOSDLG_H diff --git a/src/SMESHGUI/SMESHGUI_MeshOp.cxx b/src/SMESHGUI/SMESHGUI_MeshOp.cxx index 351e6034e..9ad1a47fc 100644 --- a/src/SMESHGUI/SMESHGUI_MeshOp.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshOp.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_MeshOp.cxx -// Author : Sergey LITONIN, Open CASCADE S.A.S. +// File : SMESHGUI_MeshOp.cxx +// Author : Sergey LITONIN, Open CASCADE S.A.S. + // SMESH includes -// #include "SMESHGUI_MeshOp.h" #include "SMESHGUI.h" @@ -45,10 +44,12 @@ // SALOME GUI includes #include #include +#include #include #include #include #include +#include #include #include @@ -71,6 +72,10 @@ #include #include CORBA_CLIENT_HEADER(SMESH_Gen) +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //================================================================================ /*! * \brief Constructor @@ -85,7 +90,8 @@ SMESHGUI_MeshOp::SMESHGUI_MeshOp( const bool theToCreate, const bool theIsMesh ) myToCreate( theToCreate ), myIsMesh( theIsMesh ), myDlg( 0 ), - myShapeByMeshOp( 0 ) + myShapeByMeshOp( 0 ), + myHypoSet( 0 ) { if ( GeometryGUI::GetGeomGen()->_is_nil() )// check that GEOM_Gen exists GeometryGUI::InitGeomGen(); @@ -141,14 +147,21 @@ bool SMESHGUI_MeshOp::onApply() aMess = ""; try { + QStringList anEntryList; if ( myToCreate && myIsMesh ) - aResult = createMesh( aMess ); + aResult = createMesh( aMess, anEntryList ); if ( myToCreate && !myIsMesh ) - aResult = createSubMesh( aMess ); + aResult = createSubMesh( aMess, anEntryList ); else if ( !myToCreate ) aResult = editMeshOrSubMesh( aMess ); if ( aResult ) + { + SMESHGUI::Modified(); update( UF_ObjBrowser | UF_Model ); + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + myObjectToSelect = anApp->browseObjects( anEntryList, isApplyAndClose() ); + } } catch ( const SALOME::SALOME_Exception& S_ex ) { @@ -232,14 +245,36 @@ void SMESHGUI_MeshOp::startOperation() else myDlg->activateObject( SMESHGUI_MeshDlg::Obj ); - myDlg->setHypoSets( SMESH::GetHypothesesSets() ); - myDlg->setCurrentTab( SMESH::DIM_3D ); myDlg->show(); - + myDlg->setGeomPopupEnabled(false); selectionDone(); myIgnoreAlgoSelection = false; + + myObjectToSelect.clear(); +} + +//================================================================================= +/*! + * \brief Selects a recently created mesh or sub-mesh if necessary + * + * Virtual method redefined from base class called when operation is commited + * selects a recently created mesh or sub-mesh if necessary. Allows to perform + * selection when the custom selection filters are removed. + */ +//================================================================================= +void SMESHGUI_MeshOp::commitOperation() +{ + SMESHGUI_SelectionOp::commitOperation(); + + if ( !myObjectToSelect.isEmpty() ) { + if ( LightApp_SelectionMgr* aSelectionMgr = selectionMgr() ) { + SUIT_DataOwnerPtrList aList; + aList.append( new LightApp_DataOwner( myObjectToSelect ) ); + aSelectionMgr->setSelected( aList ); + } + } } //================================================================================ @@ -271,11 +306,10 @@ SUIT_SelectionFilter* SMESHGUI_MeshOp::createFilter( const int theId ) const //================================================================================ /*! - * \brief check if selected shape is a subshape of the shape to mesh + * \brief check if selected shape is a sub-shape of the shape to mesh * \retval bool - check result */ //================================================================================ - bool SMESHGUI_MeshOp::isSubshapeOk() const { if ( !myToCreate || myIsMesh ) // not submesh creation @@ -338,11 +372,10 @@ bool SMESHGUI_MeshOp::isSubshapeOk() const //================================================================================ /*! * \brief Return name of the algorithm that does not support submeshes and makes - * submesh creation useless + * submesh creation useless * \retval char* - string is to be deleted!!! */ //================================================================================ - char* SMESHGUI_MeshOp::isSubmeshIgnored() const { if ( myToCreate && !myIsMesh ) { @@ -388,7 +421,6 @@ char* SMESHGUI_MeshOp::isSubmeshIgnored() const * \retval _PTR(SObject) - the found submesh SObject */ //================================================================================ - _PTR(SObject) SMESHGUI_MeshOp::getSubmeshByGeom() const { QString aMeshEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Mesh ); @@ -499,48 +531,52 @@ void SMESHGUI_MeshOp::selectionDone() } if (aSeq->length() > 0) { - shapeDim = 0; + shapeDim = -1; for (int iss = 0; iss < aSeq->length() && shapeDim < 3; iss++) { GEOM::GEOM_Object_var aGeomVar = aSeq[iss]; switch ( aGeomVar->GetShapeType() ) { case GEOM::SOLID: shapeDim = 3; break; case GEOM::SHELL: - { - //shapeDim = 3; // Bug 0016155: EDF PAL 447: If the shape is a Shell, disable 3D tab - shapeDim = (shapeDim < 2) ? 2 : shapeDim; - TopoDS_Shape aShape; - if (GEOMBase::GetShape(aGeomVar, aShape)) { - if (/*aShape.Closed()*/BRep_Tool::IsClosed(aShape)) - shapeDim = 3; - } - } - break; - case GEOM::FACE: shapeDim = (shapeDim < 2) ? 2 : shapeDim; break; + // Bug 0016155: EDF PAL 447: If the shape is a Shell, disable 3D tab + // { + // TopoDS_Shape aShape; + // bool isClosed = GEOMBase::GetShape(aGeomVar, aShape) && /*aShape.Closed()*/BRep_Tool::IsClosed(aShape); + // shapeDim = qMax(isClosed ? 3 : 2, shapeDim); + // } + // break; + case GEOM::FACE: shapeDim = qMax(2, shapeDim); break; case GEOM::WIRE: - case GEOM::EDGE: shapeDim = (shapeDim < 1) ? 1 : shapeDim; break; - case GEOM::VERTEX: break; + case GEOM::EDGE: shapeDim = qMax(1, shapeDim); break; + case GEOM::VERTEX: shapeDim = qMax(0, shapeDim); break; default: { TopoDS_Shape aShape; - if (GEOMBase::GetShape(aGeomVar, aShape)) { - TopExp_Explorer exp (aShape, TopAbs_SHELL); + if (GEOMBase::GetShape(aGeomVar, aShape)) + { + TopExp_Explorer exp (aShape, TopAbs_SOLID); if (exp.More()) { - //shapeDim = 3; // Bug 0016155: EDF PAL 447: If the shape is a Shell, disable 3D tab - shapeDim = (shapeDim < 2) ? 2 : shapeDim; - for (; exp.More() && shapeDim == 2; exp.Next()) { - if (/*exp.Current().Closed()*/BRep_Tool::IsClosed(exp.Current())) - shapeDim = 3; - } + shapeDim = 3; } + // Bug 0016155: EDF PAL 447: If the shape is a Shell, disable 3D tab + // else if ( exp.Init( aShape, TopAbs_SHELL ), exp.More() ) + // { + // shapeDim = 2; + // for (; exp.More() && shapeDim == 2; exp.Next()) { + // if (/*exp.Current().Closed()*/BRep_Tool::IsClosed(exp.Current())) + // shapeDim = 3; + // } + // } else if ( exp.Init( aShape, TopAbs_FACE ), exp.More() ) - shapeDim = (shapeDim < 2) ? 2 : shapeDim; + shapeDim = qMax(2, shapeDim); else if ( exp.Init( aShape, TopAbs_EDGE ), exp.More() ) - shapeDim = (shapeDim < 1) ? 1 : shapeDim; - else - ;//shapeDim = 0; + shapeDim = qMax(1, shapeDim); + else if ( exp.Init( aShape, TopAbs_VERTEX ), exp.More() ) + shapeDim = qMax(0, shapeDim); } } } + if ( shapeDim == 3 ) + break; } } for (int i = SMESH::DIM_3D; i > shapeDim; i--) { @@ -548,7 +584,7 @@ void SMESHGUI_MeshOp::selectionDone() onAlgoSelected(-1, i); } myDlg->setMaxHypoDim( shapeDim ); - + myDlg->setHypoSets( SMESH::GetHypothesesSets( shapeDim )); if (!myToCreate) // edition: read hypotheses { @@ -575,11 +611,11 @@ void SMESHGUI_MeshOp::selectionDone() SMESH::SObjectToInterface( pSubmesh ); bool editSubmesh = ( !sm->_is_nil() && SUIT_MessageBox::question( myDlg, tr( "SMESH_WARNING" ), - tr( "EDIT_SUBMESH_QUESTION"), - SUIT_MessageBox::Yes | - SUIT_MessageBox::No, - SUIT_MessageBox::No ) - == SUIT_MessageBox::Yes ); + tr( "EDIT_SUBMESH_QUESTION"), + SUIT_MessageBox::Yes | + SUIT_MessageBox::No, + SUIT_MessageBox::No ) + == SUIT_MessageBox::Yes ); if ( editSubmesh ) { selectionMgr()->clearFilters(); @@ -612,13 +648,19 @@ void SMESHGUI_MeshOp::selectionDone() QString aMeshEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Mesh ); if ( _PTR(SObject) pMesh = studyDS()->FindObjectID( aMeshEntry.toLatin1().data() )) { SMESH::SMESH_Mesh_var mesh = SMESH::SObjectToInterface( pMesh ); - if ( !mesh->_is_nil() ) + if ( !mesh->_is_nil() ) { + //rnv: issue 21056: EDF 1608 SMESH: Dialog Box "Create Sub Mesh": focus should automatically switch to geometry + QString aGeomEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Geom ); + _PTR(SObject) pGeom = studyDS()->FindObjectID( aGeomEntry.toLatin1().data() ); + if ( !pGeom || GEOM::GEOM_Object::_narrow( _CAST( SObject,pGeom )->GetObject() )->_is_nil() ) + myDlg->activateObject(SMESHGUI_MeshDlg::Geom); enable = ( shapeDim > 1 ) && ( mesh->NbEdges() > 0 ); + } } myDlg->setGeomPopupEnabled( enable ); } } - else { + else { // no geometry defined myDlg->enableTab( SMESH::DIM_3D ); QStringList hypList; availableHyps( SMESH::DIM_3D, Algo, hypList, @@ -738,7 +780,6 @@ bool SMESHGUI_MeshOp::isValid( QString& theMess ) const * \retval bool - check result */ //================================================================================ - static bool isCompatible(const HypothesisData* theAlgoData, const HypothesisData* theHypData, const int theHypType) @@ -856,6 +897,7 @@ void SMESHGUI_MeshOp::existingHyps( const int theDim, if ( !aHypVar->_is_nil() ) { HypothesisData* aData = SMESH::GetHypothesisData( aHypVar->GetName() ); + if ( !aData) continue; if ( ( theDim == -1 || aData->Dim.contains( theDim ) ) && ( isCompatible ( theAlgoData, aData, theHypType )) && ( isAux == aData->IsAux )) @@ -874,14 +916,13 @@ void SMESHGUI_MeshOp::existingHyps( const int theDim, //================================================================================ /*! * \brief If create or edit a submesh, return a hypothesis holding parameters used - * to mesh a subshape + * to mesh a sub-shape * \param aHypType - The hypothesis type name * \param aServerLib - Server library name * \param hypData - The structure holding the hypothesis type etc. * \retval SMESH::SMESH_Hypothesis_var - the hypothesis holding parameter values */ //================================================================================ - SMESH::SMESH_Hypothesis_var SMESHGUI_MeshOp::getInitParamsHypothesis( const QString& aHypType, const QString& aServerLib ) const @@ -944,12 +985,19 @@ SMESHGUI_MeshOp::getInitParamsHypothesis( const QString& aHypType, } } - return SMESHGUI::GetSMESHGen()->GetHypothesisParameterValues( aHypType.toLatin1().data(), - aServerLib.toLatin1().data(), - aMeshVar, - aGeomVar, - /*byMesh = */isSubMesh); - + SMESH::SMESH_Hypothesis_var hyp = + SMESHGUI::GetSMESHGen()->GetHypothesisParameterValues( aHypType.toLatin1().data(), + aServerLib.toLatin1().data(), + aMeshVar, + aGeomVar, + /*byMesh = */isSubMesh); + if ( hyp->_is_nil() && isSubMesh ) + hyp = SMESHGUI::GetSMESHGen()->GetHypothesisParameterValues( aHypType.toLatin1().data(), + aServerLib.toLatin1().data(), + aMeshVar, + aGeomVar, + /*byMesh = */false); + return hyp; } //================================================================================ @@ -960,7 +1008,6 @@ SMESHGUI_MeshOp::getInitParamsHypothesis( const QString& aHypType, * \retval int - dimention */ //================================================================================ - static int getTabDim (const QObject* tab, SMESHGUI_MeshDlg* dlg ) { int aDim = -1; @@ -997,14 +1044,6 @@ void SMESHGUI_MeshOp::onCreateHyp( const int theHypType, const int theIndex ) createHypothesis(aDim, theHypType, aHypTypeName); } -//================================================================================ -/*! - * Create hypothesis and update dialog. - * \param theDim - dimension of hypothesis to be created - * \param theType - hypothesis category (algorithm, hypothesis, additional hypothesis) - * \param theTypeName - specifies hypothesis to be created - */ -//================================================================================ namespace { QString GetUniqueName (const QStringList& theHypNames, @@ -1018,18 +1057,24 @@ namespace } } -void SMESHGUI_MeshOp::createHypothesis (const int theDim, - const int theType, - const QString& theTypeName) +//================================================================================ +/*! + * Create hypothesis and update dialog. + * \param theDim - dimension of hypothesis to be created + * \param theType - hypothesis category (algorithm, hypothesis, additional hypothesis) + * \param theTypeName - specifies hypothesis to be created + */ +//================================================================================ +void SMESHGUI_MeshOp::createHypothesis(const int theDim, + const int theType, + const QString& theTypeName) { - // During a hypothesis creation we might need to select some objects. - // Main dialog must not update it's own selected objects in this case. - dlg()->deactivateAll(); - HypothesisData* aData = SMESH::GetHypothesisData(theTypeName); if (!aData) return; + myDim = theDim; + myType = theType; QStringList aHypNames; TDim2Type2HypList::const_iterator aDimIter = myExistingHyps.begin(); for ( ; aDimIter != myExistingHyps.end(); aDimIter++) { @@ -1048,14 +1093,21 @@ void SMESHGUI_MeshOp::createHypothesis (const int theDim, QString aHypName = GetUniqueName( aHypNames, aData->Label); // existing hypos - int nbHyp = myExistingHyps[theDim][theType].count(); + bool dialog = false; QString aClientLibName = aData->ClientLibName; if (aClientLibName == "") { // Call hypothesis creation server method (without GUI) - SMESH::CreateHypothesis(theTypeName, aHypName, false); + SMESH::SMESH_Hypothesis_var aHyp = + SMESH::CreateHypothesis(theTypeName, aHypName, false); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(aHyp)) + aHyp->UnRegister(); +#endif } else { // Get hypotheses creator client (GUI) + // BUG 0020378 + //SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(theTypeName); SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(theTypeName); // Create hypothesis @@ -1063,23 +1115,110 @@ void SMESHGUI_MeshOp::createHypothesis (const int theDim, // Get parameters appropriate to initialize a new hypothesis SMESH::SMESH_Hypothesis_var initParamHyp = getInitParamsHypothesis(theTypeName, aData->ServerLibName); + + removeCustomFilters(); // Issue 0020170 + + // Get Entry of the Geom object + QString aGeomEntry = ""; + QString aMeshEntry = ""; + QString anObjEntry = ""; + aGeomEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Geom ); + aMeshEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Mesh ); + anObjEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Obj ); + + if ( myToCreate && myIsMesh ) + aMeshEntry = aGeomEntry; + + if ( aMeshEntry != aGeomEntry ) { // Get Geom object from Mesh of a sub-mesh being edited + _PTR(SObject) pObj = studyDS()->FindObjectID( aMeshEntry.toLatin1().data() ); + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( pObj ); + aMeshEntry = ( aGeomVar->_is_nil() ) ? "" : aMeshEntry = aGeomVar->GetStudyEntry(); + } + + if ( aMeshEntry == "" && aGeomEntry == "" ) { // get geom of an object being edited + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + bool isMesh; + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( pObj, &isMesh ); + if ( !aGeomVar->_is_nil() ) + { + aGeomEntry = aGeomVar->GetStudyEntry(); + if ( isMesh ) + aMeshEntry = aGeomEntry; + } + } + + if ( anObjEntry != "" && aGeomEntry != "" && aMeshEntry == "" ) { + // take geometry from submesh being created + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + if ( pObj ) { + // if current object is sub-mesh + SMESH::SMESH_subMesh_var aSubMeshVar = + SMESH::SMESH_subMesh::_narrow( _CAST( SObject,pObj )->GetObject() ); + if ( !aSubMeshVar->_is_nil() ) { + SMESH::SMESH_Mesh_var aMeshVar = aSubMeshVar->GetFather(); + if ( !aMeshVar->_is_nil() ) { + _PTR(SObject) aMeshSO = SMESH::FindSObject( aMeshVar ); + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( aMeshSO ); + if ( !aGeomVar->_is_nil() ) + aMeshEntry = aGeomVar->GetStudyEntry(); + } + } + } + } + + aCreator->setShapeEntry( aGeomEntry ); + if ( aMeshEntry != "" ) + aCreator->setMainShapeEntry( aMeshEntry ); myDlg->setEnabled( false ); - aCreator->create(initParamHyp, aHypName, myDlg); - myDlg->setEnabled( true ); - } else { - SMESH::CreateHypothesis(theTypeName, aHypName, false); + aCreator->create(initParamHyp, aHypName, myDlg, this, SLOT( onHypoCreated( int ) ) ); + dialog = true; + } + else { + SMESH::SMESH_Hypothesis_var aHyp = + SMESH::CreateHypothesis(theTypeName, aHypName, false); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(aHyp)) + aHyp->UnRegister(); +#endif } } + if( !dialog ) + onHypoCreated(2); +} + +//================================================================================ +/*! + * Necessary steps after hypothesis creation + * \param result - creation result: + * 0 = rejected + * 1 = accepted + * 2 = additional value meaning that slot is called not from dialog box + */ +//================================================================================ +void SMESHGUI_MeshOp::onHypoCreated( int result ) +{ + if( result != 2 ) + { + int obj = myDlg->getActiveObject(); + onActivateObject( obj ); // Issue 0020170. Restore filters + myDlg->setEnabled( true ); + } + _PTR(SComponent) aFather = SMESH::GetActiveStudyDocument()->FindComponent("SMESH"); - HypothesisData* algoData = hypData( theDim, Algo, currentHyp( theDim, Algo )); + int nbHyp = myExistingHyps[myDim][myType].count(); + HypothesisData* algoData = hypData( myDim, Algo, currentHyp( myDim, Algo )); QStringList aNewHyps; - existingHyps(theDim, theType, aFather, aNewHyps, myExistingHyps[theDim][theType], algoData); - if (aNewHyps.count() > nbHyp) { + existingHyps(myDim, myType, aFather, aNewHyps, myExistingHyps[myDim][myType], algoData); + if (aNewHyps.count() > nbHyp) + { for (int i = nbHyp; i < aNewHyps.count(); i++) - myDlg->tab(theDim)->addHyp(theType, aNewHyps[i]); + myDlg->tab(myDim)->addHyp(myType, aNewHyps[i]); } + + if( result!=2 && myHypoSet ) + processSet(); } //================================================================================ @@ -1107,18 +1246,86 @@ void SMESHGUI_MeshOp::onEditHyp( const int theHypType, const int theIndex ) if ( aHyp->_is_nil() ) return; - SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator( aHyp->GetName() ); - if ( aCreator ) { + SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(aHyp->GetName()); + if ( aCreator ) + { // Get initial parameters SMESH::SMESH_Hypothesis_var initParamHyp = getInitParamsHypothesis( aHyp->GetName(), aHyp->GetLibName()); - myDlg->setEnabled( false ); aCreator->setInitParamsHypothesis( initParamHyp ); - aCreator->edit( aHyp.in(), aHypItem.second, dlg() ); - myDlg->setEnabled( true ); + + // Get Entry of the Geom object + QString aGeomEntry = ""; + QString aMeshEntry = ""; + QString anObjEntry = ""; + aGeomEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Geom ); + aMeshEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Mesh ); + anObjEntry = myDlg->selectedObject( SMESHGUI_MeshDlg::Obj ); + + if ( myToCreate && myIsMesh ) + aMeshEntry = aGeomEntry; + + if ( aMeshEntry != aGeomEntry ) { // Get Geom object from Mesh of a sub-mesh being edited + _PTR(SObject) pObj = studyDS()->FindObjectID( aMeshEntry.toLatin1().data() ); + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( pObj ); + aMeshEntry = ( aGeomVar->_is_nil() ) ? "" : aMeshEntry = aGeomVar->GetStudyEntry(); + } + + if ( aMeshEntry == "" && aGeomEntry == "" ) { // get geom of an object being edited + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + bool isMesh; + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( pObj, &isMesh ); + if ( !aGeomVar->_is_nil() ) + { + aGeomEntry = aGeomVar->GetStudyEntry(); + if ( isMesh ) + aMeshEntry = aGeomEntry; + } + } + + if ( anObjEntry != "" && aGeomEntry != "" && aMeshEntry == "" ) { + // take geometry from submesh being created + _PTR(SObject) pObj = studyDS()->FindObjectID( anObjEntry.toLatin1().data() ); + if ( pObj ) { + // if current object is sub-mesh + SMESH::SMESH_subMesh_var aSubMeshVar = + SMESH::SMESH_subMesh::_narrow( _CAST( SObject,pObj )->GetObject() ); + if ( !aSubMeshVar->_is_nil() ) { + SMESH::SMESH_Mesh_var aMeshVar = aSubMeshVar->GetFather(); + if ( !aMeshVar->_is_nil() ) { + _PTR(SObject) aMeshSO = SMESH::FindSObject( aMeshVar ); + GEOM::GEOM_Object_var aGeomVar = SMESH::GetShapeOnMeshOrSubMesh( aMeshSO ); + if ( !aGeomVar->_is_nil() ) + aMeshEntry = aGeomVar->GetStudyEntry(); + } + } + } + } + + aCreator->setShapeEntry( aGeomEntry ); + if ( aMeshEntry != "" ) + aCreator->setMainShapeEntry( aMeshEntry ); + removeCustomFilters(); // Issue 0020170 + myDlg->setEnabled( false ); + aCreator->edit( aHyp.in(), aHypItem.second, dlg(), this, SLOT( onHypoEdited( int ) ) ); } } +//================================================================================ +/*! + * Necessary steps after hypothesis edition + * \param result - creation result: + * 0 = rejected + * 1 = accepted + */ +//================================================================================ +void SMESHGUI_MeshOp::onHypoEdited( int result ) +{ + int obj = myDlg->getActiveObject(); + onActivateObject( obj ); // Issue 0020170. Restore filters + myDlg->setEnabled( true ); +} + //================================================================================ /*! * \brief access to hypothesis data @@ -1128,7 +1335,6 @@ void SMESHGUI_MeshOp::onEditHyp( const int theHypType, const int theIndex ) * \retval HypothesisData* - result data, may be 0 */ //================================================================================ - HypothesisData* SMESHGUI_MeshOp::hypData( const int theDim, const int theHypType, const int theIndex) @@ -1146,7 +1352,6 @@ HypothesisData* SMESHGUI_MeshOp::hypData( const int theDim, * \param theIndex - algorithm index */ //================================================================================ - void SMESHGUI_MeshOp::onAlgoSelected( const int theIndex, const int theDim ) { @@ -1255,7 +1460,7 @@ void SMESHGUI_MeshOp::onAlgoSelected( const int theIndex, CORBA::String_var curHypType = curHyp->GetName(); if ( !algoDeselectedByUser && myObjHyps[ dim ][ type ].count() > 0 && - curHypType == myObjHyps[ dim ][ type ].first().first->GetName()) + !strcmp( curHypType, myObjHyps[ dim ][ type ].first().first->GetName()) ) { HypothesisData* hypData = SMESH::GetHypothesisData( curHyp->GetName() ); for (int i = 0; i < myAvailableHypData[ dim ][ Algo ].count(); ++i) { @@ -1310,8 +1515,9 @@ void SMESHGUI_MeshOp::onAlgoSelected( const int theIndex, //================================================================================ void SMESHGUI_MeshOp::onHypoSet( const QString& theSetName ) { - HypothesesSet* aHypoSet = SMESH::GetHypothesesSet(theSetName); - if (!aHypoSet) return; + myHypoSet = SMESH::GetHypothesesSet(theSetName); + if (!myHypoSet) + return; // clear all hyps for (int dim = SMESH::DIM_0D; dim <= SMESH::DIM_3D; dim++) { @@ -1320,57 +1526,81 @@ void SMESHGUI_MeshOp::onHypoSet( const QString& theSetName ) setCurrentHyp(dim, MainHyp, -1); } - for (int aHypType = Algo; aHypType < AddHyp; aHypType++) { - bool isAlgo = (aHypType == Algo); + myHypoSet->init(true); //algorithms + processSet(); + myHypoSet->init(false); //hypotheses + processSet(); + myHypoSet = 0; +} - // set hyps from the set - QStringList* aHypoList = isAlgo ? &aHypoSet->AlgoList : &aHypoSet->HypoList; - for (int i = 0, n = aHypoList->count(); i < n; i++) { - const QString& aHypoTypeName = (*aHypoList)[ i ]; - HypothesisData* aHypData = SMESH::GetHypothesisData(aHypoTypeName); - if (!aHypData) - continue; +//================================================================================ +/*! + * \brief One step of hypothesis/algorithm list creation + * + * Creates a hypothesis or an algorithm for current item of internal list of names myHypoSet + */ +//================================================================================ +void SMESHGUI_MeshOp::processSet() +{ + myHypoSet->next(); + if( !myHypoSet->more() ) + return; - int aDim = aHypData->Dim[0]; - // create or/and set - if (isAlgo) { - int index = myAvailableHypData[aDim][Algo].indexOf( aHypData ); - if ( index < 0 ) { - QStringList anAvailable; - availableHyps( aDim, Algo, anAvailable, myAvailableHypData[aDim][Algo] ); - myDlg->tab( aDim )->setAvailableHyps( Algo, anAvailable ); - index = myAvailableHypData[aDim][Algo].indexOf( aHypData ); - } - setCurrentHyp( aDim, Algo, index ); - onAlgoSelected( index, aDim ); - } - else { - bool mainHyp = true; - QStringList anAvailable; - availableHyps( aDim, MainHyp, anAvailable, myAvailableHypData[aDim][MainHyp] ); - myDlg->tab( aDim )->setAvailableHyps( MainHyp, anAvailable ); - int index = myAvailableHypData[aDim][MainHyp].indexOf( aHypData ); - if ( index < 0 ) { - mainHyp = false; - index = myAvailableHypData[aDim][AddHyp].indexOf( aHypData ); - } - if (index >= 0) - createHypothesis(aDim, mainHyp ? MainHyp : AddHyp, aHypoTypeName); - } - } // loop on hypos in the set - } // loop on algo/hypo + bool isAlgo = myHypoSet->isAlgo(); + QString aHypoTypeName = myHypoSet->current(); + HypothesisData* aHypData = SMESH::GetHypothesisData(aHypoTypeName); + if (!aHypData) + { + processSet(); + return; + } + + int aDim = aHypData->Dim[0]; + // create or/and set + if (isAlgo) + { + int index = myAvailableHypData[aDim][Algo].indexOf( aHypData ); + if ( index < 0 ) + { + QStringList anAvailable; + availableHyps( aDim, Algo, anAvailable, myAvailableHypData[aDim][Algo] ); + myDlg->tab( aDim )->setAvailableHyps( Algo, anAvailable ); + index = myAvailableHypData[aDim][Algo].indexOf( aHypData ); + } + setCurrentHyp( aDim, Algo, index ); + onAlgoSelected( index, aDim ); + processSet(); + } + else + { + bool mainHyp = true; + QStringList anAvailable; + availableHyps( aDim, MainHyp, anAvailable, myAvailableHypData[aDim][MainHyp] ); + myDlg->tab( aDim )->setAvailableHyps( MainHyp, anAvailable ); + int index = myAvailableHypData[aDim][MainHyp].indexOf( aHypData ); + if ( index < 0 ) + { + mainHyp = false; + index = myAvailableHypData[aDim][AddHyp].indexOf( aHypData ); + } + if (index >= 0) + createHypothesis(aDim, mainHyp ? MainHyp : AddHyp, aHypoTypeName); + else + processSet(); + } } //================================================================================ /*! * \brief Creates mesh * \param theMess - Output parameter intended for returning error message + * \param theEntryList - List of entries of published objects * \retval bool - TRUE if mesh is created, FALSE otherwise * * Creates mesh */ //================================================================================ -bool SMESHGUI_MeshOp::createMesh( QString& theMess ) +bool SMESHGUI_MeshOp::createMesh( QString& theMess, QStringList& theEntryList ) { theMess = ""; @@ -1398,8 +1628,10 @@ bool SMESHGUI_MeshOp::createMesh( QString& theMess ) if ( aMeshVar->_is_nil() ) return false; _PTR(SObject) aMeshSO = SMESH::FindSObject( aMeshVar.in() ); - if ( aMeshSO ) + if ( aMeshSO ) { SMESH::SetName( aMeshSO, myDlg->objectText( SMESHGUI_MeshDlg::Obj ) ); + theEntryList.append( aMeshSO->GetID().c_str() ); + } for ( int aDim = SMESH::DIM_0D; aDim <= SMESH::DIM_3D; aDim++ ) { if ( !isAccessibleDim( aDim )) continue; @@ -1418,7 +1650,13 @@ bool SMESHGUI_MeshOp::createMesh( QString& theMess ) if ( !anAlgoVar->_is_nil() ) SMESH::AddHypothesisOnMesh( aMeshVar, anAlgoVar ); } - +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + if (aMeshSO) + aMeshVar->UnRegister(); +#endif } return true; } @@ -1427,12 +1665,13 @@ bool SMESHGUI_MeshOp::createMesh( QString& theMess ) /*! * \brief Creates sub-mesh * \param theMess - Output parameter intended for returning error message + * \param theEntryList - List of entries of published objects * \retval bool - TRUE if sub-mesh is created, FALSE otherwise * * Creates sub-mesh */ //================================================================================ -bool SMESHGUI_MeshOp::createSubMesh( QString& theMess ) +bool SMESHGUI_MeshOp::createSubMesh( QString& theMess, QStringList& theEntryList ) { theMess = ""; @@ -1506,8 +1745,8 @@ bool SMESHGUI_MeshOp::createSubMesh( QString& theMess ) QString aNewGeomGroupName ("Auto_group_for_"); aNewGeomGroupName += aName; SALOMEDS::SObject_var aNewGroupSO = - geomGen->AddInStudy(aSMESHGen->GetCurrentStudy(), aGeomVar, - aNewGeomGroupName.toLatin1().data(), mainGeom); + geomGen->AddInStudy(aSMESHGen->GetCurrentStudy(), aGeomVar, + aNewGeomGroupName.toLatin1().data(), mainGeom); } } } @@ -1522,8 +1761,10 @@ bool SMESHGUI_MeshOp::createSubMesh( QString& theMess ) // create sub-mesh SMESH::SMESH_subMesh_var aSubMeshVar = aMeshVar->GetSubMesh( aGeomVar, aName.toLatin1().data() ); _PTR(SObject) aSubMeshSO = SMESH::FindSObject( aSubMeshVar.in() ); - if ( aSubMeshSO ) + if ( aSubMeshSO ) { SMESH::SetName( aSubMeshSO, aName.toLatin1().data() ); + theEntryList.append( aSubMeshSO->GetID().c_str() ); + } for ( int aDim = SMESH::DIM_0D; aDim <= SMESH::DIM_3D; aDim++ ) { @@ -1547,7 +1788,7 @@ bool SMESHGUI_MeshOp::createSubMesh( QString& theMess ) } } - // deselect geometry: next submesh should be created on other subshape + // deselect geometry: next submesh should be created on other sub-shape myDlg->clearSelection( SMESHGUI_MeshDlg::Geom ); selectObject( _PTR(SObject)() ); selectionDone(); @@ -1579,7 +1820,7 @@ int SMESHGUI_MeshOp::currentHyp( const int theDim, const int theHypType ) const //================================================================================ bool SMESHGUI_MeshOp::isAccessibleDim( const int theDim ) const { - return myDlg->tab( theDim )->isEnabled(); + return myDlg->isTabEnabled( theDim ); } //================================================================================ @@ -1653,36 +1894,53 @@ SMESH::SMESH_Hypothesis_var SMESHGUI_MeshOp::getAlgo( const int theDim ) QStringList tmp; existingHyps( theDim, Algo, pObj, tmp, myExistingHyps[ theDim ][ Algo ]); - // look for anexisting algo of such a type + // look for an existing algo of such a type THypList& aHypVarList = myExistingHyps[ theDim ][ Algo ]; THypList::iterator anIter = aHypVarList.begin(); for ( ; anIter != aHypVarList.end(); anIter++) { SMESH::SMESH_Hypothesis_var aHypVar = (*anIter).first; CORBA::String_var aName = aHypVar->GetName(); - if ( !aHypVar->_is_nil() && aHypName == aName ) + if ( !aHypVar->_is_nil() && !strcmp(aHypName.toLatin1().data(), aName) ) { anAlgoVar = aHypVar; break; } } - if (anAlgoVar->_is_nil()) { + if (anAlgoVar->_is_nil()) + { HypothesisData* aHypData = SMESH::GetHypothesisData( aHypName ); - if (aHypData) { + if (aHypData) + { QString aClientLibName = aHypData->ClientLibName; - if (aClientLibName == "") { + if (aClientLibName == "") + { // Call hypothesis creation server method (without GUI) - SMESH::CreateHypothesis(aHypName, aHypData->Label, true); - } else { + SMESH::SMESH_Hypothesis_var aHyp = + SMESH::CreateHypothesis(aHypName, aHypName, true); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(aHyp)) + aHyp->UnRegister(); +#endif + } + else + { // Get hypotheses creator client (GUI) + // BUG 0020378 SMESHGUI_GenericHypothesisCreator* aCreator = SMESH::GetHypothesisCreator(aHypName); // Create algorithm if (aCreator) - aCreator->create(true, aHypName, myDlg); - else - SMESH::CreateHypothesis(aHypName, aHypData->Label, true); + aCreator->create(true, aHypName, myDlg, 0, QString::null ); + else { + SMESH::SMESH_Hypothesis_var aHyp = + SMESH::CreateHypothesis(aHypName, aHypName, true); +#ifdef WITHGENERICOBJ + if (!CORBA::is_nil(aHyp)) + aHyp->UnRegister(); +#endif + } } QStringList tmpList; _PTR(SComponent) aFather = SMESH::GetActiveStudyDocument()->FindComponent( "SMESH" ); @@ -1694,7 +1952,7 @@ SMESH::SMESH_Hypothesis_var SMESHGUI_MeshOp::getAlgo( const int theDim ) { SMESH::SMESH_Hypothesis_var aHypVar = (*anIter).first; CORBA::String_var aName = aHypVar->GetName(); - if ( !aHypVar->_is_nil() && aHypName == aName ) + if ( !aHypVar->_is_nil() && !strcmp(aHypName.toLatin1().data(), aName) ) { anAlgoVar = aHypVar; break; @@ -1983,14 +2241,14 @@ bool SMESHGUI_MeshOp::editMeshOrSubMesh( QString& theMess ) //================================================================================ /*! * \brief Verifies whether given operator is valid for this one - * \param theOtherOp - other operation - * \return Returns TRUE if the given operator is valid for this one, FALSE otherwise -* -* method redefined from base class verifies whether given operator is valid for -* this one (i.e. can be started "above" this operator). In current implementation method -* retuns false if theOtherOp operation is not intended for deleting objects or mesh -* elements. -*/ + * \param theOtherOp - other operation + * \return Returns TRUE if the given operator is valid for this one, FALSE otherwise + * + * method redefined from base class verifies whether given operator is valid for + * this one (i.e. can be started "above" this operator). In current implementation method + * retuns false if theOtherOp operation is not intended for deleting objects or mesh + * elements. + */ //================================================================================ bool SMESHGUI_MeshOp::isValid( SUIT_Operation* theOp ) const { @@ -2000,10 +2258,9 @@ bool SMESHGUI_MeshOp::isValid( SUIT_Operation* theOp ) const //================================================================================ /*! * \brief SLOT. Is called when the user selects a way of geometry selection - * \param theByMesh - true if the user wants to find geometry by mesh element + * \param theByMesh - true if the user wants to find geometry by mesh element */ //================================================================================ - void SMESHGUI_MeshOp::onGeomSelectionByMesh( bool theByMesh ) { if ( theByMesh ) { @@ -2035,7 +2292,6 @@ void SMESHGUI_MeshOp::onGeomSelectionByMesh( bool theByMesh ) * \brief SLOT. Is called when Ok is pressed in SMESHGUI_ShapeByMeshDlg */ //================================================================================ - void SMESHGUI_MeshOp::onPublishShapeByMeshDlg(SUIT_Operation* op) { if ( myShapeByMeshOp == op ) { @@ -2059,7 +2315,6 @@ void SMESHGUI_MeshOp::onPublishShapeByMeshDlg(SUIT_Operation* op) * \brief SLOT. Is called when Close is pressed in SMESHGUI_ShapeByMeshDlg */ //================================================================================ - void SMESHGUI_MeshOp::onCloseShapeByMeshDlg(SUIT_Operation* op) { if ( myShapeByMeshOp == op && myDlg ) { @@ -2070,10 +2325,9 @@ void SMESHGUI_MeshOp::onCloseShapeByMeshDlg(SUIT_Operation* op) //================================================================================ /*! * \brief Selects a SObject - * \param theSObj - the SObject to select + * \param theSObj - the SObject to select */ //================================================================================ - void SMESHGUI_MeshOp::selectObject( _PTR(SObject) theSObj ) const { if ( LightApp_SelectionMgr* sm = selectionMgr() ) { diff --git a/src/SMESHGUI/SMESHGUI_MeshOp.h b/src/SMESHGUI/SMESHGUI_MeshOp.h index 1046b699c..c7b11d437 100644 --- a/src/SMESHGUI/SMESHGUI_MeshOp.h +++ b/src/SMESHGUI/SMESHGUI_MeshOp.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshOp.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -31,6 +32,7 @@ #include "SMESHGUI_SelectionOp.h" +class HypothesesSet; class SMESHGUI_MeshDlg; class SMESHGUI_ShapeByMeshOp; class HypothesisData; @@ -64,6 +66,7 @@ public: protected: virtual void startOperation(); + virtual void commitOperation(); virtual void selectionDone(); virtual SUIT_SelectionFilter* createFilter( const int ) const; virtual bool isValid( SUIT_Operation* ) const; @@ -77,6 +80,9 @@ protected slots: void onPublishShapeByMeshDlg( SUIT_Operation* ); void onCloseShapeByMeshDlg( SUIT_Operation* ); void onAlgoSelected( const int, const int = -1 ); + void processSet(); + void onHypoCreated( int ); + void onHypoEdited( int ); private: typedef QList THypDataList; // typedef: list of hypothesis data @@ -98,10 +104,10 @@ private: const int ); // access to myAvailableHypData void createHypothesis( const int, const int, - const QString& ); + const QString& ); - bool createMesh( QString& ); - bool createSubMesh( QString& ); + bool createMesh( QString&, QStringList& ); + bool createSubMesh( QString&, QStringList& ); bool editMeshOrSubMesh( QString& ); int currentHyp( const int, const int ) const; @@ -135,6 +141,10 @@ private: THypDataList myAvailableHypData[4][NbHypTypes]; bool myIgnoreAlgoSelection; + HypothesesSet* myHypoSet; + int myDim, myType; + + QString myObjectToSelect; }; #endif // SMESHGUI_MESHOP_H diff --git a/src/SMESHGUI/SMESHGUI_MeshOrderDlg.cxx b/src/SMESHGUI/SMESHGUI_MeshOrderDlg.cxx new file mode 100644 index 000000000..7b55ee575 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshOrderDlg.cxx @@ -0,0 +1,330 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshOrderDlg.cxx +// Author : Pavel TELKOV, Open CASCADE S.A.S. +// SMESH includes +// +#include "SMESHGUI_MeshOrderDlg.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPACING 6 +#define MARGIN 11 + +/*! + * Enumeartion of list widget item types (mesh name or separator) + */ +enum MeshOrderItemType { MeshItem = QListWidgetItem::UserType, SeparatorItem }; + +// ========================================================================================= +/*! + * \brief Constructor + */ +// ========================================================================================= + +SMESHGUI_MeshOrderBox::SMESHGUI_MeshOrderBox(QWidget* theParent) +: QGroupBox( theParent ), myIsChanged( false ), myUpBtn(0), myDownBtn(0) +{ + QHBoxLayout* hBoxLayout = new QHBoxLayout(this); + hBoxLayout->setMargin( MARGIN ); + hBoxLayout->setSpacing( SPACING ); + + myMeshNames = new QListWidget(this); + myMeshNames->setSelectionMode(QAbstractItemView::SingleSelection); + myMeshNames->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding )); + hBoxLayout->addWidget(myMeshNames); + + QGroupBox* btnGrp = new QGroupBox(this); + hBoxLayout->addWidget(btnGrp); + + myUpBtn = new QToolButton(btnGrp); + myDownBtn = new QToolButton(btnGrp); + myUpBtn-> setArrowType( Qt::UpArrow ); + myDownBtn->setArrowType( Qt::DownArrow ); + + QVBoxLayout* vBoxLayout = new QVBoxLayout(btnGrp); + vBoxLayout->addSpacerItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ) ); + vBoxLayout->addWidget( myUpBtn ); + vBoxLayout->addWidget( myDownBtn ); + vBoxLayout->addSpacerItem( new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ) ); + + connect( myUpBtn, SIGNAL( clicked() ), this, SLOT( onMoveItem() ) ); + connect( myDownBtn, SIGNAL( clicked() ), this, SLOT( onMoveItem() ) ); + connect( myMeshNames, SIGNAL( itemSelectionChanged() ), this, SLOT( onSelectionChanged() ) ); + + onSelectionChanged(); +} + +// ========================================================================================= +/*! + * \brief Destructor + */ +//======================================================================= + +SMESHGUI_MeshOrderBox::~SMESHGUI_MeshOrderBox() +{ +} + +// ========================================================================================= +/*! + * \brief add separator item + */ +// ========================================================================================= + +static void addSeparator( QListWidget* theList ) +{ + QListWidgetItem* item = new QListWidgetItem( theList, SeparatorItem ); + QFrame* hline = new QFrame( theList ); + hline->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + theList->addItem( item ); + theList->setItemWidget( item, hline ); +} + +// ========================================================================================= +/*! + * \brief add sub-mesh item + */ +// ========================================================================================= + +static void addMeshItem( QListWidget* theList, + const QString& theName, + const int theId ) +{ + QListWidgetItem* item = new QListWidgetItem( theName, theList, MeshItem ); + item->setData( Qt::UserRole, theId ); + theList->addItem( item ); +} + +// ========================================================================================= +/*! + * \brief Clear submesh names and indeces + */ +// ========================================================================================= + +void SMESHGUI_MeshOrderBox::Clear() +{ + myMeshNames->clear(); + myIsChanged = false; +} + +// ========================================================================================= +/*! + * \brief Set submesh names and indeces + */ +// ========================================================================================= + +void SMESHGUI_MeshOrderBox::SetMeshes(const ListListName& theMeshNames, + const ListListId& theMeshIds) +{ + Clear(); + ListListName::const_iterator nLIt = theMeshNames.constBegin(); + ListListId::const_iterator idLIt = theMeshIds.constBegin(); + for ( ; nLIt != theMeshNames.constEnd(); ++nLIt, ++idLIt ) + { + const QStringList& names = (*nLIt); + const QList& ids = (*idLIt); + if ( myMeshNames->count() ) + addSeparator( myMeshNames ); + QStringList::const_iterator nameIt = names.constBegin(); + QList::const_iterator idIt = ids.constBegin(); + for ( ; nameIt != names.constEnd(); ++nameIt, ++idIt ) + addMeshItem( myMeshNames, *nameIt, *idIt ); + } +} + +// ========================================================================================= +/*! + * \brief cehck that item exists and not a separator + */ +// ========================================================================================= + +static bool checkItem(QListWidgetItem* theItem) +{ + return theItem && (int)theItem->type() != (int)SeparatorItem; +} + +// ========================================================================================= +/*! + * \brief Returns result (ordered by user) mesh names + */ +// ========================================================================================= + +ListListId SMESHGUI_MeshOrderBox::GetMeshIds() const +{ + ListListId aLLIds; + aLLIds.append( QList() ); + for ( int i = 0, n = myMeshNames->count(); i < n; i++ ) + { + QListWidgetItem* it = myMeshNames->item( i ); + if (checkItem( it )) + aLLIds.last().append( it->data( Qt::UserRole ).toInt() ); + else // separator before next list of mesh items + aLLIds.append( QList() ); + } + return aLLIds; +} + +// ========================================================================================= +/*! + * \brief Returns result (ordered by user) mesh indeces + */ +// ========================================================================================= + +ListListName SMESHGUI_MeshOrderBox::GetMeshNames() const +{ + ListListName aLLNames; + aLLNames.append( QStringList() ); + for ( int i = 0, n = myMeshNames->count(); i < n; i++ ) + { + QListWidgetItem* it = myMeshNames->item( i ); + if (checkItem( it )) + aLLNames.last().append( it->text() ); + else // separator before next list of mesh items + aLLNames.append( QStringList() ); + } + return aLLNames; +} + +// ========================================================================================= +/*! + * \brief update state of arrow buttons according to selection + */ +// ========================================================================================= + +void SMESHGUI_MeshOrderBox::onSelectionChanged() +{ + bool isUp = false; + bool isDown = false; + QList items = myMeshNames->selectedItems(); + if ( !items.isEmpty() ) + { + QListWidgetItem* selItem = (*(items.begin())); + if (checkItem(selItem)) + { + const int rowId = myMeshNames->row( selItem ); + isUp = checkItem( myMeshNames->item( rowId - 1 ) ); + isDown = checkItem( myMeshNames->item( rowId + 1 ) ); + } + } + myUpBtn-> setEnabled( isUp ); + myDownBtn->setEnabled( isDown ); +} + +// ========================================================================================= +/*! + * \brief move item according to clicked arrow button + */ +// ========================================================================================= + +void SMESHGUI_MeshOrderBox::onMoveItem() +{ + moveItem( sender() == myUpBtn ); +} + +// ========================================================================================= +/*! + * \brief move mesh in order up or down + */ +// ========================================================================================= + +void SMESHGUI_MeshOrderBox::moveItem(const bool theIsUp) +{ + // move selected list item up or down + QList items = myMeshNames->selectedItems(); + if ( items.isEmpty() ) + return; + QListWidgetItem * selItem = (*(items.begin())); + if (!checkItem(selItem)) + return; + int rowId = myMeshNames->row( selItem ); + if ( rowId == -1 ) + return; + + // move item in list widget + myIsChanged = true; + myMeshNames->takeItem( rowId ); + myMeshNames->insertItem(theIsUp ? rowId-1 : rowId+1, selItem ); + + // restore selection and current status + selItem->setSelected( true ); + myMeshNames->setCurrentItem( selItem ); +} + +// ========================================================================================= +/*! + * \brief returns status is order changed by user + */ +// ========================================================================================= + +bool SMESHGUI_MeshOrderBox:: IsOrderChanged() const +{ + return myIsChanged; +} + +// ========================================================================================= +/*! + * \brief Constructor + */ +// ========================================================================================= + +SMESHGUI_MeshOrderDlg::SMESHGUI_MeshOrderDlg(QWidget* theParent) +: SMESHGUI_Dialog( theParent, false, false, OK | Cancel | Help ) +{ + setWindowTitle( tr( "SMESH_MESHORDER_TITLE") ); + QFrame* main = mainFrame(); + + QVBoxLayout* aDlgLay = new QVBoxLayout (main); + aDlgLay->setMargin( 0 ); + aDlgLay->setSpacing( SPACING ); + + myBox = new SMESHGUI_MeshOrderBox( main ); + + aDlgLay->addWidget(myBox); + aDlgLay->setStretchFactor(main, 1); +} + +// ========================================================================================= +/*! + * \brief Destructor + */ +// ========================================================================================= + +SMESHGUI_MeshOrderDlg::~SMESHGUI_MeshOrderDlg() +{ +} + +// ========================================================================================= +/*! + * \brief return Box widget to show mesh order + */ +// ========================================================================================= + +SMESHGUI_MeshOrderBox* SMESHGUI_MeshOrderDlg::GetMeshOrderBox() const +{ + return myBox; +} diff --git a/src/SMESHGUI/SMESHGUI_MeshOrderDlg.h b/src/SMESHGUI/SMESHGUI_MeshOrderDlg.h new file mode 100644 index 000000000..ec12055bb --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshOrderDlg.h @@ -0,0 +1,112 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshOrderDlg.h +// Author : Pavel TELKOV, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_MeshOrderDlg_H +#define SMESHGUI_MeshOrderDlg_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_Dialog.h" + +// Qt includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QToolButton; +class QListWidget; + +typedef QList ListListName; +typedef QList ListId; +typedef QList ListListId; + +/*! + * \brief Reusable widget that shows and allows modify meshing order + */ + +class SMESHGUI_EXPORT SMESHGUI_MeshOrderBox : public QGroupBox +{ + Q_OBJECT + + public: + /*! Public methods */ + SMESHGUI_MeshOrderBox( QWidget* ); + ~SMESHGUI_MeshOrderBox(); + + //! Clear mesh box + void Clear(); + + //! Set mesh (submesh) names and indeces + void SetMeshes(const ListListName& theMeshNames, + const ListListId& theMeshIds); + + //! returns status is order changed by user + bool IsOrderChanged() const; + + //! Returns result (ordered by user) mesh names + ListListId GetMeshIds() const; + //! Returns result (ordered by user) mesh indeces + ListListName GetMeshNames() const; + + private slots: + /*! Private slots */ + //! update state of arrow buttons according to selection + void onSelectionChanged(); + //! move item according to clicked arrow button + void onMoveItem(); + + private: + /*! Privatemethods */ + //! move mesh in order up or down + void moveItem(const bool theIsUp); + + private: + /*! Private fields */ + bool myIsChanged; + QToolButton* myUpBtn; + QToolButton* myDownBtn; + QListWidget* myMeshNames; +}; + +/*! + * \brief Dialog contains mesh order box for modification operation + */ + +class SMESHGUI_EXPORT SMESHGUI_MeshOrderDlg : public SMESHGUI_Dialog +{ + Q_OBJECT + + public: + /*! Public methods */ + SMESHGUI_MeshOrderDlg( QWidget* ); + ~SMESHGUI_MeshOrderDlg(); + + SMESHGUI_MeshOrderBox* GetMeshOrderBox() const; + + private: + /*! Private fields */ + SMESHGUI_MeshOrderBox* myBox; +}; + +#endif // SMESHGUI_MeshOrderDlg_H diff --git a/src/SMESHGUI/SMESHGUI_MeshOrderOp.cxx b/src/SMESHGUI/SMESHGUI_MeshOrderOp.cxx new file mode 100644 index 000000000..f2a138d1d --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshOrderOp.cxx @@ -0,0 +1,325 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshOrderOp.cxx +// Author : Pavel TELKOV, Open CASCADE S.A.S. +// +#include "SMESHGUI_MeshOrderOp.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_MeshUtils.h" + +// SALOME GUI includes +#include +#include +#include +#include +#include +#include + +// SALOME KERNEL includes +#include +#include + +// STL includes +#include + +//================================================================================ +/*! + * \brief Constructor +*/ +//================================================================================ + +SMESHGUI_MeshOrderOp::SMESHGUI_MeshOrderOp() + : SMESHGUI_Operation(), myDlg(0), myMgr(0) +{ + myDlg = new SMESHGUI_MeshOrderDlg( desktop() ); + + myHelpFileName = "constructing_meshes_page.html#mesh_order_anchor"; +} + +//================================================================================ +/*! + * \brief Destructor +*/ +//================================================================================ + +SMESHGUI_MeshOrderOp::~SMESHGUI_MeshOrderOp() +{ +} + +//================================================================================ +/*! + * \brief Return operation dialog + */ +//================================================================================ + +LightApp_Dialog* SMESHGUI_MeshOrderOp::dlg() const +{ + return myDlg; +} + +//================================================================================ +/*! + * \brief perform it's intention action: compute 2D mesh on 3D + */ +//================================================================================ + +void SMESHGUI_MeshOrderOp::startOperation() +{ + SMESHGUI_Operation::startOperation(); + if (myMgr) + myDlg->show(); +} + +//================================================================================ +/*! + * \brief Init dialog and mesh order box + */ +//================================================================================ + +void SMESHGUI_MeshOrderOp::initDialog() +{ + if (!myDlg ) + return; + + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_nil(); + // check selection + LightApp_SelectionMgr *Sel = selectionMgr(); + SALOME_ListIO selected; Sel->selectedObjects( selected ); + + if (selected.Extent() == 1) + aMesh = SMESH::GetMeshByIO(selected.First()); + if (aMesh->_is_nil()) { + SUIT_MessageBox::warning(desktop(), + tr("SMESH_WRN_WARNING"), + tr("SMESH_WRN_NO_AVAILABLE_DATA")); + onCancel(); + return; + } + + myMgr = new SMESHGUI_MeshOrderMgr( myDlg->GetMeshOrderBox() ); + myMgr->SetMesh( aMesh ); + if ( !myMgr->GetMeshOrder() ) { + SUIT_MessageBox::information(desktop(), + tr("SMESH_INFORMATION"), + tr("SMESH_NO_CONCURENT_MESH")); + + onCancel(); + return; + } +} + +//================================================================================ +/*! + * \brief Apply changes + */ +//================================================================================ + +bool SMESHGUI_MeshOrderOp::onApply() +{ + SUIT_OverrideCursor aWaitCursor; + bool res = myMgr ? myMgr->SetMeshOrder() : false; + + if( res ) + SMESHGUI::Modified(); + + delete myMgr; + myMgr = 0; + + return res; +} + +//================================================================================ +/*! + * \brief Apply changes + */ +//================================================================================ + +void SMESHGUI_MeshOrderOp::onCancel() +{ + delete myMgr; + myMgr = 0; + + abort(); +} + +//================================================================================ +/*! + * \brief Constructor +*/ +//================================================================================ + +SMESHGUI_MeshOrderMgr::SMESHGUI_MeshOrderMgr( SMESHGUI_MeshOrderBox* theBox ) +: myBox( theBox ) +{ + myMesh = SMESH::SMESH_Mesh::_nil(); +} + +//================================================================================ +/*! + * \brief Destructor +*/ +//================================================================================ + +SMESHGUI_MeshOrderMgr::~SMESHGUI_MeshOrderMgr() +{ +} + +//================================================================================ +/*! + * \brief Set root mesh object + */ +//================================================================================ + +void SMESHGUI_MeshOrderMgr::SetMesh(SMESH::SMESH_Mesh_var& theMesh) +{ + myMesh = SMESH::SMESH_Mesh::_duplicate(theMesh); + _PTR(SObject) aMeshSObj = SMESH::FindSObject(theMesh); + if ( myBox && aMeshSObj ) + myBox->setTitle( aMeshSObj->GetName().c_str() ); +} + +//================================================================================ +/*! + * \brief Check for concurents between submesh objects + */ +//================================================================================ + +bool SMESHGUI_MeshOrderMgr::GetMeshOrder() +{ + ListListId idListList; + return GetMeshOrder(idListList); +} + +//================================================================================ +/*! + * \brief Check for concurents between submesh objects + */ +//================================================================================ + +bool SMESHGUI_MeshOrderMgr::GetMeshOrder(ListListId& theIdListList) +{ + if (!myBox || myMesh->_is_nil()) + return false; + myBox->Clear(); + SMESH::submesh_array_array_var meshOrder = myMesh->GetMeshOrder(); + if ( !meshOrder.operator->() || !meshOrder->length() ) + return false; + ListListName nameListList; + for ( int i = 0, n = meshOrder->length(); i < n; i++ ) + { + QList idList; + QStringList nameList; + const SMESH::submesh_array& aSMArray = meshOrder[i]; + for ( int j = 0, jn = aSMArray.length(); j < jn; j++ ) + { + const SMESH::SMESH_subMesh_var subMesh = aSMArray[j]; + + _PTR(SObject) aSubMeshSObj = SMESH::FindSObject(subMesh); + if ( !aSubMeshSObj ) + continue; + + idList.append(subMesh->GetId() ); + nameList.append( QString(aSubMeshSObj->GetName().c_str()) ); + } + theIdListList.append(idList); + nameListList.append(nameList); + } + myBox->SetMeshes(nameListList, theIdListList); + return !theIdListList.isEmpty(); +} + +//================================================================================ +/*! + * \brief Returns status is order changed by user + */ +//================================================================================ + +bool SMESHGUI_MeshOrderMgr::IsOrderChanged() const +{ + return myBox && myBox->IsOrderChanged(); +} + +//================================================================================ +/*! + * \brief Store submesh priority order + */ +//================================================================================ + +bool SMESHGUI_MeshOrderMgr::SetMeshOrder() +{ + return myBox ? SetMeshOrder(myBox->GetMeshIds()) : false; +} + +//================================================================================ +/*! + * \brief Store submesh priority order + */ +//================================================================================ + +bool SMESHGUI_MeshOrderMgr::SetMeshOrder( const ListListId& theListListIds ) +{ + if (theListListIds.isEmpty() || myMesh->_is_nil()) + return false; + + _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); + _PTR(SObject) aMeshSObj = SMESH::FindSObject(myMesh); + if ( !aStudy || !aMeshSObj ) + return false; + + std::map mapOfSubMesh; + for (int i = SMESH::Tag_FirstSubMesh; i <= SMESH::Tag_LastSubMesh; i++) { + _PTR(SObject) aSubmeshRoot; + if ( !aMeshSObj->FindSubObject( i, aSubmeshRoot ) ) + continue; + _PTR(ChildIterator) smIter = aStudy->NewChildIterator( aSubmeshRoot ); + for ( ; smIter->More(); smIter->Next() ) { + _PTR(SObject) aSmObj = smIter->Value(); + SMESH::SMESH_subMesh_var sm = + SMESH::SObjectToInterface( aSmObj ); + mapOfSubMesh[ sm->GetId() ] = SMESH::SMESH_subMesh::_duplicate(sm); + } + } + + // is it enought to set modifid attribute on root mesh objects only? + // it is seems that modifaction flag will be set on child submeshes + // automatically (see SMESH::ModifiedMesh for details) + SMESH::ModifiedMesh( aMeshSObj, false, false ); + + SMESH::submesh_array_array_var meshOrder = new SMESH::submesh_array_array(); + meshOrder->length(theListListIds.count() ); + ListListId::const_iterator it = theListListIds.constBegin(); + for ( int i = 0; it != theListListIds.constEnd(); ++it ) { + const QList& ids = *it; + SMESH::submesh_array_var subMeshList = new SMESH::submesh_array(); + subMeshList->length( ids.count() ); + QList::const_iterator subIt = ids.constBegin(); + for( int j = 0; subIt != ids.constEnd(); ++subIt ) + if ( mapOfSubMesh.find( *subIt ) != mapOfSubMesh.end() ) + subMeshList[ j++ ] = mapOfSubMesh[ *subIt ]; + + meshOrder[ i++ ] = subMeshList; + } + // update object browser + SMESHGUI::GetSMESHGUI()->updateObjBrowser( true, 0 ); + + return myMesh->SetMeshOrder(meshOrder); +} diff --git a/src/SMESHGUI/SMESHGUI_MeshOrderOp.h b/src/SMESHGUI/SMESHGUI_MeshOrderOp.h new file mode 100644 index 000000000..f21e84842 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_MeshOrderOp.h @@ -0,0 +1,93 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_MeshOrderOp.h +// Author : Pavel TELKOV, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_MeshOrderOp_H +#define SMESHGUI_MeshOrderOp_H + + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +#include "SMESHGUI_Operation.h" +#include "SMESHGUI_MeshOrderDlg.h" + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Gen) +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +/*! + * \brief Operator to check and modify mesh computation submesh priority (order) + */ +class SMESHGUI_EXPORT SMESHGUI_MeshOrderMgr +{ +public: + SMESHGUI_MeshOrderMgr( SMESHGUI_MeshOrderBox* ); + virtual ~SMESHGUI_MeshOrderMgr(); + //! Set root mesh object + void SetMesh( SMESH::SMESH_Mesh_var& theMesh ); + //! Check for concurents between submesh objects + bool GetMeshOrder(); + //! Check for concurents between submesh objects + bool GetMeshOrder( ListListId& theIds ); + //! Store submesh priority order + bool SetMeshOrder(); + //! Store given submesh priority order + bool SetMeshOrder( const ListListId& theIds ); + + //! Returns status is order changed by user + bool IsOrderChanged() const; + +private: + SMESH::SMESH_Mesh_var myMesh; + SMESHGUI_MeshOrderBox* myBox; +}; + +/*! + * \brief Operator to check and modify mesh computation submesh priority (order) + */ +class SMESHGUI_EXPORT SMESHGUI_MeshOrderOp: public SMESHGUI_Operation +{ + Q_OBJECT + +public: + SMESHGUI_MeshOrderOp(); + virtual ~SMESHGUI_MeshOrderOp(); + +protected: + virtual LightApp_Dialog* dlg() const; + + virtual void startOperation(); + + //! sets the dialog widgets to state just after operation start + virtual void initDialog(); + + protected slots: + virtual bool onApply(); + virtual void onCancel(); + + private: + SMESHGUI_MeshOrderDlg* myDlg; + SMESHGUI_MeshOrderMgr* myMgr; +}; + +#endif // SMESHGUI_MeshOrderOp_H diff --git a/src/SMESHGUI/SMESHGUI_MeshPatternDlg.cxx b/src/SMESHGUI/SMESHGUI_MeshPatternDlg.cxx index 8456928f7..cfb1f0e64 100755 --- a/src/SMESHGUI/SMESHGUI_MeshPatternDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshPatternDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshPatternDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -397,7 +398,7 @@ void SMESHGUI_MeshPatternDlg::Init() updateGeometry(); - resize(minimumSize()); + resize(100,100); activateSelection(); onSelectionDone(); @@ -420,10 +421,10 @@ bool SMESHGUI_MeshPatternDlg::isValid (const bool theMess) ok = myNode2->isValid( msg, theMess ) && ok; if( !ok ) { if( theMess ) { - QString str( tr( "SMESH_INCORRECT_INPUT" ) ); - if ( !msg.isEmpty() ) - str += "\n" + msg; - SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); + QString str( tr( "SMESH_INCORRECT_INPUT" ) ); + if ( !msg.isEmpty() ) + str += "\n" + msg; + SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); } return false; } @@ -432,22 +433,22 @@ bool SMESHGUI_MeshPatternDlg::isValid (const bool theMess) QList ids; if ((isRefine() && (myMesh->_is_nil() || !getIds(ids) || getNode(false) < 0 || - myType == Type_3d && (getNode(true) < 0 || getNode(false) == getNode(true)))) + (myType == Type_3d && (getNode(true) < 0 || getNode(false) == getNode(true))))) || (!isRefine() && (myMesh->_is_nil() || myMeshShape->_is_nil() || myGeomObj[ Object ]->_is_nil() || - myGeomObj[ Vertex1 ]->_is_nil() || myType == Type_3d && myGeomObj[ Vertex2 ]->_is_nil()))) + myGeomObj[ Vertex1 ]->_is_nil() || (myType == Type_3d && myGeomObj[ Vertex2 ]->_is_nil())))) { if (theMess) SUIT_MessageBox::information(this, tr("SMESH_INSUFFICIENT_DATA"), - tr("SMESHGUI_INVALID_PARAMETERS")); + tr("SMESHGUI_INVALID_PARAMETERS")); return false; } if ( myName->text().isEmpty() ) { if (theMess) SUIT_MessageBox::information(this, tr("SMESH_INSUFFICIENT_DATA"), - tr("SMESHGUI_INVALID_PARAMETERS")); + tr("SMESHGUI_INVALID_PARAMETERS")); return false; } @@ -467,22 +468,23 @@ bool SMESHGUI_MeshPatternDlg::onApply() erasePreview(); if (isRefine()) { // Refining existing mesh elements + { + QStringList aParameters; + aParameters << myNode1->text(); + if(myType == Type_3d ) + aParameters << myNode2->text(); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + } QList ids; getIds(ids); SMESH::long_array_var varIds = new SMESH::long_array(); varIds->length(ids.count()); int i = 0; for (QList::iterator it = ids.begin(); it != ids.end(); ++it) - varIds[i++] = *it; + varIds[i++] = *it; myType == Type_2d - ? myPattern->ApplyToMeshFaces (myMesh, varIds, getNode(false), myReverseChk->isChecked()) - : myPattern->ApplyToHexahedrons(myMesh, varIds, getNode(false), getNode(true)); - - QStringList aParameters; - aParameters << myNode1->text(); - if(myType == Type_3d ) - aParameters << myNode2->text(); - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); + ? myPattern->ApplyToMeshFaces (myMesh, varIds, getNode(false), myReverseChk->isChecked()) + : myPattern->ApplyToHexahedrons(myMesh, varIds, getNode(false), getNode(true)); } else { // Applying a pattern to geometrical object if (myType == Type_2d) @@ -497,18 +499,19 @@ bool SMESHGUI_MeshPatternDlg::onApply() //mySelectionMgr->clearSelected(); bool autoUpdate = SMESHGUI::automaticUpdate(); if (!isRefine() && autoUpdate) { - _PTR(SObject) aSO = SMESH::FindSObject(myMesh.in()); - SMESH_Actor* anActor = SMESH::FindActorByEntry(aSO->GetID().c_str()); - if (!anActor) { - anActor = SMESH::CreateActor(aSO->GetStudy(), aSO->GetID().c_str()); - if (anActor) { - SMESH::DisplayActor(SMESH::GetActiveWindow(), anActor); - SMESH::FitAll(); - } - } + _PTR(SObject) aSO = SMESH::FindSObject(myMesh.in()); + SMESH_Actor* anActor = SMESH::FindActorByEntry(aSO->GetID().c_str()); + if (!anActor) { + anActor = SMESH::CreateActor(aSO->GetStudy(), aSO->GetID().c_str()); + if (anActor) { + SMESH::DisplayActor(SMESH::GetActiveWindow(), anActor); + SMESH::FitAll(); + } + } } mySelectionMgr->clearSelected(); SMESH::UpdateView(); + SMESHGUI::Modified(); mySMESHGUI->updateObjBrowser(true); @@ -517,7 +520,7 @@ bool SMESHGUI_MeshPatternDlg::onApply() return true; } else { SUIT_MessageBox::information(this, tr("SMESH_ERROR"), - tr("SMESH_OPERATION_FAILED")); + tr("SMESH_OPERATION_FAILED")); return false; } } catch (const SALOME::SALOME_Exception& S_ex) { @@ -565,17 +568,17 @@ void SMESHGUI_MeshPatternDlg::onHelp() if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { - QString platform; + QString platform; #ifdef WIN32 - platform = "winapplication"; + platform = "winapplication"; #else - platform = "application"; + platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -593,7 +596,7 @@ void SMESHGUI_MeshPatternDlg::onSelectionDone() SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); if (aList.Extent() != 1) - return; + return; // Retrieve mesh from selection Handle(SALOME_InteractiveObject) anIO = aList.First(); @@ -624,11 +627,11 @@ void SMESHGUI_MeshPatternDlg::onSelectionDone() SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); if (aList.Extent() != 1) - return; + return; QString anIds; if (!SMESH::GetNameOfSelectedElements(mySelector, aList.First(), anIds)) - anIds = ""; + anIds = ""; myBusy = true; mySelEdit[ Ids ]->setText(anIds); @@ -638,7 +641,7 @@ void SMESHGUI_MeshPatternDlg::onSelectionDone() SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList, SVTK_Viewer::Type()); if (aList.Extent() != 1) - return; + return; // Get geom object from selection Handle(SALOME_InteractiveObject) anIO = aList.First(); @@ -809,14 +812,14 @@ void SMESHGUI_MeshPatternDlg::onOpen() QFile aFile(fName); if (!aFile.open(QIODevice::ReadOnly)) { SUIT_MessageBox::information(this, tr("SMESH_ERROR"), - tr("ERROR_OF_OPENING")); + tr("ERROR_OF_OPENING")); return; } QByteArray aDataArray = aFile.readAll(); if (aDataArray.isEmpty()) { SUIT_MessageBox::information(this, tr("SMESH_ERROR"), - tr("ERROR_OF_READING")); + tr("ERROR_OF_READING")); return; } @@ -1046,11 +1049,11 @@ void SMESHGUI_MeshPatternDlg::updateWgState() if (!CORBA::is_nil(myPattern)/* && getIds(ids)*/) { SMESH::long_array_var keyPoints = myPattern->GetKeyPoints(); if (keyPoints->length()) { - myNode1->setEnabled(true); - myNode2->setEnabled(true); - myNode1->setRange(1, keyPoints->length()); - myNode2->setRange(1, keyPoints->length()); - return; + myNode1->setEnabled(true); + myNode2->setEnabled(true); + myNode1->setRange(1, keyPoints->length()); + myNode2->setRange(1, keyPoints->length()); + return; } } @@ -1075,13 +1078,13 @@ void SMESHGUI_MeshPatternDlg::activateSelection() if (myType == Type_2d) { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); } else { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(CellSelection); } } else { @@ -1123,7 +1126,7 @@ bool SMESHGUI_MeshPatternDlg::loadFromFile (const QString& theName) SMESH::SMESH_Pattern_var aPattern = SMESH::GetPattern(); if (!aPattern->LoadFromFile(theName.toLatin1().data()) || - myType == Type_2d && !aPattern->Is2D()) { + (myType == Type_2d && !aPattern->Is2D())) { SMESH::SMESH_Pattern::ErrorCode aCode = aPattern->GetErrorCode(); QString aMess; if (aCode == SMESH::SMESH_Pattern::ERR_READ_NB_POINTS ) aMess = tr("ERR_READ_NB_POINTS"); @@ -1146,7 +1149,7 @@ bool SMESHGUI_MeshPatternDlg::loadFromFile (const QString& theName) } catch (const SALOME::SALOME_Exception& S_ex) { SalomeApp_Tools::QtCatchCorbaException(S_ex); SUIT_MessageBox::information(this, tr("SMESH_ERROR"), - tr("ERROR_OF_LOADING") ); + tr("ERROR_OF_LOADING") ); return false; } } @@ -1229,13 +1232,13 @@ vtkUnstructuredGrid* SMESHGUI_MeshPatternDlg::getGrid() varIds->length(ids.count()); int i = 0; for (QList::iterator it = ids.begin(); it != ids.end(); ++it) - varIds[i++] = *it; + varIds[i++] = *it; pnts = myType == Type_2d - ? myPattern->ApplyToMeshFaces (myMesh, varIds, getNode(false), myReverseChk->isChecked()) - : myPattern->ApplyToHexahedrons(myMesh, varIds, getNode(false), getNode(true)); + ? myPattern->ApplyToMeshFaces (myMesh, varIds, getNode(false), myReverseChk->isChecked()) + : myPattern->ApplyToHexahedrons(myMesh, varIds, getNode(false), getNode(true)); } else { pnts = myType == Type_2d - ? myPattern->ApplyToFace (myGeomObj[ Object ], myGeomObj[ Vertex1 ], myReverseChk->isChecked()) + ? myPattern->ApplyToFace (myGeomObj[ Object ], myGeomObj[ Vertex1 ], myReverseChk->isChecked()) : myPattern->ApplyTo3DBlock(myGeomObj[ Object ], myGeomObj[ Vertex1 ], myGeomObj[ Vertex2 ]); } @@ -1380,7 +1383,7 @@ void SMESHGUI_MeshPatternDlg::onTextChanged (const QString& theNewText) for (int i = 0; i < aListId.count(); i++) { const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); if (e && e->GetType() == (myType == Type_2d ? SMDSAbs_Face : SMDSAbs_Volume)) - newIndices.Add(e->GetID()); + newIndices.Add(e->GetID()); } mySelector->AddOrRemoveIndex( anActor->getIO(), newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) diff --git a/src/SMESHGUI/SMESHGUI_MeshPatternDlg.h b/src/SMESHGUI/SMESHGUI_MeshPatternDlg.h index e3e2d6870..76e6aca77 100755 --- a/src/SMESHGUI/SMESHGUI_MeshPatternDlg.h +++ b/src/SMESHGUI/SMESHGUI_MeshPatternDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_MeshPatternDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. // diff --git a/src/SMESHGUI/SMESHGUI_MeshUtils.cxx b/src/SMESHGUI/SMESHGUI_MeshUtils.cxx index b524c2f83..13898d991 100644 --- a/src/SMESHGUI/SMESHGUI_MeshUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_MeshUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshUtils.cxx // Author : Open CASCADE S.A.S. @@ -26,6 +27,7 @@ // #include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI.h" #include "SMESHGUI_Utils.h" // SALOME KERNEL includes @@ -37,6 +39,7 @@ // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Group) +#include CORBA_SERVER_HEADER(SMESH_Measurements) namespace SMESH { @@ -46,13 +49,13 @@ namespace SMESH if(!CORBA::is_nil(anObj)){ SMESH_Mesh_var aMesh = SMESH_Mesh::_narrow(anObj); if(!CORBA::is_nil(aMesh)) - return aMesh; + return aMesh; SMESH_GroupBase_var aGroup = SMESH_GroupBase::_narrow(anObj); if(!CORBA::is_nil(aGroup)) - return aGroup->GetMesh(); + return aGroup->GetMesh(); SMESH_subMesh_var aSubMesh = SMESH_subMesh::_narrow(anObj); if(!CORBA::is_nil(aSubMesh)) - return aSubMesh->GetFather(); + return aSubMesh->GetFather(); } return SMESH_Mesh::_nil(); } @@ -65,20 +68,69 @@ namespace SMESH QString name = baseName; while ( !aStudy->FindObjectByName( name.toLatin1().data(), "SMESH" ).empty() ) { int nb = 0; - QStringList names = name.split("_", QString::KeepEmptyParts); - if ( names.count() > 0 ) { - bool ok; - int index = names.last().toInt( &ok ); - if ( ok ) { - nb = index; - names.removeLast(); - } - } - names.append( QString::number( nb+1 ) ); - name = names.join( "_" ); + QStringList names = name.split("_", QString::KeepEmptyParts); + if ( names.count() > 0 ) { + bool ok; + int index = names.last().toInt( &ok ); + if ( ok ) { + nb = index; + names.removeLast(); + } + } + names.append( QString::number( nb+1 ) ); + name = names.join( "_" ); } return name; } return baseName; } + + QString UniqueName(const QString& theBaseName, _PTR(SObject) theParent, const QString& thePostfix) + { + QString baseName = thePostfix.isEmpty() ? + theBaseName : theBaseName + "_" + thePostfix; + QString name = baseName; + if ( _PTR(Study) aStudy = GetActiveStudyDocument() ) { + _PTR(SObject) p = theParent; + if ( !p ) p = aStudy->FindComponent( "SMESH" ); + if ( p ) { + _PTR(ChildIterator) iter = aStudy->NewChildIterator( p ); + int idx = 0; + while( true ) { + bool found = false; + for ( ; iter->More(); iter->Next() ) { + _PTR(SObject) so = iter->Value(); + if ( !so ) continue; // skip bad objects + _PTR(SObject) ref; + if ( so->ReferencedObject( ref ) ) continue; // skip references + QString n = so->GetName().c_str(); + if ( !n.isEmpty() && n == name ) { + QStringList names = name.split("_", QString::KeepEmptyParts); + if ( names.count() > 0 ) { + bool ok; + names.last().toInt( &ok ); + if ( ok ) + names.removeLast(); + } + names.append( QString::number( ++idx ) ); + name = names.join( "_" ); + found = true; + break; + } + } + if ( !found ) break; + } + } + } + return name; + } + + SMESH::Measurements_var& GetMeasurements() + { + static SMESH::Measurements_var aMeasurements; + if (CORBA::is_nil(aMeasurements)) { + aMeasurements = SMESHGUI::GetSMESHGen()->CreateMeasurements(); + } + return aMeasurements; + } } // end of namespace SMESH diff --git a/src/SMESHGUI/SMESHGUI_MeshUtils.h b/src/SMESHGUI/SMESHGUI_MeshUtils.h index 0544d4306..1adbabccc 100644 --- a/src/SMESHGUI/SMESHGUI_MeshUtils.h +++ b/src/SMESHGUI/SMESHGUI_MeshUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_MeshUtils.h // Author : Open CASCADE S.A.S. @@ -35,9 +36,15 @@ // SALOME GUI includes #include +// SALOME KERNEL includes +#include + // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_Measurements) + +class SALOMEDSClient_SObject; namespace SMESH { @@ -46,6 +53,10 @@ namespace SMESH SMESHGUI_EXPORT QString UniqueMeshName( const QString&, const QString& = QString() ); + SMESHGUI_EXPORT + QString UniqueName( const QString&, _PTR(SObject) = _PTR(SObject)(), const QString& = QString() ); + + SMESHGUI_EXPORT SMESH::Measurements_var& GetMeasurements(); } #endif // SMESHGUI_MESHUTILS_H diff --git a/src/SMESHGUI/SMESHGUI_MoveNodesDlg.cxx b/src/SMESHGUI/SMESHGUI_MoveNodesDlg.cxx deleted file mode 100644 index 199575293..000000000 --- a/src/SMESHGUI/SMESHGUI_MoveNodesDlg.cxx +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_MoveNodesDlg.cxx -// Author : Nicolas REJNERI, Open CASCADE S.A.S. -// SMESH includes -// -#include "SMESHGUI_MoveNodesDlg.h" - -#include "SMESHGUI.h" -#include "SMESHGUI_SpinBox.h" -#include "SMESHGUI_IdValidator.h" -#include "SMESHGUI_Utils.h" -#include "SMESHGUI_VTKUtils.h" -#include "SMESHGUI_MeshUtils.h" - -#include -#include - -// SALOME GUI includes -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -// OCCT includes -#include - -// VTK includes -#include -#include -#include -#include -#include -#include - -// Qt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// IDL includes -#include -#include CORBA_SERVER_HEADER(SMESH_Mesh) -#include CORBA_SERVER_HEADER(SMESH_MeshEditor) - -#define SPACING 6 -#define MARGIN 11 - -//================================================================================= -// name : SMESHGUI_MoveNodesDlg::SMESHGUI_MoveNodesDlg -// Purpose : -//================================================================================= -SMESHGUI_MoveNodesDlg::SMESHGUI_MoveNodesDlg(SMESHGUI* theModule): - QDialog(SMESH::GetDesktop(theModule)), - mySelectionMgr(SMESH::GetSelectionMgr(theModule)), - mySMESHGUI(theModule) -{ - myPreviewActor = 0; - myBusy = false; - - setModal(false); - setWindowTitle(tr("CAPTION")); - - QVBoxLayout* aDlgLay = new QVBoxLayout(this); - aDlgLay->setSpacing(SPACING); - aDlgLay->setMargin(MARGIN); - - QWidget* aMainFrame = createMainFrame (this); - QWidget* aBtnFrame = createButtonFrame(this); - - aDlgLay->addWidget(aMainFrame); - aDlgLay->addWidget(aBtnFrame); - - mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); - - myHelpFileName = "moving_nodes_page.html"; - - Init(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::createButtonFrame -// Purpose : Create frame containing buttons -//======================================================================= -QWidget* SMESHGUI_MoveNodesDlg::createButtonFrame (QWidget* theParent) -{ - QFrame* aFrame = new QFrame(theParent); - aFrame->setFrameStyle(QFrame::Box | QFrame::Sunken); - - myOkBtn = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), aFrame); - myApplyBtn = new QPushButton(tr("SMESH_BUT_APPLY"), aFrame); - myCloseBtn = new QPushButton(tr("SMESH_BUT_CLOSE"), aFrame); - myHelpBtn = new QPushButton(tr("SMESH_BUT_HELP"), aFrame); - - QHBoxLayout* aLay = new QHBoxLayout(aFrame); - aLay->setSpacing(SPACING); - aLay->setMargin(MARGIN); - - aLay->addWidget(myOkBtn); - aLay->addSpacing(10); - aLay->addWidget(myApplyBtn); - aLay->addSpacing(10); - aLay->addStretch(); - aLay->addWidget(myCloseBtn); - aLay->addWidget(myHelpBtn); - - connect(myOkBtn, SIGNAL(clicked()), SLOT(onOk())); - connect(myCloseBtn, SIGNAL(clicked()), SLOT(onClose())); - connect(myApplyBtn, SIGNAL(clicked()), SLOT(onApply())); - connect(myHelpBtn, SIGNAL(clicked()), SLOT(onHelp())); - - return aFrame; -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::createMainFrame -// Purpose : Create frame containing dialog's input fields -//======================================================================= -QWidget* SMESHGUI_MoveNodesDlg::createMainFrame (QWidget* theParent) -{ - QWidget* aFrame = new QWidget(theParent); - - QPixmap iconMoveNode (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_MOVE_NODE"))); - QPixmap iconSelect (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT"))); - - //------------------------------------------------------------ - QGroupBox* aPixGrp = new QGroupBox(tr("MESH_NODE"), aFrame); - QButtonGroup* aBtnGrp = new QButtonGroup(this); - QHBoxLayout* aPixGrpLayout = new QHBoxLayout(aPixGrp); - aPixGrpLayout->setSpacing(SPACING); - aPixGrpLayout->setMargin(MARGIN); - - QRadioButton* aRBut = new QRadioButton(aPixGrp); - aRBut->setIcon(iconMoveNode); - aRBut->setChecked(true); - - aPixGrpLayout->addWidget(aRBut); - aBtnGrp->addButton(aRBut, 0); - - //------------------------------------------------------------ - QGroupBox* anIdGrp = new QGroupBox(tr("SMESH_MOVE"), aFrame); - QHBoxLayout* anIdGrpLayout = new QHBoxLayout(anIdGrp); - anIdGrpLayout->setSpacing(SPACING); - anIdGrpLayout->setMargin(MARGIN); - - QLabel* idLabl = new QLabel(tr("NODE_ID"), anIdGrp); - QPushButton* idBtn = new QPushButton(anIdGrp); - idBtn->setIcon(iconSelect); - myId = new QLineEdit(anIdGrp); - myId->setValidator(new SMESHGUI_IdValidator(this, 1)); - - anIdGrpLayout->addWidget(idLabl); - anIdGrpLayout->addWidget(idBtn); - anIdGrpLayout->addWidget(myId); - - //------------------------------------------------------------ - QGroupBox* aCoordGrp = new QGroupBox(tr("SMESH_COORDINATES"), aFrame); - QHBoxLayout* aCoordGrpLayout = new QHBoxLayout(aCoordGrp); - aCoordGrpLayout->setSpacing(SPACING); - aCoordGrpLayout->setMargin(MARGIN); - - QLabel* aXLabel = new QLabel(tr("SMESH_X"), aCoordGrp); - myX = new SMESHGUI_SpinBox(aCoordGrp); - - QLabel* aYLabel = new QLabel(tr("SMESH_Y"), aCoordGrp); - myY = new SMESHGUI_SpinBox(aCoordGrp); - - QLabel* aZLabel = new QLabel(tr("SMESH_Z"), aCoordGrp); - myZ = new SMESHGUI_SpinBox(aCoordGrp); - - aCoordGrpLayout->addWidget(aXLabel); - aCoordGrpLayout->addWidget(myX); - aCoordGrpLayout->addWidget(aYLabel); - aCoordGrpLayout->addWidget(myY); - aCoordGrpLayout->addWidget(aZLabel); - aCoordGrpLayout->addWidget(myZ); - - //------------------------------------------------------------ - myX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 25.0, DBL_DIGITS_DISPLAY); - myY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 25.0, DBL_DIGITS_DISPLAY); - myZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 25.0, DBL_DIGITS_DISPLAY); - - //------------------------------------------------------------ - QVBoxLayout* aLay = new QVBoxLayout(aFrame); - aLay->setMargin(0); - aLay->setMargin(SPACING); - aLay->addWidget(aPixGrp); - aLay->addWidget(anIdGrp); - aLay->addWidget(aCoordGrp); - - //------------------------------------------------------------ - // connect signale and slots - connect(myX, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); - connect(myY, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); - connect(myZ, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); - connect(myId, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); - - return aFrame; -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::~SMESHGUI_MoveNodesDlg -// Purpose : -//======================================================================= -SMESHGUI_MoveNodesDlg::~SMESHGUI_MoveNodesDlg() -{ - erasePreview(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::Init -// Purpose : Init dialog fields -//======================================================================= -void SMESHGUI_MoveNodesDlg::Init() -{ - myPreviewActor = 0; - myMeshActor = 0; - myBusy = false; - - mySMESHGUI->SetActiveDialogBox((QDialog*)this); - - // selection and SMESHGUI - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone())); - connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), SLOT(onDeactivate())); - connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(onClose())); - - reset(); - setEnabled(true); - - // set selection mode - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); - - onSelectionDone(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::isValid -// Purpose : Verify validity of entry information -//======================================================================= -bool SMESHGUI_MoveNodesDlg::isValid (const bool theMess) -{ - if (myId->text().isEmpty()) { - if (theMess) - SUIT_MessageBox::information(this, tr("SMESH_WARNING"), - tr("NODE_ID_IS_NOT_DEFINED")); - return false; - } - - QString msg; - bool ok = true; - ok = myX->isValid( msg, theMess ) && ok; - ok = myY->isValid( msg, theMess ) && ok; - ok = myZ->isValid( msg, theMess ) && ok; - if( !ok ) { - if( theMess ) { - QString str( tr( "SMESH_INCORRECT_INPUT" ) ); - if ( !msg.isEmpty() ) - str += "\n" + msg; - SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); - } - return false; - } - - return true; -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::reset -// Purpose : Reset the dialog state -//======================================================================= -void SMESHGUI_MoveNodesDlg::reset() -{ - myId->clear(); - myX->SetValue(0); - myY->SetValue(0); - myZ->SetValue(0); - redisplayPreview(); - updateButtons(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onApply -// Purpose : SLOT called when "Apply" button pressed. -//======================================================================= -bool SMESHGUI_MoveNodesDlg::onApply() -{ - if (mySMESHGUI->isActiveStudyLocked()) - return false; - - if (!isValid(true)) - return false; - - SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myMeshActor->getIO()); - if (aMesh->_is_nil()) { - SUIT_MessageBox::information(this, tr("SMESH_ERROR"), - tr("SMESHG_NO_MESH")); - return false; - } - - SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); - if (aMeshEditor->_is_nil()) - return false; - - int anId = myId->text().toInt(); - bool aResult = false; - try { - aResult = aMeshEditor->MoveNode(anId, myX->GetValue(), myY->GetValue(), myZ->GetValue()); - - QStringList aParameters; - aParameters << myX->text(); - aParameters << myY->text(); - aParameters << myZ->text(); - aMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - } catch (...) { - } - - if (aResult) { - SALOME_ListIO aList; - aList.Append(myMeshActor->getIO()); - mySelectionMgr->setSelectedObjects(aList,false); - SMESH::UpdateView(); - reset(); - } - - return aResult; -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onOk -// Purpose : SLOT called when "Ok" button pressed. -//======================================================================= -void SMESHGUI_MoveNodesDlg::onOk() -{ - if (onApply()) - onClose(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onClose -// Purpose : SLOT called when "Close" button pressed. Close dialog -//======================================================================= -void SMESHGUI_MoveNodesDlg::onClose() -{ - //mySelectionMgr->clearSelected(); - SMESH::SetPointRepresentation(false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); - disconnect(mySelectionMgr, 0, this, 0); - disconnect(mySMESHGUI, 0, this, 0); - erasePreview(); - mySMESHGUI->ResetState(); - reject(); -} - -//================================================================================= -// function : onHelp() -// purpose : -//================================================================================= -void SMESHGUI_MoveNodesDlg::onHelp() -{ - LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); - else { - QString platform; -#ifdef WIN32 - platform = "winapplication"; -#else - platform = "application"; -#endif - SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); - } -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onTextChange -// Purpose : -//======================================================================= -void SMESHGUI_MoveNodesDlg::onTextChange (const QString& theNewText) -{ - if (myBusy) return; - - myOkBtn->setEnabled(false); - myApplyBtn->setEnabled(false); - erasePreview(); - - // select entered node - if(myMeshActor){ - if(SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh()){ - myBusy = true; - Handle(SALOME_InteractiveObject) anIO = myMeshActor->getIO(); - SALOME_ListIO aList; - aList.Append(anIO); - mySelectionMgr->setSelectedObjects(aList,false); - myBusy = false; - - if(const SMDS_MeshElement *anElem = aMesh->FindElement(theNewText.toInt())) { - TColStd_MapOfInteger aListInd; - aListInd.Add(anElem->GetID()); - mySelector->AddOrRemoveIndex(anIO,aListInd, false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight(anIO,true,true); - - onSelectionDone(); - } - } - } -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onSelectionDone -// Purpose : SLOT called when selection changed -//======================================================================= -void SMESHGUI_MoveNodesDlg::onSelectionDone() -{ - if (myBusy) return; - myMeshActor = 0; - - SALOME_ListIO aList; - mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); - - if (aList.Extent() == 1) { - Handle(SALOME_InteractiveObject) anIO = aList.First(); - myMeshActor = SMESH::FindActorByEntry(anIO->getEntry()); - if(myMeshActor){ - QString aText; - if (SMESH::GetNameOfSelectedNodes(mySelector,anIO,aText) == 1) { - if(SMDS_Mesh* aMesh = myMeshActor->GetObject()->GetMesh()) { - if(const SMDS_MeshNode* aNode = aMesh->FindNode(aText.toInt())) { - myBusy = true; - myId->setText(aText); - myX->SetValue(aNode->X()); - myY->SetValue(aNode->Y()); - myZ->SetValue(aNode->Z()); - myBusy = false; - erasePreview(); // avoid overlapping of a selection and a preview - updateButtons(); - return; - } - } - } - } - } - - reset(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::onDeactivate -// Purpose : SLOT called when dialog must be deativated -//======================================================================= -void SMESHGUI_MoveNodesDlg::onDeactivate() -{ - setEnabled(false); - erasePreview(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::enterEvent -// Purpose : Event filter -//======================================================================= -void SMESHGUI_MoveNodesDlg::enterEvent (QEvent*) -{ - if (!isEnabled()) { - mySMESHGUI->EmitSignalDeactivateDialog(); - - // set selection mode - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); - - redisplayPreview(); - - setEnabled(true); - } -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::closeEvent -// Purpose : -//======================================================================= -void SMESHGUI_MoveNodesDlg::closeEvent (QCloseEvent*) -{ - onClose(); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->Repaint(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::hideEvent -// Purpose : may be caused by ESC key -//======================================================================= -void SMESHGUI_MoveNodesDlg::hideEvent (QHideEvent*) -{ - if (!isMinimized()) - onClose(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::updateButtons -// Purpose : Update buttons state -//======================================================================= -void SMESHGUI_MoveNodesDlg::updateButtons() -{ - bool isEnabled = isValid(false); - myOkBtn->setEnabled(isEnabled); - myApplyBtn->setEnabled(isEnabled); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::erasePreview -// Purpose : Erase preview -//======================================================================= -void SMESHGUI_MoveNodesDlg::erasePreview() -{ - if (myPreviewActor == 0) - return; - - SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI ); - if (aViewWindow) - aViewWindow->RemoveActor(myPreviewActor); - myPreviewActor->Delete(); - myPreviewActor = 0; - if (aViewWindow) - aViewWindow->Repaint(); -} - -//======================================================================= -// name : SMESHGUI_MoveNodesDlg::redisplayPreview -// Purpose : Redisplay preview -//======================================================================= -void SMESHGUI_MoveNodesDlg::redisplayPreview() -{ - if (myBusy) - return; - - if (myPreviewActor != 0) - erasePreview(); - - if (!isValid(false)) - return; - - vtkUnstructuredGrid* aGrid = vtkUnstructuredGrid::New(); - - vtkPoints* aPoints = vtkPoints::New(); - aPoints->SetNumberOfPoints(1); - aPoints->SetPoint(0, myX->GetValue(), myY->GetValue(), myZ->GetValue()); - - // Create cells - - vtkIdList *anIdList = vtkIdList::New(); - anIdList->SetNumberOfIds(1); - - vtkCellArray *aCells = vtkCellArray::New(); - aCells->Allocate(2, 0); - - vtkUnsignedCharArray* aCellTypesArray = vtkUnsignedCharArray::New(); - aCellTypesArray->SetNumberOfComponents(1); - aCellTypesArray->Allocate(1); - - anIdList->SetId(0, 0); - aCells->InsertNextCell(anIdList); - aCellTypesArray->InsertNextValue(VTK_VERTEX); - anIdList->Delete(); - - VTKViewer_CellLocationsArray* aCellLocationsArray = VTKViewer_CellLocationsArray::New(); - aCellLocationsArray->SetNumberOfComponents(1); - aCellLocationsArray->SetNumberOfTuples(1); - - aCells->InitTraversal(); - vtkIdType npts; - aCellLocationsArray->SetValue(0, aCells->GetTraversalLocation(npts)); - - aGrid->SetPoints(aPoints); - aPoints->Delete(); - - aGrid->SetCells(aCellTypesArray,aCellLocationsArray,aCells); - aCellLocationsArray->Delete(); - aCellTypesArray->Delete(); - aCells->Delete(); - - // Create and display actor - vtkDataSetMapper* aMapper = vtkDataSetMapper::New(); - aMapper->SetInput(aGrid); - aGrid->Delete(); - - myPreviewActor = SALOME_Actor::New(); - myPreviewActor->PickableOff(); - myPreviewActor->SetMapper(aMapper); - aMapper->Delete(); - - vtkProperty* aProp = vtkProperty::New(); - aProp->SetRepresentationToWireframe(); - aProp->SetColor(250, 0, 250); - aProp->SetPointSize(5); - myPreviewActor->SetProperty(aProp); - aProp->Delete(); - - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - { - aViewWindow->AddActor(myPreviewActor); - aViewWindow->Repaint(); - } -} - -//================================================================================= -// function : keyPressEvent() -// purpose : -//================================================================================= -void SMESHGUI_MoveNodesDlg::keyPressEvent( QKeyEvent* e ) -{ - QDialog::keyPressEvent( e ); - if ( e->isAccepted() ) - return; - - if ( e->key() == Qt::Key_F1 ) { - e->accept(); - onHelp(); - } -} diff --git a/src/SMESHGUI/SMESHGUI_MoveNodesDlg.h b/src/SMESHGUI/SMESHGUI_MoveNodesDlg.h deleted file mode 100644 index cfd43786f..000000000 --- a/src/SMESHGUI/SMESHGUI_MoveNodesDlg.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_MoveNodesDlg.h -// Author : Nicolas REJNERI, Open CASCADE S.A.S. -// -#ifndef SMESHGUI_MOVENODESDLG_H -#define SMESHGUI_MOVENODESDLG_H - -// SMESH includes -#include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include - -class QLineEdit; -class QPushButton; - -class SMESHGUI; -class SMESH_Actor; -class SMESHGUI_SpinBox; -class SALOME_Actor; -class SVTK_Selector; -class LightApp_SelectionMgr; - -//================================================================================= -// class : SMESHGUI_MoveNodesDlg -// purpose : -//================================================================================= -class SMESHGUI_EXPORT SMESHGUI_MoveNodesDlg : public QDialog -{ - Q_OBJECT - -public: - SMESHGUI_MoveNodesDlg( SMESHGUI* ); - virtual ~SMESHGUI_MoveNodesDlg(); - - void Init(); - -private slots: - void onOk(); - bool onApply(); - void onClose(); - void onHelp(); - - void onDeactivate(); - - void onSelectionDone(); - void redisplayPreview(); - void onTextChange( const QString& ); - -private: - void closeEvent( QCloseEvent* ); - void enterEvent( QEvent* ); - void hideEvent( QHideEvent* ); - void keyPressEvent( QKeyEvent* ); - void erasePreview(); - QWidget* createButtonFrame( QWidget* ); - QWidget* createMainFrame ( QWidget* ); - bool isValid( const bool ); - void reset(); - void updateButtons(); - -private: - QPushButton* myOkBtn; - QPushButton* myApplyBtn; - QPushButton* myCloseBtn; - QPushButton* myHelpBtn; - - QLineEdit* myId; - SMESHGUI_SpinBox* myX; - SMESHGUI_SpinBox* myY; - SMESHGUI_SpinBox* myZ; - - LightApp_SelectionMgr* mySelectionMgr; - SVTK_Selector* mySelector; - SMESHGUI* mySMESHGUI; - - SALOME_Actor* myPreviewActor; - SMESH_Actor* myMeshActor; - bool myBusy; - - QString myHelpFileName; -}; - -#endif // SMESHGUI_MOVENODESDLG_H diff --git a/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx b/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx index 526c3f3e3..a12ca8fbe 100755 --- a/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_MultiEditDlg.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_MultiEditDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. // SMESH includes -// + #include "SMESHGUI_MultiEditDlg.h" #include "SMESHGUI.h" @@ -39,15 +40,17 @@ #include // SALOME GUI includes -#include #include -#include #include +#include +#include +#include #include #include #include #include +#include #include #include @@ -100,8 +103,8 @@ //======================================================================= SMESHGUI_MultiEditDlg ::SMESHGUI_MultiEditDlg(SMESHGUI* theModule, - const int theMode, - const bool the3d2d): + const int theMode, + const bool the3d2d): QDialog(SMESH::GetDesktop(theModule)), mySelector(SMESH::GetViewWindow(theModule)->GetSelector()), mySelectionMgr(SMESH::GetSelectionMgr(theModule)), @@ -192,7 +195,7 @@ QWidget* SMESHGUI_MultiEditDlg::createMainFrame (QWidget* theParent, const bool myToAllChk = new QCheckBox(tr("TO_ALL"), mySelGrp); mySelGrpLayout->addWidget(myToAllChk, mySelGrpLayout->rowCount(), 0, - 1, mySelGrpLayout->columnCount()); + 1, mySelGrpLayout->columnCount()); // Split/Join criterion group myCriterionGrp = new QGroupBox(tr("SPLIT_JOIN_CRITERION"), aMainGrp); @@ -301,7 +304,7 @@ QWidget* SMESHGUI_MultiEditDlg::createButtonFrame (QWidget* theParent) bool SMESHGUI_MultiEditDlg::isValid (const bool /*theMess*/) { return (!myMesh->_is_nil() && - (myListBox->count() > 0 || (myToAllChk->isChecked() && myActor))); + (myListBox->count() > 0 || (myToAllChk->isChecked()/* && myActor*/))); } //======================================================================= @@ -423,63 +426,65 @@ void SMESHGUI_MultiEditDlg::onOk() //======================================================================= // name : SMESHGUI_MultiEditDlg::getIds -// Purpose : Retrive identifiers from list box +// Purpose : Retrive identifiers from list box or the whole object //======================================================================= -SMESH::long_array_var SMESHGUI_MultiEditDlg::getIds() + +SMESH::long_array_var SMESHGUI_MultiEditDlg::getIds(SMESH::SMESH_IDSource_var& obj) { SMESH::long_array_var anIds = new SMESH::long_array; if (myToAllChk->isChecked()) { myIds.Clear(); - SMESH_Actor * anActor = SMESH::FindActorByObject(myMesh); - if (!anActor) - anActor = myActor; - if (anActor != 0) - { - // skl 07.02.2006 - SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); - if( myFilterType == SMESH::TriaFilter || - myFilterType == SMESH::QuadFilter || - myFilterType == SMESH::FaceFilter ) { - SMDS_FaceIteratorPtr it = aMesh->facesIterator(); - while(it->more()) { - const SMDS_MeshFace* f = it->next(); - if(myFilterType == SMESH::FaceFilter) { - myIds.Add(f->GetID()); - } - else if( myFilterType==SMESH::TriaFilter && - ( f->NbNodes()==3 || f->NbNodes()==6 ) ) { - myIds.Add(f->GetID()); - } - else if( myFilterType==SMESH::QuadFilter && - ( f->NbNodes()==4 || f->NbNodes()==8 ) ) { - myIds.Add(f->GetID()); - } - } - } - else if(myFilterType == SMESH::VolumeFilter) { - SMDS_VolumeIteratorPtr it = aMesh->volumesIterator(); - while(it->more()) { - const SMDS_MeshVolume* f = it->next(); - myIds.Add(f->GetID()); - } - } - /* commented by skl 07.02.2006 + obj = SMESH::SMESH_IDSource::_narrow( myMesh ); +// SMESH_Actor * anActor = SMESH::FindActorByObject(myMesh); +// if (!anActor) +// anActor = myActor; +// if (anActor != 0) +// { +// // skl 07.02.2006 +// SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); +// if( myFilterType == SMESH::TriaFilter || +// myFilterType == SMESH::QuadFilter || +// myFilterType == SMESH::FaceFilter ) { +// SMDS_FaceIteratorPtr it = aMesh->facesIterator(); +// while(it->more()) { +// const SMDS_MeshFace* f = it->next(); +// if(myFilterType == SMESH::FaceFilter) { +// myIds.Add(f->GetID()); +// } +// else if( myFilterType==SMESH::TriaFilter && +// ( f->NbNodes()==3 || f->NbNodes()==6 ) ) { +// myIds.Add(f->GetID()); +// } +// else if( myFilterType==SMESH::QuadFilter && +// ( f->NbNodes()==4 || f->NbNodes()==8 ) ) { +// myIds.Add(f->GetID()); +// } +// } +// } +// else if(myFilterType == SMESH::VolumeFilter) { +// SMDS_VolumeIteratorPtr it = aMesh->volumesIterator(); +// while(it->more()) { +// const SMDS_MeshVolume* f = it->next(); +// myIds.Add(f->GetID()); +// } +// } + /* commented by skl 07.02.2006 - to work with quadratic elements TVisualObjPtr aVisualObj = anActor->GetObject(); vtkUnstructuredGrid* aGrid = aVisualObj->GetUnstructuredGrid(); if (aGrid != 0) { for (int i = 0, n = aGrid->GetNumberOfCells(); i < n; i++) { vtkCell* aCell = aGrid->GetCell(i); if (aCell != 0) { - vtkTriangle* aTri = vtkTriangle::SafeDownCast(aCell); - vtkQuad* aQua = vtkQuad::SafeDownCast(aCell); - vtkPolygon* aPG = vtkPolygon::SafeDownCast(aCell); + vtkTriangle* aTri = vtkTriangle::SafeDownCast(aCell); + vtkQuad* aQua = vtkQuad::SafeDownCast(aCell); + vtkPolygon* aPG = vtkPolygon::SafeDownCast(aCell); - vtkCell3D* a3d = vtkCell3D::SafeDownCast(aCell); - vtkConvexPointSet* aPH = vtkConvexPointSet::SafeDownCast(aCell); + vtkCell3D* a3d = vtkCell3D::SafeDownCast(aCell); + vtkConvexPointSet* aPH = vtkConvexPointSet::SafeDownCast(aCell); - if (aTri && myFilterType == SMESHGUI_TriaFilter || + if (aTri && myFilterType == SMESHGUI_TriaFilter || aQua && myFilterType == SMESHGUI_QuadFilter || (aTri || aQua || aPG) && myFilterType == SMESHGUI_FaceFilter || (a3d || aPH) && myFilterType == SMESHGUI_VolumeFilter) { @@ -490,7 +495,7 @@ SMESH::long_array_var SMESHGUI_MultiEditDlg::getIds() } } */ - } + //} } anIds->length(myIds.Extent()); @@ -540,10 +545,10 @@ void SMESHGUI_MultiEditDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -594,10 +599,10 @@ void SMESHGUI_MultiEditDlg::onSelectionDone() if (aNbItems > 0) { QStringList anElements = aListStr.split(" ", QString::SkipEmptyParts); for (QStringList::iterator it = anElements.begin(); it != anElements.end(); ++it) { - QList items = myListBox->findItems(*it, Qt::MatchExactly); - QListWidgetItem* anItem; - foreach(anItem, items) - anItem->setSelected(true); + QList items = myListBox->findItems(*it, Qt::MatchExactly); + QListWidgetItem* anItem; + foreach(anItem, items) + anItem->setSelected(true); } } myMesh = SMESH::GetMeshByIO(anIO); @@ -753,9 +758,8 @@ void SMESHGUI_MultiEditDlg::onAddBtn() for ( ; anIter.More(); anIter.Next()) { SMESH::SMESH_GroupBase_var aGroup = SMESH::IObjectToInterface(anIter.Value()); - if (!aGroup->_is_nil() && (aGroup->GetType() == SMESH::FACE && - entityType() == 0 || aGroup->GetType() == SMESH::VOLUME && - entityType() == 1)) { + if (!aGroup->_is_nil() && ((aGroup->GetType() == SMESH::FACE && entityType() == 0) || + (aGroup->GetType() == SMESH::VOLUME && entityType() == 1))) { if (aGroup->GetMesh()->GetId() == myMesh->GetId()) { SMESH::long_array_var anIds = aGroup->GetListOfID(); for (int i = 0, n = anIds->length(); i < n; i++) { @@ -772,12 +776,13 @@ void SMESHGUI_MultiEditDlg::onAddBtn() bool isGroupOrSubmesh = (mySubmeshChk->isChecked() || myGroupChk->isChecked()); mySubmeshChk->setChecked(false); myGroupChk->setChecked(false); + QStringList items; for(int i = 1; i <= toBeAdded.Extent(); i++) if (myIds.Add(toBeAdded(i))) { - QListWidgetItem* item = new QListWidgetItem(QString("%1").arg(toBeAdded(i))); - myListBox->addItem(item); - item->setSelected(true); + items.append(QString("%1").arg(toBeAdded(i))); } + myListBox->addItems(items); + myListBox->selectAll(); myBusy = false; emit ListContensChanged(); @@ -1034,9 +1039,12 @@ bool SMESHGUI_MultiEditDlg::onApply() myBusy = true; - SMESH::long_array_var anIds = getIds(); + SUIT_OverrideCursor aWaitCursor; + + SMESH::SMESH_IDSource_var obj; + SMESH::long_array_var anIds = getIds(obj); - bool aResult = process(aMeshEditor, anIds.inout()); + bool aResult = process(aMeshEditor, anIds.inout(), obj); if (aResult) { if (myActor) { SALOME_ListIO sel; @@ -1044,6 +1052,7 @@ bool SMESHGUI_MultiEditDlg::onApply() mySelector->ClearIndex(); mySelectionMgr->setSelectedObjects( sel ); SMESH::UpdateView(); + SMESHGUI::Modified(); } myListBox->clear(); @@ -1124,9 +1133,13 @@ SMESHGUI_ChangeOrientationDlg::~SMESHGUI_ChangeOrientationDlg() } bool SMESHGUI_ChangeOrientationDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, - const SMESH::long_array& theIds) + const SMESH::long_array& theIds, + SMESH::SMESH_IDSource_ptr obj) { - return theEditor->Reorient(theIds); + if ( CORBA::is_nil( obj )) + return theEditor->Reorient(theIds); + else + return theEditor->ReorientObject( obj ); } /*! @@ -1152,7 +1165,7 @@ SMESHGUI_UnionOfTrianglesDlg QLabel* aLab = new QLabel (tr("MAXIMUM_ANGLE"), aMaxAngleGrp); myMaxAngleSpin = new SMESHGUI_SpinBox (aMaxAngleGrp); - myMaxAngleSpin->RangeStepAndValidator(0, 180.0, 1.0, 3); + myMaxAngleSpin->RangeStepAndValidator(0, 180.0, 1.0, "angle_precision"); myMaxAngleSpin->SetValue(30.0); aMaxAngleGrpLayout->addWidget(aLab); @@ -1180,7 +1193,7 @@ bool SMESHGUI_UnionOfTrianglesDlg::isValid (const bool theMess) if( theMess ) { QString str( tr( "SMESH_INCORRECT_INPUT" ) ); if ( !msg.isEmpty() ) - str += "\n" + msg; + str += "\n" + msg; SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); } return false; @@ -1190,16 +1203,21 @@ bool SMESHGUI_UnionOfTrianglesDlg::isValid (const bool theMess) } bool SMESHGUI_UnionOfTrianglesDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, - const SMESH::long_array& theIds) + const SMESH::long_array& theIds, + SMESH::SMESH_IDSource_ptr obj) { - SMESH::NumericalFunctor_var aCriterion = getNumericalFunctor(); - double aMaxAngle = myMaxAngleSpin->GetValue() * PI / 180.0; - bool ok = theEditor->TriToQuad(theIds, aCriterion, aMaxAngle); - if( ok ) { + { QStringList aParameters; aParameters << myMaxAngleSpin->text(); - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); } + SMESH::NumericalFunctor_var aCriterion = getNumericalFunctor(); + double aMaxAngle = myMaxAngleSpin->GetValue() * M_PI / 180.0; + bool ok; + if ( CORBA::is_nil( obj )) + ok = theEditor->TriToQuad(theIds, aCriterion, aMaxAngle); + else + ok = theEditor->TriToQuadObject(obj, aCriterion, aMaxAngle); return ok; } @@ -1243,19 +1261,21 @@ void SMESHGUI_CuttingOfQuadsDlg::onClose() } bool SMESHGUI_CuttingOfQuadsDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, - const SMESH::long_array& theIds) + const SMESH::long_array& theIds, + SMESH::SMESH_IDSource_ptr obj) { + bool hasObj = (! CORBA::is_nil( obj )); switch (myGroupChoice->checkedId()) { case 0: // use diagonal 1-3 - return theEditor->SplitQuad(theIds, true); + return hasObj ? theEditor->SplitQuadObject(obj, true) : theEditor->SplitQuad(theIds, true); case 1: // use diagonal 2-4 - return theEditor->SplitQuad(theIds, false); + return hasObj ? theEditor->SplitQuadObject(obj, false) : theEditor->SplitQuad(theIds, false); default: // use numeric functor break; } - SMESH::NumericalFunctor_var aCriterion = getNumericalFunctor(); - return theEditor->QuadToTri(theIds, aCriterion); + SMESH::NumericalFunctor_var aCrit = getNumericalFunctor(); + return hasObj ? theEditor->QuadToTriObject(obj, aCrit) : theEditor->QuadToTri(theIds, aCrit); } void SMESHGUI_CuttingOfQuadsDlg::onCriterionRB() @@ -1295,8 +1315,9 @@ void SMESHGUI_CuttingOfQuadsDlg::displayPreview() erasePreview(); // get Ids of elements - SMESH::long_array_var anElemIds = getIds(); - if (getIds()->length() == 0) + SMESH::SMESH_IDSource_var obj; + SMESH::long_array_var anElemIds = getIds(obj); + if (anElemIds->length() == 0 && obj->_is_nil() ) return; SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); @@ -1449,3 +1470,55 @@ void SMESHGUI_CuttingOfQuadsDlg::displayPreview() aCellTypesArray->Delete(); aCellLocationsArray->Delete(); } + +/*! + * Class : SMESHGUI_CuttingIntoTetraDlg + * Description : Modification of orientation of faces + */ + +SMESHGUI_CuttingIntoTetraDlg::SMESHGUI_CuttingIntoTetraDlg(SMESHGUI* theModule) + : SMESHGUI_MultiEditDlg(theModule, SMESH::VolumeFilter, false) +{ + setWindowTitle(tr("CAPTION")); + myHelpFileName = "split_to_tetra_page.html"; + myEntityType = 1; + + myToAllChk->setChecked( true ); //aplly to the whole mesh by default + + bool hasHexa = true;//myMesh->_is_nil() ? false : myMesh->NbHexas(); + + if ( hasHexa ) + { + myGroupChoice->button(0)->setText( tr("SPLIT_HEX_TO_5_TETRA")); + myGroupChoice->button(1)->setText( tr("SPLIT_HEX_TO_6_TETRA")); + myGroupChoice->button(2)->setText( tr("SPLIT_HEX_TO_24_TETRA")); + + myCriterionGrp->setTitle( tr("SPLIT_METHOD")); + myCriterionGrp->show(); + myComboBoxFunctor->hide(); + myChoiceWidget->show(); + } + setSelectionMode(); + updateButtons(); +} + +SMESHGUI_CuttingIntoTetraDlg::~SMESHGUI_CuttingIntoTetraDlg() +{ +} + +bool SMESHGUI_CuttingIntoTetraDlg::process (SMESH::SMESH_MeshEditor_ptr theEditor, + const SMESH::long_array& theIds, + SMESH::SMESH_IDSource_ptr theObj) +{ + SMESH::SMESH_IDSource_var obj = theObj; + if ( CORBA::is_nil( obj )) + obj = theEditor->MakeIDSource( theIds, myEntityType ? SMESH::VOLUME : SMESH::FACE ); + try { + theEditor->SplitVolumesIntoTetra( obj, myGroupChoice->checkedId()+1 ); + } + catch ( const SALOME::SALOME_Exception& S_ex ) { + SalomeApp_Tools::QtCatchCorbaException( S_ex ); + return false; + } + return true; +} diff --git a/src/SMESHGUI/SMESHGUI_MultiEditDlg.h b/src/SMESHGUI/SMESHGUI_MultiEditDlg.h index 0ada531f9..de9b0b67f 100755 --- a/src/SMESHGUI/SMESHGUI_MultiEditDlg.h +++ b/src/SMESHGUI/SMESHGUI_MultiEditDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_MultiEditDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. // @@ -108,12 +109,13 @@ protected: QWidget* createButtonFrame( QWidget* ); QWidget* createMainFrame( QWidget*, const bool ); virtual bool isValid( const bool ); - SMESH::long_array_var getIds(); + SMESH::long_array_var getIds(SMESH::SMESH_IDSource_var& obj); void updateButtons(); void setSelectionMode(); virtual bool isIdValid( const int ) const; virtual bool process( SMESH::SMESH_MeshEditor_ptr, - const SMESH::long_array& ) = 0; + const SMESH::long_array& , + SMESH::SMESH_IDSource_ptr obj) = 0; int entityType(); protected: @@ -174,7 +176,9 @@ public: virtual ~SMESHGUI_ChangeOrientationDlg(); protected: - virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& ); + virtual bool process( SMESH::SMESH_MeshEditor_ptr, + const SMESH::long_array& , + SMESH::SMESH_IDSource_ptr obj); }; /*! @@ -191,7 +195,9 @@ public: protected: virtual bool isValid( const bool ); - virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& ); + virtual bool process( SMESH::SMESH_MeshEditor_ptr, + const SMESH::long_array&, + SMESH::SMESH_IDSource_ptr obj ); private: SMESHGUI_SpinBox* myMaxAngleSpin; @@ -210,7 +216,9 @@ public: virtual ~SMESHGUI_CuttingOfQuadsDlg(); protected: - virtual bool process( SMESH::SMESH_MeshEditor_ptr, const SMESH::long_array& ); + virtual bool process( SMESH::SMESH_MeshEditor_ptr, + const SMESH::long_array& , + SMESH::SMESH_IDSource_ptr obj); protected slots: virtual void onClose(); @@ -226,4 +234,22 @@ private: QCheckBox* myPreviewChk; }; +/*! + * Class : SMESHGUI_CuttingIntoTetraDlg + * Description : Split all volumes into tetrahedrons + */ +class SMESHGUI_CuttingIntoTetraDlg : public SMESHGUI_MultiEditDlg +{ + Q_OBJECT + +public: + SMESHGUI_CuttingIntoTetraDlg( SMESHGUI* ); + virtual ~SMESHGUI_CuttingIntoTetraDlg(); + +protected: + virtual bool process( SMESH::SMESH_MeshEditor_ptr, + const SMESH::long_array&, + SMESH::SMESH_IDSource_ptr obj ); +}; + #endif // SMESHGUI_MULTIEDITDLG_H diff --git a/src/SMESHGUI/SMESHGUI_NodesDlg.cxx b/src/SMESHGUI/SMESHGUI_NodesDlg.cxx index 38869cedd..3f369e790 100644 --- a/src/SMESHGUI/SMESHGUI_NodesDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_NodesDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_NodesDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -31,6 +32,7 @@ #include "SMESHGUI_Utils.h" #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_GroupUtils.h" #include #include @@ -49,7 +51,10 @@ #include #include +#include + #include +#include #include // SALOME KERNEL includes @@ -69,6 +74,7 @@ #include // Qt includes +#include #include #include #include @@ -87,18 +93,19 @@ namespace SMESH { - void AddNode( SMESH::SMESH_Mesh_ptr theMesh, float x, float y, float z, const QStringList& theParameters ) + long AddNode( SMESH::SMESH_Mesh_ptr theMesh, float x, float y, float z, const QStringList& theParameters ) { + long aNodeId = -1; SUIT_OverrideCursor wc; try { _PTR(SObject) aSobj = SMESH::FindSObject( theMesh ); SMESH::SMESH_MeshEditor_var aMeshEditor = theMesh->GetMeshEditor(); - aMeshEditor->AddNode( x, y, z ); - theMesh->SetParameters( SMESHGUI::JoinObjectParameters(theParameters) ); + theMesh->SetParameters( theParameters.join(":").toLatin1().constData() ); + aNodeId = aMeshEditor->AddNode( x, y, z ); _PTR(Study) aStudy = GetActiveStudyDocument(); CORBA::Long anId = aStudy->StudyId(); if (TVisualObjPtr aVisualObj = SMESH::GetVisualObj( anId, aSobj->GetID().c_str() ) ) { - aVisualObj->Update( true ); + aVisualObj->Update( true ); } } catch ( SALOME::SALOME_Exception& exc ) { @@ -110,6 +117,7 @@ namespace SMESH catch ( ... ) { INFOS( "Unknown exception was cought !!!" ); } + return aNodeId; } class TNodeSimulation @@ -234,7 +242,7 @@ SMESHGUI_NodesDlg::SMESHGUI_NodesDlg( SMESHGUI* theModule ): mySimulation = new SMESH::TNodeSimulation( SMESH::GetViewWindow( mySMESHGUI ) ); QPixmap image0( SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap( "SMESH", - tr( "ICON_DLG_NODE" ) ) ); + tr( "ICON_DLG_NODE" ) ) ); QVBoxLayout* SMESHGUI_NodesDlgLayout = new QVBoxLayout( this ); SMESHGUI_NodesDlgLayout->setSpacing( SPACING ); @@ -270,11 +278,29 @@ SMESHGUI_NodesDlg::SMESHGUI_NodesDlg( SMESHGUI* theModule ): SpinBox_Z = new SMESHGUI_SpinBox( GroupCoordinates ); GroupCoordinatesLayout->addWidget( TextLabel_X ); - GroupCoordinatesLayout->addWidget( SpinBox_X ); + GroupCoordinatesLayout->addWidget( SpinBox_X ); GroupCoordinatesLayout->addWidget( TextLabel_Y); GroupCoordinatesLayout->addWidget( SpinBox_Y ); GroupCoordinatesLayout->addWidget( TextLabel_Z ); GroupCoordinatesLayout->addWidget( SpinBox_Z ); + GroupCoordinatesLayout->setStretch(1, 1); + GroupCoordinatesLayout->setStretch(3, 1); + GroupCoordinatesLayout->setStretch(5, 1); + + /***************************************************************/ + GroupGroups = new QGroupBox( tr( "SMESH_ADD_TO_GROUP" ), this ); + GroupGroups->setCheckable( true ); + QHBoxLayout* GroupGroupsLayout = new QHBoxLayout(GroupGroups); + GroupGroupsLayout->setSpacing(SPACING); + GroupGroupsLayout->setMargin(MARGIN); + + TextLabel_GroupName = new QLabel( tr( "SMESH_GROUP" ), GroupGroups ); + ComboBox_GroupName = new QComboBox( GroupGroups ); + ComboBox_GroupName->setEditable( true ); + ComboBox_GroupName->setInsertPolicy( QComboBox::NoInsert ); + + GroupGroupsLayout->addWidget( TextLabel_GroupName ); + GroupGroupsLayout->addWidget( ComboBox_GroupName, 1 ); /***************************************************************/ GroupButtons = new QGroupBox( this ); @@ -302,6 +328,7 @@ SMESHGUI_NodesDlg::SMESHGUI_NodesDlg( SMESHGUI* theModule ): /***************************************************************/ SMESHGUI_NodesDlgLayout->addWidget( GroupConstructors ); SMESHGUI_NodesDlgLayout->addWidget( GroupCoordinates ); + SMESHGUI_NodesDlgLayout->addWidget( GroupGroups ); SMESHGUI_NodesDlgLayout->addWidget( GroupButtons ); myHelpFileName = "adding_nodes_and_elements_page.html#adding_nodes_anchor"; @@ -329,13 +356,16 @@ void SMESHGUI_NodesDlg::Init() double step = 25.0; /* min, max, step and decimals for spin boxes */ - SpinBox_X->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, DBL_DIGITS_DISPLAY ); - SpinBox_Y->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, DBL_DIGITS_DISPLAY ); - SpinBox_Z->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, DBL_DIGITS_DISPLAY ); + SpinBox_X->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, "length_precision" ); + SpinBox_Y->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, "length_precision" ); + SpinBox_Z->RangeStepAndValidator( COORD_MIN, COORD_MAX, step, "length_precision" ); SpinBox_X->SetValue( 0.0 ); SpinBox_Y->SetValue( 0.0 ); SpinBox_Z->SetValue( 0.0 ); + /* reset "Add to group" control */ + GroupGroups->setChecked( false ); + mySMESHGUI->SetActiveDialogBox( this ); /* signals and slots connections */ @@ -352,6 +382,7 @@ void SMESHGUI_NodesDlg::Init() connect( mySMESHGUI, SIGNAL( SignalDeactivateActiveDialog() ), SLOT( DeactivateActiveDialog() ) ); /* to close dialog if study frame change */ connect( mySMESHGUI, SIGNAL( SignalStudyFrameChanged() ), SLOT( ClickOnCancel() ) ); + connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), SLOT(ClickOnCancel())); // set selection mode SMESH::SetPointRepresentation( true ); @@ -397,7 +428,7 @@ bool SMESHGUI_NodesDlg::ClickOnApply() if ( myMesh->_is_nil() ) { SUIT_MessageBox::warning( this, tr( "SMESH_WRN_WARNING" ), - tr( "MESH_IS_NOT_SELECTED" ) ); + tr( "MESH_IS_NOT_SELECTED" ) ); return false; } @@ -414,16 +445,74 @@ bool SMESHGUI_NodesDlg::ClickOnApply() aParameters << SpinBox_Y->text(); aParameters << SpinBox_Z->text(); + bool addToGroup = GroupGroups->isChecked(); + QString aGroupName; + + SMESH::SMESH_GroupBase_var aGroup; + int idx = 0; + if( addToGroup ) { + aGroupName = ComboBox_GroupName->currentText(); + for ( int i = 1; i < ComboBox_GroupName->count(); i++ ) { + QString aName = ComboBox_GroupName->itemText( i ); + if ( aGroupName == aName && ( i == ComboBox_GroupName->currentIndex() || idx == 0 ) ) + idx = i; + } + if ( idx > 0 && idx < myGroups.count() ) { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( myGroups[idx-1] ); + if ( !aGeomGroup->_is_nil() ) { + int res = SUIT_MessageBox::question( this, tr( "SMESH_WRN_WARNING" ), + tr( "MESH_STANDALONE_GRP_CHOSEN" ).arg( aGroupName ), + tr( "SMESH_BUT_YES" ), tr( "SMESH_BUT_NO" ), 0, 1 ); + if ( res == 1 ) return false; + } + aGroup = myGroups[idx-1]; + } + } + mySimulation->SetVisibility( false ); - SMESH::AddNode( myMesh, x, y, z, aParameters ); + + long aNodeId = SMESH::AddNode( myMesh, x, y, z, aParameters ); + SMESH::SetPointRepresentation( true ); + if ( aNodeId != -1 && addToGroup && !aGroupName.isEmpty() ) { + SMESH::SMESH_Group_var aGroupUsed; + if ( aGroup->_is_nil() ){ + // create new group + aGroupUsed = SMESH::AddGroup( myMesh, SMESH::NODE, aGroupName ); + if ( !aGroupUsed->_is_nil() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroupUsed)); + ComboBox_GroupName->addItem( aGroupName ); + } + } + else { + SMESH::SMESH_GroupOnGeom_var aGeomGroup = SMESH::SMESH_GroupOnGeom::_narrow( aGroup ); + if ( !aGeomGroup->_is_nil() ) { + aGroupUsed = myMesh->ConvertToStandalone( aGeomGroup ); + if ( !aGroupUsed->_is_nil() && idx > 0 ) { + myGroups[idx-1] = SMESH::SMESH_GroupBase::_duplicate(aGroupUsed); + SMESHGUI::GetSMESHGUI()->getApp()->updateObjectBrowser(); + } + } + else + aGroupUsed = SMESH::SMESH_Group::_narrow( aGroup ); + } + + if ( !aGroupUsed->_is_nil() ) { + SMESH::long_array_var anIdList = new SMESH::long_array; + anIdList->length( 1 ); + anIdList[0] = aNodeId; + aGroupUsed->Add( anIdList.inout() ); + } + } + // select myMesh SALOME_ListIO aList; mySelectionMgr->selectedObjects( aList ); if ( aList.Extent() != 1 ) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetCurrentVtkView() ) { - vtkActorCollection *aCollection = aViewWindow->getRenderer()->GetActors(); + VTK::ActorCollectionCopy aCopy(aViewWindow->getRenderer()->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while ( vtkActor *anAct = aCollection->GetNextActor() ) { if ( SMESH_Actor *anActor = dynamic_cast( anAct ) ) { @@ -441,6 +530,11 @@ bool SMESHGUI_NodesDlg::ClickOnApply() } } } + + SMESHGUI::Modified(); + SMESH::UpdateView(); + mySimulation->SetVisibility(false); + return true; } @@ -470,7 +564,7 @@ void SMESHGUI_NodesDlg::ClickOnHelp() LightApp_Application* app = (LightApp_Application*)( SUIT_Session::session()->activeApplication() ); if ( app ) app->onHelpContextModule( mySMESHGUI ? app->moduleName( mySMESHGUI->moduleName() ) : - QString( "" ), myHelpFileName ); + QString( "" ), myHelpFileName ); else { QString platform; #ifdef WIN32 @@ -479,10 +573,10 @@ void SMESHGUI_NodesDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning( this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg( app->resourceMgr()->stringValue( "ExternalBrowser", - platform ) ). - arg( myHelpFileName ) ); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg( app->resourceMgr()->stringValue( "ExternalBrowser", + platform ) ). + arg( myHelpFileName ) ); } } @@ -498,27 +592,48 @@ void SMESHGUI_NodesDlg::SelectionIntoArgument() mySimulation->SetVisibility( false ); SMESH::SetPointRepresentation( true ); + QString aCurrentEntry = myEntry; + const SALOME_ListIO& aList = mySelector->StoredIObjects(); if ( aList.Extent() == 1 ) { Handle(SALOME_InteractiveObject) anIO = aList.First(); if ( anIO->hasEntry() ) { + myEntry = anIO->getEntry(); myMesh = SMESH::GetMeshByIO( anIO ); if ( myMesh->_is_nil() ) return; QString aText; if ( SMESH::GetNameOfSelectedNodes( mySelector, anIO, aText ) == 1 ) { - if ( SMESH_Actor* anActor = SMESH::FindActorByObject( myMesh.in() ) ) { - if ( SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh() ) { - if ( const SMDS_MeshNode* aNode = aMesh->FindNode( aText.toInt() ) ) { - SpinBox_X->SetValue( aNode->X() ); - SpinBox_Y->SetValue( aNode->Y() ); - SpinBox_Z->SetValue( aNode->Z() ); + if ( SMESH_Actor* anActor = SMESH::FindActorByObject( myMesh.in() ) ) { + if ( SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh() ) { + if ( const SMDS_MeshNode* aNode = aMesh->FindNode( aText.toInt() ) ) { + SpinBox_X->SetValue( aNode->X() ); + SpinBox_Y->SetValue( aNode->Y() ); + SpinBox_Z->SetValue( aNode->Z() ); } - } - } + } + } } mySimulation->SetPosition( SpinBox_X->GetValue(), - SpinBox_Y->GetValue(), - SpinBox_Z->GetValue() ); + SpinBox_Y->GetValue(), + SpinBox_Z->GetValue() ); + } + } + + // process groups + if ( !myMesh->_is_nil() && myEntry != aCurrentEntry ) { + myGroups.clear(); + ComboBox_GroupName->clear(); + ComboBox_GroupName->addItem( QString() ); + SMESH::ListOfGroups aListOfGroups = *myMesh->GetGroups(); + for( int i = 0, n = aListOfGroups.length(); i < n; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = aListOfGroups[i]; + if ( !aGroup->_is_nil() && aGroup->GetType() == SMESH::NODE ) { + QString aGroupName( aGroup->GetName() ); + if ( !aGroupName.isEmpty() ) { + myGroups.append(SMESH::SMESH_GroupBase::_duplicate(aGroup)); + ComboBox_GroupName->addItem( aGroupName ); + } + } } } } @@ -621,5 +736,10 @@ bool SMESHGUI_NodesDlg::isValid() SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); return false; } + + if( GroupGroups->isChecked() && ComboBox_GroupName->currentText().isEmpty() ) { + SUIT_MessageBox::warning( this, tr( "SMESH_WRN_WARNING" ), tr( "GROUP_NAME_IS_EMPTY" ) ); + return false; + } return true; } diff --git a/src/SMESHGUI/SMESHGUI_NodesDlg.h b/src/SMESHGUI/SMESHGUI_NodesDlg.h index 8fc28eacc..de2058f5a 100644 --- a/src/SMESHGUI/SMESHGUI_NodesDlg.h +++ b/src/SMESHGUI/SMESHGUI_NodesDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_NodesDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -36,6 +37,7 @@ #include #include CORBA_SERVER_HEADER(SMESH_Mesh) +class QComboBox; class QGroupBox; class QLabel; class QPushButton; @@ -64,12 +66,16 @@ public: ~SMESHGUI_NodesDlg(); private: + typedef QList GrpList; + LightApp_SelectionMgr* mySelectionMgr; SVTK_Selector* mySelector; SMESHGUI* mySMESHGUI; SMESH::SMESH_Mesh_var myMesh; SMESH::TNodeSimulation* mySimulation; + QString myEntry; + GrpList myGroups; void Init(); void enterEvent( QEvent* ); @@ -78,7 +84,7 @@ private: void keyPressEvent( QKeyEvent* ); bool isValid(); - + QGroupBox* GroupConstructors; QRadioButton* Constructor1; QGroupBox* GroupCoordinates; @@ -90,6 +96,10 @@ private: QLabel* TextLabel_Y; QLabel* TextLabel_Z; + QGroupBox* GroupGroups; + QLabel* TextLabel_GroupName; + QComboBox* ComboBox_GroupName; + QGroupBox* GroupButtons; QPushButton* buttonApply; QPushButton* buttonOk; diff --git a/src/SMESHGUI/SMESHGUI_Operation.cxx b/src/SMESHGUI/SMESHGUI_Operation.cxx index 40bb88e65..5646c7202 100755 --- a/src/SMESHGUI/SMESHGUI_Operation.cxx +++ b/src/SMESHGUI/SMESHGUI_Operation.cxx @@ -1,29 +1,27 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMDS : implementaion of Salome mesh data structure -// File : SMESHGUI_Operation.cxx -// Author : Sergey LITONIN, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_Operation.cxx +// Author : Sergey LITONIN, Open CASCADE S.A.S. + #include "SMESHGUI_Operation.h" #include "SMESHGUI.h" @@ -51,7 +49,8 @@ // Purpose : Constructor //======================================================================= SMESHGUI_Operation::SMESHGUI_Operation() -: LightApp_Operation() +: LightApp_Operation(), + myIsApplyAndClose( false ) { myHelpFileName = ""; } @@ -86,19 +85,19 @@ void SMESHGUI_Operation::startOperation() disconnect( dlg(), SIGNAL( dlgCancel() ), this, SLOT( onCancel() ) ); disconnect( dlg(), SIGNAL( dlgClose() ), this, SLOT( onCancel() ) ); disconnect( dlg(), SIGNAL( dlgHelp() ), this, SLOT( onHelp() ) ); - + if( dlg()->testButtonFlags( QtxDialog::OK ) ) connect( dlg(), SIGNAL( dlgOk() ), this, SLOT( onOk() ) ); - + if( dlg()->testButtonFlags( QtxDialog::Apply ) ) connect( dlg(), SIGNAL( dlgApply() ), this, SLOT( onApply() ) ); - + if( dlg()->testButtonFlags( QtxDialog::Cancel ) ) connect( dlg(), SIGNAL( dlgCancel() ), this, SLOT( onCancel() ) ); if( dlg()->testButtonFlags( QtxDialog::Help ) ) connect( dlg(), SIGNAL( dlgHelp() ), this, SLOT( onHelp() ) ); - + //if( dlg()->testButtonFlags( QtxDialog::Close ) ) //if dialog hasn't close, cancel, no and etc buttons, dlgClose will be emitted when dialog is closed not by OK connect( dlg(), SIGNAL( dlgClose() ), this, SLOT( onCancel() ) ); @@ -120,18 +119,18 @@ bool SMESHGUI_Operation::isReadyToStart() const else if ( getSMESHGUI() == 0 ) { SUIT_MessageBox::warning( desktop(), tr( "SMESH_WRN_WARNING" ), - tr( "NO_MODULE" ) ); + tr( "NO_MODULE" ) ); return false; } else if ( isStudyLocked() ) return false; - + return true; } //======================================================================= // name : setDialogActive -// Purpose : +// Purpose : //======================================================================= void SMESHGUI_Operation::setDialogActive( const bool active ) { @@ -159,8 +158,10 @@ _PTR(Study) SMESHGUI_Operation::studyDS() const //======================================================================= void SMESHGUI_Operation::onOk() { + setIsApplyAndClose( true ); if( onApply() ) commit(); + setIsApplyAndClose( false ); //else // abort(); } @@ -190,7 +191,7 @@ void SMESHGUI_Operation::onCancel() void SMESHGUI_Operation::onHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(getSMESHGUI() ? app->moduleName(getSMESHGUI()->moduleName()) : QString(""), myHelpFileName); else { QString platform; @@ -200,10 +201,10 @@ void SMESHGUI_Operation::onHelp() platform = "application"; #endif SUIT_MessageBox::warning( desktop(), tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName) ); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName) ); } } @@ -215,6 +216,26 @@ void SMESHGUI_Operation::initDialog() { } +//================================================================ +// name : setIsApplyAndClose +// Purpose : Set value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +void SMESHGUI_Operation::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +//================================================================ +// name : isApplyAndClose +// Purpose : Get value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +bool SMESHGUI_Operation::isApplyAndClose() const +{ + return myIsApplyAndClose; +} + /*! * \brief Verifies whether study of operation is locked * \param theMess - specifies whether message box must be shown if study is locked @@ -231,11 +252,11 @@ bool SMESHGUI_Operation::isStudyLocked( const bool theMess ) const { if ( theMess ) SUIT_MessageBox::warning( SMESHGUI::desktop(), tr( "WRN_WARNING" ), - tr( "WRN_STUDY_LOCKED" ) ); + tr( "WRN_STUDY_LOCKED" ) ); return true; } } - + return false; } @@ -258,9 +279,9 @@ bool SMESHGUI_Operation::isValid( SUIT_Operation* theOtherOp ) const // to do add other operations here } - return theOtherOp && theOtherOp->inherits( "SMESHGUI_Operation" ) && - ( !anOps.contains( theOtherOp->metaObject()->className() ) || - anOps.contains( metaObject()->className() ) ); + return ( theOtherOp && + ( ( theOtherOp->inherits("SMESHGUI_Operation") && ( !anOps.contains(theOtherOp->metaObject()->className() ) || anOps.contains(metaObject()->className()) ) ) || + ( theOtherOp->inherits("LightApp_ShowHideOp") ) ) ); return true; } diff --git a/src/SMESHGUI/SMESHGUI_Operation.h b/src/SMESHGUI/SMESHGUI_Operation.h index b3b119e71..2bbad5c1f 100755 --- a/src/SMESHGUI/SMESHGUI_Operation.h +++ b/src/SMESHGUI/SMESHGUI_Operation.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMDS : implementaion of Salome mesh data structure // File : SMESHGUI_Operation.h // Author : Sergey LITONIN, Open CASCADE S.A.S. @@ -57,6 +58,9 @@ protected: virtual void startOperation(); virtual bool isReadyToStart() const; + virtual void setIsApplyAndClose( const bool theFlag ); + virtual bool isApplyAndClose() const; + //! Set according dialog active or inactive virtual void setDialogActive( const bool ); @@ -68,6 +72,7 @@ protected: virtual bool isValid( SUIT_Operation* ) const; QString myHelpFileName; + bool myIsApplyAndClose; protected slots: virtual void onOk(); diff --git a/src/SMESHGUI/SMESHGUI_PatternUtils.cxx b/src/SMESHGUI/SMESHGUI_PatternUtils.cxx index d6ea44b2e..349314182 100644 --- a/src/SMESHGUI/SMESHGUI_PatternUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_PatternUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_PatternUtils.cxx // Author : Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_PatternUtils.h b/src/SMESHGUI/SMESHGUI_PatternUtils.h index cd5fd7040..a455061c6 100644 --- a/src/SMESHGUI/SMESHGUI_PatternUtils.h +++ b/src/SMESHGUI/SMESHGUI_PatternUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_PatternUtils.h // Author : Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_PatternWidget.cxx b/src/SMESHGUI/SMESHGUI_PatternWidget.cxx index c8ab77ac8..f3483ba25 100644 --- a/src/SMESHGUI/SMESHGUI_PatternWidget.cxx +++ b/src/SMESHGUI/SMESHGUI_PatternWidget.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_PatternWidget.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -95,9 +96,9 @@ void SMESHGUI_PatternWidget::paintEvent( QPaintEvent* ) QPoint aQPnt = mapCoords( aPoint.x, aPoint.y ); painter.drawPie( aQPnt.x() - Radius, aQPnt.y() - Radius, - Radius * 2, Radius * 2, 0, 360 * 16 ); + Radius * 2, Radius * 2, 0, 360 * 16 ); painter.drawText( aQPnt.x() + Shift, aQPnt.y() - Shift, - QString::number( i+1 ) ); + QString::number( i+1 ) ); } // Draw lines diff --git a/src/SMESHGUI/SMESHGUI_PatternWidget.h b/src/SMESHGUI/SMESHGUI_PatternWidget.h index e4a6e41d3..82ffdeea9 100644 --- a/src/SMESHGUI/SMESHGUI_PatternWidget.h +++ b/src/SMESHGUI/SMESHGUI_PatternWidget.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_PatternWidget.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -53,8 +54,8 @@ public: ~SMESHGUI_PatternWidget(); void SetPoints( const PointVector&, - const QVector&, - const ConnectivityVector& ); + const QVector&, + const ConnectivityVector& ); private: PointVector myPoints; diff --git a/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.cxx b/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.cxx index 3b16aaf0b..c7b6d5e7d 100644 --- a/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Preferences_ColorDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -27,12 +28,18 @@ #include "SMESHGUI_Preferences_ColorDlg.h" #include "SMESHGUI.h" +#include "SMESHGUI_SpinBox.h" #include "SMESHGUI_Utils.h" // SALOME GUI includes #include +#include +#include +#include #include -#include +#include +#include +#include // Qt includes #include @@ -41,7 +48,8 @@ #include #include #include -#include +#include +#include #define SPACING 6 #define MARGIN 11 @@ -58,7 +66,7 @@ SMESHGUI_Preferences_ColorDlg::SMESHGUI_Preferences_ColorDlg( SMESHGUI* theModul mySMESHGUI( theModule ) { setModal( true ); - setWindowTitle( tr( "Preferences - Set Color" ) ); + setWindowTitle( tr( "DIALOG_TITLE" ) ); setSizeGripEnabled( true ); // ------------------------------- @@ -67,65 +75,148 @@ SMESHGUI_Preferences_ColorDlg::SMESHGUI_Preferences_ColorDlg( SMESHGUI* theModul topLayout->setMargin( MARGIN ); // ------------------------------- - QGroupBox* ButtonGroup1 = new QGroupBox( tr( "Elements" ), this ); + QGroupBox* ButtonGroup1 = new QGroupBox( tr( "GRP_ELEMENTS" ), this ); QGridLayout* ButtonGroup1Layout = new QGridLayout( ButtonGroup1 ); ButtonGroup1Layout->setSpacing( SPACING ); ButtonGroup1Layout->setMargin( MARGIN ); - QLabel* TextLabel_Fill = new QLabel( tr( "Fill" ), ButtonGroup1 ); - btnFillColor = new QtxColorButton( ButtonGroup1 ); + QLabel* TextLabel_Fill = new QLabel( tr( "SURFACE_COLOR_LBL" ), ButtonGroup1 ); - QLabel* TextLabel_BackFace = new QLabel( tr( "Back Face" ), ButtonGroup1 ); - btnBackFaceColor = new QtxColorButton( ButtonGroup1 ); + toolSurfColor = new QtxBiColorTool(ButtonGroup1); + toolSurfColor->setText( tr( "BACKSURFACE_COLOR_LBL" )); - QLabel* TextLabel_Outine = new QLabel( tr( "Outline" ), ButtonGroup1 ); + QLabel* TextLabel_Outline = new QLabel( tr( "OUTLINE_COLOR_LBL" ), ButtonGroup1 ); btnOutlineColor = new QtxColorButton( ButtonGroup1 ); - QLabel* TextLabel_Width = new QLabel( tr( "Width" ), ButtonGroup1 ); - SpinBox_Width = new QSpinBox( ButtonGroup1 ); - SpinBox_Width->setRange( 0, 5 ); + QLabel* TextLabel_Wireframe = new QLabel( tr( "WIREFRAME_COLOR_LBL" ), ButtonGroup1 ); + btnWireframeColor = new QtxColorButton( ButtonGroup1 ); + + QLabel* TextLabel_0DElements_Color = new QLabel( tr( "0D_ELEMENTS_COLOR_LBL" ), ButtonGroup1 ); + btn0DElementsColor = new QtxColorButton( ButtonGroup1 ); + + QLabel* TextLabel_0DElements_Size = new QLabel( tr( "0D_ELEMENTS_SIZE_LBL" ), ButtonGroup1 ); + SpinBox_0DElements_Size = new SalomeApp_IntSpinBox( ButtonGroup1 ); + SpinBox_0DElements_Size->setAcceptNames( false ); // No Notebook variables allowed + SpinBox_0DElements_Size->setRange( 1, 10 ); + SpinBox_0DElements_Size->setSingleStep( 1 ); + SpinBox_0DElements_Size->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + SpinBox_0DElements_Size->setButtonSymbols( QSpinBox::PlusMinus ); + + QLabel* TextLabel_BallElem_Color = new QLabel( tr( "0D_ELEMENTS_COLOR_LBL" ), ButtonGroup1 ); + btnBallElemColor = new QtxColorButton( ButtonGroup1 ); + + QLabel* TextLabel_BallElem_Size = new QLabel( tr( "BALLELEM_SIZE_LBL" ), ButtonGroup1 ); + SpinBox_BallElem_Size = new SalomeApp_IntSpinBox( ButtonGroup1 ); + SpinBox_BallElem_Size->setAcceptNames( false ); // No Notebook variables allowed + SpinBox_BallElem_Size->setRange( 1, 10 ); + SpinBox_BallElem_Size->setSingleStep( 1 ); + SpinBox_BallElem_Size->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + SpinBox_BallElem_Size->setButtonSymbols( QSpinBox::PlusMinus ); + + QLabel* TextLabel_Width = new QLabel( tr( "LINE_WIDTH_LBL" ), ButtonGroup1 ); + SpinBox_Width = new SalomeApp_IntSpinBox( ButtonGroup1 ); + SpinBox_Width->setAcceptNames( false ); // No Notebook variables allowed + SpinBox_Width->setRange( 1, 5 ); SpinBox_Width->setSingleStep( 1 ); SpinBox_Width->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); SpinBox_Width->setButtonSymbols( QSpinBox::PlusMinus ); - QLabel* TextLabel_ShrinkCoeff = new QLabel( tr( "Shrink coef." ), ButtonGroup1 ); - SpinBox_Shrink = new QtxIntSpinBox( ButtonGroup1 ); + QLabel* TextLabel_ShrinkCoeff = new QLabel( tr( "SHRINK_COEF_LBL" ), ButtonGroup1 ); + SpinBox_Shrink = new SalomeApp_IntSpinBox( ButtonGroup1 ); + SpinBox_Shrink->setAcceptNames( false ); // No Notebook variables allowed SpinBox_Shrink->setRange( 20, 100 ); SpinBox_Shrink->setSingleStep( 1 ); SpinBox_Shrink->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); SpinBox_Shrink->setButtonSymbols( QSpinBox::PlusMinus ); - ButtonGroup1Layout->addWidget( TextLabel_Fill, 0, 0 ); - ButtonGroup1Layout->addWidget( btnFillColor, 0, 1 ); - ButtonGroup1Layout->addWidget( TextLabel_BackFace, 0, 2 ); - ButtonGroup1Layout->addWidget( btnBackFaceColor, 0, 3 ); - ButtonGroup1Layout->addWidget( TextLabel_Outine, 1, 0 ); - ButtonGroup1Layout->addWidget( btnOutlineColor, 1, 1 ); - ButtonGroup1Layout->addWidget( TextLabel_Width, 1, 2 ); - ButtonGroup1Layout->addWidget( SpinBox_Width, 1, 3 ); - ButtonGroup1Layout->addWidget( TextLabel_ShrinkCoeff, 2, 0 ); - ButtonGroup1Layout->addWidget( SpinBox_Shrink, 2, 1, 1, 3 ); + ButtonGroup1Layout->addWidget( TextLabel_Fill, 0, 0 ); + ButtonGroup1Layout->addWidget( toolSurfColor, 0, 1, 1, 3 ); + + ButtonGroup1Layout->addWidget( TextLabel_Outline, 1, 0 ); + ButtonGroup1Layout->addWidget( btnOutlineColor, 1, 1 ); + ButtonGroup1Layout->addWidget( TextLabel_Wireframe, 1, 2 ); + ButtonGroup1Layout->addWidget( btnWireframeColor, 1, 3 ); + + ButtonGroup1Layout->addWidget( TextLabel_0DElements_Color, 2, 0 ); + ButtonGroup1Layout->addWidget( btn0DElementsColor, 2, 1 ); + + ButtonGroup1Layout->addWidget( TextLabel_0DElements_Size, 2, 2 ); + ButtonGroup1Layout->addWidget( SpinBox_0DElements_Size, 2, 3 ); + + ButtonGroup1Layout->addWidget( TextLabel_BallElem_Color, 2, 0 ); + ButtonGroup1Layout->addWidget( btnBallElemColor, 2, 1 ); + + ButtonGroup1Layout->addWidget( TextLabel_BallElem_Size, 2, 2 ); + ButtonGroup1Layout->addWidget( SpinBox_BallElem_Size, 2, 3 ); + + ButtonGroup1Layout->addWidget( TextLabel_Width, 3, 0 ); + ButtonGroup1Layout->addWidget( SpinBox_Width, 3, 1 ); + ButtonGroup1Layout->addWidget( TextLabel_ShrinkCoeff, 3, 2 ); + ButtonGroup1Layout->addWidget( SpinBox_Shrink, 3, 3 ); // ------------------------------- - QGroupBox* ButtonGroup2 = new QGroupBox( tr( "Nodes" ), this ); - QHBoxLayout* ButtonGroup2Layout = new QHBoxLayout( ButtonGroup2 ); + QGroupBox* ButtonGroup2 = new QGroupBox( tr( "GRP_NODES" ), this ); + QGridLayout* ButtonGroup2Layout = new QGridLayout( ButtonGroup2 ); ButtonGroup2Layout->setSpacing( SPACING ); ButtonGroup2Layout->setMargin( MARGIN ); - QLabel* TextLabel_Nodes_Color = new QLabel( tr( "Color" ), ButtonGroup2 ); + QLabel* TextLabel_Nodes_Color = new QLabel( tr( "NODES_COLOR_LBL" ), ButtonGroup2 ); btnNodeColor = new QtxColorButton( ButtonGroup2 ); - QLabel* TextLabel_Nodes_Size = new QLabel( tr( "Size" ), ButtonGroup2 ); - SpinBox_Nodes_Size = new QSpinBox( ButtonGroup2 ); - SpinBox_Nodes_Size->setRange( 0, 5 ); - SpinBox_Nodes_Size->setSingleStep( 1 ); - SpinBox_Nodes_Size->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); - SpinBox_Nodes_Size->setButtonSymbols( QSpinBox::PlusMinus ); + QGroupBox* MarkerGroup = new QGroupBox( tr( "NODES_MARKER_LBL" ), ButtonGroup2 ); + QVBoxLayout* MarkerGroupLayout = new QVBoxLayout( MarkerGroup ); + MarkerGroupLayout->setSpacing( 0 ); + MarkerGroupLayout->setMargin( 0 ); + + MarkerWidget = new VTKViewer_MarkerWidget( MarkerGroup ); + + MarkerGroupLayout->addWidget( MarkerWidget ); + + ButtonGroup2Layout->addWidget( TextLabel_Nodes_Color, 0, 0 ); + ButtonGroup2Layout->addWidget( btnNodeColor, 0, 1 ); + ButtonGroup2Layout->addWidget( MarkerGroup, 1, 0, 1, 3 ); + ButtonGroup2Layout->setColumnStretch( 2, 1 ); + + // ------------------------------- + QGroupBox* ButtonGroup3 = new QGroupBox( tr( "GRP_ORIENTATION" ), this ); + QGridLayout* ButtonGroup3Layout = new QGridLayout( ButtonGroup3 ); + ButtonGroup3Layout->setSpacing( SPACING ); + ButtonGroup3Layout->setMargin( MARGIN ); + + QLabel* TextLabel_Orientation_Color = new QLabel( tr( "ORIENTATION_COLOR_LBL" ), ButtonGroup3 ); + btnOrientationColor = new QtxColorButton( ButtonGroup3 ); + + QLabel* TextLabel_Orientation_Scale = new QLabel( tr( "ORIENTATION_SCALE_LBL" ), ButtonGroup3 ); + SpinBox_Orientation_Scale = new SMESHGUI_SpinBox( ButtonGroup3 ); + SpinBox_Orientation_Scale->setAcceptNames( false ); // No Notebook variables allowed + SpinBox_Orientation_Scale->RangeStepAndValidator( .05, .5, .05, "parametric_precision" ); + SpinBox_Orientation_Scale->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + SpinBox_Orientation_Scale->setButtonSymbols( QSpinBox::PlusMinus ); + + CheckBox_Orientation_3DVectors = new QCheckBox( tr( "3D_VECTORS_LBL" ), ButtonGroup3 ); + + ButtonGroup3Layout->addWidget( TextLabel_Orientation_Color, 0, 0 ); + ButtonGroup3Layout->addWidget( btnOrientationColor, 0, 1 ); + ButtonGroup3Layout->addWidget( TextLabel_Orientation_Scale, 0, 2 ); + ButtonGroup3Layout->addWidget( SpinBox_Orientation_Scale, 0, 3 ); + ButtonGroup3Layout->addWidget( CheckBox_Orientation_3DVectors, 1, 0, 1, 4 ); - ButtonGroup2Layout->addWidget( TextLabel_Nodes_Color ); - ButtonGroup2Layout->addWidget( btnNodeColor ); - ButtonGroup2Layout->addWidget( TextLabel_Nodes_Size ); - ButtonGroup2Layout->addWidget( SpinBox_Nodes_Size ); + // ------------------------------- + QGroupBox* ButtonGroup4 = new QGroupBox( tr( "GRP_SELECTION" ), this ); + QGridLayout* ButtonGroup4Layout = new QGridLayout( ButtonGroup4 ); + ButtonGroup3Layout->setSpacing( SPACING ); + ButtonGroup3Layout->setMargin( MARGIN ); + + QLabel* TextLabel_Selection_Color = new QLabel( tr( "SELECTION_COLOR_LBL" ), ButtonGroup4 ); + btnSelectionColor = new QtxColorButton( ButtonGroup4 ); + + QLabel* TextLabel_Preselection_Color = new QLabel( tr( "PRESELECTION_COLOR_LBL" ), ButtonGroup4 ); + btnPreselectionColor = new QtxColorButton( ButtonGroup4 ); + + ButtonGroup4Layout->addWidget( TextLabel_Selection_Color, 0, 0 ); + ButtonGroup4Layout->addWidget( btnSelectionColor, 0, 1 ); + ButtonGroup4Layout->addWidget( TextLabel_Preselection_Color, 0, 2 ); + ButtonGroup4Layout->addWidget( btnPreselectionColor, 0, 3 ); // ------------------------------- QGroupBox* GroupButtons = new QGroupBox( this ); @@ -133,35 +224,46 @@ SMESHGUI_Preferences_ColorDlg::SMESHGUI_Preferences_ColorDlg( SMESHGUI* theModul GroupButtonsLayout->setSpacing( SPACING ); GroupButtonsLayout->setMargin( MARGIN ); - QPushButton* buttonOk = new QPushButton( tr( "&OK" ), GroupButtons ); + QPushButton* buttonOk = new QPushButton( tr( "SMESH_BUT_OK" ), GroupButtons ); buttonOk->setAutoDefault( true ); buttonOk->setDefault( true ); - QPushButton* buttonCancel = new QPushButton( tr( "&Cancel" ), GroupButtons ); + QPushButton* buttonCancel = new QPushButton( tr( "SMESH_BUT_CANCEL" ), GroupButtons ); buttonCancel->setAutoDefault( true ); + QPushButton* buttonHelp = new QPushButton( tr( "SMESH_BUT_HELP" ), GroupButtons ); + buttonHelp->setAutoDefault( true ); + GroupButtonsLayout->addWidget( buttonOk ); GroupButtonsLayout->addSpacing( 10 ); GroupButtonsLayout->addStretch(); GroupButtonsLayout->addWidget( buttonCancel ); + GroupButtonsLayout->addWidget( buttonHelp ); // ------------------------------- topLayout->addWidget( ButtonGroup1 ); topLayout->addWidget( ButtonGroup2 ); + topLayout->addWidget( ButtonGroup3 ); + // rnv: Selection and preselection colors are defined only in the Preferences + // topLayout->addWidget( ButtonGroup4 ); + ButtonGroup4->hide(); topLayout->addWidget( GroupButtons ); // ------------------------------- mySMESHGUI->SetActiveDialogBox( this ); + myHelpFileName = "colors_size_page.html"; + /* signals and slots connections */ connect( buttonOk, SIGNAL( clicked() ), this, SLOT( ClickOnOk() ) ); connect( buttonCancel, SIGNAL( clicked() ), this, SLOT( ClickOnCancel() ) ); + connect( buttonHelp, SIGNAL( clicked() ), this, SLOT( ClickOnHelp() ) ); connect( mySMESHGUI, SIGNAL ( SignalDeactivateActiveDialog() ), - this, SLOT( DeactivateActiveDialog() ) ); + this, SLOT( DeactivateActiveDialog() ) ); /* to close dialog if study change */ connect( mySMESHGUI, SIGNAL ( SignalCloseAllDialogs() ), - this, SLOT( ClickOnCancel() ) ); + this, SLOT( ClickOnCancel() ) ); } //================================================================================= @@ -192,6 +294,30 @@ void SMESHGUI_Preferences_ColorDlg::ClickOnCancel() reject(); } +//================================================================================= +// function : ClickOnHelp() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::ClickOnHelp() +{ + LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + else { + QString platform; +#ifdef WIN32 + platform = "winapplication"; +#else + platform = "application"; +#endif + SUIT_MessageBox::warning(this, tr("WRN_WARNING"), + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); + } +} + //================================================================================= // function : DeactivateActiveDialog() // purpose : @@ -226,10 +352,15 @@ void SMESHGUI_Preferences_ColorDlg::ActivateThisDialog() void SMESHGUI_Preferences_ColorDlg::SetColor( int type, const QColor& color ) { switch ( type ) { - case 1 : btnFillColor->setColor( color ); break; // fill - case 2 : btnOutlineColor->setColor( color ); break; // outline - case 3 : btnNodeColor->setColor( color ); break; // node - case 4 : btnBackFaceColor->setColor( color ); break; // back face + case 1 : toolSurfColor->setMainColor( color ); break; // fill + case 2 : btnWireframeColor->setColor( color ); break; // wireframe + case 3 : btnNodeColor->setColor( color ); break; // node + case 4 : btnOutlineColor->setColor( color ); break; // outline + case 5 : btn0DElementsColor->setColor( color ); break; // 0d elements + case 6 : btnBallElemColor->setColor( color ); break; // ball elements + case 7 : btnOrientationColor->setColor( color ); break; // orientation of faces + case 8 : btnSelectionColor->setColor( color ); break; // selection color + case 9 : btnPreselectionColor->setColor( color ); break; // pre-selection color default: break; } } @@ -242,10 +373,16 @@ QColor SMESHGUI_Preferences_ColorDlg::GetColor( int type ) { QColor color; switch ( type ) { - case 1 : color = btnFillColor->color(); break; // fill - case 2 : color = btnOutlineColor->color(); break; // outline - case 3 : color = btnNodeColor->color(); break; // node - case 4 : color = btnBackFaceColor->color(); break; // back face + case 1 : color = toolSurfColor->mainColor(); break; // fill + case 2 : color = btnWireframeColor->color(); break; // outline + case 3 : color = btnNodeColor->color(); break; // node + case 4 : color = btnOutlineColor->color(); break; // node + case 5 : color = btn0DElementsColor->color(); break; // 0d elements + case 6 : color = btnBallElemColor->color(); break; // 0d elements + case 7 : color = btnOrientationColor->color(); break; // orientation of faces + case 8 : color = btnSelectionColor->color(); break; // selection color + case 9 : color = btnPreselectionColor->color(); break; // pre-selection color + default: break; } return color; @@ -258,9 +395,10 @@ QColor SMESHGUI_Preferences_ColorDlg::GetColor( int type ) void SMESHGUI_Preferences_ColorDlg::SetIntValue( int type, int value ) { switch ( type ) { - case 1 : SpinBox_Width->setValue( value ); break; // width - case 2 : SpinBox_Nodes_Size->setValue( value ); break; // nodes size = value; break; - case 3 : SpinBox_Shrink->setValue( value ); break; // shrink coeff + case 1 : SpinBox_Width->setValue( value ); break; // width + case 2 : SpinBox_Shrink->setValue( value ); break; // shrink coeff + case 3 : SpinBox_0DElements_Size->setValue( value ); break; // 0d elements + case 4 : SpinBox_BallElem_Size->setValue( value ); break; // 0d elements default: break; } } @@ -273,10 +411,160 @@ int SMESHGUI_Preferences_ColorDlg::GetIntValue( int type ) { int res = 0; switch ( type ) { - case 1 : res = SpinBox_Width->value(); break; // width - case 2 : res = SpinBox_Nodes_Size->value(); break; // nodes size - case 3 : res = SpinBox_Shrink->value(); break; // shrink coeff + case 1 : res = SpinBox_Width->value(); break; // width + case 2 : res = SpinBox_Shrink->value(); break; // shrink coeff + case 3 : res = SpinBox_0DElements_Size->value(); break; // 0d elements + case 4 : res = SpinBox_BallElem_Size->value(); break; // 0d elements + default: break; + } + return res; +} + +//================================================================================= +// function : SetDoubleValue() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::SetDoubleValue( int type, double value ) +{ + switch ( type ) { + case 1 : SpinBox_Orientation_Scale->setValue( value ); break; // orientation scale + default: break; + } +} + +//================================================================================= +// function : GetDoubleValue() +// purpose : +//================================================================================= +double SMESHGUI_Preferences_ColorDlg::GetDoubleValue( int type ) +{ + double res = 0; + switch ( type ) { + case 1 : res = SpinBox_Orientation_Scale->value(); break; // orientation scale + default: break; + } + return res; +} + +//================================================================================= +// function : SetBooleanValue() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::SetBooleanValue( int type, bool value ) +{ + switch ( type ) { + case 1 : CheckBox_Orientation_3DVectors->setChecked( value ); break; // 3D vectors + default: break; + } +} + +//================================================================================= +// function : GetBooleanValue() +// purpose : +//================================================================================= +bool SMESHGUI_Preferences_ColorDlg::GetBooleanValue( int type ) +{ + bool res = false; + switch ( type ) { + case 1 : res = CheckBox_Orientation_3DVectors->isChecked(); break; // 3D vectors default: break; } return res; } + +//================================================================================= +// function : setCustomMarkerMap() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::setCustomMarkerMap( VTK::MarkerMap theMarkerMap ) +{ + MarkerWidget->setCustomMarkerMap( theMarkerMap ); +} + +//================================================================================= +// function : getCustomMarkerMap() +// purpose : +//================================================================================= +VTK::MarkerMap SMESHGUI_Preferences_ColorDlg::getCustomMarkerMap() +{ + return MarkerWidget->getCustomMarkerMap(); +} + +//================================================================================= +// function : setStandardMarker() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::setStandardMarker( VTK::MarkerType theMarkerType, + VTK::MarkerScale theMarkerScale ) +{ + MarkerWidget->setStandardMarker( theMarkerType, theMarkerScale ); +} + +//================================================================================= +// function : setCustomMarker() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::setCustomMarker( int theId ) +{ + MarkerWidget->setCustomMarker( theId ); +} + +//================================================================================= +// function : getMarkerType() +// purpose : +//================================================================================= +VTK::MarkerType SMESHGUI_Preferences_ColorDlg::getMarkerType() const +{ + return MarkerWidget->getMarkerType(); +} + +//================================================================================= +// function : getStandardMarkerScale() +// purpose : +//================================================================================= +VTK::MarkerScale SMESHGUI_Preferences_ColorDlg::getStandardMarkerScale() const +{ + return MarkerWidget->getStandardMarkerScale(); +} + +//================================================================================= +// function : getCustomMarkerID() +// purpose : +//================================================================================= +int SMESHGUI_Preferences_ColorDlg::getCustomMarkerID() const +{ + return MarkerWidget->getCustomMarkerID(); +} + +//================================================================================= +// function : SetDeltaBrightness(int) +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::SetDeltaBrightness(int delta) +{ + toolSurfColor->setDelta(delta); +} +//================================================================================= +// function : GetDeltaBrightness() +// purpose : +//================================================================================= +int SMESHGUI_Preferences_ColorDlg::GetDeltaBrightness() +{ + return toolSurfColor->delta(); +} + +//================================================================================= +// function : keyPressEvent() +// purpose : +//================================================================================= +void SMESHGUI_Preferences_ColorDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( e->isAccepted() ) + return; + + if ( e->key() == Qt::Key_F1 ) { + e->accept(); + ClickOnHelp(); + } +} diff --git a/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.h b/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.h index f3f43f94d..9533ca748 100644 --- a/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.h +++ b/src/SMESHGUI/SMESHGUI_Preferences_ColorDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Preferences_ColorDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -29,13 +30,19 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +// SALOME GUI includes +#include +#include + // Qt includes #include -class QSpinBox; +class QCheckBox; class SMESHGUI; -class QtxIntSpinBox; +class SMESHGUI_SpinBox; +class SalomeApp_IntSpinBox; class QtxColorButton; +class VTKViewer_MarkerWidget; class SMESHGUI_EXPORT SMESHGUI_Preferences_ColorDlg : public QDialog { @@ -49,26 +56,55 @@ public: QColor GetColor( int ); void SetIntValue( int, int ); int GetIntValue( int ); + void SetDoubleValue( int, double ); + double GetDoubleValue( int ); + void SetBooleanValue( int, bool ); + bool GetBooleanValue( int ); + + void setCustomMarkerMap( VTK::MarkerMap ); + VTK::MarkerMap getCustomMarkerMap(); + + void SetDeltaBrightness(int); + int GetDeltaBrightness(); + + void setStandardMarker( VTK::MarkerType, VTK::MarkerScale ); + void setCustomMarker( int ); + VTK::MarkerType getMarkerType() const; + VTK::MarkerScale getStandardMarkerScale() const; + int getCustomMarkerID() const; protected: void closeEvent( QCloseEvent* ); + void keyPressEvent( QKeyEvent* ); private slots: void ClickOnOk(); void ClickOnCancel(); + void ClickOnHelp(); void DeactivateActiveDialog(); void ActivateThisDialog(); private: SMESHGUI* mySMESHGUI; - - QtxColorButton* btnFillColor; - QtxColorButton* btnBackFaceColor; + + QtxBiColorTool* toolSurfColor; + QtxColorButton* btnWireframeColor; QtxColorButton* btnOutlineColor; - QSpinBox* SpinBox_Width; - QtxIntSpinBox* SpinBox_Shrink; + QtxColorButton* btn0DElementsColor; + QtxColorButton* btnBallElemColor; + SalomeApp_IntSpinBox* SpinBox_0DElements_Size; + SalomeApp_IntSpinBox* SpinBox_BallElem_Size; + SalomeApp_IntSpinBox* SpinBox_Width; + SalomeApp_IntSpinBox* SpinBox_Shrink; QtxColorButton* btnNodeColor; - QSpinBox* SpinBox_Nodes_Size; + VTKViewer_MarkerWidget* MarkerWidget; + QtxColorButton* btnOrientationColor; + SMESHGUI_SpinBox* SpinBox_Orientation_Scale; + QCheckBox* CheckBox_Orientation_3DVectors; + QtxColorButton* btnPreselectionColor; + QtxColorButton* btnSelectionColor; + + QString myHelpFileName; }; #endif // SMESHGUI_PREFERENCES_COLORDLG_H diff --git a/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.cxx b/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.cxx index a6d44e57a..7f0a861ae 100644 --- a/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Preferences_ScalarBarDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -27,10 +28,14 @@ #include "SMESHGUI_Preferences_ScalarBarDlg.h" #include "SMESHGUI.h" +#include "SMESHGUI_SpinBox.h" #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_Utils.h" #include +#include +#include +#include // SALOME GUI includes #include @@ -41,8 +46,8 @@ #include #include #include +#include -#include #include // Qt includes @@ -54,7 +59,6 @@ #include #include #include -#include #include #include #include @@ -62,7 +66,6 @@ // VTK includes #include -#include #include #define MINIMUM_WIDTH 70 @@ -199,13 +202,15 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* QHBoxLayout* myLabColorGrpLayout = new QHBoxLayout( myLabColorGrp ); myLabColorGrpLayout->setSpacing( SPACING_SIZE ); myLabColorGrpLayout->setMargin( MARGIN_SIZE ); - myColorsSpin = new QSpinBox( myLabColorGrp ); + myColorsSpin = new SalomeApp_IntSpinBox( myLabColorGrp ); + myColorsSpin->setAcceptNames( false ); // No Notebook variables allowed myColorsSpin->setRange( 2, 256 ); myColorsSpin->setSingleStep( 1 ); myColorsSpin->setMinimumWidth( MINIMUM_WIDTH ); myColorsSpin->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); - myLabelsSpin = new QSpinBox( myLabColorGrp ); + myLabelsSpin = new SalomeApp_IntSpinBox( myLabColorGrp ); + myLabelsSpin->setAcceptNames( false ); // No Notebook variables allowed myLabelsSpin->setRange( 2, 65 ); myLabelsSpin->setSingleStep( 1 ); myLabelsSpin->setMinimumWidth( MINIMUM_WIDTH ); @@ -242,19 +247,27 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* QGridLayout* myOriginDimGrpLayout = new QGridLayout( myOriginDimGrp ); myOriginDimGrpLayout->setSpacing( SPACING_SIZE ); myOriginDimGrpLayout->setMargin( MARGIN_SIZE ); - myXSpin = new QtxDoubleSpinBox (0.0, 1.0, 0.1, myOriginDimGrp); + myXSpin = new SMESHGUI_SpinBox(myOriginDimGrp); + myXSpin->setAcceptNames( false ); + myXSpin->RangeStepAndValidator( 0.0, 1.0, 0.1, "parametric_precision" ); myXSpin->setMinimumWidth( MINIMUM_WIDTH ); myXSpin->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); - myYSpin = new QtxDoubleSpinBox(0.0, 1.0, 0.1, myOriginDimGrp); + myYSpin = new SMESHGUI_SpinBox(myOriginDimGrp); + myYSpin->setAcceptNames( false ); + myYSpin->RangeStepAndValidator( 0.0, 1.0, 0.1, "parametric_precision" ); myYSpin->setMinimumWidth( MINIMUM_WIDTH ); myYSpin->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); - myWidthSpin = new QtxDoubleSpinBox(0.0, 1.0, 0.1, myOriginDimGrp); + myWidthSpin = new SMESHGUI_SpinBox(myOriginDimGrp); + myWidthSpin->setAcceptNames( false ); + myWidthSpin->RangeStepAndValidator( 0.0, 1.0, 0.1, "parametric_precision" ); myWidthSpin->setMinimumWidth( MINIMUM_WIDTH ); myWidthSpin->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); - myHeightSpin = new QtxDoubleSpinBox(0.0, 1.0, 0.1, myOriginDimGrp); + myHeightSpin = new SMESHGUI_SpinBox(myOriginDimGrp); + myHeightSpin->setAcceptNames( false ); + myHeightSpin->RangeStepAndValidator( 0.0, 1.0, 0.1, "parametric_precision" ); myHeightSpin->setMinimumWidth( MINIMUM_WIDTH ); myHeightSpin->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); @@ -268,8 +281,35 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* myOriginDimGrpLayout->addWidget( myHeightSpin, 1, 3 ); aTopLayout->addWidget( myOriginDimGrp ); + /******************************************************************************/ - /***************************************************************/ + // Destribution + myDistributionGrp = new QGroupBox ( tr( "SMESH_DISTRIBUTION_SCALARBAR" ), this ); + myDistributionGrp->setCheckable(true); + QHBoxLayout* aDistributionGrpLayout = new QHBoxLayout( myDistributionGrp ); + aDistributionGrpLayout->setSpacing( SPACING_SIZE ); aDistributionGrpLayout->setMargin( MARGIN_SIZE ); + + myDistribColorGrp = new QButtonGroup( this ); + + myDMonoColor = new QRadioButton( tr( "SMESH_MONOCOLOR" ) , myDistributionGrp ); + myDMultiColor = new QRadioButton( tr( "SMESH_MULTICOLOR" ), myDistributionGrp ); + myDMonoColor->setChecked( true ); + + myDistribColorGrp->addButton(myDMonoColor);myDistribColorGrp->setId(myDMonoColor,1); + myDistribColorGrp->addButton(myDMultiColor);myDistribColorGrp->setId(myDMultiColor,2); + + aDistributionGrpLayout->addWidget( myDMultiColor ); + aDistributionGrpLayout->addWidget( myDMonoColor ); + + //Color of the Distribution in monocolor case: + myDistributionColorLbl = new QLabel( tr( "SMESH_DISTRIBUTION_COLOR" ), myDistributionGrp ); + aDistributionGrpLayout->addWidget( myDistributionColorLbl ); + myMonoColorBtn = new QtxColorButton( myDistributionGrp ); + aDistributionGrpLayout->addWidget(myMonoColorBtn); + + aTopLayout->addWidget(myDistributionGrp); + + /******************************************************************************/ // Common buttons myButtonGrp = new QGroupBox( this ); QHBoxLayout* myButtonGrpLayout = new QHBoxLayout( myButtonGrp ); @@ -300,7 +340,7 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* SUIT_ResourceMgr* mgr = SMESH::GetResourceMgr( mySMESHGUI ); QColor titleColor = mgr->colorValue("SMESH", "scalar_bar_title_color", - QColor(255, 255, 255)); + QColor(255, 255, 255)); myTitleColorBtn->setColor(titleColor); myTitleFontCombo->setCurrentIndex(0); if (mgr->hasValue("SMESH", "scalar_bar_title_font")) { @@ -316,9 +356,9 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* myTitleItalicCheck->setChecked( f.italic() ); myTitleShadowCheck->setChecked( f.overline() ); } - + QColor labelColor = mgr->colorValue("SMESH", "scalar_bar_label_color", - QColor(255, 255, 255)); + QColor(255, 255, 255)); myLabelsColorBtn->setColor(labelColor); myLabelsFontCombo->setCurrentIndex(0); if (mgr->hasValue("SMESH", "scalar_bar_label_font")) { @@ -352,19 +392,39 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* QString name = isHoriz ? "scalar_bar_horizontal_%1" : "scalar_bar_vertical_%1"; myIniX = mgr->doubleValue("SMESH", name.arg( "x" ), - myHorizRadioBtn->isChecked() ? DEF_HOR_X : DEF_VER_X); + myHorizRadioBtn->isChecked() ? DEF_HOR_X : DEF_VER_X); myIniY = mgr->doubleValue("SMESH", name.arg( "y" ), - myHorizRadioBtn->isChecked() ? DEF_HOR_Y : DEF_VER_Y); + myHorizRadioBtn->isChecked() ? DEF_HOR_Y : DEF_VER_Y); myIniW = mgr->doubleValue("SMESH", name.arg( "width" ), - myHorizRadioBtn->isChecked() ? DEF_HOR_W : DEF_VER_W); + myHorizRadioBtn->isChecked() ? DEF_HOR_W : DEF_VER_W); myIniH = mgr->doubleValue("SMESH", name.arg( "height" ), - myHorizRadioBtn->isChecked() ? DEF_HOR_H : DEF_VER_H); + myHorizRadioBtn->isChecked() ? DEF_HOR_H : DEF_VER_H); setOriginAndSize(myIniX, myIniY, myIniW, myIniH); + + bool distributionVisibility = mgr->booleanValue("SMESH","distribution_visibility"); + myDistributionGrp->setChecked(distributionVisibility); + + int coloringType = mgr->integerValue("SMESH", "distribution_coloring_type", 0); + if( coloringType == SMESH_MONOCOLOR_TYPE ) { + myDMonoColor->setChecked(true); + onDistributionChanged(myDistribColorGrp->id(myDMonoColor)); + } else { + myDMultiColor->setChecked(true); + onDistributionChanged(myDistribColorGrp->id(myDMultiColor)); + + } + + QColor distributionColor = mgr->colorValue("SMESH", "distribution_color", + QColor(255, 255, 255)); + myMonoColorBtn->setColor(distributionColor); + + + // --> then init from selection if necessary onSelectionChanged(); @@ -377,10 +437,12 @@ SMESHGUI_Preferences_ScalarBarDlg::SMESHGUI_Preferences_ScalarBarDlg( SMESHGUI* connect( myXSpin, SIGNAL( valueChanged( double ) ), this, SLOT( onXYChanged() ) ); connect( myYSpin, SIGNAL( valueChanged( double ) ), this, SLOT( onXYChanged() ) ); connect( aOrientationGrp, SIGNAL( buttonClicked( int ) ), this, SLOT( onOrientationChanged() ) ); + connect( myDistributionGrp, SIGNAL( toggled(bool) ), this, SLOT(onDistributionActivated(bool)) ); + connect( myDistribColorGrp, SIGNAL( buttonClicked( int ) ), this, SLOT( onDistributionChanged( int ) ) ); connect( mySelectionMgr, SIGNAL( currentSelectionChanged() ), this, SLOT( onSelectionChanged() ) ); connect( mySMESHGUI, SIGNAL( SignalCloseAllDialogs() ), this, SLOT( onCancel() ) ); - myHelpFileName = "about_quality_controls_page.html"; + myHelpFileName = "quality_page.html"; } //================================================================================================= @@ -419,7 +481,7 @@ bool SMESHGUI_Preferences_ScalarBarDlg::onApply() // Scalar Bar properties if (!myActor) return false; - vtkScalarBarActor* myScalarBarActor = myActor->GetScalarBarActor(); + SMESH_ScalarBarActor* myScalarBarActor = myActor->GetScalarBarActor(); vtkTextProperty* aTitleTextPrp = myScalarBarActor->GetTitleTextProperty(); QColor aTColor = myTitleColorBtn->color(); @@ -450,7 +512,6 @@ bool SMESHGUI_Preferences_ScalarBarDlg::onApply() myScalarBarActor->SetLabelTextProperty( aLabelsTextPrp ); myScalarBarActor->SetNumberOfLabels( myLabelsSpin->value() ); - myScalarBarActor->SetMaximumNumberOfColors( myColorsSpin->value() ); if ( myHorizRadioBtn->isChecked() ) myScalarBarActor->SetOrientationToHorizontal(); @@ -461,13 +522,59 @@ bool SMESHGUI_Preferences_ScalarBarDlg::onApply() myScalarBarActor->SetWidth( myWidthSpin->value() ); myScalarBarActor->SetHeight( myHeightSpin->value() ); + // Distribution + bool distributionTypeChanged = false, colorChanged=false; + myScalarBarActor->SetDistributionVisibility((int)myDistributionGrp->isChecked()); + if( myDistributionGrp->isChecked() ) { + int ColoringType = myDMultiColor->isChecked() ? SMESH_MULTICOLOR_TYPE : SMESH_MONOCOLOR_TYPE; + distributionTypeChanged = (ColoringType != myScalarBarActor->GetDistributionColoringType()); + if(distributionTypeChanged) + myScalarBarActor->SetDistributionColoringType(ColoringType); + + if( !myDMultiColor->isChecked() ) { + QColor aTColor = myMonoColorBtn->color(); + double rgb[3], oldRgb[3];; + rgb [0] = aTColor.red()/255.; + rgb [1] = aTColor.green()/255.; + rgb [2] = aTColor.blue()/255.; + myScalarBarActor->GetDistributionColor(oldRgb); + colorChanged = (rgb[0] != oldRgb[0] || rgb[1] != oldRgb[1] || rgb[2] != oldRgb[2]); + if(colorChanged) + myScalarBarActor->SetDistributionColor(rgb); + } + } + double aMin = myMinEdit->text().toDouble(); double aMax = myMaxEdit->text().toDouble(); vtkLookupTable* myLookupTable = static_cast(myScalarBarActor->GetLookupTable()); + double oldMinMax[2] = { myLookupTable->GetRange()[0], myLookupTable->GetRange()[1] }; + bool rangeChanges = ( fabs( oldMinMax[0] - aMin ) + fabs( oldMinMax[1] - aMax ) > + 0.001 * ( aMax-aMin + oldMinMax[1]-oldMinMax[0] )); + + bool nbColorsChanged = (myColorsSpin->value() != myScalarBarActor->GetMaximumNumberOfColors()); + if(nbColorsChanged) + myScalarBarActor->SetMaximumNumberOfColors(myColorsSpin->value()); + + myLookupTable->SetRange( aMin, aMax ); myLookupTable->SetNumberOfTableValues(myColorsSpin->value()); myLookupTable->Build(); + + if( nbColorsChanged || rangeChanges) + myActor->UpdateDistribution(); + +#ifndef DISABLE_PLOT2DVIEWER + if( myActor->GetPlot2Histogram() && + (nbColorsChanged || + rangeChanges || + distributionTypeChanged || + colorChanged )) + SMESH::ProcessIn2DViewers(myActor); +#endif + + + SMESH::RepaintCurrentView(); return true; } @@ -504,10 +611,10 @@ void SMESHGUI_Preferences_ScalarBarDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -528,56 +635,71 @@ void SMESHGUI_Preferences_ScalarBarDlg::onSelectionChanged() if( anIO->hasEntry() ) { SMESH_Actor* anActor = SMESH::FindActorByEntry(anIO->getEntry()); if ( anActor && anActor->GetScalarBarActor() && anActor->GetControlMode() != SMESH_Actor::eNone ) { - myActor = anActor; - vtkScalarBarActor* myScalarBarActor = myActor->GetScalarBarActor(); - - if ( myScalarBarActor->GetLookupTable() ) { - vtkFloatingPointType *range = myScalarBarActor->GetLookupTable()->GetRange(); - myMinEdit->setText( QString::number( range[0],'g',12 ) ); - myMaxEdit->setText( QString::number( range[1],'g',12 ) ); - } - - vtkTextProperty* aTitleTextPrp = myScalarBarActor->GetTitleTextProperty(); - vtkFloatingPointType aTColor[3]; - aTitleTextPrp->GetColor( aTColor ); - myTitleColorBtn->setColor( QColor( (int)( aTColor[0]*255 ), (int)( aTColor[1]*255 ), (int)( aTColor[2]*255 ) ) ); - myTitleFontCombo->setCurrentIndex( aTitleTextPrp->GetFontFamily() ); - myTitleBoldCheck->setChecked( aTitleTextPrp->GetBold() ); - myTitleItalicCheck->setChecked( aTitleTextPrp->GetItalic() ); - myTitleShadowCheck->setChecked( aTitleTextPrp->GetShadow() ); - - vtkTextProperty* aLabelsTextPrp = myScalarBarActor->GetLabelTextProperty(); - vtkFloatingPointType aLColor[3]; - aLabelsTextPrp->GetColor( aLColor ); - myLabelsColorBtn->setColor( QColor( (int)( aLColor[0]*255 ), (int)( aLColor[1]*255 ), (int)( aLColor[2]*255 ) ) ); - myLabelsFontCombo->setCurrentIndex( aLabelsTextPrp->GetFontFamily() ); - myLabelsBoldCheck->setChecked( aLabelsTextPrp->GetBold() ); - myLabelsItalicCheck->setChecked( aLabelsTextPrp->GetItalic() ); - myLabelsShadowCheck->setChecked( aLabelsTextPrp->GetShadow() ); - - myLabelsSpin->setValue( myScalarBarActor->GetNumberOfLabels() ); - myColorsSpin->setValue( myScalarBarActor->GetMaximumNumberOfColors() ); - - if ( myScalarBarActor->GetOrientation() == VTK_ORIENT_VERTICAL ) - myVertRadioBtn->setChecked( true ); - else - myHorizRadioBtn->setChecked( true ); - myIniOrientation = myVertRadioBtn->isChecked(); - - myIniX = myScalarBarActor->GetPosition()[0]; - myIniY = myScalarBarActor->GetPosition()[1]; - myIniW = myScalarBarActor->GetWidth(); - myIniH = myScalarBarActor->GetHeight(); - setOriginAndSize( myIniX, myIniY, myIniW, myIniH ); - - myRangeGrp->setEnabled( true ); - myFontGrp->setEnabled( true ); - myLabColorGrp->setEnabled( true ); - myOrientationGrp->setEnabled( true ); - myOriginDimGrp->setEnabled( true ); - myOkBtn->setEnabled( true ); - myApplyBtn->setEnabled( true ); - return; + myActor = anActor; + SMESH_ScalarBarActor* myScalarBarActor = myActor->GetScalarBarActor(); + + if ( myScalarBarActor->GetLookupTable() ) { + vtkFloatingPointType *range = myScalarBarActor->GetLookupTable()->GetRange(); + myMinEdit->setText( QString::number( range[0],'g',12 ) ); + myMaxEdit->setText( QString::number( range[1],'g',12 ) ); + } + + vtkTextProperty* aTitleTextPrp = myScalarBarActor->GetTitleTextProperty(); + vtkFloatingPointType aTColor[3]; + aTitleTextPrp->GetColor( aTColor ); + myTitleColorBtn->setColor( QColor( (int)( aTColor[0]*255 ), (int)( aTColor[1]*255 ), (int)( aTColor[2]*255 ) ) ); + myTitleFontCombo->setCurrentIndex( aTitleTextPrp->GetFontFamily() ); + myTitleBoldCheck->setChecked( aTitleTextPrp->GetBold() ); + myTitleItalicCheck->setChecked( aTitleTextPrp->GetItalic() ); + myTitleShadowCheck->setChecked( aTitleTextPrp->GetShadow() ); + + vtkTextProperty* aLabelsTextPrp = myScalarBarActor->GetLabelTextProperty(); + vtkFloatingPointType aLColor[3]; + aLabelsTextPrp->GetColor( aLColor ); + myLabelsColorBtn->setColor( QColor( (int)( aLColor[0]*255 ), (int)( aLColor[1]*255 ), (int)( aLColor[2]*255 ) ) ); + myLabelsFontCombo->setCurrentIndex( aLabelsTextPrp->GetFontFamily() ); + myLabelsBoldCheck->setChecked( aLabelsTextPrp->GetBold() ); + myLabelsItalicCheck->setChecked( aLabelsTextPrp->GetItalic() ); + myLabelsShadowCheck->setChecked( aLabelsTextPrp->GetShadow() ); + + myLabelsSpin->setValue( myScalarBarActor->GetNumberOfLabels() ); + myColorsSpin->setValue( myScalarBarActor->GetMaximumNumberOfColors() ); + + if ( myScalarBarActor->GetOrientation() == VTK_ORIENT_VERTICAL ) + myVertRadioBtn->setChecked( true ); + else + myHorizRadioBtn->setChecked( true ); + myIniOrientation = myVertRadioBtn->isChecked(); + + myIniX = myScalarBarActor->GetPosition()[0]; + myIniY = myScalarBarActor->GetPosition()[1]; + myIniW = myScalarBarActor->GetWidth(); + myIniH = myScalarBarActor->GetHeight(); + setOriginAndSize( myIniX, myIniY, myIniW, myIniH ); + + int coloringType = myScalarBarActor->GetDistributionColoringType(); + myScalarBarActor->GetDistributionColor( aTColor ); + myMonoColorBtn->setColor( QColor( (int)( aTColor[0]*255 ), (int)( aTColor[1]*255 ), (int)( aTColor[2]*255 ) ) ); + if ( coloringType == SMESH_MONOCOLOR_TYPE ) { + myDMonoColor->setChecked(true); + onDistributionChanged(myDistribColorGrp->id(myDMonoColor)); + } else { + myDMultiColor->setChecked(true); + onDistributionChanged(myDistribColorGrp->id(myDMultiColor)); + } + myDistributionGrp->setChecked((bool)myScalarBarActor->GetDistributionVisibility()); + onDistributionActivated(myScalarBarActor->GetDistributionVisibility()); + + + myRangeGrp->setEnabled( true ); + myFontGrp->setEnabled( true ); + myLabColorGrp->setEnabled( true ); + myOrientationGrp->setEnabled( true ); + myOriginDimGrp->setEnabled( true ); + myOkBtn->setEnabled( true ); + myApplyBtn->setEnabled( true ); + myDistributionGrp->setEnabled( true ); + return; } } } @@ -589,6 +711,7 @@ void SMESHGUI_Preferences_ScalarBarDlg::onSelectionChanged() myOriginDimGrp->setEnabled( false ); myOkBtn->setEnabled( false ); myApplyBtn->setEnabled( false ); + myDistributionGrp->setEnabled( false ); } //================================================================================================= @@ -625,9 +748,9 @@ void SMESHGUI_Preferences_ScalarBarDlg::onXYChanged() */ //================================================================================================= void SMESHGUI_Preferences_ScalarBarDlg::setOriginAndSize( const double x, - const double y, - const double w, - const double h ) + const double y, + const double w, + const double h ) { blockSignals( true ); myXSpin->setValue( x ); @@ -640,6 +763,42 @@ void SMESHGUI_Preferences_ScalarBarDlg::setOriginAndSize( const double x, onXYChanged(); } + +//================================================================================================= +/*! + * SMESHGUI_Preferences_ScalarBarDlg::onDistributionChanged + * + * Called when coloring type of the distribution is changed + */ +//================================================================================================= +void SMESHGUI_Preferences_ScalarBarDlg::onDistributionChanged( int id ) { + + bool isActive = myDistribColorGrp->id(myDMonoColor) == id; + + myMonoColorBtn->setEnabled(isActive); + myDistributionColorLbl->setEnabled(isActive); +} +//================================================================================================= +/*! + * SMESHGUI_Preferences_ScalarBarDlg::onDistributionActivated + * + * Called when distribution group check box is changed + */ +//================================================================================================= +void SMESHGUI_Preferences_ScalarBarDlg::onDistributionActivated(bool on) { + if(on) { + if(myDMonoColor->isChecked()) + onDistributionChanged(myDistribColorGrp->id(myDMonoColor) ); + else if(myDMultiColor->isChecked()) + onDistributionChanged(myDistribColorGrp->id(myDMultiColor) ); + } + else { + myMonoColorBtn->setEnabled(false); + myDistributionColorLbl->setEnabled(false); + } +} + + //================================================================================================= /*! * SMESHGUI_Preferences_ScalarBarDlg::onOrientationChanged @@ -656,9 +815,9 @@ void SMESHGUI_Preferences_ScalarBarDlg::onOrientationChanged() setOriginAndSize( myIniX, myIniY, myIniW, myIniH ); else setOriginAndSize( aOrientation ? DEF_VER_X : DEF_HOR_X, - aOrientation ? DEF_VER_Y : DEF_HOR_Y, - aOrientation ? DEF_VER_W : DEF_HOR_W, - aOrientation ? DEF_VER_H : DEF_HOR_H ); + aOrientation ? DEF_VER_Y : DEF_HOR_Y, + aOrientation ? DEF_VER_W : DEF_HOR_W, + aOrientation ? DEF_VER_H : DEF_HOR_H ); } //================================================================================================= diff --git a/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.h b/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.h index 609631267..804b4aa6a 100644 --- a/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.h +++ b/src/SMESHGUI/SMESHGUI_Preferences_ScalarBarDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Preferences_ScalarBarDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -39,11 +40,13 @@ class QLineEdit; class QPushButton; class QToolButton; class QRadioButton; -class QSpinBox; +class QButtonGroup; +class QLabel; class SMESHGUI; class SMESH_Actor; -class QtxDoubleSpinBox; +class SMESHGUI_SpinBox; +class SalomeApp_IntSpinBox; class QtxColorButton; class LightApp_SelectionMgr; @@ -63,9 +66,9 @@ public: void closeEvent( QCloseEvent* ); void setOriginAndSize( const double, - const double, - const double, - const double ); + const double, + const double, + const double ); void initScalarBarFromResources(); protected slots: @@ -76,6 +79,8 @@ protected slots: void onSelectionChanged(); void onXYChanged(); void onOrientationChanged(); + void onDistributionChanged( int ); + void onDistributionActivated( bool ); private: SMESHGUI* mySMESHGUI; @@ -103,20 +108,27 @@ private: QCheckBox* myLabelsShadowCheck; QGroupBox* myLabColorGrp; - QSpinBox* myColorsSpin; - QSpinBox* myLabelsSpin; + SalomeApp_IntSpinBox* myColorsSpin; + SalomeApp_IntSpinBox* myLabelsSpin; QGroupBox* myOrientationGrp; QRadioButton* myVertRadioBtn; QRadioButton* myHorizRadioBtn; QGroupBox* myOriginDimGrp; - QtxDoubleSpinBox* myXSpin; - QtxDoubleSpinBox* myYSpin; - QtxDoubleSpinBox* myWidthSpin; - QtxDoubleSpinBox* myHeightSpin; + SMESHGUI_SpinBox* myXSpin; + SMESHGUI_SpinBox* myYSpin; + SMESHGUI_SpinBox* myWidthSpin; + SMESHGUI_SpinBox* myHeightSpin; + + QGroupBox* myDistributionGrp; + QRadioButton* myDMonoColor; + QRadioButton* myDMultiColor; + QtxColorButton* myMonoColorBtn; + QLabel* myDistributionColorLbl; QGroupBox* myButtonGrp; + QButtonGroup* myDistribColorGrp; QPushButton* myOkBtn; QPushButton* myApplyBtn; QPushButton* myCancelBtn; diff --git a/src/SMESHGUI/SMESHGUI_PreviewDlg.cxx b/src/SMESHGUI/SMESHGUI_PreviewDlg.cxx new file mode 100644 index 000000000..0a9bf6842 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_PreviewDlg.cxx @@ -0,0 +1,125 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_PreviewDlg.cxx +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. +// SMESH includes +// + +//SMESH includes +#include "SMESHGUI.h" +#include "SMESHGUI_PreviewDlg.h" +#include "SMESHGUI_MeshEditPreview.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_Utils.h" + +//GUI includes +#include + +//QT includes +#include + + +//================================================================================= +// class : SMESHGUI_SMESHGUI_PreviewDlg() +// purpose : +//================================================================================= +SMESHGUI_PreviewDlg::SMESHGUI_PreviewDlg(SMESHGUI* theModule) : + mySMESHGUI(theModule), + QDialog(SMESH::GetDesktop( theModule )), + myIsApplyAndClose( false ) +{ + mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI )); +} + +//================================================================================= +// function : ~SMESHGUI_PreviewDlg() +// purpose : Destroys the object and frees any allocated resources +//================================================================================= +SMESHGUI_PreviewDlg::~SMESHGUI_PreviewDlg() +{ + delete mySimulation; +} + +//================================================================================= +// function : showPreview +// purpose : Show preview in the viewer +//================================================================================= +void SMESHGUI_PreviewDlg::showPreview(){ + if(mySimulation) + mySimulation->SetVisibility(true); +} + +//================================================================================= +// function : hidePreview +// purpose : Hide preview in the viewer +//================================================================================= +void SMESHGUI_PreviewDlg::hidePreview(){ + if(mySimulation) + mySimulation->SetVisibility(false); +} + +//================================================================================= +// function : connectPreviewControl +// purpose : Connect the preview check box +//================================================================================= +void SMESHGUI_PreviewDlg::connectPreviewControl(){ + connect(myPreviewCheckBox, SIGNAL(toggled(bool)), this, SLOT(onDisplaySimulation(bool))); +} + + +//================================================================================= +// function : toDisplaySimulation +// purpose : +//================================================================================= +void SMESHGUI_PreviewDlg::toDisplaySimulation() { + onDisplaySimulation(true); +} + +//================================================================================= +// function : onDisplaySimulation +// purpose : +//================================================================================= +void SMESHGUI_PreviewDlg::onDisplaySimulation(bool toDisplayPreview) { + //Empty implementation here +} + +//================================================================ +// Function : setIsApplyAndClose +// Purpose : Set value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +void SMESHGUI_PreviewDlg::setIsApplyAndClose( const bool theFlag ) +{ + myIsApplyAndClose = theFlag; +} + +//================================================================ +// Function : isApplyAndClose +// Purpose : Get value of the flag indicating that the dialog is +// accepted by Apply & Close button +//================================================================ +bool SMESHGUI_PreviewDlg::isApplyAndClose() const +{ + return myIsApplyAndClose; +} diff --git a/src/SMESHGUI/SMESHGUI_PreviewDlg.h b/src/SMESHGUI/SMESHGUI_PreviewDlg.h new file mode 100644 index 000000000..f4731149f --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_PreviewDlg.h @@ -0,0 +1,67 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_RotationDlg.h +// Author : Roman NIKOLAEV, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_PREVIEWDLG_H +#define SMESHGUI_PREVIEWDLG_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +// Qt includes +#include + +class SMESHGUI; +class SMESHGUI_MeshEditPreview; +class QCheckBox; + +class SMESHGUI_EXPORT SMESHGUI_PreviewDlg : public QDialog { + Q_OBJECT +public: + SMESHGUI_PreviewDlg( SMESHGUI* ); + ~SMESHGUI_PreviewDlg(); + + void showPreview(); + void hidePreview(); + +protected: + void connectPreviewControl(); + + virtual void setIsApplyAndClose( const bool theFlag ); + virtual bool isApplyAndClose() const; + +protected slots: + void toDisplaySimulation(); + virtual void onDisplaySimulation( bool ); + + +protected: + SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ + SMESHGUI_MeshEditPreview* mySimulation; + QCheckBox* myPreviewCheckBox; + bool myIsApplyAndClose; +}; + +#endif diff --git a/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.cxx b/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.cxx index 118d2d17c..f16f568fe 100644 --- a/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RemoveElementsDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -122,6 +123,7 @@ SMESHGUI_RemoveElementsDlg SelectButtonC1A1->setIcon(image1); LineEditC1A1 = new QLineEdit(GroupC1); LineEditC1A1->setValidator(new SMESHGUI_IdValidator(this)); + LineEditC1A1->setMaxLength(-1); QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupC1 ); connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); @@ -204,7 +206,7 @@ void SMESHGUI_RemoveElementsDlg::Init() /* to close dialog if study change */ connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); connect(myEditCurrentArgument, SIGNAL(textChanged(const QString&)), - SLOT(onTextChange(const QString&))); + SLOT(onTextChange(const QString&))); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->SetSelectionMode(CellSelection); @@ -231,7 +233,7 @@ void SMESHGUI_RemoveElementsDlg::ClickOnApply() bool aResult = false; try { SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - aResult = aMeshEditor->RemoveElements(anArrayOfIdeces.inout()); + aResult = aMeshEditor->RemoveElements(anArrayOfIdeces.in()); } catch (const SALOME::SALOME_Exception& S_ex) { SalomeApp_Tools::QtCatchCorbaException(S_ex); myEditCurrentArgument->clear(); @@ -243,6 +245,7 @@ void SMESHGUI_RemoveElementsDlg::ClickOnApply() myEditCurrentArgument->clear(); mySelector->ClearIndex(); SMESH::UpdateView(); + SMESHGUI::Modified(); } } } @@ -291,10 +294,10 @@ void SMESHGUI_RemoveElementsDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -309,9 +312,6 @@ void SMESHGUI_RemoveElementsDlg::onTextChange(const QString& theNewText) myNbOkElements = 0; - buttonOk->setEnabled(false); - buttonApply->setEnabled(false); - // hilight entered elements if(myActor){ if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){ @@ -321,24 +321,20 @@ void SMESHGUI_RemoveElementsDlg::onTextChange(const QString& theNewText) QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); for (int i = 0; i < aListId.count(); i++) { - if(const SMDS_MeshElement *anElem = aMesh->FindElement(aListId[i].toInt())) { - newIndices.Add(anElem->GetID()); - myNbOkElements++; - } + if(const SMDS_MeshElement *anElem = aMesh->FindElement(aListId[i].toInt())) { + newIndices.Add(anElem->GetID()); + myNbOkElements++; + } } mySelector->AddOrRemoveIndex(anIO,newIndices,false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight(anIO,true,true); + aViewWindow->highlight(anIO,true,true); } } - if (myNbOkElements) { - buttonOk->setEnabled(true); - buttonApply->setEnabled(true); - } - myBusy = false; + updateButtons(); } //================================================================================= @@ -347,56 +343,52 @@ void SMESHGUI_RemoveElementsDlg::onTextChange(const QString& theNewText) //================================================================================= void SMESHGUI_RemoveElementsDlg::SelectionIntoArgument() { - if (myBusy) return; + if (myBusy) return; // busy + if (myFilterDlg && myFilterDlg->isVisible()) return; // filter digl active + if (!GroupButtons->isEnabled()) return; // inactive // clear - myNbOkElements = false; + myNbOkElements = 0; myActor = 0; myBusy = true; myEditCurrentArgument->setText(""); myBusy = false; - if (!GroupButtons->isEnabled()) // inactive - return; - - buttonOk->setEnabled(false); - buttonApply->setEnabled(false); - // get selected mesh SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); int nbSel = aList.Extent(); - if (nbSel != 1) - return; - - Handle(SALOME_InteractiveObject) anIO = aList.First(); - myMesh = SMESH::GetMeshByIO(anIO); - if (myMesh->_is_nil()) - return; - - myActor = SMESH::FindActorByEntry(anIO->getEntry()); - if (!myActor) - return; - - // get selected nodes - QString aString = ""; - int nbElems = SMESH::GetNameOfSelectedElements(mySelector,anIO,aString); - if(nbElems < 1) - return; - myBusy = true; - myEditCurrentArgument->setText(aString); - myBusy = false; - - // OK - - myNbOkElements = nbElems; - - buttonOk->setEnabled(true); - buttonApply->setEnabled(true); + if (nbSel == 1) { + + Handle(SALOME_InteractiveObject) anIO = aList.First(); + myMesh = SMESH::GetMeshByIO(anIO); + + if (!myMesh->_is_nil()) { + + myActor = SMESH::FindActorByEntry(anIO->getEntry()); + if (myActor) { + + // get selected nodes + QString aString = ""; + int nbElems = SMESH::GetNameOfSelectedElements(mySelector,anIO,aString); + if (nbElems > 0) { + myBusy = true; + myEditCurrentArgument->setText(aString); + myBusy = false; + + // OK + + myNbOkElements = nbElems; + } // if (nbElems > 0) + } // if (myActor) + } // if (!myMesh->_is_nil()) + } // if (nbSel == 1) { + + updateButtons(); } //================================================================================= @@ -410,8 +402,8 @@ void SMESHGUI_RemoveElementsDlg::SetEditCurrentArgument() case 0: /* default constructor */ { if(send == SelectButtonC1A1) { - LineEditC1A1->setFocus(); - myEditCurrentArgument = LineEditC1A1; + LineEditC1A1->setFocus(); + myEditCurrentArgument = LineEditC1A1; } SelectionIntoArgument(); break; @@ -507,6 +499,12 @@ void SMESHGUI_RemoveElementsDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_RemoveElementsDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); @@ -516,3 +514,13 @@ void SMESHGUI_RemoveElementsDlg::setFilters() myFilterDlg->show(); } + +//================================================================================= +// function : updateButtons +// purpose : enable / disable control buttons +//================================================================================= +void SMESHGUI_RemoveElementsDlg::updateButtons() +{ + buttonOk->setEnabled(myNbOkElements > 0); + buttonApply->setEnabled(myNbOkElements > 0); +} diff --git a/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.h b/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.h index 7138acfe5..6cc8d5ae4 100644 --- a/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.h +++ b/src/SMESHGUI/SMESHGUI_RemoveElementsDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RemoveElementsDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -106,6 +107,7 @@ private slots: void ActivateThisDialog(); void onTextChange( const QString& ); void setFilters(); + void updateButtons(); }; #endif // SMESHGUI_REMOVEELEMENTSDLG_H diff --git a/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.cxx b/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.cxx index bf218290a..303aee6f6 100644 --- a/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RemoveNodesDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -122,6 +123,7 @@ SMESHGUI_RemoveNodesDlg SelectButtonC1A1->setIcon(image1); LineEditC1A1 = new QLineEdit(GroupC1); LineEditC1A1->setValidator(new SMESHGUI_IdValidator(this)); + LineEditC1A1->setMaxLength(-1); QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupC1 ); connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); @@ -204,7 +206,7 @@ void SMESHGUI_RemoveNodesDlg::Init() /* to close dialog if study change */ connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); connect(myEditCurrentArgument, SIGNAL(textChanged(const QString&)), - SLOT(onTextChange(const QString&))); + SLOT(onTextChange(const QString&))); SMESH::SetPointRepresentation(true); @@ -233,7 +235,7 @@ void SMESHGUI_RemoveNodesDlg::ClickOnApply() bool aResult = false; try { SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); - aResult = aMeshEditor->RemoveNodes(anArrayOfIdeces.inout()); + aResult = aMeshEditor->RemoveNodes(anArrayOfIdeces.in()); } catch (const SALOME::SALOME_Exception& S_ex) { SalomeApp_Tools::QtCatchCorbaException(S_ex); myEditCurrentArgument->clear(); @@ -245,6 +247,7 @@ void SMESHGUI_RemoveNodesDlg::ClickOnApply() myEditCurrentArgument->clear(); mySelector->ClearIndex(); SMESH::UpdateView(); + SMESHGUI::Modified(); } SMESH::SetPointRepresentation(true); @@ -297,10 +300,10 @@ void SMESHGUI_RemoveNodesDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -315,9 +318,6 @@ void SMESHGUI_RemoveNodesDlg::onTextChange(const QString& theNewText) myNbOkNodes = 0; - buttonOk->setEnabled(false); - buttonApply->setEnabled(false); - // hilight entered nodes if(myActor){ if(SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh()){ @@ -327,24 +327,20 @@ void SMESHGUI_RemoveNodesDlg::onTextChange(const QString& theNewText) QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); for (int i = 0; i < aListId.count(); i++) { - if (const SMDS_MeshNode *aNode = aMesh->FindNode(aListId[i].toInt())) { - newIndices.Add(aNode->GetID()); - myNbOkNodes++; - } + if (const SMDS_MeshNode *aNode = aMesh->FindNode(aListId[i].toInt())) { + newIndices.Add(aNode->GetID()); + myNbOkNodes++; + } } mySelector->AddOrRemoveIndex(anIO,newIndices,false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight(anIO,true,true); + aViewWindow->highlight(anIO,true,true); } } - if (myNbOkNodes) { - buttonOk->setEnabled(true); - buttonApply->setEnabled(true); - } - myBusy = false; + updateButtons(); } //================================================================================= @@ -353,56 +349,51 @@ void SMESHGUI_RemoveNodesDlg::onTextChange(const QString& theNewText) //================================================================================= void SMESHGUI_RemoveNodesDlg::SelectionIntoArgument() { - if (myBusy) return; - + if (myBusy) return; // busy + if (myFilterDlg && myFilterDlg->isVisible()) return; // filter dlg active + if (!GroupButtons->isEnabled()) return; // inactive + // clear - myNbOkNodes = false; + myNbOkNodes = 0; myActor = 0; myBusy = true; myEditCurrentArgument->setText(""); myBusy = false; - if (!GroupButtons->isEnabled()) // inactive - return; - - buttonOk->setEnabled(false); - buttonApply->setEnabled(false); - // get selected mesh SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); int nbSel = aList.Extent(); - if (nbSel != 1) - return; - - Handle(SALOME_InteractiveObject) anIO = aList.First(); - myMesh = SMESH::GetMeshByIO(anIO); - if (myMesh->_is_nil()) - return; - - myActor = SMESH::FindActorByEntry(anIO->getEntry()); - if (!myActor) - return; - - // get selected nodes - - QString aString = ""; - int nbNodes = SMESH::GetNameOfSelectedNodes(mySelector,anIO,aString); - if(nbNodes < 1) - return; - myBusy = true; - myEditCurrentArgument->setText(aString); - myBusy = false; - - // OK - - myNbOkNodes = true; - - buttonOk->setEnabled(true); - buttonApply->setEnabled(true); + if (nbSel == 1) { + + Handle(SALOME_InteractiveObject) anIO = aList.First(); + myMesh = SMESH::GetMeshByIO(anIO); + + if (!myMesh->_is_nil()) { + + myActor = SMESH::FindActorByEntry(anIO->getEntry()); + if (myActor) { + + // get selected nodes + QString aString = ""; + int nbNodes = SMESH::GetNameOfSelectedNodes(mySelector,anIO,aString); + if (nbNodes > 0) { + myBusy = true; + myEditCurrentArgument->setText(aString); + myBusy = false; + + // OK + + myNbOkNodes = nbNodes; + } // if (nbNodes > 0) + } // if (myActor) + } // if (!myMesh->_is_nil()) + } // if (nbSel == 1) + + updateButtons(); } //================================================================================= @@ -416,8 +407,8 @@ void SMESHGUI_RemoveNodesDlg::SetEditCurrentArgument() case 0: /* default constructor */ { if(send == SelectButtonC1A1) { - LineEditC1A1->setFocus(); - myEditCurrentArgument = LineEditC1A1; + LineEditC1A1->setFocus(); + myEditCurrentArgument = LineEditC1A1; } SelectionIntoArgument(); break; @@ -514,6 +505,12 @@ void SMESHGUI_RemoveNodesDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_RemoveNodesDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::NODE ); @@ -523,3 +520,13 @@ void SMESHGUI_RemoveNodesDlg::setFilters() myFilterDlg->show(); } + +//================================================================================= +// function : updateButtons +// purpose : enable / disable control buttons +//================================================================================= +void SMESHGUI_RemoveNodesDlg::updateButtons() +{ + buttonOk->setEnabled(myNbOkNodes > 0); + buttonApply->setEnabled(myNbOkNodes > 0); +} diff --git a/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.h b/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.h index e26e963d3..75a7afed3 100644 --- a/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.h +++ b/src/SMESHGUI/SMESHGUI_RemoveNodesDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RemoveNodesDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -106,6 +107,7 @@ private slots: void ActivateThisDialog(); void onTextChange( const QString& ); void setFilters(); + void updateButtons(); }; #endif // SMESHGUI_REMOVENODESDLG_H diff --git a/src/SMESHGUI/SMESHGUI_RenumberingDlg.cxx b/src/SMESHGUI/SMESHGUI_RenumberingDlg.cxx index 26a2614ac..278fe0b96 100644 --- a/src/SMESHGUI/SMESHGUI_RenumberingDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_RenumberingDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RenumberingDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -78,14 +79,14 @@ SMESHGUI_RenumberingDlg::SMESHGUI_RenumberingDlg( SMESHGUI* theModule, const int setModal(false); setAttribute(Qt::WA_DeleteOnClose, true); setWindowTitle(unit == 0 ? - tr("SMESH_RENUMBERING_NODES_TITLE") : - tr("SMESH_RENUMBERING_ELEMENTS_TITLE")); + tr("SMESH_RENUMBERING_NODES_TITLE") : + tr("SMESH_RENUMBERING_ELEMENTS_TITLE")); setSizeGripEnabled(true); SUIT_ResourceMgr* resMgr = SMESH::GetResourceMgr( mySMESHGUI ); QPixmap image0(resMgr->loadPixmap("SMESH", unit == 0 ? - tr("ICON_DLG_RENUMBERING_NODES") : - tr("ICON_DLG_RENUMBERING_ELEMENTS"))); + tr("ICON_DLG_RENUMBERING_NODES") : + tr("ICON_DLG_RENUMBERING_ELEMENTS"))); QPixmap image1(resMgr->loadPixmap("SMESH",tr("ICON_SELECT"))); QVBoxLayout* SMESHGUI_RenumberingDlgLayout = new QVBoxLayout(this); @@ -94,9 +95,9 @@ SMESHGUI_RenumberingDlg::SMESHGUI_RenumberingDlg( SMESHGUI* theModule, const int /***************************************************************/ GroupConstructors = new QGroupBox(unit == 0 ? - tr("SMESH_NODES") : - tr("SMESH_ELEMENTS"), - this); + tr("SMESH_NODES") : + tr("SMESH_ELEMENTS"), + this); myHelpFileName = unit == 0 ? "renumbering_nodes_and_elements_page.html#renumbering_nodes_anchor" : "renumbering_nodes_and_elements_page.html#renumbering_elements_anchor"; @@ -219,22 +220,22 @@ void SMESHGUI_RenumberingDlg::ClickOnApply() bool isUnitsLabeled = false; if (myUnit == 0 && anActor) { - isUnitsLabeled = anActor->GetPointsLabeled(); - if (isUnitsLabeled) anActor->SetPointsLabeled(false); + isUnitsLabeled = anActor->GetPointsLabeled(); + if (isUnitsLabeled) anActor->SetPointsLabeled(false); } else if (myUnit == 1 && anActor) { - isUnitsLabeled = anActor->GetCellsLabeled(); - if (isUnitsLabeled) anActor->SetCellsLabeled(false); + isUnitsLabeled = anActor->GetCellsLabeled(); + if (isUnitsLabeled) anActor->SetCellsLabeled(false); } SUIT_OverrideCursor aWaitCursor; if (myUnit == 0) { - aMeshEditor->RenumberNodes(); - if (isUnitsLabeled && anActor) anActor->SetPointsLabeled(true); + aMeshEditor->RenumberNodes(); + if (isUnitsLabeled && anActor) anActor->SetPointsLabeled(true); } else if (myUnit == 1) { - aMeshEditor->RenumberElements(); - if (isUnitsLabeled && anActor) anActor->SetCellsLabeled(true); + aMeshEditor->RenumberElements(); + if (isUnitsLabeled && anActor) anActor->SetCellsLabeled(true); } } catch(...) { @@ -242,6 +243,7 @@ void SMESHGUI_RenumberingDlg::ClickOnApply() //mySelectionMgr->clearSelected(); SMESH::UpdateView(); + SMESHGUI::Modified(); } } @@ -285,10 +287,10 @@ void SMESHGUI_RenumberingDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -316,7 +318,7 @@ void SMESHGUI_RenumberingDlg::SelectionIntoArgument() Handle(SALOME_InteractiveObject) IO = aList.First(); myMesh = SMESH::IObjectToInterface(IO); if (myMesh->_is_nil()) - aString = ""; + aString = ""; } } @@ -338,12 +340,12 @@ void SMESHGUI_RenumberingDlg::SetEditCurrentArgument() { case 0: /* default constructor */ { - if(send == SelectButton) { - LineEditMesh->setFocus(); - myEditCurrentArgument = LineEditMesh; - } - SelectionIntoArgument(); - break; + if(send == SelectButton) { + LineEditMesh->setFocus(); + myEditCurrentArgument = LineEditMesh; + } + SelectionIntoArgument(); + break; } } } diff --git a/src/SMESHGUI/SMESHGUI_RenumberingDlg.h b/src/SMESHGUI/SMESHGUI_RenumberingDlg.h index 95c9d4688..5ebbe6ca3 100644 --- a/src/SMESHGUI/SMESHGUI_RenumberingDlg.h +++ b/src/SMESHGUI/SMESHGUI_RenumberingDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RenumberingDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx b/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx new file mode 100644 index 000000000..cc5db9fe2 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.cxx @@ -0,0 +1,852 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_ReorientFacesDlg.cxx +// Author : Edward AGAPOV, Open CASCADE S.A.S. +// SMESH includes +// +#include "SMESHGUI_ReorientFacesDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_SpinBox.h" +#include "SMESHGUI_MeshEditPreview.h" + +#include +#include +#include +#include +#include +#include + +// SALOME GEOM includes +#include + +// SALOME GUI includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// SALOME KERNEL includes +#include + +// OCCT includes +#include +#include +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VTK includes +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) + +// std +#include + +#define SPACING 6 +#define MARGIN 11 + +enum { CONSTRUCTOR_POINT=0, CONSTRUCTOR_FACE, + EObject, EPoint, EFace, EDirection }; + +//======================================================================= +/*! + * \brief Dialog to reorient faces acoording to vector + */ +//======================================================================= + +SMESHGUI_ReorientFacesDlg::SMESHGUI_ReorientFacesDlg() + : SMESHGUI_Dialog( 0, false, true ) +{ + setWindowTitle(tr("CAPTION")); + + QVBoxLayout* aDlgLay = new QVBoxLayout (mainFrame()); + aDlgLay->setMargin(0); + aDlgLay->setSpacing(SPACING); + + QWidget* aMainFrame = createMainFrame (mainFrame()); + + aDlgLay->addWidget(aMainFrame); + + aDlgLay->setStretchFactor(aMainFrame, 1); +} + +//================================================================================ +/*! + * \brief Create frame containing dialog's input fields + */ +//================================================================================ + +QWidget* SMESHGUI_ReorientFacesDlg::createMainFrame (QWidget* theParent) +{ + QWidget* aFrame = new QWidget(theParent); + + // constructors + + QPixmap iconReoriPoint (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_POINT"))); + QPixmap iconReoriFace (resMgr()->loadPixmap("SMESH", tr("ICON_DLG_REORIENT2D_FACE"))); + + QGroupBox* aConstructorBox = new QGroupBox(tr("REORIENT_FACES"), aFrame); + myConstructorGrp = new QButtonGroup(aConstructorBox); + QHBoxLayout* aConstructorGrpLayout = new QHBoxLayout(aConstructorBox); + aConstructorGrpLayout->setMargin(MARGIN); + aConstructorGrpLayout->setSpacing(SPACING); + + QRadioButton* aPntBut = new QRadioButton(aConstructorBox); + aPntBut->setIcon(iconReoriPoint); + aPntBut->setChecked(true); + aConstructorGrpLayout->addWidget(aPntBut); + myConstructorGrp->addButton(aPntBut, CONSTRUCTOR_POINT); + + QRadioButton* aFaceBut= new QRadioButton(aConstructorBox); + aFaceBut->setIcon(iconReoriFace); + aConstructorGrpLayout->addWidget(aFaceBut); + myConstructorGrp->addButton(aFaceBut, CONSTRUCTOR_FACE); + + // Create other controls + + setObjectPixmap( "SMESH", tr( "ICON_SELECT" ) ); + + createObject( tr("OBJECT") , aFrame, EObject ); + createObject( tr("POINT") , aFrame, EPoint ); + createObject( tr("FACE") , aFrame, EFace ); + createObject( tr("DIRECTION"), aFrame, EDirection ); + setNameIndication( EObject, OneName ); + setNameIndication( EFace, OneName ); + setReadOnly( EFace, false ); + if ( QLineEdit* le = qobject_cast( objectWg( EFace, Control ) )) + le->setValidator( new SMESHGUI_IdValidator( this,1 )); + + const int width = aFaceBut->fontMetrics().width( tr("DIRECTION")); + objectWg( EDirection, Label )->setFixedWidth( width ); + objectWg( EObject , Label )->setFixedWidth( width ); + objectWg( EPoint , Label )->setFixedWidth( width ); + objectWg( EFace , Label )->setFixedWidth( width ); + + QLabel* aXLabel = new QLabel(tr("SMESH_X"), aFrame); + myX = new SMESHGUI_SpinBox(aFrame); + QLabel* aYLabel = new QLabel(tr("SMESH_Y"), aFrame); + myY = new SMESHGUI_SpinBox(aFrame); + QLabel* aZLabel = new QLabel(tr("SMESH_Z"), aFrame); + myZ = new SMESHGUI_SpinBox(aFrame); + + myX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myX->SetValue(0); + myY->SetValue(0); + myZ->SetValue(0); + + QLabel* aDXLabel = new QLabel(tr("SMESH_DX"), aFrame); + myDX = new SMESHGUI_SpinBox(aFrame); + QLabel* aDYLabel = new QLabel(tr("SMESH_DY"), aFrame); + myDY = new SMESHGUI_SpinBox(aFrame); + QLabel* aDZLabel = new QLabel(tr("SMESH_DZ"), aFrame); + myDZ = new SMESHGUI_SpinBox(aFrame); + myDX->SetValue(1); + myDY->SetValue(0); + myDZ->SetValue(0); + + myDX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myDY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + myDZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + + // Layouting + + QGroupBox* anObjectGrp = new QGroupBox(tr("FACES"), aFrame); + QHBoxLayout* anObjectGrpLayout = new QHBoxLayout(anObjectGrp); + anObjectGrpLayout->setMargin(MARGIN); + anObjectGrpLayout->setSpacing(SPACING); + anObjectGrpLayout->addWidget( objectWg( EObject, Label )); + anObjectGrpLayout->addWidget( objectWg( EObject, Btn )); + anObjectGrpLayout->addWidget( objectWg( EObject, Control )); + + myPointFrm = new QFrame(aFrame); + QHBoxLayout* aPointGrpLayout = new QHBoxLayout(myPointFrm); + aPointGrpLayout->setMargin(0); + objectWg( EPoint, Control )->hide(); + aPointGrpLayout->addWidget( objectWg( EPoint, Label ) ); + aPointGrpLayout->addWidget( objectWg( EPoint, Btn ) ); + aPointGrpLayout->addWidget( aXLabel ); + aPointGrpLayout->addWidget( myX ); + aPointGrpLayout->addWidget( aYLabel ); + aPointGrpLayout->addWidget( myY ); + aPointGrpLayout->addWidget( aZLabel ); + aPointGrpLayout->addWidget( myZ ); + + myFaceFrm = new QFrame(aFrame); + QHBoxLayout* aFaceGrpLayout = new QHBoxLayout(myFaceFrm); + aFaceGrpLayout->setMargin(0); + aFaceGrpLayout->addWidget( objectWg( EFace, Label ) ); + aFaceGrpLayout->addWidget( objectWg( EFace, Btn ) ); + aFaceGrpLayout->addWidget( objectWg( EFace, Control ) ); + + QFrame* aDirectFrm = new QFrame(aFrame); + QHBoxLayout* aDirectGrpLayout = new QHBoxLayout(aDirectFrm); + aDirectGrpLayout->setMargin(0); + objectWg( EDirection, Control )->hide(); + aDirectGrpLayout->addWidget( objectWg( EDirection, Label ) ); + aDirectGrpLayout->addWidget( objectWg( EDirection, Btn ) ); + aDirectGrpLayout->addWidget(aDXLabel ); + aDirectGrpLayout->addWidget(myDX ); + aDirectGrpLayout->addWidget(aDYLabel ); + aDirectGrpLayout->addWidget(myDY ); + aDirectGrpLayout->addWidget(aDZLabel ); + aDirectGrpLayout->addWidget(myDZ ); + + + QGroupBox* anOrientGrp = new QGroupBox(tr("ORIENTATION"), aFrame); + QVBoxLayout* anOrientGrpLayout = new QVBoxLayout ( anOrientGrp ); + anOrientGrpLayout->addWidget(myPointFrm); + anOrientGrpLayout->addWidget(myFaceFrm); + anOrientGrpLayout->addWidget(aDirectFrm); + + + QVBoxLayout* aLay = new QVBoxLayout(aFrame); + aLay->addWidget(aConstructorBox); + aLay->addWidget(anObjectGrp); + aLay->addWidget(anOrientGrp); + + connect( myConstructorGrp, SIGNAL(buttonClicked (int)), this, SLOT(constructorChange(int))); + + return aFrame; +} + +//================================================================================ +/*! + * \brief Show point or face + */ +//================================================================================ + +void SMESHGUI_ReorientFacesDlg::constructorChange(int id) +{ + if ( id == CONSTRUCTOR_FACE ) + { + myPointFrm->hide(); + myFaceFrm->show(); + activateObject( EFace ); + } + else + { + myFaceFrm->hide(); + myPointFrm->show(); + activateObject( EPoint ); + } +} + +//================================================================================ +/*! + * \brief Constructor +*/ +//================================================================================ + +SMESHGUI_ReorientFacesOp::SMESHGUI_ReorientFacesOp() + :SMESHGUI_SelectionOp( ActorSelection ) +{ + //myVectorPreview = 0; + myHelpFileName = "reorient_faces_page.html"; + + myDlg = new SMESHGUI_ReorientFacesDlg; + myDlg->constructorChange( CONSTRUCTOR_POINT ); + + // connect signals and slots + connect( myDlg->objectWg( EFace, LightApp_Dialog::Control ), SIGNAL(textChanged(const QString&)), + this, SLOT(onTextChange(const QString&))); + // connect(myDlg->myX, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + // connect(myDlg->myY, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + // connect(myDlg->myZ, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + // connect(myDlg->myDX, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + // connect(myDlg->myDY, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + // connect(myDlg->myDZ, SIGNAL (valueChanged(double)), this, SLOT(redisplayPreview())); + +} + +//======================================================================= +// function : startOperation() +// purpose : Init dialog fields, connect signals and slots, show dialog +//======================================================================= + +void SMESHGUI_ReorientFacesOp::startOperation() +{ + myObjectActor = 0; + + // init simulation with a current View + //if ( myVectorPreview ) delete myVectorPreview; + // myVectorPreview = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( getSMESHGUI() )); + // vtkProperty* aProp = vtkProperty::New(); + // aProp->SetRepresentationToWireframe(); + // aProp->SetColor(250, 0, 250); + // aProp->SetPointSize(5); + // aProp->SetLineWidth( SMESH::GetFloat("SMESH:element_width",1) + 1); + // myVectorPreview->GetActor()->SetProperty(aProp); + // aProp->Delete(); + + SMESHGUI_SelectionOp::startOperation(); + + myDlg->show(); + + mySelectionMode = 0; + myDlg->activateObject( EObject ); +} + +//================================================================================ +/*! + * \brief Stops operation + */ +//================================================================================ + +void SMESHGUI_ReorientFacesOp::stopOperation() +{ + //myVectorPreview->SetVisibility(false); + if ( myObjectActor ) { + myObjectActor->SetPointRepresentation(false); + SMESH::RepaintCurrentView(); + myObjectActor = 0; + } + SMESHGUI_SelectionOp::stopOperation(); + myDlg->deactivateAll(); +} + +//================================================================================ +/*! + * \brief Set selection mode corresponding to a pressed selection button + */ +//================================================================================ + +void SMESHGUI_ReorientFacesOp::onActivateObject( int what ) +{ + if ( what == mySelectionMode ) + return; + mySelectionMode = what; + switch ( mySelectionMode ) + { + case EPoint: + case EDirection: + SMESH::SetPointRepresentation(true); + setSelectionMode( NodeSelection ); + SMESH::SetPickable(); + break; + case EObject: + SMESH::SetPointRepresentation(false); + setSelectionMode( ActorSelection ); + break; + case EFace: + SMESH::SetPointRepresentation(false); + setSelectionMode( FaceSelection ); + if ( myObjectActor ) + SMESH::SetPickable( myObjectActor ); + else + SMESH::SetPickable(); + break; + } + SMESHGUI_SelectionOp::onActivateObject( what ); +} + +//================================================================================ +/*! + * \brief Creates a filter corresponding to a pressed selection button + */ +//================================================================================ + +SUIT_SelectionFilter* SMESHGUI_ReorientFacesOp::createFilter( const int what ) const +{ + switch ( what ) + { + case EObject: + { + QList filters; + filters.append( new SMESH_TypeFilter( MESH )); + filters.append( new SMESH_TypeFilter( SUBMESH_FACE )); + filters.append( new SMESH_TypeFilter( GROUP_FACE )); + return new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); + } + case EPoint: + { + QList filters; + filters.append( new SMESH_TypeFilter( IDSOURCE )); + filters.append( new SMESH_NumberFilter( "GEOM",TopAbs_VERTEX, 1, TopAbs_VERTEX )); + return new SMESH_LogicalFilter( filters, SMESH_LogicalFilter::LO_OR ); + } + case EFace: + case EDirection: + { + return new SMESH_TypeFilter( IDSOURCE ); + } + } + return NULL; +} + +//================================================================================ +/*! + * \brief get data from selection + */ +//================================================================================ + +void SMESHGUI_ReorientFacesOp::selectionDone() +{ + if ( !myDlg->isVisible() || !myDlg->isEnabled() ) + return; + + myDlg->clearSelection( mySelectionMode ); + + SALOME_ListIO aList; + selectionMgr()->selectedObjects(aList); + const int nbSelected = aList.Extent(); + if ( nbSelected == 0 ) + return; + + Handle(SALOME_InteractiveObject) anIO = aList.First(); + + try + { + switch ( mySelectionMode ) + { + case EObject: { // get an actor of object + + if ( nbSelected == 1 ) + { + myDlg->selectObject( EObject, anIO->getName(), 0, anIO->getEntry(), true ); + // typeById( aList.First()->getEntry(), + // SMESHGUI_SelectionOp::Object ), + myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() ); + } + break; + } + case EFace: { // get a face ID + + if ( nbSelected == 1 ) + { + TColStd_IndexedMapOfInteger faceIndices; + selector()->GetIndex( anIO, faceIndices ); + if ( faceIndices.Extent() == 1 ) + { + SMESH_Actor* savedActor = myObjectActor; + myObjectActor = 0; // to prevent work of onTextChange() + myDlg->setObjectText( EFace, QString("%1").arg( faceIndices(1) )); + myObjectActor = savedActor; + + if ( !myObjectActor ) + { + myDlg->selectObject( EObject, anIO->getName(), 0, anIO->getEntry(), true ); + // typeById( aList.First()->getEntry(), + // SMESHGUI_SelectionOp::Object ), + myObjectActor = SMESH::FindActorByEntry( anIO->getEntry() ); + } + } + } + break; + } + case EPoint: + case EDirection: { // set XYZ by selected nodes or vertices + + if ( mySelectionMode == EPoint && aList.Extent() > 1 ) + return; + + TColgp_SequenceOfXYZ points; + for( SALOME_ListIteratorOfListIO anIt( aList ); anIt.More(); anIt.Next() ) + { + anIO = anIt.Value(); + GEOM::GEOM_Object_var geom = SMESH::IObjectToInterface(anIO); + if ( !geom->_is_nil() ) { + TopoDS_Vertex aShape; + if ( GEOMBase::GetShape(geom, aShape) && aShape.ShapeType() == TopAbs_VERTEX ) { + gp_Pnt P = BRep_Tool::Pnt(aShape); + points.Append( P.XYZ() ); + } + } + else + { + TColStd_IndexedMapOfInteger nodeIndices; + selector()->GetIndex( anIO, nodeIndices ); + if ( nodeIndices.Extent() > 0 && nodeIndices.Extent() <=2 ) + { + if ( SMESH_Actor* aMeshActor = SMESH::FindActorByEntry(anIO->getEntry())) + if (SMDS_Mesh* aMesh = aMeshActor->GetObject()->GetMesh()) + { + if (const SMDS_MeshNode* aNode = aMesh->FindNode( nodeIndices(1))) + points.Append( gp_XYZ( aNode->X(), aNode->Y(), aNode->Z())); + if ( nodeIndices.Extent() == 2 ) + if (const SMDS_MeshNode* aNode = aMesh->FindNode( nodeIndices(2))) + points.Append( gp_XYZ( aNode->X(), aNode->Y(), aNode->Z())); + } + } + } + } + gp_XYZ xyz; + if ( points.Length() == 1 ) + xyz = points(1); + else if ( points.Length() == 2 ) + xyz = points(2) - points(1); + else + return; + if ( points.Length() == 1 && mySelectionMode == EPoint ) + { + myDlg->myX->SetValue( xyz.X() ); + myDlg->myY->SetValue( xyz.Y() ); + myDlg->myZ->SetValue( xyz.Z() ); + redisplayPreview(); + } + if ( mySelectionMode == EDirection ) + { + myDlg->myDX->SetValue( xyz.X() ); + myDlg->myDY->SetValue( xyz.Y() ); + myDlg->myDZ->SetValue( xyz.Z() ); + redisplayPreview(); + } + break; + } // case EPoint || EDirection + } // switch + } + catch (...) + { + } +} + +//================================================================================ +/*! + * \brief SLOT called when the face id is changed + */ +//================================================================================ + +void SMESHGUI_ReorientFacesOp::onTextChange( const QString& theText ) +{ + if( myObjectActor ) + { + sender()->blockSignals( true ); + if ( mySelectionMode != EFace ) + { + myDlg->activateObject( EFace ); + myDlg->setObjectText( EFace, theText ); + } + TColStd_MapOfInteger ids; + if ( !theText.isEmpty() && theText.toInt() > 0 ) + ids.Add( theText.toInt() ); + + SMESHGUI_SelectionOp::addOrRemoveIndex( myObjectActor->getIO(), ids, false ); + SMESHGUI_SelectionOp::highlight( myObjectActor->getIO(), true, true ); + sender()->blockSignals( false ); + } +} + +//================================================================================ +/*! + * \brief perform it's intention action: reorient faces of myObject + */ +//================================================================================ + +bool SMESHGUI_ReorientFacesOp::onApply() +{ + if( isStudyLocked() ) + return false; + + QString msg; + if ( !isValid( msg ) ) { // node id is invalid + if( !msg.isEmpty() ) + SUIT_MessageBox::warning( dlg(), tr( "SMESH_WRN_WARNING" ), msg ); + dlg()->show(); + return false; + } + + QStringList aParameters; + aParameters << myDlg->myDX->text(); + aParameters << myDlg->myDY->text(); + aParameters << myDlg->myDZ->text(); + aParameters << myDlg->myX->text(); + aParameters << myDlg->myY->text(); + aParameters << myDlg->myZ->text(); + + try { + SUIT_OverrideCursor wc; + SMESH::SMESH_Mesh_var aMesh = myObject->GetMesh(); + if ( aMesh->_is_nil() ) return false; + + SMESH::DirStruct direction; + direction.PS.x = myDlg->myDX->GetValue(); + direction.PS.y = myDlg->myDY->GetValue(); + direction.PS.z = myDlg->myDZ->GetValue(); + + long face = myDlg->objectText( EFace ).toInt(); + if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_POINT ) + face = -1; + + SMESH::PointStruct point; + point.x = myDlg->myX->GetValue(); + point.y = myDlg->myY->GetValue(); + point.z = myDlg->myZ->GetValue(); + + SMESH::SMESH_MeshEditor_var aMeshEditor = aMesh->GetMeshEditor(); + if (aMeshEditor->_is_nil()) return false; + + aMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + + int aResult = aMeshEditor->Reorient2D( myObject, direction, face, point ); + if (aResult) + { + SALOME_ListIO aList; + selectionMgr()->setSelectedObjects(aList,false); + SMESH::UpdateView(); + SMESHGUI::Modified(); + } + wc.suspend(); + SUIT_MessageBox::information(SMESHGUI::desktop(), tr("SMESH_INFORMATION"), + tr("NB_REORIENTED").arg(aResult)); + } + catch (const SALOME::SALOME_Exception& S_ex) { + SalomeApp_Tools::QtCatchCorbaException(S_ex); + } + catch (...) { + } + + return true; +} + +//================================================================================ +/*! + * \brief Check data validity + */ +//================================================================================ + +bool SMESHGUI_ReorientFacesOp::isValid( QString& msg ) +{ + // check object + QString objectEntry = myDlg->selectedObject( EObject ); + _PTR(SObject) pSObject = studyDS()->FindObjectID( objectEntry.toLatin1().data() ); + myObject = SMESH::SMESH_IDSource::_narrow( _CAST( SObject,pSObject )->GetObject() ); + if ( myObject->_is_nil() ) + { + msg = tr("NO_OBJECT_SELECTED"); + return false; + } + bool hasFaces = false; + SMESH::array_of_ElementType_var types = myObject->GetTypes(); + for ( size_t i = 0; i < types->length() && !hasFaces; ++i ) + hasFaces = ( types[i] == SMESH::FACE ); + if ( !hasFaces ) + { + msg = tr("NO_FACES"); + return false; + } + + // check vector + gp_Vec vec( myDlg->myDX->GetValue(), + myDlg->myDY->GetValue(), + myDlg->myDZ->GetValue() ); + if ( vec.Magnitude() < std::numeric_limits::min() ) + { + msg = tr("ZERO_SIZE_VECTOR"); + return false; + } + + // check face ID + if ( myDlg->myConstructorGrp->checkedId() == CONSTRUCTOR_FACE ) + { + int faceID = myDlg->objectText( EFace ).toInt(); + bool faceOK = ( faceID > 0 ); + if ( faceOK ) + { + if ( myObjectActor ) + { + faceOK = ( myObjectActor->GetObject()->GetElemDimension( faceID ) == 2 ); + } + else + { + SMESH::SMESH_Mesh_var aMesh = myObject->GetMesh(); + if ( !aMesh->_is_nil() ) + faceOK = ( aMesh->GetElementType( faceID, true ) == SMESH::FACE ); + } + } + if ( !faceOK ) + { + msg = tr("INVALID_FACE"); + return false; + } + } + + return true; +} + +//================================================================================ +/*! + * \brief Destructor +*/ +//================================================================================ + +SMESHGUI_ReorientFacesOp::~SMESHGUI_ReorientFacesOp() +{ + if ( myDlg ) delete myDlg; + //if ( myVectorPreview ) delete myVectorPreview; +} + +//================================================================================ +/*! + * \brief Gets dialog of this operation + * \retval LightApp_Dialog* - pointer to dialog of this operation + */ +//================================================================================ + +LightApp_Dialog* SMESHGUI_ReorientFacesOp::dlg() const +{ + return myDlg; +} + +//================================================================================ +/*! + * \brief update preview + */ +//================================================================================ + +void SMESHGUI_ReorientFacesOp::redisplayPreview() +{ +// SMESH::MeshPreviewStruct_var aMeshPreviewStruct; + +// bool moveShown = false; +// if ( myObjectActor) +// { +// const bool autoSearch = myDlg->myAutoSearchChkBox->isChecked(); +// const bool preview = myDlg->myPreviewChkBox->isChecked(); +// if ( autoSearch ) +// { +// myDlg->myCurrentX->SetValue(0); +// myDlg->myCurrentY->SetValue(0); +// myDlg->myCurrentZ->SetValue(0); +// myDlg->myDX->SetValue(0); +// myDlg->myDY->SetValue(0); +// myDlg->myDZ->SetValue(0); +// myDlg->myId->setText(""); +// } +// QString msg; +// if ( autoSearch || isValid( msg ) ) +// { +// try { +// SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(myObjectActor->getIO()); +// if (!aMesh->_is_nil()) { +// SMESH::SMESH_MeshEditor_var aPreviewer = aMesh->GetMeshEditPreviewer(); +// if (!aPreviewer->_is_nil()) +// { +// SUIT_OverrideCursor aWaitCursor; + +// int anId = 0; +// if ( autoSearch ) +// anId = aPreviewer->FindNodeClosestTo(myDlg->myX->GetValue(), +// myDlg->myY->GetValue(), +// myDlg->myZ->GetValue()); +// else +// anId = myDlg->myId->text().toInt(); + +// // find id and/or just compute preview +// aPreviewer->MoveNode(anId, +// myDlg->myX->GetValue(), +// myDlg->myY->GetValue(), +// myDlg->myZ->GetValue()); +// if ( autoSearch ) { // set found id +// QString idTxt("%1"); +// if ( anId > 0 ) +// idTxt = idTxt.arg( anId ); +// else +// idTxt = ""; +// myDlg->myId->setText( idTxt ); +// } + +// SMESH::double_array* aXYZ = aMesh->GetNodeXYZ( anId ); +// if( aXYZ && aXYZ->length() >= 3 ) +// { +// double x = aXYZ->operator[](0); +// double y = aXYZ->operator[](1); +// double z = aXYZ->operator[](2); +// double dx = myDlg->myX->GetValue() - x; +// double dy = myDlg->myY->GetValue() - y; +// double dz = myDlg->myZ->GetValue() - z; +// myDlg->myCurrentX->SetValue(x); +// myDlg->myCurrentY->SetValue(y); +// myDlg->myCurrentZ->SetValue(z); +// myDlg->myDX->SetValue(dx); +// myDlg->myDY->SetValue(dy); +// myDlg->myDZ->SetValue(dz); +// } + +// if ( preview ) { // fill preview data +// aMeshPreviewStruct = aPreviewer->GetPreviewData(); +// moveShown = ( anId > 0 ); +// } +// } +// } +// }catch (...) { +// } +// } +// } + +// if ( !moveShown ) +// { +// aMeshPreviewStruct = new SMESH::MeshPreviewStruct(); + +// aMeshPreviewStruct->nodesXYZ.length(1); +// aMeshPreviewStruct->nodesXYZ[0].x = myDlg->myX->GetValue(); +// aMeshPreviewStruct->nodesXYZ[0].y = myDlg->myY->GetValue(); +// aMeshPreviewStruct->nodesXYZ[0].z = myDlg->myZ->GetValue(); + +// aMeshPreviewStruct->elementTypes.length(1); +// aMeshPreviewStruct->elementTypes[0].SMDS_ElementType = SMESH::NODE; +// aMeshPreviewStruct->elementTypes[0].isPoly = false; +// aMeshPreviewStruct->elementTypes[0].nbNodesInElement = 1; + +// aMeshPreviewStruct->elementConnectivities.length(1); +// aMeshPreviewStruct->elementConnectivities[0] = 0; +// } + +// // display data +// if ( aMeshPreviewStruct.operator->() ) +// { +// myVectorPreview->SetData(aMeshPreviewStruct._retn()); +// } +// else +// { +// myVectorPreview->SetVisibility(false); +// } + +} diff --git a/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h b/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h new file mode 100644 index 000000000..aaea78340 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_ReorientFacesDlg.h @@ -0,0 +1,118 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESHGUI_ReorientFacesDlg.h +// Author : Edward AGAPOV, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_ReorientFacesDlg_H +#define SMESHGUI_ReorientFacesDlg_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" + +#include "SMESHGUI_Dialog.h" +#include "SMESHGUI_SelectionOp.h" + +class QButtonGroup; +class QLineEdit; +class SMESHGUI_SpinBox; +class SMESHGUI_ReorientFacesDlg; + +/*! + * \brief Operation to reorient faces acoording to vector + */ +class SMESHGUI_EXPORT SMESHGUI_ReorientFacesOp: public SMESHGUI_SelectionOp +{ + Q_OBJECT + +public: + SMESHGUI_ReorientFacesOp(); + virtual ~SMESHGUI_ReorientFacesOp(); + + virtual LightApp_Dialog* dlg() const; + +protected: + + virtual void startOperation(); + virtual void stopOperation(); + + virtual SUIT_SelectionFilter* createFilter( const int ) const; + virtual void selectionDone(); + + bool isValid( QString& ); + +protected slots: + virtual bool onApply(); + +private slots: + virtual void onActivateObject( int ); + void redisplayPreview(); + void onTextChange( const QString& ); + +private: + SMESHGUI_ReorientFacesDlg* myDlg; + + //SMESHGUI_MeshEditPreview* myVectorPreview; + SMESH_Actor* myObjectActor; + int mySelectionMode; + + SMESH::SMESH_IDSource_var myObject; +}; + +/*! + * \brief Dialog to reorient faces acoording to vector + */ + +class SMESHGUI_EXPORT SMESHGUI_ReorientFacesDlg : public SMESHGUI_Dialog +{ + Q_OBJECT + +public: + SMESHGUI_ReorientFacesDlg(); + +public slots: + void constructorChange(int id); + +private: + QWidget* createMainFrame( QWidget* ); + + QButtonGroup* myConstructorGrp; + QFrame* myFaceFrm; + QFrame* myPointFrm; + SMESHGUI_SpinBox* myX; + SMESHGUI_SpinBox* myY; + SMESHGUI_SpinBox* myZ; + //QPushButton* myIdBtn; + //QLineEdit* myId; + SMESHGUI_SpinBox* myDX; + SMESHGUI_SpinBox* myDY; + SMESHGUI_SpinBox* myDZ; + + QString myHelpFileName; + + friend class SMESHGUI_ReorientFacesOp; + + //private slots: + //void ButtonToggled( bool ); +}; + +#endif // SMESHGUI_ReorientFacesDlg_H diff --git a/src/SMESHGUI/SMESHGUI_RevolutionDlg.cxx b/src/SMESHGUI/SMESHGUI_RevolutionDlg.cxx index 355e01ec3..2d198bb68 100644 --- a/src/SMESHGUI/SMESHGUI_RevolutionDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_RevolutionDlg.cxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RevolutionDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. // SMESH includes -// + #include "SMESHGUI_RevolutionDlg.h" #include "SMESHGUI.h" @@ -88,12 +89,12 @@ // purpose : //================================================================================= SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), + : SMESHGUI_PreviewDlg( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), myVectorDefinition(NONE_SELECT), myFilterDlg( 0 ), - mySelectedObject(SMESH::SMESH_IDSource::_nil()) + mySelectedObject(SMESH::SMESH_IDSource::_nil()), + myActor(0) { mySimulation = new SMESHGUI_MeshEditPreview(SMESH::GetViewWindow( mySMESHGUI )); @@ -144,8 +145,9 @@ SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -229,7 +231,7 @@ SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) SpinBox_Tolerance = new SMESHGUI_SpinBox(GroupArguments); // Control for mesh preview - CheckBoxPreview = new QCheckBox(tr("PREVIEW"), GroupArguments); + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); // CheckBox for groups generation MakeGroupsCheck = new QCheckBox(tr("SMESH_MAKE_GROUPS"), GroupArguments); @@ -238,13 +240,13 @@ SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2); - GroupArgumentsLayout->addWidget(filterBtn, 0, 3); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 3); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 4); GroupArgumentsLayout->addWidget(GroupAxis, 2, 0, 1, 4); GroupArgumentsLayout->addWidget(GroupAngleBox, 3, 0, 1, 4); GroupArgumentsLayout->addWidget(TextLabelTolerance, 4, 0, 1, 2); GroupArgumentsLayout->addWidget(SpinBox_Tolerance, 4, 2, 1, 2); - GroupArgumentsLayout->addWidget(CheckBoxPreview, 5, 0, 1, 4); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 5, 0, 1, 4); GroupArgumentsLayout->addWidget(MakeGroupsCheck, 6, 0, 1, 4); /***************************************************************/ @@ -277,20 +279,20 @@ SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) SMESHGUI_RevolutionDlgLayout->addWidget(GroupButtons); /* Initialisations */ - SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); + SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); RadioButton3->setChecked(true); - SpinBox_Angle->RangeStepAndValidator(COORD_MIN, COORD_MAX, 5.0, 3); + SpinBox_Angle->RangeStepAndValidator(COORD_MIN, COORD_MAX, 5.0, "angle_precision"); SpinBox_NbSteps->setRange(1, 999999); - SpinBox_Tolerance->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, 6); + SpinBox_Tolerance->RangeStepAndValidator(0.0, COORD_MAX, 0.00001, "len_tol_precision"); RadioButton1->setChecked(true); @@ -349,7 +351,9 @@ SMESHGUI_RevolutionDlg::SMESHGUI_RevolutionDlg( SMESHGUI* theModule ) connect(SpinBox_Angle, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); connect(SpinBox_NbSteps, SIGNAL(valueChanged(int)), this, SLOT(toDisplaySimulation())); connect(SpinBox_Tolerance, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); - connect(CheckBoxPreview, SIGNAL(toggled(bool)), this, SLOT(onDisplaySimulation(bool))); + + //To Connect preview check box + connectPreviewControl(); connect(SpinBox_Angle, SIGNAL(textChanged(const QString&)), this, SLOT(onAngleTextChange(const QString&))); @@ -383,6 +387,7 @@ void SMESHGUI_RevolutionDlg::Init (bool ResetControls) LineEditElements->clear(); myElementsId = ""; myNbOkElements = 0; + myIDs.clear(); myActor = 0; myMesh = SMESH::SMESH_Mesh::_nil(); @@ -401,7 +406,7 @@ void SMESHGUI_RevolutionDlg::Init (bool ResetControls) CheckBoxMesh->setChecked(false); onSelectMesh(false); - CheckBoxPreview->setChecked(false); + myPreviewCheckBox->setChecked(false); onDisplaySimulation(false); } } @@ -412,14 +417,13 @@ void SMESHGUI_RevolutionDlg::Init (bool ResetControls) //================================================================================= void SMESHGUI_RevolutionDlg::ConstructorsClicked (int constructorId) { - //disconnect(mySelectionMgr, 0, this, 0); + disconnect(mySelectionMgr, 0, this, 0); - SALOME_ListIO io; + /* SALOME_ListIO io; mySelectionMgr->selectedObjects( io ); SALOME_ListIO aList; - mySelectionMgr->setSelectedObjects( aList ); -// LineEditElements->clear(); - myNbOkElements = 0; + mySelectionMgr->setSelectedObjects( aList );*/ + buttonApply->setEnabled(false); buttonOk->setEnabled(false); mySimulation->SetVisibility(false); @@ -447,8 +451,11 @@ void SMESHGUI_RevolutionDlg::ConstructorsClicked (int constructorId) if (!CheckBoxMesh->isChecked()) { + LineEditElements->clear(); + myIDs.clear(); + myNbOkElements = 0; if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(aSelMode); + aViewWindow->SetSelectionMode(aSelMode); } myEditCurrentArgument = (QWidget*)LineEditElements; @@ -457,8 +464,8 @@ void SMESHGUI_RevolutionDlg::ConstructorsClicked (int constructorId) if (CheckBoxMesh->isChecked()) onSelectMesh(true); - //connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); - mySelectionMgr->setSelectedObjects( io ); + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + // mySelectionMgr->setSelectedObjects( io ); } //================================================================================= @@ -491,7 +498,7 @@ bool SMESHGUI_RevolutionDlg::ClickOnApply() anAxis.vy = SpinBox_DY->GetValue(); anAxis.vz = SpinBox_DZ->GetValue(); - double anAngle = (SpinBox_Angle->GetValue())*PI/180; + double anAngle = (SpinBox_Angle->GetValue())*M_PI/180.; long aNbSteps = (long)SpinBox_NbSteps->value(); double aTolerance = SpinBox_Tolerance->GetValue(); @@ -513,44 +520,48 @@ bool SMESHGUI_RevolutionDlg::ClickOnApply() SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) { if( CheckBoxMesh->isChecked() ) { - if( GetConstructorId() == 0 ) - SMESH::ListOfGroups_var groups = - aMeshEditor->RotationSweepObject1DMakeGroups(mySelectedObject, anAxis, - anAngle, aNbSteps, aTolerance); - else - SMESH::ListOfGroups_var groups = - aMeshEditor->RotationSweepObject2DMakeGroups(mySelectedObject, anAxis, - anAngle, aNbSteps, aTolerance); - } - else - SMESH::ListOfGroups_var groups = - aMeshEditor->RotationSweepMakeGroups(anElementsId.inout(), anAxis, - anAngle, aNbSteps, aTolerance); + if( GetConstructorId() == 0 ) + SMESH::ListOfGroups_var groups = + aMeshEditor->RotationSweepObject1DMakeGroups(mySelectedObject, anAxis, + anAngle, aNbSteps, aTolerance); + else + SMESH::ListOfGroups_var groups = + aMeshEditor->RotationSweepObject2DMakeGroups(mySelectedObject, anAxis, + anAngle, aNbSteps, aTolerance); + } + else + SMESH::ListOfGroups_var groups = + aMeshEditor->RotationSweepMakeGroups(anElementsId.inout(), anAxis, + anAngle, aNbSteps, aTolerance); } else { if( CheckBoxMesh->isChecked() ) { - if( GetConstructorId() == 0 ) - aMeshEditor->RotationSweepObject1D(mySelectedObject, anAxis, anAngle, aNbSteps, aTolerance); - else - aMeshEditor->RotationSweepObject2D(mySelectedObject, anAxis, anAngle, aNbSteps, aTolerance); - } - else - aMeshEditor->RotationSweep(anElementsId.inout(), anAxis, anAngle, aNbSteps, aTolerance); + if( GetConstructorId() == 0 ) + aMeshEditor->RotationSweepObject1D(mySelectedObject, anAxis, anAngle, aNbSteps, aTolerance); + else + aMeshEditor->RotationSweepObject2D(mySelectedObject, anAxis, anAngle, aNbSteps, aTolerance); + } + else + aMeshEditor->RotationSweep(anElementsId.inout(), anAxis, anAngle, aNbSteps, aTolerance); } - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); } catch (...) { } SMESH::UpdateView(); + SMESH::Update(myIO, SMESH::eDisplay); if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) mySMESHGUI->updateObjBrowser(true); // new groups may appear Init(false); ConstructorsClicked(GetConstructorId()); mySelectedObject = SMESH::SMESH_IDSource::_nil(); SelectionIntoArgument(); + + SMESHGUI::Modified(); } return true; @@ -572,19 +583,14 @@ void SMESHGUI_RevolutionDlg::ClickOnOk() //================================================================================= void SMESHGUI_RevolutionDlg::ClickOnCancel() { - disconnect(mySelectionMgr, 0, this, 0); - mySelectionMgr->clearFilters(); - //mySelectionMgr->clearSelected(); - if (SMESH::GetCurrentVtkView()) { - SMESH::RemoveFilters(); // PAL6938 -- clean all mesh entity filters - SMESH::SetPointRepresentation(false); - } - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); - mySMESHGUI->ResetState(); reject(); } +void SMESHGUI_RevolutionDlg::reject() +{ + close(); +} + //================================================================================= // function : ClickOnHelp() // purpose : @@ -602,10 +608,10 @@ void SMESHGUI_RevolutionDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -653,15 +659,15 @@ void SMESHGUI_RevolutionDlg::onTextChange (const QString& theNewText) QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - myNbOkElements++; + const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); + if (e) + newIndices.Add(e->GetID()); + myNbOkElements++; } mySelector->AddOrRemoveIndex(myActor->getIO(), newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); + aViewWindow->highlight( myActor->getIO(), true, true ); myElementsId = theNewText; } @@ -685,7 +691,6 @@ void SMESHGUI_RevolutionDlg::SelectionIntoArgument() if (myBusy) return; // clear - myActor = 0; QString aString = ""; myBusy = true; @@ -704,24 +709,27 @@ void SMESHGUI_RevolutionDlg::SelectionIntoArgument() const SALOME_ListIO& aList = mySelector->StoredIObjects(); int nbSel = aList.Extent(); - if (nbSel != 1) + if (nbSel != 1) return; Handle(SALOME_InteractiveObject) IO = aList.First(); - myMesh = SMESH::GetMeshByIO(IO); - if (myMesh->_is_nil()) + SMESH::SMESH_Mesh_var aMeshVar = SMESH::GetMeshByIO(IO); + if (aMeshVar->_is_nil()) return; - myActor = SMESH::FindActorByObject(myMesh); - if (!myActor) - myActor = SMESH::FindActorByEntry(IO->getEntry()); - if (!myActor) + SMESH_Actor* anActor = SMESH::FindActorByObject(aMeshVar); + if (!anActor) + anActor = SMESH::FindActorByEntry(IO->getEntry()); + if (!anActor && !CheckBoxMesh->isChecked()) return; int aNbUnits = 0; if (myEditCurrentArgument == (QWidget*)LineEditElements) { myElementsId = ""; + myMesh = aMeshVar; + myActor = anActor; + myIO = IO; // MakeGroups is available if there are groups if ( myMesh->NbGroups() == 0 ) { @@ -742,12 +750,12 @@ void SMESHGUI_RevolutionDlg::SelectionIntoArgument() aNbUnits = SMESH::GetNameOfSelectedElements(mySelector, IO, aString); myElementsId = aString; if (aNbUnits < 1) - return; + return; } myNbOkElements = true; } else { - SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); + SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); if (!aMesh) return; @@ -832,20 +840,20 @@ void SMESHGUI_RevolutionDlg::SetEditCurrentArgument() SMESH::SetPointRepresentation(false); if (CheckBoxMesh->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { int aConstructorId = GetConstructorId(); if (aConstructorId == 0) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); + } else if (aConstructorId == 1) - { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + } } } else if (send == SelectPointButton) { myEditCurrentArgument = (QWidget*)SpinBox_X; @@ -909,17 +917,16 @@ void SMESHGUI_RevolutionDlg::enterEvent (QEvent*) void SMESHGUI_RevolutionDlg::closeEvent (QCloseEvent*) { /* same than click on cancel button */ - ClickOnCancel(); -} - -//======================================================================= -// function : hideEvent() -// purpose : caused by ESC key -//======================================================================= -void SMESHGUI_RevolutionDlg::hideEvent (QHideEvent*) -{ - if (!isMinimized()) - ClickOnCancel(); + disconnect(mySelectionMgr, 0, this, 0); + mySelectionMgr->clearFilters(); + //mySelectionMgr->clearSelected(); + if (SMESH::GetCurrentVtkView()) { + SMESH::RemoveFilters(); // PAL6938 -- clean all mesh entity filters + SMESH::SetPointRepresentation(false); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySMESHGUI->ResetState(); } //======================================================================= @@ -928,10 +935,13 @@ void SMESHGUI_RevolutionDlg::hideEvent (QHideEvent*) //======================================================================= void SMESHGUI_RevolutionDlg::onSelectMesh (bool toSelectMesh) { - if (toSelectMesh) + if (toSelectMesh) { + myIDs = LineEditElements->text(); TextLabelElements->setText(tr("SMESH_NAME")); + } else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); if (myEditCurrentArgument != LineEditElements) { LineEditElements->clear(); @@ -952,13 +962,13 @@ void SMESHGUI_RevolutionDlg::onSelectMesh (bool toSelectMesh) int aConstructorId = GetConstructorId(); if (aConstructorId == 0) { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(EdgeSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(EdgeSelection); } - else if (aConstructorId == 0) + else if (aConstructorId == 1) { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); } LineEditElements->setReadOnly(false); @@ -968,6 +978,9 @@ void SMESHGUI_RevolutionDlg::onSelectMesh (bool toSelectMesh) } SelectionIntoArgument(); + + if (!toSelectMesh) + LineEditElements->setText( myIDs ); } //================================================================================= @@ -986,8 +999,8 @@ int SMESHGUI_RevolutionDlg::GetConstructorId() bool SMESHGUI_RevolutionDlg::IsAxisOk() { return (SpinBox_DX->GetValue() != 0 || - SpinBox_DY->GetValue() != 0 || - SpinBox_DZ->GetValue() != 0); + SpinBox_DY->GetValue() != 0 || + SpinBox_DZ->GetValue() != 0); } //================================================================================= @@ -1022,22 +1035,13 @@ void SMESHGUI_RevolutionDlg::keyPressEvent( QKeyEvent* e ) } } -//================================================================================= -// function : toDisplaySimulation() -// purpose : -//================================================================================= -void SMESHGUI_RevolutionDlg::toDisplaySimulation() -{ - onDisplaySimulation(true); -} - //================================================================================= // function : onDisplaySimulation() -// purpose : +// purpose : Show/Hide preview //================================================================================= void SMESHGUI_RevolutionDlg::onDisplaySimulation(bool toDisplayPreview) { - if (CheckBoxPreview->isChecked() && toDisplayPreview) + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { //display preview if (myNbOkElements && IsAxisOk()) @@ -1048,7 +1052,7 @@ void SMESHGUI_RevolutionDlg::onDisplaySimulation(bool toDisplayPreview) anElementsId->length(aListElementsId.count()); for (int i = 0; i < aListElementsId.count(); i++) - anElementsId[i] = aListElementsId[i].toInt(); + anElementsId[i] = aListElementsId[i].toInt(); SMESH::AxisStruct anAxis; @@ -1058,33 +1062,33 @@ void SMESHGUI_RevolutionDlg::onDisplaySimulation(bool toDisplayPreview) anAxis.vx = SpinBox_DX->GetValue(); anAxis.vy = SpinBox_DY->GetValue(); anAxis.vz = SpinBox_DZ->GetValue(); - - double anAngle = (SpinBox_Angle->GetValue())*PI/180; + + double anAngle = (SpinBox_Angle->GetValue())*M_PI/180.; long aNbSteps = (long)SpinBox_NbSteps->value(); double aTolerance = SpinBox_Tolerance->GetValue(); if (GroupAngle->checkedId() == 1) - anAngle = anAngle/aNbSteps; + anAngle = anAngle/aNbSteps; try { - SUIT_OverrideCursor aWaitCursor; - SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); if( CheckBoxMesh->isChecked() ) { - if( GetConstructorId() == 0 ) - aMeshEditor->RotationSweepObject1D(mySelectedObject, anAxis, - anAngle, aNbSteps, aTolerance); - else - aMeshEditor->RotationSweepObject2D(mySelectedObject, anAxis, - anAngle, aNbSteps, aTolerance); - } - else - aMeshEditor->RotationSweep(anElementsId.inout(), - anAxis, - anAngle, - aNbSteps, - aTolerance); - SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); - mySimulation->SetData(aMeshPreviewStruct._retn()); + if( GetConstructorId() == 0 ) + aMeshEditor->RotationSweepObject1D(mySelectedObject, anAxis, + anAngle, aNbSteps, aTolerance); + else + aMeshEditor->RotationSweepObject2D(mySelectedObject, anAxis, + anAngle, aNbSteps, aTolerance); + } + else + aMeshEditor->RotationSweep(anElementsId.inout(), + anAxis, + anAngle, + aNbSteps, + aTolerance); + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); } catch (...) {} } else @@ -1117,6 +1121,8 @@ void SMESHGUI_RevolutionDlg::onSelectVectorMenu( QAction* action){ if(!action) return; + disconnect(mySelectionMgr, 0, this, 0); + switch(myMenuActions[action]) { case POINT_SELECT: SMESH::SetPointRepresentation(true); @@ -1144,6 +1150,12 @@ void SMESHGUI_RevolutionDlg::onSelectVectorMenu( QAction* action){ //================================================================================= void SMESHGUI_RevolutionDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) { QList types; diff --git a/src/SMESHGUI/SMESHGUI_RevolutionDlg.h b/src/SMESHGUI/SMESHGUI_RevolutionDlg.h index a4c3fc111..91d1e4268 100644 --- a/src/SMESHGUI/SMESHGUI_RevolutionDlg.h +++ b/src/SMESHGUI/SMESHGUI_RevolutionDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RevolutionDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -28,9 +29,12 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_PreviewDlg.h" + +// SALOME GUI includes +#include // Qt includes -#include #include // IDL includes @@ -62,7 +66,7 @@ class QAction; // class : SMESHGUI_RevolutionDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_RevolutionDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_RevolutionDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -70,26 +74,27 @@ public: SMESHGUI_RevolutionDlg( SMESHGUI* ); ~SMESHGUI_RevolutionDlg(); + void reject(); + private: enum {NONE_SELECT, POINT_SELECT, FACE_SELECT}; void Init( bool = true); void closeEvent( QCloseEvent* ); void enterEvent( QEvent* ); /* mouse enter the QWidget */ - void hideEvent( QHideEvent* ); /* ESC key */ void keyPressEvent( QKeyEvent* ); int GetConstructorId(); bool IsAxisOk(); bool isValid(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ int myNbOkElements; /* to check when elements are defined */ QString myElementsId; QWidget* myEditCurrentArgument; /* Current argument */ SVTK_Selector* mySelector; + Handle(SALOME_InteractiveObject) myIO; SMESH::SMESH_IDSource_var mySelectedObject; @@ -120,7 +125,6 @@ private: QButtonGroup* GroupAngle; QRadioButton* RadioButton3; QRadioButton* RadioButton4; - QCheckBox* CheckBoxPreview; QLabel* TextLabelPoint; QPushButton* SelectPointButton; @@ -152,8 +156,13 @@ private: QString myHelpFileName; + QString myIDs; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; + +protected slots: + virtual void onDisplaySimulation( bool ); private slots: void ConstructorsClicked( int ); @@ -169,8 +178,6 @@ private slots: void onAngleTextChange( const QString& ); void onSelectMesh( bool ); void onVectorChanged(); - void toDisplaySimulation(); - void onDisplaySimulation( bool ); void onSelectVectorMenu( QAction* ); void onSelectVectorButton(); void setFilters(); diff --git a/src/SMESHGUI/SMESHGUI_RotationDlg.cxx b/src/SMESHGUI/SMESHGUI_RotationDlg.cxx index 1e7010974..f257c3c87 100644 --- a/src/SMESHGUI/SMESHGUI_RotationDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_RotationDlg.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_RotationDlg.cxx -// Author : Michael ZORIN, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_RotationDlg.cxx +// Author : Michael ZORIN, Open CASCADE S.A.S. +// SMESH includes + #include "SMESHGUI_RotationDlg.h" #include "SMESHGUI.h" @@ -33,6 +32,7 @@ #include "SMESHGUI_MeshUtils.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" #include #include @@ -83,13 +83,16 @@ enum { MOVE_ELEMS_BUTTON = 0, COPY_ELEMS_BUTTON, MAKE_MESH_BUTTON }; //!< action #define SPACING 8 #define MARGIN 11 +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //================================================================================= // class : SMESHGUI_RotationDlg() // purpose : //================================================================================= -SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), +SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) : + SMESHGUI_PreviewDlg( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), myFilterDlg(0), mySelectedObject(SMESH::SMESH_IDSource::_nil()) @@ -133,8 +136,9 @@ SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) SelectElementsButton->setIcon(image1); LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -212,10 +216,14 @@ SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) // Name of a mesh to create LineEditNewMesh = new QLineEdit(GroupArguments); + + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2, 1, 1); - GroupArgumentsLayout->addWidget(filterBtn, 0, 3); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 3); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 4); GroupArgumentsLayout->addWidget(GroupAxis, 2, 0, 1, 4); GroupArgumentsLayout->addWidget(TextLabelAngle, 3, 0, 1, 2); @@ -223,6 +231,8 @@ SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) GroupArgumentsLayout->addWidget(ActionBox, 4, 0, 3, 3); GroupArgumentsLayout->addWidget(MakeGroupsCheck, 5, 3); GroupArgumentsLayout->addWidget(LineEditNewMesh, 6, 3); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 7, 0); + /***************************************************************/ GroupButtons = new QGroupBox(this); @@ -254,14 +264,14 @@ SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) SMESHGUI_RotationDlgLayout->addWidget(GroupButtons); /* Initialisations */ - SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); + SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); - SpinBox_Angle->RangeStepAndValidator(-360.0, +360.0, 5.0, 3); + SpinBox_Angle->RangeStepAndValidator(-360.0, +360.0, 5.0, "angle_precision"); myConstructorId = 0; RadioButton1->setChecked(true); @@ -307,6 +317,17 @@ SMESHGUI_RotationDlg::SMESHGUI_RotationDlg( SMESHGUI* theModule ) connect(CheckBoxMesh, SIGNAL(toggled(bool)), SLOT(onSelectMesh(bool))); connect(ActionGroup, SIGNAL(buttonClicked(int)), SLOT(onActionClicked(int))); + connect(SpinBox_X, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Y, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Z, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DX, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DY, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DZ, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Angle, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + //To Connect preview check box + connectPreviewControl(); + onActionClicked(MOVE_ELEMS_BUTTON); } @@ -354,6 +375,9 @@ void SMESHGUI_RotationDlg::Init (bool ResetControls) ActionGroup->button( MOVE_ELEMS_BUTTON )->setChecked(true); CheckBoxMesh->setChecked(false); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); + // MakeGroupsCheck->setChecked(false); // MakeGroupsCheck->setEnabled(false); // onSelectMesh(false); @@ -392,7 +416,7 @@ bool SMESHGUI_RotationDlg::ClickOnApply() anAxis.vy = SpinBox_DY->GetValue(); anAxis.vz = SpinBox_DZ->GetValue(); - double anAngle = (SpinBox_Angle->GetValue())*PI/180; + double anAngle = (SpinBox_Angle->GetValue())*M_PI/180.; QStringList aParameters; aParameters << SpinBox_X->text(); @@ -405,17 +429,19 @@ bool SMESHGUI_RotationDlg::ClickOnApply() int actionButton = ActionGroup->checkedId(); bool makeGroups = ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ); + QStringList anEntryList; try { SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + + myMesh->SetParameters(aParameters.join(":").toLatin1().constData()); + switch ( actionButton ) { case MOVE_ELEMS_BUTTON: if(CheckBoxMesh->isChecked()) aMeshEditor->RotateObject(mySelectedObject, anAxis, anAngle, false); else aMeshEditor->Rotate(anElementsId, anAxis, anAngle, false); - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); break; case COPY_ELEMS_BUTTON: if ( makeGroups ) { @@ -428,33 +454,47 @@ bool SMESHGUI_RotationDlg::ClickOnApply() else { if(CheckBoxMesh->isChecked()) aMeshEditor->RotateObject(mySelectedObject, anAxis, anAngle, true); - else + else aMeshEditor->Rotate(anElementsId, anAxis, anAngle, true); } - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); break; - case MAKE_MESH_BUTTON: + case MAKE_MESH_BUTTON: { SMESH::SMESH_Mesh_var mesh; - if(CheckBoxMesh->isChecked()) + if (CheckBoxMesh->isChecked()) mesh = aMeshEditor->RotateObjectMakeMesh(mySelectedObject, anAxis, anAngle, makeGroups, LineEditNewMesh->text().toLatin1().data()); - else + else mesh = aMeshEditor->RotateMakeMesh(anElementsId, anAxis, anAngle, makeGroups, LineEditNewMesh->text().toLatin1().data()); - if( !mesh->_is_nil()) - mesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); + if (!mesh->_is_nil()) { + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( mesh ) ) + anEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + mesh->UnRegister(); +#endif + } + break; + } } } catch (...) { } SMESH::UpdateView(); - if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() || - actionButton == MAKE_MESH_BUTTON ) + if ( ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) || + actionButton == MAKE_MESH_BUTTON ) { mySMESHGUI->updateObjBrowser(true); // new groups may appear + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + } Init(false); mySelectedObject = SMESH::SMESH_IDSource::_nil(); SelectionIntoArgument(); + + SMESHGUI::Modified(); } return true; @@ -466,6 +506,7 @@ bool SMESHGUI_RotationDlg::ClickOnApply() //================================================================================= void SMESHGUI_RotationDlg::ClickOnOk() { + setIsApplyAndClose( true ); if( ClickOnApply() ) ClickOnCancel(); } @@ -496,7 +537,7 @@ void SMESHGUI_RotationDlg::ClickOnCancel() void SMESHGUI_RotationDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { QString platform; @@ -506,10 +547,10 @@ void SMESHGUI_RotationDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -538,21 +579,21 @@ void SMESHGUI_RotationDlg::onTextChange (const QString& theNewText) if (aMesh) { if (send == LineEditElements) { Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); - + TColStd_MapOfInteger newIndices; - + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - myNbOkElements++; + const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); + if (e) + newIndices.Add(e->GetID()); + myNbOkElements++; } mySelector->AddOrRemoveIndex( anIO, newIndices, false ); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( anIO, true, true ); - + aViewWindow->highlight( anIO, true, true ); + myElementsId = theNewText; } } @@ -605,7 +646,7 @@ void SMESHGUI_RotationDlg::SelectionIntoArgument() myActor = SMESH::FindActorByObject(myMesh); if (!myActor) myActor = SMESH::FindActorByEntry(IO->getEntry()); - if (!myActor) + if (!myActor && !CheckBoxMesh->isChecked()) return; int aNbUnits = 0; @@ -645,7 +686,7 @@ void SMESHGUI_RotationDlg::SelectionIntoArgument() } else if (!SMESH::IObjectToInterface(IO)->_is_nil()) { //SUBMESH // get submesh SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(IO); - + // get IDs from submesh SMESH::long_array_var anElementsIds = new SMESH::long_array; anElementsIds = aSubMesh->GetElementsId(); @@ -711,7 +752,7 @@ void SMESHGUI_RotationDlg::SelectionIntoArgument() LineEditElements->setText(aString); LineEditElements->repaint(); LineEditElements->setEnabled(false); // to update lineedit IPAL 19809 - LineEditElements->setEnabled(true); + LineEditElements->setEnabled(true); setNewMeshName(); } myBusy = false; @@ -721,6 +762,7 @@ void SMESHGUI_RotationDlg::SelectionIntoArgument() buttonOk->setEnabled(true); buttonApply->setEnabled(true); } + onDisplaySimulation(true); } //================================================================================= @@ -743,23 +785,23 @@ void SMESHGUI_RotationDlg::SetEditCurrentArgument() myEditCurrentArgument = (QWidget*)LineEditElements; SMESH::SetPointRepresentation(false); if (CheckBoxMesh->isChecked()) { - if ( aViewWindow ) - aViewWindow->SetSelectionMode(ActorSelection); + if ( aViewWindow ) + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { - if ( aViewWindow ) - aViewWindow->SetSelectionMode( CellSelection ); - } + if ( aViewWindow ) + aViewWindow->SetSelectionMode( CellSelection ); + } } else if (send == SelectPointButton) { myEditCurrentArgument = (QWidget*)SpinBox_X; SMESH::SetPointRepresentation(true); - if ( aViewWindow ) - aViewWindow->SetSelectionMode( NodeSelection ); + if ( aViewWindow ) + aViewWindow->SetSelectionMode( NodeSelection ); } else if (send == SelectVectorButton) { myEditCurrentArgument = (QWidget*)SpinBox_DX; SMESH::SetPointRepresentation(true); - if ( aViewWindow ) - aViewWindow->SetSelectionMode( NodeSelection ); + if ( aViewWindow ) + aViewWindow->SetSelectionMode( NodeSelection ); } break; } @@ -845,6 +887,7 @@ void SMESHGUI_RotationDlg::onSelectMesh (bool toSelectMesh) TextLabelElements->setText(tr("SMESH_NAME")); else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); if (myEditCurrentArgument != LineEditElements) { LineEditElements->clear(); @@ -866,6 +909,7 @@ void SMESHGUI_RotationDlg::onSelectMesh (bool toSelectMesh) LineEditElements->setReadOnly(false); LineEditElements->setValidator(myIdValidator); onTextChange(LineEditElements->text()); + hidePreview(); } SelectionIntoArgument(); @@ -878,8 +922,8 @@ void SMESHGUI_RotationDlg::onSelectMesh (bool toSelectMesh) bool SMESHGUI_RotationDlg::IsAxisOk() { return (SpinBox_DX->GetValue() != 0 || - SpinBox_DY->GetValue() != 0 || - SpinBox_DZ->GetValue() != 0); + SpinBox_DY->GetValue() != 0 || + SpinBox_DZ->GetValue() != 0); } //================================================================================= @@ -928,6 +972,7 @@ void SMESHGUI_RotationDlg::onActionClicked(int button) break; } setNewMeshName(); + toDisplaySimulation(); } //======================================================================= @@ -974,6 +1019,12 @@ void SMESHGUI_RotationDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_RotationDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); @@ -1010,3 +1061,52 @@ bool SMESHGUI_RotationDlg::isValid() } return true; } + + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_RotationDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + if(myNbOkElements && isValid() && IsAxisOk()) { + QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + SMESH::long_array_var anElementsId = new SMESH::long_array; + + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::AxisStruct anAxis; + + anAxis.x = SpinBox_X->GetValue(); + anAxis.y = SpinBox_Y->GetValue(); + anAxis.z = SpinBox_Z->GetValue();; + anAxis.vx = SpinBox_DX->GetValue(); + anAxis.vy = SpinBox_DY->GetValue(); + anAxis.vz = SpinBox_DZ->GetValue(); + double anAngle = (SpinBox_Angle->GetValue())*M_PI/180.; + + try { + SUIT_OverrideCursor aWaitCursor; + bool copy = ( ActionGroup->checkedId() == COPY_ELEMS_BUTTON || + ActionGroup->checkedId() == MAKE_MESH_BUTTON ); + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + if(CheckBoxMesh->isChecked()) + aMeshEditor->RotateObject(mySelectedObject, anAxis, anAngle, copy); + else + aMeshEditor->Rotate(anElementsId, anAxis, anAngle, copy); + + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + } catch (...) { + hidePreview(); + } + } + else { + hidePreview(); + } + } else { + hidePreview(); + } +} diff --git a/src/SMESHGUI/SMESHGUI_RotationDlg.h b/src/SMESHGUI/SMESHGUI_RotationDlg.h index 4dd572f29..a4e6b4af2 100644 --- a/src/SMESHGUI/SMESHGUI_RotationDlg.h +++ b/src/SMESHGUI/SMESHGUI_RotationDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_RotationDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -28,9 +29,8 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_PreviewDlg.h" -// Qt includes -#include // IDL includes #include @@ -56,7 +56,7 @@ class SMESH_LogicalFilter; // class : SMESHGUI_RotationDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_RotationDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_RotationDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -75,7 +75,6 @@ private: bool isValid(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ int myNbOkElements; /* to check when elements are defined */ @@ -131,8 +130,12 @@ private: QString myHelpFileName; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; +protected slots: + virtual void onDisplaySimulation( bool ); + private slots: void ClickOnOk(); void ClickOnCancel(); diff --git a/src/SMESHGUI/SMESHGUI_ScaleDlg.cxx b/src/SMESHGUI/SMESHGUI_ScaleDlg.cxx new file mode 100644 index 000000000..1e3cd9f95 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_ScaleDlg.cxx @@ -0,0 +1,1136 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESHGUI_ScaleDlg.cxx +// Author : Michael ZORIN, Open CASCADE S.A.S. +// SMESH includes + +#include "SMESHGUI_ScaleDlg.h" + +#include "SMESHGUI.h" +#include "SMESHGUI_SpinBox.h" +#include "SMESHGUI_Utils.h" +#include "SMESHGUI_VTKUtils.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESHGUI_IdValidator.h" +#include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" + +#include +#include +#include +#include + +// SALOME GUI includes +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +// SALOME KERNEL includes +#include + +// OCCT includes +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Group) +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) + +enum { MOVE_ELEMS_BUTTON = 0, COPY_ELEMS_BUTTON, MAKE_MESH_BUTTON }; //!< action type + +/*! + \class BusyLocker + \brief Simple 'busy state' flag locker. + \internal +*/ + +class BusyLocker +{ +public: + //! Constructor. Sets passed boolean flag to \c true. + BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; } + //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false. + ~BusyLocker() { myBusy = false; } +private: + bool& myBusy; //! External 'busy state' boolean flag +}; + +#define SPACING 6 +#define MARGIN 11 + +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + +//================================================================================= +// class : SMESHGUI_ScaleDlg() +// purpose : +//================================================================================= +SMESHGUI_ScaleDlg::SMESHGUI_ScaleDlg( SMESHGUI* theModule ) : + SMESHGUI_PreviewDlg( theModule ), + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myFilterDlg(0), + mySelectedObject(SMESH::SMESH_IDSource::_nil()) +{ + QPixmap image0 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_MESH_SCALE"))); + QPixmap image1 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_DLG_SCALE_ALONG_AXES"))); + QPixmap image2 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SELECT"))); + + setModal(false); + setAttribute(Qt::WA_DeleteOnClose, true); + setWindowTitle(tr("SMESH_SCALE_TITLE")); + setSizeGripEnabled(true); + + QVBoxLayout* SMESHGUI_ScaleDlgLayout = new QVBoxLayout(this); + SMESHGUI_ScaleDlgLayout->setSpacing(SPACING); + SMESHGUI_ScaleDlgLayout->setMargin(MARGIN); + + /***************************************************************/ + ConstructorsBox = new QGroupBox(tr("SMESH_SCALE"), this); + GroupConstructors = new QButtonGroup(this); + QHBoxLayout* ConstructorsBoxLayout = new QHBoxLayout(ConstructorsBox); + ConstructorsBoxLayout->setSpacing(SPACING); + ConstructorsBoxLayout->setMargin(MARGIN); + + RadioButton1= new QRadioButton(ConstructorsBox); + RadioButton1->setIcon(image0); + RadioButton2= new QRadioButton(ConstructorsBox); + RadioButton2->setIcon(image1); + + ConstructorsBoxLayout->addWidget(RadioButton1); + ConstructorsBoxLayout->addWidget(RadioButton2); + GroupConstructors->addButton(RadioButton1, 0); + GroupConstructors->addButton(RadioButton2, 1); + + /***************************************************************/ + GroupArguments = new QGroupBox(tr("SMESH_ARGUMENTS"), this); + QGridLayout* GroupArgumentsLayout = new QGridLayout(GroupArguments); + GroupArgumentsLayout->setSpacing(SPACING); + GroupArgumentsLayout->setMargin(MARGIN); + + myIdValidator = new SMESHGUI_IdValidator(this); + + // Controls for elements selection + TextLabelElements = new QLabel(tr("SMESH_ID_ELEMENTS"), GroupArguments); + SelectElementsButton = new QPushButton(GroupArguments); + SelectElementsButton->setIcon(image2); + LineEditElements = new QLineEdit(GroupArguments); + LineEditElements->setValidator(myIdValidator); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + + // Control for the whole mesh selection + CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); + + // Controls for vector and points selection + TextLabel1 = new QLabel(tr("SMESH_BASE_POINT"), GroupArguments); + SelectButton1 = new QPushButton(GroupArguments); + SelectButton1->setIcon(image2); + + TextLabel1_1 = new QLabel(tr("SMESH_X"), GroupArguments); + SpinBox1_1 = new SMESHGUI_SpinBox(GroupArguments); + TextLabel1_2 = new QLabel(tr("SMESH_Y"), GroupArguments); + SpinBox1_2 = new SMESHGUI_SpinBox(GroupArguments); + TextLabel1_3 = new QLabel(tr("SMESH_Z"), GroupArguments); + SpinBox1_3 = new SMESHGUI_SpinBox(GroupArguments); + + TextLabel2 = new QLabel(tr("SMESH_SCALE_FACTOR"), GroupArguments); + SpinBox_FX = new SMESHGUI_SpinBox(GroupArguments); + + TextLabel3 = new QLabel(tr("SMESH_SCALE_FACTOR_Y"), GroupArguments); + SpinBox_FY = new SMESHGUI_SpinBox(GroupArguments); + + TextLabel4 = new QLabel(tr("SMESH_SCALE_FACTOR_Z"), GroupArguments); + SpinBox_FZ = new SMESHGUI_SpinBox(GroupArguments); + + + // switch of action type + ActionBox = new QGroupBox(GroupArguments); + ActionGroup = new QButtonGroup(GroupArguments); + QVBoxLayout* ActionBoxLayout = new QVBoxLayout(ActionBox); + ActionBoxLayout->addSpacing(SPACING); + ActionBoxLayout->setMargin(MARGIN); + + QRadioButton* aMoveElements = new QRadioButton(tr("SMESH_MOVE_ELEMENTS"), ActionBox); + QRadioButton* aCopyElements = new QRadioButton(tr("SMESH_COPY_ELEMENTS"), ActionBox); + QRadioButton* aCreateMesh = new QRadioButton(tr("SMESH_CREATE_MESH"), ActionBox); + + ActionBoxLayout->addWidget(aMoveElements); + ActionBoxLayout->addWidget(aCopyElements); + ActionBoxLayout->addWidget(aCreateMesh); + ActionGroup->addButton(aMoveElements, MOVE_ELEMS_BUTTON); + ActionGroup->addButton(aCopyElements, COPY_ELEMS_BUTTON); + ActionGroup->addButton(aCreateMesh, MAKE_MESH_BUTTON); + + // CheckBox for groups generation + MakeGroupsCheck = new QCheckBox(tr("SMESH_MAKE_GROUPS"), GroupArguments); + MakeGroupsCheck->setChecked(false); + + // Name of a mesh to create + LineEditNewMesh = new QLineEdit(GroupArguments); + + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + + // layout + GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); + GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); + GroupArgumentsLayout->addWidget(LineEditElements, 0, 2, 1, 5); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 7); + GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 8); + GroupArgumentsLayout->addWidget(TextLabel1, 2, 0); + GroupArgumentsLayout->addWidget(SelectButton1, 2, 1); + GroupArgumentsLayout->addWidget(TextLabel1_1, 2, 2); + GroupArgumentsLayout->addWidget(SpinBox1_1, 2, 3); + GroupArgumentsLayout->addWidget(TextLabel1_2, 2, 4); + GroupArgumentsLayout->addWidget(SpinBox1_2, 2, 5); + GroupArgumentsLayout->addWidget(TextLabel1_3, 2, 6); + GroupArgumentsLayout->addWidget(SpinBox1_3, 2, 7); + GroupArgumentsLayout->addWidget(TextLabel2, 3, 0); + GroupArgumentsLayout->addWidget(SpinBox_FX, 3, 3); + GroupArgumentsLayout->addWidget(TextLabel3, 4, 0); + GroupArgumentsLayout->addWidget(SpinBox_FY, 4, 3); + GroupArgumentsLayout->addWidget(TextLabel4, 5, 0); + GroupArgumentsLayout->addWidget(SpinBox_FZ, 5, 3); + GroupArgumentsLayout->addWidget(ActionBox, 7, 0, 3, 4); + GroupArgumentsLayout->addWidget(MakeGroupsCheck, 8, 5, 1, 4); + GroupArgumentsLayout->addWidget(LineEditNewMesh, 9, 5, 1, 4); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 10, 0); + + /***************************************************************/ + GroupButtons = new QGroupBox(this); + QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons); + GroupButtonsLayout->setSpacing(SPACING); + GroupButtonsLayout->setMargin(MARGIN); + + buttonOk = new QPushButton(tr("SMESH_BUT_APPLY_AND_CLOSE"), GroupButtons); + buttonOk->setAutoDefault(true); + buttonOk->setDefault(true); + buttonApply = new QPushButton(tr("SMESH_BUT_APPLY"), GroupButtons); + buttonApply->setAutoDefault(true); + buttonCancel = new QPushButton(tr("SMESH_BUT_CLOSE"), GroupButtons); + buttonCancel->setAutoDefault(true); + buttonHelp = new QPushButton(tr("SMESH_BUT_HELP"), GroupButtons); + buttonHelp->setAutoDefault(true); + + GroupButtonsLayout->addWidget(buttonOk); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addWidget(buttonApply); + GroupButtonsLayout->addSpacing(10); + GroupButtonsLayout->addStretch(); + GroupButtonsLayout->addWidget(buttonCancel); + GroupButtonsLayout->addWidget(buttonHelp); + + /***************************************************************/ + SMESHGUI_ScaleDlgLayout->addWidget(ConstructorsBox); + SMESHGUI_ScaleDlgLayout->addWidget(GroupArguments); + SMESHGUI_ScaleDlgLayout->addWidget(GroupButtons); + + /* Initialisations */ + SpinBox1_1->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox1_2->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox1_3->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_FX->RangeStepAndValidator(1.e-6, 1.e+6, 1.0, "parametric_precision"); + SpinBox_FX->SetStep(0.1); + SpinBox_FY->RangeStepAndValidator(1.e-6, 1.e+6, 1.0, "parametric_precision"); + SpinBox_FY->SetStep(0.1); + SpinBox_FZ->RangeStepAndValidator(1.e-6, 1.e+6, 1.0, "parametric_precision"); + SpinBox_FZ->SetStep(0.1); + + RadioButton1->setChecked(true); + + mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); + + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + // Costruction of the logical filter + SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (MESHorSUBMESH); + SMESH_TypeFilter* aSmeshGroupFilter = new SMESH_TypeFilter (GROUP); + + QList aListOfFilters; + if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter); + if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); + + myMeshOrSubMeshOrGroupFilter = + new SMESH_LogicalFilter(aListOfFilters, SMESH_LogicalFilter::LO_OR); + + myHelpFileName = "scale_page.html"; + + Init(); + + /* signals and slots connections */ + connect(buttonOk, SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(ClickOnCancel())); + connect(buttonApply, SIGNAL(clicked()), this, SLOT(ClickOnApply())); + connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); + connect(GroupConstructors, SIGNAL(buttonClicked(int)), SLOT(ConstructorsClicked(int))); + + connect(SelectElementsButton, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument())); + connect(SelectButton1, SIGNAL (clicked()), this, SLOT(SetEditCurrentArgument())); + + connect(mySMESHGUI, SIGNAL (SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + /* to close dialog if study change */ + connect(mySMESHGUI, SIGNAL (SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); + connect(LineEditElements, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); + connect(CheckBoxMesh, SIGNAL(toggled(bool)), SLOT(onSelectMesh(bool))); + connect(ActionGroup, SIGNAL(buttonClicked(int)), SLOT(onActionClicked(int))); + + connect(SpinBox1_1, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox1_2, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox1_3, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + connect(SpinBox_FX, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_FY, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_FZ, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + //To Connect preview check box + connectPreviewControl(); + + ConstructorsClicked(0); + SelectionIntoArgument(); + onActionClicked(MOVE_ELEMS_BUTTON); +} + +//================================================================================= +// function : ~SMESHGUI_ScaleDlg() +// purpose : Destroys the object and frees any allocated resources +//================================================================================= +SMESHGUI_ScaleDlg::~SMESHGUI_ScaleDlg() +{ + if ( myFilterDlg ) { + myFilterDlg->setParent( 0 ); + delete myFilterDlg; + myFilterDlg = 0; + } +} + +//================================================================================= +// function : Init() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::Init (bool ResetControls) +{ + myBusy = false; + + myEditCurrentArgument = 0; + LineEditElements->clear(); + myElementsId = ""; + myNbOkElements = 0; + + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + + myActor = 0; + myMesh = SMESH::SMESH_Mesh::_nil(); + + if (ResetControls) { + SpinBox1_1->SetValue(0.0); + SpinBox1_2->SetValue(0.0); + SpinBox1_3->SetValue(0.0); + SpinBox_FX->SetValue(1.0); + SpinBox_FY->SetValue(1.0); + SpinBox_FZ->SetValue(1.0); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); + + ActionGroup->button( MOVE_ELEMS_BUTTON )->setChecked(true); + CheckBoxMesh->setChecked(false); + onSelectMesh(false); + } +} + +//================================================================================= +// function : ConstructorsClicked() +// purpose : Radio button management +//================================================================================= +void SMESHGUI_ScaleDlg::ConstructorsClicked (int constructorId) +{ + disconnect(mySelectionMgr, 0, this, 0); + + switch (constructorId) { + case 0: + { + TextLabel2->setText(tr("SMESH_SCALE_FACTOR")); + TextLabel3->hide(); + TextLabel4->hide(); + SpinBox_FY->hide(); + SpinBox_FZ->hide(); + break; + } + case 1: + { + TextLabel2->setText(tr("SMESH_SCALE_FACTOR_X")); + TextLabel3->show(); + TextLabel4->show(); + SpinBox_FY->show(); + SpinBox_FZ->show(); + break; + } + } + + if (myEditCurrentArgument != (QWidget*)LineEditElements) { + SMESH::SetPointRepresentation(false); + if (!CheckBoxMesh->isChecked()) + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + } + + myEditCurrentArgument = (QWidget*)LineEditElements; + LineEditElements->setFocus(); + + if (CheckBoxMesh->isChecked()) + onSelectMesh(true); + + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + + QApplication::instance()->processEvents(); + updateGeometry(); + resize(100,100); +} + +//================================================================================= +// function : ClickOnApply() +// purpose : +//================================================================================= +bool SMESHGUI_ScaleDlg::ClickOnApply() +{ + if (mySMESHGUI->isActiveStudyLocked()) + return false; + + if( !isValid() ) + return false; + + if (myNbOkElements) { + QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + + SMESH::long_array_var anElementsId = new SMESH::long_array; + + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::PointStruct aPoint; + SMESH::double_array_var aScaleFact = new SMESH::double_array; + getScale(aPoint, aScaleFact); + + QStringList aParameters; + aParameters << SpinBox1_1->text(); + aParameters << SpinBox1_2->text(); + aParameters << SpinBox1_3->text(); + aParameters << SpinBox_FX->text(); + if (GetConstructorId() == 1) { + aParameters << SpinBox_FX->text(); + aParameters << SpinBox_FX->text(); + } + else { + aParameters << SpinBox_FY->text(); + aParameters << SpinBox_FZ->text(); + } + + int actionButton = ActionGroup->checkedId(); + bool makeGroups = ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ); + QStringList anEntryList; + try { + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + + SMESH::SMESH_IDSource_var obj; + if ( CheckBoxMesh->isChecked() ) + obj = mySelectedObject; + else + obj = aMeshEditor->MakeIDSource(anElementsId, SMESH::ALL); + + switch ( actionButton ) { + + case MOVE_ELEMS_BUTTON: + aMeshEditor->Scale(obj, aPoint, aScaleFact, false); + break; + + case COPY_ELEMS_BUTTON: + if ( makeGroups ) + SMESH::ListOfGroups_var groups = + aMeshEditor->ScaleMakeGroups(obj, aPoint, aScaleFact); + else + aMeshEditor->Scale(obj, aPoint, aScaleFact, true); + break; + + case MAKE_MESH_BUTTON: { + SMESH::SMESH_Mesh_var mesh = + aMeshEditor->ScaleMakeMesh(obj, aPoint, aScaleFact, makeGroups, + LineEditNewMesh->text().toLatin1().data()); + if (!mesh->_is_nil()) { + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( mesh ) ) + anEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + mesh->UnRegister(); +#endif + } + break; + } + } + } catch (...) { + } + + SMESH::UpdateView(); + if ( ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) || + actionButton == MAKE_MESH_BUTTON ) { + mySMESHGUI->updateObjBrowser(true); // new groups may appear + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + } + Init(false); + ConstructorsClicked(GetConstructorId()); + mySelectedObject = SMESH::SMESH_IDSource::_nil(); + SelectionIntoArgument(); + + SMESHGUI::Modified(); + } + + return true; +} + +//================================================================================= +// function : ClickOnOk() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::ClickOnOk() +{ + setIsApplyAndClose( true ); + if( ClickOnApply() ) + ClickOnCancel(); +} + +//================================================================================= +// function : ClickOnCancel() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::ClickOnCancel() +{ + disconnect(mySelectionMgr, 0, this, 0); + mySelectionMgr->clearFilters(); + //mySelectionMgr->clearSelected(); + if (SMESH::GetCurrentVtkView()) { + SMESH::RemoveFilters(); // PAL6938 -- clean all mesh entity filters + SMESH::SetPointRepresentation(false); + } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + mySMESHGUI->ResetState(); + reject(); +} + +//================================================================================= +// function : ClickOnHelp() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::ClickOnHelp() +{ + LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); + if (app) + app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); + else { + QString platform; +#ifdef WIN32 + platform = "winapplication"; +#else + platform = "application"; +#endif + SUIT_MessageBox::warning(this, tr("WRN_WARNING"), + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); + } +} + +//======================================================================= +// function : onTextChange() +// purpose : +//======================================================================= +void SMESHGUI_ScaleDlg::onTextChange (const QString& theNewText) +{ + QLineEdit* send = (QLineEdit*)sender(); + + if (myBusy) return; + BusyLocker lock( myBusy ); + + if (send == LineEditElements) + myNbOkElements = 0; + + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + + // hilight entered elements + SMDS_Mesh* aMesh = 0; + if (myActor) + aMesh = myActor->GetObject()->GetMesh(); + + if (aMesh) { + Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); + + TColStd_MapOfInteger newIndices; + + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); + + if (send == LineEditElements) { + for (int i = 0; i < aListId.count(); i++) { + const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); + if (e) + newIndices.Add(e->GetID()); + myNbOkElements++; + } + } + + mySelector->AddOrRemoveIndex( anIO, newIndices, false ); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( anIO, true, true ); + + myElementsId = theNewText; + } + + if (myNbOkElements) { + buttonOk->setEnabled(true); + buttonApply->setEnabled(true); + } +} + +//================================================================================= +// function : SelectionIntoArgument() +// purpose : Called when selection as changed or other case +//================================================================================= +void SMESHGUI_ScaleDlg::SelectionIntoArgument() +{ + if (myBusy) return; + BusyLocker lock( myBusy ); + // clear + myActor = 0; + QString aString = ""; + + if (myEditCurrentArgument == (QWidget*)LineEditElements) { + LineEditElements->setText(aString); + myNbOkElements = 0; + buttonOk->setEnabled(false); + buttonApply->setEnabled(false); + } + + if (!GroupButtons->isEnabled()) // inactive + return; + + // get selected mesh + SALOME_ListIO aList; + mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); + + int nbSel = aList.Extent(); + if (nbSel != 1) + return; + + Handle(SALOME_InteractiveObject) IO = aList.First(); + myMesh = SMESH::GetMeshByIO(IO); + if (myMesh->_is_nil()) + return; + + myActor = SMESH::FindActorByObject(myMesh); + if (!myActor) + myActor = SMESH::FindActorByEntry(IO->getEntry()); + if (!myActor && !CheckBoxMesh->isChecked()) + return; + + int aNbUnits = 0; + + if (myEditCurrentArgument == (QWidget*)LineEditElements) { + myElementsId = ""; + + // MakeGroups is available if there are groups and "Copy" + if ( myMesh->NbGroups() == 0 ) { + MakeGroupsCheck->setChecked(false); + MakeGroupsCheck->setEnabled(false); + } + else if ( ActionGroup->checkedId() != MOVE_ELEMS_BUTTON ) { + MakeGroupsCheck->setEnabled(true); + } + + if (CheckBoxMesh->isChecked()) { + SMESH::GetNameOfSelectedIObjects( mySelectionMgr, aString ); + + if (!SMESH::IObjectToInterface(IO)->_is_nil()) { //MESH, SUBMESH, OR GROUP + mySelectedObject = SMESH::IObjectToInterface(IO); + } + else + return; + // get IDs from mesh + /* + SMDS_Mesh* aSMDSMesh = myActor->GetObject()->GetMesh(); + if (!aSMDSMesh) + return; + + for (int i = aSMDSMesh->MinElementID(); i <= aSMDSMesh->MaxElementID(); i++) { + const SMDS_MeshElement * e = aSMDSMesh->FindElement(i); + if (e) { + myElementsId += QString(" %1").arg(i); + aNbUnits++; + } + } + } else if (!SMESH::IObjectToInterface(IO)->_is_nil()) { //SUBMESH + // get submesh + SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(IO); + + // get IDs from submesh + SMESH::long_array_var anElementsIds = new SMESH::long_array; + anElementsIds = aSubMesh->GetElementsId(); + for (int i = 0; i < anElementsIds->length(); i++) { + myElementsId += QString(" %1").arg(anElementsIds[i]); + } + aNbUnits = anElementsIds->length(); + } else { // GROUP + // get smesh group + SMESH::SMESH_GroupBase_var aGroup = + SMESH::IObjectToInterface(IO); + if (aGroup->_is_nil()) + return; + + // get IDs from smesh group + SMESH::long_array_var anElementsIds = new SMESH::long_array; + anElementsIds = aGroup->GetListOfID(); + for (int i = 0; i < anElementsIds->length(); i++) { + myElementsId += QString(" %1").arg(anElementsIds[i]); + } + aNbUnits = anElementsIds->length(); + } + */ + } else { + aNbUnits = SMESH::GetNameOfSelectedElements(mySelector, IO, aString); + myElementsId = aString; + if (aNbUnits < 1) + return; + } + + myNbOkElements = true; + } else { + aNbUnits = SMESH::GetNameOfSelectedNodes(mySelector, IO, aString); + if (aNbUnits != 1) + return; + + SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); + if (!aMesh) + return; + + const SMDS_MeshNode * n = aMesh->FindNode(aString.toInt()); + if (!n) + return; + + double x = n->X(); + double y = n->Y(); + double z = n->Z(); + + if (myEditCurrentArgument == (QWidget*)SpinBox1_1) { + SpinBox1_1->SetValue(x); + SpinBox1_2->SetValue(y); + SpinBox1_3->SetValue(z); + } + else if (myEditCurrentArgument == (QWidget*)SpinBox_FX) { + SpinBox_FX->SetValue(x); + SpinBox_FY->SetValue(y); + SpinBox_FZ->SetValue(z); + } + } + + if (myEditCurrentArgument == (QWidget*)LineEditElements) { + LineEditElements->setText(aString); + LineEditElements->repaint(); + LineEditElements->setEnabled(false); // to fully update lineedit IPAL 19809 + LineEditElements->setEnabled(true); + setNewMeshName(); + } + + // OK + if (myNbOkElements) { + buttonOk->setEnabled(true); + buttonApply->setEnabled(true); + } + + onDisplaySimulation(true); +} + +//================================================================================= +// function : SetEditCurrentArgument() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::SetEditCurrentArgument() +{ + QPushButton* send = (QPushButton*)sender(); + + disconnect(mySelectionMgr, 0, this, 0); + mySelectionMgr->clearSelected(); + mySelectionMgr->clearFilters(); + + if (send == SelectElementsButton) { + myEditCurrentArgument = (QWidget*)LineEditElements; + SMESH::SetPointRepresentation(false); + if (CheckBoxMesh->isChecked()) { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); + } + else { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + } + } + else if (send == SelectButton1) { + myEditCurrentArgument = (QWidget*)SpinBox1_1; + SMESH::SetPointRepresentation(true); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( NodeSelection ); + } + + myEditCurrentArgument->setFocus(); + connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + SelectionIntoArgument(); +} + +//================================================================================= +// function : DeactivateActiveDialog() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::DeactivateActiveDialog() +{ + if (ConstructorsBox->isEnabled()) { + ConstructorsBox->setEnabled(false); + GroupArguments->setEnabled(false); + GroupButtons->setEnabled(false); + mySMESHGUI->ResetState(); + mySMESHGUI->SetActiveDialogBox(0); + } +} + +//================================================================================= +// function : ActivateThisDialog() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::ActivateThisDialog() +{ + /* Emit a signal to deactivate the active dialog */ + mySMESHGUI->EmitSignalDeactivateDialog(); + ConstructorsBox->setEnabled(true); + GroupArguments->setEnabled(true); + GroupButtons->setEnabled(true); + + mySMESHGUI->SetActiveDialogBox((QDialog*)this); + + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + + SelectionIntoArgument(); +} + +//================================================================================= +// function : enterEvent() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::enterEvent (QEvent*) +{ + if (!ConstructorsBox->isEnabled()) + ActivateThisDialog(); +} + +//================================================================================= +// function : closeEvent() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::closeEvent (QCloseEvent*) +{ + /* same than click on cancel button */ + ClickOnCancel(); +} + +//======================================================================= +//function : hideEvent +//purpose : caused by ESC key +//======================================================================= +void SMESHGUI_ScaleDlg::hideEvent (QHideEvent*) +{ + if (!isMinimized()) + ClickOnCancel(); +} + +//======================================================================= +//function : onSelectMesh +//purpose : +//======================================================================= +void SMESHGUI_ScaleDlg::onSelectMesh (bool toSelectMesh) +{ + if (toSelectMesh) + TextLabelElements->setText(tr("SMESH_NAME")); + else + TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); + + if (myEditCurrentArgument != LineEditElements) { + LineEditElements->clear(); + return; + } + + mySelectionMgr->clearFilters(); + SMESH::SetPointRepresentation(false); + + if (toSelectMesh) { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); + LineEditElements->setReadOnly(true); + LineEditElements->setValidator(0); + } else { + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( CellSelection ); + LineEditElements->setReadOnly(false); + LineEditElements->setValidator(myIdValidator); + onTextChange(LineEditElements->text()); + hidePreview(); + } + + SelectionIntoArgument(); +} + +//======================================================================= +//function : onActionClicked +//purpose : slot called when an action type changed +//======================================================================= + +void SMESHGUI_ScaleDlg::onActionClicked(int button) +{ + switch ( button ) { + case MOVE_ELEMS_BUTTON: + MakeGroupsCheck->setEnabled(false); + LineEditNewMesh->setEnabled(false); + break; + case COPY_ELEMS_BUTTON: + LineEditNewMesh->setEnabled(false); + MakeGroupsCheck->setText( tr("SMESH_MAKE_GROUPS")); + if ( myMesh->_is_nil() || myMesh->NbGroups() > 0) + MakeGroupsCheck->setEnabled(true); + else + MakeGroupsCheck->setEnabled(false); + break; + case MAKE_MESH_BUTTON: + LineEditNewMesh->setEnabled(true); + MakeGroupsCheck->setText( tr("SMESH_COPY_GROUPS")); + if ( myMesh->_is_nil() || myMesh->NbGroups() > 0) + MakeGroupsCheck->setEnabled(true); + else + MakeGroupsCheck->setEnabled(false); + break; + } + setNewMeshName(); + toDisplaySimulation(); +} + +//======================================================================= +//function : setNewMeshName +//purpose : update contents of LineEditNewMesh +//======================================================================= + +void SMESHGUI_ScaleDlg::setNewMeshName() +{ + LineEditNewMesh->setText(""); + if ( LineEditNewMesh->isEnabled() && !myMesh->_is_nil() ) { + QString name; + if ( CheckBoxMesh->isChecked() ) { + name = LineEditElements->text(); + } + else { + _PTR(SObject) meshSO = SMESH::FindSObject( myMesh ); + name = meshSO->GetName().c_str(); + } + if ( !name.isEmpty() ) + LineEditNewMesh->setText( SMESH::UniqueMeshName( name, "scaled")); + } +} + +//================================================================================= +// function : GetConstructorId() +// purpose : +//================================================================================= +int SMESHGUI_ScaleDlg::GetConstructorId() +{ + return GroupConstructors->checkedId(); +} + +//================================================================================= +// function : keyPressEvent() +// purpose : +//================================================================================= +void SMESHGUI_ScaleDlg::keyPressEvent( QKeyEvent* e ) +{ + QDialog::keyPressEvent( e ); + if ( e->isAccepted() ) + return; + + if ( e->key() == Qt::Key_F1 ) { + e->accept(); + ClickOnHelp(); + } +} + +//================================================================================= +// function : setFilters() +// purpose : SLOT. Called when "Filter" button pressed. +//================================================================================= +void SMESHGUI_ScaleDlg::setFilters() +{ + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } + if ( !myFilterDlg ) + myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); + + myFilterDlg->SetSelection(); + myFilterDlg->SetMesh( myMesh ); + myFilterDlg->SetSourceWg( LineEditElements ); + + myFilterDlg->show(); +} + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= +bool SMESHGUI_ScaleDlg::isValid() +{ + bool ok = true; + QString msg; + + ok = SpinBox1_1->isValid( msg, true ) && ok; + ok = SpinBox1_2->isValid( msg, true ) && ok; + ok = SpinBox1_3->isValid( msg, true ) && ok; + ok = SpinBox_FX->isValid( msg, true ) && ok; + if (GetConstructorId() == 1) { + ok = SpinBox_FY->isValid( msg, true ) && ok; + ok = SpinBox_FZ->isValid( msg, true ) && ok; + } + + if( !ok ) { + QString str( tr( "SMESH_INCORRECT_INPUT" ) ); + if ( !msg.isEmpty() ) + str += "\n" + msg; + SUIT_MessageBox::critical( this, tr( "SMESH_ERROR" ), str ); + return false; + } + return true; +} + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_ScaleDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + if ( myNbOkElements && isValid() ) { + QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + + SMESH::long_array_var anElementsId = new SMESH::long_array; + + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::PointStruct aPoint; + SMESH::double_array_var aScaleFact = new SMESH::double_array; + getScale(aPoint, aScaleFact); + + try { + bool copy = ( ActionGroup->checkedId() == COPY_ELEMS_BUTTON || + ActionGroup->checkedId() == MAKE_MESH_BUTTON ); + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + SMESH::SMESH_IDSource_var obj; + if ( CheckBoxMesh->isChecked() ) + obj = mySelectedObject; + else + obj = aMeshEditor->MakeIDSource(anElementsId, SMESH::ALL); + aMeshEditor->Scale(obj, aPoint, aScaleFact, copy); + + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + + } catch (...) { + hidePreview(); + } + } else { + hidePreview(); + } + } else { + hidePreview(); + } +} + +//================================================================================= +// function : getScale +// purpose : get scale parameters +//================================================================================= +void SMESHGUI_ScaleDlg::getScale( SMESH::PointStruct& thePoint , SMESH::double_array_var& theScaleFact) { + thePoint.x = SpinBox1_1->GetValue(); + thePoint.y = SpinBox1_2->GetValue(); + thePoint.z = SpinBox1_3->GetValue(); + + theScaleFact->length(3); + theScaleFact[0] = SpinBox_FX->GetValue(); + if (GetConstructorId() == 0) { + theScaleFact[1] = SpinBox_FX->GetValue(); + theScaleFact[2] = SpinBox_FX->GetValue(); + } + else { + theScaleFact[1] = SpinBox_FY->GetValue(); + theScaleFact[2] = SpinBox_FZ->GetValue(); + } +} diff --git a/src/SMESHGUI/SMESHGUI_ScaleDlg.h b/src/SMESHGUI/SMESHGUI_ScaleDlg.h new file mode 100644 index 000000000..7293a99e7 --- /dev/null +++ b/src/SMESHGUI/SMESHGUI_ScaleDlg.h @@ -0,0 +1,150 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESHGUI : GUI for SMESH component +// File : SMESHGUI_ScaleDlg.h +// Author : Sergey Kuul, Open CASCADE S.A.S. +// +#ifndef SMESHGUI_SCALEDLG_H +#define SMESHGUI_SCALEDLG_H + +// SMESH includes +#include "SMESH_SMESHGUI.hxx" +#include "SMESHGUI_PreviewDlg.h" + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + +class QButtonGroup; +class QGroupBox; +class QLabel; +class QLineEdit; +class QPushButton; +class QRadioButton; +class QCheckBox; +class SMESHGUI; +class SMESHGUI_IdValidator; +class SMESHGUI_SpinBox; +class SMESHGUI_FilterDlg; +class SMESH_Actor; +class SVTK_Selector; +class LightApp_SelectionMgr; +class SMESH_LogicalFilter; + +//================================================================================= +// class : SMESHGUI_ScaleDlg +// purpose : +//================================================================================= +class SMESHGUI_EXPORT SMESHGUI_ScaleDlg : public SMESHGUI_PreviewDlg +{ + Q_OBJECT + +public: + SMESHGUI_ScaleDlg( SMESHGUI* ); + ~SMESHGUI_ScaleDlg(); + +private: + void Init( bool = true ); + void closeEvent( QCloseEvent* ); + void enterEvent( QEvent* ); /* mouse enter the QWidget */ + void hideEvent( QHideEvent* ); /* ESC key */ + void keyPressEvent( QKeyEvent* ); + int GetConstructorId(); + void setNewMeshName(); + + bool isValid(); + void getScale( SMESH::PointStruct& thePoint, + SMESH::double_array_var& theScaleFact); + + SMESHGUI_IdValidator* myIdValidator; + LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ + QString myElementsId; + int myNbOkElements; /* to check when elements are defined */ + + SVTK_Selector* mySelector; + + QWidget* myEditCurrentArgument; + + bool myBusy; + SMESH::SMESH_Mesh_var myMesh; + SMESH_Actor* myActor; + SMESH_LogicalFilter* myMeshOrSubMeshOrGroupFilter; + + SMESH::SMESH_IDSource_var mySelectedObject; + + QGroupBox* ConstructorsBox; + QButtonGroup* GroupConstructors; + QRadioButton* RadioButton1; + QRadioButton* RadioButton2; + QGroupBox* GroupButtons; + QPushButton* buttonOk; + QPushButton* buttonCancel; + QPushButton* buttonApply; + QPushButton* buttonHelp; + QGroupBox* GroupArguments; + QLabel* TextLabelElements; + QPushButton* SelectElementsButton; + QLineEdit* LineEditElements; + QCheckBox* CheckBoxMesh; + QLabel* TextLabel1; + QPushButton* SelectButton1; + QLabel* TextLabel1_1; + SMESHGUI_SpinBox* SpinBox1_1; + QLabel* TextLabel1_2; + SMESHGUI_SpinBox* SpinBox1_2; + QLabel* TextLabel1_3; + SMESHGUI_SpinBox* SpinBox1_3; + QLabel* TextLabel2; + SMESHGUI_SpinBox* SpinBox_FX; + QLabel* TextLabel3; + SMESHGUI_SpinBox* SpinBox_FY; + QLabel* TextLabel4; + SMESHGUI_SpinBox* SpinBox_FZ; + QGroupBox* ActionBox; + QButtonGroup* ActionGroup; + QCheckBox* MakeGroupsCheck; + QLineEdit* LineEditNewMesh; + + QString myHelpFileName; + + QPushButton* myFilterBtn; + SMESHGUI_FilterDlg* myFilterDlg; + + +protected slots: + virtual void onDisplaySimulation( bool ); + +private slots: + void ConstructorsClicked( int ); + void ClickOnOk(); + void ClickOnCancel(); + bool ClickOnApply(); + void ClickOnHelp(); + void SetEditCurrentArgument(); + void SelectionIntoArgument(); + void DeactivateActiveDialog(); + void ActivateThisDialog(); + void onTextChange( const QString& ); + void onSelectMesh( bool ); + void onActionClicked( int ); + void setFilters(); +}; + +#endif // SMESHGUI_SCALEDLG_H diff --git a/src/SMESHGUI/SMESHGUI_Selection.cxx b/src/SMESHGUI/SMESHGUI_Selection.cxx index e0e63d4cc..8fff50e3c 100644 --- a/src/SMESHGUI/SMESHGUI_Selection.cxx +++ b/src/SMESHGUI/SMESHGUI_Selection.cxx @@ -1,37 +1,41 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI_Selection // File : SMESHGUI_Selection.cxx // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. -// SMESH includes // + +// SMESH includes #include "SMESHGUI_Selection.h" #include "SMESHGUI_Utils.h" #include "SMESHGUI_VTKUtils.h" #include "SMESHGUI_GEOMGenUtils.h" +#include "SMESHGUI_ComputeDlg.h" #include #include +#include // SALOME GUI includes #include @@ -85,7 +89,7 @@ void SMESHGUI_Selection::init( const QString& client, LightApp_SelectionMgr* mgr //function : processOwner //purpose : //======================================================================= -void SMESHGUI_Selection::processOwner( const LightApp_DataOwner* ow ) +bool SMESHGUI_Selection::processOwner( const LightApp_DataOwner* ow ) { const LightApp_SVTKDataOwner* owner = dynamic_cast ( ow ); @@ -93,6 +97,7 @@ void SMESHGUI_Selection::processOwner( const LightApp_DataOwner* ow ) myActors.append( dynamic_cast( owner->GetActor() ) ); else myActors.append( 0 ); + return true; } //======================================================================= @@ -107,16 +112,21 @@ QVariant SMESHGUI_Selection::parameter( const int ind, const QString& p ) const else if ( p=="elemTypes" ) val = QVariant( elemTypes( ind ) ); else if ( p=="isAutoColor" ) val = QVariant( isAutoColor( ind ) ); else if ( p=="numberOfNodes" ) val = QVariant( numberOfNodes( ind ) ); + else if ( p=="dim" ) val = QVariant( dim( ind ) ); else if ( p=="labeledTypes" ) val = QVariant( labeledTypes( ind ) ); else if ( p=="shrinkMode" ) val = QVariant( shrinkMode( ind ) ); else if ( p=="entityMode" ) val = QVariant( entityMode( ind ) ); else if ( p=="controlMode" ) val = QVariant( controlMode( ind ) ); + else if ( p=="isNumFunctor" ) val = QVariant( isNumFunctor( ind ) ); else if ( p=="displayMode" ) val = QVariant( displayMode( ind ) ); else if ( p=="isComputable" ) val = QVariant( isComputable( ind ) ); + else if ( p=="isPreComputable" ) val = QVariant( isPreComputable( ind ) ); else if ( p=="hasReference" ) val = QVariant( hasReference( ind ) ); else if ( p=="isImported" ) val = QVariant( isImported( ind ) ); else if ( p=="facesOrientationMode" ) val = QVariant( facesOrientationMode( ind ) ); else if ( p=="groupType" ) val = QVariant( groupType( ind ) ); + else if ( p=="quadratic2DMode") val = QVariant(quadratic2DMode(ind)); + else if ( p=="isDistributionVisible") val = QVariant(isDistributionVisible(ind)); if( val.isValid() ) return val; @@ -139,7 +149,7 @@ SMESH_Actor* SMESHGUI_Selection::getActor( int ind ) const //======================================================================= //function : elemTypes -//purpose : may return {'Edge' 'Face' 'Volume'} at most +//purpose : may return {'Elem0d' 'Edge' 'Face' 'Volume' 'BallElem'} at most //======================================================================= QList SMESHGUI_Selection::elemTypes( int ind ) const @@ -149,6 +159,8 @@ QList SMESHGUI_Selection::elemTypes( int ind ) const if ( actor ) { TVisualObjPtr object = actor->GetObject(); if ( object ) { + if ( object->GetNbEntities( SMDSAbs_0DElement )) types.append( "Elem0d" ); + if ( object->GetNbEntities( SMDSAbs_Ball )) types.append( "BallElem" ); if ( object->GetNbEntities( SMDSAbs_Edge )) types.append( "Edge" ); if ( object->GetNbEntities( SMDSAbs_Face )) types.append( "Face" ); if ( object->GetNbEntities( SMDSAbs_Volume )) types.append( "Volume" ); @@ -192,6 +204,34 @@ QString SMESHGUI_Selection::displayMode( int ind ) const return "Unknown"; } + +//======================================================================= +//function : quadratic2DMode +//purpose : return SMESH_Actor::EQuadratic2DRepresentation +//======================================================================= +QString SMESHGUI_Selection::quadratic2DMode( int ind ) const +{ + SMESH_Actor* actor = getActor( ind ); + if ( actor ) { + switch( actor->GetQuadratic2DRepresentation() ) { + case SMESH_Actor::eLines: return "eLines"; + case SMESH_Actor::eArcs: return "eArcs"; + default: break; + } + } + return "Unknown"; +} + +//======================================================================= +//function : isDistributionVisible +//purpose : Visible/Invisible distribution of the ScalarBar Actor +//======================================================================= + +bool SMESHGUI_Selection::isDistributionVisible(int ind) const { + SMESH_Actor* actor = getActor( ind ); + return (actor && actor->GetScalarBarActor() && actor->GetScalarBarActor()->GetDistributionVisibility()); +} + //======================================================================= //function : shrinkMode //purpose : return either 'IsSrunk', 'IsNotShrunk' or 'IsNotShrinkable' @@ -210,7 +250,7 @@ QString SMESHGUI_Selection::shrinkMode( int ind ) const //======================================================================= //function : entityMode -//purpose : may return {'Edge' 'Face' 'Volume'} at most +//purpose : may return {'Elem0d' 'Edge' 'Face' 'Volume' 'BallElem' } at most //======================================================================= QList SMESHGUI_Selection::entityMode( int ind ) const @@ -219,9 +259,11 @@ QList SMESHGUI_Selection::entityMode( int ind ) const SMESH_Actor* actor = getActor( ind ); if ( actor ) { unsigned int aMode = actor->GetEntityMode(); - if ( aMode & SMESH_Actor::eVolumes) types.append( "Volume"); - if ( aMode & SMESH_Actor::eFaces ) types.append( "Face" ); - if ( aMode & SMESH_Actor::eEdges ) types.append( "Edge" ); + if ( aMode & SMESH_Actor::eVolumes ) types.append( "Volume" ); + if ( aMode & SMESH_Actor::eFaces ) types.append( "Face" ); + if ( aMode & SMESH_Actor::eEdges ) types.append( "Edge" ); + if ( aMode & SMESH_Actor::e0DElements ) types.append( "Elem0d" ); + if ( aMode & SMESH_Actor::eBallElem ) types.append( "BallElem" ); } return types; } @@ -233,28 +275,69 @@ QList SMESHGUI_Selection::entityMode( int ind ) const QString SMESHGUI_Selection::controlMode( int ind ) const { + SMESH_Actor* actor = getActor( ind ); + QString mode = "eNone"; + if ( actor ) { + switch( actor->GetControlMode() ) { + case SMESH_Actor::eLength: mode = "eLength"; break; + case SMESH_Actor::eLength2D: mode = "eLength2D"; break; + case SMESH_Actor::eFreeEdges: mode = "eFreeEdges"; break; + case SMESH_Actor::eFreeNodes: mode = "eFreeNodes"; break; + case SMESH_Actor::eFreeBorders: mode = "eFreeBorders"; break; + case SMESH_Actor::eFreeFaces: mode = "eFreeFaces"; break; + case SMESH_Actor::eMultiConnection: mode = "eMultiConnection"; break; + case SMESH_Actor::eMultiConnection2D: mode = "eMultiConnection2D"; break; + case SMESH_Actor::eArea: mode = "eArea"; break; + case SMESH_Actor::eVolume3D: mode = "eVolume3D"; break; + case SMESH_Actor::eMaxElementLength2D: mode = "eMaxElementLength2D"; break; + case SMESH_Actor::eMaxElementLength3D: mode = "eMaxElementLength3D"; break; + case SMESH_Actor::eTaper: mode = "eTaper"; break; + case SMESH_Actor::eAspectRatio: mode = "eAspectRatio"; break; + case SMESH_Actor::eAspectRatio3D: mode = "eAspectRatio3D"; break; + case SMESH_Actor::eMinimumAngle: mode = "eMinimumAngle"; break; + case SMESH_Actor::eWarping: mode = "eWarping"; break; + case SMESH_Actor::eSkew: mode = "eSkew"; break; + case SMESH_Actor::eBareBorderFace: mode = "eBareBorderFace"; break; + case SMESH_Actor::eBareBorderVolume: mode = "eBareBorderVolume"; break; + case SMESH_Actor::eOverConstrainedFace: mode = "eOverConstrainedFace"; break; + case SMESH_Actor::eOverConstrainedVolume: mode = "eOverConstrainedVolume"; break; + case SMESH_Actor::eCoincidentNodes: mode = "eCoincidentNodes"; break; + case SMESH_Actor::eCoincidentElems1D: mode = "eCoincidentElems1D"; break; + case SMESH_Actor::eCoincidentElems2D: mode = "eCoincidentElems2D"; break; + case SMESH_Actor::eCoincidentElems3D: mode = "eCoincidentElems3D"; break; + default:break; + } + } + return mode; +} + +bool SMESHGUI_Selection::isNumFunctor( int ind ) const +{ + bool result = false; SMESH_Actor* actor = getActor( ind ); if ( actor ) { switch( actor->GetControlMode() ) { - case SMESH_Actor::eLength: return "eLength"; - case SMESH_Actor::eLength2D: return "eLength2D"; - case SMESH_Actor::eFreeEdges: return "eFreeEdges"; - case SMESH_Actor::eFreeBorders: return "eFreeBorders"; - case SMESH_Actor::eFreeFaces: return "eFreeFaces"; - case SMESH_Actor::eMultiConnection: return "eMultiConnection"; - case SMESH_Actor::eMultiConnection2D: return "eMultiConnection2D"; - case SMESH_Actor::eArea: return "eArea"; - case SMESH_Actor::eVolume3D: return "eVolume3D"; - case SMESH_Actor::eTaper: return "eTaper"; - case SMESH_Actor::eAspectRatio: return "eAspectRatio"; - case SMESH_Actor::eAspectRatio3D: return "eAspectRatio3D"; - case SMESH_Actor::eMinimumAngle: return "eMinimumAngle"; - case SMESH_Actor::eWarping: return "eWarping"; - case SMESH_Actor::eSkew: return "eSkew"; - default:; + case SMESH_Actor::eLength: + case SMESH_Actor::eLength2D: + case SMESH_Actor::eMultiConnection: + case SMESH_Actor::eMultiConnection2D: + case SMESH_Actor::eArea: + case SMESH_Actor::eVolume3D: + case SMESH_Actor::eMaxElementLength2D: + case SMESH_Actor::eMaxElementLength3D: + case SMESH_Actor::eTaper: + case SMESH_Actor::eAspectRatio: + case SMESH_Actor::eAspectRatio3D: + case SMESH_Actor::eMinimumAngle: + case SMESH_Actor::eWarping: + case SMESH_Actor::eSkew: + result = true; + break; + default: + break; } } - return "eNone"; + return result; } //======================================================================= @@ -321,6 +404,42 @@ int SMESHGUI_Selection::numberOfNodes( int ind ) const return 0; } +//================================================================================ +/*! + * \brief return dimension of elements of the selected object + * + * \retval int - 0 for 0D elements, -1 for an empty object (the rest as usual) + */ +//================================================================================ + +int SMESHGUI_Selection::dim( int ind ) const +{ + int dim = -1; + if ( ind >= 0 && ind < myTypes.count() && myTypes[ind] != "Unknown" ) + { + _PTR(SObject) sobj = SMESH::GetActiveStudyDocument()->FindObjectID( entry( ind ).toLatin1().data() ); + CORBA::Object_var obj = SMESH::SObjectToObject( sobj, SMESH::GetActiveStudyDocument() ); + + if ( ! CORBA::is_nil( obj )) { + SMESH::SMESH_IDSource_var idSrc = SMESH::SMESH_IDSource::_narrow( obj ); + if ( ! idSrc->_is_nil() ) + { + SMESH::array_of_ElementType_var types = idSrc->GetTypes(); + for ( int i = 0; i < types->length(); ++ i) + switch ( types[i] ) { + case SMESH::EDGE : dim = std::max( dim, 1 ); break; + case SMESH::FACE : dim = std::max( dim, 2 ); break; + case SMESH::VOLUME: dim = std::max( dim, 3 ); break; + case SMESH::ELEM0D: dim = std::max( dim, 0 ); break; + case SMESH::BALL : dim = std::max( dim, 0 ); break; + default:; + } + } + } + } + return dim; +} + //======================================================================= //function : isComputable //purpose : @@ -336,7 +455,7 @@ QVariant SMESHGUI_Selection::isComputable( int ind ) const SMESH::SMESH_Mesh_var mesh = SMESH::GetMeshByIO(io); // m,sm,gr->m if ( !mesh->_is_nil() ) {*/ _PTR(SObject) so = SMESH::GetActiveStudyDocument()->FindObjectID( entry( ind ).toLatin1().data() ); - //FindSObject( mesh ); + //FindSObject( mesh ); if ( so ) { CORBA::Object_var obj = SMESH::SObjectToObject(so, SMESH::GetActiveStudyDocument()); if(!CORBA::is_nil(obj)){ @@ -364,6 +483,23 @@ QVariant SMESHGUI_Selection::isComputable( int ind ) const return QVariant( false ); } +//======================================================================= +//function : isPreComputable +//purpose : +//======================================================================= + +QVariant SMESHGUI_Selection::isPreComputable( int ind ) const +{ + if ( ind >= 0 && ind < myTypes.count() && myTypes[ind] != "Unknown" ) + { + QMap modeMap; + _PTR(SObject) pMesh = SMESH::GetActiveStudyDocument()->FindObjectID( entry( ind ).toLatin1().data() ); + SMESHGUI_PrecomputeOp::getAssignedAlgos( pMesh, modeMap ); + return QVariant( modeMap.size() > 1 ); + } + return QVariant( false ); +} + //======================================================================= //function : hasReference //purpose : @@ -387,7 +523,7 @@ QVariant SMESHGUI_Selection::isVisible( int ind ) const SMESH_Actor* actor = SMESH::FindActorByEntry( ent.toLatin1().data() ); if ( actor && actor->hasIO() ) { if(SVTK_ViewWindow* aViewWindow = SMESH::GetCurrentVtkView()) - return QVariant( aViewWindow->isVisible( actor->getIO() ) ); + return QVariant( aViewWindow->isVisible( actor->getIO() ) ); } } return QVariant( false ); @@ -512,7 +648,6 @@ bool SMESHGUI_Selection::isImported( const int ind ) const QString e = entry( ind ); _PTR(SObject) SO = SMESH::GetActiveStudyDocument()->FindObjectID( e.toLatin1().constData() ); bool res = false; - /* if( SO ) { SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( SMESH::SObjectToObject( SO ) ); @@ -522,7 +657,6 @@ bool SMESHGUI_Selection::isImported( const int ind ) const res = strlen( (char*)inf->fileName ) > 0; } } - */ return res; } @@ -535,17 +669,19 @@ QString SMESHGUI_Selection::groupType( int ind ) const { QString e = entry( ind ); _PTR(SObject) SO = SMESH::GetActiveStudyDocument()->FindObjectID( e.toLatin1().constData() ); - QString type; if( SO ) { - CORBA::Object_var obj = SMESH::SObjectToObject( SO ); - - SMESH::SMESH_Group_var aGroup = SMESH::SMESH_Group::_narrow( obj ); - SMESH::SMESH_GroupOnGeom_var aGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow( obj ); - if( !aGroup->_is_nil() ) - type = QString( "Group" ); - else if ( !aGroupOnGeom->_is_nil() ) - type = QString( "GroupOnGeom" ); + SMESH::SMESH_Group_var g = SMESH::SObjectToInterface( SO ); + if( !g->_is_nil() ) + return "Group"; + + SMESH::SMESH_GroupOnGeom_var gog = SMESH::SObjectToInterface( SO ); + if( !gog->_is_nil() ) + return "GroupOnGeom"; + + SMESH::SMESH_GroupOnFilter_var gof = SMESH::SObjectToInterface(SO); + if ( !gof->_is_nil() ) + return "GroupOnFilter"; } - return type; + return ""; } diff --git a/src/SMESHGUI/SMESHGUI_Selection.h b/src/SMESHGUI/SMESHGUI_Selection.h index 72ed58cb4..82293358c 100644 --- a/src/SMESHGUI/SMESHGUI_Selection.h +++ b/src/SMESHGUI/SMESHGUI_Selection.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI_Selection // File : SMESHGUI_Selection.h // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. @@ -48,15 +49,21 @@ public: virtual void init( const QString&, LightApp_SelectionMgr* ); virtual QVariant parameter( const int, const QString& ) const; - virtual void processOwner( const LightApp_DataOwner* ); + virtual bool processOwner( const LightApp_DataOwner* ); // got from object, not from actor virtual bool isAutoColor( int ) const; virtual int numberOfNodes( int ) const; + virtual int dim( int ) const; virtual QVariant isComputable( int ) const; + virtual QVariant isPreComputable( int ) const; virtual QVariant hasReference( int ) const; virtual QVariant isVisible( int ) const; + virtual QString quadratic2DMode(int ) const; + + virtual bool isDistributionVisible(int ) const; + // parameters got from actor return nothing if an actor is not visible virtual QList elemTypes( int ) const; virtual QList labeledTypes( int ) const; @@ -64,6 +71,7 @@ public: virtual QString shrinkMode( int ) const; virtual QList entityMode( int ) const; virtual QString controlMode( int ) const; + virtual bool isNumFunctor( int ) const; virtual QString facesOrientationMode( int ) const; virtual QString groupType( int ) const; diff --git a/src/SMESHGUI/SMESHGUI_SelectionOp.cxx b/src/SMESHGUI/SMESHGUI_SelectionOp.cxx index 1cb2fc213..7e4039a3a 100644 --- a/src/SMESHGUI/SMESHGUI_SelectionOp.cxx +++ b/src/SMESHGUI/SMESHGUI_SelectionOp.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_SelectionOp.cxx // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // SMESH includes @@ -423,9 +424,9 @@ void SMESHGUI_SelectionOp::selected( QStringList& names, SalomeApp_Study* _study = dynamic_cast( study() ); if( _study ) { - _PTR(SObject) obj = _study->studyDS()->FindObjectID( anIt.Value()->getEntry() ); - if( obj ) - names.append( obj->GetName().c_str() ); + _PTR(SObject) obj = _study->studyDS()->FindObjectID( anIt.Value()->getEntry() ); + if( obj ) + names.append( obj->GetName().c_str() ); } } } diff --git a/src/SMESHGUI/SMESHGUI_SelectionOp.h b/src/SMESHGUI/SMESHGUI_SelectionOp.h index 34e5e1135..874bcea22 100644 --- a/src/SMESHGUI/SMESHGUI_SelectionOp.h +++ b/src/SMESHGUI/SMESHGUI_SelectionOp.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_SelectionOp.h // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // @@ -103,18 +104,18 @@ protected: //! Hilight object in VTK viewer void highlight( const Handle( SALOME_InteractiveObject )&, - const bool, const bool = true ); + const bool, const bool = true ); //! Select some nodes or elements in VTK void addOrRemoveIndex( const Handle( SALOME_InteractiveObject )&, - const TColStd_MapOfInteger&, const bool ); + const TColStd_MapOfInteger&, const bool isModeShift); SVTK_ViewWindow* viewWindow() const; SVTK_Selector* selector() const; //! Get names, types and ids of selected objects virtual void selected( QStringList&, - SMESHGUI_Dialog::TypesList&, QStringList& ) const; + SMESHGUI_Dialog::TypesList&, QStringList& ) const; //! Find type by id virtual int typeById( const QString&, const EntityType ) const; diff --git a/src/SMESHGUI/SMESHGUI_SewingDlg.cxx b/src/SMESHGUI/SMESHGUI_SewingDlg.cxx index 1017b19e9..24d85f012 100644 --- a/src/SMESHGUI/SMESHGUI_SewingDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_SewingDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SewingDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -385,9 +386,9 @@ void SMESHGUI_SewingDlg::ConstructorsClicked (int constructorId) LineEdit5->setEnabled(false); if (!CheckBoxPolygons->isVisible()) - CheckBoxPolygons->show(); + CheckBoxPolygons->show(); if (!CheckBoxPolyedrs->isVisible()) - CheckBoxPolyedrs->show(); + CheckBoxPolyedrs->show(); myOk5 = true; @@ -412,7 +413,7 @@ void SMESHGUI_SewingDlg::ConstructorsClicked (int constructorId) SMESH::SetPointRepresentation(false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + aViewWindow->SetSelectionMode(CellSelection); break; } } @@ -439,7 +440,7 @@ void SMESHGUI_SewingDlg::ConstructorsClicked (int constructorId) QApplication::instance()->processEvents(); updateGeometry(); - resize( minimumSize() ); + resize(100,100); } //================================================================================= @@ -472,8 +473,8 @@ bool SMESHGUI_SewingDlg::ClickOnApply() LineEdit4->text().toLong(), LineEdit5->text().toLong(), LineEdit6->text().toLong(), - toCreatePolygons, - toCreatePolyedrs); + toCreatePolygons, + toCreatePolyedrs); else if (aConstructorId == 1) anError = aMeshEditor->SewConformFreeBorders(LineEdit1->text().toLong(), LineEdit2->text().toLong(), @@ -486,8 +487,8 @@ bool SMESHGUI_SewingDlg::ClickOnApply() LineEdit3->text().toLong(), LineEdit4->text().toLong(), LineEdit6->text().toLong(), - toCreatePolygons, - toCreatePolyedrs); + toCreatePolygons, + toCreatePolyedrs); else if (aConstructorId == 3) { QStringList aListElementsId1 = LineEdit1->text().split(" ", QString::SkipEmptyParts); QStringList aListElementsId2 = LineEdit4->text().split(" ", QString::SkipEmptyParts); @@ -532,6 +533,8 @@ bool SMESHGUI_SewingDlg::ClickOnApply() Init(); ConstructorsClicked(GetConstructorId()); + + SMESHGUI::Modified(); } } @@ -580,10 +583,10 @@ void SMESHGUI_SewingDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -632,15 +635,15 @@ void SMESHGUI_SewingDlg::onTextChange (const QString& theNewText) SMESH::SetPointRepresentation(true); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(NodeSelection); + aViewWindow->SetSelectionMode(NodeSelection); const SMDS_MeshNode * n = aMesh->FindNode(theNewText.toInt()); if (n) { - newIndices.Add(n->GetID()); - mySelector->AddOrRemoveIndex(myActor->getIO(), newIndices, false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); - + newIndices.Add(n->GetID()); + mySelector->AddOrRemoveIndex(myActor->getIO(), newIndices, false); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->highlight( myActor->getIO(), true, true ); + if (send == LineEdit1) myOk1 = true; else if (send == LineEdit2) @@ -658,7 +661,7 @@ void SMESHGUI_SewingDlg::onTextChange (const QString& theNewText) SMESH::SetPointRepresentation(false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + aViewWindow->SetSelectionMode(CellSelection); QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); @@ -667,8 +670,8 @@ void SMESHGUI_SewingDlg::onTextChange (const QString& theNewText) for (int i = 0; i < aListId.count(); i++) { const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); if (e) - newIndices.Add(e->GetID()); - + newIndices.Add(e->GetID()); + if (!isEvenOneExists) isEvenOneExists = true; } @@ -676,7 +679,7 @@ void SMESHGUI_SewingDlg::onTextChange (const QString& theNewText) mySelector->AddOrRemoveIndex(myActor->getIO(), newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); + aViewWindow->highlight( myActor->getIO(), true, true ); if (isEvenOneExists) { if (send == LineEdit1) diff --git a/src/SMESHGUI/SMESHGUI_SewingDlg.h b/src/SMESHGUI/SMESHGUI_SewingDlg.h index d21d1b573..f8ff617d0 100644 --- a/src/SMESHGUI/SMESHGUI_SewingDlg.h +++ b/src/SMESHGUI/SMESHGUI_SewingDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SewingDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.cxx b/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.cxx index 82352334b..940dd4f3e 100644 --- a/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_ShapeByMeshDlg.cxx // Author : Edward AGAPOV, Open CASCADE S.A.S. // SMESH includes @@ -129,7 +130,7 @@ QFrame* SMESHGUI_ShapeByMeshDlg::createMainFrame (QWidget* theParent) QLabel* anIdLabel = new QLabel( tr("ELEMENT_ID"), aMainGrp ); myElementId = new QLineEdit( aMainGrp ); myElementId->setValidator( new SMESHGUI_IdValidator( theParent, - !myIsMultipleAllowed ? 1 : 0 ) ); // 0 for any number of entities + !myIsMultipleAllowed ? 1 : 0 ) ); // 0 for any number of entities // shape name QLabel* aNameLabel = new QLabel( tr("GEOMETRY_NAME"), aMainGrp ); @@ -250,36 +251,36 @@ void SMESHGUI_ShapeByMeshOp::SetMesh (SMESH::SMESH_Mesh_ptr thePtr) int shapeDim = 0; // max dim with several shapes //if ( /*mySelectionMgr*/ selectionMgr()->isOk(anIObj) ) // check that the mesh has a valid shape { - _PTR(SObject) aSO = SMESH::FindSObject(myMesh.in()); - GEOM::GEOM_Object_var mainShape = SMESH::GetGeom(aSO); - if ( !mainShape->_is_nil() ) - { - TopoDS_Shape aShape; - if ( GEOMBase::GetShape(mainShape, aShape)) - { - TopAbs_ShapeEnum types[4] = { TopAbs_EDGE, TopAbs_FACE, TopAbs_SHELL, TopAbs_SOLID }; - for ( int dim = 4; dim > 0; --dim ) { - TopAbs_ShapeEnum type = types[ dim - 1 ]; - TopAbs_ShapeEnum avoid = ( type == TopAbs_SHELL ) ? TopAbs_SOLID : TopAbs_SHAPE; - TopExp_Explorer exp( aShape, type, avoid ); - for ( ; nbShapes[ type ] < 2 && exp.More(); exp.Next() ) - ++nbShapes[ type ]; - if ( nbShapes[ type ] > 1 ) { - shapeDim = dim; - break; - } - } - } - } + _PTR(SObject) aSO = SMESH::FindSObject(myMesh.in()); + GEOM::GEOM_Object_var mainShape = SMESH::GetGeom(aSO); + if ( !mainShape->_is_nil() ) + { + TopoDS_Shape aShape; + if ( GEOMBase::GetShape(mainShape, aShape)) + { + TopAbs_ShapeEnum types[4] = { TopAbs_EDGE, TopAbs_FACE, TopAbs_SHELL, TopAbs_SOLID }; + for ( int dim = 4; dim > 0; --dim ) { + TopAbs_ShapeEnum type = types[ dim - 1 ]; + TopAbs_ShapeEnum avoid = ( type == TopAbs_SHELL ) ? TopAbs_SOLID : TopAbs_SHAPE; + TopExp_Explorer exp( aShape, type, avoid ); + for ( ; nbShapes[ type ] < 2 && exp.More(); exp.Next() ) + ++nbShapes[ type ]; + if ( nbShapes[ type ] > 1 ) { + shapeDim = dim; + break; + } + } + } + } } if (shapeDim > 0) - { - if ( nbShapes[ TopAbs_SHELL ] + nbShapes[ TopAbs_SOLID ] > 1 ) - shapeDim = 3; - hasElement[ EDGE ] = shapeDim > 0 && myMesh->NbEdges(); - hasElement[ FACE ] = shapeDim > 1 && myMesh->NbFaces(); - hasElement[ VOLUME ] = shapeDim > 2 && myMesh->NbVolumes(); - } + { + if ( nbShapes[ TopAbs_SHELL ] + nbShapes[ TopAbs_SOLID ] > 1 ) + shapeDim = 3; + hasElement[ EDGE ] = shapeDim > 0 && myMesh->NbEdges(); + hasElement[ FACE ] = shapeDim > 1 && myMesh->NbFaces(); + hasElement[ VOLUME ] = shapeDim > 2 && myMesh->NbVolumes(); + } myHasSolids = nbShapes[ TopAbs_SOLID ]; } @@ -306,83 +307,83 @@ void SMESHGUI_ShapeByMeshOp::commitOperation() QStringList aListId = myDlg->myElementId->text().split( " ", QString::SkipEmptyParts); if (aListId.count() == 1) { - int elemID = (aListId.first()).toInt(); - myGeomObj = GEOM::GEOM_Object::_duplicate( - SMESHGUI::GetSMESHGen()->GetGeometryByMeshElement - ( myMesh.in(), elemID, myDlg->myGeomName->text().toLatin1().constData()) ); + int elemID = (aListId.first()).toInt(); + myGeomObj = GEOM::GEOM_Object::_duplicate( + SMESHGUI::GetSMESHGen()->GetGeometryByMeshElement + ( myMesh.in(), elemID, myDlg->myGeomName->text().toLatin1().constData()) ); } else { - GEOM::GEOM_Gen_var geomGen = SMESH::GetGEOMGen(); - _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); - - if (geomGen->_is_nil() || !aStudy) - return; - - GEOM::GEOM_IShapesOperations_var aShapesOp = - geomGen->GetIShapesOperations(aStudy->StudyId()); - if (aShapesOp->_is_nil() ) - return; - - TopAbs_ShapeEnum aGroupType = TopAbs_SHAPE; - - std::map aGeomObjectsMap; - GEOM::GEOM_Object_var aGeomObject; - - GEOM::GEOM_Object_var aMeshShape = myMesh->GetShapeToMesh(); - - for ( int i = 0; i < aListId.count(); i++ ) - { - aGeomObject = - SMESHGUI::GetSMESHGen()->FindGeometryByMeshElement(myMesh.in(), aListId[i].toInt()); - - if (aGeomObject->_is_nil()) continue; - - double anId = aShapesOp->GetSubShapeIndex(aMeshShape, aGeomObject); - if (aShapesOp->IsDone() && aGeomObjectsMap.find(anId) == aGeomObjectsMap.end()) - { - aGeomObjectsMap[anId] = aGeomObject; - - TopAbs_ShapeEnum aSubShapeType = (TopAbs_ShapeEnum)aGeomObject->GetShapeType(); - if (i == 0) - aGroupType = aSubShapeType; - else if (aSubShapeType != aGroupType) - aGroupType = TopAbs_SHAPE; - } - } - - int aNumberOfGO = aGeomObjectsMap.size(); - if (aNumberOfGO == 1) - myGeomObj = (*aGeomObjectsMap.begin()).second; - else if (aNumberOfGO > 1) - { - GEOM::GEOM_IGroupOperations_var aGroupOp = - geomGen->GetIGroupOperations(aStudy->StudyId()); - if(aGroupOp->_is_nil()) - return; - - GEOM::ListOfGO_var aGeomObjects = new GEOM::ListOfGO(); - aGeomObjects->length( aNumberOfGO ); - - int i = 0; - std::map::iterator anIter; - for (anIter = aGeomObjectsMap.begin(); anIter!=aGeomObjectsMap.end(); anIter++) - aGeomObjects[i++] = (*anIter).second; - - //create geometry group - myGeomObj = aGroupOp->CreateGroup(aMeshShape, aGroupType); - aGroupOp->UnionList(myGeomObj, aGeomObjects); - - if (!aGroupOp->IsDone()) - return; - } - - // publish the GEOM object in study - QString aNewGeomGroupName ( myDlg->myGeomName->text() ); - - SALOMEDS::SObject_var aNewGroupSO = - geomGen->AddInStudy(SMESHGUI::GetSMESHGen()->GetCurrentStudy(), myGeomObj, - aNewGeomGroupName.toLatin1().data(), aMeshShape); + GEOM::GEOM_Gen_var geomGen = SMESH::GetGEOMGen(); + _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); + + if (geomGen->_is_nil() || !aStudy) + return; + + GEOM::GEOM_IShapesOperations_var aShapesOp = + geomGen->GetIShapesOperations(aStudy->StudyId()); + if (aShapesOp->_is_nil() ) + return; + + TopAbs_ShapeEnum aGroupType = TopAbs_SHAPE; + + std::map aGeomObjectsMap; + GEOM::GEOM_Object_var aGeomObject; + + GEOM::GEOM_Object_var aMeshShape = myMesh->GetShapeToMesh(); + + for ( int i = 0; i < aListId.count(); i++ ) + { + aGeomObject = + SMESHGUI::GetSMESHGen()->FindGeometryByMeshElement(myMesh.in(), aListId[i].toInt()); + + if (aGeomObject->_is_nil()) continue; + + double anId = aShapesOp->GetSubShapeIndex(aMeshShape, aGeomObject); + if (aShapesOp->IsDone() && aGeomObjectsMap.find(anId) == aGeomObjectsMap.end()) + { + aGeomObjectsMap[anId] = aGeomObject; + + TopAbs_ShapeEnum aSubShapeType = (TopAbs_ShapeEnum)aGeomObject->GetShapeType(); + if (i == 0) + aGroupType = aSubShapeType; + else if (aSubShapeType != aGroupType) + aGroupType = TopAbs_SHAPE; + } + } + + int aNumberOfGO = aGeomObjectsMap.size(); + if (aNumberOfGO == 1) + myGeomObj = (*aGeomObjectsMap.begin()).second; + else if (aNumberOfGO > 1) + { + GEOM::GEOM_IGroupOperations_var aGroupOp = + geomGen->GetIGroupOperations(aStudy->StudyId()); + if(aGroupOp->_is_nil()) + return; + + GEOM::ListOfGO_var aGeomObjects = new GEOM::ListOfGO(); + aGeomObjects->length( aNumberOfGO ); + + int i = 0; + std::map::iterator anIter; + for (anIter = aGeomObjectsMap.begin(); anIter!=aGeomObjectsMap.end(); anIter++) + aGeomObjects[i++] = (*anIter).second; + + //create geometry group + myGeomObj = aGroupOp->CreateGroup(aMeshShape, aGroupType); + aGroupOp->UnionList(myGeomObj, aGeomObjects); + + if (!aGroupOp->IsDone()) + return; + } + + // publish the GEOM object in study + QString aNewGeomGroupName ( myDlg->myGeomName->text() ); + + SALOMEDS::SObject_var aNewGroupSO = + geomGen->AddInStudy(SMESHGUI::GetSMESHGen()->GetCurrentStudy(), myGeomObj, + aNewGeomGroupName.toLatin1().data(), aMeshShape); } } catch (const SALOME::SALOME_Exception& S_ex) { @@ -422,7 +423,7 @@ void SMESHGUI_ShapeByMeshOp::onSelectionDone() aList.First(), aString); if (nbElems > 0) { if (!myIsMultipleAllowed && nbElems != 1 ) - return; + return; setElementID( aString ); myDlg->setButtonEnabled( true, QtxDialog::OK ); } @@ -485,31 +486,31 @@ void SMESHGUI_ShapeByMeshOp::onElemIdChanged(const QString& theNewText) { if ( SMDS_Mesh* aMesh = actor->GetObject()->GetMesh() ) { - SMDSAbs_ElementType type = SMDSAbs_Edge; - switch ( myDlg->myElemTypeGroup->checkedId() ) { - case EDGE : type = SMDSAbs_Edge; break; - case FACE : type = SMDSAbs_Face; break; - case VOLUME: type = SMDSAbs_Volume; break; - default: return; - } - TColStd_MapOfInteger newIndices; - QStringList aListId = theNewText.split( " ", QString::SkipEmptyParts); - for ( int i = 0; i < aListId.count(); i++ ) { - if ( const SMDS_MeshElement * e = aMesh->FindElement( aListId[ i ].toInt() )) - if ( e->GetType() == type ) - newIndices.Add( e->GetID() ); - } - - if ( !newIndices.IsEmpty() ) - { - if (!myIsMultipleAllowed && newIndices.Extent() != 1) - return; - if ( SVTK_Selector* s = selector() ) { - s->AddOrRemoveIndex( actor->getIO(), newIndices, false ); - viewWindow()->highlight( actor->getIO(), true, true ); - myDlg->setButtonEnabled( true, QtxDialog::OK ); - } - } + SMDSAbs_ElementType type = SMDSAbs_Edge; + switch ( myDlg->myElemTypeGroup->checkedId() ) { + case EDGE : type = SMDSAbs_Edge; break; + case FACE : type = SMDSAbs_Face; break; + case VOLUME: type = SMDSAbs_Volume; break; + default: return; + } + TColStd_MapOfInteger newIndices; + QStringList aListId = theNewText.split( " ", QString::SkipEmptyParts); + for ( int i = 0; i < aListId.count(); i++ ) { + if ( const SMDS_MeshElement * e = aMesh->FindElement( aListId[ i ].toInt() )) + if ( e->GetType() == type ) + newIndices.Add( e->GetID() ); + } + + if ( !newIndices.IsEmpty() ) + { + if (!myIsMultipleAllowed && newIndices.Extent() != 1) + return; + if ( SVTK_Selector* s = selector() ) { + s->AddOrRemoveIndex( actor->getIO(), newIndices, false ); + viewWindow()->highlight( actor->getIO(), true, true ); + myDlg->setButtonEnabled( true, QtxDialog::OK ); + } + } } } } diff --git a/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.h b/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.h index 51f74b810..63477e34c 100644 --- a/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.h +++ b/src/SMESHGUI/SMESHGUI_ShapeByMeshDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_ShapeByMeshDlg.h // Author : Edward AGAPOV, Open CASCADE S.A.S. // diff --git a/src/SMESHGUI/SMESHGUI_SingleEditDlg.cxx b/src/SMESHGUI/SMESHGUI_SingleEditDlg.cxx index 6a27b17a3..f93ce9556 100755 --- a/src/SMESHGUI/SMESHGUI_SingleEditDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_SingleEditDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_SingleEditDlg.cxx // Author : Sergey LITONIN, Open CASCADE S.A.S. // SMESH includes @@ -278,10 +279,10 @@ void SMESHGUI_SingleEditDlg::onHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -312,12 +313,14 @@ static bool findTriangles (const SMDS_MeshNode * theNode1, const SMDS_MeshElement* elem = it->next(); if (elem->GetType() == SMDSAbs_Face && emap.find(elem) != emap.end()) + { if (theTria1) { theTria2 = elem; break; } else { theTria1 = elem; } + } } return (theTria1 && theTria2); } @@ -348,13 +351,13 @@ void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText) int id1, id2; if ( !getNodeIds(myEdge->text(), id1, id2) ) - return; + return; const SMDS_MeshNode* aNode1 = aMesh->FindNode( id1 ); const SMDS_MeshNode* aNode2 = aMesh->FindNode( id2 ); if ( !aNode1 || !aNode2 || aNode1 == aNode2 ) - return; + return; // find a triangle and an edge index const SMDS_MeshElement* tria1; @@ -362,23 +365,23 @@ void SMESHGUI_SingleEditDlg::onTextChange (const QString& theNewText) if ( findTriangles(aNode1,aNode2,tria1,tria2) ) { - newIndices.Add(tria1->GetID()); - - const SMDS_MeshNode* a3Nodes[3]; - SMDS_ElemIteratorPtr it; - int edgeInd = 2, i; - for (i = 0, it = tria1->nodesIterator(); it->more(); i++) { - a3Nodes[ i ] = static_cast(it->next()); - if (i > 0 && ( a3Nodes[ i ] == aNode1 && a3Nodes[ i - 1] == aNode2 || - a3Nodes[ i ] == aNode2 && a3Nodes[ i - 1] == aNode1 ) ) { - edgeInd = i - 1; - break; - } - } - newIndices.Add(-edgeInd-1); - - myOkBtn->setEnabled(true); - myApplyBtn->setEnabled(true); + newIndices.Add(tria1->GetID()); + + const SMDS_MeshNode* a3Nodes[3]; + SMDS_ElemIteratorPtr it; + int edgeInd = 2, i; + for (i = 0, it = tria1->nodesIterator(); it->more(); i++) { + a3Nodes[ i ] = static_cast(it->next()); + if (i > 0 && ( (a3Nodes[ i ] == aNode1 && a3Nodes[ i - 1] == aNode2) || + (a3Nodes[ i ] == aNode2 && a3Nodes[ i - 1] == aNode1) ) ) { + edgeInd = i - 1; + break; + } + } + newIndices.Add(-edgeInd-1); + + myOkBtn->setEnabled(true); + myApplyBtn->setEnabled(true); } mySelector->AddOrRemoveIndex(anIO,newIndices, false); SMESH::GetViewWindow(mySMESHGUI)->highlight( anIO, true, true ); @@ -416,17 +419,17 @@ void SMESHGUI_SingleEditDlg::onSelectionDone() { const SMDS_MeshElement* tria[2]; if( SMESH::GetEdgeNodes( mySelector, aVisualObj, anId1, anId2 ) >= 1 && - findTriangles( aMesh->FindNode( anId1 ), aMesh->FindNode( anId2 ), tria[0],tria[1] ) ) + findTriangles( aMesh->FindNode( anId1 ), aMesh->FindNode( anId2 ), tria[0],tria[1] ) ) { - QString aText = QString("%1-%2").arg(anId1).arg(anId2); - myEdge->setText(aText); - - myOkBtn->setEnabled(true); - myApplyBtn->setEnabled(true); + QString aText = QString("%1-%2").arg(anId1).arg(anId2); + myEdge->setText(aText); + + myOkBtn->setEnabled(true); + myApplyBtn->setEnabled(true); } else { - myEdge->clear(); + myEdge->clear(); } } } @@ -494,8 +497,8 @@ bool SMESHGUI_SingleEditDlg::onApply() if (aMesh->_is_nil()) { SUIT_MessageBox::information(SMESH::GetDesktop(mySMESHGUI), - tr("SMESH_ERROR"), - tr("SMESHG_NO_MESH")); + tr("SMESH_ERROR"), + tr("SMESHG_NO_MESH")); return false; } @@ -513,6 +516,7 @@ bool SMESHGUI_SingleEditDlg::onApply() mySelectionMgr->setSelectedObjects(aList, false); onSelectionDone(); SMESH::UpdateView(); + SMESHGUI::Modified(); } return aResult; diff --git a/src/SMESHGUI/SMESHGUI_SingleEditDlg.h b/src/SMESHGUI/SMESHGUI_SingleEditDlg.h index df0192cb0..cc04610ac 100755 --- a/src/SMESHGUI/SMESHGUI_SingleEditDlg.h +++ b/src/SMESHGUI/SMESHGUI_SingleEditDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESHGUI_SingleEditDlg.h // Author : Sergey LITONIN, Open CASCADE S.A.S. // diff --git a/src/SMESHGUI/SMESHGUI_SmoothingDlg.cxx b/src/SMESHGUI/SMESHGUI_SmoothingDlg.cxx index c2144b1f4..2eb9b521e 100644 --- a/src/SMESHGUI/SMESHGUI_SmoothingDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_SmoothingDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SmoothingDlg.cxx // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -84,6 +85,24 @@ #define SPACING 6 #define MARGIN 11 +/*! + \class BusyLocker + \brief Simple 'busy state' flag locker. + \internal +*/ + +class BusyLocker +{ +public: + //! Constructor. Sets passed boolean flag to \c true. + BusyLocker( bool& busy ) : myBusy( busy ) { myBusy = true; } + //! Destructor. Clear external boolean flag passed as parameter to the constructor to \c false. + ~BusyLocker() { myBusy = false; } +private: + bool& myBusy; //! External 'busy state' boolean flag +}; + + //================================================================================= // function : SMESHGUI_SmoothingDlg() // purpose : constructor @@ -137,8 +156,9 @@ SMESHGUI_SmoothingDlg::SMESHGUI_SmoothingDlg( SMESHGUI* theModule ) LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterElemBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterElemBtn, SIGNAL(clicked()), this, SLOT(setElemFilters())); + LineEditElements->setMaxLength(-1); + myElemFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myElemFilterBtn, SIGNAL(clicked()), this, SLOT(setElemFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -151,6 +171,7 @@ SMESHGUI_SmoothingDlg::SMESHGUI_SmoothingDlg( SMESHGUI* theModule ) LineEditNodes = new QLineEdit(GroupArguments); LineEditNodes->setValidator(myIdValidator); + LineEditNodes->setMaxLength(-1); QPushButton* filterNodeBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); connect(filterNodeBtn, SIGNAL(clicked()), this, SLOT(setNodeFilters())); @@ -175,7 +196,7 @@ SMESHGUI_SmoothingDlg::SMESHGUI_SmoothingDlg( SMESHGUI* theModule ) GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2); - GroupArgumentsLayout->addWidget(filterElemBtn, 0, 3); + GroupArgumentsLayout->addWidget(myElemFilterBtn, 0, 3); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 4); GroupArgumentsLayout->addWidget(TextLabelNodes, 2, 0); GroupArgumentsLayout->addWidget(SelectNodesButton, 2, 1); @@ -229,7 +250,7 @@ SMESHGUI_SmoothingDlg::SMESHGUI_SmoothingDlg( SMESHGUI* theModule ) SpinBox_IterationLimit->setRange(1, 999999); SpinBox_IterationLimit->setValue(20); - SpinBox_AspectRatio->RangeStepAndValidator(0.0, +999999.999, 0.1, 3); + SpinBox_AspectRatio->RangeStepAndValidator(0.0, +999999.999, 0.1, "parametric_precision"); SpinBox_AspectRatio->SetValue(1.1); GroupArguments->show(); @@ -241,12 +262,8 @@ SMESHGUI_SmoothingDlg::SMESHGUI_SmoothingDlg( SMESHGUI* theModule ) mySMESHGUI->SetActiveDialogBox(this); // Costruction of the logical filter for the elements: mesh/sub-mesh/group - SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (MESHorSUBMESH); - SMESH_TypeFilter* aSmeshGroupFilter = new SMESH_TypeFilter (GROUP); - QList aListOfFilters; - if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter); - if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); + aListOfFilters << new SMESH_TypeFilter(MESHorSUBMESH) << new SMESH_TypeFilter(GROUP); myMeshOrSubMeshOrGroupFilter = new SMESH_LogicalFilter (aListOfFilters, SMESH_LogicalFilter::LO_OR); @@ -306,10 +323,10 @@ void SMESHGUI_SmoothingDlg::Init() LineEditElements->setFocus(); LineEditElements->clear(); LineEditNodes->clear(); - myElementsId = ""; myNbOkElements = 0; myNbOkNodes = 0; myActor = 0; + myIO.Nullify(); myMesh = SMESH::SMESH_Mesh::_nil(); CheckBoxMesh->setChecked(false); @@ -329,7 +346,7 @@ bool SMESHGUI_SmoothingDlg::ClickOnApply() return false; if (myNbOkElements && (myNbOkNodes || LineEditNodes->text().trimmed().isEmpty())) { - QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + QStringList aListElementsId = LineEditElements->text().split(" ", QString::SkipEmptyParts); QStringList aListNodesId = LineEditNodes->text().split(" ", QString::SkipEmptyParts); SMESH::long_array_var anElementsId = new SMESH::long_array; @@ -363,35 +380,31 @@ bool SMESHGUI_SmoothingDlg::ClickOnApply() SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + myMesh->SetParameters( aParameters.join(":").toLatin1().constData() ); + if ( CheckBoxParametric->isChecked() ) { if(CheckBoxMesh->isChecked()) - aResult = aMeshEditor->SmoothParametricObject(mySelectedObject, aNodesId.inout(), - anIterationLimit, aMaxAspectRatio, aMethod); - else - aResult = aMeshEditor->SmoothParametric(anElementsId.inout(), aNodesId.inout(), - anIterationLimit, aMaxAspectRatio, aMethod); + aResult = aMeshEditor->SmoothParametricObject(mySelectedObject, aNodesId.inout(), + anIterationLimit, aMaxAspectRatio, aMethod); + else + aResult = aMeshEditor->SmoothParametric(anElementsId.inout(), aNodesId.inout(), + anIterationLimit, aMaxAspectRatio, aMethod); } else { if(CheckBoxMesh->isChecked()) - aResult = aMeshEditor->SmoothObject(mySelectedObject, aNodesId.inout(), - anIterationLimit, aMaxAspectRatio, aMethod); - else - aResult = aMeshEditor->Smooth(anElementsId.inout(), aNodesId.inout(), - anIterationLimit, aMaxAspectRatio, aMethod); + aResult = aMeshEditor->SmoothObject(mySelectedObject, aNodesId.inout(), + anIterationLimit, aMaxAspectRatio, aMethod); + else + aResult = aMeshEditor->Smooth(anElementsId.inout(), aNodesId.inout(), + anIterationLimit, aMaxAspectRatio, aMethod); } - myMesh->SetParameters( SMESHGUI::JoinObjectParameters(aParameters) ); - } catch (...) { } if (aResult) { - Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); - - SALOME_ListIO aList; - aList.Append(anIO); - mySelectionMgr->setSelectedObjects(aList, false); - SMESH::UpdateView(); + SMESH::Update(myIO, SMESH::eDisplay); + SMESHGUI::Modified(); Init(); mySelectedObject = SMESH::SMESH_IDSource::_nil(); @@ -448,10 +461,10 @@ void SMESHGUI_SmoothingDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -464,10 +477,10 @@ void SMESHGUI_SmoothingDlg::onTextChange (const QString& theNewText) QLineEdit* send = (QLineEdit*)sender(); // return if busy - if (myBusy) return; + if (myBusy || myIO.IsNull()) return; // set busy flag - myBusy = true; + BusyLocker lock( myBusy ); if (send == LineEditElements) myNbOkElements = 0; @@ -478,40 +491,37 @@ void SMESHGUI_SmoothingDlg::onTextChange (const QString& theNewText) buttonApply->setEnabled(false); // hilight entered elements/nodes - SMDS_Mesh* aMesh = 0; - if (myActor) - aMesh = myActor->GetObject()->GetMesh(); - - if (aMesh) { - - QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); - - if (send == LineEditElements) { - const Handle(SALOME_InteractiveObject)& anIO = myActor->getIO(); - TColStd_MapOfInteger newIndices; - for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - myNbOkElements++; + SMDS_Mesh* aMesh = myActor ? myActor->GetObject()->GetMesh() : 0; + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); + + if (send == LineEditElements) { + TColStd_MapOfInteger newIndices; + for (int i = 0; i < aListId.count(); i++) { + int id = aListId[ i ].toInt(); + if ( id > 0 ) { + bool validId = aMesh ? ( aMesh->FindElement( id ) != 0 ) : ( myMesh->GetElementType( id, true ) != SMESH::EDGE ); + if ( validId ) + newIndices.Add( id ); } - mySelector->AddOrRemoveIndex(anIO, newIndices, false); + myNbOkElements = newIndices.Extent(); + mySelector->AddOrRemoveIndex(myIO, newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( anIO, true, true ); - myElementsId = theNewText; - - } else if (send == LineEditNodes) { - TColStd_MapOfInteger newIndices; - - for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshNode * n = aMesh->FindNode(aListId[ i ].toInt()); - if (n) - newIndices.Add(n->GetID()); - myNbOkNodes++; + aViewWindow->highlight( myIO, true, true ); + } + } + else if (send == LineEditNodes) { + TColStd_MapOfInteger newIndices; + for (int i = 0; i < aListId.count(); i++) { + int id = aListId[ i ].toInt(); + if ( id > 0 ) { + bool validId = aMesh ? ( aMesh->FindNode( id ) != 0 ) : ( myMesh->GetElementType( id, false ) != SMESH::EDGE ); + if ( validId ) + newIndices.Add( id ); } - mySelector->AddOrRemoveIndex(myActor->getIO(), newIndices, false); + myNbOkNodes = newIndices.Extent(); + mySelector->AddOrRemoveIndex(myIO, newIndices, false); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( myActor->getIO(), true, true ); + aViewWindow->highlight( myIO, true, true ); } } @@ -519,8 +529,6 @@ void SMESHGUI_SmoothingDlg::onTextChange (const QString& theNewText) buttonOk->setEnabled(true); buttonApply->setEnabled(true); } - - myBusy = false; } //================================================================================= @@ -534,19 +542,23 @@ void SMESHGUI_SmoothingDlg::SelectionIntoArgument() // clear QString aString = ""; - myBusy = true; + // set busy flag + BusyLocker lock( myBusy ); + if (myEditCurrentArgument == LineEditElements || myEditCurrentArgument == LineEditNodes) { myEditCurrentArgument->setText(aString); - if (myEditCurrentArgument == LineEditElements) + if (myEditCurrentArgument == LineEditElements) { myNbOkElements = 0; - else + myActor = 0; + myIO.Nullify(); + } + else { myNbOkNodes = 0; + } buttonOk->setEnabled(false); buttonApply->setEnabled(false); - myActor = 0; } - myBusy = false; if (!GroupButtons->isEnabled()) // inactive return; @@ -559,50 +571,45 @@ void SMESHGUI_SmoothingDlg::SelectionIntoArgument() return; Handle(SALOME_InteractiveObject) IO = aList.First(); - myMesh = SMESH::GetMeshByIO(IO); - if (myMesh->_is_nil()) - return; - - myActor = SMESH::FindActorByObject(myMesh); - if (!myActor) - return; - - int aNbUnits = 0; if (myEditCurrentArgument == LineEditElements) { - myElementsId = ""; + myMesh = SMESH::GetMeshByIO(IO); + if (myMesh->_is_nil()) + return; + myIO = IO; + myActor = SMESH::FindActorByObject(myMesh); if (CheckBoxMesh->isChecked()) { SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aString); - if (!SMESH::IObjectToInterface(IO)->_is_nil()) - mySelectedObject = SMESH::IObjectToInterface(IO); + SMESH::SMESH_IDSource_var obj = SMESH::IObjectToInterface( myIO ); + if ( !CORBA::is_nil( obj ) ) + mySelectedObject = obj; else return; + myNbOkElements = true; } else { - aNbUnits = SMESH::GetNameOfSelectedElements(mySelector, IO, aString); - myElementsId = aString; - if (aNbUnits < 1) + // get indices of selected elements + TColStd_IndexedMapOfInteger aMapIndex; + mySelector->GetIndex(IO,aMapIndex); + myNbOkElements = aMapIndex.Extent(); + + if (myNbOkElements < 1) return; + + QStringList elements; + for ( int i = 0; i < myNbOkElements; ++i ) + elements << QString::number( aMapIndex( i+1 ) ); + aString = elements.join(" "); } - } else if (myEditCurrentArgument == LineEditNodes && !myMesh->_is_nil() && myActor) { - myNbOkNodes = 0; - aNbUnits = SMESH::GetNameOfSelectedNodes(mySelector, IO, aString); - } else { + } else if (myEditCurrentArgument == LineEditNodes && !myMesh->_is_nil() && myIO == IO ) { + myNbOkNodes = SMESH::GetNameOfSelectedNodes(mySelector, IO, aString); } - myBusy = true; myEditCurrentArgument->setText(aString); myEditCurrentArgument->repaint(); myEditCurrentArgument->setEnabled(false); // to update lineedit IPAL 19809 myEditCurrentArgument->setEnabled(true); - myBusy = false; - - // OK - if (myEditCurrentArgument == LineEditElements) - myNbOkElements = aNbUnits; - else if (myEditCurrentArgument == LineEditNodes) - myNbOkNodes = aNbUnits; if (myNbOkElements && (myNbOkNodes || LineEditNodes->text().trimmed().isEmpty())) { buttonOk->setEnabled(true); @@ -629,21 +636,21 @@ void SMESHGUI_SmoothingDlg::SetEditCurrentArgument() myEditCurrentArgument = LineEditElements; SMESH::SetPointRepresentation(false); if (CheckBoxMesh->isChecked()) { - // mySelectionMgr->setSelectionModes(ActorSelection); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); - mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); + // mySelectionMgr->setSelectionModes(ActorSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(ActorSelection); + mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(FaceSelection); - } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(FaceSelection); + } } else if (send == SelectNodesButton) { - LineEditNodes->clear(); + LineEditNodes->clear(); myEditCurrentArgument = LineEditNodes; SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) { - aViewWindow->SetSelectionMode(NodeSelection); - } + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) { + aViewWindow->SetSelectionMode(NodeSelection); + } } myEditCurrentArgument->setFocus(); @@ -727,6 +734,7 @@ void SMESHGUI_SmoothingDlg::onSelectMesh (bool toSelectMesh) TextLabelElements->setText(tr("SMESH_NAME")); else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myElemFilterBtn->setEnabled(!toSelectMesh); if (myEditCurrentArgument != LineEditElements && myEditCurrentArgument != LineEditNodes) { @@ -779,6 +787,12 @@ void SMESHGUI_SmoothingDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_SmoothingDlg::setFilters( const bool theIsElem ) { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) { QList types; diff --git a/src/SMESHGUI/SMESHGUI_SmoothingDlg.h b/src/SMESHGUI/SMESHGUI_SmoothingDlg.h index 40b8170a3..efb9723a6 100644 --- a/src/SMESHGUI/SMESHGUI_SmoothingDlg.h +++ b/src/SMESHGUI/SMESHGUI_SmoothingDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SmoothingDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -32,6 +33,9 @@ // Qt includes #include +// SALOME GUI includes +#include + // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Mesh) @@ -78,7 +82,6 @@ private: SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ - QString myElementsId; int myNbOkElements; /* to check when elements are defined */ int myNbOkNodes; /* to check when fixed nodes are defined */ int myConstructorId; /* Current constructor id = radio button id */ @@ -90,6 +93,7 @@ private: bool myBusy; SMESH::SMESH_Mesh_var myMesh; SMESH_Actor* myActor; + Handle(SALOME_InteractiveObject) myIO; SMESH_LogicalFilter* myMeshOrSubMeshOrGroupFilter; QGroupBox* GroupConstructors; @@ -116,7 +120,8 @@ private: QCheckBox* CheckBoxParametric; QString myHelpFileName; - + + QPushButton* myElemFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; private slots: diff --git a/src/SMESHGUI/SMESHGUI_SpinBox.cxx b/src/SMESHGUI/SMESHGUI_SpinBox.cxx index 1b30cb08a..cda1357f0 100644 --- a/src/SMESHGUI/SMESHGUI_SpinBox.cxx +++ b/src/SMESHGUI/SMESHGUI_SpinBox.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SpinBox.cxx // Author : Lucien PIGNOLONI, Open CASCADE S.A.S. @@ -26,9 +27,12 @@ // #include "SMESHGUI_SpinBox.h" +#include +#include + // Qt includes -#include #include +#include //================================================================================= // class : SMESHGUI_SpinBox() @@ -62,7 +66,7 @@ void SMESHGUI_SpinBox::SetStep( double newStep ) //================================================================================= void SMESHGUI_SpinBox::SetValue( double v ) { - setValue(v); + setValue(valueFromText(textFromValue(v))); editor()->setCursorPosition( 0 ); } @@ -93,28 +97,29 @@ QLineEdit* SMESHGUI_SpinBox::editor() const return SalomeApp_DoubleSpinBox::lineEdit(); } -//================================================================================= -// function : validator() -// purpose : returns validator -//================================================================================= -QDoubleValidator* SMESHGUI_SpinBox::validator() const -{ - return (QDoubleValidator*)editor()->validator(); -} - //================================================================================= // function : RangeStepAndValidator() // purpose : //================================================================================= void SMESHGUI_SpinBox::RangeStepAndValidator( double min, - double max, - double step, - unsigned short precision ) + double max, + double step, + const char* quantity ) { - setPrecision(precision*(-1)); // PAL8769. Minus is for using 'g' double->string conversion specifier, - // see QtxDoubleSpinBox::mapValueToText( double v ) - setDecimals(32); + // Obtain precision from preferences + SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr(); + int precision = resMgr->integerValue( "SMESH", quantity, -3 ); + + setPrecision(precision); // PAL8769. Minus is for using 'g' double->string conversion specifier, + // see QtxDoubleSpinBox::mapValueToText( double v ) + // san: this can be achieved using preferences + setDecimals( 20 ); // qAbs(precision) setRange(min, max); setSingleStep( step ); setDefaultValue( min ); + + // Add a hint for the user saying how to tune precision + QString userPropName = QObject::tr( QString( "SMESH_PREF_%1" ).arg( quantity ).toLatin1().constData() ); + setProperty( "validity_tune_hint", + QVariant( QObject::tr( "SMESH_PRECISION_HINT" ).arg( userPropName ) ) ); } diff --git a/src/SMESHGUI/SMESHGUI_SpinBox.h b/src/SMESHGUI/SMESHGUI_SpinBox.h index cdca31653..d12cd6560 100644 --- a/src/SMESHGUI/SMESHGUI_SpinBox.h +++ b/src/SMESHGUI/SMESHGUI_SpinBox.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SpinBox.h // Author : Lucien PIGNOLONI, Open CASCADE S.A.S. @@ -52,14 +53,13 @@ public: ~SMESHGUI_SpinBox(); void RangeStepAndValidator( double = -1000000.0, - double = +1000000.0, - double = 100.0, - unsigned short = 3 ); + double = +1000000.0, + double = 100.0, + const char* = "length_precision" ); void SetValue( double ); double GetValue() const; QString GetString() const; QLineEdit* editor() const; - QDoubleValidator* validator() const; public slots: void SetStep( double ); diff --git a/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.cxx b/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.cxx deleted file mode 100644 index 4b7540fc0..000000000 --- a/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.cxx +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_StandardMeshInfosDlg.cxx -// Author : Michael ZORIN, Open CASCADE S.A.S. -// SMESH includes -// -#include "SMESHGUI_StandardMeshInfosDlg.h" - -#include "SMESHGUI.h" -#include "SMESHGUI_Utils.h" -#include "SMESHGUI_MeshUtils.h" - -#include - -// SALOME KERNEL includes -#include -#include - -// SALOME GUI includes -#include -#include -#include -#include -#include - -#include -#include - -#include - -// Qt includes -#include -#include -#include -#include -#include -#include -#include -#include - -// IDL includes -#include -#include CORBA_SERVER_HEADER(SMESH_Mesh) -#include CORBA_SERVER_HEADER(SMESH_Group) - -#define SPACING 6 -#define MARGIN 11 - -//================================================================================= -/*! - * SMESHGUI_StandardMeshInfosDlg::SMESHGUI_StandardMeshInfosDlg - * - * Constructor - */ -//================================================================================= -SMESHGUI_StandardMeshInfosDlg::SMESHGUI_StandardMeshInfosDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) -{ - setModal(false); - setAttribute(Qt::WA_DeleteOnClose, true); - setWindowTitle(tr("SMESH_STANDARD_MESHINFO_TITLE")); - setSizeGripEnabled(true); - - myStartSelection = true; - myIsActiveWindow = true; - - // dialog layout - QVBoxLayout* aDlgLayout = new QVBoxLayout(this); - aDlgLayout->setSpacing(SPACING); - aDlgLayout->setMargin(MARGIN); - - // mesh group box - myMeshGroup = new QGroupBox(tr("SMESH_MESH"), this); - QHBoxLayout* myMeshGroupLayout = new QHBoxLayout(myMeshGroup); - myMeshGroupLayout->setSpacing(SPACING); - myMeshGroupLayout->setMargin(MARGIN); - - // select button, label and line edit with mesh name - myNameLab = new QLabel(tr("SMESH_NAME"), myMeshGroup); - myMeshGroupLayout->addWidget(myNameLab); - - QPixmap image0(SUIT_Session::session()->resourceMgr()->loadPixmap("SMESH",tr("ICON_SELECT"))); - mySelectBtn = new QPushButton(myMeshGroup); - mySelectBtn->setIcon(image0); - myMeshGroupLayout->addWidget(mySelectBtn); - - myMeshLine = new QLineEdit(myMeshGroup); - myMeshGroupLayout->addWidget(myMeshLine); - - aDlgLayout->addWidget(myMeshGroup); - - // information group box - myInfoGroup = new QGroupBox(tr("SMESH_INFORMATION"), this); - QVBoxLayout* myInfoGroupLayout = new QVBoxLayout(myInfoGroup); - myInfoGroupLayout->setSpacing(SPACING); - myInfoGroupLayout->setMargin(MARGIN); - - // information text browser - myInfo = new QTextBrowser(myInfoGroup); - myInfo->setMinimumSize(200, 150); - myInfoGroupLayout->addWidget(myInfo); - - aDlgLayout->addWidget(myInfoGroup); - - // buttons group - myButtonsGroup = new QGroupBox(this); - QHBoxLayout* myButtonsGroupLayout = new QHBoxLayout(myButtonsGroup); - myButtonsGroupLayout->setSpacing(SPACING); - myButtonsGroupLayout->setMargin(MARGIN); - - // buttons --> OK and Help buttons - myOkBtn = new QPushButton(tr("SMESH_BUT_OK"), myButtonsGroup); - myOkBtn->setAutoDefault(true); myOkBtn->setDefault(true); - myHelpBtn = new QPushButton(tr("SMESH_BUT_HELP"), myButtonsGroup); - myHelpBtn->setAutoDefault(true); - - myButtonsGroupLayout->addWidget(myOkBtn); - myButtonsGroupLayout->addSpacing(10); - myButtonsGroupLayout->addStretch(); - myButtonsGroupLayout->addWidget(myHelpBtn); - - aDlgLayout->addWidget(myButtonsGroup); - - mySMESHGUI->SetActiveDialogBox(this); - - // connect signals - connect( myOkBtn, SIGNAL(clicked()), this, SLOT(close())); - connect( myHelpBtn, SIGNAL(clicked()), this, SLOT(onHelp())); - connect( mySelectBtn, SIGNAL(clicked()), this, SLOT(onStartSelection())); - connect( mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(close())); - connect( mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); - connect( mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onSelectionChanged())); - - // init dialog with current selection - myMeshFilter = new SMESH_TypeFilter (MESH); - mySelectionMgr->installFilter(myMeshFilter); - onSelectionChanged(); - - myHelpFileName = "mesh_infos_page.html#standard_mesh_infos_anchor"; -} - -//================================================================================= -/*! - * SMESHGUI_StandardMeshInfosDlg::~SMESHGUI_StandardMeshInfosDlg - * - * Destructor - */ -//================================================================================= -SMESHGUI_StandardMeshInfosDlg::~SMESHGUI_StandardMeshInfosDlg() -{ -} - -//================================================================================= -/*! - * SMESHGUI_StandardMeshInfosDlg::DumpMeshInfos - */ -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::DumpMeshInfos() -{ - SUIT_OverrideCursor wc; - - SALOME_ListIO aList; - mySelectionMgr->selectedObjects(aList); - - int nbSel = aList.Extent(); - myInfo->clear(); - if (nbSel == 1) { - myStartSelection = false; - myMeshLine->setText(""); - SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(aList.First()); - - if (!aMesh->_is_nil()) { - QString aName, anInfo; - SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aName); - myMeshLine->setText(aName); - int aNbNodes = (int)aMesh->NbNodes(); - int aNbEdges = (int)aMesh->NbEdges(); - int aNbFaces = (int)aMesh->NbFaces(); - int aNbVolumes = (int)aMesh->NbVolumes(); - - int aDimension = 0; - double aNbDimElements = 0; - if (aNbVolumes > 0) { - aNbDimElements = aNbVolumes; - aDimension = 3; - } - else if(aNbFaces > 0) { - aNbDimElements = aNbFaces; - aDimension = 2; - } - else if(aNbEdges > 0) { - aNbDimElements = aNbEdges; - aDimension = 1; - } - else if(aNbNodes > 0) { - aNbDimElements = aNbNodes; - aDimension = 0; - } - - // information about the mesh - anInfo.append(QString("Nb of element of dimension %1: %2
                    ").arg(aDimension).arg(aNbDimElements)); - anInfo.append(QString("Nb of nodes: %1

                    ").arg(aNbNodes)); - - // information about the groups of the mesh - _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); - _PTR(SObject) aMeshSO = SMESH::FindSObject(aMesh); - _PTR(SObject) anObj; - - bool hasGroup = false; - - // info about groups on nodes - aMeshSO->FindSubObject(SMESH::Tag_NodeGroups, anObj); - if (anObj) { - _PTR(ChildIterator) it = aStudy->NewChildIterator(anObj); - if (it->More()) { - anInfo.append(QString("Groups:

                    ")); - hasGroup = true; - } - for ( ; it->More(); it->Next()) { - _PTR(SObject) subObj = it->Value(); - CORBA::Object_var anObject = SMESH::SObjectToObject(subObj); - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObject); - if (!aGroup->_is_nil()) { - anInfo.append(QString("- %1
                    ").arg(aGroup->GetName())); - anInfo.append(QString("%1
                    ").arg("on nodes")); - anInfo.append(QString("%1
                    ").arg(aGroup->Size())); - // check if the group based on geometry - SMESH::SMESH_GroupOnGeom_var aGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow(aGroup); - if (!aGroupOnGeom->_is_nil()) { - GEOM::GEOM_Object_var aGroupMainShape = aGroupOnGeom->GetShape(); - QString aShapeName = ""; - _PTR(SObject) aGeomObj, aRef; - if (subObj->FindSubObject(1, aGeomObj) && aGeomObj->ReferencedObject(aRef)) - aShapeName = aRef->GetName().c_str(); - anInfo.append(QString("based on %1 geometry object

                    ").arg(aShapeName)); - } else { - anInfo.append(QString("
                    ")); - } - } - } - } - - // info about groups on edges - anObj.reset(); - aMeshSO->FindSubObject(SMESH::Tag_EdgeGroups, anObj); - if (anObj) { - _PTR(ChildIterator) it = aStudy->NewChildIterator(anObj); - if (!hasGroup && it->More()) { - anInfo.append(QString("Groups:

                    ")); - hasGroup = true; - } - for ( ; it->More(); it->Next()) { - _PTR(SObject) subObj = it->Value(); - CORBA::Object_var anObject = SMESH::SObjectToObject(subObj); - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObject); - if (!aGroup->_is_nil()) { - anInfo.append(QString("- %1
                    ").arg(aGroup->GetName())); - anInfo.append(QString("%1
                    ").arg("on edges")); - anInfo.append(QString("%1
                    ").arg(aGroup->Size())); - // check if the group based on geometry - SMESH::SMESH_GroupOnGeom_var aGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow(aGroup); - if (!aGroupOnGeom->_is_nil()) { - GEOM::GEOM_Object_var aGroupMainShape = aGroupOnGeom->GetShape(); - QString aShapeName = ""; - _PTR(SObject) aGeomObj, aRef; - if (subObj->FindSubObject(1, aGeomObj) && aGeomObj->ReferencedObject(aRef)) - aShapeName = aRef->GetName().c_str(); - anInfo.append(QString("based on %1 geometry object

                    ").arg(aShapeName)); - } else { - anInfo.append(QString("
                    ")); - } - } - } - } - - // info about groups on faces - anObj.reset(); - aMeshSO->FindSubObject(SMESH::Tag_FaceGroups, anObj); - if (anObj) { - _PTR(ChildIterator) it = aStudy->NewChildIterator(anObj); - if (!hasGroup && it->More()) { - anInfo.append(QString("Groups:

                    ")); - hasGroup = true; - } - for ( ; it->More(); it->Next()) { - _PTR(SObject) subObj = it->Value(); - CORBA::Object_var anObject = SMESH::SObjectToObject(subObj); - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObject); - if (!aGroup->_is_nil()) { - anInfo.append(QString("- %1
                    ").arg(aGroup->GetName())); - anInfo.append(QString("%1
                    ").arg("on faces")); - anInfo.append(QString("%1
                    ").arg(aGroup->Size())); - // check if the group based on geometry - SMESH::SMESH_GroupOnGeom_var aGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow(aGroup); - if (!aGroupOnGeom->_is_nil()) { - GEOM::GEOM_Object_var aGroupMainShape = aGroupOnGeom->GetShape(); - QString aShapeName = ""; - _PTR(SObject) aGeomObj, aRef; - if (subObj->FindSubObject(1, aGeomObj) && aGeomObj->ReferencedObject(aRef)) - aShapeName = aRef->GetName().c_str(); - anInfo.append(QString("based on %1 geometry object

                    ").arg(aShapeName)); - } else { - anInfo.append(QString("
                    ")); - } - } - } - } - - // info about groups on volumes - anObj.reset(); - aMeshSO->FindSubObject(SMESH::Tag_VolumeGroups, anObj); - if (anObj) { - _PTR(ChildIterator) it = aStudy->NewChildIterator(anObj); - if (!hasGroup && it->More()) - anInfo.append(QString("Groups:
                    ")); - for ( ; it->More(); it->Next()) { - _PTR(SObject) subObj = it->Value(); - CORBA::Object_var anObject = SMESH::SObjectToObject(subObj); - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObject); - if (!aGroup->_is_nil()) { - anInfo.append(QString("- %1
                    ").arg(aGroup->GetName())); - anInfo.append(QString("%1
                    ").arg("on volumes")); - anInfo.append(QString("%1
                    ").arg(aGroup->Size())); - // check if the group based on geometry - SMESH::SMESH_GroupOnGeom_var aGroupOnGeom = SMESH::SMESH_GroupOnGeom::_narrow(aGroup); - if (!aGroupOnGeom->_is_nil()) { - GEOM::GEOM_Object_var aGroupMainShape = aGroupOnGeom->GetShape(); - QString aShapeName = ""; - _PTR(SObject) aGeomObj, aRef; - if (subObj->FindSubObject(1, aGeomObj) && aGeomObj->ReferencedObject(aRef)) - aShapeName = aRef->GetName().c_str(); - anInfo.append(QString("based on %1 geometry object

                    ").arg(aShapeName)); - } else { - anInfo.append(QString("
                    ")); - } - } - } - } - - myInfo->setText(anInfo); - return; - } - } -} - -//================================================================================= -// function : SelectionIntoArgument() -// purpose : Called when selection has changed -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::onSelectionChanged() -{ - if (myStartSelection) - DumpMeshInfos(); -} - -//================================================================================= -// function : closeEvent() -// purpose : -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::closeEvent (QCloseEvent* e) -{ - mySelectionMgr->clearFilters(); - mySMESHGUI->ResetState(); - QDialog::closeEvent(e); -} - -//================================================================================= -// function : windowActivationChange() -// purpose : called when window is activated/deactivated -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::windowActivationChange (bool oldActive) -{ - QDialog::windowActivationChange(oldActive); - if (isActiveWindow() && myIsActiveWindow != isActiveWindow()) - ActivateThisDialog(); - myIsActiveWindow = isActiveWindow(); -} - -//================================================================================= -// function : DeactivateActiveDialog() -// purpose : -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::DeactivateActiveDialog() -{ - disconnect(mySelectionMgr, 0, this, 0); -} - -//================================================================================= -// function : ActivateThisDialog() -// purpose : -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::ActivateThisDialog() -{ - /* Emit a signal to deactivate any active dialog */ - mySMESHGUI->EmitSignalDeactivateDialog(); - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(onSelectionChanged())); -} - -//================================================================================= -// function : onStartSelection() -// purpose : starts selection -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::onStartSelection() -{ - myStartSelection = true; - mySelectionMgr->installFilter(myMeshFilter); - myMeshLine->setText(tr("Select a mesh")); - onSelectionChanged(); - myStartSelection = true; -} - -//================================================================================= -// function : onHelp() -// purpose : -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::onHelp() -{ - LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); - else { - QString platform; -#ifdef WIN32 - platform = "winapplication"; -#else - platform = "application"; -#endif - SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); - } -} - -//================================================================================= -// function : keyPressEvent() -// purpose : -//================================================================================= -void SMESHGUI_StandardMeshInfosDlg::keyPressEvent( QKeyEvent* e ) -{ - QDialog::keyPressEvent( e ); - if ( e->isAccepted() ) - return; - - if ( e->key() == Qt::Key_F1 ) { - e->accept(); - onHelp(); - } -} diff --git a/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.h b/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.h deleted file mode 100644 index e6e34328a..000000000 --- a/src/SMESHGUI/SMESHGUI_StandardMeshInfosDlg.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_StandardMeshInfosDlg.h -// Author : Michael ZORIN, Open CASCADE S.A.S. -// -#ifndef SMESHGUI_STANDARDMESHINFOSDLG_H -#define SMESHGUI_STANDARDMESHINFOSDLG_H - -// SMESH includes -#include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include - -class QGroupBox; -class QLabel; -class QPushButton; -class QLineEdit; -class QTextBrowser; -class SMESHGUI; -class LightApp_SelectionMgr; -class SUIT_SelectionFilter; - -class SMESHGUI_EXPORT SMESHGUI_StandardMeshInfosDlg : public QDialog -{ - Q_OBJECT - -public: - SMESHGUI_StandardMeshInfosDlg( SMESHGUI* ); - ~SMESHGUI_StandardMeshInfosDlg(); - -protected: - void closeEvent( QCloseEvent* ); - void keyPressEvent( QKeyEvent* ); - void windowActivationChange( bool ); - void DumpMeshInfos(); - -private slots: - void onSelectionChanged(); - void DeactivateActiveDialog(); - void ActivateThisDialog(); - void onStartSelection(); - void onHelp(); - -private: - SMESHGUI* mySMESHGUI; - LightApp_SelectionMgr* mySelectionMgr; - bool myStartSelection; - bool myIsActiveWindow; - - SUIT_SelectionFilter* myMeshFilter; - - QLabel* myNameLab; - QPushButton* mySelectBtn; - QLineEdit* myMeshLine; - - QTextBrowser* myInfo; - - QGroupBox* myMeshGroup; - QGroupBox* myInfoGroup; - - QGroupBox* myButtonsGroup; - QPushButton* myOkBtn; - QPushButton* myHelpBtn; - - QString myHelpFileName; -}; - -#endif // SMESHGUI_STANDARDMESHINFOSDLG_H diff --git a/src/SMESHGUI/SMESHGUI_SymmetryDlg.cxx b/src/SMESHGUI/SMESHGUI_SymmetryDlg.cxx index ed49f5217..5f4b1e206 100644 --- a/src/SMESHGUI/SMESHGUI_SymmetryDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_SymmetryDlg.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_SymmetryDlg.cxx -// Author : Michael ZORIN, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_SymmetryDlg.cxx +// Author : Michael ZORIN, Open CASCADE S.A.S. +// SMESH includes + #include "SMESHGUI_SymmetryDlg.h" #include "SMESHGUI.h" @@ -33,6 +32,7 @@ #include "SMESHGUI_MeshUtils.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" #include #include @@ -83,14 +83,17 @@ enum { MOVE_ELEMS_BUTTON = 0, COPY_ELEMS_BUTTON, MAKE_MESH_BUTTON }; //!< action #define SPACING 6 #define MARGIN 11 +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //================================================================================= // class : SMESHGUI_SymmetryDlg() // purpose : //================================================================================= SMESHGUI_SymmetryDlg::SMESHGUI_SymmetryDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), + : SMESHGUI_PreviewDlg( theModule ), mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), myFilterDlg(0), mySelectedObject(SMESH::SMESH_IDSource::_nil()) @@ -144,8 +147,9 @@ SMESHGUI_SymmetryDlg::SMESHGUI_SymmetryDlg( SMESHGUI* theModule ) SelectElementsButton->setIcon(image3); LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -220,16 +224,21 @@ SMESHGUI_SymmetryDlg::SMESHGUI_SymmetryDlg( SMESHGUI* theModule ) // Name of a mesh to create LineEditNewMesh = new QLineEdit(GroupArguments); + + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + // layout GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2, 1, 1); - GroupArgumentsLayout->addWidget(filterBtn, 0, 3); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 3); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 4); GroupArgumentsLayout->addWidget(GroupMirror, 2, 0, 1, 4); GroupArgumentsLayout->addWidget(ActionBox, 3, 0, 3, 3); GroupArgumentsLayout->addWidget(MakeGroupsCheck, 4, 3); GroupArgumentsLayout->addWidget(LineEditNewMesh, 5, 3); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 6, 0); /***************************************************************/ GroupButtons = new QGroupBox(this); @@ -261,12 +270,12 @@ SMESHGUI_SymmetryDlg::SMESHGUI_SymmetryDlg( SMESHGUI* theModule ) SMESHGUI_SymmetryDlgLayout->addWidget(GroupButtons); /* Initialisations */ - SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); - SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, 3); + SpinBox_X->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Y->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_Z->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DX->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DY->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox_DZ->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); RadioButton1->setChecked(true); @@ -312,6 +321,16 @@ SMESHGUI_SymmetryDlg::SMESHGUI_SymmetryDlg( SMESHGUI* theModule ) connect(CheckBoxMesh, SIGNAL(toggled(bool)), SLOT(onSelectMesh(bool))); connect(ActionGroup, SIGNAL(buttonClicked(int)), SLOT(onActionClicked(int))); + connect(SpinBox_X, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Y, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_Z, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DX, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DY, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox_DZ, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + //To Connect preview check box + connectPreviewControl(); + ConstructorsClicked(0); SelectionIntoArgument(); onActionClicked(MOVE_ELEMS_BUTTON); @@ -358,6 +377,9 @@ void SMESHGUI_SymmetryDlg::Init (bool ResetControls) ActionGroup->button( MOVE_ELEMS_BUTTON )->setChecked(true); CheckBoxMesh->setChecked(false); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); + // MakeGroupsCheck->setChecked(false); // MakeGroupsCheck->setEnabled(false); onSelectMesh(false); @@ -416,8 +438,8 @@ void SMESHGUI_SymmetryDlg::ConstructorsClicked (int constructorId) SMESH::SetPointRepresentation(false); if (!CheckBoxMesh->isChecked()) { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode(CellSelection); } } @@ -429,9 +451,11 @@ void SMESHGUI_SymmetryDlg::ConstructorsClicked (int constructorId) connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + onDisplaySimulation(true); + QApplication::instance()->processEvents(); updateGeometry(); - resize( minimumSize() ); + resize(100,100); } //================================================================================= @@ -456,17 +480,8 @@ bool SMESHGUI_SymmetryDlg::ClickOnApply() anElementsId[i] = aListElementsId[i].toInt(); SMESH::AxisStruct aMirror; - - aMirror.x = SpinBox_X->GetValue(); - aMirror.y = SpinBox_Y->GetValue(); - aMirror.z = SpinBox_Z->GetValue(); - if (GetConstructorId() == 0) { - aMirror.vx = aMirror.vy = aMirror.vz = 0; - } else { - aMirror.vx = SpinBox_DX->GetValue(); - aMirror.vy = SpinBox_DY->GetValue(); - aMirror.vz = SpinBox_DZ->GetValue(); - } + SMESH::SMESH_MeshEditor::MirrorType aMirrorType; + getMirror(aMirror,aMirrorType); QStringList aParameters; aParameters << SpinBox_X->text(); @@ -476,31 +491,22 @@ bool SMESHGUI_SymmetryDlg::ClickOnApply() aParameters << ( GetConstructorId() == 0 ? QString::number(0) : SpinBox_DY->text() ); aParameters << ( GetConstructorId() == 0 ? QString::number(0) : SpinBox_DZ->text() ); - SMESH::SMESH_MeshEditor::MirrorType aMirrorType; - - if (GetConstructorId() == 0) - aMirrorType = SMESH::SMESH_MeshEditor::POINT; - if (GetConstructorId() == 1) - aMirrorType = SMESH::SMESH_MeshEditor::AXIS; - if (GetConstructorId() == 2) - aMirrorType = SMESH::SMESH_MeshEditor::PLANE; - int actionButton = ActionGroup->checkedId(); bool makeGroups = ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ); - + QStringList anEntryList; try { SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + myMesh->SetParameters(aParameters.join(":").toLatin1().constData()); + switch ( actionButton ) { case MOVE_ELEMS_BUTTON: { if(CheckBoxMesh->isChecked()) aMeshEditor->MirrorObject(mySelectedObject, aMirror, aMirrorType, false ); else aMeshEditor->Mirror(anElementsId, aMirror, aMirrorType, false ); - - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); + break; } case COPY_ELEMS_BUTTON: { @@ -517,34 +523,44 @@ bool SMESHGUI_SymmetryDlg::ClickOnApply() else aMeshEditor->Mirror(anElementsId, aMirror, aMirrorType, true); } - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); break; } case MAKE_MESH_BUTTON: { SMESH::SMESH_Mesh_var mesh; - if(CheckBoxMesh->isChecked()) + if (CheckBoxMesh->isChecked()) mesh = aMeshEditor->MirrorObjectMakeMesh(mySelectedObject, aMirror, aMirrorType, makeGroups, LineEditNewMesh->text().toLatin1().data()); else mesh = aMeshEditor->MirrorMakeMesh(anElementsId, aMirror, aMirrorType, makeGroups, LineEditNewMesh->text().toLatin1().data()); - if( !mesh->_is_nil()) - mesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( mesh ) ) + anEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + mesh->UnRegister(); +#endif + } break; } - } } catch (...) { } - + SMESH::UpdateView(); - if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() || - actionButton == MAKE_MESH_BUTTON ) + if ( ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) || + actionButton == MAKE_MESH_BUTTON ) { mySMESHGUI->updateObjBrowser(true); // new groups may appear + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + } Init(false); ConstructorsClicked(GetConstructorId()); mySelectedObject = SMESH::SMESH_IDSource::_nil(); SelectionIntoArgument(); + + SMESHGUI::Modified(); } return true; } @@ -555,6 +571,7 @@ bool SMESHGUI_SymmetryDlg::ClickOnApply() //================================================================================= void SMESHGUI_SymmetryDlg::ClickOnOk() { + setIsApplyAndClose( true ); if( ClickOnApply() ) ClickOnCancel(); } @@ -585,7 +602,7 @@ void SMESHGUI_SymmetryDlg::ClickOnCancel() void SMESHGUI_SymmetryDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { QString platform; @@ -595,10 +612,10 @@ void SMESHGUI_SymmetryDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -628,21 +645,21 @@ void SMESHGUI_SymmetryDlg::onTextChange (const QString& theNewText) Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); TColStd_MapOfInteger newIndices; - + QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); if (send == LineEditElements) { for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - myNbOkElements++; + const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); + if (e) + newIndices.Add(e->GetID()); + myNbOkElements++; } mySelector->AddOrRemoveIndex( anIO, newIndices, false ); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( anIO, true, true ); - + aViewWindow->highlight( anIO, true, true ); + myElementsId = theNewText; } } @@ -695,7 +712,7 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() myActor = SMESH::FindActorByObject(myMesh); if (!myActor) myActor = SMESH::FindActorByEntry(IO->getEntry()); - if (!myActor) + if (!myActor && !CheckBoxMesh->isChecked()) return; int aNbUnits = 0; @@ -732,7 +749,6 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() aNbUnits++; } } - } else if (!SMESH::IObjectToInterface(IO)->_is_nil()) { //SUBMESH // get submesh SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(IO); @@ -745,7 +761,6 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() myElementsId += QString(" %1").arg(anElementsIds[i]); } aNbUnits = anElementsIds->length(); - } else { // GROUP // get smesh group SMESH::SMESH_GroupBase_var aGroup = @@ -768,7 +783,7 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() if (aNbUnits < 1) return; } - + myNbOkElements = true; } else { aNbUnits = SMESH::GetNameOfSelectedNodes(mySelector, IO, aString); @@ -803,7 +818,7 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() LineEditElements->setText(aString); LineEditElements->repaint(); LineEditElements->setEnabled(false); // to update lineedit IPAL 19809 - LineEditElements->setEnabled(true); + LineEditElements->setEnabled(true); setNewMeshName(); } myBusy = false; @@ -813,6 +828,7 @@ void SMESHGUI_SymmetryDlg::SelectionIntoArgument() buttonOk->setEnabled(true); buttonApply->setEnabled(true); } + onDisplaySimulation(true); } //================================================================================= @@ -832,11 +848,11 @@ void SMESHGUI_SymmetryDlg::SetEditCurrentArgument() SMESH::SetPointRepresentation(false); if (CheckBoxMesh->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(ActorSelection); + aViewWindow->SetSelectionMode(ActorSelection); mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode(CellSelection); + aViewWindow->SetSelectionMode(CellSelection); } } else if (send == SelectPointButton) { myEditCurrentArgument = (QWidget*)SpinBox_X; @@ -931,6 +947,7 @@ void SMESHGUI_SymmetryDlg::onSelectMesh (bool toSelectMesh) TextLabelElements->setText(tr("SMESH_NAME")); else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); if (myEditCurrentArgument != LineEditElements) { LineEditElements->clear(); @@ -952,6 +969,7 @@ void SMESHGUI_SymmetryDlg::onSelectMesh (bool toSelectMesh) LineEditElements->setReadOnly(false); LineEditElements->setValidator(myIdValidator); onTextChange(LineEditElements->text()); + hidePreview(); } SelectionIntoArgument(); @@ -1027,6 +1045,7 @@ void SMESHGUI_SymmetryDlg::onActionClicked(int button) break; } setNewMeshName(); + toDisplaySimulation(); } //======================================================================= @@ -1073,6 +1092,12 @@ void SMESHGUI_SymmetryDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_SymmetryDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); @@ -1110,3 +1135,68 @@ bool SMESHGUI_SymmetryDlg::isValid() } return true; } + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_SymmetryDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + if ( myNbOkElements && isValid() && IsMirrorOk() ) { + QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + SMESH::long_array_var anElementsId = new SMESH::long_array; + + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::AxisStruct aMirror; + SMESH::SMESH_MeshEditor::MirrorType aMirrorType; + + getMirror(aMirror,aMirrorType); + + try { + bool copy = ( ActionGroup->checkedId() == COPY_ELEMS_BUTTON || + ActionGroup->checkedId() == MAKE_MESH_BUTTON ); + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + if(CheckBoxMesh->isChecked()) + aMeshEditor->MirrorObject(mySelectedObject, aMirror, aMirrorType, copy ); + else + aMeshEditor->Mirror(anElementsId, aMirror, aMirrorType, copy ); + + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + } catch (...) { + hidePreview(); + } + } else { + hidePreview(); + } + } else { + hidePreview(); + } +} + +//================================================================================= +// function : getMirror +// purpose : return mirror parameters +//================================================================================= +void SMESHGUI_SymmetryDlg::getMirror(SMESH::AxisStruct& theMirror, SMESH::SMESH_MeshEditor::MirrorType& theMirrorType) { + theMirror.x = SpinBox_X->GetValue(); + theMirror.y = SpinBox_Y->GetValue(); + theMirror.z = SpinBox_Z->GetValue(); + if (GetConstructorId() == 0) { + theMirror.vx = theMirror.vy = theMirror.vz = 0; + } else { + theMirror.vx = SpinBox_DX->GetValue(); + theMirror.vy = SpinBox_DY->GetValue(); + theMirror.vz = SpinBox_DZ->GetValue(); + } + if (GetConstructorId() == 0) + theMirrorType = SMESH::SMESH_MeshEditor::POINT; + if (GetConstructorId() == 1) + theMirrorType = SMESH::SMESH_MeshEditor::AXIS; + if (GetConstructorId() == 2) + theMirrorType = SMESH::SMESH_MeshEditor::PLANE; +} diff --git a/src/SMESHGUI/SMESHGUI_SymmetryDlg.h b/src/SMESHGUI/SMESHGUI_SymmetryDlg.h index c56317d0b..a039ac27f 100644 --- a/src/SMESHGUI/SMESHGUI_SymmetryDlg.h +++ b/src/SMESHGUI/SMESHGUI_SymmetryDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_SymmetryDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -28,13 +29,12 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include +#include "SMESHGUI_PreviewDlg.h" // IDL includes #include #include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_MeshEditor) class QButtonGroup; class QGroupBox; @@ -56,7 +56,7 @@ class SMESH_LogicalFilter; // class : SMESHGUI_SymmetryDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_SymmetryDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_SymmetryDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -74,9 +74,10 @@ private: bool IsMirrorOk(); void setNewMeshName(); + void getMirror(SMESH::AxisStruct& theMirror, SMESH::SMESH_MeshEditor::MirrorType& aMirrorType); + bool isValid(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ int myNbOkElements; /* to check when elements are defined */ @@ -133,7 +134,11 @@ private: QString myHelpFileName; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; + +protected slots: + virtual void onDisplaySimulation( bool ); private slots: void ConstructorsClicked( int ); diff --git a/src/SMESHGUI/SMESHGUI_TranslationDlg.cxx b/src/SMESHGUI/SMESHGUI_TranslationDlg.cxx index 091446d10..31ff3a796 100644 --- a/src/SMESHGUI/SMESHGUI_TranslationDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_TranslationDlg.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_TranslationDlg.cxx -// Author : Michael ZORIN, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESHGUI_TranslationDlg.cxx +// Author : Michael ZORIN, Open CASCADE S.A.S. +// SMESH includes + #include "SMESHGUI_TranslationDlg.h" #include "SMESHGUI.h" @@ -33,6 +32,7 @@ #include "SMESHGUI_MeshUtils.h" #include "SMESHGUI_IdValidator.h" #include "SMESHGUI_FilterDlg.h" +#include "SMESHGUI_MeshEditPreview.h" #include #include @@ -101,16 +101,19 @@ private: #define SPACING 6 #define MARGIN 11 +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ + //================================================================================= // class : SMESHGUI_TranslationDlg() // purpose : //================================================================================= -SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), - myFilterDlg(0), - mySelectedObject(SMESH::SMESH_IDSource::_nil()) +SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) : + SMESHGUI_PreviewDlg( theModule ), + mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ), + myFilterDlg(0), + mySelectedObject(SMESH::SMESH_IDSource::_nil()) { QPixmap image0 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SMESH_TRANSLATION_POINTS"))); QPixmap image1 (SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap("SMESH", tr("ICON_SMESH_TRANSLATION_VECTOR"))); @@ -156,8 +159,9 @@ SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) SelectElementsButton->setIcon(image2); LineEditElements = new QLineEdit(GroupArguments); LineEditElements->setValidator(myIdValidator); - QPushButton* filterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); - connect(filterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); + LineEditElements->setMaxLength(-1); + myFilterBtn = new QPushButton( tr( "SMESH_BUT_FILTER" ), GroupArguments ); + connect(myFilterBtn, SIGNAL(clicked()), this, SLOT(setFilters())); // Control for the whole mesh selection CheckBoxMesh = new QCheckBox(tr("SMESH_SELECT_WHOLE_MESH"), GroupArguments); @@ -210,11 +214,14 @@ SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) // Name of a mesh to create LineEditNewMesh = new QLineEdit(GroupArguments); + //Preview check box + myPreviewCheckBox = new QCheckBox(tr("PREVIEW"), GroupArguments); + // layout GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); GroupArgumentsLayout->addWidget(SelectElementsButton, 0, 1); GroupArgumentsLayout->addWidget(LineEditElements, 0, 2, 1, 5); - GroupArgumentsLayout->addWidget(filterBtn, 0, 7); + GroupArgumentsLayout->addWidget(myFilterBtn, 0, 7); GroupArgumentsLayout->addWidget(CheckBoxMesh, 1, 0, 1, 8); GroupArgumentsLayout->addWidget(TextLabel1, 2, 0); GroupArgumentsLayout->addWidget(SelectButton1, 2, 1); @@ -235,6 +242,7 @@ SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) GroupArgumentsLayout->addWidget(ActionBox, 4, 0, 3, 4); GroupArgumentsLayout->addWidget(MakeGroupsCheck, 5, 5, 1, 4); GroupArgumentsLayout->addWidget(LineEditNewMesh, 6, 5, 1, 4); + GroupArgumentsLayout->addWidget(myPreviewCheckBox, 7, 0); /***************************************************************/ GroupButtons = new QGroupBox(this); @@ -266,12 +274,12 @@ SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) SMESHGUI_TranslationDlgLayout->addWidget(GroupButtons); /* Initialisations */ - SpinBox1_1->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - SpinBox1_2->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - SpinBox1_3->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - SpinBox2_1->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - SpinBox2_2->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); - SpinBox2_3->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, DBL_DIGITS_DISPLAY); + SpinBox1_1->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox1_2->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox1_3->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox2_1->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox2_2->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); + SpinBox2_3->RangeStepAndValidator(COORD_MIN, COORD_MAX, 10.0, "length_precision"); RadioButton1->setChecked(true); @@ -313,6 +321,19 @@ SMESHGUI_TranslationDlg::SMESHGUI_TranslationDlg( SMESHGUI* theModule ) connect(CheckBoxMesh, SIGNAL(toggled(bool)), SLOT(onSelectMesh(bool))); connect(ActionGroup, SIGNAL(buttonClicked(int)), SLOT(onActionClicked(int))); + connect(SpinBox1_1, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox1_2, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox1_3, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + connect(SpinBox2_1, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox2_2, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox2_3, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + connect(SpinBox2_3, SIGNAL(valueChanged(double)), this, SLOT(toDisplaySimulation())); + + + //To Connect preview check box + connectPreviewControl(); + ConstructorsClicked(0); SelectionIntoArgument(); onActionClicked(MOVE_ELEMS_BUTTON); @@ -362,6 +383,8 @@ void SMESHGUI_TranslationDlg::Init (bool ResetControls) CheckBoxMesh->setChecked(false); // MakeGroupsCheck->setChecked(false); // MakeGroupsCheck->setEnabled(false); + myPreviewCheckBox->setChecked(false); + onDisplaySimulation(false); onSelectMesh(false); } } @@ -417,7 +440,7 @@ void SMESHGUI_TranslationDlg::ConstructorsClicked (int constructorId) SMESH::SetPointRepresentation(false); if (!CheckBoxMesh->isChecked()) if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( CellSelection ); + aViewWindow->SetSelectionMode( CellSelection ); } myEditCurrentArgument = (QWidget*)LineEditElements; @@ -428,9 +451,11 @@ void SMESHGUI_TranslationDlg::ConstructorsClicked (int constructorId) connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + onDisplaySimulation(true); + QApplication::instance()->processEvents(); updateGeometry(); - resize( minimumSize() ); + resize(100,100); } //================================================================================= @@ -455,44 +480,43 @@ bool SMESHGUI_TranslationDlg::ClickOnApply() anElementsId[i] = aListElementsId[i].toInt(); SMESH::DirStruct aVector; + QStringList aParameters; if (GetConstructorId() == 0) { aVector.PS.x = SpinBox2_1->GetValue() - SpinBox1_1->GetValue(); aVector.PS.y = SpinBox2_2->GetValue() - SpinBox1_2->GetValue(); aVector.PS.z = SpinBox2_3->GetValue() - SpinBox1_3->GetValue(); + // not supported so far + // aParameters << QString("%1 - %2").arg( SpinBox2_1->text() ).arg( SpinBox1_1->text() ); + // aParameters << QString("%1 - %2").arg( SpinBox2_2->text() ).arg( SpinBox1_2->text() ); + // aParameters << QString("%1 - %2").arg( SpinBox2_3->text() ).arg( SpinBox1_3->text() ); } else if (GetConstructorId() == 1) { aVector.PS.x = SpinBox1_1->GetValue(); aVector.PS.y = SpinBox1_2->GetValue(); aVector.PS.z = SpinBox1_3->GetValue(); + aParameters << SpinBox1_1->text(); + aParameters << SpinBox1_2->text(); + aParameters << SpinBox1_3->text(); } - QStringList aParameters; - aParameters << SpinBox1_1->text(); - if (GetConstructorId() == 0) - aParameters << SpinBox2_1->text(); - aParameters << SpinBox1_2->text(); - if (GetConstructorId() == 0) - aParameters << SpinBox2_2->text(); - aParameters << SpinBox1_3->text(); - if (GetConstructorId() == 0) - aParameters << SpinBox2_3->text(); - int actionButton = ActionGroup->checkedId(); bool makeGroups = ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ); + QStringList anEntryList; try { SUIT_OverrideCursor aWaitCursor; SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditor(); + + myMesh->SetParameters(aParameters.join(":").toLatin1().constData()); + switch ( actionButton ) { case MOVE_ELEMS_BUTTON: if(CheckBoxMesh->isChecked()) aMeshEditor->TranslateObject(mySelectedObject, aVector, false); else aMeshEditor->Translate(anElementsId, aVector, false); - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); break; case COPY_ELEMS_BUTTON: if ( makeGroups ) { - SMESH::ListOfGroups_var groups; + SMESH::ListOfGroups_var groups; if(CheckBoxMesh->isChecked()) groups = aMeshEditor->TranslateObjectMakeGroups(mySelectedObject,aVector); else @@ -504,33 +528,45 @@ bool SMESHGUI_TranslationDlg::ClickOnApply() else aMeshEditor->Translate(anElementsId, aVector, true); } - if( !myMesh->_is_nil()) - myMesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); break; - case MAKE_MESH_BUTTON: - SMESH::SMESH_Mesh_var mesh; - if(CheckBoxMesh->isChecked()) + case MAKE_MESH_BUTTON: { + SMESH::SMESH_Mesh_var mesh; + if (CheckBoxMesh->isChecked()) mesh = aMeshEditor->TranslateObjectMakeMesh(mySelectedObject, aVector, makeGroups, LineEditNewMesh->text().toLatin1().data()); else mesh = aMeshEditor->TranslateMakeMesh(anElementsId, aVector, makeGroups, LineEditNewMesh->text().toLatin1().data()); - if( !mesh->_is_nil()) - mesh->SetParameters(SMESHGUI::JoinObjectParameters(aParameters)); + if( _PTR(SObject) aSObject = SMESH::ObjectToSObject( mesh ) ) + anEntryList.append( aSObject->GetID().c_str() ); +#ifdef WITHGENERICOBJ + // obj has been published in study. Its refcount has been incremented. + // It is safe to decrement its refcount + // so that it will be destroyed when the entry in study will be removed + mesh->UnRegister(); +#endif + } } } catch (...) { } - + SMESH::UpdateView(); - if ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() || - actionButton == MAKE_MESH_BUTTON ) + if ( ( MakeGroupsCheck->isEnabled() && MakeGroupsCheck->isChecked() ) || + actionButton == MAKE_MESH_BUTTON ) { mySMESHGUI->updateObjBrowser(true); // new groups may appear + if( LightApp_Application* anApp = + dynamic_cast( SUIT_Session::session()->activeApplication() ) ) + anApp->browseObjects( anEntryList, isApplyAndClose() ); + } + Init(false); ConstructorsClicked(GetConstructorId()); mySelectedObject = SMESH::SMESH_IDSource::_nil(); SelectionIntoArgument(); + + SMESHGUI::Modified(); } - + return true; } @@ -540,6 +576,7 @@ bool SMESHGUI_TranslationDlg::ClickOnApply() //================================================================================= void SMESHGUI_TranslationDlg::ClickOnOk() { + setIsApplyAndClose( true ); if( ClickOnApply() ) ClickOnCancel(); } @@ -570,7 +607,7 @@ void SMESHGUI_TranslationDlg::ClickOnCancel() void SMESHGUI_TranslationDlg::ClickOnHelp() { LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) + if (app) app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); else { QString platform; @@ -580,10 +617,10 @@ void SMESHGUI_TranslationDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - platform)). - arg(myHelpFileName)); + tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + platform)). + arg(myHelpFileName)); } } @@ -611,24 +648,24 @@ void SMESHGUI_TranslationDlg::onTextChange (const QString& theNewText) if (aMesh) { Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); - + TColStd_MapOfInteger newIndices; QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); if (send == LineEditElements) { for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - myNbOkElements++; + const SMDS_MeshElement * e = aMesh->FindElement(aListId[ i ].toInt()); + if (e) + newIndices.Add(e->GetID()); + myNbOkElements++; } } mySelector->AddOrRemoveIndex( anIO, newIndices, false ); if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) aViewWindow->highlight( anIO, true, true ); - + myElementsId = theNewText; } @@ -669,19 +706,24 @@ void SMESHGUI_TranslationDlg::SelectionIntoArgument() return; Handle(SALOME_InteractiveObject) IO = aList.First(); - myMesh = SMESH::GetMeshByIO(IO); - if (myMesh->_is_nil()) + SMESH::SMESH_Mesh_var aMesh = SMESH::GetMeshByIO(IO); + if (aMesh->_is_nil()) return; - myActor = SMESH::FindActorByObject(myMesh); - if (!myActor) - myActor = SMESH::FindActorByEntry(IO->getEntry()); - if (!myActor) - return; + SMESH_Actor* anActor = SMESH::FindActorByObject(aMesh); + if (!anActor) + anActor = SMESH::FindActorByEntry(IO->getEntry()); + + if (!anActor && !CheckBoxMesh->isChecked()) + return; int aNbUnits = 0; - if (myEditCurrentArgument == (QWidget*)LineEditElements) { + if (myEditCurrentArgument == (QWidget*)LineEditElements) + { + myMesh = aMesh; + myActor = anActor; + myElementsId = ""; // MakeGroups is available if there are groups and "Copy" @@ -701,51 +743,11 @@ void SMESHGUI_TranslationDlg::SelectionIntoArgument() } else return; - // get IDs from mesh - /* - SMDS_Mesh* aSMDSMesh = myActor->GetObject()->GetMesh(); - if (!aSMDSMesh) - return; - - for (int i = aSMDSMesh->MinElementID(); i <= aSMDSMesh->MaxElementID(); i++) { - const SMDS_MeshElement * e = aSMDSMesh->FindElement(i); - if (e) { - myElementsId += QString(" %1").arg(i); - aNbUnits++; - } - } - } else if (!SMESH::IObjectToInterface(IO)->_is_nil()) { //SUBMESH - // get submesh - SMESH::SMESH_subMesh_var aSubMesh = SMESH::IObjectToInterface(IO); - - // get IDs from submesh - SMESH::long_array_var anElementsIds = new SMESH::long_array; - anElementsIds = aSubMesh->GetElementsId(); - for (int i = 0; i < anElementsIds->length(); i++) { - myElementsId += QString(" %1").arg(anElementsIds[i]); - } - aNbUnits = anElementsIds->length(); - } else { // GROUP - // get smesh group - SMESH::SMESH_GroupBase_var aGroup = - SMESH::IObjectToInterface(IO); - if (aGroup->_is_nil()) - return; - - // get IDs from smesh group - SMESH::long_array_var anElementsIds = new SMESH::long_array; - anElementsIds = aGroup->GetListOfID(); - for (int i = 0; i < anElementsIds->length(); i++) { - myElementsId += QString(" %1").arg(anElementsIds[i]); - } - aNbUnits = anElementsIds->length(); - } - */ } else { aNbUnits = SMESH::GetNameOfSelectedElements(mySelector, IO, aString); myElementsId = aString; if (aNbUnits < 1) - return; + return; } myNbOkElements = true; @@ -754,7 +756,7 @@ void SMESHGUI_TranslationDlg::SelectionIntoArgument() if (aNbUnits != 1) return; - SMDS_Mesh* aMesh = myActor->GetObject()->GetMesh(); + SMDS_Mesh* aMesh = anActor->GetObject()->GetMesh(); if (!aMesh) return; @@ -781,7 +783,7 @@ void SMESHGUI_TranslationDlg::SelectionIntoArgument() LineEditElements->setText(aString); LineEditElements->repaint(); LineEditElements->setEnabled(false); // to fully update lineedit IPAL 19809 - LineEditElements->setEnabled(true); + LineEditElements->setEnabled(true); setNewMeshName(); } @@ -790,6 +792,7 @@ void SMESHGUI_TranslationDlg::SelectionIntoArgument() buttonOk->setEnabled(true); buttonApply->setEnabled(true); } + onDisplaySimulation(true); } //================================================================================= @@ -803,18 +806,18 @@ void SMESHGUI_TranslationDlg::SetEditCurrentArgument() disconnect(mySelectionMgr, 0, this, 0); mySelectionMgr->clearSelected(); mySelectionMgr->clearFilters(); - + if (send == SelectElementsButton) { myEditCurrentArgument = (QWidget*)LineEditElements; SMESH::SetPointRepresentation(false); if (CheckBoxMesh->isChecked()) { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( ActorSelection ); + aViewWindow->SetSelectionMode( ActorSelection ); mySelectionMgr->installFilter(myMeshOrSubMeshOrGroupFilter); } else { if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( CellSelection ); + aViewWindow->SetSelectionMode( CellSelection ); } } else if (send == SelectButton1) { myEditCurrentArgument = (QWidget*)SpinBox1_1; @@ -910,6 +913,7 @@ void SMESHGUI_TranslationDlg::onSelectMesh (bool toSelectMesh) TextLabelElements->setText(tr("SMESH_NAME")); else TextLabelElements->setText(tr("SMESH_ID_ELEMENTS")); + myFilterBtn->setEnabled(!toSelectMesh); if (myEditCurrentArgument != LineEditElements) { LineEditElements->clear(); @@ -931,6 +935,7 @@ void SMESHGUI_TranslationDlg::onSelectMesh (bool toSelectMesh) LineEditElements->setReadOnly(false); LineEditElements->setValidator(myIdValidator); onTextChange(LineEditElements->text()); + hidePreview(); } SelectionIntoArgument(); @@ -966,6 +971,7 @@ void SMESHGUI_TranslationDlg::onActionClicked(int button) break; } setNewMeshName(); + toDisplaySimulation(); } //======================================================================= @@ -1021,6 +1027,12 @@ void SMESHGUI_TranslationDlg::keyPressEvent( QKeyEvent* e ) //================================================================================= void SMESHGUI_TranslationDlg::setFilters() { + if(myMesh->_is_nil()) { + SUIT_MessageBox::critical(this, + tr("SMESH_ERROR"), + tr("NO_MESH_SELECTED")); + return; + } if ( !myFilterDlg ) myFilterDlg = new SMESHGUI_FilterDlg( mySMESHGUI, SMESH::ALL ); @@ -1058,3 +1070,54 @@ bool SMESHGUI_TranslationDlg::isValid() } return true; } + +//================================================================================= +// function : onDisplaySimulation +// purpose : Show/Hide preview +//================================================================================= +void SMESHGUI_TranslationDlg::onDisplaySimulation( bool toDisplayPreview ) { + if (myPreviewCheckBox->isChecked() && toDisplayPreview) { + + if (isValid() && myNbOkElements) { + QStringList aListElementsId = myElementsId.split(" ", QString::SkipEmptyParts); + + SMESH::long_array_var anElementsId = new SMESH::long_array; + + anElementsId->length(aListElementsId.count()); + for (int i = 0; i < aListElementsId.count(); i++) + anElementsId[i] = aListElementsId[i].toInt(); + + SMESH::DirStruct aVector; + if (GetConstructorId() == 0) { + aVector.PS.x = SpinBox2_1->GetValue() - SpinBox1_1->GetValue(); + aVector.PS.y = SpinBox2_2->GetValue() - SpinBox1_2->GetValue(); + aVector.PS.z = SpinBox2_3->GetValue() - SpinBox1_3->GetValue(); + } else if (GetConstructorId() == 1) { + aVector.PS.x = SpinBox1_1->GetValue(); + aVector.PS.y = SpinBox1_2->GetValue(); + aVector.PS.z = SpinBox1_3->GetValue(); + } + + try { + bool copy = ( ActionGroup->checkedId() == COPY_ELEMS_BUTTON || + ActionGroup->checkedId() == MAKE_MESH_BUTTON ); + SUIT_OverrideCursor aWaitCursor; + SMESH::SMESH_MeshEditor_var aMeshEditor = myMesh->GetMeshEditPreviewer(); + if(CheckBoxMesh->isChecked()) + aMeshEditor->TranslateObject(mySelectedObject, aVector, copy); + else + aMeshEditor->Translate(anElementsId, aVector, copy); + + SMESH::MeshPreviewStruct_var aMeshPreviewStruct = aMeshEditor->GetPreviewData(); + mySimulation->SetData(aMeshPreviewStruct._retn()); + } catch (...) { + + } + } + else { + hidePreview(); + } + } else { + hidePreview(); + } +} diff --git a/src/SMESHGUI/SMESHGUI_TranslationDlg.h b/src/SMESHGUI/SMESHGUI_TranslationDlg.h index 279ec596b..2c7294378 100644 --- a/src/SMESHGUI/SMESHGUI_TranslationDlg.h +++ b/src/SMESHGUI/SMESHGUI_TranslationDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_TranslationDlg.h // Author : Michael ZORIN, Open CASCADE S.A.S. @@ -28,9 +29,7 @@ // SMESH includes #include "SMESH_SMESHGUI.hxx" - -// Qt includes -#include +#include "SMESHGUI_PreviewDlg.h" // IDL includes #include @@ -56,7 +55,7 @@ class SMESH_LogicalFilter; // class : SMESHGUI_TranslationDlg // purpose : //================================================================================= -class SMESHGUI_EXPORT SMESHGUI_TranslationDlg : public QDialog +class SMESHGUI_EXPORT SMESHGUI_TranslationDlg : public SMESHGUI_PreviewDlg { Q_OBJECT @@ -75,7 +74,6 @@ private: bool isValid(); - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ SMESHGUI_IdValidator* myIdValidator; LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ QString myElementsId; @@ -130,7 +128,11 @@ private: QString myHelpFileName; + QPushButton* myFilterBtn; SMESHGUI_FilterDlg* myFilterDlg; + +protected slots: + virtual void onDisplaySimulation( bool ); private slots: void ConstructorsClicked( int ); diff --git a/src/SMESHGUI/SMESHGUI_TransparencyDlg.cxx b/src/SMESHGUI/SMESHGUI_TransparencyDlg.cxx index b979ddffc..a279d0be4 100644 --- a/src/SMESHGUI/SMESHGUI_TransparencyDlg.cxx +++ b/src/SMESHGUI/SMESHGUI_TransparencyDlg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_TransparencyDlg.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -86,7 +87,7 @@ SMESHGUI_TransparencyDlg::SMESHGUI_TransparencyDlg( SMESHGUI* theModule ) GroupC1Layout->setMargin( MARGIN ); TextLabelTransparent = new QLabel( tr( "SMESH_TRANSPARENCY_TRANSPARENT" ), GroupC1 ); - TextLabelTransparent->setAlignment( Qt::AlignLeft ); + TextLabelTransparent->setAlignment( Qt::AlignRight ); ValueLab = new QLabel( GroupC1 ); ValueLab->setAlignment( Qt::AlignCenter ); @@ -94,7 +95,7 @@ SMESHGUI_TransparencyDlg::SMESHGUI_TransparencyDlg( SMESHGUI* theModule ) QFont fnt = ValueLab->font(); fnt.setBold( true ); ValueLab->setFont( fnt ); TextLabelOpaque = new QLabel( tr( "SMESH_TRANSPARENCY_OPAQUE" ), GroupC1 ); - TextLabelOpaque->setAlignment( Qt::AlignRight ); + TextLabelOpaque->setAlignment( Qt::AlignLeft ); Slider1 = new QSlider( Qt::Horizontal, GroupC1 ); Slider1->setRange( 0, 100 ); @@ -106,9 +107,9 @@ SMESHGUI_TransparencyDlg::SMESHGUI_TransparencyDlg( SMESHGUI* theModule ) Slider1->setFocusPolicy( Qt::NoFocus ); Slider1->setMinimumWidth( 300 ); - GroupC1Layout->addWidget( TextLabelTransparent, 0, 0 ); + GroupC1Layout->addWidget( TextLabelOpaque, 0, 0 ); GroupC1Layout->addWidget( ValueLab, 0, 1 ); - GroupC1Layout->addWidget( TextLabelOpaque, 0, 2 ); + GroupC1Layout->addWidget( TextLabelTransparent, 0, 2 ); GroupC1Layout->addWidget( Slider1, 1, 0, 1, 3 ); /*************************************************************************/ @@ -172,7 +173,7 @@ void SMESHGUI_TransparencyDlg::ClickOnHelp() LightApp_Application* app = (LightApp_Application*)( SUIT_Session::session()->activeApplication() ); if ( app ) app->onHelpContextModule( mySMESHGUI ? app->moduleName( mySMESHGUI->moduleName() ) : - QString( "" ), myHelpFileName ); + QString( "" ), myHelpFileName ); else { QString platform; #ifdef WIN32 @@ -181,10 +182,10 @@ void SMESHGUI_TransparencyDlg::ClickOnHelp() platform = "application"; #endif SUIT_MessageBox::warning( this, tr( "WRN_WARNING" ), - tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). - arg( app->resourceMgr()->stringValue( "ExternalBrowser", - platform ) ). - arg( myHelpFileName ) ); + tr( "EXTERNAL_BROWSER_CANNOT_SHOW_PAGE" ). + arg( app->resourceMgr()->stringValue( "ExternalBrowser", + platform ) ). + arg( myHelpFileName ) ); } } @@ -197,7 +198,7 @@ void SMESHGUI_TransparencyDlg::SetTransparency() { if ( myViewWindow ) { SUIT_OverrideCursor wc; - float opacity = Slider1->value() / 100.; + float opacity = ( 100 - Slider1->value() ) / 100.; SALOME_ListIO aList; mySelectionMgr->selectedObjects( aList ); @@ -207,7 +208,7 @@ void SMESHGUI_TransparencyDlg::SetTransparency() Handle(SALOME_InteractiveObject) IOS = It.Value(); SMESH_Actor* anActor = SMESH::FindActorByEntry( IOS->getEntry() ); if ( anActor ) - anActor->SetOpacity( opacity ); + anActor->SetOpacity( opacity ); } myViewWindow->Repaint(); } @@ -238,35 +239,35 @@ void SMESHGUI_TransparencyDlg::onSelectionChanged() if ( aList.Extent() == 1 ) { Handle(SALOME_InteractiveObject) FirstIOS = aList.First(); if ( !FirstIOS.IsNull() ) { - SMESH_Actor* anActor = SMESH::FindActorByEntry( FirstIOS->getEntry() ); - if ( anActor ) - opacity = int( anActor->GetOpacity() * 100. + 0.5 ); + SMESH_Actor* anActor = SMESH::FindActorByEntry( FirstIOS->getEntry() ); + if ( anActor ) + opacity = int( anActor->GetOpacity() * 100. + 0.5 ); } } else if ( aList.Extent() > 1 ) { SALOME_ListIteratorOfListIO It( aList ); int setOp = -1; for ( ; It.More(); It.Next() ) { - Handle(SALOME_InteractiveObject) IO = It.Value(); - if ( !IO.IsNull() ) { - SMESH_Actor* anActor = SMESH::FindActorByEntry( IO->getEntry() ); - if ( anActor ) { - int op = int( anActor->GetOpacity() * 100. + 0.5 ); - if ( setOp < 0 ) - setOp = op; - else if ( setOp != op ) { - setOp = 100; - break; - } - } - } + Handle(SALOME_InteractiveObject) IO = It.Value(); + if ( !IO.IsNull() ) { + SMESH_Actor* anActor = SMESH::FindActorByEntry( IO->getEntry() ); + if ( anActor ) { + int op = int( anActor->GetOpacity() * 100. + 0.5 ); + if ( setOp < 0 ) + setOp = op; + else if ( setOp != op ) { + setOp = 100; + break; + } + } + } } if ( setOp >= 0 ) - opacity = setOp; + opacity = setOp; } else { } - Slider1->setValue( opacity ); + Slider1->setValue( 100 - opacity ); } ValueHasChanged(); } diff --git a/src/SMESHGUI/SMESHGUI_TransparencyDlg.h b/src/SMESHGUI/SMESHGUI_TransparencyDlg.h index 28595ed7e..7659b59cc 100644 --- a/src/SMESHGUI/SMESHGUI_TransparencyDlg.h +++ b/src/SMESHGUI/SMESHGUI_TransparencyDlg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_TransparencyDlg.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. diff --git a/src/SMESHGUI/SMESHGUI_Utils.cxx b/src/SMESHGUI/SMESHGUI_Utils.cxx index 33eb25b96..94ef5c93b 100644 --- a/src/SMESHGUI/SMESHGUI_Utils.cxx +++ b/src/SMESHGUI/SMESHGUI_Utils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Utils.cxx // Author : Open CASCADE S.A.S. @@ -26,6 +27,8 @@ // #include "SMESHGUI_Utils.h" #include "SMESHGUI.h" +#include "SMESHGUI_Selection.h" +#include "SMESH_Type.h" #include #include @@ -47,6 +50,8 @@ #include #include +#include CORBA_SERVER_HEADER(SMESH_Group) + namespace SMESH { SUIT_Desktop* @@ -80,11 +85,11 @@ namespace SMESH if(theOwner){ const Handle(SALOME_InteractiveObject)& anIO = theOwner->IO(); if(!anIO.IsNull()){ - if(anIO->hasEntry()){ - _PTR(Study) aStudy = GetActiveStudyDocument(); - _PTR(SObject) aSObj = aStudy->FindObjectID(anIO->getEntry()); - anObj = SObjectToObject(aSObj,aStudy); - } + if(anIO->hasEntry()){ + _PTR(Study) aStudy = GetActiveStudyDocument(); + _PTR(SObject) aSObj = aStudy->FindObjectID(anIO->getEntry()); + anObj = SObjectToObject(aSObj,aStudy); + } } } return anObj; @@ -124,9 +129,9 @@ namespace SMESH (SUIT_Session::session()->activeApplication()); if (app && !CORBA::is_nil(theObject)) { if(_PTR(Study) aStudy = GetActiveStudyDocument()){ - CORBA::String_var anIOR = app->orb()->object_to_string(theObject); - if (strcmp(anIOR.in(), "") != 0) - return aStudy->FindObjectIOR(anIOR.in()); + CORBA::String_var anIOR = app->orb()->object_to_string(theObject); + if (strcmp(anIOR.in(), "") != 0) + return aStudy->FindObjectIOR(anIOR.in()); } } return _PTR(SObject)(); @@ -187,9 +192,12 @@ namespace SMESH if (theSObject) { _PTR(GenericAttribute) anAttr; if (theSObject->FindAttribute(anAttr, "AttributeIOR")) { - _PTR(AttributeIOR) anIOR = anAttr; - CORBA::String_var aVal = anIOR->Value().c_str(); - return app->orb()->string_to_object(aVal); + _PTR(AttributeIOR) anIOR = anAttr; + CORBA::String_var aVal = anIOR->Value().c_str(); + // string_to_object() DOC: If the input string is not valid ... + // a CORBA::SystemException is thrown. + if ( aVal && strlen( aVal ) > 0 ) + return app->orb()->string_to_object(aVal); } } return CORBA::Object::_nil(); @@ -201,13 +209,27 @@ namespace SMESH return SObjectToObject(theSObject,aStudy); } + _PTR(SObject) ObjectToSObject( CORBA::Object_ptr theObject ) + { + _PTR(SObject) res; + SalomeApp_Application* app = dynamic_cast + (SUIT_Session::session()->activeApplication()); + if ( app ) { + QString IOR = app->orb()->object_to_string( theObject ); + SalomeApp_Study* study = dynamic_cast( app->activeStudy() ); + if ( study && !IOR.isEmpty() ) + res = study->studyDS()->FindObjectIOR( IOR.toLatin1().constData() ); + } + return res; + } + CORBA::Object_var IObjectToObject (const Handle(SALOME_InteractiveObject)& theIO) { if (!theIO.IsNull()) { if (theIO->hasEntry()) { - _PTR(Study) aStudy = GetActiveStudyDocument(); - _PTR(SObject) anObj = aStudy->FindObjectID(theIO->getEntry()); - return SObjectToObject(anObj,aStudy); + _PTR(Study) aStudy = GetActiveStudyDocument(); + _PTR(SObject) anObj = aStudy->FindObjectID(theIO->getEntry()); + return SObjectToObject(anObj,aStudy); } } return CORBA::Object::_nil(); @@ -230,7 +252,7 @@ namespace SMESH int aNbSel = selected.Extent(); if (aNbSel == 1) { Handle(SALOME_InteractiveObject) anIObject = selected.First(); - theName = anIObject->getName(); + theName = QString( anIObject->getName() ).trimmed(); } else { theName = QObject::tr("SMESH_OBJECTS_SELECTED").arg(aNbSel); } @@ -261,31 +283,63 @@ namespace SMESH _PTR(GenericAttribute) anAttr = aBuilder->FindOrCreateAttribute(theSObject,"AttributePixMap"); _PTR(AttributePixMap) aPixmap = anAttr; - if (theIsNotModif) { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH"); - } else if ( isEmptyMesh ) { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH_WARN"); - } else { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH_PARTIAL"); - } + + std::string pmName; + if (theIsNotModif) + pmName = "ICON_SMESH_TREE_MESH"; + else if ( isEmptyMesh ) + pmName = "ICON_SMESH_TREE_MESH_WARN"; + else + pmName = "ICON_SMESH_TREE_MESH_PARTIAL"; + aPixmap->SetPixMap( pmName ); _PTR(ChildIterator) anIter = aStudy->NewChildIterator(theSObject); for (int i = 1; anIter->More(); anIter->Next(), i++) { _PTR(SObject) aSObj = anIter->Value(); if (i >= 4) { - _PTR(ChildIterator) anIter1 = aStudy->NewChildIterator(aSObj); - for ( ; anIter1->More(); anIter1->Next()) { - _PTR(SObject) aSObj1 = anIter1->Value(); - anAttr = aBuilder->FindOrCreateAttribute(aSObj1, "AttributePixMap"); - aPixmap = anAttr; - if (theIsNotModif) { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH"); - } else if ( isEmptyMesh ) { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH_WARN"); - } else { - aPixmap->SetPixMap("ICON_SMESH_TREE_MESH_PARTIAL"); + _PTR(ChildIterator) anIter1 = aStudy->NewChildIterator(aSObj); + for ( ; anIter1->More(); anIter1->Next()) + { + _PTR(SObject) aSObj1 = anIter1->Value(); + + anAttr = aBuilder->FindOrCreateAttribute(aSObj1, "AttributePixMap"); + aPixmap = anAttr; + + std::string entry = aSObj1->GetID(); + int objType = SMESHGUI_Selection::type( entry.c_str(), aStudy ); + + SMESH::SMESH_IDSource_var idSrc = SObjectToInterface( aSObj1 ); + if ( !idSrc->_is_nil() ) + { + SMESH::SMESH_GroupOnFilter_var gof = + SObjectToInterface( aSObj1 ); + const bool isGroupOnFilter = !gof->_is_nil(); + + bool isEmpty = false; + if ( !isGroupOnFilter ) // GetTypes() can be very long on isGroupOnFilter! + { + SMESH::array_of_ElementType_var elemTypes = idSrc->GetTypes(); + isEmpty = ( elemTypes->length() == 0 ); + } + if ( isEmpty ) + aPixmap->SetPixMap("ICON_SMESH_TREE_MESH_WARN"); + else if ( objType != GROUP ) + aPixmap->SetPixMap( "ICON_SMESH_TREE_MESH" ); + else if ( isGroupOnFilter ) + aPixmap->SetPixMap( "ICON_SMESH_TREE_GROUP_ON_FILTER" ); + else + aPixmap->SetPixMap( "ICON_SMESH_TREE_GROUP" ); + } + else + { + if ( !theIsNotModif ) + aPixmap->SetPixMap( pmName ); + else if ( objType == GROUP ) + aPixmap->SetPixMap( "ICON_SMESH_TREE_GROUP" ); + else + aPixmap->SetPixMap( "ICON_SMESH_TREE_MESH" ); } - } + } } } } @@ -300,10 +354,10 @@ namespace SMESH } else { SUIT_MessageBox::warning(0, QObject::tr("WRN_WARNING"), - QObject::tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - "application")). - arg(theHelpFileName)); + QObject::tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). + arg(app->resourceMgr()->stringValue("ExternalBrowser", + "application")). + arg(theHelpFileName)); } } diff --git a/src/SMESHGUI/SMESHGUI_Utils.h b/src/SMESHGUI/SMESHGUI_Utils.h index 77f26e3e1..c813e1078 100644 --- a/src/SMESHGUI/SMESHGUI_Utils.h +++ b/src/SMESHGUI/SMESHGUI_Utils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_Utils.h // Author : Open CASCADE S.A.S. @@ -88,7 +89,7 @@ SMESHGUI_EXPORT { CORBA::Object_var anObj = DataOwnerToObject(theDataOwner); if(!CORBA::is_nil(anObj)) - return TInterface::_narrow(anObj); + return TInterface::_narrow(anObj); return TInterface::_nil(); } @@ -114,7 +115,7 @@ SMESHGUI_EXPORT SMESHGUI_EXPORT CORBA::Object_var SObjectToObject( _PTR(SObject), - _PTR(Study) ); + _PTR(Study) ); SMESHGUI_EXPORT CORBA::Object_var SObjectToObject( _PTR(SObject) ); @@ -124,10 +125,13 @@ SMESHGUI_EXPORT { CORBA::Object_var anObj = SObjectToObject(theSObject); if(!CORBA::is_nil(anObj)) - return TInterface::_narrow(anObj); + return TInterface::_narrow(anObj); return TInterface::_nil(); } +SMESHGUI_EXPORT + _PTR(SObject) ObjectToSObject( CORBA::Object_ptr ); + SMESHGUI_EXPORT CORBA::Object_var IObjectToObject( const Handle(SALOME_InteractiveObject)& ); @@ -136,7 +140,7 @@ SMESHGUI_EXPORT { CORBA::Object_var anObj = IObjectToObject(theIO); if(!CORBA::is_nil(anObj)) - return TInterface::_narrow(anObj); + return TInterface::_narrow(anObj); return TInterface::_nil(); } @@ -148,7 +152,7 @@ SMESHGUI_EXPORT { CORBA::Object_var anObj = IORToObject( theIOR ); if ( !CORBA::is_nil( anObj ) ) - return TInterface::_narrow( anObj ); + return TInterface::_narrow( anObj ); return TInterface::_nil(); } diff --git a/src/SMESHGUI/SMESHGUI_VTKUtils.cxx b/src/SMESHGUI/SMESHGUI_VTKUtils.cxx index 9289f0108..baafa985c 100644 --- a/src/SMESHGUI/SMESHGUI_VTKUtils.cxx +++ b/src/SMESHGUI/SMESHGUI_VTKUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_VTKUtils.cxx // Author : Open CASCADE S.A.S. @@ -29,6 +30,7 @@ #include "SMESHGUI.h" #include "SMESHGUI_Utils.h" #include "SMESHGUI_Filter.h" +#include "SMESH_ControlsDef.hxx" #include #include @@ -49,6 +51,8 @@ #include #include +#include + #include #include #include @@ -62,6 +66,7 @@ #include CORBA_CLIENT_HEADER(SMESH_Group) // VTK includes +#include #include #include #include @@ -98,31 +103,54 @@ namespace SMESH */ //================================================================================ - void RemoveVisualObjectWithActors( const char* theEntry ) + void RemoveVisualObjectWithActors( const char* theEntry, bool fromAllViews ) { - SalomeApp_Application* app = dynamic_cast - ( SUIT_Session::session()->activeApplication() ); - SUIT_ViewManager* aViewManager = - app ? app->getViewManager(SVTK_Viewer::Type(), true) : 0; - if ( aViewManager ) { + SalomeApp_Application* app = dynamic_cast(SUIT_Session::session()->activeApplication()); + if(!app) + return; + SalomeApp_Study* aStudy = dynamic_cast(app->activeStudy()); + if(!aStudy) + return; + ViewManagerList aList; + + if(fromAllViews) { + app->viewManagers(SVTK_Viewer::Type() , aList); + } else { + SUIT_ViewManager* aVM = app->getViewManager(SVTK_Viewer::Type(), true); + if(aVM) + aList.append(aVM); + } + bool actorRemoved = false; + ViewManagerList::ConstIterator it = aList.begin(); + SUIT_ViewManager* aViewManager = 0; + for( ; it!=aList.end();it++) { + aViewManager = *it; QVector views = aViewManager->getViews(); for ( int iV = 0; iV < views.count(); ++iV ) { if ( SMESH_Actor* actor = FindActorByEntry( views[iV], theEntry)) { - if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(views[iV])) + if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(views[iV])) { vtkWnd->RemoveActor(actor); + actorRemoved = true; + } actor->Delete(); } } + } + + if (aViewManager ) { int aStudyId = aViewManager->study()->id(); TVisualObjCont::key_type aKey(aStudyId,theEntry); TVisualObjCont::iterator anIter = VISUAL_OBJ_CONT.find(aKey); if(anIter != VISUAL_OBJ_CONT.end()) { // for unknown reason, object destructor is not called, so clear object manually - anIter->second->GetUnstructuredGrid()->SetCells(0,0,0); + anIter->second->GetUnstructuredGrid()->SetCells(0,0,0,0,0); anIter->second->GetUnstructuredGrid()->SetPoints(0); } VISUAL_OBJ_CONT.erase(aKey); } + + if(actorRemoved) + aStudy->setVisibilityState(theEntry, Qtx::HiddenState); } //================================================================================ /*! @@ -143,10 +171,11 @@ namespace SMESH for ( int iV = 0; iV < views.count(); ++iV ) { if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(views[iV])) { vtkRenderer *aRenderer = vtkWnd->getRenderer(); - vtkActorCollection *actors = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *actors = aCopy.GetActors(); for (int i = 0; i < actors->GetNumberOfItems(); ++i ) { // size of actors changes inside the loop - while (SMESH_Actor *actor = dynamic_cast(actors->GetItemAsObject(i))) + if (SMESH_Actor *actor = dynamic_cast(actors->GetItemAsObject(i))) { vtkWnd->RemoveActor(actor); actor->Delete(); @@ -159,7 +188,7 @@ namespace SMESH TVisualObjCont::iterator anIter = VISUAL_OBJ_CONT.begin(); for ( ; anIter != VISUAL_OBJ_CONT.end(); ++anIter ) { // for unknown reason, object destructor is not called, so clear object manually - anIter->second->GetUnstructuredGrid()->SetCells(0,0,0); + anIter->second->GetUnstructuredGrid()->SetCells(0,0,0,0,0); anIter->second->GetUnstructuredGrid()->SetPoints(0); } VISUAL_OBJ_CONT.clear(); @@ -185,10 +214,11 @@ namespace SMESH for ( int iV = 0; iV < views.count(); ++iV ) { if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(views[iV])) { vtkRenderer *aRenderer = vtkWnd->getRenderer(); - vtkActorCollection *actors = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *actors = aCopy.GetActors(); for (int i = 0; i < actors->GetNumberOfItems(); ++i ) { // size of actors changes inside the loop - while(SMESH_Actor *actor = dynamic_cast(actors->GetItemAsObject(i))) + if(SMESH_Actor *actor = dynamic_cast(actors->GetItemAsObject(i))) { vtkWnd->RemoveActor(actor); actor->Delete(); @@ -199,13 +229,16 @@ namespace SMESH } } TVisualObjCont::iterator anIter = VISUAL_OBJ_CONT.begin(); - for ( ; anIter != VISUAL_OBJ_CONT.end(); ++anIter ) { + for ( ; anIter != VISUAL_OBJ_CONT.end(); ) { int curId = anIter->first.first; if ( curId == studyID ) { // for unknown reason, object destructor is not called, so clear object manually - anIter->second->GetUnstructuredGrid()->SetCells(0,0,0); + anIter->second->GetUnstructuredGrid()->SetCells(0,0,0,0,0); anIter->second->GetUnstructuredGrid()->SetPoints(0); - VISUAL_OBJ_CONT.erase( anIter-- ); // dercement occures before erase() + VISUAL_OBJ_CONT.erase( anIter++ ); // anIter++ returns a copy of self before incrementing + } + else { + anIter++; } } } @@ -227,18 +260,18 @@ namespace SMESH // char* buf = new char[100*1024]; // delete [] buf; SUIT_MessageBox::warning(SMESHGUI::desktop(), QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_VISU_PROBLEM")); + QObject::tr("SMESH_VISU_PROBLEM")); } catch (...) { // no more memory at all: last resort MESSAGE_BEGIN ( "SMESHGUI_VTKUtils::OnVisuException(), exception even at showing a message!!!" << - std::endl << "Try to remove all visual data..." ); + std::endl << "Try to remove all visual data..." ); if (theVISU_MemoryReserve) { delete theVISU_MemoryReserve; theVISU_MemoryReserve = 0; } RemoveAllObjectsWithActors(); SUIT_MessageBox::warning(SMESHGUI::desktop(), QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_VISU_PROBLEM_CLEAR")); + QObject::tr("SMESH_VISU_PROBLEM_CLEAR")); MESSAGE_END ( "...done" ); } } @@ -248,7 +281,7 @@ namespace SMESH */ //================================================================================ - TVisualObjPtr GetVisualObj(int theStudyId, const char* theEntry){ + TVisualObjPtr GetVisualObj(int theStudyId, const char* theEntry, bool nulData){ TVisualObjPtr aVisualObj; TVisualObjCont::key_type aKey(theStudyId,theEntry); try{ @@ -257,59 +290,59 @@ namespace SMESH #endif TVisualObjCont::iterator anIter = VISUAL_OBJ_CONT.find(aKey); if(anIter != VISUAL_OBJ_CONT.end()){ - aVisualObj = anIter->second; + aVisualObj = anIter->second; }else{ SalomeApp_Application* app = dynamic_cast( SMESHGUI::activeStudy()->application() ); - _PTR(Study) aStudy = SMESHGUI::activeStudy()->studyDS(); - _PTR(SObject) aSObj = aStudy->FindObjectID(theEntry); - if(aSObj){ - _PTR(GenericAttribute) anAttr; - if(aSObj->FindAttribute(anAttr,"AttributeIOR")){ - _PTR(AttributeIOR) anIOR = anAttr; - CORBA::String_var aVal = anIOR->Value().c_str(); - CORBA::Object_var anObj = app->orb()->string_to_object( aVal.in() ); - if(!CORBA::is_nil(anObj)){ - //Try narrow to SMESH_Mesh interface - SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow(anObj); - if(!aMesh->_is_nil()){ - aVisualObj.reset(new SMESH_MeshObj(aMesh)); - TVisualObjCont::value_type aValue(aKey,aVisualObj); - VISUAL_OBJ_CONT.insert(aValue); - } - //Try narrow to SMESH_Group interface - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObj); - if(!aGroup->_is_nil()){ - _PTR(SObject) aFatherSObj = aSObj->GetFather(); - if(!aFatherSObj) return aVisualObj; - aFatherSObj = aFatherSObj->GetFather(); - if(!aFatherSObj) return aVisualObj; - CORBA::String_var anEntry = aFatherSObj->GetID().c_str(); - TVisualObjPtr aVisObj = GetVisualObj(theStudyId,anEntry.in()); - if(SMESH_MeshObj* aMeshObj = dynamic_cast(aVisObj.get())){ - aVisualObj.reset(new SMESH_GroupObj(aGroup,aMeshObj)); - TVisualObjCont::value_type aValue(aKey,aVisualObj); - VISUAL_OBJ_CONT.insert(aValue); - } - } - //Try narrow to SMESH_subMesh interface - SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow(anObj); - if(!aSubMesh->_is_nil()){ - _PTR(SObject) aFatherSObj = aSObj->GetFather(); - if(!aFatherSObj) return aVisualObj; - aFatherSObj = aFatherSObj->GetFather(); - if(!aFatherSObj) return aVisualObj; - CORBA::String_var anEntry = aFatherSObj->GetID().c_str(); - TVisualObjPtr aVisObj = GetVisualObj(theStudyId,anEntry.in()); - if(SMESH_MeshObj* aMeshObj = dynamic_cast(aVisObj.get())){ - aVisualObj.reset(new SMESH_subMeshObj(aSubMesh,aMeshObj)); - TVisualObjCont::value_type aValue(aKey,aVisualObj); - VISUAL_OBJ_CONT.insert(aValue); - } - } - } - } - } + _PTR(Study) aStudy = SMESHGUI::activeStudy()->studyDS(); + _PTR(SObject) aSObj = aStudy->FindObjectID(theEntry); + if(aSObj){ + _PTR(GenericAttribute) anAttr; + if(aSObj->FindAttribute(anAttr,"AttributeIOR")){ + _PTR(AttributeIOR) anIOR = anAttr; + CORBA::String_var aVal = anIOR->Value().c_str(); + CORBA::Object_var anObj = app->orb()->string_to_object( aVal.in() ); + if(!CORBA::is_nil(anObj)){ + //Try narrow to SMESH_Mesh interface + SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow(anObj); + if(!aMesh->_is_nil()){ + aVisualObj.reset(new SMESH_MeshObj(aMesh)); + TVisualObjCont::value_type aValue(aKey,aVisualObj); + VISUAL_OBJ_CONT.insert(aValue); + } + //Try narrow to SMESH_Group interface + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow(anObj); + if(!aGroup->_is_nil()){ + _PTR(SObject) aFatherSObj = aSObj->GetFather(); + if(!aFatherSObj) return aVisualObj; + aFatherSObj = aFatherSObj->GetFather(); + if(!aFatherSObj) return aVisualObj; + CORBA::String_var anEntry = aFatherSObj->GetID().c_str(); + TVisualObjPtr aVisObj = GetVisualObj(theStudyId,anEntry.in()); + if(SMESH_MeshObj* aMeshObj = dynamic_cast(aVisObj.get())){ + aVisualObj.reset(new SMESH_GroupObj(aGroup,aMeshObj)); + TVisualObjCont::value_type aValue(aKey,aVisualObj); + VISUAL_OBJ_CONT.insert(aValue); + } + } + //Try narrow to SMESH_subMesh interface + SMESH::SMESH_subMesh_var aSubMesh = SMESH::SMESH_subMesh::_narrow(anObj); + if(!aSubMesh->_is_nil()){ + _PTR(SObject) aFatherSObj = aSObj->GetFather(); + if(!aFatherSObj) return aVisualObj; + aFatherSObj = aFatherSObj->GetFather(); + if(!aFatherSObj) return aVisualObj; + CORBA::String_var anEntry = aFatherSObj->GetID().c_str(); + TVisualObjPtr aVisObj = GetVisualObj(theStudyId,anEntry.in()); + if(SMESH_MeshObj* aMeshObj = dynamic_cast(aVisObj.get())){ + aVisualObj.reset(new SMESH_subMeshObj(aSubMesh,aMeshObj)); + TVisualObjCont::value_type aValue(aKey,aVisualObj); + VISUAL_OBJ_CONT.insert(aValue); + } + } + } + } + } } }catch(...){ INFOS("GetMeshObj - There is no SMESH_Mesh object for the SALOMEDS::Strudy and Entry!!!"); @@ -322,7 +355,11 @@ namespace SMESH #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 OCC_CATCH_SIGNALS; #endif - objModified = aVisualObj->Update(); + //MESSAGE("GetVisualObj"); + if (nulData) + objModified = aVisualObj->NulData(); + else + objModified = aVisualObj->Update(); } catch (...) { #ifdef _DEBUG_ @@ -336,34 +373,31 @@ namespace SMESH if ( objModified ) { // PAL16631. Mesurements showed that to show aVisualObj in SHADING(default) mode, - // ~10 times more memory is used than it occupies. + // ~5 times more memory is used than it occupies. // Warn the user if there is less free memory than 30 sizes of a grid // TODO: estimate memory usage in other modes and take current mode into account int freeMB = SMDS_Mesh::CheckMemory(true); int usedMB = aVisualObj->GetUnstructuredGrid()->GetActualMemorySize() / 1024; - if ( freeMB > 0 && usedMB * 30 > freeMB ) { -#ifdef _DEBUG_ - MESSAGE ( "SMESHGUI_VTKUtils::GetVisualObj(), freeMB=" << freeMB - << ", usedMB=" << usedMB ); -#endif - bool continu = false; - if ( usedMB * 10 > freeMB ) - // even dont try to show - SUIT_MessageBox::warning(SMESHGUI::desktop(), QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_NO_MESH_VISUALIZATION")); - else - // there is a chance to succeed - continu = SUIT_MessageBox::warning - (SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_CONTINUE_MESH_VISUALIZATION"), - SUIT_MessageBox::Yes | SUIT_MessageBox::No, - SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes; - if ( !continu ) { - // remove the corresponding actors from all views - RemoveVisualObjectWithActors( theEntry ); - aVisualObj.reset(); - } + MESSAGE("SMESHGUI_VTKUtils::GetVisualObj(), freeMB=" << freeMB << ", usedMB=" < 0 && usedMB * 5 > freeMB ) { + bool continu = false; + if ( usedMB * 3 > freeMB ) + // even dont try to show + SUIT_MessageBox::warning(SMESHGUI::desktop(), QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_NO_MESH_VISUALIZATION")); + else + // there is a chance to succeed + continu = SUIT_MessageBox::warning + (SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_CONTINUE_MESH_VISUALIZATION"), + SUIT_MessageBox::Yes | SUIT_MessageBox::No, + SUIT_MessageBox::Yes ) == SUIT_MessageBox::Yes; + if ( !continu ) { + // remove the corresponding actors from all views + RemoveVisualObjectWithActors( theEntry ); + aVisualObj.reset(); + } } } @@ -387,7 +421,7 @@ namespace SMESH if (anApp) { if (SVTK_ViewWindow* aView = dynamic_cast(anApp->desktop()->activeWindow())) - return aView; + return aView; SUIT_ViewManager* aViewManager = anApp->getViewManager(SVTK_Viewer::Type(), createIfNotFound); @@ -512,21 +546,22 @@ namespace SMESH SMESH_Actor* FindActorByEntry(SUIT_ViewWindow *theWindow, - const char* theEntry) + const char* theEntry) { if(SVTK_ViewWindow* aViewWindow = GetVtkViewWindow(theWindow)){ vtkRenderer *aRenderer = aViewWindow->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while(vtkActor *anAct = aCollection->GetNextActor()){ - if(SMESH_Actor *anActor = dynamic_cast(anAct)){ - if(anActor->hasIO()){ - Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); - if(anIO->hasEntry() && strcmp(anIO->getEntry(),theEntry) == 0){ - return anActor; - } - } - } + if(SMESH_Actor *anActor = dynamic_cast(anAct)){ + if(anActor->hasIO()){ + Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); + if(anIO->hasEntry() && strcmp(anIO->getEntry(),theEntry) == 0){ + return anActor; + } + } + } } } return NULL; @@ -548,8 +583,8 @@ namespace SMESH CORBA::String_var anIOR = app->orb()->object_to_string( theObject ); _PTR(SObject) aSObject = aStudy->FindObjectIOR(anIOR.in()); if(aSObject){ - CORBA::String_var anEntry = aSObject->GetID().c_str(); - return FindActorByEntry(anEntry.in()); + CORBA::String_var anEntry = aSObject->GetID().c_str(); + return FindActorByEntry(anEntry.in()); } } return NULL; @@ -557,43 +592,51 @@ namespace SMESH SMESH_Actor* CreateActor(_PTR(Study) theStudy, - const char* theEntry, - int theIsClear) + const char* theEntry, + int theIsClear) { SMESH_Actor *anActor = NULL; CORBA::Long anId = theStudy->StudyId(); if(TVisualObjPtr aVisualObj = GetVisualObj(anId,theEntry)){ _PTR(SObject) aSObj = theStudy->FindObjectID(theEntry); if(aSObj){ - _PTR(GenericAttribute) anAttr; - if(aSObj->FindAttribute(anAttr,"AttributeName")){ - _PTR(AttributeName) aName = anAttr; - std::string aNameVal = aName->Value(); - anActor = SMESH_Actor::New(aVisualObj,theEntry,aNameVal.c_str(),theIsClear); - } - - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( aSObj )); - if(!CORBA::is_nil(aGroup)) - { - SALOMEDS::Color aColor = aGroup->GetColor(); - if( !( aColor.R > 0 || aColor.G > 0 || aColor.B > 0 ) ) - { - int r = 0, g = 0, b = 0; - SMESH::GetColor( "SMESH", "fill_color", r, g, b, QColor( 0, 170, 255 ) ); - aColor.R = (float)r / 255.0; - aColor.G = (float)g / 255.0; - aColor.B = (float)b / 255.0; - aGroup->SetColor( aColor ); - } - if( aGroup->GetType() == SMESH::NODE ) - anActor->SetNodeColor( aColor.R, aColor.G, aColor.B ); - else if( aGroup->GetType() == SMESH::EDGE ) - anActor->SetEdgeColor( aColor.R, aColor.G, aColor.B ); - else - anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B ); - } + _PTR(GenericAttribute) anAttr; + if(aSObj->FindAttribute(anAttr,"AttributeName")){ + _PTR(AttributeName) aName = anAttr; + std::string aNameVal = aName->Value(); + anActor = SMESH_Actor::New(aVisualObj,theEntry,aNameVal.c_str(),theIsClear); + } + + SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH::SObjectToObject( aSObj )); + if(!CORBA::is_nil(aGroup) && anActor) + { + QColor c;int delta; + SMESH::GetColor( "SMESH", "fill_color", c, delta, "0,170,255|-100" ); + SALOMEDS::Color aColor = aGroup->GetColor(); + if( !( aColor.R > 0 || aColor.G > 0 || aColor.B > 0 )) + { + aColor.R = (float)c.red() / 255.0; + aColor.G = (float)c.green() / 255.0; + aColor.B = (float)c.blue() / 255.0; + aGroup->SetColor( aColor ); + } + if( aGroup->GetType() == SMESH::NODE ) + anActor->SetNodeColor( aColor.R, aColor.G, aColor.B ); + else if( aGroup->GetType() == SMESH::EDGE ) + anActor->SetEdgeColor( aColor.R, aColor.G, aColor.B ); + else if( aGroup->GetType() == SMESH::ELEM0D ) + anActor->Set0DColor( aColor.R, aColor.G, aColor.B ); + else if( aGroup->GetType() == SMESH::BALL ) + anActor->SetBallColor( aColor.R, aColor.G, aColor.B ); + else + anActor->SetSufaceColor( aColor.R, aColor.G, aColor.B, delta ); + } } } + MESSAGE("CreateActor " << anActor); + if( anActor ) + if( SMESHGUI* aSMESHGUI = SMESHGUI::GetSMESHGUI() ) + aSMESHGUI->addActorAsObserver( anActor ); return anActor; } @@ -604,6 +647,7 @@ namespace SMESH #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) > 0x060100 OCC_CATCH_SIGNALS; #endif + MESSAGE("DisplayActor " << theActor); vtkWnd->AddActor(theActor); vtkWnd->Repaint(); } @@ -619,16 +663,17 @@ namespace SMESH void RemoveActor( SUIT_ViewWindow *theWnd, SMESH_Actor* theActor){ if(SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(theWnd)){ + MESSAGE("RemoveActor " << theActor); vtkWnd->RemoveActor(theActor); if(theActor->hasIO()){ - Handle(SALOME_InteractiveObject) anIO = theActor->getIO(); - if(anIO->hasEntry()){ - std::string anEntry = anIO->getEntry(); - SalomeApp_Study* aStudy = dynamic_cast( vtkWnd->getViewManager()->study() ); - int aStudyId = aStudy->id(); - TVisualObjCont::key_type aKey(aStudyId,anEntry); - VISUAL_OBJ_CONT.erase(aKey); - } + Handle(SALOME_InteractiveObject) anIO = theActor->getIO(); + if(anIO->hasEntry()){ + std::string anEntry = anIO->getEntry(); + SalomeApp_Study* aStudy = dynamic_cast( vtkWnd->getViewManager()->study() ); + int aStudyId = aStudy->id(); + TVisualObjCont::key_type aKey(aStudyId,anEntry); + VISUAL_OBJ_CONT.erase(aKey); + } } theActor->Delete(); vtkWnd->Repaint(); @@ -645,10 +690,11 @@ namespace SMESH { if(SVTK_ViewWindow* aViewWindow = GetVtkViewWindow(theWnd)) { vtkRenderer *aRenderer = aViewWindow->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while(vtkActor *anAct = aCollection->GetNextActor()) - if(dynamic_cast(anAct)) + if(dynamic_cast(anAct)) return false; } return true; @@ -656,60 +702,90 @@ namespace SMESH bool UpdateView(SUIT_ViewWindow *theWnd, EDisplaing theAction, const char* theEntry) { + //MESSAGE("UpdateView"); bool OK = false; SVTK_ViewWindow* aViewWnd = GetVtkViewWindow(theWnd); if (!aViewWnd) return OK; + SVTK_ViewWindow* vtkWnd = GetVtkViewWindow(theWnd); + if (!vtkWnd) + return OK; + + SalomeApp_Study* aStudy = dynamic_cast( vtkWnd->getViewManager()->study() ); + + if (!aStudy) + return OK; + { OK = true; vtkRenderer *aRenderer = aViewWnd->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); switch (theAction) { case eDisplayAll: { - while (vtkActor *anAct = aCollection->GetNextActor()) { - if (SMESH_Actor *anActor = dynamic_cast(anAct)) { - anActor->SetVisibility(true); - } - } - break; + while (vtkActor *anAct = aCollection->GetNextActor()) { + if (SMESH_Actor *anActor = dynamic_cast(anAct)) { + MESSAGE("--- display " << anActor); + anActor->SetVisibility(true); + + if(anActor->hasIO()){ + Handle(SALOME_InteractiveObject) anIO = anActor->getIO(); + if(anIO->hasEntry()){ + aStudy->setVisibilityState(anIO->getEntry(), Qtx::ShownState); + } + } + } + } + break; } case eDisplayOnly: case eEraseAll: { - while (vtkActor *anAct = aCollection->GetNextActor()) { - if (SMESH_Actor *anActor = dynamic_cast(anAct)) { - anActor->SetVisibility(false); - } - } + //MESSAGE("---case eDisplayOnly"); + while (vtkActor *anAct = aCollection->GetNextActor()) { + if (SMESH_Actor *anActor = dynamic_cast(anAct)) { + //MESSAGE("--- erase " << anActor); + anActor->SetVisibility(false); + } + } + aStudy->setVisibilityStateForAll(Qtx::HiddenState); } default: { - if (SMESH_Actor *anActor = FindActorByEntry(theWnd,theEntry)) { - switch (theAction) { - case eDisplay: - case eDisplayOnly: - anActor->SetVisibility(true); - if (theAction == eDisplayOnly) aRenderer->ResetCameraClippingRange(); - break; - case eErase: - anActor->SetVisibility(false); - break; - } - } else { - switch (theAction) { - case eDisplay: - case eDisplayOnly: + if (SMESH_Actor *anActor = FindActorByEntry(theWnd,theEntry)) { + switch (theAction) { + case eDisplay: + case eDisplayOnly: + //MESSAGE("--- display " << anActor); + anActor->Update(); + anActor->SetVisibility(true); + if (theAction == eDisplayOnly) aRenderer->ResetCameraClippingRange(); + aStudy->setVisibilityState(theEntry, Qtx::ShownState); + break; + case eErase: + //MESSAGE("--- erase " << anActor); + anActor->SetVisibility(false); + aStudy->setVisibilityState(theEntry, Qtx::HiddenState); + break; + } + } else { + switch (theAction) { + case eDisplay: + case eDisplayOnly: { + //MESSAGE("---"); SalomeApp_Study* aStudy = dynamic_cast(theWnd->getViewManager()->study()); _PTR(Study) aDocument = aStudy->studyDS(); // Pass non-visual objects (hypotheses, etc.), return true in this case CORBA::Long anId = aDocument->StudyId(); - if (TVisualObjPtr aVisualObj = GetVisualObj(anId,theEntry)) + TVisualObjPtr aVisualObj; + if ( (aVisualObj = GetVisualObj(anId,theEntry)) && aVisualObj->IsValid()) { if ((anActor = CreateActor(aDocument,theEntry,true))) { bool needFitAll = noSmeshActors(theWnd); // fit for the first object only DisplayActor(theWnd,anActor); + aStudy->setVisibilityState(theEntry, Qtx::ShownState); // FitAll(); - PAL16770(Display of a group performs an automatic fit all) if (needFitAll) FitAll(); } else { @@ -718,8 +794,8 @@ namespace SMESH } break; } - } - } + } + } } } } @@ -728,6 +804,7 @@ namespace SMESH bool UpdateView(EDisplaing theAction, const char* theEntry){ + //MESSAGE("UpdateView"); SalomeApp_Study* aStudy = dynamic_cast< SalomeApp_Study* >( GetActiveStudy() ); SalomeApp_Application* app = dynamic_cast< SalomeApp_Application* >( aStudy->application() ); SUIT_ViewWindow *aWnd = app->activeViewManager()->getActiveView(); @@ -740,23 +817,24 @@ namespace SMESH SALOME_ListIO selected; mgr->selectedObjects( selected ); if( selected.Extent() == 0){ - vtkRenderer* aRenderer = aWnd->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); - aCollection->InitTraversal(); - while(vtkActor *anAct = aCollection->GetNextActor()){ - if(SMESH_Actor *anActor = dynamic_cast(anAct)){ - if(anActor->hasIO()) - if (!Update(anActor->getIO(),anActor->GetVisibility())) + vtkRenderer* aRenderer = aWnd->getRenderer(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); + aCollection->InitTraversal(); + while(vtkActor *anAct = aCollection->GetNextActor()){ + if(SMESH_Actor *anActor = dynamic_cast(anAct)){ + if(anActor->hasIO()) + if (!Update(anActor->getIO(),anActor->GetVisibility())) break; // avoid multiple warinings if visu failed - } - } + } + } }else{ - SALOME_ListIteratorOfListIO anIter( selected ); - for( ; anIter.More(); anIter.Next()){ - Handle(SALOME_InteractiveObject) anIO = anIter.Value(); - if ( !Update(anIO,true) ) + SALOME_ListIteratorOfListIO anIter( selected ); + for( ; anIter.More(); anIter.Next()){ + Handle(SALOME_InteractiveObject) anIO = anIter.Value(); + if ( !Update(anIO,true) ) break; // avoid multiple warinings if visu failed - } + } } RepaintCurrentView(); } @@ -765,6 +843,7 @@ namespace SMESH bool Update(const Handle(SALOME_InteractiveObject)& theIO, bool theDisplay) { + MESSAGE("Update"); _PTR(Study) aStudy = GetActiveStudyDocument(); CORBA::Long anId = aStudy->StudyId(); if ( TVisualObjPtr aVisualObj = SMESH::GetVisualObj(anId,theIO->getEntry())) { @@ -775,6 +854,18 @@ namespace SMESH return false; } + bool UpdateNulData(const Handle(SALOME_InteractiveObject)& theIO, bool theDisplay) + { + MESSAGE("UpdateNulData"); + _PTR(Study) aStudy = GetActiveStudyDocument(); + CORBA::Long anId = aStudy->StudyId(); + if ( TVisualObjPtr aVisualObj = SMESH::GetVisualObj(anId,theIO->getEntry(), true)) { + if ( theDisplay ) + UpdateView(SMESH::eDisplay,theIO->getEntry()); + return true; + } + return false; + } void UpdateSelectionProp( SMESHGUI* theModule ) { if( !theModule ) @@ -805,45 +896,49 @@ namespace SMESH QColor aHiColor = mgr->colorValue( "SMESH", "selection_object_color", Qt::white ), aSelColor = mgr->colorValue( "SMESH", "selection_element_color", Qt::yellow ), - aPreColor = mgr->colorValue( "SMESH", "highlight_color", Qt::cyan ); + aPreColor = mgr->colorValue( "SMESH", "highlight_color", Qt::cyan ); - int SW = mgr->integerValue( "SMESH", "selection_width", 5 ), - PW = mgr->integerValue( "SMESH", "highlight_width", 5 ); + int aElem0DSize = mgr->integerValue("SMESH", "elem0d_size", 5); + int aBallSize = mgr->integerValue("SMESH", "ball_elem_size", 5); + int aLineWidth = mgr->integerValue("SMESH", "element_width", 1); + int maxSize = aElem0DSize; + if (aElem0DSize > maxSize) maxSize = aElem0DSize; + if (aLineWidth > maxSize) maxSize = aLineWidth; + if (aBallSize > maxSize) maxSize = aBallSize; double SP1 = mgr->doubleValue( "SMESH", "selection_precision_node", 0.025 ), SP2 = mgr->doubleValue( "SMESH", "selection_precision_element", 0.001 ), - SP3 = mgr->doubleValue( "SMESH", "selection_precision_object", 0.025 ); + SP3 = mgr->doubleValue( "SMESH", "selection_precision_object", 0.025 ); for ( int i=0, n=views.count(); iSetSelectionProp(aSelColor.red()/255., - aSelColor.green()/255., - aSelColor.blue()/255., - SW ); - // tolerances - aVtkView->SetSelectionTolerance(SP1, SP2, SP3); - - // pre-selection - aVtkView->SetPreselectionProp(aPreColor.red()/255., - aPreColor.green()/255., - aPreColor.blue()/255., - PW); - // update actors - vtkRenderer* aRenderer = aVtkView->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); - aCollection->InitTraversal(); - while(vtkActor *anAct = aCollection->GetNextActor()){ - if(SMESH_Actor *anActor = dynamic_cast(anAct)){ - anActor->SetHighlightColor(aHiColor.red()/255., - aHiColor.green()/255., - aHiColor.blue()/255.); - anActor->SetPreHighlightColor(aPreColor.red()/255., - aPreColor.green()/255., - aPreColor.blue()/255.); - } - } + // mesh element selection + aVtkView->SetSelectionProp(aSelColor.red()/255., + aSelColor.green()/255., + aSelColor.blue()/255.); + // tolerances + aVtkView->SetSelectionTolerance(SP1, SP2, SP3); + + // pre-selection + aVtkView->SetPreselectionProp(aPreColor.red()/255., + aPreColor.green()/255., + aPreColor.blue()/255.); + // update actors + vtkRenderer* aRenderer = aVtkView->getRenderer(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); + aCollection->InitTraversal(); + while(vtkActor *anAct = aCollection->GetNextActor()){ + if(SMESH_Actor *anActor = dynamic_cast(anAct)){ + anActor->SetHighlightColor(aHiColor.red()/255., + aHiColor.green()/255., + aHiColor.blue()/255.); + anActor->SetPreHighlightColor(aPreColor.red()/255., + aPreColor.green()/255., + aPreColor.blue()/255.); + } + } } } } @@ -860,7 +955,7 @@ namespace SMESH } void SetFilter(const Handle(VTKViewer_Filter)& theFilter, - SVTK_Selector* theSelector) + SVTK_Selector* theSelector) { if (theSelector) theSelector->SetFilter(theFilter); @@ -888,7 +983,7 @@ namespace SMESH } bool IsValid(SALOME_Actor* theActor, int theCellId, - SVTK_Selector* theSelector) + SVTK_Selector* theSelector) { return theSelector->IsValid(theActor,theCellId); } @@ -898,14 +993,15 @@ namespace SMESH void SetPointRepresentation(bool theIsVisible){ if(SVTK_ViewWindow* aViewWindow = GetCurrentVtkView()){ vtkRenderer *aRenderer = aViewWindow->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while(vtkActor *anAct = aCollection->GetNextActor()){ - if(SMESH_Actor *anActor = dynamic_cast(anAct)){ - if(anActor->GetVisibility()){ - anActor->SetPointRepresentation(theIsVisible); - } - } + if(SMESH_Actor *anActor = dynamic_cast(anAct)){ + if(anActor->GetVisibility()){ + anActor->SetPointRepresentation(theIsVisible); + } + } } RepaintCurrentView(); } @@ -916,17 +1012,18 @@ namespace SMESH if(SVTK_ViewWindow* aWnd = GetCurrentVtkView()){ int anIsAllPickable = (theActor == NULL); vtkRenderer *aRenderer = aWnd->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while(vtkActor *anAct = aCollection->GetNextActor()){ - if(SALOME_Actor *anActor = dynamic_cast(anAct)){ - if(anActor->GetVisibility()){ - anActor->SetPickable(anIsAllPickable); - } - } + if(SALOME_Actor *anActor = dynamic_cast(anAct)){ + if(anActor->GetVisibility()){ + anActor->SetPickable(anIsAllPickable); + } + } } if(theActor) - theActor->SetPickable(!anIsAllPickable); + theActor->SetPickable(!anIsAllPickable); RepaintCurrentView(); } } @@ -934,8 +1031,8 @@ namespace SMESH //---------------------------------------------------------------------------- int GetNameOfSelectedNodes(SVTK_Selector* theSelector, - const Handle(SALOME_InteractiveObject)& theIO, - QString& theName) + const Handle(SALOME_InteractiveObject)& theIO, + QString& theName) { theName = ""; TColStd_IndexedMapOfInteger aMapIndex; @@ -948,8 +1045,8 @@ namespace SMESH } int GetNameOfSelectedElements(SVTK_Selector* theSelector, - const Handle(SALOME_InteractiveObject)& theIO, - QString& theName) + const Handle(SALOME_InteractiveObject)& theIO, + QString& theName) { theName = ""; TColStd_IndexedMapOfInteger aMapIndex; @@ -969,9 +1066,9 @@ namespace SMESH int GetEdgeNodes(SVTK_Selector* theSelector, - const TVisualObjPtr& theVisualObject, - int& theId1, - int& theId2) + const TVisualObjPtr& theVisualObject, + int& theId1, + int& theId2) { const SALOME_ListIO& selected = theSelector->StoredIObjects(); @@ -991,9 +1088,9 @@ namespace SMESH for ( int i = 1; i <= aMapIndex.Extent(); i++ ) { int aVal = aMapIndex( i ); if ( aVal > 0 ) - anObjId = aVal; + anObjId = aVal; else - anEdgeNum = abs( aVal ) - 1; + anEdgeNum = abs( aVal ) - 1; } if ( anObjId == -1 || anEdgeNum == -1 ) @@ -1004,18 +1101,18 @@ namespace SMESH //---------------------------------------------------------------------------- int GetNameOfSelectedNodes(LightApp_SelectionMgr *theMgr, - const Handle(SALOME_InteractiveObject)& theIO, - QString& theName) + const Handle(SALOME_InteractiveObject)& theIO, + QString& theName) { theName = ""; if(theIO->hasEntry()){ if(FindActorByEntry(theIO->getEntry())){ - TColStd_IndexedMapOfInteger aMapIndex; - theMgr->GetIndexes(theIO,aMapIndex); - for(int i = 1; i <= aMapIndex.Extent(); i++){ - theName += QString(" %1").arg(aMapIndex(i)); - } - return aMapIndex.Extent(); + TColStd_IndexedMapOfInteger aMapIndex; + theMgr->GetIndexes(theIO,aMapIndex); + for(int i = 1; i <= aMapIndex.Extent(); i++){ + theName += QString(" %1").arg(aMapIndex(i)); + } + return aMapIndex.Extent(); } } return -1; @@ -1033,23 +1130,23 @@ namespace SMESH int GetNameOfSelectedElements(LightApp_SelectionMgr *theMgr, - const Handle(SALOME_InteractiveObject)& theIO, - QString& theName) + const Handle(SALOME_InteractiveObject)& theIO, + QString& theName) { theName = ""; if(theIO->hasEntry()){ if(FindActorByEntry(theIO->getEntry())){ - TColStd_IndexedMapOfInteger aMapIndex; - theMgr->GetIndexes(theIO,aMapIndex); - typedef std::set TIdContainer; - TIdContainer anIdContainer; - for( int i = 1; i <= aMapIndex.Extent(); i++) - anIdContainer.insert(aMapIndex(i)); - TIdContainer::const_iterator anIter = anIdContainer.begin(); - for( ; anIter != anIdContainer.end(); anIter++){ - theName += QString(" %1").arg(*anIter); - } - return aMapIndex.Extent(); + TColStd_IndexedMapOfInteger aMapIndex; + theMgr->GetIndexes(theIO,aMapIndex); + typedef std::set TIdContainer; + TIdContainer anIdContainer; + for( int i = 1; i <= aMapIndex.Extent(); i++) + anIdContainer.insert(aMapIndex(i)); + TIdContainer::const_iterator anIter = anIdContainer.begin(); + for( ; anIter != anIdContainer.end(); anIter++){ + theName += QString(" %1").arg(*anIter); + } + return aMapIndex.Extent(); } } return -1; @@ -1069,8 +1166,8 @@ namespace SMESH } int GetSelected(LightApp_SelectionMgr* theMgr, - TColStd_IndexedMapOfInteger& theMap, - const bool theIsElement) + TColStd_IndexedMapOfInteger& theMap, + const bool theIsElement) { theMap.Clear(); SALOME_ListIO selected; theMgr->selectedObjects( selected ); @@ -1079,7 +1176,7 @@ namespace SMESH { Handle(SALOME_InteractiveObject) anIO = selected.First(); if ( anIO->hasEntry() ) { - theMgr->GetIndexes( anIO, theMap ); + theMgr->GetIndexes( anIO, theMap ); } } return theMap.Extent(); @@ -1110,9 +1207,9 @@ namespace SMESH for ( int i = 1; i <= aMapIndex.Extent(); i++ ) { int aVal = aMapIndex( i ); if ( aVal > 0 ) - anObjId = aVal; + anObjId = aVal; else - anEdgeNum = abs( aVal ); + anEdgeNum = abs( aVal ); } if ( anObjId == -1 || anEdgeNum == -1 ) @@ -1126,7 +1223,8 @@ namespace SMESH if( SVTK_ViewWindow* aWnd = SMESH::GetCurrentVtkView() ) { vtkRenderer *aRenderer = aWnd->getRenderer(); - vtkActorCollection *aCollection = aRenderer->GetActors(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); aCollection->InitTraversal(); while ( vtkActor *anAct = aCollection->GetNextActor()) @@ -1140,4 +1238,134 @@ namespace SMESH } } + + //---------------------------------------------------------------------------- + // internal function + void ComputeBoundsParam( vtkFloatingPointType theBounds[6], + vtkFloatingPointType theDirection[3], + vtkFloatingPointType theMinPnt[3], + vtkFloatingPointType& theMaxBoundPrj, + vtkFloatingPointType& theMinBoundPrj ) + { + //Enlarge bounds in order to avoid conflicts of precision + for(int i = 0; i < 6; i += 2){ + static double EPS = 1.0E-3; + vtkFloatingPointType aDelta = (theBounds[i+1] - theBounds[i])*EPS; + theBounds[i] -= aDelta; + theBounds[i+1] += aDelta; + } + + vtkFloatingPointType aBoundPoints[8][3] = { {theBounds[0],theBounds[2],theBounds[4]}, + {theBounds[1],theBounds[2],theBounds[4]}, + {theBounds[0],theBounds[3],theBounds[4]}, + {theBounds[1],theBounds[3],theBounds[4]}, + {theBounds[0],theBounds[2],theBounds[5]}, + {theBounds[1],theBounds[2],theBounds[5]}, + {theBounds[0],theBounds[3],theBounds[5]}, + {theBounds[1],theBounds[3],theBounds[5]}}; + + int aMaxId = 0; + theMaxBoundPrj = vtkMath::Dot(theDirection,aBoundPoints[aMaxId]); + theMinBoundPrj = theMaxBoundPrj; + for(int i = 1; i < 8; i++){ + vtkFloatingPointType aTmp = vtkMath::Dot(theDirection,aBoundPoints[i]); + if(theMaxBoundPrj < aTmp){ + theMaxBoundPrj = aTmp; + aMaxId = i; + } + if(theMinBoundPrj > aTmp){ + theMinBoundPrj = aTmp; + } + } + vtkFloatingPointType *aMinPnt = aBoundPoints[aMaxId]; + theMinPnt[0] = aMinPnt[0]; + theMinPnt[1] = aMinPnt[1]; + theMinPnt[2] = aMinPnt[2]; + } + + // internal function + void DistanceToPosition( vtkFloatingPointType theBounds[6], + vtkFloatingPointType theDirection[3], + vtkFloatingPointType theDist, + vtkFloatingPointType thePos[3] ) + { + vtkFloatingPointType aMaxBoundPrj, aMinBoundPrj, aMinPnt[3]; + ComputeBoundsParam(theBounds,theDirection,aMinPnt,aMaxBoundPrj,aMinBoundPrj); + vtkFloatingPointType aLength = (aMaxBoundPrj-aMinBoundPrj)*theDist; + thePos[0] = aMinPnt[0]-theDirection[0]*aLength; + thePos[1] = aMinPnt[1]-theDirection[1]*aLength; + thePos[2] = aMinPnt[2]-theDirection[2]*aLength; + } + + // internal function (currently unused, left just in case) + void PositionToDistance( vtkFloatingPointType theBounds[6], + vtkFloatingPointType theDirection[3], + vtkFloatingPointType thePos[3], + vtkFloatingPointType& theDist ) + { + vtkFloatingPointType aMaxBoundPrj, aMinBoundPrj, aMinPnt[3]; + ComputeBoundsParam(theBounds,theDirection,aMinPnt,aMaxBoundPrj,aMinBoundPrj); + vtkFloatingPointType aPrj = vtkMath::Dot(theDirection,thePos); + theDist = (aPrj-aMinBoundPrj)/(aMaxBoundPrj-aMinBoundPrj); + } + + bool ComputeClippingPlaneParameters( std::list theActorList, + vtkFloatingPointType theNormal[3], + vtkFloatingPointType theDist, + vtkFloatingPointType theBounds[6], + vtkFloatingPointType theOrigin[3] ) + { + bool anIsOk = false; + theBounds[0] = theBounds[2] = theBounds[4] = VTK_DOUBLE_MAX; + theBounds[1] = theBounds[3] = theBounds[5] = -VTK_DOUBLE_MAX; + std::list::iterator anIter = theActorList.begin(); + for( ; anIter != theActorList.end(); anIter++ ) { + if( vtkActor* aVTKActor = *anIter ) { + if( SMESH_Actor* anActor = SMESH_Actor::SafeDownCast( aVTKActor ) ) { + vtkFloatingPointType aBounds[6]; + anActor->GetUnstructuredGrid()->GetBounds( aBounds ); + theBounds[0] = std::min( theBounds[0], aBounds[0] ); + theBounds[1] = std::max( theBounds[1], aBounds[1] ); + theBounds[2] = std::min( theBounds[2], aBounds[2] ); + theBounds[3] = std::max( theBounds[3], aBounds[3] ); + theBounds[4] = std::min( theBounds[4], aBounds[4] ); + theBounds[5] = std::max( theBounds[5], aBounds[5] ); + anIsOk = true; + } + } + } + + if( !anIsOk ) + return false; + + DistanceToPosition( theBounds, theNormal, theDist, theOrigin ); + return true; + } + +#ifndef DISABLE_PLOT2DVIEWER + //================================================================================ + /*! + * \brief Find all SMESH_Actor's in the View Window. + * If actor constains Plot2d_Histogram object remove it from each Plot2d Viewer. + */ + //================================================================================ + + void ClearPlot2Viewers( SUIT_ViewWindow* theWindow ) { + if(SVTK_ViewWindow* aViewWindow = GetVtkViewWindow(theWindow)){ + vtkRenderer *aRenderer = aViewWindow->getRenderer(); + VTK::ActorCollectionCopy aCopy(aRenderer->GetActors()); + vtkActorCollection *aCollection = aCopy.GetActors(); + aCollection->InitTraversal(); + while(vtkActor *anAct = aCollection->GetNextActor()){ + if(SMESH_Actor *anActor = dynamic_cast(anAct)){ + if(anActor->hasIO() && anActor->GetPlot2Histogram() ){ + ProcessIn2DViewers(anActor,RemoveFrom2dViewer); + } + } + } + } + } + +#endif + } // end of namespace SMESH diff --git a/src/SMESHGUI/SMESHGUI_VTKUtils.h b/src/SMESHGUI/SMESHGUI_VTKUtils.h index 05d2e268b..94d69a9cd 100644 --- a/src/SMESHGUI/SMESHGUI_VTKUtils.h +++ b/src/SMESHGUI/SMESHGUI_VTKUtils.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : SMESHGUI_VTKUtils.h // Author : Open CASCADE S.A.S. @@ -56,20 +57,22 @@ class SMESHGUI; class SMESH_Actor; class SALOME_Actor; +class vtkActor; + namespace SMESH { //---------------------------------------------------------------------------- typedef std::pair TKeyOfVisualObj; SMESHGUI_EXPORT - TVisualObjPtr GetVisualObj( int, const char* ); + TVisualObjPtr GetVisualObj( int, const char*, bool nulData =false ); SMESHGUI_EXPORT void OnVisuException(); // PAL16631 //---------------------------------------------------------------------------- SMESHGUI_EXPORT SVTK_ViewWindow* GetViewWindow( const SalomeApp_Module* = 0, - bool = false ); + bool = false ); SMESHGUI_EXPORT SVTK_ViewWindow* FindVtkViewWindow( SUIT_ViewManager*, SUIT_ViewWindow* ); SMESHGUI_EXPORT @@ -112,14 +115,17 @@ SMESHGUI_EXPORT SMESHGUI_EXPORT bool UpdateView( SUIT_ViewWindow*, EDisplaing, const char* = "" ); -SMESHGUI_EXPORT +SMESHGUI_EXPORT bool UpdateView( EDisplaing, const char* = "" ); SMESHGUI_EXPORT void UpdateView(); SMESHGUI_EXPORT - bool Update( const Handle(SALOME_InteractiveObject)&, bool ); + bool UpdateNulData( const Handle(SALOME_InteractiveObject)& theIO, bool theDisplay); + +SMESHGUI_EXPORT + bool Update( const Handle(SALOME_InteractiveObject)& theIO, bool theDisplay); //---------------------------------------------------------------------------- SMESHGUI_EXPORT @@ -153,37 +159,52 @@ SMESHGUI_EXPORT //---------------------------------------------------------------------------- SMESHGUI_EXPORT int GetNameOfSelectedNodes( SVTK_Selector*, - const Handle(SALOME_InteractiveObject)&, - QString& ); + const Handle(SALOME_InteractiveObject)&, + QString& ); SMESHGUI_EXPORT int GetNameOfSelectedElements( SVTK_Selector*, - const Handle(SALOME_InteractiveObject)&, - QString& ); + const Handle(SALOME_InteractiveObject)&, + QString& ); SMESHGUI_EXPORT int GetEdgeNodes( SVTK_Selector*, const TVisualObjPtr&, int&, int& ); //---------------------------------------------------------------------------- SMESHGUI_EXPORT int GetNameOfSelectedNodes( LightApp_SelectionMgr*, - const Handle(SALOME_InteractiveObject)&, - QString& ); + const Handle(SALOME_InteractiveObject)&, + QString& ); SMESHGUI_EXPORT int GetNameOfSelectedNodes( LightApp_SelectionMgr*, QString& ); SMESHGUI_EXPORT int GetNameOfSelectedElements( LightApp_SelectionMgr*, - const Handle(SALOME_InteractiveObject)&, - QString& ); + const Handle(SALOME_InteractiveObject)&, + QString& ); SMESHGUI_EXPORT int GetNameOfSelectedElements( LightApp_SelectionMgr*, QString& ); SMESHGUI_EXPORT int GetSelected( LightApp_SelectionMgr*, TColStd_IndexedMapOfInteger&, - const bool = true ); + const bool = true ); SMESHGUI_EXPORT int GetEdgeNodes( LightApp_SelectionMgr*, int&, int& ); SMESHGUI_EXPORT void SetControlsPrecision( const long ); + +#ifndef DISABLE_PLOT2DVIEWER +SMESHGUI_EXPORT + void ClearPlot2Viewers( SUIT_ViewWindow* theWindow ); +#endif + + //---------------------------------------------------------------------------- +SMESHGUI_EXPORT + bool ComputeClippingPlaneParameters( std::list theActorList, + vtkFloatingPointType theNormal[3], + vtkFloatingPointType theDist, + vtkFloatingPointType theBounds[6], + vtkFloatingPointType theOrigin[3] ); + SMESHGUI_EXPORT + void RemoveVisualObjectWithActors( const char* theEntry, bool fromAllViews = false ); }; #endif // SMESHGUI_VTKUTILS_H diff --git a/src/SMESHGUI/SMESHGUI_WhatIsDlg.cxx b/src/SMESHGUI/SMESHGUI_WhatIsDlg.cxx deleted file mode 100755 index c49e2dc1c..000000000 --- a/src/SMESHGUI/SMESHGUI_WhatIsDlg.cxx +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_WhatIsDlg.cxx -// Author : Vladimir TURIN, Open CASCADE S.A.S. -// SMESH includes -// -#include "SMESHGUI_WhatIsDlg.h" - -#include "SMESHGUI.h" -#include "SMESHGUI_Utils.h" -#include "SMESHGUI_VTKUtils.h" -#include "SMESHGUI_MeshUtils.h" -#include "SMESHGUI_IdValidator.h" - -#include "SMESH_Actor.h" -#include "SMESH_TypeFilter.hxx" -#include "SMESH_LogicalFilter.hxx" -#include "SMDS_Mesh.hxx" -#include "SMDS_VolumeTool.hxx" - -// SALOME GUI includes -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -// OCCT includes -#include -#include - -// Qt includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// IDL includes -#include -#include CORBA_SERVER_HEADER(SMESH_Group) - -#define SPACING 6 -#define MARGIN 11 - -//================================================================================= -// class : SMESHGUI_WhatIsDlg() -// purpose : -//================================================================================= -SMESHGUI_WhatIsDlg::SMESHGUI_WhatIsDlg( SMESHGUI* theModule ) - : QDialog( SMESH::GetDesktop( theModule ) ), - mySMESHGUI( theModule ), - mySelectionMgr( SMESH::GetSelectionMgr( theModule ) ) -{ - setModal( false ); - setAttribute( Qt::WA_DeleteOnClose, true ); - setWindowTitle(tr("SMESH_WHAT_IS_TITLE")); - setSizeGripEnabled(true); - QVBoxLayout* SMESHGUI_WhatIsDlgLayout = new QVBoxLayout(this); - SMESHGUI_WhatIsDlgLayout->setSpacing(SPACING); - SMESHGUI_WhatIsDlgLayout->setMargin(MARGIN); - - /***************************************************************/ - GroupMesh = new QGroupBox(this); - QHBoxLayout* GroupMeshLayout = new QHBoxLayout(GroupMesh); - GroupMeshLayout->setSpacing(SPACING); - GroupMeshLayout->setMargin(MARGIN); - - MeshLabel = new QLabel(tr("SMESH_NAME"), GroupMesh); - GroupMeshLayout->addWidget(MeshLabel); - MeshName = new QLineEdit(GroupMesh); - MeshName->setReadOnly(true); - GroupMeshLayout->addWidget(MeshName); - - /***************************************************************/ - GroupSelections = new QGroupBox(tr("ENTITY_TYPE"), this); - QButtonGroup* GroupSel = new QButtonGroup(this); - QHBoxLayout* GroupSelectionsLayout = new QHBoxLayout(GroupSelections); - GroupSelectionsLayout->setSpacing(SPACING); - GroupSelectionsLayout->setMargin(MARGIN); - - RadioButtonNodes = new QRadioButton(tr("SMESH_NODES"), GroupSelections); - GroupSelectionsLayout->addWidget(RadioButtonNodes); - GroupSel->addButton(RadioButtonNodes, 0); - RadioButtonElements = new QRadioButton(tr("SMESH_ELEMENTS"), GroupSelections); - GroupSelectionsLayout->addWidget(RadioButtonElements); - GroupSel->addButton(RadioButtonElements, 1); - - /***************************************************************/ - GroupArguments = new QGroupBox(tr("SMESH_INFORMATION"), this); - QGridLayout* GroupArgumentsLayout = new QGridLayout(GroupArguments); - GroupArgumentsLayout->setSpacing(SPACING); - GroupArgumentsLayout->setMargin(MARGIN); - - // Controls for elements selection - TextLabelElements = new QLabel(tr("SMESH_ID_ELEMENTS"), GroupArguments); - GroupArgumentsLayout->addWidget(TextLabelElements, 0, 0); - - LineEditElements = new QLineEdit(GroupArguments); - LineEditElements->setValidator(new SMESHGUI_IdValidator(this)); - GroupArgumentsLayout->addWidget(LineEditElements, 0, 1); - - // information text browser - Info = new QTextBrowser(GroupArguments); - Info->setMinimumSize(200, 150); - GroupArgumentsLayout->addWidget(Info, 1, 0, 1, 2); - - /***************************************************************/ - GroupButtons = new QGroupBox(this); - QHBoxLayout* GroupButtonsLayout = new QHBoxLayout(GroupButtons); - GroupButtonsLayout->setSpacing(SPACING); - GroupButtonsLayout->setMargin(MARGIN); - - buttonOk = new QPushButton(tr("SMESH_BUT_OK"), GroupButtons); - buttonOk->setAutoDefault(true); - buttonOk->setDefault(true); - buttonHelp = new QPushButton(tr("SMESH_BUT_HELP"), GroupButtons); - buttonHelp->setAutoDefault(true); - - GroupButtonsLayout->addWidget(buttonOk); - GroupButtonsLayout->addSpacing(10); - GroupButtonsLayout->addStretch(); - GroupButtonsLayout->addWidget(buttonHelp); - - SMESHGUI_WhatIsDlgLayout->addWidget(GroupMesh); - SMESHGUI_WhatIsDlgLayout->addWidget(GroupSelections); - SMESHGUI_WhatIsDlgLayout->addWidget(GroupArguments); - SMESHGUI_WhatIsDlgLayout->addWidget(GroupButtons); - - RadioButtonNodes->setChecked(true); - - mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); - - mySMESHGUI->SetActiveDialogBox((QDialog*)this); - - // Costruction of the logical filter - SMESH_TypeFilter* aMeshOrSubMeshFilter = new SMESH_TypeFilter (MESHorSUBMESH); - SMESH_TypeFilter* aSmeshGroupFilter = new SMESH_TypeFilter (GROUP); - - QList aListOfFilters; - if (aMeshOrSubMeshFilter) aListOfFilters.append(aMeshOrSubMeshFilter); - if (aSmeshGroupFilter) aListOfFilters.append(aSmeshGroupFilter); - - myMeshOrSubMeshOrGroupFilter = - new SMESH_LogicalFilter(aListOfFilters, SMESH_LogicalFilter::LO_OR); - - myHelpFileName = "mesh_infos_page.html#mesh_element_info_anchor"; - - Init(); - - /* signals and slots connections */ - connect(buttonOk, SIGNAL(clicked()), this, SLOT(ClickOnOk())); - connect(buttonHelp, SIGNAL(clicked()), this, SLOT(ClickOnHelp())); - connect(GroupSel, SIGNAL(buttonClicked(int)), SLOT(SelectionsClicked(int))); - - connect(mySMESHGUI, SIGNAL(SignalDeactivateActiveDialog()), this, SLOT(DeactivateActiveDialog())); - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); - /* to close dialog if study change */ - connect(mySMESHGUI, SIGNAL(SignalCloseAllDialogs()), this, SLOT(ClickOnCancel())); - connect(LineEditElements, SIGNAL(textChanged(const QString&)), SLOT(onTextChange(const QString&))); - - SelectionsClicked(0); - SelectionIntoArgument(); -} - -//================================================================================= -// function : ~SMESHGUI_WhatIsDlg() -// purpose : Destroys the object and frees any allocated resources -//================================================================================= -SMESHGUI_WhatIsDlg::~SMESHGUI_WhatIsDlg() -{ -} - -//================================================================================= -// function : Init() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::Init (bool ResetControls) -{ - myBusy = false; - - LineEditElements->clear(); - - myActor = 0; - myMesh = SMESH::SMESH_Mesh::_nil(); - - if (ResetControls) { - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( CellSelection ); - onTextChange(LineEditElements->text()); - - SelectionIntoArgument(); - } -} - -//================================================================================= -// function : SelectionsClicked() -// purpose : Radio button management -//================================================================================= -void SMESHGUI_WhatIsDlg::SelectionsClicked (int selectionId) -{ - disconnect(mySelectionMgr, 0, this, 0); - - mySelectionMgr->clearFilters(); - - switch (selectionId) { - case 0: - { - SMESH::SetPointRepresentation(true); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( NodeSelection ); - break; - } - case 1: - { - SMESH::SetPointRepresentation(false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( CellSelection ); - break; - } - } - - connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); - SelectionIntoArgument(); -} - -//================================================================================= -// function : ClickOnOk() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::ClickOnOk() -{ - if (mySMESHGUI->isActiveStudyLocked()) - return; - - SMESH::UpdateView(); - Init(false); - SelectionIntoArgument(); - ClickOnCancel(); -} - -//================================================================================= -// function : ClickOnCancel() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::ClickOnCancel() -{ - disconnect(mySelectionMgr, 0, this, 0); - mySelectionMgr->clearFilters(); - SMESH::SetPointRepresentation(false); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->SetSelectionMode( ActorSelection ); - mySMESHGUI->ResetState(); - reject(); -} - -//================================================================================= -// function : ClickOnHelp() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::ClickOnHelp() -{ - LightApp_Application* app = (LightApp_Application*)(SUIT_Session::session()->activeApplication()); - if (app) - app->onHelpContextModule(mySMESHGUI ? app->moduleName(mySMESHGUI->moduleName()) : QString(""), myHelpFileName); - else { - SUIT_MessageBox::warning(this, tr("WRN_WARNING"), - tr("EXTERNAL_BROWSER_CANNOT_SHOW_PAGE"). - arg(app->resourceMgr()->stringValue("ExternalBrowser", - "application")). - arg(myHelpFileName)); - } -} - -//======================================================================= -// function : onTextChange() -// purpose : -//======================================================================= -void SMESHGUI_WhatIsDlg::onTextChange (const QString& theNewText) -{ - if (myBusy) return; - myBusy = true; - - // hilight entered elements - SMDS_Mesh* aMesh = 0; - if (myActor) - aMesh = myActor->GetObject()->GetMesh(); - - if (aMesh) { - Handle(SALOME_InteractiveObject) anIO = myActor->getIO(); - - TColStd_MapOfInteger newIndices; - - QStringList aListId = theNewText.split(" ", QString::SkipEmptyParts); - - for (int i = 0; i < aListId.count(); i++) { - const SMDS_MeshElement * e = RadioButtonNodes->isChecked()? - aMesh->FindNode(aListId[ i ].toInt()): - aMesh->FindElement(aListId[ i ].toInt()); - if (e) - newIndices.Add(e->GetID()); - } - - mySelector->AddOrRemoveIndex( anIO, newIndices, false ); - if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) - aViewWindow->highlight( anIO, true, true ); - } - - SelectionIntoArgument(); - - myBusy = false; -} - -//================================================================================= -// function : SelectionIntoArgument() -// purpose : Called when selection as changed or other case -//================================================================================= -void SMESHGUI_WhatIsDlg::SelectionIntoArgument() -{ - int curBusy = myBusy; - - // clear - myActor = 0; - QString aString = ""; - - myBusy = true; - if(!curBusy) - LineEditElements->setText(aString); - MeshName->setText(aString); - GroupMesh->setTitle(tr("")); - Info->clear(); - myBusy = curBusy; - - if (!GroupButtons->isEnabled()) // inactive - return; - - // get selected mesh - SALOME_ListIO aList; - mySelectionMgr->selectedObjects(aList,SVTK_Viewer::Type()); - - int nbSel = aList.Extent(); - - if (nbSel != 1) - return; - - Handle(SALOME_InteractiveObject) IO = aList.First(); - myMesh = SMESH::GetMeshByIO(IO); - if (myMesh->_is_nil()) - return; - - myActor = SMESH::FindActorByObject(myMesh); - if (!myActor) - myActor = SMESH::FindActorByEntry(IO->getEntry()); - if (!myActor) - return; - - QString aName; - SMESH::GetNameOfSelectedIObjects(mySelectionMgr, aName); - MeshName->setText(aName); - if(!SMESH::IObjectToInterface(IO)->_is_nil()) { - GroupMesh->setTitle(tr("SMESH_MESH")); - } else if(!SMESH::IObjectToInterface(IO)->_is_nil()) { - GroupMesh->setTitle(tr("SMESH_SUBMESH")); - } else if(!SMESH::IObjectToInterface(IO)->_is_nil()) { - GroupMesh->setTitle(tr("SMESH_GROUP")); - } - - int aNbUnits = 0; - - aNbUnits = SMESH::GetNameOfSelectedElements(mySelector, IO, aString); - - if (aNbUnits < 1) - return; - - const SMDS_MeshElement * e = RadioButtonNodes->isChecked()? - myActor->GetObject()->GetMesh()->FindNode(aString.toInt()): - myActor->GetObject()->GetMesh()->FindElement(aString.toInt()); - if (e) { - QString anInfo; - anInfo=tr("ENTITY_TYPE") + ": "; - if(e->GetType() == SMDSAbs_Node) { - anInfo+=tr("MESH_NODE")+"\n"; - //const SMDS_MeshNode *en = (SMDS_MeshNode*) e; // VSR: not used! - } else if(e->GetType() == SMDSAbs_Edge) { - anInfo+=tr("SMESH_EDGE")+"\n"; - anInfo+=tr("SMESH_MESHINFO_TYPE")+": "; - const SMDS_MeshEdge *ee = (SMDS_MeshEdge*) e; - anInfo+=(ee->IsQuadratic()?tr("SMESH_MESHINFO_ORDER2"):tr("SMESH_MESHINFO_ORDER1"))+"\n"; - } else if(e->GetType() == SMDSAbs_Face) { - const SMDS_MeshFace *ef = (SMDS_MeshFace*) e; - anInfo+=tr("SMESH_FACE")+"\n"; - anInfo+=tr("SMESH_MESHINFO_TYPE")+": "; - if(!ef->IsPoly()) - anInfo+=(ef->IsQuadratic()?tr("SMESH_MESHINFO_ORDER2"):tr("SMESH_MESHINFO_ORDER1"))+" "; - switch(ef->NbNodes()) { - case 3: - case 6: - { - anInfo+=tr("SMESH_TRIANGLE"); - break; - } - case 4: - case 8: - { - anInfo+=tr("SMESH_QUADRANGLE"); - break; - } - default: - break; - } - anInfo+="\n"; - } else if(e->GetType() == SMDSAbs_Volume) { - anInfo+=tr("SMESH_VOLUME")+"\n"; - anInfo+=tr("SMESH_MESHINFO_TYPE")+": "; - const SMDS_MeshVolume *ev = (SMDS_MeshVolume*) e; - SMDS_VolumeTool vt(ev); - if(vt.GetVolumeType() != SMDS_VolumeTool::POLYHEDA) - anInfo+=(ev->IsQuadratic()?tr("SMESH_MESHINFO_ORDER2"):tr("SMESH_MESHINFO_ORDER1"))+" "; - switch(vt.GetVolumeType()) { - case SMDS_VolumeTool::TETRA: - case SMDS_VolumeTool::QUAD_TETRA: - { - anInfo+=tr("SMESH_TETRAS"); - break; - } - case SMDS_VolumeTool::PYRAM: - case SMDS_VolumeTool::QUAD_PYRAM: - { - anInfo+=tr("SMESH_PYRAMID"); - break; - } - case SMDS_VolumeTool::PENTA: - case SMDS_VolumeTool::QUAD_PENTA: - { - anInfo+=tr("SMESH_PRISM"); - break; - } - case SMDS_VolumeTool::HEXA: - case SMDS_VolumeTool::QUAD_HEXA: - { - anInfo+=tr("SMESH_HEXAS"); - break; - } - case SMDS_VolumeTool::POLYHEDA: - { - anInfo+=tr("SMESH_POLYEDRON"); - break; - } - default: - break; - } - anInfo+="\n"; - } - if(e->GetType() != SMDSAbs_Node) - anInfo+=tr("GRAVITY_CENTER") + ":\n"; - gp_XYZ anXYZ(0.,0.,0.); - SMDS_ElemIteratorPtr nodeIt = e->nodesIterator(); - int nbNodes = 0; - for( ; nodeIt->more(); nbNodes++) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - anXYZ.Add( gp_XYZ( node->X(), node->Y(), node->Z() ) ); - } - anXYZ.Divide(e->NbNodes()); - anInfo+=QString("X=%1\nY=%2\nZ=%3\n").arg(anXYZ.X()).arg(anXYZ.Y()).arg(anXYZ.Z()); - Info->setText(anInfo); - } - - if(!curBusy) { - myBusy = true; - LineEditElements->setText(aString); - myBusy = false; - } -} - -//================================================================================= -// function : DeactivateActiveDialog() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::DeactivateActiveDialog() -{ - if (GroupArguments->isEnabled()) { - GroupSelections->setEnabled(false); - GroupMesh->setEnabled(false); - GroupArguments->setEnabled(false); - GroupButtons->setEnabled(false); - mySMESHGUI->ResetState(); - mySMESHGUI->SetActiveDialogBox(0); - } -} - -//================================================================================= -// function : ActivateThisDialog() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::ActivateThisDialog() -{ - /* Emit a signal to deactivate the active dialog */ - mySMESHGUI->EmitSignalDeactivateDialog(); - GroupArguments->setEnabled(true); - GroupButtons->setEnabled(true); - GroupSelections->setEnabled(true); - GroupMesh->setEnabled(true); - - mySMESHGUI->SetActiveDialogBox((QDialog*)this); - - if ( SMESH::GetViewWindow( mySMESHGUI )) - SelectionsClicked(RadioButtonNodes->isChecked()?0:1); - - SelectionIntoArgument(); -} - -//================================================================================= -// function : enterEvent() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::enterEvent (QEvent*) -{ - if (!GroupArguments->isEnabled()) - ActivateThisDialog(); -} - -//================================================================================= -// function : closeEvent() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::closeEvent (QCloseEvent*) -{ - /* same than click on cancel button */ - ClickOnCancel(); -} - -//======================================================================= -//function : hideEvent -//purpose : caused by ESC key -//======================================================================= -void SMESHGUI_WhatIsDlg::hideEvent (QHideEvent*) -{ - if (!isMinimized()) - ClickOnCancel(); -} - -//================================================================================= -// function : keyPressEvent() -// purpose : -//================================================================================= -void SMESHGUI_WhatIsDlg::keyPressEvent( QKeyEvent* e ) -{ - QDialog::keyPressEvent( e ); - if ( e->isAccepted() ) - return; - - if ( e->key() == Qt::Key_F1 ) { - e->accept(); - ClickOnHelp(); - } -} diff --git a/src/SMESHGUI/SMESHGUI_WhatIsDlg.h b/src/SMESHGUI/SMESHGUI_WhatIsDlg.h deleted file mode 100755 index e308c53c1..000000000 --- a/src/SMESHGUI/SMESHGUI_WhatIsDlg.h +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH SMESHGUI : GUI for SMESH component -// File : SMESHGUI_WhatIsDlg.h -// Author : Vladimir TURIN, Open CASCADE S.A.S. -// -#ifndef SMESHGUI_WHATISDLG_H -#define SMESHGUI_WHATISDLG_H - -// Qt includes -#include - -// IDL includes -#include -#include CORBA_SERVER_HEADER(SMESH_Mesh) - -class QGroupBox; -class QLabel; -class QLineEdit; -class QPushButton; -class QRadioButton; -class QTextBrowser; -class SMESHGUI; -class SMESH_Actor; -class SVTK_Selector; -class LightApp_SelectionMgr; -class SMESH_LogicalFilter; - -//================================================================================= -// class : SMESHGUI_WhatIsDlg -// purpose : -//================================================================================= -class SMESHGUI_WhatIsDlg : public QDialog -{ - Q_OBJECT - -public: - SMESHGUI_WhatIsDlg( SMESHGUI* ); - ~SMESHGUI_WhatIsDlg(); - -private: - void Init( bool = true ); - void closeEvent( QCloseEvent* ); - void enterEvent( QEvent* ); /* mouse enter the QWidget */ - void hideEvent( QHideEvent* ); /* ESC key */ - void keyPressEvent( QKeyEvent* ); - - SMESHGUI* mySMESHGUI; /* Current SMESHGUI object */ - LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ - - SVTK_Selector* mySelector; - - bool myBusy; - SMESH::SMESH_Mesh_var myMesh; - SMESH_Actor* myActor; - SMESH_LogicalFilter* myMeshOrSubMeshOrGroupFilter; - - QGroupBox* GroupSelections; - QRadioButton* RadioButtonNodes; - QRadioButton* RadioButtonElements; - QGroupBox* GroupButtons; - QPushButton* buttonOk; - QPushButton* buttonHelp; - QGroupBox* GroupArguments; - QGroupBox* GroupMesh; - QLabel* TextLabelElements; - QLineEdit* LineEditElements; - QLabel* MeshLabel; - QLineEdit* MeshName; - - QTextBrowser* Info; - - QString myHelpFileName; - -private slots: - void SelectionsClicked( int ); - void ClickOnOk(); - void ClickOnCancel(); - void ClickOnHelp(); - void SelectionIntoArgument(); - void DeactivateActiveDialog(); - void ActivateThisDialog(); - void onTextChange( const QString& ); -}; - -#endif // SMESHGUI_WHATISDLG_H diff --git a/src/SMESHGUI/SMESHGUI_XmlHandler.cxx b/src/SMESHGUI/SMESHGUI_XmlHandler.cxx index f10cb0341..d02d9cd02 100644 --- a/src/SMESHGUI/SMESHGUI_XmlHandler.cxx +++ b/src/SMESHGUI/SMESHGUI_XmlHandler.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : reading of xml file with list of available hypotheses and algorithms // File : SMESHGUI_XmlHandler.cxx // Author : Julia DOROVSKIKH, Open CASCADE S.A.S. @@ -103,7 +104,7 @@ bool SMESHGUI_XmlHandler::startElement (const QString&, const QString&, { MESSAGE("Loading Resources " << aResName.toLatin1().data()); SUIT_ResourceMgr* resMgr = SMESHGUI::resourceMgr(); - QString lang = resMgr->stringValue( resMgr->langSection(), "language", "en" ); + QString lang = resMgr->stringValue( resMgr->langSection(), "language", "en" ); resMgr->loadTranslator( "resources", QString( "%1_msg_%2.qm" ).arg( aResName, lang ) ); resMgr->loadTranslator( "resources", QString( "%1_images.qm" ).arg( aResName, lang ) ); } @@ -164,11 +165,11 @@ bool SMESHGUI_XmlHandler::startElement (const QString&, const QString&, if (qName == "algorithm") { - myAlgorithmsMap[aHypAlType] = aHypData; + myAlgorithmsMap.insert(aHypAlType,aHypData); } else { - myHypothesesMap[aHypAlType] = aHypData; + myHypothesesMap.insert(aHypAlType,aHypData); } } } @@ -186,11 +187,17 @@ bool SMESHGUI_XmlHandler::startElement (const QString&, const QString&, { QString aHypos = isHypo ? atts.value("hypos") : atts.value("algos"); aHypos = aHypos.remove( ' ' ); - QStringList* aHypoList = isHypo ? & aHypoSet->HypoList : & aHypoSet->AlgoList; - *aHypoList = aHypos.split( ',', QString::SkipEmptyParts ); + aHypoSet->set( !isHypo, aHypos.split( ',', QString::SkipEmptyParts ) ); } } } + else if ( qName == "python-wrap" || + qName == "algo" || + qName == "hypo" ) + { + // elements used in SMESH_2smeshpy + return true; + } else { // error diff --git a/src/SMESHGUI/SMESHGUI_XmlHandler.h b/src/SMESHGUI/SMESHGUI_XmlHandler.h index 09db44219..813938ddf 100644 --- a/src/SMESHGUI/SMESHGUI_XmlHandler.h +++ b/src/SMESHGUI/SMESHGUI_XmlHandler.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : reading of xml file with list of available hypotheses and algorithms // File : SMESHGUI_XmlHandler.h // Author : Julia DOROVSKIKH, Open CASCADE S.A.S. @@ -34,6 +35,9 @@ #include #include +//GUI includes +#include + class HypothesisData; class HypothesesSet; @@ -45,7 +49,7 @@ public: bool startDocument(); bool startElement( const QString&, const QString&, - const QString&, const QXmlAttributes& ); + const QString&, const QXmlAttributes& ); bool endElement( const QString&, const QString&, const QString& ); bool characters( const QString& ); @@ -54,8 +58,8 @@ public: bool fatalError( const QXmlParseException& ); public: - QMap myHypothesesMap; - QMap myAlgorithmsMap; + IMap myHypothesesMap; + IMap myAlgorithmsMap; QList myListOfHypothesesSets; diff --git a/src/SMESHGUI/SMESH_SMESHGUI.hxx b/src/SMESHGUI/SMESH_SMESHGUI.hxx index 46a6cb93a..62d860960 100755 --- a/src/SMESHGUI/SMESH_SMESHGUI.hxx +++ b/src/SMESHGUI/SMESH_SMESHGUI.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_SMESHGUI.hxx // Author : Alexander BORODIN, Open CASCADE S.A.S. // @@ -26,7 +27,7 @@ #define SMESH_SMESHGUI_HXX #ifdef WNT - #if defined SMESHGUI_EXPORTS + #if defined SMESHGUI_EXPORTS || defined SMESH_EXPORTS #define SMESHGUI_EXPORT __declspec( dllexport ) #else #define SMESHGUI_EXPORT __declspec( dllimport ) diff --git a/src/SMESHGUI/SMESH_images.ts b/src/SMESHGUI/SMESH_images.ts index 0319ce689..6012d758c 100644 --- a/src/SMESHGUI/SMESH_images.ts +++ b/src/SMESHGUI/SMESH_images.ts @@ -1,28 +1,6 @@ + - - + @default @@ -53,6 +31,10 @@ ICON_BUILD_COMPOUND mesh_build_compound.png + + ICON_COPY_MESH + copy_mesh.png + ICON_COMPUTE mesh_compute.png @@ -109,6 +91,22 @@ ICON_DLG_BUILD_COMPOUND_MESH mesh_build_compound.png + + ICON_DLG_COPY_MESH + copy_mesh.png + + + ICON_DLG_BALL + mesh_ball.png + + + ICON_DLG_BALL_ELEMENT + mesh_ball.png + + + ICON_DLG_ELEM0D + mesh_vertex.png + ICON_DLG_EDGE mesh_line.png @@ -153,6 +151,14 @@ ICON_DLG_NODE mesh_vertex.png + + ICON_DLG_OCTA + mesh_octahedron.png + + + ICON_DLG_PENTA + mesh_pentahedron.png + ICON_DLG_POLYGON mesh_polygon.png @@ -161,6 +167,10 @@ ICON_DLG_POLYHEDRON mesh_polyhedron.png + + ICON_DLG_PYRAMID + mesh_pyramid.png + ICON_DLG_QUADRANGLE mesh_quad.png @@ -173,6 +183,10 @@ ICON_DLG_QUADRATIC_HEXAHEDRON mesh_quad_hexahedron.png + + ICON_DLG_TRIQUADRATIC_HEXAHEDRON + mesh_triquad_hexahedron.png + ICON_DLG_QUADRATIC_PENTAHEDRON mesh_quad_pentahedron.png @@ -185,6 +199,10 @@ ICON_DLG_QUADRATIC_QUADRANGLE mesh_quad_quadrangle.png + + ICON_DLG_BIQUADRATIC_QUADRANGLE + mesh_biquad_quadrangle.png + ICON_DLG_QUADRATIC_TETRAHEDRON mesh_quad_tetrahedron.png @@ -201,6 +219,10 @@ ICON_DLG_REM_NODE mesh_rem_node.png + + ICON_DLG_REM_ORPHAN_NODES + mesh_rem_orphan_nodes.png + ICON_DLG_RENUMBERING_ELEMENTS mesh_renumbering_elements.png @@ -253,6 +275,10 @@ ICON_FREE_FACES mesh_free_faces.png + + ICON_FIND_ELEM + mesh_find_elem_by_point.png + ICON_HYPO mesh_hypo_length.png @@ -277,6 +303,14 @@ ICON_MAP mesh_pattern.png + + ICON_MAX_ELEMENT_LENGTH_2D + mesh_max_element_length_2d.png + + + ICON_MAX_ELEMENT_LENGTH_3D + mesh_max_element_length_3d.png + ICON_OBJBROWSER_SMESH mesh.png @@ -365,6 +399,34 @@ ICON_SMESH_TRANSLATION_VECTOR mesh_translation_vector.png + + ICON_DLG_MESH_SCALE + scale.png + + + ICON_DLG_SCALE_ALONG_AXES + scale_along_axes.png + + + ICON_DLG_REORIENT2D_POINT + reorient_faces_point.png + + + ICON_DLG_REORIENT2D_FACE + reorient_faces_face.png + + + ICON_REORIENT_2D + reorient_faces_face.png + + + ICON_SMESH_DUPLICATE_NODES + mesh_duplicate_nodes.png + + + ICON_SMESH_DUPLICATE_NODES_WITH_ELEM + mesh_duplicate_nodes_with_elem.png + ICON_SMESH_TREE_ALGO mesh_tree_algo.png @@ -373,6 +435,10 @@ ICON_SMESH_TREE_GROUP mesh_tree_group.png + + ICON_SMESH_TREE_GROUP_ON_FILTER + mesh_tree_group_on_filter.png + ICON_SMESH_TREE_HYPO mesh_tree_hypo.png @@ -421,13 +487,45 @@ ICON_VOLUME_3D mesh_volume_3d.png + + ICON_BARE_BORDER_VOLUME + bare_border_volume.png + + + ICON_BARE_BORDER_FACE + bare_border_face.png + + + ICON_OVER_CONSTRAINED_VOLUME + over_constrained_volume.png + + + ICON_OVER_CONSTRAINED_FACE + over_constrained_face.png + + + ICON_EQUAL_NODE + mesh_equal_node.png + + + ICON_EQUAL_EDGE + mesh_equal_edge.png + + + ICON_EQUAL_FACE + mesh_equal_face.png + + + ICON_EQUAL_VOLUME + mesh_equal_volume.png + ICON_WARP mesh_wrap.png ICON_WHAT_IS - mesh_whatis.png + mesh_elem_info.png ICON_WIRE @@ -441,5 +539,21 @@ ICON_UNDERLYING_ELEMS mesh_extractGroup.png + + ICON_2D_FROM_3D + mesh_2d_from_3d.png + + + ICON_SPLIT_TO_TETRA + split_into_tetra.png + + + ICON_MEASURE_MIN_DIST + mesh_min_dist.png + + + ICON_MEASURE_BND_BOX + mesh_bounding_box.png + diff --git a/src/SMESHGUI/SMESH_msg_en.ts b/src/SMESHGUI/SMESH_msg_en.ts index a93b7d1f6..2ac37e0f5 100644 --- a/src/SMESHGUI/SMESH_msg_en.ts +++ b/src/SMESHGUI/SMESH_msg_en.ts @@ -1,4045 +1,6592 @@ + - - - - @default - - AREA_ELEMENTS - Area - - - ASPECTRATIO_3D_ELEMENTS - Aspect Ratio 3D - - - ASPECTRATIO_ELEMENTS - Aspect Ratio - - - COL_ALGO_HEADER - Algorithm - - - COL_ERROR_HEADER - Error - - - COL_SHAPE_HEADER - SubShape - - - COMPERR_ALGO_FAILED - Algorithm failed - - - COMPERR_BAD_INPUT_MESH - Invalid input mesh - - - COMPERR_BAD_SHAPE - Unexpected geometry - - - COMPERR_EXCEPTION - Unknown exception - - - COMPERR_MEMORY_PB - Memory allocation problem - - - COMPERR_OCC_EXCEPTION - OCC exception - - - COMPERR_OK - No errors - - - COMPERR_SLM_EXCEPTION - SALOME exception - - - COMPERR_STD_EXCEPTION - std::exception - - - SMESH_GEOM - Geometry - - - DIRECT_GEOM_SELECTION - Direct geometry selection - - - ELEMENT_ID - Element ID - - - FREE_BORDERS - Free Borders - - - GEOMETRY_NAME - Geometry name - - - GEOM_BY_MESH_ELEM_SELECTION - Find geometry by mesh element selection - - - GLOBAL_ALGO - Global - - - INF_SELECT_OBJECT - Select an object - - - LENGTH2D_EDGES - Length 2D - - - LENGTH_EDGES - Length - - - LOCAL_ALGO - Local - - - MEN_ADD - Add - - - MEN_ADV_INFO - Advanced Mesh Infos - - - MEN_ALL - All - - - MEN_AREA - Area - - - MEN_ASPECT - Aspect Ratio - - - MEN_ASPECT_3D - Aspect Ratio 3D - - - MEN_AUTO_COLOR - Auto Color - - - MEN_AUTO_UPD - Automatic Update - - - MEN_BUILD_COMPOUND - Build Compound - - - MEN_CLIP - Clipping - - - MEN_COLORS - Colors / Size - - - MEN_COMPUTE - Compute - - - MEN_PRECOMPUTE - Preview - - - MEN_CONNECTION - Borders at Multi-Connection - - - MEN_CONNECTION_2D - Borders at Multi-Connection 2D - - - MEN_CONSTRUCT_GROUP - Construct Group - - - MEN_CONV_TO_QUAD - Convert to/from quadratic - - - MEN_CREATE_GROUP - Create Group - - - MEN_CREATE_GEO_GROUP - Create Groups from Geometry - - - MEN_CREATE_MESH - Create Mesh - - - MEN_CREATE_SUBMESH - Create Sub-mesh - - - MEN_CTRL - Controls - - - MEN_CUT - Cutting of Quadrangles - - - MEN_CUT_GROUP - Cut Groups - - - MEN_DAT - DAT File - - - MEN_DELETE - Delete - - - MEN_DEL_GROUP - Delete Groups - - - MEN_FACE_ORIENTATION - Orientation of Faces - - - MEN_DISABLE_AUTO_COLOR - Disable Auto Color - - - MEN_DISPLAY_ONLY - Show Only - - - MEN_DISPMODE - Display Mode - - - MEN_DISP_ENT - Display Entity - - - MEN_EDGE - Edge - - - MEN_EDGES - Edges - - - MEN_EDIT - Edit - - - MEN_EDIT_GROUP - Edit Group - - - MEN_EDIT_GEOMGROUP_AS_GROUP - Edit Group as Standalone - - - MEN_EDIT_HYPO - Edit Hypothesis - - - MEN_EDIT_MESHSUBMESH - Edit Mesh/Sub-mesh - - - MEN_EXPORT - Export - - - MEN_EXPORT_DAT - Export to DAT File - - - MEN_EXPORT_MED - Export to MED File - - - MEN_EXPORT_STL - Export to STL File - - - MEN_EXPORT_UNV - Export to UNV File - - - MEN_EXTRUSION - Extrusion - - - MEN_EXTRUSION_ALONG - Extrusion Along a Path - - - MEN_FACES - Faces - - - MEN_FILE - File - - - MEN_FREE_BORDER - Free Borders - - - MEN_FREE_EDGE - Free Edges - - - MEN_FREE_NODE - Free Nodes - - - MEN_FREE_FACES - Free Faces - - - MEN_GLOBAL_HYPO - Global Hypothesis - - - MEN_HEXA - Hexahedron - - - MEN_HIDE - Hide - - - MEN_HYPO - Hypotheses - - - MEN_IMPORT - Import - - - MEN_INT_GROUP - Intersect Groups - - - MEN_INV - Diagonal Inversion - - - MEN_LENGTH - Length - - - MEN_LENGTH_2D - Length 2D - - - MEN_MAP - Pattern Mapping - - - MEN_MED - MED file - - - MEN_MERGE - Merge Nodes - - - MEN_MERGE_ELEMENTS - Merge Elements - - - MEN_MESH - Mesh - - - MEN_MESH_THROU_POINT - Mesh to Pass Through a Point - - - MEN_MIN_ANG - Minimum Angle - - - MEN_MODIFY - Modification - - - MEN_MOVE - Move Node - - - MEN_NODE - Node - - - MEN_NODES - Nodes - - - MEN_NUM - Numbering - - - MEN_NUM_ELEMENTS - Display Elements # - - - MEN_NUM_NODES - Display Nodes # - - - MEN_ORIENT - Orientation - - - MEN_POLYGON - Polygon - - - MEN_POLYHEDRON - Polyhedron - - - MEN_PRECISION - Precision - - - MEN_PREF - Preferences - - - MEN_QUAD - Quadrangle - - - MEN_QUADRATIC_EDGE - Quadratic Edge - - - MEN_QUADRATIC_HEXAHEDRON - Quadratic Hexahedron - - - MEN_QUADRATIC_PENTAHEDRON - Quadratic Pentahedron - - - MEN_QUADRATIC_PYRAMID - Quadratic Pyramid - - - MEN_QUADRATIC_QUADRANGLE - Quadratic Quadrangle - - - MEN_QUADRATIC_TETRAHEDRON - Quadratic Tetrahedron - - - MEN_QUADRATIC_TRIANGLE - Quadratic Triangle - - - MEN_QUALITY - Quality Controls - - - MEN_REMOVE - Remove - - - MEN_REMOVE_ELEMENTS - Elements - - - MEN_REMOVE_NODES - Nodes - - - MEN_RENAME - Rename - - - MEN_RENUM - Renumbering - - - MEN_RENUM_ELEMENTS - Elements - - - MEN_RENUM_NODES - Nodes - - - MEN_RESET - Reset - - - MEN_REVOLUTION - Revolution - - - MEN_ROT - Rotation - - - MEN_SCALAR_BAR - Scalar Bar - - - MEN_SCALAR_BAR_PROP - Scalar Bar Properties - - - MEN_SELECTION - Selection - - - MEN_SEL_FILTER_LIB - Selection Filters Library - - - MEN_SEW - Sewing - - - MEN_SHADE - Shading - - - MEN_SHOW - Show - - - MEN_SHRINK - Shrink - - - MEN_SKEW - Skew - - - MEN_SMOOTH - Smoothing - - - MEN_STD_INFO - Standard Mesh Infos - - - MEN_STL - STL File - - - MEN_SYM - Symmetry - - - MEN_TAPER - Taper - - - MEN_TETRA - Tetrahedron - - - MEN_TOOLS - Tools - - - MEN_TRANS - Translation - - - MEN_TRANSF - Transformation - - - MEN_TRANSP - Transparency - - - MEN_TRIANGLE - Triangle - - - MEN_UNASSIGN - Unassign - - - MEN_UNION - Union of Triangles - - - MEN_UNION2 - Union of Two Triangles - - - MEN_UNV - UNV File - - - MEN_UN_GROUP - Union Groups - - - MEN_UNDERLYING_ELEMS - Group of underlying entities - - - MEN_UPDATE - Update - - - MEN_VIEW - View - - - MEN_VOLUMES - Volumes - - - MEN_VOLUME_3D - Volume - - - MEN_WARP - Warping Angle - - - MEN_WHAT_IS - Mesh Element Info - - - MEN_WIRE - Wireframe - - - MESHERS_FILE_CANT_OPEN - Can not open resource file - - - MESHERS_FILE_CHECK_VARIABLE - Check environment variable SMESH_MeshersList - - - MESHERS_FILE_NO_VARIABLE - Environment variable SMESH_MeshersList is not defined - - - MESH_IS_NOT_SELECTED - There is no selected mesh + + + @default + + SMESH_EXPORT_MESH + Export mesh + + + MED_FILES_FILTER + MED files + + + IDEAS_FILES_FILTER + IDEAS files + + + DAT_FILES_FILTER + DAT files + + + TEXT_FILES_FILTER + TXT files + + + MED_VX_FILES_FILTER + MED %1 files + + + STL_ASCII_FILES_FILTER + STL ASCII files + + + CGNS_FILES_FILTER + CGNS files + + + STL_BIN_FILES_FILTER + STL binary files + + + ALL_FILES_FILTER + All files + + + SMESH_AND + and + + + AREA_ELEMENTS + Area + + + ASPECTRATIO_3D_ELEMENTS + Aspect Ratio 3D + + + ASPECTRATIO_ELEMENTS + Aspect Ratio + + + NODE_POSITION + Position + + + U_POSITION + U + + + V_POSITION + V + + + COL_ALGO_HEADER + Algorithm + + + COL_ERROR_HEADER + Error + + + COL_SHAPE_HEADER + Sub-shape + + + COMPERR_ALGO_FAILED + Algorithm failed + + + COMPERR_BAD_INPUT_MESH + Invalid input mesh + + + COMPERR_BAD_SHAPE + Unexpected geometry + + + COMPERR_EXCEPTION + Unknown exception + + + COMPERR_MEMORY_PB + Memory allocation problem + + + COMPERR_OCC_EXCEPTION + OCC exception + + + COMPERR_OK + No errors + + + COMPERR_SLM_EXCEPTION + SALOME exception + + + COMPERR_STD_EXCEPTION + std::exception + + + COMPERR_UNKNOWN + Unknown error + + + COMPERR_CANCELED + Computation canceled + + + SMESH_GEOM + Geometry + + + DIRECT_GEOM_SELECTION + Direct geometry selection + + + ELEMENT_ID + Element ID + + + FREE_BORDERS + Free Borders + + + GEOMETRY_NAME + Geometry name + + + GEOM_BY_MESH_ELEM_SELECTION + Find geometry by mesh element selection + + + GLOBAL_ALGO + Global + + + INF_SELECT_OBJECT + Select an object + + + LENGTH2D_EDGES + Length 2D + + + LENGTH_EDGES + Length + + + LOCAL_ALGO + Local + + + MAX_ELEMENT_LENGTH_2D + Element Diameter 2D + + + MAX_ELEMENT_LENGTH_3D + Element Diameter 3D + + + MEN_ADD + Add + + + MEN_ADV_INFO + Mesh Information + + + MEN_ALL + All + + + MEN_AREA + Area + + + MEN_ASPECT + Aspect Ratio + + + MEN_ASPECT_3D + Aspect Ratio 3D + + + MEN_AUTO_COLOR + Auto Color + + + MEN_AUTO_UPD + Automatic Update + + + MEN_BUILD_COMPOUND + Build Compound + + + MEN_COPY_MESH + Copy Mesh + + + MEN_CLIP + Clipping + + + MEN_COLORS + Properties + + + MEN_COMPUTE + Compute + + + MEN_PRECOMPUTE + Preview + + + MEN_EVALUATE + Evaluate + + + MEN_CONNECTION + Borders at Multi-Connection + + + MEN_CONNECTION_2D + Borders at Multi-Connection 2D + + + MEN_CONSTRUCT_GROUP + Construct Group + + + MEN_CONV_TO_QUAD + Convert to/from quadratic + + + MEN_2D_FROM_3D + Create boundary elements + + + MEN_MESH_ORDER + Change submesh priority + + + MEN_CREATE_GROUP + Create Group + + + MEN_CREATE_GEO_GROUP + Create Groups from Geometry + + + MEN_CREATE_MESH + Create Mesh + + + MEN_CREATE_SUBMESH + Create Sub-mesh + + + MEN_CTRL + Controls + + + MEN_NODE_CTRL + Node Controls + + + MEN_EDGE_CTRL + Edge Controls + + + MEN_FACE_CTRL + Face Controls + + + MEN_VOLUME_CTRL + Volume Controls + + + MEN_CUT + Cutting of Quadrangles + + + MEN_CUT_GROUP + Cut Groups + + + MEN_IMPORT_DAT + DAT File + + + MEN_DAT + DAT File + + + MEN_DELETE + Delete + + + MEN_DEL_GROUP + Delete Groups with Contents + + + MEN_FACE_ORIENTATION + Orientation of Faces + + + MEN_DISABLE_AUTO_COLOR + Disable Auto Color + + + MEN_DISPLAY_ONLY + Show Only + + + MEN_DISPMODE + Display Mode + + + MEN_DISP_ENT + Display Entity + + + MEN_ELEM0D + 0D Element + + + MEN_ELEMS0D + 0D Elements + + + MEN_BALL + Ball + + + MEN_BALLS + Balls + + + MEN_EDGE + Edge + + + MEN_EDGES + Edges + + + MEN_EDIT + Edit + + + MEN_EDIT_GROUP + Edit Group + + + MEN_EDIT_GEOMGROUP_AS_GROUP + Edit Group as Standalone + + + MEN_EDIT_HYPO + Edit Hypothesis + + + MEN_EDIT_MESHSUBMESH + Edit Mesh/Sub-mesh + + + MEN_EXPORT + Export + + + MEN_EXPORT_DAT + Export to DAT File + + + MEN_EXPORT_MED + Export to MED File + + + MEN_EXPORT_CGNS + Export to CGNS File + + + MEN_EXPORT_SAUV + Export to SAUV file + + + MEN_EXPORT_STL + Export to STL File + + + MEN_EXPORT_UNV + Export to UNV File + + + MEN_EXTRUSION + Extrusion + + + MEN_EXTRUSION_ALONG + Extrusion Along a Path + + + MEN_FACES + Faces + + + MEN_FILE + File + + + MEN_FIND_ELEM + Find Element by Point + + + TOP_REORIENT_2D + Reorient faces by vector + + + MEN_REORIENT_2D + Reorient faces by vector + + + STB_REORIENT_2D + Reorient faces by vector + + + TOP_FIND_ELEM + Find Element by Point + + + STB_FIND_ELEM + Find Element by Point + + + EQUAL_NODE + Double nodes + + + MEN_EQUAL_NODE + Double nodes + + + STB_EQUAL_NODE + Double nodes + + + TOP_EQUAL_NODE + Double nodes + + + EQUAL_EDGE + Double edges + + + MEN_EQUAL_EDGE + Double edges + + + STB_EQUAL_EDGE + Double edges + + + TOP_EQUAL_EDGE + Double edges + + + EQUAL_FACE + Double faces + + + MEN_EQUAL_FACE + Double faces + + + STB_EQUAL_FACE + Double faces + + + TOP_EQUAL_FACE + Double faces + + + EQUAL_VOLUME + Double volumes + + + MEN_EQUAL_VOLUME + Double volumes + + + STB_EQUAL_VOLUME + Double volumes + + + TOP_EQUAL_VOLUME + Double volumes + + + MEN_BARE_BORDER_VOLUME + Volumes with bare border + + + MEN_BARE_BORDER_FACE + Faces with bare border + + + MEN_OVER_CONSTRAINED_VOLUME + Over-constrained volumes + + + MEN_OVER_CONSTRAINED_FACE + Over-constrained faces + + + MEN_FREE_BORDER + Free Borders + + + MEN_FREE_EDGE + Free Edges + + + MEN_FREE_NODE + Free Nodes + + + MEN_FREE_FACES + Free Faces + + + MEN_GLOBAL_HYPO + Global Hypothesis + + + MEN_HEXA + Hexahedron + + + MEN_HIDE + Hide + + + MEN_HYPO + Hypotheses + + + MEN_IMPORT + Import + + + MEN_INT_GROUP + Intersect Groups + + + MEN_INV + Diagonal Inversion + + + MEN_LENGTH + Length + + + MEN_LENGTH_2D + Length 2D + + + MEN_MAP + Pattern Mapping + + + MEN_MAX_ELEMENT_LENGTH_2D + Element Diameter 2D + + + MEN_MAX_ELEMENT_LENGTH_3D + Element Diameter 3D + + + MEN_IMPORT_MED + MED file + + + MEN_MED + MED file + + + MEN_IMPORT_CGNS + CGNS file + + + MEN_CGNS + CGNS file + + + MEN_IMPORT_SAUV + SAUV file + + + MEN_SAUV + SAUV file + + + MEN_MERGE + Merge Nodes + + + MEN_MERGE_ELEMENTS + Merge Elements + + + MEN_MESH + Mesh + + + MEN_MESH_THROU_POINT + Move Node + + + MEN_MIN_ANG + Minimum Angle + + + MEN_MODIFY + Modification + + + MEN_MEASURE + Measurements + + + MEN_MEASURE_MIN_DIST + Minimum Distance + + + STB_MEASURE_MIN_DIST + Calculate minimum distance between two objects + + + TOP_MEASURE_MIN_DIST + Minimum distance + + + MEN_MEASURE_BND_BOX + Bounding Box + + + STB_MEASURE_BND_BOX + Calculate bounding box for the selected object(s) + + + TOP_MEASURE_BND_BOX + Bounding box + + + MEN_MOVE + Move Node + + + MEN_NODE + Node + + + MEN_NODES + Nodes + + + MEN_NUM + Numbering + + + MEN_NUM_ELEMENTS + Display Elements # + + + MEN_NUM_NODES + Display Nodes # + + + MEN_ORIENT + Orientation + + + MEN_POLYGON + Polygon + + + MEN_POLYHEDRON + Polyhedron + + + MEN_PRECISION + Precision + + + MEN_PREF + Preferences + + + MEN_QUAD + Quadrangle + + + MEN_QUADRATIC_EDGE + Quadratic Edge + + + MEN_QUADRATIC_HEXAHEDRON + Quadratic Hexahedron + + + MEN_QUADRATIC_PENTAHEDRON + Quadratic Pentahedron + + + MEN_QUADRATIC_PYRAMID + Quadratic Pyramid + + + MEN_QUADRATIC_QUADRANGLE + Quadratic Quadrangle + + + MEN_QUADRATIC_TETRAHEDRON + Quadratic Tetrahedron + + + MEN_QUADRATIC_TRIANGLE + Quadratic Triangle + + + MEN_QUALITY + Quality Controls + + + MEN_REMOVE + Remove + + + MEN_REMOVE_ELEMENTS + Elements + + + MEN_REMOVE_NODES + Nodes + + + MEN_REMOVE_ORPHAN_NODES + Orphan Nodes + + + MEN_RENAME + Rename + + + MEN_RENUM + Renumbering + + + MEN_RENUM_ELEMENTS + Elements + + + MEN_RENUM_NODES + Nodes + + + MEN_RESET + Reset + + + MEN_DISTRIBUTION_CTRL + Distribution + + + MEN_SAVE_DISTRIBUTION + Export ... + + + MEN_SHOW_DISTRIBUTION + Show + + + MEN_PLOT_DISTRIBUTION + Plot + + + DISTRIBUTION_NB_ENT + Number of entities + + + MEN_REVOLUTION + Revolution + + + MEN_ROT + Rotation + + + MEN_SCALAR_BAR + Scalar Bar + + + MEN_SCALAR_BAR_PROP + Scalar Bar Properties + + + MEN_SELECTION + Selection + + + MEN_SEL_FILTER_LIB + Selection Filters Library + + + MEN_SEW + Sewing + + + MEN_SHADE + Shading + + + MEN_QUADRATIC_REPRESENT + 2D Quadratic + + + MEN_LINE_REPRESENTATION + Lines + + + MEN_ARC_REPRESENTATION + Arcs + + + MEN_SHOW + Show + + + MEN_SHRINK + Shrink + + + MEN_SKEW + Skew + + + MEN_SMOOTH + Smoothing + + + MEN_STD_INFO + Standard Mesh Infos + + + MEN_IMPORT_STL + STL File + + + MEN_STL + STL File + + + MEN_SYM + Symmetry + + + MEN_TAPER + Taper + + + MEN_TETRA + Tetrahedron + + + MEN_TOOLS + Tools + + + MEN_TRANS + Translation + + + MEN_SCALE + Scale Transform + + + MEN_DUPLICATE_NODES + Duplicate Nodes + + + MEN_TRANSF + Transformation + + + MEN_TRANSP + Transparency + + + MEN_TRIANGLE + Triangle + + + MEN_UNASSIGN + Unassign + + + MEN_UNION + Union of Triangles + + + MEN_UNION2 + Union of Two Triangles + + + MEN_IMPORT_UNV + UNV File + + + MEN_UNV + UNV File + + + MEN_UN_GROUP + Union Groups + + + MEN_UNDERLYING_ELEMS + Group of underlying entities + + + MEN_UPDATE + Update + + + MEN_VIEW + View + + + MEN_VOLUMES + Volumes + + + MEN_VOLUME_3D + Volume + + + MEN_WARP + Warping Angle + + + MEN_WHAT_IS + Mesh Element Information + + + MEN_WIRE + Wireframe + + + MEN_SPLIT_TO_TETRA + Split into Tetrahedra + + + TOP_SPLIT_TO_TETRA + Split into Tetrahedra + + + STB_SPLIT_TO_TETRA + Split into Tetrahedra + + + MESHERS_FILE_CANT_OPEN + Can not open resource file + + + MESHERS_FILE_CHECK_VARIABLE + Check environment variable SMESH_MeshersList + + + MESHERS_FILE_NO_VARIABLE + Environment variable SMESH_MeshersList is not defined + + + MESH_IS_NOT_SELECTED + There is no selected mesh Please, select a mesh and try again - - - MESH_NODE - Node - - - MESH_NODE_TITLE - Add Node - - - MINIMUMANGLE_ELEMENTS - Minimum Angle - - - MULTI2D_BORDERS - Borders at Multi-Connections 2D - - - MULTI_BORDERS - Borders at Multi-Connections - - - NODE_ID - Node ID - - - NON_SMESH_OBJECTS_SELECTED - There are objects selected which do not belong to %1 component. - - - PREVIEW - Preview - - - SKEW_ELEMENTS - Skew - - - SMESHGUI_INVALID_PARAMETERS - Parameters are not correctly specified + + + MESH_NODE + Node + + + MESH_NODE_TITLE + Add Node + + + MINIMUMANGLE_ELEMENTS + Minimum Angle + + + MULTI2D_BORDERS + Borders at Multi-Connections 2D + + + MULTI_BORDERS + Borders at Multi-Connections + + + GROUP_NAME_IS_EMPTY + Name of group is not specified. +Please enter a name of new group to be created or choose an existing one. + + + MESH_STANDALONE_GRP_CHOSEN + Group on geometry is chosen: %1. +Do you want to convert it to the standalone group? + + + NODE_ID + Node ID + + + NON_SMESH_OBJECTS_SELECTED + There are objects selected which do not belong to %1 component. + + + PREVIEW + Preview + + + SKEW_ELEMENTS + Skew + + + SMESHGUI_INVALID_PARAMETERS + Parameters are not correctly specified Please enter correct values and try again - - - SMESH_ADD_ALGORITHM - Algorithms - - - SMESH_ADD_ALGORITHM_TITLE - Algorithms Assignation - - - SMESH_ADD_EDGE - Add Edge - - - SMESH_ADD_EDGE_TITLE - Add Edge - - - SMESH_ADD_HEXAS - Add Hexahedron - - - SMESH_ADD_HEXAS_TITLE - Add Hexahedron - - - SMESH_ADD_HYPOTHESIS - Hypothesis - - - SMESH_ADD_HYPOTHESIS_TITLE - Hypothesis Assignation - - - SMESH_ADD_HYP_WRN - "%1" assigned but: + + + SMESH_ADD_ALGORITHM + Algorithms + + + SMESH_ADD_ALGORITHM_TITLE + Algorithms Assignation + + + SMESH_ADD_ELEM0D + Add 0D Element + + + SMESH_ADD_ELEM0D_TITLE + Add 0D Element + + + SMESH_ADD_BALL + Add Ball Element + + + SMESH_ADD_BALL_TITLE + Add Ball Element + + + SMESH_ADD_EDGE + Add Edge + + + SMESH_ADD_EDGE_TITLE + Add Edge + + + SMESH_ADD_HEXAS + Add Hexahedron + + + SMESH_ADD_HEXAS_TITLE + Add Hexahedron + + + SMESH_ADD_HYPOTHESIS + Hypothesis + + + SMESH_ADD_HYPOTHESIS_TITLE + Hypothesis Assignation + + + SMESH_ADD_HYP_WRN + "%1" assigned but: + + + + SMESH_ADD_OCTA + Add hexagonal prism + + + SMESH_ADD_OCTA_TITLE + Add Hexagonal Prism + + + SMESH_ADD_POLYGON + Add polygon + + + SMESH_ADD_POLYGON_TITLE + Add Polygon + + + SMESH_ADD_PENTA + Add pentahedron + + + SMESH_ADD_PENTA_TITLE + Add Pentahedron + + + SMESH_ADD_PYRAMID + Add pyramid + + + SMESH_ADD_PYRAMID_TITLE + Add Pyramid + + + SMESH_ADD_QUADRANGLE + Add Quadrangle + + + SMESH_ADD_QUADRANGLE_TITLE + Add Quadrangle + + + SMESH_ADD_QUADRATIC_EDGE_TITLE + Add Quadratic Edge + + + SMESH_ADD_QUADRATIC_HEXAHEDRON_TITLE + Add Quadratic Hexahedron + + + SMESH_ADD_TRIQUADRATIC_HEXAHEDRON_TITLE + Add TriQuadratic Hexahedron + + + SMESH_ADD_QUADRATIC_PENTAHEDRON_TITLE + Add Quadratic Pentahedron + + + SMESH_ADD_QUADRATIC_PYRAMID_TITLE + Add Quadratic Pyramid + + + SMESH_ADD_BIQUADRATIC_QUADRANGLE_TITLE + Add BiQuadratic Quadrangle + + + SMESH_ADD_QUADRATIC_QUADRANGLE_TITLE + Add Quadratic Quadrangle + + + SMESH_ADD_QUADRATIC_TETRAHEDRON_TITLE + Add Quadratic Tetrahedron + + + SMESH_ADD_QUADRATIC_TRIANGLE_TITLE + Add Quadratic Triangle + + + SMESH_ADD_SUBMESH + SubMesh Construction + + + SMESH_ADD_TETRAS + Add Tetrahedron + + + SMESH_ADD_TETRAS_TITLE + Add Tetrahedron + + + SMESH_ADD_TO_GROUP + Add to group + + + SMESH_ADD_TRIANGLE + Add Triangle + + + SMESH_ADD_TRIANGLE_TITLE + Add Triangle + + + SMESH_ANGLE + Angle + + + SMESH_ARGUMENTS + Arguments + + + SMESH_AUTO_GROUPS + Automatically create groups + + + SMESH_AVAILABLE + Available + + + SMESH_AVAILABLE_ALGORITHMS + Available algorithms + + + SMESH_AVAILABLE_HYPOTHESES + Available hypotheses + + + SMESH_AXIS + Axis + + + SMESH_BAD_SELECTION + No valid selection + + + SMESH_BAD_MESH_SELECTION + No valid mesh selection + + + SMESH_BOUNDARYEDGES + Boundary Edges + + + SMESH_BUILD_COMPOUND_TITLE + Create a Compound + + + SMESH_COPY_MESH_TITLE + Copy Mesh + + + SMESH_KEEP_IDS + Preserve IDs of elements + + + SMESH_BUT_ADD + A&dd + + + SMESH_BUT_APPLY + &Apply + + + SMESH_BUT_CANCEL + &Cancel + + + SMESH_BUT_CLOSE + &Close + + + SMESH_BUT_CREATE + Create + + + SMESH_BUT_DELETE + Delete + + + SMESH_BUT_FILTER + Set &Filter + + + SMESH_BUT_HELP + &Help + + + SMESH_BUT_NEW + New + + + SMESH_BUT_NO + &No + + + SMESH_BUT_OK + &Ok + + + SMESH_BUT_OVERWRITE + Over&write + + + SMESH_BUT_APPLY_AND_CLOSE + A&pply and Close + + + SMESH_BUT_REMOVE + &Remove + + + SMESH_BUT_SORT + &Sort List + + + SMESH_BUT_YES + &Yes + + + SMESH_CANT_ADD_HYP + Can not assign "%1": - - - SMESH_ADD_POLYGON - Add polygon - - - SMESH_ADD_POLYGON_TITLE - Add polygon - - - SMESH_ADD_QUADRANGLE - Add Quadrangle - - - SMESH_ADD_QUADRANGLE_TITLE - Add Quadrangle - - - SMESH_ADD_QUADRATIC_EDGE_TITLE - Add Quadratic Edge - - - SMESH_ADD_QUADRATIC_HEXAHEDRON_TITLE - Add Quadratic Hexahedron - - - SMESH_ADD_QUADRATIC_PENTAHEDRON_TITLE - Add Quadratic Pentahedron - - - SMESH_ADD_QUADRATIC_PYRAMID_TITLE - Add Quadratic Pyramid - - - SMESH_ADD_QUADRATIC_QUADRANGLE_TITLE - Add Quadratic Quadrangle - - - SMESH_ADD_QUADRATIC_TETRAHEDRON_TITLE - Add Quadratic Tetrahedron - - - SMESH_ADD_QUADRATIC_TRIANGLE_TITLE - Add Quadratic Triangle - - - SMESH_ADD_SUBMESH - SubMesh Construction - - - SMESH_ADD_TETRAS - Add Tetrahedron - - - SMESH_ADD_TETRAS_TITLE - Add Tetrahedron - - - SMESH_ADD_TRIANGLE - Add Triangle - - - SMESH_ADD_TRIANGLE_TITLE - Add Triangle - - - SMESH_ANGLE - Angle - - - SMESH_ARGUMENTS - Arguments - - - SMESH_AUTO_GROUPS - Automatically create groups - - - SMESH_AVAILABLE - Available - - - SMESH_AVAILABLE_ALGORITHMS - Available algorithms - - - SMESH_AVAILABLE_HYPOTHESES - Available hypotheses - - - SMESH_AXIS - Axis - - - SMESH_BAD_SELECTION - No valid selection - - - SMESH_BAD_MESH_SELECTION - No valid mesh selection - - - SMESH_BOUNDARYEDGES - Boundary Edges - - - SMESH_BUILD_COMPOUND_TITLE - Create a Compound - - - SMESH_BUT_ADD - A&dd - - - SMESH_BUT_APPLY - &Apply - - - SMESH_BUT_CANCEL - &Cancel - - - SMESH_BUT_CLOSE - &Close - - - SMESH_BUT_CREATE - Create - - - SMESH_BUT_DELETE - Delete - - - SMESH_BUT_FILTER - Set &Filters - - - SMESH_BUT_HELP - &Help - - - SMESH_BUT_NEW - New - - - SMESH_BUT_NO - &No - - - SMESH_BUT_OK - &Ok - - - SMESH_BUT_APPLY_AND_CLOSE - A&pply and Close - - - SMESH_BUT_REMOVE - &Remove - - - SMESH_BUT_SORT - &Sort List - - - SMESH_BUT_YES - &Yes - - - SMESH_CANT_ADD_HYP - Can not assign "%1": + + + SMESH_CANT_RM_HYP + Can not unassign "%1": + + + + SMESH_CHECK_COLOR + Color + + + SMESH_CLIPPING_FROM + From <--- + + + SMESH_CLIPPING_INTO + ---> Into + + + SMESH_CLIPPING_TITLE + Change Clipping + + + SMESH_COMPUTE_SUCCEED + Mesh computation succeed + + + SMESH_EVALUATE_SUCCEED + Mesh evaluation succeed + + + SMESH_CONTENT + Content + + + SMESH_CONTINUE_MESH_VISUALIZATION + It seems that there is not enough memory to show the mesh +so that the application may crash. Do you wish to continue visualization? + + + SMESH_COORDINATES + Coordinates + + + SMESH_COPY_ELEMENTS + Copy Elements + + + SMESH_COPY_GROUPS + Copy groups + + + SMESH_CREATE_ALGORITHMS + Create algorithms + + + SMESH_CREATE_COPY + Create a copy + + + SMESH_CREATE_GROUP_TITLE + Create Group + + + SMESH_CREATE_GEO_GROUP + Create Groups from Geometry + + + SMESH_CREATE_HYPOTHESES + Create hypotheses + + + SMESH_CREATE_MESH + Create a new mesh + + + SMESH_CREATE_POLYHEDRAL_VOLUME_TITLE + Create polyhedral volume + + + SMESH_DIAGONAL + Diagonal Inversion + + + SMESH_DIAGONAL_INVERSION_TITLE + Diagonal Inversion + + + SMESH_DISTANCE + Distance + + + SMESH_DRS_1 + MED file contains no mesh with the given name + + + SMESH_DRS_2 + MED file has overlapped ranges of element numbers, the numbers from the file are ignored + + + SMESH_DRS_3 + Some elements were skipped due to incorrect file data + + + SMESH_DRS_4 + The file is incorrect, some data is missed + + + SMESH_DRS_EMPTY + The file is empty, there is nothing to be published + + + SMESH_DX + dX + + + SMESH_DY + dY + + + SMESH_DZ + dZ + + + SMESH_ELEM0D + 0D Element + + + SMESH_ELEMS0D + 0D Elements + + + SMESH_BALL_ELEM + Ball + + + SMESH_BALL + Ball + + + SMESH_BALLS + Balls + + + SMESH_EDGE + Edge + + + SMESH_EDGES + Edges + + + SMESH_EDGES_CONNECTIVITY_TITLE + Edges Connectivity + + + SMESH_EDIT_GROUP_TITLE + Edit Group + + + SMESH_EDIT_GEOMGROUP_AS_GROUP_TITLE + Edit Group as Standalone + + + SMESH_EDIT_HYPOTHESES + Hypotheses Assignation + + + SMESH_EDIT_USED + Used + + + SMESH_ELEMENTS + Elements + + + SMESH_ELEMENTS_COLOR + Mesh Element Color + + + SMESH_ELEMENTS_TYPE + Elements Type + + + SMESH_ELEMENT_TYPE + Element Type + + + SMESH_ERROR + Error + + + SMESH_ERR_SCALARBAR_PARAMS + Warning! The parameters is incorrect + + + SMESH_EXPORT_FAILED + Mesh export failed. +Probably, there is not enough space on disk. + + + SMESH_EXPORT_MED_DUPLICATED_GRP + There are duplicated group names in mesh "%1". +You can cancel exporting and rename them, +otherwise some group names in the resulting file +will not match ones in the study. +Do you want to continue ? + + + SMESH_EXPORT_MED_DUPLICATED_MESH_NAMES + There are some mesh objects with the same names in the selection. +The result file may be incorrect. +Do you want to continue ? + + + SMESH_EXPORT_MED_V2_1 + During export mesh with name - "%1" to MED 2.1 +polygons and polyhedrons elements will be missed +For correct export use MED 2.2 +Are you sure want to export to MED 2.1 ? + + + SMESH_EXPORT_MED_VERSION_COLLISION + MED version of the file "%1" +is unknown or doesn't match the selected version. +Overwrite the file? + + + SMESH_EXPORT_MED_MESH_NAMES_COLLISION + The selected file already contains +meshes with the following names: %1 +The result file may be incorrect. +Overwrite the file? + + + EXPORT_NOT_SUPPORTED + During export mesh with name "%1" to %2 +%3 will be missed. +Do you want to continue ? + + + SMESH_EXTRUSION + Extrusion + + + SMESH_EXTRUSION_TO_DISTANCE + Extrusion To Distance + + + SMESH_EXTRUSION_ALONG_VECTOR + Extrusion Along Vector + + + SMESH_FACE + Face + + + SMESH_FEATUREANGLE + Feature Angle + + + SMESH_FEATUREEDGES + Feature Edges + + + SMESH_FILE_EXISTS + The file "%1" already exists. +Do you want to overwrite it or +add the exported data to its contents? + + + SMESH_FONT_ARIAL + Arial + + + SMESH_FONT_BOLD + Bold + + + SMESH_FONT_COURIER + Courier + + + SMESH_FONT_ITALIC + Italic + + + SMESH_FONT_SCALARBAR + Font + + + SMESH_FONT_SHADOW + Shadow + + + SMESH_FONT_TIMES + Times + + + SMESH_GEOM_GROUP + Geometry group + + + SMESH_GROUP + Group + + + SMESH_GROUP_GEOMETRY + Group on geometry + + + SMESH_GROUP_FILTER + Group on filter + + + SMESH_GROUP_SELECTED + %1 Groups + + + SMESH_GROUP_STANDALONE + Standalone group + + + SMESH_GROUP_TYPE + Group type + + + SMESH_HEIGHT + Height: + + + SMESH_HEXAS + Hexahedron + + + SMESH_HEXAHEDRA + Hexahedrons + + + SMESH_HILIGHT_COLOR + Highlight Color + + + SMESH_HORIZONTAL + Horizontal + + + SMESH_HYPOTHESES + Hypotheses + + + SMESH_HYP_1 + Algorithm misses a hypothesis + + + SMESH_HYP_10 + Hypothesis and submesh dimensions mismatch + + + SMESH_HYP_11 + Shape is neither the main one, nor its sub-shape, nor a valid group + + + SMESH_HYP_12 + Geometry mismatches algorithm's expectation +Check algorithm documentation for supported geometry + + + SMESH_HYP_13 + Algorithm can't work without shape + + + SMESH_HYP_2 + There are concurrent hypotheses on a shape + + + SMESH_HYP_3 + Hypothesis has a bad parameter value + + + SMESH_HYP_4 + Submesh is ignored as there is another algorithm of upper dimension generating %1D elements + + + SMESH_HYP_5 + Algorithm hides algorithm(s) of lower dimension by generating all-dimensions elements + + + SMESH_HYP_6 + Unknown fatal error at hypothesis definition + + + SMESH_HYP_7 + Hypothesis is not suitable in the current context + + + SMESH_HYP_8 + Non-conform mesh is produced using applied hypotheses + + + SMESH_HYP_9 + Such dimention hypothesis is already assigned to the shape + + + SMESH_ID_DIAGONAL + Id Edges + + + SMESH_ID_ELEMENTS + Id Elements + + + SMESH_ID_FACES + Id Faces + + + SMESH_ID_NODES + Id Nodes + + + SMESH_INCORRECT_INPUT + Incorrect input data! + + + SMESH_INFORMATION + Information + + + SMESH_INIT + Mesh + + + SMESH_INIT_MESH + Mesh Construction + + + SMESH_INSUFFICIENT_DATA + Insufficient input value + + + SMESH_LABELS + Labels: + + + SMESH_LABELS_COLORS_SCALARBAR + Colors && Labels + + + SMESH_LENGTH + Length + + + SMESH_MAKE_GROUPS + Generate groups + + + SMESH_MANIFOLDEDGES + Manifold Edges + + + SMESH_MAX + Max + + + SMESH_MEN_ALGORITHMS + Algorithms + + + SMESH_MEN_APPLIED_ALGORIHTMS + Applied Algorithms + + + SMESH_MEN_APPLIED_HYPOTHESIS + Applied Hypotheses + + + SMESH_MEN_COMPONENT + SMESH + + + SMESH_MEN_HYPOTHESIS + Hypotheses + + + SMESH_MEN_SubMeshesOnCompound + SubMeshes On Compound + + + SMESH_MEN_SubMeshesOnEdge + SubMeshes On Edge + + + SMESH_MEN_SubMeshesOnFace + SubMeshes On Face + + + SMESH_MEN_SubMeshesOnSolid + SubMeshes On Solid + + + SMESH_MEN_SubMeshesOnVertex + SubMeshes On Vertex + + + SMESH_AUTOMATIC + Automatic + + + SMESH_MANUAL + Manual + + + SMESH_MERGE_ELEMENTS + Merge elements + + + SMESH_MODE + Mode + + + SMESH_MERGED_ELEMENTS + %1 elements successfully merged. + + + SMESH_MERGED_NODES + %1 nodes successfully merged. + + + SMESH_NO_ELEMENTS_DETECTED + There are no elements to merge. + + + SMESH_NO_NODES_DETECTED + There are no nodes to merge. + + + SMESH_MERGE_NODES + Merge nodes + + + SMESH_MESH + Mesh + + + SMESH_MESHINFO_0DELEMS + 0D Elements + + + SMESH_MESHINFO_BALLS + Balls + + + SMESH_MESHINFO_ALL_TYPES + Heterogenous + + + SMESH_MESHINFO_EDGES + Edges + + + SMESH_MESHINFO_ELEMENTS + Elements + + + SMESH_MESHINFO_ENTITIES + Entities + + + SMESH_MESHINFO_FACES + Faces + + + SMESH_MESHINFO_HEXAS + Hexahedrons + + + SMESH_MESHINFO_NAME + Name + + + SMESH_MESHINFO_NODES + Nodes + + + SMESH_MESHINFO_ORDER0 + Total + + + SMESH_MESHINFO_ORDER1 + Linear + + + SMESH_MESHINFO_ORDER2 + Quadratic + + + SMESH_MESHINFO_HEXAPRISM + Hexagonal prisms + + + SMESH_MESHINFO_POLYEDRES + Polyhedrons + + + SMESH_MESHINFO_POLYGONES + Polygons + + + SMESH_MESHINFO_PRISMS + Prisms + + + SMESH_MESHINFO_PYRAS + Pyramids + + + SMESH_MESHINFO_QUADRANGLES + Quadrangles + + + SMESH_MESHINFO_TETRAS + Tetrahedrons + + + SMESH_MESHINFO_TITLE + Mesh Infos + + + SMESH_MESHINFO_TOTAL + Total + + + SMESH_MESHINFO_TRIANGLES + Triangles + + + SMESH_MESHINFO_TYPE + Type + + + SMESH_MESHINFO_VOLUMES + Volumes + + + SMESH_MIN + Min + + + SMESH_MOVE + Move + + + SMESH_MOVE_ELEMENTS + Move Elements + + + SMESH_MOVE_NODES_TITLE + Move Node + + + SMESH_NAME + Name + + + SMESH_NODES + Nodes + + + SMESH_NONMANIFOLDEDGES + Non Manifold Edges + + + SMESH_NORMAL + Normal + + + SMESH_NO_MESH_VISUALIZATION + There is not enough memory to show the mesh + + + SMESH_NUMBEROFCOLORS + Nb of colors: + + + SMESH_NUMBEROFLABELS + Nb of labels: + + + SMESH_NUMBEROFSTEPS + Number of steps: + + + SMESH_OBJECTS_SELECTED + %1_objects + + + SMESH_OBJECT_ALGORITHM + Algorithm + + + SMESH_OBJECT_GEOM + Geometrical Object + + + SMESH_OBJECT_HYPOTHESIS + Hypothesis + + + SMESH_OBJECT_MESH + Mesh + + + SMESH_OBJECT_MESHorSUBMESH + Mesh or SubMesh + + + SMESH_OPERATION_FAILED + Operation failed + + + SMESH_OCTA + Octogonal prism + + + SMESH_OCTAHEDRA + Octogonal prisms + + + TOP_OCTA + Hexagonal prism + + + MEN_OCTA + Hexagonal prism + + + STB_OCTA + Hexagonal prism + + + SMESH_ORIENTATION + Orientation + + + SMESH_ORIENTATION_ELEMENTS_TITLE + Change Orientation + + + SMESH_OUTLINE_COLOR + Mesh Object Color + + + SMESH_PARAMETERS + Parameters + + + SMESH_PENTA + Pentahedron + + + SMESH_PENTAHEDRA + Pentahedrons + + + TOP_PENTA + Pentahedron + + + MEN_PENTA + Pentahedron + + + STB_PENTA + Pentahedron + + + SMESH_PLANE + Plane + + + SMESH_POINT + Point + + + SMESH_POINT_1 + Point 1 + + + SMESH_POINT_2 + Point 2 + + + SMESH_BASE_POINT + Base Point + + + SMESH_POLYEDRE_CREATE_ERROR + Polyedron creation error + + + SMESH_POLYEDRON + Polyhedron + + + SMESH_POLYEDRONS + Polyhedrons + + + SMESH_QUADRATIC_POLYEDRON + Quadratic Polyhedron + + + SMESH_QUADRATIC_POLYEDRONS + Quadratic Polyhedrons + + + SMESH_POLYGON + Polygon + + + SMESH_POLYGONS + Polygons + + + SMESH_QUADRATIC_POLYGON + Quadratic Polygon + + + SMESH_QUADRATIC_POLYGONS + Quadratic Polygons + + + SMESH_POSITION_SIZE_SCALARBAR + Origin && Size + + + SMESH_DISTRIBUTION_SCALARBAR + Distribution + + + SMESH_SHOW_DISTRIBUTION_SCALARBAR + Show Distribution + + + SMESH_PRECISION + Precision + + + SMESH_PREFERENCES_SCALARBAR + Scalar Bar Preferences + + + SMESH_PREF_SELECTION + Preferences - Selection + + + SMESH_PRESELECTION + Preselection + + + SMESH_PRISM + Prism + + + SMESH_PROPERTIES_SCALARBAR + Scalar Bar Properties + + + SMESH_PYRAMID + Pyramid + + + SMESH_PYRAMIDS + Pyramids + + + MEN_PYRAMID + Pyramid + + + TOP_PYRAMID + Pyramid + + + STB_PYRAMID + Pyramid + + + SMESH_QUADRANGLE + Quadrangle + + + SMESH_QUADRANGLES + Quadrangles + + + SMESH_QUADRATIC_EDGE + Quadratic Edge + + + SMESH_QUADRATIC_EDGES + Quadratic Edges + + + SMESH_QUADRATIC_HEXAHEDRON + Quadratic Hexahedron + + + SMESH_QUADRATIC_HEXAHEDRONS + Quadratic Hexahedrons + + + SMESH_TRIQUADRATIC_HEXAHEDRON + TriQuadratic Hexahedron + + + SMESH_TRIQUADRATIC_HEXAHEDRONS + TriQuadratic Hexahedrons + + + TOP_TRIQUADRATIC_HEXAHEDRON + TriQuadratic Hexahedron + + + MEN_TRIQUADRATIC_HEXAHEDRON + TriQuadratic Hexahedron + + + STB_TRIQUADRATIC_HEXAHEDRON + TriQuadratic Hexahedron + + + SMESH_QUADRATIC_PENTAHEDRON + Quadratic Pentahedron + + + SMESH_QUADRATIC_PENTAHEDRONS + Quadratic Pentahedrons + + + SMESH_QUADRATIC_PYRAMID + Quadratic Pyramid + + + SMESH_QUADRATIC_PYRAMIDS + Quadratic Pyramids + + + SMESH_QUADRATIC_QUADRANGLE + Quadratic Quadrangle + + + SMESH_QUADRATIC_QUADRANGLES + Quadratic Quadrangles + + + SMESH_BIQUADRATIC_QUADRANGLE + BiQuadratic Quadrangle + + + SMESH_BIQUADRATIC_QUADRANGLES + BiQuadratic Quadrangles + + + MEN_BIQUADRATIC_QUADRANGLE + BiQuadratic Quadrangle + + + TOP_BIQUADRATIC_QUADRANGLE + BiQuadratic Quadrangle + + + STB_BIQUADRATIC_QUADRANGLE + BiQuadratic Quadrangle + + + SMESH_QUADRATIC_TETRAHEDRON + Quadratic Tetrahedron + + + SMESH_QUADRATIC_TETRAHEDRONS + Quadratic Tetrahedrons + + + SMESH_QUADRATIC_TRIANGLE + Quadratic Triangle + + + SMESH_QUADRATIC_TRIANGLES + Quadratic Triangles + + + SMESH_RANGE_MAX + Max value: + + + SMESH_RANGE_MIN + Min value: + + + SMESH_RANGE_SCALARBAR + Scalar Range + + + SMESH_REALLY_DELETE + Do you really want to delete this %1 object(s)? : %2 + + + SMESH_REMOVE + Remove + + + SMESH_REMOVE_ELEMENTS_TITLE + Remove Elements + + + SMESH_REMOVE_NODES_TITLE + Remove Nodes + + + SMESH_RENUMBERING + Renumbering + + + SMESH_RENUMBERING_ELEMENTS_TITLE + Renumbering elements + + + SMESH_RENUMBERING_NODES_TITLE + Renumbering nodes + + + SMESH_REVERSE + Reverse + + + SMESH_REVOLUTION + Revolution + + + SMESH_RM_HYP_WRN + "%1" unassigned but: - - - SMESH_CANT_RM_HYP - Can not unassign "%1": + + + SMESH_ROTATION + Rotation + + + SMESH_ROTATION_TITLE + Rotation about an axis + + + SMESH_SCALARBAR + Scalar Bar + + + SMESH_SEGMENTS + Segments + + + SMESH_SELECTION + Selection + + + SMESH_SELECT_FROM + Select From + + + SMESH_SELECT_WHOLE_MESH + Select whole mesh, submesh or group + + + SMESH_SET_COLOR + Color group + + + SMESH_SEWING + Sewing + + + SMESH_SMOOTHING + Smoothing + + + SMESH_STANDARD_MESHINFO_TITLE + Standard Mesh Infos + + + SMESH_SUBMESH + SubMesh + + + SMESH_SUBMESH_SELECTED + %1 SubMeshes + + + SMESH_SYMMETRY + Symmetry + + + SMESH_TETRAS + Tetrahedron + + + SMESH_TETRAHEDRA + Tetrahedrons + + + SMESH_TITLE + Title: + + + SMESH_TOLERANCE + Tolerance + + + SMESH_TRANSLATION + Translation + + + SMESH_SCALE_TITLE + Scale Transform + + + SMESH_DUPLICATE_TITLE + Duplicate Nodes + + + SMESH_SCALE + Scale + + + SMESH_SCALE_FACTOR + Scale Factor : + + + SMESH_SCALE_FACTOR_X + Scale Factor X : + + + SMESH_SCALE_FACTOR_Y + Scale Factor Y : + + + SMESH_SCALE_FACTOR_Z + Scale Factor Z : + + + SMESH_TRANSPARENCY_OPAQUE + Opaque + + + SMESH_TRANSPARENCY_TITLE + Change Transparency + + + SMESH_TRANSPARENCY_TRANSPARENT + Transparent + + + SMESH_TRIANGLE + Triangle + + + SMESH_TRIANGLES + Triangles + + + SMESH_UPDATEVIEW + Update View + + + SMESH_VALUE + Value + + + SMESH_VECTOR + Vector + + + SMESH_VERTICAL + Vertical + + + SMESH_DISTRIBUTION_COLORING_TYPE + Coloring Type + + + SMESH_MONOCOLOR + Monocolor + + + SMESH_DISTRIBUTION_COLOR + Distribution color: + + + SMESH_MULTICOLOR + Multicolor + + + SMESH_VISU_PROBLEM + Mesh visualization failed, probably due to lack of memory + + + SMESH_VISU_PROBLEM_CLEAR + Mesh visualization failed, no memory even to show a message, +so all visual data have been removed to let the application live. +Consider saving your work before application crash + + + SMESH_VOLUME + Volume + + + SMESH_WARNING + Warning + + + SMESH_WHAT_IS_TITLE + Mesh Element Info + + + SMESH_WIDTH + Width: + + + SMESH_WRN_ALGORITHM_ALREADYEXIST + Algorithm already exists + + + SMESH_WRN_COMPUTE_FAILED + Mesh computation failed + + + SMESH_WRN_EVALUATE_FAILED + Mesh evaluation failed + + + SMESH_WRN_EMPTY_NAME + Empty name is not valid + + + SMESH_WRN_HYPOTHESIS_ALREADYEXIST + Hypothesis already exists + + + SMESH_WRN_HYPOTHESIS_NOTEXIST + Hypothesis or Algorithm not exists + + + SMESH_WRN_MISSING_PARAMETERS + Missing parameters + + + SMESH_WRN_NO_AVAILABLE_DATA + No available data in selection + + + SMESH_WRN_SELECTIONMODE_DIAGONAL + Activate Link Selection Mode + + + SMESH_WRN_SELECTIONMODE_ELEMENTS + Activate Elements Selection Mode + + + SMESH_WRN_SELECTIONMODE_NODES + Activate Nodes Selection Mode + + + SMESH_WRN_VIEWER_VTK + Study frame with VTK Viewer must be activated + + + SMESH_WRN_SIZE_LIMIT_EXCEEDED + No automatic update of the presentation has been done: new mesh size (%1 elements) exceeds current size limit (%2 elements). +Please check preferences of Mesh module. - - - SMESH_CHECK_COLOR - Color - - - SMESH_CLIPPING_FROM - From <--- - - - SMESH_CLIPPING_INTO - ---> Into - - - SMESH_CLIPPING_TITLE - Change Clipping - - - SMESH_COMPUTE_SUCCEED - Mesh computation succeed - - - SMESH_CONTENT - Content - - - SMESH_CONTINUE_MESH_VISUALIZATION - It seems that there is not enough memory to show the mesh -so that the application may crash. Do you wish to continue visualization? - - - SMESH_COORDINATES - Coordinates - - - SMESH_COPY_ELEMENTS - Copy Elements - - - SMESH_COPY_GROUPS - Copy groups - - - SMESH_CREATE_ALGORITHMS - Create algorithms - - - SMESH_CREATE_COPY - Create a copy - - - SMESH_CREATE_GROUP_TITLE - Create Group - - - SMESH_CREATE_GEO_GROUP - Create Groups from Geometry - - - SMESH_CREATE_HYPOTHESES - Create hypotheses - - - SMESH_CREATE_MESH - Create a new mesh - - - SMESH_CREATE_POLYHEDRAL_VOLUME_TITLE - Create polyhedral volume - - - SMESH_DIAGONAL - Diagonal Inversion - - - SMESH_DIAGONAL_INVERSION_TITLE - Diagonal Inversion - - - SMESH_DISTANCE - Distance - - - SMESH_DRS_1 - MED file contains no mesh with the given name - - - SMESH_DRS_2 - MED file has overlapped ranges of element numbers, the numbers from the file are ignored - - - SMESH_DRS_3 - Some elements were skipped due to incorrect file data - - - SMESH_DRS_4 - The file is incorrect, some data is missed - - - SMESH_DRS_EMPTY - The file is empty, there is nothing to be published - - - SMESH_DX - dX - - - SMESH_DY - dY - - - SMESH_DZ - dZ - - - SMESH_EDGE - Edge - - - SMESH_EDGES_CONNECTIVITY_TITLE - Edges Connectivity - - - SMESH_EDIT_GROUP_TITLE - Edit Group - - - SMESH_EDIT_GEOMGROUP_AS_GROUP_TITLE - Edit Group as Standalone - - - SMESH_EDIT_HYPOTHESES - Hypotheses Assignation - - - SMESH_EDIT_USED - Used - - - SMESH_ELEMENTS - Elements - - - SMESH_ELEMENTS_COLOR - Mesh Element Color - - - SMESH_ELEMENTS_TYPE - Elements Type - - - SMESH_ELEMENT_TYPE - Element Type - - - SMESH_ERROR - Error - - - SMESH_ERR_SCALARBAR_PARAMS - Warning! The parameters is incorrect - - - SMESH_EXPORT_FAILED - Mesh export failed. -Probably, there is not enough space on disk. - - - SMESH_EXPORT_MED_DUPLICATED_GRP - There are duplicated group names in mesh "%1". -You can cancel exporting and rename them, -otherwise some group names in the resulting MED file -will not match ones in the study. -Do you want to continue ? - - - SMESH_EXPORT_MED_V2_1 - During export mesh with name - "%1" to MED 2.1 -polygons and polyhedrons elements will be missed -For correct export use MED 2.2 -Are you sure want to export to MED 2.1 ? - - - SMESH_EXPORT_STL1 - Mesh - "%1" does not contain triangles - - - SMESH_EXPORT_STL2 - Mesh - "%1" contains another than triangles elements, they are ignored during writing to STL - - - SMESH_EXPORT_UNV - During export mesh with name - "%1" to UNV - pyramid's elements will be missed - - - SMESH_EXTRUSION - Extrusion - - - SMESH_FACE - Face - - - SMESH_FEATUREANGLE - Feature Angle - - - SMESH_FEATUREEDGES - Feature Edges - - - SMESH_FONT_ARIAL - Arial - - - SMESH_FONT_BOLD - Bold - - - SMESH_FONT_COURIER - Courier - - - SMESH_FONT_ITALIC - Italic - - - SMESH_FONT_SCALARBAR - Font - - - SMESH_FONT_SHADOW - Shadow - - - SMESH_FONT_TIMES - Times - - - SMESH_GEOM_GROUP - Geometry group - - - SMESH_GROUP - Group - - - SMESH_GROUP_GEOMETRY - Group on geometry - - - SMESH_GROUP_SELECTED - %1 Groups - - - SMESH_GROUP_STANDALONE - Standalone group - - - SMESH_GROUP_TYPE - Group type - - - SMESH_HEIGHT - Height: - - - SMESH_HEXAS - Hexahedron - - - SMESH_HILIGHT_COLOR - Highlight Color - - - SMESH_HORIZONTAL - Horizontal - - - SMESH_HYPOTHESES - Hypotheses - - - SMESH_HYP_1 - Algorithm misses a hypothesis - - - SMESH_HYP_10 - Hypothesis and submesh dimensions mismatch - - - SMESH_HYP_11 - Shape is neither the main one, nor its subshape, nor a valid group - - - SMESH_HYP_12 - Geomerty mismatches algorithm's expectation - - - SMESH_HYP_13 - Algorithm can't work without shape - - - SMESH_HYP_2 - There are concurrent hypotheses on a shape - - - SMESH_HYP_3 - Hypothesis has a bad parameter value - - - SMESH_HYP_4 - Submesh is ignored as there is another algorithm of upper dimension generating %1D elements - - - SMESH_HYP_5 - Algorithm hides algorithm(s) of lower dimension by generating all-dimensions elements - - - SMESH_HYP_6 - Unknown fatal error at hypothesis definition - - - SMESH_HYP_7 - Hypothesis is not suitable in the current context - - - SMESH_HYP_8 - Non-conform mesh is produced using applied hypotheses - - - SMESH_HYP_9 - Such dimention hypothesis is already assigned to the shape - - - SMESH_ID_DIAGONAL - Id Edges - - - SMESH_ID_ELEMENTS - Id Elements - - - SMESH_ID_FACES - Id Faces - - - SMESH_ID_NODES - Id Nodes - - - SMESH_INCORRECT_INPUT - Incorrect input data! - - - SMESH_INFORMATION - Information - - - SMESH_INIT - Mesh - - - SMESH_INIT_MESH - Mesh Construction - - - SMESH_INSUFFICIENT_DATA - Insufficient input value - - - SMESH_LABELS - Labels: - - - SMESH_LABELS_COLORS_SCALARBAR - Colors && Labels - - - SMESH_LENGTH - Length - - - SMESH_MAKE_GROUPS - Generate groups - - - SMESH_MANIFOLDEDGES - Manifold Edges - - - SMESH_MAX - Max - - - SMESH_MEN_ALGORITHMS - Algorithms - - - SMESH_MEN_APPLIED_ALGORIHTMS - Applied Algorithms - - - SMESH_MEN_APPLIED_HYPOTHESIS - Applied Hypotheses - - - SMESH_MEN_COMPONENT - SMESH - - - SMESH_MEN_HYPOTHESIS - Hypotheses - - - SMESH_MEN_SubMeshesOnCompound - SubMeshes On Compound - - - SMESH_MEN_SubMeshesOnEdge - SubMeshes On Edge - - - SMESH_MEN_SubMeshesOnFace - SubMeshes On Face - - - SMESH_MEN_SubMeshesOnSolid - SubMeshes On Solid - - - SMESH_MEN_SubMeshesOnVertex - SubMeshes On Vertex - - - SMESH_MERGE_ELEMENTS - Merge elements - - - SMESH_MERGE_NODES - Merge nodes - - - SMESH_MESH - Mesh - - - SMESH_MESHINFO_ALL_TYPES - Heterogenous - - - SMESH_MESHINFO_EDGES - Edges - - - SMESH_MESHINFO_ELEMENTS - Elements - - - SMESH_MESHINFO_ENTITIES - Entities - - - SMESH_MESHINFO_FACES - Faces - - - SMESH_MESHINFO_HEXAS - Hexahedrons - - - SMESH_MESHINFO_NAME - Name - - - SMESH_MESHINFO_NODES - Nodes - - - SMESH_MESHINFO_ORDER0 - Total - - - SMESH_MESHINFO_ORDER1 - Linear - - - SMESH_MESHINFO_ORDER2 - Quadratic - - - SMESH_MESHINFO_POLYEDRES - Polyhedrons - - - SMESH_MESHINFO_POLYGONES - Polygons - - - SMESH_MESHINFO_PRISMS - Prisms - - - SMESH_MESHINFO_PYRAS - Pyramids - - - SMESH_MESHINFO_QUADRANGLES - Quadrangles - - - SMESH_MESHINFO_TETRAS - Tetrahedrons - - - SMESH_MESHINFO_TITLE - Mesh Infos - - - SMESH_MESHINFO_TOTAL - Total - - - SMESH_MESHINFO_TRIANGLES - Triangles - - - SMESH_MESHINFO_TYPE - Type - - - SMESH_MESHINFO_VOLUMES - Volumes - - - SMESH_MIN - Min - - - SMESH_MOVE - Move - - - SMESH_MOVE_ELEMENTS - Move Elements - - - SMESH_MOVE_NODES_TITLE - Move Node - - - SMESH_NAME - Name - - - SMESH_NODES - Nodes - - - SMESH_NONMANIFOLDEDGES - Non Manifold Edges - - - SMESH_NORMAL - Normal - - - SMESH_NO_MESH_VISUALIZATION - There is not enough memory to show the mesh - - - SMESH_NUMBEROFCOLORS - Nb of colors: - - - SMESH_NUMBEROFLABELS - Nb of labels: - - - SMESH_NUMBEROFSTEPS - Number of steps: - - - SMESH_OBJECTS_SELECTED - %1_objects - - - SMESH_OBJECT_ALGORITHM - Algorithm - - - SMESH_OBJECT_GEOM - Geometrical Object - - - SMESH_OBJECT_HYPOTHESIS - Hypothesis - - - SMESH_OBJECT_MESH - Mesh - - - SMESH_OBJECT_MESHorSUBMESH - Mesh or SubMesh - - - SMESH_OPERATION_FAILED - Operation failed - - - SMESH_ORIENTATION - Orientation - - - SMESH_ORIENTATION_ELEMENTS_TITLE - Change Orientation - - - SMESH_OUTLINE_COLOR - Mesh Object Color - - - SMESH_PARAMETERS - Parameters - - - SMESH_PLANE - Plane - - - SMESH_POINT - Point - - - SMESH_POINT_1 - Point 1 - - - SMESH_POINT_2 - Point 2 - - - SMESH_POLYEDRE_CREATE_ERROR - Polyedron creation error - - - SMESH_POLYEDRON - Polyhedron - - - SMESH_POLYGON - Polygon - - - SMESH_POSITION_SIZE_SCALARBAR - Origin && Size - - - SMESH_PRECISION - Precision - - - SMESH_PREFERENCES_SCALARBAR - Scalar Bar Preferences - - - SMESH_PREF_SELECTION - Preferences - Selection - - - SMESH_PRESELECTION - Preselection - - - SMESH_PRISM - Prism - - - SMESH_PROPERTIES_SCALARBAR - Scalar Bar Properties - - - SMESH_PYRAMID - Pyramid - - - SMESH_QUADRANGLE - Quadrangle - - - SMESH_QUADRATIC_EDGE - Quadratic Edge - - - SMESH_QUADRATIC_HEXAHEDRON - Quadratic Hexahedron - - - SMESH_QUADRATIC_PENTAHEDRON - Quadratic Pentahedron - - - SMESH_QUADRATIC_PYRAMID - Quadratic Pyramid - - - SMESH_QUADRATIC_QUADRANGLE - Quadratic Quadrangle - - - SMESH_QUADRATIC_TETRAHEDRON - Quadratic Tetrahedron - - - SMESH_QUADRATIC_TRIANGLE - Quadratic Triangle - - - SMESH_RANGE_MAX - Max value: - - - SMESH_RANGE_MIN - Min value: - - - SMESH_RANGE_SCALARBAR - Scalar Range - - - SMESH_REALLY_DELETE - Do you really want to delete this %1 object(s)? : %2 - - - SMESH_REMOVE - Remove - - - SMESH_REMOVE_ELEMENTS_TITLE - Remove Elements - - - SMESH_REMOVE_NODES_TITLE - Remove Nodes - - - SMESH_RENUMBERING - Renumbering - - - SMESH_RENUMBERING_ELEMENTS_TITLE - Renumbering elements - - - SMESH_RENUMBERING_NODES_TITLE - Renumbering nodes - - - SMESH_REVERSE - Reverse - - - SMESH_REVOLUTION - Revolution - - - SMESH_RM_HYP_WRN - "%1" unassigned but: + + + SMESH_WRN_WARNING + Warning + + + SMESH_X + X + + + SMESH_X_SCALARBAR + X: + + + SMESH_Y + Y + + + SMESH_Y_SCALARBAR + Y: + + + SMESH_Z + Z + + + STATE_ALGO_MISSING + %3 %2D algorithm is missing + + + STATE_HYP_BAD_GEOMETRY + %3 %2D algorithm "%1" is assigned to geometry mismatching its expectation + + + STATE_HYP_BAD_PARAMETER + Hypothesis of %3 %2D algorithm "%1" has a bad parameter value + + + STATE_HYP_MISSING + %3 %2D algorithm "%1" misses %4D hypothesis + + + STATE_HYP_NOTCONFORM + %3 %2D algorithm "%1" would produce not conform mesh: global "Not Conform Mesh Allowed" hypotesis is missing + + + STB_ADV_INFO + Show base information about the mesh object + + + STB_ALL + All + + + STB_AREA + Area + + + STB_ASPECT + Aspect Ratio + + + STB_ASPECT_3D + Aspect Ratio 3D + + + STB_AUTO_COLOR + Auto color + + + STB_AUTO_UPD + Automatic update + + + STB_BUILD_COMPOUND + Build Compound Mesh + + + STB_COPY_MESH + Copy Mesh + + + STB_CLIP + Clipping + + + STB_COLORS + Properties + + + STB_COMPUTE + Compute + + + STB_PRECOMPUTE + Preview + + + STB_EVALUATE + Evaluate + + + STB_CONNECTION + Borders at Multi-Connection + + + STB_CONNECTION_2D + Borders at Multi-Connection 2D + + + STB_CONSTRUCT_GROUP + Construct Group + + + STB_CONV_TO_QUAD + Convert to/from quadratic + + + STB_2D_FROM_3D + Create boundary elements + + + STB_MESH_ORDER + Change submesh priority + + + STB_CREATE_GROUP + Create Group + + + STB_CREATE_GEO_GROUP + Create Group from Geometry + + + STB_CREATE_MESH + Create Mesh + + + STB_CREATE_SUBMESH + Create Sub-mesh + + + STB_CUT + Cutting of quadrangles + + + STB_CUT_GROUP + Cut Groups + + + STB_DAT + Export DAT file + + + STB_IMPORT_DAT + Import DAT file + + + STB_DELETE + Delete + + + STB_DEL_GROUP + Delete Groups with Contents + + + STB_FACE_ORIENTATION + Orientation of faces + + + STB_DISABLE_AUTO_COLOR + Disable auto color + + + STB_DISPLAY_ONLY + Show only + + + STB_DISP_ENT + Display entity + + + STB_ELEM0D + 0D Element + + + STB_ELEMS0D + 0D Elements + + + STB_BALLS + Ball Elements + + + STB_BALL + Ball Element + + + STB_EDGE + Edge + + + STB_EDGES + Edges + + + STB_EDIT_GROUP + Edit Group + + + STB_EDIT_GEOMGROUP_AS_GROUP + Edit Group as Standalone + + + STB_EDIT_HYPO + Edit Hypothesis + + + STB_EDIT_MESHSUBMESH + Edit Mesh/Sub-mesh + + + STB_EXPORT_DAT + Export to DAT file + + + STB_EXPORT_MED + Export to MED file + + + STB_EXPORT_CGNS + Export to CGNS file + + + STB_EXPORT_SAUV + Export to SAUV file + + + STB_EXPORT_STL + Export to STL file + + + STB_EXPORT_UNV + Export to UNV file + + + STB_EXTRUSION + Extrusion + + + STB_EXTRUSION_ALONG + Extrusion along a path + + + STB_FACES + Faces + + + STB_BARE_BORDER_VOLUME + Volumes with bare border + + + STB_BARE_BORDER_FACE + Faces with bare border + + + STB_OVER_CONSTRAINED_VOLUME + Over-constrained volumes + + + STB_OVER_CONSTRAINED_FACE + Over-constrained faces + + + STB_FREE_BORDER + Free Borders + + + STB_FREE_EDGE + Free Edges + + + STB_FREE_NODE + Free Nodes + + + STB_FREE_FACES + Free Faces + + + STB_GLOBAL_HYPO + Global Hypothesis + + + STB_HEXA + Hexahedron + + + STB_HIDE + Hide + + + STB_INT_GROUP + Intersect Groups + + + STB_INV + Diagonal Inversion + + + STB_LENGTH + Length + + + STB_LENGTH_2D + Length 2D + + + STB_MAP + Pattern mapping + + + STB_MAX_ELEMENT_LENGTH_2D + Element Diameter 2D + + + STB_MAX_ELEMENT_LENGTH_3D + Element Diameter 3D + + + STB_IMPORT_MED + Import MED file + + + STB_MED + Export MED file + + + STB_IMPORT_STL + Import STL file + + + STB_STL + Export STL file + + + STB_IMPORT_CGNS + Import CGNS file + + + STB_CGNS + Export CGNS file + + + STB_IMPORT_SAUV + Import SAUV file + + + STB_SAUV + Export SAUV file + + + STB_MERGE + Merge nodes + + + STB_MERGE_ELEMENTS + Merge elements + + + STB_MESH_THROU_POINT + Move Node + + + STB_MIN_ANG + Minimum Angle + + + STB_MOVE + Move Node + + + STB_NODE + Node + + + STB_NODES + Nodes + + + STB_NUM_ELEMENTS + Display Elements + + + STB_NUM_NODES + Display Nodes + + + STB_ORIENT + Orientation + + + STB_POLYGON + Polygon + + + STB_POLYHEDRON + Polyhedron + + + STB_PRECISION + Precision + + + STB_QUAD + Quadrangle + + + STB_QUADRATIC_EDGE + Quadratic Edge + + + STB_QUADRATIC_HEXAHEDRON + Quadratic Hexahedron + + + STB_QUADRATIC_PENTAHEDRON + Quadratic Pentahedron + + + STB_QUADRATIC_PYRAMID + Quadratic Pyramid + + + STB_QUADRATIC_QUADRANGLE + Quadratic Quadrangle + + + STB_QUADRATIC_TETRAHEDRON + Quadratic Tetrahedron + + + STB_QUADRATIC_TRIANGLE + Quadratic Triangle + + + STB_REMOVE_ELEMENTS + Remove elements + + + STB_REMOVE_NODES + Remove nodes + + + STB_REMOVE_ORPHAN_NODES + Remove orphan nodes + + + STB_RENAME + Rename + + + STB_RENUM_ELEMENTS + Renumbering elements + + + STB_RENUM_NODES + Renumbering nodes + + + STB_RESET + Reset + + + STB_SAVE_DISTRIBUTION + Save distribution to the file + + + STB_SHOW_DISTRIBUTION + Show Distribution + + + STB_REVOLUTION + Revolution + + + STB_ROT + Rotation + + + STB_SCALAR_BAR + Scalar bar + + + STB_SCALAR_BAR_PROP + Scalar bar Properties + + + STB_SELECTION + Selection + + + STB_SEL_FILTER_LIB + Selection filters library + + + STB_SEW + Sewing + + + STB_SHADE + Shading + + + STB_SHOW + Show + + + STB_SHRINK + Shrink + + + STB_SKEW + Skew + + + STB_SMOOTH + Smoothing + + + STB_STD_INFO + Standard Mesh Infos + + + STB_SYM + Symmetry + + + STB_TAPER + Taper + + + STB_TETRA + Tetrahedron + + + STB_TRANS + Translation + + + STB_SCALE + Scale Transform + + + STB_DUPLICATE_NODES + Duplicate Nodes + + + STB_TRANSP + Transparency + + + STB_TRIANGLE + Triangle + + + STB_UNASSIGN + Unassign + + + STB_UNION + Union of triangles + + + STB_UNION2 + Union of two triangles + + + STB_IMPORT_UNV + Import UNV file + + + STB_UNV + Export UNV file + + + STB_UN_GROUP + Union Groups + + + STB_UNDERLYING_ELEMS + Create groups of entities from existing groups of superior dimensions + + + STB_UPDATE + Update + + + STB_VOLUMES + Volumes + + + STB_VOLUME_3D + Volume + + + STB_WARP + Warping angle + + + STB_WHAT_IS + Show information about the mesh node or element + + + STB_WIRE + Wireframe + + + TAPER_ELEMENTS + Taper + + + TB_ADD_REMOVE + Add/Remove Toolbar + + + TB_CTRL + Controls Toolbar + + + TB_DISP_MODE + Display Mode Toolbar + + + TB_HYPO + Hypotheses Toolbar + + + TB_MESH + Mesh Toolbar + + + TB_MODIFY + Modification Toolbar + + + TOP_ADV_INFO + Mesh Information + + + TOP_ALL + All + + + TOP_AREA + Area + + + TOP_ASPECT + Aspect Ratio + + + TOP_ASPECT_3D + Aspect Ratio 3D + + + TOP_AUTO_COLOR + Auto color + + + TOP_AUTO_UPD + Automatic update + + + TOP_BUILD_COMPOUND + Build Compound Mesh + + + TOP_COPY_MESH + Copy Mesh + + + TOP_CLIP + Clipping + + + TOP_COLORS + Properties + + + TOP_COMPUTE + Compute + + + TOP_PRECOMPUTE + Preview + + + TOP_EVALUATE + Evaluate + + + TOP_CONNECTION + Borders at Multi-Connection + + + TOP_CONNECTION_2D + Borders at Multi-Connection 2D + + + TOP_CONSTRUCT_GROUP + Construct Group + + + TOP_CONV_TO_QUAD + Convert to/from quadratic + + + TOP_2D_FROM_3D + Create boundary elements + + + TOP_MESH_ORDER + Change submesh priority + + + TOP_CREATE_GROUP + Create Group + + + TOP_CREATE_GEO_GROUP + Create Groups from Geometry + + + TOP_CREATE_MESH + Create Mesh + + + TOP_CREATE_SUBMESH + Create Sub-mesh + + + TOP_CUT + Cutting of quadrangles + + + TOP_CUT_GROUP + Cut Groups + + + TOP_IMPORT_DAT + Import DAT file + + + TOP_DAT + Export DAT file + + + TOP_DELETE + Delete + + + TOP_DEL_GROUP + Delete Groups with Contents + + + TOP_FACE_ORIENTATION + Orientation of faces + + + TOP_DISABLE_AUTO_COLOR + Disable auto color + + + TOP_DISPLAY_ONLY + Show only + + + TOP_DISP_ENT + Display entity + + + TOP_ELEM0D + 0D Element + + + TOP_ELEMS0D + 0D Elements + + + TOP_BALL + Ball + + + TOP_BALLS + Balls + + + TOP_EDGE + Edge + + + TOP_EDGES + Edges + + + TOP_EDIT_GROUP + Edit Group + + + TOP_EDIT_GEOMGROUP_AS_GROUP + Edit Group as Standalone + + + TOP_EDIT_HYPO + Edit Hypothesis + + + TOP_EDIT_MESHSUBMESH + Edit Mesh/Sub-mesh + + + TOP_EXPORT_DAT + Export to DAT file + + + TOP_EXPORT_MED + Export to MED file + + + TOP_EXPORT_SAUV + Export to SAUV file + + + TOP_EXPORT_STL + Export to STL file + + + TOP_EXPORT_UNV + Export to UNV file + + + TOP_EXTRUSION + Extrusion + + + TOP_EXTRUSION_ALONG + Extrusion along a path + + + TOP_FACES + Faces + + + TOP_BARE_BORDER_VOLUME + Volumes with bare border + + + TOP_BARE_BORDER_FACE + Faces with bare border + + + TOP_OVER_CONSTRAINED_VOLUME + Over-constrained volumes + + + TOP_OVER_CONSTRAINED_FACE + Over-constrained faces + + + TOP_FREE_BORDER + Free Borders + + + TOP_FREE_EDGE + Free Edges + + + TOP_FREE_NODE + Free Nodes + + + TOP_FREE_FACES + Free Faces + + + TOP_GLOBAL_HYPO + Global Hypothesis + + + TOP_HEXA + Hexahedron + + + TOP_HIDE + Hide + + + TOP_INT_GROUP + Intersect Groups + + + TOP_INV + Diagonal Inversion + + + TOP_LENGTH + Length + + + TOP_LENGTH_2D + Length 2D + + + TOP_MAP + Pattern mapping + + + TOP_MAX_ELEMENT_LENGTH_2D + Element Diameter 2D + + + TOP_MAX_ELEMENT_LENGTH_3D + Element Diameter 3D + + + TOP_IMPORT_MED + Import MED file + + + TOP_MED + Export MED file + + + TOP_IMPORT_STL + Import STL file + + + TOP_STL + Export STL file + + + TOP_IMPORT_CGNS + Import CGNS file + + + TOP_CGNS + Export CGNS file + + + TOP_IMPORT_SAUV + Import SAUV file + + + TOP_SAUV + Export SAUV file + + + TOP_MERGE + Merge nodes + + + TOP_MERGE_ELEMENTS + Merge elements + + + TOP_MESH_THROU_POINT + Move Node + + + TOP_MIN_ANG + Minimum Angle + + + TOP_MOVE + Move Node + + + TOP_NODE + Node + + + TOP_NODES + Nodes + + + TOP_NUM_ELEMENTS + Display Elements + + + TOP_NUM_NODES + Display Nodes + + + TOP_ORIENT + Orientation + + + TOP_POLYGON + Polygon + + + TOP_POLYHEDRON + Polyhedron + + + TOP_PRECISION + Precision + + + TOP_QUAD + Quadrangle + + + TOP_QUADRATIC_EDGE + Quadratic Edge + + + TOP_QUADRATIC_HEXAHEDRON + Quadratic Hexahedron + + + TOP_QUADRATIC_PENTAHEDRON + Quadratic Pentahedron + + + TOP_QUADRATIC_PYRAMID + Quadratic Pyramid + + + TOP_QUADRATIC_QUADRANGLE + Quadratic Quadrangle + + + TOP_QUADRATIC_TETRAHEDRON + Quadratic Tetrahedron + + + TOP_QUADRATIC_TRIANGLE + Quadratic Triangle + + + TOP_REMOVE_ELEMENTS + Remove elements + + + TOP_REMOVE_NODES + Remove nodes + + + TOP_REMOVE_ORPHAN_NODES + Remove orphan nodes + + + TOP_RENAME + Rename + + + TOP_RENUM_ELEMENTS + Renumbering elements + + + TOP_RENUM_NODES + Renumbering nodes + + + TOP_RESET + Reset + + + TOP_SAVE_DISTRIBUTION + Export distribution + + + TOP_SHOW_DISTRIBUTION + Show Distribution + + + TOP_REVOLUTION + Revolution + + + TOP_ROT + Rotation + + + TOP_SCALAR_BAR + Scalar bar + + + TOP_SCALAR_BAR_PROP + Scalar bar Properties + + + TOP_SELECTION + Selection + + + TOP_SEL_FILTER_LIB + Selection filters library + + + TOP_SEW + Sewing + + + TOP_SHADE + Shading + + + TOP_SHOW + Show + + + TOP_SHRINK + Shrink + + + TOP_SKEW + Skew + + + TOP_SMOOTH + Smoothing + + + TOP_STD_INFO + Standard Mesh Infos + + + TOP_SYM + Symmetry + + + TOP_TAPER + Taper + + + TOP_TETRA + Tetrahedron + + + TOP_TRANS + Translation + + + TOP_SCALE + Scale Transform + + + TOP_DUPLICATE_NODES + Duplicate Nodes + + + TOP_TRANSP + Transparency + + + TOP_TRIANGLE + Triangle + + + TOP_UNASSIGN + Unassign + + + TOP_UNION + Union of triangles + + + TOP_UNION2 + Union of two triangles + + + TOP_IMPORT_UNV + Import UNV file + + + TOP_UNV + Export UNV file + + + TOP_UN_GROUP + Union Groups + + + TOP_UNDERLYING_ELEMS + Create groups of entities from existing groups of superior dimensions + + + TOP_UPDATE + Update + + + TOP_VOLUMES + Volumes + + + TOP_VOLUME_3D + Volume + + + TOP_WARP + Warping angle + + + TOP_WHAT_IS + Mesh Element Information + + + TOP_WIRE + Wireframe + + + UNKNOWN_CONTROL + Unknown + + + VOLUME_3D_ELEMENTS + Volume + + + WARP_ELEMENTS + Warping + + + MEN_FILE_INFO + MED File Information + + + SMESH_WRN_NO_APPROPRIATE_SELECTION + No appropriate objects selected + + + MEN_CLEAR_MESH + Clear Mesh Data + + + TOP_CLEAR_MESH + Clear Mesh Data + + + STB_CLEAR_MESH + Clear Mesh Data + + + SMESH_IMPORT_MESH + Import mesh data from files + + + SMESH_ERR_NOT_SUPPORTED_FORMAT + Unsupported file format + + + SMESH_ERR_UNKNOWN_IMPORT_ERROR + Unknown error + + + SMESH_IMPORT_ERRORS + Import operation has finished with errors: + + + SMESH_DRS_SOME_EMPTY + One or more mesh files were empty, data has not been published + + + NO_MESH_SELECTED + No mesh selected + + + SMESH_PREF_def_precision + Default precision + + + SMESH_PREF_length_precision + Length precision + + + SMESH_PREF_angle_precision + Angular precision + + + SMESH_PREF_len_tol_precision + Length tolerance precision + + + SMESH_PREF_parametric_precision + Parametric precision + + + SMESH_PREF_area_precision + Area precision + + + FULL_RECOMPUTE_QUESTION + +The mesh has been edited since a last total re-compute +that may prevent successful computation. +Do you wish to re-compute the mesh totally to discard the modifications? - - - SMESH_ROTATION - Rotation - - - SMESH_ROTATION_TITLE - Rotation about an axis - - - SMESH_SCALARBAR - Scalar Bar - - - SMESH_SEGMENTS - Segments - - - SMESH_SELECTION - Selection - - - SMESH_SELECT_FROM - Select From - - - SMESH_SELECT_WHOLE_MESH - Select whole mesh, submesh or group - - - SMESH_SET_COLOR - Color group - - - SMESH_SEWING - Sewing - - - SMESH_SMOOTHING - Smoothing - - - SMESH_STANDARD_MESHINFO_TITLE - Standard Mesh Infos - - - SMESH_SUBMESH - SubMesh - - - SMESH_SUBMESH_SELECTED - %1 SubMeshes - - - SMESH_SYMMETRY - Symmetry - - - SMESH_TETRAS - Tetrahedron - - - SMESH_TITLE - Title: - - - SMESH_TOLERANCE - Tolerance - - - SMESH_TRANSLATION - Translation - - - SMESH_TRANSPARENCY_OPAQUE - ---> Opaque - - - SMESH_TRANSPARENCY_TITLE - Change Transparency - - - SMESH_TRANSPARENCY_TRANSPARENT - Transparent <--- - - - SMESH_TRIANGLE - Triangle - - - SMESH_UPDATEVIEW - Update View - - - SMESH_VALUE - Value - - - SMESH_VECTOR - Vector - - - SMESH_VERTICAL - Vertical - - - SMESH_VISU_PROBLEM - Mesh visualization failed, probably due to lack of memory - - - SMESH_VISU_PROBLEM_CLEAR - Mesh visualization failed, no memory even to show a message, -so all visual data have been removed to let the application live. -Consider saving your work before application crash - - - SMESH_VOLUME - Volume - - - SMESH_WARNING - Warning - - - SMESH_WHAT_IS_TITLE - Mesh Element Info - - - SMESH_WIDTH - Width: - - - SMESH_WRN_ALGORITHM_ALREADYEXIST - Algorithm already exists - - - SMESH_WRN_COMPUTE_FAILED - Mesh computation failed - - - SMESH_WRN_EMPTY_NAME - Empty name is not valid - - - SMESH_WRN_HYPOTHESIS_ALREADYEXIST - Hypothesis already exists - - - SMESH_WRN_HYPOTHESIS_NOTEXIST - Hypothesis or Algorithm not exists - - - SMESH_WRN_MISSING_PARAMETERS - Missing parameters - - - SMESH_WRN_NO_AVAILABLE_DATA - No available data in selection - - - SMESH_WRN_SELECTIONMODE_DIAGONAL - Activate Link Selection Mode - - - SMESH_WRN_SELECTIONMODE_ELEMENTS - Activate Elements Selection Mode - - - SMESH_WRN_SELECTIONMODE_NODES - Activate Nodes Selection Mode - - - SMESH_WRN_VIEWER_VTK - Study frame with VTK Viewer must be activated - - - SMESH_WRN_WARNING - Warning - - - SMESH_X - X - - - SMESH_X_SCALARBAR - X: - - - SMESH_Y - Y - - - SMESH_Y_SCALARBAR - Y: - - - SMESH_Z - Z - - - STATE_ALGO_MISSING - %3 %2D algorithm is missing - - - STATE_HYP_BAD_GEOMETRY - %3 %2D algorithm "%1" is assigned to geometry mismatching its expectation - - - STATE_HYP_BAD_PARAMETER - Hypothesis of %3 %2D algorithm "%1" has a bad parameter value - - - STATE_HYP_MISSING - %3 %2D algorithm "%1" misses %4D hypothesis - - - STATE_HYP_NOTCONFORM - %3 %2D algorithm "%1" would produce not conform mesh: global "Not Conform Mesh Allowed" hypotesis is missing - - - STB_ADV_INFO - Advanced Mesh Infos - - - STB_ALL - All - - - STB_AREA - Area - - - STB_ASPECT - Aspect Ratio - - - STB_ASPECT_3D - Aspect Ratio 3D - - - STB_AUTO_COLOR - Auto color - - - STB_AUTO_UPD - Automatic update - - - STB_BUILD_COMPOUND - Build Compound Mesh - - - STB_CLIP - Clipping - - - STB_COLORS - Colors / Size - - - STB_COMPUTE - Compute - - - STB_PRECOMPUTE - Preview - - - STB_CONNECTION - Borders at Multi-Connection - - - STB_CONNECTION_2D - Borders at Multi-Connection 2D - - - STB_CONSTRUCT_GROUP - Construct Group - - - STB_CONV_TO_QUAD - Convert to/from quadratic - - - STB_CREATE_GROUP - Create Group - - - STB_CREATE_GEO_GROUP - Create Groups from Geometry - - - STB_CREATE_MESH - Create Mesh - - - STB_CREATE_SUBMESH - Create Sub-mesh - - - STB_CUT - Cutting of quadrangles - - - STB_CUT_GROUP - Cut Groups - - - STB_DAT - Import DAT file - - - STB_DELETE - Delete - - - STB_DEL_GROUP - Delete Groups - - - STB_FACE_ORIENTATION - Orientation of faces - - - STB_DISABLE_AUTO_COLOR - Disable auto color - - - STB_DISPLAY_ONLY - Show only - - - STB_DISP_ENT - Display entity - - - STB_EDGE - Edge - - - STB_EDGES - Edges - - - STB_EDIT_GROUP - Edit Group - - - STB_EDIT_GEOMGROUP_AS_GROUP - Edit Group as Standalone - - - STB_EDIT_HYPO - Edit Hypothesis - - - STB_EDIT_MESHSUBMESH - Edit Mesh/Sub-mesh - - - STB_EXPORT_DAT - Export to DAT file - - - STB_EXPORT_MED - Export to MED file - - - STB_EXPORT_STL - Export to STL file - - - STB_EXPORT_UNV - Export to UNV file - - - STB_EXTRUSION - Extrusion - - - STB_EXTRUSION_ALONG - Extrusion along a path - - - STB_FACES - Faces - - - STB_FREE_BORDER - Free Borders - - - STB_FREE_EDGE - Free Edges - - - STB_FREE_NODE - Free Nodes - - - - - STB_FREE_FACES - Free Faces - - - STB_GLOBAL_HYPO - Global Hypothesis - - - STB_HEXA - Hexahedron - - - STB_HIDE - Hide - - - STB_INT_GROUP - Intersect Groups - - - STB_INV - Diagonal Inversion - - - STB_LENGTH - Length - - - STB_LENGTH_2D - Length 2D - - - STB_MAP - Pattern mapping - - - STB_MED - Import MED file - - - STB_MERGE - Merge nodes - - - STB_MERGE_ELEMENTS - Merge elements - - - STB_MESH_THROU_POINT - Mesh to pass through a point - - - STB_MIN_ANG - Minimum Angle - - - STB_MOVE - Move Node - - - STB_NODE - Node - - - STB_NODES - Nodes - - - STB_NUM_ELEMENTS - Display Elements - - - STB_NUM_NODES - Display Nodes - - - STB_ORIENT - Orientation - - - STB_POLYGON - Polygon - - - STB_POLYHEDRON - Polyhedron - - - STB_PRECISION - Precision - - - STB_QUAD - Quadrangle - - - STB_QUADRATIC_EDGE - Quadratic Edge - - - STB_QUADRATIC_HEXAHEDRON - Quadratic Hexahedron - - - STB_QUADRATIC_PENTAHEDRON - Quadratic Pentahedron - - - STB_QUADRATIC_PYRAMID - Quadratic Pyramid - - - STB_QUADRATIC_QUADRANGLE - Quadratic Quadrangle - - - STB_QUADRATIC_TETRAHEDRON - Quadratic Tetrahedron - - - STB_QUADRATIC_TRIANGLE - Quadratic Triangle - - - STB_REMOVE_ELEMENTS - Remove elements - - - STB_REMOVE_NODES - Remove nodes - - - STB_RENAME - Rename - - - STB_RENUM_ELEMENTS - Renumbering elements - - - STB_RENUM_NODES - Renumbering nodes - - - STB_RESET - Reset - - - STB_REVOLUTION - Revolution - - - STB_ROT - Rotation - - - STB_SCALAR_BAR - Scalar bar - - - STB_SCALAR_BAR_PROP - Scalar bar Properties - - - STB_SELECTION - Selection - - - STB_SEL_FILTER_LIB - Selection filters library - - - STB_SEW - Sewing - - - STB_SHADE - Shading - - - STB_SHOW - Show - - - STB_SHRINK - Shrink - - - STB_SKEW - Skew - - - STB_SMOOTH - Smoothing - - - STB_STD_INFO - Standard Mesh Infos - - - STB_SYM - Symmetry - - - STB_TAPER - Taper - - - STB_TETRA - Tetrahedron - - - STB_TRANS - Translation - - - STB_TRANSP - Transparency - - - STB_TRIANGLE - Triangle - - - STB_UNASSIGN - Unassign - - - STB_UNION - Union of triangles - - - STB_UNION2 - Union of two triangles - - - STB_UNV - Import UNV file - - - STB_UN_GROUP - Union Groups - - - STB_UNDERLYING_ELEMS - Create groups of entities from existing groups of superior dimensions - - - STB_UPDATE - Update - - - STB_VOLUMES - Volumes - - - STB_VOLUME_3D - Volume - - - STB_WARP - Warping angle - - - STB_WHAT_IS - Mesh Element Info - - - STB_WIRE - Wireframe - - - TAPER_ELEMENTS - Taper - - - TB_ADD_REMOVE - Add/Remove Toolbar - - - TB_CTRL - Controls Toolbar - - - TB_DISP_MODE - Display Mode Toolbar - - - TB_HYPO - Hypotheses Toolbar - - - TB_MESH - Mesh Toolbar - - - TB_MODIFY - Modification Toolbar - - - TOP_ADV_INFO - Advanced Mesh Infos - - - TOP_ALL - All - - - TOP_AREA - Area - - - TOP_ASPECT - Aspect Ratio - - - TOP_ASPECT_3D - Aspect Ratio 3D - - - TOP_AUTO_COLOR - Auto color - - - TOP_AUTO_UPD - Automatic update - - - TOP_BUILD_COMPOUND - Build Compound Mesh - - - TOP_CLIP - Clipping - - - TOP_COLORS - Colors / Size - - - TOP_COMPUTE - Compute - - - TOP_PRECOMPUTE - Preview - - - TOP_CONNECTION - Borders at Multi-Connection - - - TOP_CONNECTION_2D - Borders at Multi-Connection 2D - - - TOP_CONSTRUCT_GROUP - Construct Group - - - TOP_CONV_TO_QUAD - Convert to/from quadratic - - - TOP_CREATE_GROUP - Create Group - - - TOP_CREATE_GEO_GROUP - Create Groups from Geometry - - - TOP_CREATE_MESH - Create Mesh - - - TOP_CREATE_SUBMESH - Create Sub-mesh - - - TOP_CUT - Cutting of quadrangles - - - TOP_CUT_GROUP - Cut Groups - - - TOP_DAT - Import DAT file - - - TOP_DELETE - Delete - - - TOP_DEL_GROUP - Delete Groups - - - TOP_FACE_ORIENTATION - Orientation of faces - - - TOP_DISABLE_AUTO_COLOR - Disable auto color - - - TOP_DISPLAY_ONLY - Show only - - - TOP_DISP_ENT - Display entity - - - TOP_EDGE - Edge - - - TOP_EDGES - Edges - - - TOP_EDIT_GROUP - Edit Group - - - TOP_EDIT_GEOMGROUP_AS_GROUP - Edit Group as Standalone - - - TOP_EDIT_HYPO - Edit Hypothesis - - - TOP_EDIT_MESHSUBMESH - Edit Mesh/Sub-mesh - - - TOP_EXPORT_DAT - Export to DAT file - - - TOP_EXPORT_MED - Export to MED file - - - TOP_EXPORT_STL - Export to STL file - - - TOP_EXPORT_UNV - Export to UNV file - - - TOP_EXTRUSION - Extrusion - - - TOP_EXTRUSION_ALONG - Extrusion along a path - - - TOP_FACES - Faces - - - TOP_FREE_BORDER - Free Borders - - - TOP_FREE_EDGE - Free Edges - - - TOP_FREE_NODE - Free Nodes - - - - - TOP_FREE_FACES - Free Faces - - - TOP_GLOBAL_HYPO - Global Hypothesis - - - TOP_HEXA - Hexahedron - - - TOP_HIDE - Hide - - - TOP_INT_GROUP - Intersect Groups - - - TOP_INV - Diagonal Inversion - - - TOP_LENGTH - Length - - - TOP_LENGTH_2D - Length 2D - - - TOP_MAP - Pattern mapping - - - TOP_MED - Import MED file - - - TOP_MERGE - Merge nodes - - - TOP_MERGE_ELEMENTS - Merge elements - - - TOP_MESH_THROU_POINT - Mesh to pass through a point - - - TOP_MIN_ANG - Minimum Angle - - - TOP_MOVE - Move Node - - - TOP_NODE - Node - - - TOP_NODES - Nodes - - - TOP_NUM_ELEMENTS - Display Elements - - - TOP_NUM_NODES - Display Nodes - - - TOP_ORIENT - Orientation - - - TOP_POLYGON - Polygon - - - TOP_POLYHEDRON - Polyhedron - - - TOP_PRECISION - Precision - - - TOP_QUAD - Quadrangle - - - TOP_QUADRATIC_EDGE - Quadratic Edge - - - TOP_QUADRATIC_HEXAHEDRON - Quadratic Hexahedron - - - TOP_QUADRATIC_PENTAHEDRON - Quadratic Pentahedron - - - TOP_QUADRATIC_PYRAMID - Quadratic Pyramid - - - TOP_QUADRATIC_QUADRANGLE - Quadratic Quadrangle - - - TOP_QUADRATIC_TETRAHEDRON - Quadratic Tetrahedron - - - TOP_QUADRATIC_TRIANGLE - Quadratic Triangle - - - TOP_REMOVE_ELEMENTS - Remove elements - - - TOP_REMOVE_NODES - Remove nodes - - - TOP_RENAME - Rename - - - TOP_RENUM_ELEMENTS - Renumbering elements - - - TOP_RENUM_NODES - Renumbering nodes - - - TOP_RESET - Reset - - - TOP_REVOLUTION - Revolution - - - TOP_ROT - Rotation - - - TOP_SCALAR_BAR - Scalar bar - - - TOP_SCALAR_BAR_PROP - Scalar bar Properties - - - TOP_SELECTION - Selection - - - TOP_SEL_FILTER_LIB - Selection filters library - - - TOP_SEW - Sewing - - - TOP_SHADE - Shading - - - TOP_SHOW - Show - - - TOP_SHRINK - Shrink - - - TOP_SKEW - Skew - - - TOP_SMOOTH - Smoothing - - - TOP_STD_INFO - Standard Mesh Infos - - - TOP_SYM - Symmetry - - - TOP_TAPER - Taper - - - TOP_TETRA - Tetrahedron - - - TOP_TRANS - Translation - - - TOP_TRANSP - Transparency - - - TOP_TRIANGLE - Triangle - - - TOP_UNASSIGN - Unassign - - - TOP_UNION - Union of triangles - - - TOP_UNION2 - Union of two triangles - - - TOP_UNV - Import UNV file - - - TOP_UN_GROUP - Union Groups - - - TOP_UNDERLYING_ELEMS - Create groups of entities from existing groups of superior dimensions - - - TOP_UPDATE - Update - - - TOP_VOLUMES - Volumes - - - TOP_VOLUME_3D - Volume - - - TOP_WARP - Warping angle - - - TOP_WHAT_IS - Mesh Element Info - - - TOP_WIRE - Wireframe - - - VOLUME_3D_ELEMENTS - Area - - - WARP_ELEMENTS - Warping - - - MEN_FILE_INFO - MED File Information - - - SMESH_WRN_NO_APPROPRIATE_SELECTION - No appropriate objects selected - - - MEN_CLEAR_MESH - Clear Mesh Data - - - TOP_CLEAR_MESH - Clear Mesh Data - - - STB_CLEAR_MESH - Clear Mesh Data - - - SMESH_IMPORT_MESH - Import mesh data from files - - - SMESH_ERR_NOT_SUPPORTED_FORMAT - Unsupported file format - - - SMESH_ERR_UNKNOWN_IMPORT_ERROR - Unknown error - - - SMESH_IMPORT_ERRORS - Import operation has finished with errors: - - - SMESH_DRS_SOME_EMPTY - One or more mesh files were empty, data has not been published - - - - SMESHGUI - - NOT_A_VTK_VIEWER - This command is available in VTK viewer only + + + SMESH_PREF_vol_precision + Volume precision + + + SMESH_PRECISION_HINT + +Input value precision can be adjusted using +'%1' parameter in Mesh module preferences. + + + REMOVE_ORPHAN_NODES_QUESTION + Do you really want to remove all orphan nodes? + + + NB_NODES_REMOVED + Removed %1 node(s). + + + SMESH_SAVE_DISTRIBUTION + Export Distribution + + + SMESH_PLUGINS_OTHER + SMESH plugins + + + MESH_LOADING_MSG + Loading mesh %0 in progress, please wait... + + + MESH_LOADING_MSG_FINISHED + Mesh %0 loading done + + + BALL_DIAMETER + Diameter + + + BALL_ELEMENT + Ball + + + DEP_OBJECT + Selected object has been used to create another one. +It can't be deleted + + + + SMESHGUI_GroupDlg + + ALLOW_ELEM_LIST_MODIF + Enable manual edition + + + + SMESHGUI + + NOT_A_VTK_VIEWER + This command is available in VTK viewer only Please, create VTK viewer and try again - - - PREF_AUTO_GROUPS - Automatically create groups for MED export - - - PREF_GROUP_SEGMENT_LENGTH - Automatic parameters - - - PREF_SEGMENT_LENGTH - Ratio Bounding Box Diagonal / Max Size - - - PREF_NB_SEGMENTS - Default Number of Segments - - - PREF_AUTO_UPDATE - Automatic update - - - PREF_BACKFACE - Back face - - - PREF_COLOR - Color - - - PREF_ORIENTATION_COLOR - Color - - - PREF_ORIENTATION_3D_VECTORS - 3D vectors - - - PREF_ORIENTATION_SCALE - Scale - - - PREF_DISPLAY_ENTITY - Display entity - - - PREF_DISPLAY_MODE - Display mode - - - PREF_ELEMENTS - Elements - - - PREF_ELEMENT_COLOR - Element color - - - PREF_FILL - Fill - - - PREF_NOTIFY_MODE - Show a computation result notification - - - PREF_GROUP_ELEMENTS - Elements - - - PREF_GROUP_EXPORT - Mesh export - - - PREF_GROUP_FACES_ORIENTATION - Orientation of faces - - - PREF_GROUP_COMPUTE - Mesh computation - - - PREF_GROUP_NODES - Nodes - - - PREF_GROUP_PRECISION - Precision - - - PREF_GROUP_PRESELECTION - Preselection - - - PREF_GROUP_QUALITY - Quality controls - - - PREF_GROUP_SELECTION - Selection - - - PREF_GROUP_UPDATE - Update - - - PREF_HIGHLIGHT_COLOR - Highlight color - - - PREF_LABELS_COLOR - Labels color - - - PREF_NODES - Nodes - - - PREF_OBJECTS - Objects - - - PREF_OBJECT_COLOR - Object color - - - PREF_OUTLINE - Outline - - - PREF_PRECISION_USE - Use precision - - - PREF_PRECISION_VALUE - Number of digits after point - - - PREF_RENUMBER - Automatic renumbering - - - PREF_SHRINK_COEFF - Shrink coef. - - - PREF_SIZE - Size - - - PREF_TAB_GENERAL - General - - - PREF_TAB_MESH - Mesh - - - PREF_TAB_SELECTION - Selection - - - PREF_TITLE_COLOR - Title color - - - PREF_WIDTH - Width - - - - SMESHGUI_AddQuadraticElementDlg - - SMESH_ADD_QUADRATIC_EDGE - Add Quadratic Edge - - - SMESH_ADD_QUADRATIC_HEXAHEDRON - Add Quadratic Hexahedron - - - SMESH_ADD_QUADRATIC_PENTAHEDRON - Add Quadratic Pentahedron - - - SMESH_ADD_QUADRATIC_PYRAMID - Add Quadratic Pyramid - - - SMESH_ADD_QUADRATIC_QUADRANGLE - Add Quadratic Quadrangle - - - SMESH_ADD_QUADRATIC_TETRAHEDRON - Add Quadratic Tetrahedron - - - SMESH_ADD_QUADRATIC_TRIANGLE - Add Quadratic Triangle - - - SMESH_CORNER_NODES - Corner Nodes: - - - SMESH_FIRST - First - - - SMESH_LAST - Last - - - SMESH_MIDDLE - Middle - - - - SMESHGUI_BuildCompoundDlg - - COMPOUND - Compound - - - COMPOUND_MESH - Compound_Mesh - - - CREATE_COMMON_GROUPS - Create common groups for initial meshes - - - MERGE_NODES_AND_ELEMENTS - Merge coincident nodes and elements - - - MESHES - Meshes - - - PROCESSING_IDENTICAL_GROUPS - Processing identical groups - - - RENAME - Rename - - - RESULT_NAME - Result name - - - UNITE - Unite - - - - SMESHGUI_ChangeOrientationDlg - - CAPTION - Modification of orientation - - - - SMESHGUI_ComputeDlg - - CAPTION - Compute mesh failed - - - CONSTRUCTOR - Compute mesh - - - ERRORS - Errors - - - MEMORY_LACK - Memory allocation problem - - - PUBLISH_SHAPE - Publish SubShape - - - SHOW_SHAPE - Show SubShape - - - SHOW_BAD_MESH - Show bad Mesh - - - - SMESHGUI_PrecomputeDlg - - CAPTION - Preview and Compute mesh - - - PREVIEW - Preview - - - PREVIEW_1 - 1D Mesh - - - PREVIEW_2 - 2D Mesh - - - COMPUTE - Compute - - - - SMESHGUI_PrecomputeOp - - CLEAR_SUBMESH_QUESTION - A temporary submeshes on the selected geometry - created during preview operation. - Do you want to remove all this submeshes? - - - - SMESHGUI_ConvToQuadDlg - - CAPTION - Convert to/from quadratic - - - MEDIUMNDS - Medium nodes on geometry - - - MESH - Mesh - - - RADIOBTN_1 - Convert to quadratic - - - RADIOBTN_2 - Convert from quadratic - - - - SMESHGUI_ConvToQuadOp - - MESH_IS_NOT_SELECTED - Mesh is not selected + + + PREF_AUTO_GROUPS + Automatically create groups for MED export + + + PREF_GROUP_SEGMENT_LENGTH + Automatic parameters + + + PREF_SEGMENT_LENGTH + Ratio Bounding Box Diagonal / Max Size + + + PREF_NB_SEGMENTS + Default Number of Segments + + + SMESH_PREF_MESH_LOADING + Mesh loading + + + PREF_FORGET_MESH_AT_HYP_MODIF + No mesh loading from study file at hypothesis modification + + + PREF_AUTO_UPDATE + Automatic update + + + PREF_UPDATE_LIMIT + Size limit (elements) + + + PREF_UPDATE_LIMIT_NOLIMIT + No limit + + + PREF_BACKFACE + Back surface color + + + PREF_WIREFRAME + Wireframe color + + + PREF_SELECTION + Selection color + + + PREF_PRE_SELECTION + Pre-selection color + + + PREF_COLOR + Color + + + PREF_ORIENTATION_COLOR + Color + + + PREF_ORIENTATION_3D_VECTORS + 3D vectors + + + PREF_ORIENTATION_SCALE + Scale + + + PREF_DISPLAY_ENTITY + Display entity + + + QUADRATIC_REPRESENT_MODE + Representation of the 2D quadratic elements + + + MAX_ARC_ANGLE + Maximum angle + + + PREF_DISPLAY_MODE + Display mode + + + PREF_ELEMENTS + Elements + + + PREF_ELEMENT_COLOR + Element color + + + PREF_FILL + Surface color + + + PREF_NOTIFY_MODE + Show a computation result notification + + + PREF_NOTIFY_NEVER + Never + + + PREF_NOTIFY_ERROR + Errors only + + + PREF_NOTIFY_ALWAYS + Always + + + PREF_ELEM_INFO + Mesh element information + + + PREF_ELEM_INFO_SIMPLE + Simple + + + PREF_ELEM_INFO_TREE + Tree + + + PREF_GPP_NODES_LIMIT + Automatic nodes compute limit + + + SMESH_PREF_GROUP_PRECISION + Input fields precision + + + SMESH_PREF_GROUP_PREVIEW + Preview + + + PREF_GROUP_ELEMENTS + Elements + + + PREF_GROUP_EXPORT + Mesh export + + + PREF_GROUP_FACES_ORIENTATION + Orientation of faces + + + PREF_GROUP_COMPUTE + Mesh computation + + + PREF_GROUP_NODES + Nodes + + + PREF_GROUP_GROUPS + Groups + + + PREF_GRP_NAMES + Names color + + + PREF_GROUP_PRECISION + Precision + + + PREF_GROUP_PRESELECTION + Preselection + + + PREF_GROUP_QUALITY + Quality controls + + + PREF_GROUP_SELECTION + Selection + + + PREF_GROUP_INFO + Mesh information + + + PREF_HIGHLIGHT_COLOR + Highlight color + + + PREF_LABELS_COLOR + Labels color + + + PREF_MARKER_SCALE + Scale of marker + + + PREF_NODES + Nodes + + + PREF_OBJECTS + Objects + + + PREF_OBJECT_COLOR + Object color + + + PREF_OUTLINE + Outline color + + + PREF_PRECISION_USE + Use precision + + + PREF_PRECISION_VALUE + Number of digits after point + + + PREF_EQUAL_NODES_TOL + Double nodes tolerance + + + PREF_RENUMBER + Automatic renumbering + + + PREF_SHRINK_COEFF + Shrink coef. + + + PREF_PYTHON_DUMP + Python Dump + + + PREF_HISTORICAL_PYTHON_DUMP + Historical python dump + + + PREF_TAB_GENERAL + General + + + PREF_TAB_MESH + Mesh + + + PREF_TAB_SELECTION + Selection + + + PREF_TITLE_COLOR + Title color + + + PREF_TYPE_OF_MARKER + Type of marker + + + PREF_COLOR_0D + 0D elements + + + PREF_SIZE_0D + Size of 0D elements + + + PREF_BALL_COLOR + Balls + + + PREF_BALL_SIZE + Size of ball elements + + + PREF_WIDTH + Width + + + PREF_PREVIEW_CHUNK_SIZE + Sub-shapes preview chunk size + + + + SMESHGUI_AddQuadraticElementDlg + + SMESH_ADD_QUADRATIC_EDGE + Add Quadratic Edge + + + SMESH_ADD_QUADRATIC_HEXAHEDRON + Add Quadratic Hexahedron + + + SMESH_ADD_TRIQUADRATIC_HEXAHEDRON + Add TriQuadratic Hexahedron + + + SMESH_ADD_QUADRATIC_PENTAHEDRON + Add Quadratic Pentahedron + + + SMESH_ADD_QUADRATIC_PYRAMID + Add Quadratic Pyramid + + + SMESH_ADD_QUADRATIC_QUADRANGLE + Add Quadratic Quadrangle + + + SMESH_ADD_BIQUADRATIC_QUADRANGLE + Add BiQuadratic Quadrangle + + + SMESH_ADD_QUADRATIC_TETRAHEDRON + Add Quadratic Tetrahedron + + + SMESH_ADD_QUADRATIC_TRIANGLE + Add Quadratic Triangle + + + SMESH_CORNER_NODES + Corner Nodes: + + + SMESH_MIDFACE_NODES + Mid-face Nodes: + + + SMESH_CENTER_NODE + Center Node: + + + SMESH_FIRST + First + + + SMESH_LAST + Last + + + SMESH_MIDDLE + Middle + + + + SMESHGUI_BuildCompoundDlg + + COMPOUND + Compound + + + COMPOUND_MESH + Compound_Mesh + + + CREATE_COMMON_GROUPS + Create common groups for initial meshes + + + MERGE_NODES_AND_ELEMENTS + Merge coincident nodes and elements + + + MESHES + Meshes + + + PROCESSING_IDENTICAL_GROUPS + Processing identical groups + + + RENAME + Rename + + + RESULT_NAME + Result name + + + UNITE + Unite + + + + SMESHGUI_ChangeOrientationDlg + + CAPTION + Modification of orientation + + + + SMESHGUI_ComputeDlg + + CAPTION + Compute mesh failed + + + CONSTRUCTOR + Compute mesh + + + EVAL_DLG + Evaluate mesh + + + ERRORS + Errors + + + MEMORY_LACK + Memory allocation problem + + + COMPUTE_WARNING + The mesh seems to be OK but there are some errors reported + + + PUBLISH_SHAPE + Publish Sub-shape + + + SHOW_SHAPE + Show Sub-shape + + + SHOW_BAD_MESH + Show bad Mesh + + + + SMESHGUI_PrecomputeDlg + + CAPTION + Preview and Compute mesh + + + PREVIEW + Preview + + + PREVIEW_1 + 1D Mesh + + + PREVIEW_2 + 2D Mesh + + + COMPUTE + Compute + + + + SMESHGUI_PrecomputeOp + + CLEAR_SUBMESH_QUESTION + Temporary submeshes on the selected geometry +were created during preview operation. +Do you want to remove all these submeshes? + + + SMESH_WRN_NOTHING_PREVIEW + No mesh preview is available + + + SMESH_REJECT_MESH_ORDER + The submesh priority changed during preview operation. +Do you want to restore original submesh priority? + + + + SMESHGUI_ConvToQuadDlg + + CAPTION + Convert to/from quadratic + + + MEDIUMNDS + Medium nodes on geometry + + + MESH + Mesh or Sub-mesh + + + RADIOBTN_1 + Convert to quadratic + + + RADIOBTN_2 + Convert from quadratic + + + NON_CONFORM_WARNING + Warning: mesh can become non-conformal + + + + SMESHGUI_ConvToQuadOp + + MESH_IS_NOT_SELECTED + Mesh is not selected +Please specify it and try again + + + REF_IS_NULL + No valid mesh object selected + + + + SMESHGUI_CreatePatternDlg + + CAPTION + Pattern Creation + + + DEFAULT_2D + Pattern_2d + + + DEFAULT_3D + Pattern_3d + + + ERROR_OF_CREATION + Internal error occurs during pattern creation +Please verify validity of entered information + + + ERROR_OF_SAVING + Internal error occurs during pattern saving. Please verify +\free disk space and your write permission to this file + + + ERR_LOADF_CANT_PROJECT + Impossible to perform projection of nodes to the face + + + ERR_LOADF_CLOSED_FACE + It is impossible to create pattern from face having seam edge + + + ERR_LOADF_NARROW_FACE + It is impossible to create pattern from narrow face + + + ERR_LOADV_BAD_SHAPE + Pattern can be created from closed shell or solid with 6 faces only + + + ERR_LOADV_COMPUTE_PARAMS + It is impossible to compute point parameters + + + ERR_LOAD_EMPTY_SUBMESH + There are no elements to create pattern + + + MESH_OR_SUBMESH + Mesh or SubMesh + + + PATTERN + Pattern + + + PATTERN_FILT + Pattern files(*.smp) + + + PATTERN_NAME + Pattern name + + + PATTERN_TYPE + Pattern type + + + PROJECT + Project nodes on the face + + + SAVE + Save... + + + SAVE_PATTERN + Save Pattern + + + + SMESHGUI_CreatePolyhedralVolumeDlg + + FACES_BY_NODES + Faces by nodes + + + SMESH_POLYEDRE_CREATE_ERROR + Polyhedron creation error. + + + SMESH_POLYEDRE_PREVIEW + Polyhedron preview + + + + SMESHGUI_CuttingOfQuadsDlg + + CAPTION + Cutting of quadrangles + + + + SMESHGUI_DeleteGroupDlg + + CAPTION + Delete groups with contents + + + NO_SELECTED_GROUPS + There are no selected groups +Please select a group and try again + + + SELECTED_GROUPS + Selected groups + + + + SMESHGUI_MergeDlg + + COINCIDENT_ELEMENTS + Coincident elements + + + COINCIDENT_NODES + Coincident nodes + + + DETECT + Detect + + + EDIT_SELECTED_GROUP + Edit selected group + + + SELECT_ALL + Select all + + + EXCLUDE_GROUPS + Exclude Groups + + + + SMESHGUI_ExtrusionAlongPathDlg + + BAD_SHAPE_TYPE + The shape selected for the path is not edge + + + CANT_GET_TANGENT + Can't get tangent for one of the path nodes + + + EXTRUSION_0D + Extrusion of 0D elements + + + EXTRUSION_1D + Extrusion of 1D elements + + + EXTRUSION_2D + Extrusion of 2D elements + + + EXTRUSION_ALONG_PATH + Extrusion along a path + + + EXTR_BAD_STARTING_NODE + Wrong path starting node + + + LINEAR_ANGLES + Linear variation of the angles + + + NO_ELEMENTS_SELECTED + No mesh elements are selected for extrusion + + + SELECTED_PATH_IS_NOT_EDGE + Path mesh should be of edge type + + + SMESH_ANGLES + Rotation Angles + + + SMESH_BASE_POINT + Base Point + + + SMESH_PATH + Path + + + SMESH_PATH_MESH + Mesh or submesh + + + SMESH_PATH_SHAPE + Shape (edge) + + + SMESH_PATH_START + Start node + + + SMESH_USE_ANGLES + Use Angles + + + SMESH_USE_BASE_POINT + Use Base Point + + + WRONG_ANGLES_NUMBER + The number of angles should correspond to the number of path nodes + + + + SMESHGUI_ExtrusionDlg + + EXTRUSION_0D + Extrusion of nodes + + + EXTRUSION_1D + Extrusion of 1D elements + + + EXTRUSION_2D + Extrusion of 2D elements + + + EXTRUSION_ALONG_LINE + Extrusion along a line + + + + SMESHGUI_FilterDlg + + BAD_SHAPE_NAME + There is no "%1" geometrical object in the current study +Please select valid object and try again + + + CURRENT_DIALOG + Current Group + + + EDGES_TLT + Filter for Edges + + + FACES_TLT + Filter for Faces + + + MESH + Mesh + + + NODES_TLT + Filter for Nodes + + + SELECTION + Initial Selection + + + SET_IN_VIEWER + Insert filter in viewer + + + SHAPE_IS_NOT_A_CYLINDER + "%1" is not a cylinderical face +Please select a cylindrical face and try again + + + SHAPE_IS_NOT_A_FACE + "%1" is not a face +Please select a face and try again + + + SHAPE_IS_NOT_A_PLANE + "%1" is not a plane +Please select a plane and try again + + + FACE_ID_NOT_SELECTED + Mesh face is not selected +Please specify it and try again + + + NOT_FACE_ID + "%1" is not an ID of a mesh face. +Please select a face and try again + + + SOURCE + Source + + + TLT + Selection filter + + + VOLUMES_TLT + Filter for Volumes + + + + SMESHGUI_FilterLibraryDlg + + ADD + Add + + + ADD_TO_TLT + Add selection filter to library + + + ALL_FILES_FILTER + All Files (*.*) + + + ASSIGN_NEW_NAME + Library already contains filter with name "%1" +New name "%2" is assigned to added filter + + + COPY_FROM_TLT + Copy selection filter from library + + + DELETE + Delete + + + EDGE + Edge + + + EDIT_LIB_TLT + Selection filter library + + + ELEMENT + Element + + + EMPTY_FILTER_NAME + Name of the filter is empty +Please enter a non-empty name + + + ERROR_FILTER_NAME + Name of the filter is not unique +Please enter other name + + + ERROR_LOAD + It is impossible to load library +Please check library file name and attributes + + + ERROR_OF_ADDING + Internal error occurs during adiing new filter in library. +Please verify validity of entered information + + + ERROR_OF_COPYING + Internal error occurs during copying filter from library. +Please verify validity of entered information + + + ERROR_OF_DELETING + Internal error occurs during deleting filter from library. +Please verify validity of entered information + + + ERROR_OF_EDITING + Internal error occurs during editing filter library. +Please verify validity of entered information + + + ERROR_OF_SAVING + Internal error occurs during saving filter library +Please check input data and try again + + + FACE + Face + + + FILTER + Filter + + + FILTER_NAME + Filter name + + + FILTER_NAMES + Names of filters + + + LIBRARY_FILE + Library file name + + + LIBRARY_IS_NOT_LOADED + Library is not loaded. Please load library and try again + + + LIB_NAME + FilterLib.xml + + + NODE + Node + + + NO_PERMISSION + You do not have write permission to this file + + + OPEN_LIBRARY + Open library + + + SELECTION + Selection + + + VOLUME + Volume + + + XML_FILT + XML files(*.xml) + + + + SMESHGUI_FilterTable + + ADD + Add + + + ADDITIONAL_PARAMETERS + Additional parameters + + + ADD_TO + Add to... + + + AND + And + + + AREA + Area + + + ASPECT_RATIO + Aspect ratio + + + ASPECT_RATIO_3D + Aspect ratio 3D + + + BAD_ORIENTED_VOLUME + Bad oriented volume + + + BARE_BORDER_VOLUME + Volumes with bare border + + + BARE_BORDER_FACE + Faces with bare border + + + OVER_CONSTRAINED_VOLUME + Over-constrained volumes + + + OVER_CONSTRAINED_FACE + Over-constrained faces + + + BELONG_TO_CYLINDER + Belong to Cylinder + + + BELONG_TO_GENSURFACE + Belong to Surface + + + BELONG_TO_GEOM + Belong to Geom + + + BELONG_TO_PLANE + Belong to Plane + + + BINARY + Binary + + + CLEAR + Clear + + + COMPARE + Compare + + + COPLANAR_FACES + Coplanar faces + + + COPY_FROM + Copy from... + + + CRITERION + Criterion + + + BALLS + Balls + + + EDGES + Edges + + + ENTITY_TYPE + Entity type + + + EQUAL_TO + Equal to + + + ERROR + Threshold value is not correctly specified +Please enter correct value and try again + + + FACES + Faces + + + FILTER + Filter + + + FREE_BORDERS + Free borders + + + FREE_EDGES + Free edges + + + FREE_NODES + Free nodes + + + FREE_FACES + Free faces + + + ID + ID + + + INSERT + Insert + + + LENGTH + Length + + + LENGTH2D + Length 2D + + + LESS_THAN + Less than + + + LYING_ON_GEOM + Lying on Geom + + + MAX_ELEMENT_LENGTH_2D + Element Diameter 2D + + + MAX_ELEMENT_LENGTH_3D + Element Diameter 3D + + + MINIMUM_ANGLE + Minimum angle + + + MORE_THAN + More than + + + MULTIEDGES_ERROR + Threshold value of borders at multi-connections can not be equal 1 +Please enter correct value and try again + + + GROUPCOLOR_ERROR + Color of group can not be undefied +Please enter correct value and try again + + + MULTI_BORDERS + Borders at multi-connections + + + NODES + Nodes + + + NOT + Not + + + OR + Or + + + RANGE_OF_IDS + Range of IDs + + + REMOVE + Remove + + + SKEW + Skew + + + TAPER + Taper + + + THRESHOLD_VALUE + Threshold value + + + UNARY + Unary + + + VOLUMES + Volumes + + + VOLUME_3D + Volume + + + WARPING + Warping + + + LINEAR + Linear + + + GROUP_COLOR + Color of Group + + + ELEMENTS + Elements + + + GEOM_TYPE + Geometry type + + + GEOM_TYPE_0 + Point + + + GEOM_TYPE_1 + Edge + + + GEOM_TYPE_2 + Triangle + + + GEOM_TYPE_3 + Quadrangle + + + GEOM_TYPE_4 + Polygon + + + GEOM_TYPE_5 + Tetrahedron + + + GEOM_TYPE_6 + Pyramid + + + GEOM_TYPE_7 + Hexahedron + + + GEOM_TYPE_8 + Pentahedron + + + GEOM_TYPE_9 + Hexagonal prism + + + GEOM_TYPE_10 + Polyhedra + + + GEOM_TYPE_11 + Ball + + + + SMESHGUI_GroupOpDlg + + ARGUMENTS + Arguments + + + DIFF_MESHES + Arguments of operation are not correctly specified +Groups correspond to a different meshes +Please specify valid arguments and try again + + + DIFF_TYPES + Arguments of operation are not correctly specified +Groups contain elements of different types +Please specify valid arguments and try again + + + EMPTY_NAME + Name of group to be created is not valid +Please specify non-empty name and try again + + + INCORRECT_ARGUMENTS + Arguments of operation are not specified +Please specify them and try again + + + NAME + Name + + + OBJECT_1 + Object 1 + + + OBJECT_2 + Object 2 + + + RESULT_NAME + Result name + + + TOOL_OBJECT + Tool object + + + UNION_OF_TWO_GROUPS + Union of two groups + + + + SMESHGUI_GroupDlg + + SELECT_ALL + Select All + + + + SMESHGUI_UnionGroupsDlg + + UNION_OF_GROUPS + Union of groups + + + + SMESHGUI_DimGroupDlg + + CREATE_GROUP_OF_UNDERLYING_ELEMS + Create group of underlying entities + + + ELEMENTS_TYPE + Elements type + + + NODE + Node + + + EDGE + Edge + + + FACE + Face + + + VOLUME + Volume + + + + SMESHGUI_IntersectGroupsDlg + + INTERSECTION_OF_GROUPS + Intersection of groups + + + + SMESHGUI_CutGroupsDlg + + CUT_OF_GROUPS + Cut of groups + + + MAIN_OBJECT + Main object + + + TOOL_OBJECT + Tool object + + + + SMESHGUI_MakeNodeAtPointDlg + + AUTO_SEARCH + Find closest to destination + + + CAPTION + Move node + + + DESTINATION + Destination + + + MOVE_NODE + Move node + + + METHOD + Method + + + NODE_2MOVE + Node to move + + + NODE_2MOVE_ID + ID + + + + SMESHGUI_MakeNodeAtPointOp + + INVALID_ID + Node ID is invalid + + + INVALID_MESH + Mesh to modify not selected + + + + SMESHGUI_FindElemByPointDlg + + MESH_GROUP + Mesh or group + + + CAPTION + Find Element by Point + + + CREATE_NEW_METHOD + Create a node + + + MESH_PASS_THROUGH_POINT + Make a node at point + + + METHOD + Method + + + MOVE_EXISTING_METHOD + Move a node + + + NODE_2MOVE + Node to move + + + NODE_2MOVE_ID + ID + + + + SMESHGUI_MeshDlg + + CREATE_MESH + Create mesh + + + CREATE_SUBMESH + Create sub-mesh + + + DIM_0D + 0D + + + DIM_1D + 1D + + + DIM_2D + 2D + + + DIM_3D + 3D + + + EDIT_MESH_SUBMESH + Edit mesh/sub-mesh + + + GEOMETRY + Geometry + + + HYPOTHESES_SETS + Assign a set of hypotheses + + + MESH + Mesh + + + NAME + Name + + + + SMESHGUI_MeshOp + + ALGORITHM_WITHOUT_HYPOTHESIS + Algorithm is defined for %1 dimension but hypothesis is not defined + + + EDIT_SUBMESH_QUESTION + A submesh on the selected geometry already exists. + Do you want to edit this submesh? + + + SUBMESH_NOT_ALLOWED + No sense in creating a submesh ignored by global algorithm "%1" + + + GEOMETRY_OBJECT_IS_NOT_DEFINED + Geometry object is not defined Please specify it and try again - - - REF_IS_NULL - No valid mesh object selected - - - - SMESHGUI_CreatePatternDlg - - CAPTION - Pattern Creation - - - DEFAULT_2D - Pattern_2d - - - DEFAULT_3D - Pattern_3d - - - ERROR_OF_CREATION - Internal error occurs during pattern creation -Please verify validity of entered information - - - ERROR_OF_SAVING - Internal error occurs during pattern saving. Please verify -\free disk space and your write permission to this file - - - ERR_LOADF_CANT_PROJECT - Impossible to perform projection of nodes to the face - - - ERR_LOADF_CLOSED_FACE - It is impossible to create pattern from face having seam edge - - - ERR_LOADF_NARROW_FACE - It is impossible to create pattern from narrow face - - - ERR_LOADV_BAD_SHAPE - Pattern can be created from closed shell or solid with 6 faces only - - - ERR_LOADV_COMPUTE_PARAMS - It is impossible to compute point parameters - - - ERR_LOAD_EMPTY_SUBMESH - There are no elements to create pattern - - - MESH_OR_SUBMESH - Mesh or SubMesh - - - PATTERN - Pattern - - - PATTERN_FILT - Pattern files(*.smp) - - - PATTERN_NAME - Pattern name - - - PATTERN_TYPE - Pattern type - - - PROJECT - Project nodes on the face - - - SAVE - Save... - - - SAVE_PATTERN - Save Pattern - - - - SMESHGUI_CreatePolyhedralVolumeDlg - - FACES_BY_NODES - Faces by nodes - - - SMESH_POLYEDRE_CREATE_ERROR - Polyhedron creation error. - - - SMESH_POLYEDRE_PREVIEW - Polyhedron preview - - - - SMESHGUI_CuttingOfQuadsDlg - - CAPTION - Cutting of quadrangles - - - - SMESHGUI_DeleteGroupDlg - - CAPTION - Delete groups with contents - - - NO_SELECTED_GROUPS - There are no selected groups -Please select a groups and try again - - - SELECTED_GROUPS - Selected groups - - - - SMESHGUI_EditMeshDlg - - COINCIDENT_ELEMENTS - Coincident elements - - - COINCIDENT_NODES - Coincident nodes - - - DETECT - Detect - - - EDIT_SELECTED_GROUP - Edit selected group - - - SELECT_ALL - Select all - - - - SMESHGUI_ExtrusionAlongPathDlg - - BAD_SHAPE_TYPE - The shape selected for the path is not edge - - - CANT_GET_TANGENT - Can't get tangent for one of the path nodes - - - EXTRUSION_1D - Extrusion of 1D elements - - - EXTRUSION_2D - Extrusion of 2D elements - - - EXTRUSION_ALONG_PATH - Extrusion along a path - - - EXTR_BAD_STARTING_NODE - Wrong path starting node - - - LINEAR_ANGLES - Linear variation of the angles - - - NO_ELEMENTS_SELECTED - No mesh elements are selected for extrusion - - - SELECTED_PATH_IS_NOT_EDGE - Path mesh should be of edge type - - - SMESH_ANGLES - Rotation Angles - - - SMESH_BASE_POINT - Base Point - - - SMESH_PATH - Path - - - SMESH_PATH_MESH - Mesh - - - SMESH_PATH_SHAPE - Shape (edge) - - - SMESH_PATH_START - Start node - - - SMESH_USE_ANGLES - Use Angles - - - SMESH_USE_BASE_POINT - Use Base Point - - - WRONG_ANGLES_NUMBER - The number of angles should correspond to the number of path nodes - - - - SMESHGUI_ExtrusionDlg - - EXTRUSION_1D - Extrusion of 1D elements - - - EXTRUSION_2D - Extrusion of 2D elements - - - EXTRUSION_ALONG_LINE - Extrusion along a line - - - - SMESHGUI_FilterDlg - - BAD_SHAPE_NAME - There is no "%1" geometrical object in the current study -Please select valid object and try again - - - CURRENT_DIALOG - Current Dialog - - - EDGES_TLT - Filter for Edges - - - FACES_TLT - Filter for Faces - - - MESH - Mesh - - - NODES_TLT - Filter for Nodes - - - SELECTION - Initial Selection - - - SET_IN_VIEWER - Insert filter in viewer - - - SHAPE_IS_NOT_A_CYLINDER - "%1" is not a cylinderical face -Please select a cylindrical face and try again - - - SHAPE_IS_NOT_A_FACE - "%1" is not a face -Please select a face and try again - - - SHAPE_IS_NOT_A_PLANE - "%1" is not a plane -Please select a plane and try again - - - SOURCE - Source - - - TLT - Selection filter - - - VOLUMES_TLT - Filter for Volumes - - - - SMESHGUI_FilterLibraryDlg - - ADD - Add - - - ADD_TO_TLT - Add selection filter to library - - - ALL_FILES_FILTER - All Files (*.*) - - - ASSIGN_NEW_NAME - Library already contains filter with name "%1" -New name "%2" is assigned to added filter - - - COPY_FROM_TLT - Copy selection filter from library - - - DELETE - Delete - - - EDGE - Edge - - - EDIT_LIB_TLT - Selection filter library - - - ELEMENT - Element - - - EMPTY_FILTER_NAME - Name of the filter is empty -Please enter a non-empty name - - - ERROR_FILTER_NAME - Name of the filter is not unique -Please enter other name - - - ERROR_LOAD - It is impossible to load library -Please check library file name and attributes - - - ERROR_OF_ADDING - Internal error occurs during adiing new filter in library. -Please verify validity of entered information - - - ERROR_OF_COPYING - Internal error occurs during copying filter from library. -Please verify validity of entered information - - - ERROR_OF_DELETING - Internal error occurs during deleting filter from library. -Please verify validity of entered information - - - ERROR_OF_EDITING - Internal error occurs during editing filter library. -Please verify validity of entered information - - - ERROR_OF_SAVING - Internal error occurs during saving filter library -Please check input data and try again - - - FACE - Face - - - FILTER - Filter - - - FILTER_NAME - Filter name - - - FILTER_NAMES - Names of filters - - - LIBRARY_FILE - Library file name - - - LIBRARY_IS_NOT_LOADED - Library is not loaded. Please load library and try again - - - LIB_NAME - FilterLib.xml - - - NODE - Node - - - NO_PERMISSION - You do not have write permission to this file - - - OPEN_LIBRARY - Open library - - - SELECTION - Selection - - - VOLUME - Volume - - - XML_FILT - XML files(*.xml) - - - - SMESHGUI_FilterTable - - ADD - Add - - - ADDITIONAL_PARAMETERS - Additional parameters - - - ADD_TO - Add to... - - - AND - And - - - AREA - Area - - - ASPECT_RATIO - Aspect ratio - - - ASPECT_RATIO_3D - Aspect ratio 3D - - - BAD_ORIENTED_VOLUME - Bad oriented volume - - - BELONG_TO_CYLINDER - Belong to Cylinder - - - BELONG_TO_GENSURFACE - Belong to Surface - - - BELONG_TO_GEOM - Belong to Geom - - - BELONG_TO_PLANE - Belong to Plane - - - BINARY - Binary - - - CLEAR - Clear - - - COMPARE - Compare - - - COPY_FROM - Copy from... - - - CRITERION - Criterion - - - EDGES - Edges - - - ENTITY_TYPE - Entity type - - - EQUAL_TO - Equal to - - - ERROR - Threshold value is not correctly specified -Please enter correct value and try again - - - FACES - Faces - - - FILTER - Filter - - - FREE_BORDERS - Free borders - - - FREE_EDGES - Free edges - - - FREE_NODES - Free nodes - - - - - FREE_FACES - Free faces - - - ID - ID - - - INSERT - Insert - - - LENGTH - Length - - - LENGTH2D - Length 2D - - - LESS_THAN - Less than - - - LYING_ON_GEOM - Lying on Geom - - - MINIMUM_ANGLE - Minimum angle - - - MORE_THAN - More than - - - MULTIEDGES_ERROR - Threshold value of borders at multi-connections can not be equal 1 -Please enter correct value and try again - - - GROUPCOLOR_ERROR - Color of group can not be undefied -Please enter correct value and try again - - - MULTI_BORDERS - Borders at multi-connections - - - NODES - Nodes - - - NOT - Not - - - OR - Or - - - RANGE_OF_IDS - Range of IDs - - - REMOVE - Remove - - - SKEW - Skew - - - TAPER - Taper - - - THRESHOLD_VALUE - Threshold value - - - UNARY - Unary - - - VOLUMES - Volumes - - - VOLUME_3D - Volume - - - WARPING - Warping - - - LINEAR - Linear - - - GROUP_COLOR - Color of Group - - - ELEMENTS - Elements - - - GEOM_TYPE - Geometry type - - - GEOM_TYPE_0 - - - - GEOM_TYPE_0 - Point - - - GEOM_TYPE_1 - Edge - - - GEOM_TYPE_2 - Triangle - - - GEOM_TYPE_3 - Quadrangle - - - GEOM_TYPE_4 - Polygon - - - GEOM_TYPE_5 - Tetrahedron - - - GEOM_TYPE_6 - Pyramid - - - GEOM_TYPE_7 - Hexahedron - - - GEOM_TYPE_8 - Pentahedron - - - GEOM_TYPE_9 - Polyhedra - - - - SMESHGUI_GroupOpDlg - - ARGUMENTS - Arguments - - - DIFF_MESHES - Arguments of operation are not correctly specified -Groups correspond to a different meshes -Please specify valid arguments and try again - - - DIFF_TYPES - Arguments of operation are not correctly specified -Groups contain elements of different types -Please specify valid arguments and try again - - - EMPTY_NAME - Name of group to be created is not valid -Please specify non-empty name and try again - - - INCORRECT_ARGUMENTS - Arguments of operation are not specified -Please specify them and try again - - - NAME - Name - - - OBJECT_1 - Object 1 - - - OBJECT_2 - Object 2 - - - RESULT_NAME - Result name - - - TOOL_OBJECT - Tool object - - - UNION_OF_TWO_GROUPS - Union of two groups - - - - SMESHGUI_UnionGroupsDlg - - UNION_OF_GROUPS - Union of groups - - - - SMESHGUI_DimGroupDlg - - CREATE_GROUP_OF_UNDERLYING_ELEMS - Create group of underlying entities - + + + GEOMETRY_OBJECT_IS_NULL + Geometry object is null + + + HYPOTHESES_AND_ALGORITHMS_ARE_NOT_DEFINED + Hypotheses and algorithms are not defined + + + HYPOTHESIS_WITHOUT_ALGORITHM + Hypothesis is defined for %1 dimension but algorithm is not defined + + + IMPORTED_MESH + Mesh is not built on geometry + + + INVALID_SUBSHAPE + Geometry object is not a sub-shape of the shape to mesh + + + MESH_IS_NOT_DEFINED + Mesh is not defined +Please specify it and try again + + + MESH_IS_NULL + Mesh is null + + + NAME_OF_MESH_IS_EMPTY + Name of mesh is empty +Please enter valid name and try again + + + NAME_OF_SUBMESH_IS_EMPTY + Name of submesh is empty +Please enter valid name and try again + + + THERE_IS_NO_OBJECT_FOR_EDITING + There is no object for editing. Please +select mesh or sub-mesh and try again + + + + SMESHGUI_MeshPatternDlg + + 3D_BLOCK + 3D block + + + CAPTION + Pattern Mapping + + + CREATE_POLYEDRS_NEAR_BOUNDARY + Create polyhedrons near boundary + + + CREATE_POLYGONS_NEAR_BOUNDARY + Create polygons near boundary + + + ERROR_OF_LOADING + Loading of pattern from file failed. Probably file +is corrupted or contains pattern of the other type + + + ERROR_OF_OPENING + It is impossible to open file. Please verify whether +file exists and your permission to this file + + + ERROR_OF_READING + It is impossible to load pattern +Please verify file's contents + + + ERR_READ_3D_COORD + It is impossible to load pattern +Coordinates of 3D points out of [0,1] range + + + ERR_READ_BAD_INDEX + It is impossible to load pattern +Invalid index of point detected + + + ERR_READ_BAD_KEY_POINT + It is impossible to load pattern +Key-point not on a boundary + + + ERR_READ_ELEM_POINTS + It is impossible to load pattern +invalid number of points in element + + + ERR_READ_NB_POINTS + It is impossible to load pattern +It is impossible to read number of points from file + + + ERR_READ_NO_ELEMS + It is impossible to load pattern +There are no elements in it + + + ERR_READ_NO_KEYPOINT + It is impossible to load pattern +There are no key-points in 2D one + + + ERR_READ_POINT_COORDS + It is impossible to load pattern +It is impossible to read point coordinates from file + + + ERR_READ_TOO_FEW_POINTS + It is impossible to load pattern. There are + too few points in the file for pattern loading + + + FACE + Face + + + LOAD_PATTERN + Load pattern + + + MESH_FACES + Mesh faces + + + MESH_VOLUMES + Mesh volumes + + + NEW + New... + + + NODE_1 + Node 1 + + + NODE_2 + Node 2 + + + PATTERN + Pattern + + + PATTERN_FILT + Pattern files(*.smp) + + + PATTERN_TYPE + Pattern type + + + PREVIEW + Preview + + + REFINE + Refine selected mesh elements + + + REVERSE + Reverse order of key-points + + + VERTEX + Vertex + + + VERTEX1 + Vertex 1 + + + VERTEX2 + Vertex 2 + + + + SMESHGUI_MeshTab + + ADD_HYPOTHESIS + Add. Hypothesis + + + ALGORITHM + Algorithm + + + HYPOTHESIS + Hypothesis + + + NONE + <None> + + + + SMESHGUI_MultiEditDlg + + ADD + Add + + + FILTER + Filter + + + REMOVE + Remove + + + SELECT_FROM + Select from + + + SORT_LIST + Sort list + + + SPLIT_JOIN_CRITERION + Criterion + + + TO_ALL + Apply to all + + + USE_DIAGONAL_1_3 + Use diagonal 1-3 + + + USE_DIAGONAL_2_4 + Use diagonal 2-4 + + + USE_NUMERIC_FUNC + Use numeric functor + + + + SMESHGUI_CuttingIntoTetraDlg + + CAPTION + Splitting volumes into tetrahedra + + + SPLIT_METHOD + Split hexahedron + + + SPLIT_HEX_TO_5_TETRA + Into 5 tetrahedra + + + SPLIT_HEX_TO_6_TETRA + Into 6 tetrahedra + + + SPLIT_HEX_TO_24_TETRA + Into 24 tetrahedra + + + + SMESHGUI_PrecisionDlg + + CAPTION + Precision for mesh quality controls + + + NOT_USE + Do not use + + + PRECISION + Number of digits after point + + + + SMESHGUI_RevolutionDlg + + ANGLE_BY_STEP + Angle by Step + + + PREVIEW + Preview + + + REVOLUTION_1D + Revolution of 1D elements + + + REVOLUTION_2D + Revolution of 2D elements + + + REVOLUTION_AROUND_AXIS + Revolution around an axis + + + TOTAL_ANGLE + Total Angle + + + MEN_POINT_SELECT + From Origin to selected Point + + + MEN_FACE_SELECT + Normal to selected Face + + + + SMESHGUI_SewingDlg + + BORDER + Border + + + BORDER_1 + Border 1 + + + BORDER_2 + Border 2 + + + CREATE_POLYEDRS_NEAR_BOUNDARY + Replace affected volumes by polyedres + + + CREATE_POLYGONS_INSTEAD_SPLITTING + Create polygons instead of splitting + + + ERROR_1 + Free Border1 not found by the selected nodes + + + ERROR_2 + Free Border2 not found by the selected nodes + + + ERROR_3 + Free Border1 and Border2 not found by the selected nodes + + + ERROR_4 + No path from the first side node to the last side node have been found + + + ERROR_5 + Not allowed to splite volumes on the side! + + + ERROR_6 + Different number of elements selected on the sides + + + ERROR_7 + Element sets are topologically different or given nodes are inconvenient + + + ERROR_8 + Nodes on the side 1 are either not linked or not laying on the element set boundary + + + ERROR_9 + Nodes on the side 2 are either not linked or not laying on the element set boundary + + + FIRST_NODE_ID + First Node ID + + + LAST_NODE_ID + Last Node ID + + + MERGE_EQUAL_ELEMENTS + Merge equal elements + + + NODE1_TO_MERGE + Node 1 To Merge + + + NODE2_TO_MERGE + Node 2 To Merge + + + SECOND_NODE_ID + Second Node ID + + + SEW_BORDER_TO_SIDE + Sew Border To Side + + + SEW_CONFORM_FREE_BORDERS + Sew Conform Free Borders + + + SEW_FREE_BORDERS + Sew Free Borders + + + SEW_SIDE_ELEMENTS + Sew Side Elements + + + SIDE + Side + + + SIDE_1 + Side 1 + + + SIDE_2 + Side 2 + + + + SMESHGUI_ShapeByMeshDlg + + CAPTION + Find geometry by mesh element + + + + SMESHGUI_SingleEditDlg + + EDGE_BETWEEN + Edge between neighboring triangles + + + + SMESHGUI_SmoothingDlg + + CENTROIDAL + Centroidal + + + FIXED_NODES_IDS + Fixed nodes ids + + + IS_PARAMETRIC + in parametric space + + + ITERATION_LIMIT + Iteration limit + + + LAPLACIAN + Laplacian + + + MAX_ASPECT_RATIO + Max. aspect ratio + + + METHOD + Method + + + + SMESHGUI_TrianglesInversionDlg + + CAPTION + Diagonal inversion + + + + SMESHGUI_UnionOfTrianglesDlg + + CAPTION + Union of triangles + + + MAXIMUM_ANGLE + Maximum bending angle + + + + SMESHGUI_UnionOfTwoTrianglesDlg + + CAPTION + Union of two triangles + + + + SMESHGUI_FileInfoDlg + + CAPTION + File information + + + FILE_NAME + File name + + + FILE_SIZE + File size (bytes) + + + MED_VERSION + MED version + + + + SMESHGUI_GroupOnShapeDlg + + SMESH_CREATE_GROUP_FROM_GEOM + Create Groups from Geometry + + + + SMESHGUI_MeshOrderDlg + + SMESH_MESHORDER_TITLE + Order of submesh in meshing process + + + + SMESHGUI_MeshOrderOp + + SMESH_NO_CONCURENT_MESH + No concurent submeshes detected + + + + SMESHGUI_ClippingDlg + + CLIP_PLANES + Clipping planes + + + MESHES_SUBMESHES_GROUPS + Meshes, sub-meshes and groups + + + SELECT_ALL + Select all + + + ROTATION_AROUND_X_Y2Z + Rotation around X (Y to Z): + + + ROTATION_AROUND_Y_X2Z + Rotation around Y (X to Z): + + + ROTATION_AROUND_Z_Y2X + Rotation around Z (Y to X): + + + ROTATION_AROUND_X_Z2Y + Rotation around X (Z to Y): + + + ROTATION_AROUND_Y_Z2X + Rotation around Y (Z to X): + + + ROTATION_AROUND_Z_X2Y + Rotation around Z (X to Y): + + + SHOW_PREVIEW + Show preview + + + AUTO_APPLY + Auto Apply + + + ALONG_XY + || X-Y + + + ALONG_YZ + || Y-Z + + + ALONG_ZX + || Z-X + + + PLANE_NUM + Plane# %1 + + + NO_PLANES + No planes + + + + SMESHGUI_DuplicateNodesDlg + + DUPLICATION_MODE + Duplication mode + + + DUPLICATION_WITHOUT_ELEMS + Without duplication of border elements + + + GROUP_NODES_TO_DUPLICATE + Group of nodes to duplicate + + + GROUP_NODES_TO_REPLACE + Group of elements to replace nodes with new ones + + + DUPLICATION_WITH_ELEMS + With duplication of border elements + + + GROUP_ELEMS_TO_DUPLICATE + Group of elements to duplicate + + + GROUP_NODES_NOT_DUPLICATE + Group of nodes not to duplicate + + + GROUP_ELEMS_TO_REPLACE + Group of elements to replace nodes with new ones + + + CONSTRUCT_NEW_GROUP_NODES + Construct group with newly created nodes + + + CONSTRUCT_NEW_GROUP_ELEMENTS + Construct group with newly created elements + + + + SMESHGUI_Make2DFrom3DDlg + + CAPTION + Create boundary elements + + + Groups + Mesh or groups + + + MODE + Mode + + + 2D_FROM_3D + 2D from 3D + + + 1D_FROM_3D + 1D from 2D groups + + + 1D_FROM_2D + 1D from 2D + + + TARGET + Target + + + THIS_MESH + This mesh + + + NEW_MESH + New mesh + + + COPY_SRC + Copy source mesh + + + MISSING_ONLY + Copy missing elements only + + + CREATE_GROUP + Create group + + + + SMESHGUI_Make2DFrom3DOp + + NB_ADDED + %1 boundary elements have been added + + + WRONG_GROUPS + The following groups have not been processed +as they are of improper type: +%1 + + + SMESH_ERR_NO_INPUT_MESH + Source mesh is not specified + + + SMESH_TOO_MANY_MESHES + Only one mesh can be processed at once + + + SMESH_NOT_ONLY_GROUPS + Can't process meshes and groups at once + + + SMESH_ERR_NO_3D_ELEMENTS + The source objects do not contain 3D elements + + + SMESH_ERR_NO_2D_ELEMENTS + The source objects do not contain 2D elements + + + SMESH_ERR_MESH_NAME_NOT_SPECIFIED + New mesh name is not specified + + + SMESH_ERR_GRP_NAME_NOT_SPECIFIED + Group name is not specified + + + + SMESHGUI_MeshInfo + + NAME_LAB + Name: + + + OBJECT_LAB + Object: + + + NODES_LAB + Nodes: + + + ELEMENTS_LAB + Elements: + + + TOTAL_LAB + Total + + + LINEAR_LAB + Linear + + + QUADRATIC_LAB + Quadratic + + + 0D_LAB + 0D: + + + BALL_LAB + Balls: + + + 1D_LAB + 1D (edges): + + + 2D_LAB + 2D (faces): + + + TRIANGLES_LAB + Triangles: + + + QUADRANGLES_LAB + Quadrangles: + + + POLYGONS_LAB + Polygons: + + + 3D_LAB + 3D (volumes): + + + TETRAHEDRONS_LAB + Tetrahedrons: + + + HEXAHEDONRS_LAB + Hexahedrons: + + + PYRAMIDS_LAB + Pyramids: + + + PRISMS_LAB + Prisms: + + + HEX_PRISMS_LAB + Hexagonal Prisms: + + + POLYHEDRONS_LAB + Polyhedrons: + + + OBJECT_MESH + Mesh + + + OBJECT_SUBMESH + Sub-mesh + + + OBJECT_GROUP + Group + + + OBJECT_GROUP_NODES + Group of nodes + + + OBJECT_GROUP_EDGES + Group of edges + + + OBJECT_GROUP_FACES + Group of faces + + + OBJECT_GROUP_VOLUMES + Group of volumes + + + OBJECT_GROUP_0DELEMS + Group of 0D elements + + + OBJECT_GROUP_BALLS + Group of balls + + + BUT_LOAD_MESH + Load mesh from server + + + + SMESHGUI_MeshInfoDlg + + MESH_INFO + Mesh Information + + + BASE_INFO + Base Info + + + ELEM_INFO + Element Info + + + ADDITIONAL_INFO + Additional Info + + + NODE_MODE + Node + + + ELEM_MODE + Element + + + X_FROM_Y_ITEMS_SHOWN + %1-%2 from %3 items shown + + + + SMESHGUI_ElemInfo + + COORDINATES + COORDINATES + + + CONNECTIVITY + CONNECTIVITY + + + GRAVITY_CENTER + GRAVITY CENTER + + + NODE + NODE + + + 0D_ELEMENT + 0D ELEMENT + + + 0D_ELEMENTS + 0D ELEMENTS + + + BALL_ELEMENT + BALL ELEMENT + + + BALL_ELEMENTS + BALL ELEMENTS + + + EDGE + EDGE + + + EDGES + EDGES + + + FACE + FACE + - ELEMENTS_TYPE - Elements type - + FACES + FACES + + + VOLUME + VOLUME + + + VOLUMES + VOLUMES + + + FREE_NODE + Free node (no connectivity) + + + TYPE + TYPE + + + TRIANGLE + Triangle + + + QUADRANGLE + Quadrangle + + + POLYGON + Polygon + + + TETRAHEDRON + Tetrahedron + + + HEXAHEDRON + Hexahedron + + + PYRAMID + Pyramid + + + PRISM + Prism + + + HEX_PRISM + Hexagonal Prism + + + POLYHEDRON + Polyhedron + + + QUADRATIC + QUADRATIC + + + YES + Yes + + + NO + No + + + PROPERTY + Property + + + VALUE + Value + + + + SMESHGUI_AddInfo + + NAME + Name + + + GROUPS + Groups + + + GROUPS_1 + Nodes + + + GROUPS_2 + Edges + + + GROUPS_3 + Faces + + + GROUPS_4 + Volumes + + + GROUPS_5 + 0D elements + + + GROUPS_6 + Ball elements + + + PARENT_MESH + Parent mesh + + + TYPE + Type + + + STANDALONE_GROUP + Standalone group + + + GROUP_ON_GEOMETRY + Group on geometry + + + GROUP_ON_FILTER + Group on filter + + + GEOM_OBJECT + Shape + NODE Node - + EDGE Edge @@ -4052,662 +6599,360 @@ Please specify them and try again VOLUME Volume - - - SMESHGUI_IntersectGroupsDlg - - INTERSECTION_OF_GROUPS - Intersection of groups - - - - SMESHGUI_CutGroupsDlg - - CUT_OF_GROUPS - Cut of groups - - - MAIN_OBJECT - Main object - - - TOOL_OBJECT - Tool object - - - - SMESHGUI_MakeNodeAtPointDlg - - AUTO_SEARCH - Automatic search - - - CAPTION - Mesh to pass through a point - - - CREATE_NEW_METHOD - Create a node - - - MESH_PASS_THROUGH_POINT - Make a node at point - - - METHOD - Method - - - MOVE_EXISTING_METHOD - Move a node - - - NODE_2MOVE - Node to move - - - NODE_2MOVE_ID - ID - - - - SMESHGUI_MakeNodeAtPointOp - - INVALID_ID - Node ID is invalid - - - INVALID_MESH - Mesh to modify not selected - - - - SMESHGUI_MeshDlg - - CREATE_MESH - Create mesh - - - CREATE_SUBMESH - Create sub-mesh - - - DIM_0D - 0D - - - DIM_1D - 1D - - - DIM_2D - 2D - - - DIM_3D - 3D - - - EDIT_MESH_SUBMESH - Edit mesh/sub-mesh - - - GEOMETRY - Geometry - - - HYPOTHESES_SETS - Assign a set of hypotheses - - - MESH - Mesh - - - NAME - Name - - - - SMESHGUI_MeshOp - - ALGORITHM_WITHOUT_HYPOTHESIS - Algorithm is defined for %1 dimension but hypothesis is not defined - - - EDIT_SUBMESH_QUESTION - A submesh on the selected geometry already exists. - Do you want to edit this submesh? - - - SUBMESH_NOT_ALLOWED - No sense in creating a submesh ignored by global algorithm "%1" - - - GEOMETRY_OBJECT_IS_NOT_DEFINED - Geometry object is not defined -Please specify it and try again - - - GEOMETRY_OBJECT_IS_NULL - Geometry object is null - - - HYPOTHESES_AND_ALGORITHMS_ARE_NOT_DEFINED - Hypotheses and algorithms are not defined - - - HYPOTHESIS_WITHOUT_ALGORITHM - Hypothesis is defined for %1 dimension but algorithm is not defined - - - IMPORTED_MESH - Mesh is not built on geometry - - - INVALID_SUBSHAPE - Geometry object is not a subshape of the shape to mesh - - - MESH_IS_NOT_DEFINED - Mesh is not defined -Please specify it and try again - - - MESH_IS_NULL - Mesh is null - - - NAME_OF_MESH_IS_EMPTY - Name of mesh is empty -Please enter valid name and try again - - - NAME_OF_SUBMESH_IS_EMPTY - Name of submesh is empty -Please enter valid name and try again - - - THERE_IS_NO_OBJECT_FOR_EDITING - There is no object for editing. Please -select mesh or sub-mesh and try again - - - - SMESHGUI_MeshPatternDlg - - 3D_BLOCK - 3D block - - - CAPTION - Pattern Mapping - - - CREATE_POLYEDRS_NEAR_BOUNDARY - Create polyhedrons near boundary - - - CREATE_POLYGONS_NEAR_BOUNDARY - Create polygons near boundary - - - ERROR_OF_LOADING - Loading of pattern from file failed. Probably file -is corrupted or contains pattern of the other type - - - ERROR_OF_OPENING - It is impossible to open file. Please verify whether -file exists and your permission to this file - - - ERROR_OF_READING - It is impossible to load pattern -Please verify file's contents - - - ERR_READ_3D_COORD - It is impossible to load pattern -Coordinates of 3D points out of [0,1] range - - - ERR_READ_BAD_INDEX - It is impossible to load pattern -Invalid index of point detected - - - ERR_READ_BAD_KEY_POINT - It is impossible to load pattern -Key-point not on a boundary - - - ERR_READ_ELEM_POINTS - It is impossible to load pattern -invalid number of points in element - - - ERR_READ_NB_POINTS - It is impossible to load pattern -It is impossible to read number of points from file - - - ERR_READ_NO_ELEMS - It is impossible to load pattern -There are no elements in it - - - ERR_READ_NO_KEYPOINT - It is impossible to load pattern -There are no key-points in 2D one - - - ERR_READ_POINT_COORDS - It is impossible to load pattern -It is impossible to read point coordinates from file - - - ERR_READ_TOO_FEW_POINTS - It is impossible to load pattern. There are - too few points in the file for pattern loading - - - FACE - Face - - - LOAD_PATTERN - Load pattern - - - MESH_FACES - Mesh faces - - - MESH_VOLUMES - Mesh volumes - - - NEW - New... - - - NODE_1 - Node 1 - - - NODE_2 - Node 2 - - - PATTERN - Pattern - - - PATTERN_FILT - Pattern files(*.smp) - - - PATTERN_TYPE - Pattern type - - - PREVIEW - Preview - - - REFINE - Refine selected mesh elements - - - REVERSE - Reverse order of key-points - - - VERTEX - Vertex - - - VERTEX1 - Vertex 1 - - - VERTEX2 - Vertex 2 - - - - SMESHGUI_MeshTab - - ADD_HYPOTHESIS - Add. Hypothesis - - - ALGORITHM - Algorithm - - - HYPOTHESIS - Hypothesis - - - NONE - <None> - - - - SMESHGUI_MoveNodesDlg - - CAPTION - Move node - - - NODE_ID_IS_NOT_DEFINED - Node ID is not defined - - - - SMESHGUI_MultiEditDlg - - ADD - Add - - - FILTER - Filter - - - REMOVE - Remove - - - SELECT_FROM - Select from - - - SORT_LIST - Sort list - - - SPLIT_JOIN_CRITERION - Criterion - - - TO_ALL - Apply to all - - - USE_DIAGONAL_1_3 - Use diagonal 1-3 - - - USE_DIAGONAL_2_4 - Use diagonal 2-4 - - - USE_NUMERIC_FUNC - Use numeric functor - - - - SMESHGUI_PrecisionDlg - - CAPTION - Precision for mesh quality controls - - - NOT_USE - Do not use - - - PRECISION - Number of digits after point - - - - SMESHGUI_RevolutionDlg - - ANGLE_BY_STEP - Angle by Step - - - PREVIEW - Preview - - - REVOLUTION_1D - Revolution of 1D elements - - - REVOLUTION_2D - Revolution of 2D elements - - - REVOLUTION_AROUND_AXIS - Revolution around an axis - - - TOTAL_ANGLE - Total Angle - - - MEN_POINT_SELECT - From Origin to selected Point - - - MEN_FACE_SELECT - Normal to selected Face - - - - SMESHGUI_SewingDlg - - BORDER - Border - - - BORDER_1 - Border 1 - - - BORDER_2 - Border 2 - - - CREATE_POLYEDRS_NEAR_BOUNDARY - Replace affected volumes by polyedres - - - CREATE_POLYGONS_INSTEAD_SPLITTING - Create polygons instead of splitting - - - ERROR_1 - Free Border1 not found by the selected nodes - - - ERROR_2 - Free Border2 not found by the selected nodes - - - ERROR_3 - Free Border1 and Border2 not found by the selected nodes - - - ERROR_4 - No path from the first side node to the last side node have been found - - - ERROR_5 - Not allowed to splite volumes on the side! - - - ERROR_6 - Different number of elements selected on the sides - - - ERROR_7 - Element sets are topologically different or given nodes are inconvenient - - - ERROR_8 - Nodes on the side 1 are either not linked or not laying on the element set boundary - - - ERROR_9 - Nodes on the side 2 are either not linked or not laying on the element set boundary - - - FIRST_NODE_ID - First Node ID - - - LAST_NODE_ID - Last Node ID - - - MERGE_EQUAL_ELEMENTS - Merge equal elements - - - NODE1_TO_MERGE - Node 1 To Merge - - - NODE2_TO_MERGE - Node 2 To Merge - - - SECOND_NODE_ID - Second Node ID - - - SEW_BORDER_TO_SIDE - Sew Border To Side - - - SEW_CONFORM_FREE_BORDERS - Sew Conform Free Borders - - - SEW_FREE_BORDERS - Sew Free Borders - - - SEW_SIDE_ELEMENTS - Sew Side Elements - - - SIDE - Side - - - SIDE_1 - Side 1 - - - SIDE_2 - Side 2 - - - - SMESHGUI_ShapeByMeshDlg - - CAPTION - Find geometry by mesh element - - - - SMESHGUI_SingleEditDlg - - EDGE_BETWEEN - Edge between neighboring triangles - - - - SMESHGUI_SmoothingDlg - - CENTROIDAL - Centroidal - - - FIXED_NODES_IDS - Fixed nodes ids - - - IS_PARAMETRIC - in parametric space - - - ITERATION_LIMIT - Iteration limit - - - LAPLACIAN - Laplacian - - - MAX_ASPECT_RATIO - Max. aspect ratio - - - METHOD - Method - - - - SMESHGUI_TrianglesInversionDlg - - CAPTION - Diagonal inversion - - - - SMESHGUI_UnionOfTrianglesDlg - - CAPTION - Union of triangles - - - MAXIMUM_ANGLE - Maximum bending angle - - - - SMESHGUI_UnionOfTwoTrianglesDlg - - CAPTION - Union of two triangles - - - - SMESHGUI_WhatIsDlg - - ENTITY_TYPE - Element type - - - GRAVITY_CENTER - Gravity center - - - - SMESHGUI_FileInfoDlg - - CAPTION - File information - - - FILE_NAME - File name - - - FILE_SIZE - File size (bytes) - - - MED_VERSION - MED version - - + + 0DELEM + 0D element + + + BALL_ELEMENT + ball + + + UNKNOWN + Unknown + + + ENTITY_TYPE + Entity type + + + SIZE + Size + + + COLOR + Color + + + NB_NODES + Underlying nodes + + + COMPUTE + Compute + + + LOAD + Load + + + MESH_ON_GEOMETRY + Based on geometry + + + MESH_FROM_FILE + Imported + + + FILE_NAME + File name + + + STANDALONE_MESH + Standalone + + + SUBMESHES + Sub-meshes + + + SUBMESHES_0 + Compound + + + SUBMESHES_2 + Solid + + + SUBMESHES_3 + Shell + + + SUBMESHES_4 + Face + + + SUBMESHES_5 + Wire + + + SUBMESHES_6 + Edge + + + SUBMESHES_7 + Vertex + + + + SMESHGUI_MinDistance + + FIRST_TARGET + First target + + + SECOND_TARGET + Second target + + + NODE + Node + + + ELEMENT + Element + + + OBJECT + Object + + + ORIGIN + Origin + + + COMPUTE + Compute + + + RESULT + Distance between targets + + + DISTANCE + Distance + + + + SMESHGUI_CopyMeshDlg + + OBJECT_NAME + Source Object + + + ELEM_IDS + Source Element IDs + + + NEW_NAME + New Mesh Name + + + + SMESHGUI_MeasureDlg + + MEASUREMENTS + Measurements + + + MIN_DIST + Minimum Distance + + + BND_BOX + Bounding Box + + + + SMESHGUI_BoundingBox + + SOURCE + Source + + + OBJECTS + Objects + + + NODES + Nodes + + + ELEMENTS + Elements + + + COMPUTE + Compute + + + RESULT + Bounding Box + + + SELECTED_NB_OBJ + %1 %2 selected + + + NB_NODES + nodes + + + NB_ELEMENTS + elements + + + + SMESHGUI_MeshLoadingProgress + + MESH_LOAD_PROGRESS_TITLE + Mesh Loading + + + + StdMeshersGUI_SubShapeSelectorWdg + + X_FROM_Y_ITEMS_SHOWN + %1-%2 from %3 items shown + + + + SMESHGUI_Preferences_ColorDlg + + DIALOG_TITLE + Properties (color, line width, shrink size, ...) + + + GRP_ELEMENTS + Elements + + + SURFACE_COLOR_LBL + Surface color + + + BACKSURFACE_COLOR_LBL + Back surface color + + + OUTLINE_COLOR_LBL + Outline color + + + WIREFRAME_COLOR_LBL + Wireframe color + + + 0D_ELEMENTS_COLOR_LBL + 0D elements + + + 0D_ELEMENTS_SIZE_LBL + Size of 0D elements + + + BALL_ELEMENTS_COLOR_LBL + Ball elements + + + BALL_ELEMENTS_SIZE_LBL + Size of balls + + + LINE_WIDTH_LBL + Line width + + + SHRINK_COEF_LBL + Shrink coef. + + + GRP_NODES + Nodes + + + NODES_COLOR_LBL + Color + + + NODES_MARKER_LBL + Marker + + + GRP_ORIENTATION + Orientation of faces + + + ORIENTATION_COLOR_LBL + Color + + + ORIENTATION_SCALE_LBL + Scale + + + 3D_VECTORS_LBL + 3D vectors + + + GRP_SELECTION + Selection + + + SELECTION_COLOR_LBL + Selection color + + + PRESELECTION_COLOR_LBL + Pre-selection color + + + + SMESHGUI_ReorientFacesDlg + + CAPTION + Reorient faces by vector + + + REORIENT_FACES + Reorient + + + DIRECTION + Direction + + + OBJECT + Object + + + POINT + Point + + + FACE + Face + + + FACES + Faces source + + + ORIENTATION + Orientation + + + + SMESHGUI_ReorientFacesOp + + NO_OBJECT_SELECTED + No object selected + + + NO_FACES + Object includes no faces + + + ZERO_SIZE_VECTOR + Zero size vector + + + INVALID_FACE + Not valid face + + + NB_REORIENTED + %1 faces reversed + + diff --git a/src/SMESHGUI/SMESH_msg_fr.ts b/src/SMESHGUI/SMESH_msg_fr.ts new file mode 100755 index 000000000..1672f6fbc --- /dev/null +++ b/src/SMESHGUI/SMESH_msg_fr.ts @@ -0,0 +1,6968 @@ + + + + + @default + + SMESH_EXPORT_MESH + Exporter le maillage + + + MED_FILES_FILTER + Fichiers MED + + + IDEAS_FILES_FILTER + Fichiers IDEAS + + + DAT_FILES_FILTER + Fichiers DAT + + + TEXT_FILES_FILTER + Fichiers TXT + + + MED_VX_FILES_FILTER + Fichiers MED %1 + + + STL_ASCII_FILES_FILTER + Fichiers STL ASCII + + + CGNS_FILES_FILTER + Fichiers CGNS + + + STL_BIN_FILES_FILTER + Fichiers STL binaires + + + ALL_FILES_FILTER + Tous les fichiers + + + SMESH_AND + et + + + AREA_ELEMENTS + Aire + + + ASPECTRATIO_3D_ELEMENTS + Rapport de forme 3D + + + ASPECTRATIO_ELEMENTS + Rapport de forme + + + NODE_POSITION + Position + + + U_POSITION + U + + + V_POSITION + V + + + COL_ALGO_HEADER + Algorithme + + + COL_ERROR_HEADER + Erreur + + + COL_SHAPE_HEADER + SousObjet + + + COMPERR_ALGO_FAILED + L'algorithme n'a pas abouti + + + COMPERR_BAD_INPUT_MESH + Le maillage d'entrée est invalide + + + COMPERR_BAD_SHAPE + Géométrie inattendue + + + COMPERR_EXCEPTION + Exception inconnue + + + COMPERR_MEMORY_PB + Problème d'affectation de la mémoire + + + COMPERR_OCC_EXCEPTION + Exception OCC + + + COMPERR_OK + Pas d'erreur + + + COMPERR_SLM_EXCEPTION + Exception SALOME + + + COMPERR_STD_EXCEPTION + std::exception + + + COMPERR_UNKNOWN + Erreur inconnue + + + COMPERR_CANCELED + Calcul annulé + + + SMESH_GEOM + Géométrie + + + DIRECT_GEOM_SELECTION + Sélection directe de la géométrie + + + ELEMENT_ID + ID de l'élément + + + FREE_BORDERS + Frontières libres + + + GEOMETRY_NAME + Nom de la géométrie + + + GEOM_BY_MESH_ELEM_SELECTION + Trouver la géométrie en choisissant l'élément de maillage + + + GLOBAL_ALGO + Global + + + INF_SELECT_OBJECT + Choisir un objet + + + LENGTH2D_EDGES + Longueur 2D + + + LENGTH_EDGES + Longueur + + + LOCAL_ALGO + Local + + + MAX_ELEMENT_LENGTH_2D + Diamètre d'éléments 2D + + + MAX_ELEMENT_LENGTH_3D + Diamètre d'éléments 3D + + + MEN_ADD + Ajouter + + + MEN_ADV_INFO + Informations sur le maillage + + + MEN_ALL + Tous + + + MEN_AREA + Aire + + + MEN_ASPECT + Rapport de forme + + + MEN_ASPECT_3D + Rapport de forme 3D + + + MEN_AUTO_COLOR + Couleur automatique + + + MEN_AUTO_UPD + Mise à jour automatique + + + MEN_BUILD_COMPOUND + Construire un assemblage + + + MEN_COPY_MESH + Copier le maillage + + + MEN_CLIP + Plan de coupe + + + MEN_COLORS + Paramètres d'affichage + + + MEN_COMPUTE + Calculer + + + MEN_PRECOMPUTE + Prévisualiser + + + MEN_EVALUATE + Evaluer + + + MEN_CONNECTION + Frontières sur connexions multiples + + + MEN_CONNECTION_2D + Frontières sur connexions multiples 2D + + + MEN_CONSTRUCT_GROUP + Construire un groupe + + + MEN_CONV_TO_QUAD + Convertir vers/de quadratique + + + MEN_2D_FROM_3D + Créer les éléments de frontière + + + MEN_MESH_ORDER + Changer la priorité des sous-maillages + + + MEN_CREATE_GROUP + Créer un groupe + + + MEN_CREATE_GEO_GROUP + Créer des groupes à partir de la géométrie + + + MEN_CREATE_MESH + Créer un maillage + + + MEN_CREATE_SUBMESH + Créer un sous-maillage + + + MEN_CTRL + Contrôles + + + MEN_NODE_CTRL + Contrôles des nœuds + + + MEN_EDGE_CTRL + Contrôles des arêtes + + + MEN_FACE_CTRL + Contrôles des faces + + + MEN_VOLUME_CTRL + Contrôles des volumes + + + MEN_CUT + Découpe des quadrangles + + + MEN_CUT_GROUP + Découpe des groupes + + + MEN_IMPORT_DAT + Fichier DAT + + + MEN_DAT + Fichier DAT + + + MEN_DELETE + Supprimer + + + MEN_DEL_GROUP + Supprimer les groupes et leur contenu + + + MEN_FACE_ORIENTATION + Orientation des faces + + + MEN_DISABLE_AUTO_COLOR + Désactiver la couleur automatique + + + MEN_DISPLAY_ONLY + Afficher uniquement + + + MEN_DISPMODE + Mode de visualisation + + + MEN_DISP_ENT + Montrer l'entité + + + MEN_ELEM0D + Elément 0D + + + MEN_ELEMS0D + Eléments 0D + + + MEN_BALL + Particulaire + + + MEN_BALLS + Particulaires + + + MEN_EDGE + Arête + + + MEN_EDGES + Arêtes + + + MEN_EDIT + Edition + + + MEN_EDIT_GROUP + Editer un groupe + + + MEN_EDIT_GEOMGROUP_AS_GROUP + Editer un groupe en tant qu'autonome + + + MEN_EDIT_HYPO + Editer une hypothèse + + + MEN_EDIT_MESHSUBMESH + Editer un maillage/sous-maillage + + + MEN_EXPORT + Exporter + + + MEN_EXPORT_DAT + Exporter au format DAT + + + MEN_EXPORT_MED + Exporter au format MED + + + MEN_EXPORT_CGNS + Exporter au format CGNS + + + MEN_EXPORT_SAUV + Exporter au format SAUV (ASCII) + + + MEN_EXPORT_STL + Exporter au format STL + + + MEN_EXPORT_UNV + Exporter au format UNV + + + MEN_EXTRUSION + Extrusion + + + MEN_EXTRUSION_ALONG + Extrusion suivant un chemin + + + MEN_FACES + Faces + + + MEN_FILE + Fichier + + + MEN_FIND_ELEM + Trouver un élément par un point + + + TOP_REORIENT_2D + Réorienter des faces selon un vecteur + + + MEN_REORIENT_2D + Réorienter des faces selon un vecteur + + + STB_REORIENT_2D + Réorienter des faces selon un vecteur + + + TOP_FIND_ELEM + Trouver un élément par un point + + + STB_FIND_ELEM + Trouver un élément par un point + + + EQUAL_NODE + Nœuds en double + + + MEN_EQUAL_NODE + Nœuds en double + + + STB_EQUAL_NODE + Nœuds en double + + + TOP_EQUAL_NODE + Nœuds en double + + + EQUAL_EDGE + Arêtes en double + + + MEN_EQUAL_EDGE + Arêtes en double + + + STB_EQUAL_EDGE + Arêtes en double + + + TOP_EQUAL_EDGE + Arêtes en double + + + EQUAL_FACE + Faces en double + + + MEN_EQUAL_FACE + Faces en double + + + STB_EQUAL_FACE + Faces en double + + + TOP_EQUAL_FACE + Faces en double + + + EQUAL_VOLUME + Volumes en double + + + MEN_EQUAL_VOLUME + Volumes en double + + + STB_EQUAL_VOLUME + Volumes en double + + + TOP_EQUAL_VOLUME + Volumes en double + + + MEN_BARE_BORDER_VOLUME + Volumes avec éléments de peau 2D manquants + + + MEN_BARE_BORDER_FACE + Faces avec éléments de peau 1D manquants + + + MEN_OVER_CONSTRAINED_VOLUME + Volumes sur-contraints + + + MEN_OVER_CONSTRAINED_FACE + Faces sur-contraintes + + + MEN_FREE_BORDER + Frontières libres + + + MEN_FREE_EDGE + Arêtes libres + + + MEN_FREE_NODE + Nœuds libres + + + MEN_FREE_FACES + Faces libres + + + MEN_GLOBAL_HYPO + Hypothèse globale + + + MEN_HEXA + Hexaèdre + + + MEN_HIDE + Cacher + + + MEN_HYPO + Hypothèses + + + MEN_IMPORT + Importer + + + MEN_INT_GROUP + Intersection des groupes + + + MEN_INV + Inversion de diagonale + + + MEN_LENGTH + Longueur + + + MEN_LENGTH_2D + Longueur 2D + + + MEN_MAP + Projection de motif + + + MEN_MAX_ELEMENT_LENGTH_2D + Diamètre des éléments 2D + + + MEN_MAX_ELEMENT_LENGTH_3D + Diamètre des éléments 3D + + + MEN_IMPORT_MED + Fichier MED + + + MEN_MED + Fichier MED + + + MEN_IMPORT_CGNS + Fichier CGNS + + + MEN_CGNS + Fichier CGNS + + + MEN_IMPORT_SAUV + Fichier SAUV (ASCII) + + + MEN_SAUV + Fichier SAUV (ASCII) + + + MEN_MERGE + Fusionner les nœuds + + + MEN_MERGE_ELEMENTS + Fusionner les éléments + + + MEN_MESH + Maillage + + + MEN_MESH_THROU_POINT + Déplacer un nœud + + + MEN_MIN_ANG + Angle minimal + + + MEN_MODIFY + Modification + + + MEN_MEASURE + Outils de mesure + + + MEN_MEASURE_MIN_DIST + Distance minimale + + + STB_MEASURE_MIN_DIST + Calcule la distance minimum entre deux objets + + + TOP_MEASURE_MIN_DIST + Distance minimum + + + MEN_MEASURE_BND_BOX + Boîte englobante + + + STB_MEASURE_BND_BOX + Calcule la boîte englobante pour le(s) objet(s) sélectionné(s) + + + TOP_MEASURE_BND_BOX + Boîte englobante + + + MEN_MOVE + Déplacer un nœud + + + MEN_NODE + Nœud + + + MEN_NODES + Nœuds + + + MEN_NUM + Numérotation + + + MEN_NUM_ELEMENTS + Montrer les n° des éléments + + + MEN_NUM_NODES + Montrer les n° des nœuds + + + MEN_ORIENT + Orientation + + + MEN_POLYGON + Polygone + + + MEN_POLYHEDRON + Polyèdre + + + MEN_PRECISION + Précision + + + MEN_PREF + Préférences + + + MEN_QUAD + Quadrangle + + + MEN_QUADRATIC_EDGE + Arête quadratique + + + MEN_QUADRATIC_HEXAHEDRON + Hexaèdre quadratique + + + MEN_QUADRATIC_PENTAHEDRON + Pentaèdre quadratique + + + MEN_QUADRATIC_PYRAMID + Pyramide quadratique + + + MEN_QUADRATIC_QUADRANGLE + Quadrangle quadratique + + + MEN_QUADRATIC_TETRAHEDRON + Tétraèdre quadratique + + + MEN_QUADRATIC_TRIANGLE + Triangle quadratique + + + MEN_QUALITY + Contrôles de qualité + + + MEN_REMOVE + Supprimer + + + MEN_REMOVE_ELEMENTS + Eléments + + + MEN_REMOVE_NODES + Nœuds + + + MEN_REMOVE_ORPHAN_NODES + Nœuds orphelins + + + MEN_RENAME + Renommer + + + MEN_RENUM + Renuméroter + + + MEN_RENUM_ELEMENTS + Eléments + + + MEN_RENUM_NODES + Nœuds + + + MEN_RESET + Restaurer + + + MEN_DISTRIBUTION_CTRL + Distribution + + + MEN_SAVE_DISTRIBUTION + Exporter la distribution... + + + MEN_SHOW_DISTRIBUTION + Afficher la distribution + + + MEN_PLOT_DISTRIBUTION + Tracer + + + DISTRIBUTION_NB_ENT + TODO + + + MEN_REVOLUTION + Révolution + + + MEN_ROT + Rotation + + + MEN_SCALAR_BAR + Barre scalaire + + + MEN_SCALAR_BAR_PROP + Propriétés de la barre scalaire + + + MEN_SELECTION + Sélection + + + MEN_SEL_FILTER_LIB + Librairie des filtres de sélection + + + MEN_SEW + Couture + + + MEN_SHADE + Ombrage + + + MEN_QUADRATIC_REPRESENT + Quadratique 2D + + + MEN_LINE_REPRESENTATION + Lignes + + + MEN_ARC_REPRESENTATION + Arcs + + + MEN_SHOW + Afficher + + + MEN_SHRINK + Contraction + + + MEN_SKEW + Inclinaison d'angle + + + MEN_SMOOTH + Lissage + + + MEN_STD_INFO + Informations sur le maillage + + + MEN_IMPORT_STL + Fichier STL + + + MEN_STL + Fichier STL + + + MEN_SYM + Symétrie + + + MEN_TAPER + Cône + + + MEN_TETRA + Tétraèdre + + + MEN_TOOLS + Outils + + + MEN_TRANS + Translation + + + MEN_SCALE + Transformation d'échelle + + + MEN_DUPLICATE_NODES + Dupliquer les nœuds + + + MEN_TRANSF + Transformation + + + MEN_TRANSP + Transparence + + + MEN_TRIANGLE + Triangle + + + MEN_UNASSIGN + Désassocier + + + MEN_UNION + Union des triangles + + + MEN_UNION2 + Union de deux triangles + + + MEN_IMPORT_UNV + Fichier UNV + + + MEN_UNV + Fichier UNV + + + MEN_UN_GROUP + Union des groupes + + + MEN_UNDERLYING_ELEMS + Groupe des entités sous-jacentes + + + MEN_UPDATE + Mettre à jour + + + MEN_VIEW + Affichage + + + MEN_VOLUMES + Volumes + + + MEN_VOLUME_3D + Volume + + + MEN_WARP + Angle de déformation + + + MEN_WHAT_IS + Information sur un élément de maillage + + + MEN_WIRE + Contours + + + MEN_SPLIT_TO_TETRA + Explosion en tétraèdres + + + TOP_SPLIT_TO_TETRA + Eclater en tétraèdres + + + STB_SPLIT_TO_TETRA + Eclater en tétraèdres + + + MESHERS_FILE_CANT_OPEN + Impossible d'ouvrir le fichier de ressource + + + MESHERS_FILE_CHECK_VARIABLE + Vérifier la variable d'environnement SMESH_MeshersList + + + MESHERS_FILE_NO_VARIABLE + La variable d'environnement SMESH_MeshersList n'est pas définie + + + MESH_IS_NOT_SELECTED + Il n'y a pas de maillage sélectionné. +Choisissez un maillage et essayez de nouveau + + + MESH_NODE + Nœud + + + MESH_NODE_TITLE + Ajouter un nœud + + + MINIMUMANGLE_ELEMENTS + Angle minimal + + + MULTI2D_BORDERS + Frontières sur multi-connexions 2D + + + MULTI_BORDERS + Frontières sur multi-connexions + + + GROUP_NAME_IS_EMPTY + Le nom du groupe n'est pas indiqué. +Indiquez le nom d'un nouveau groupe à créer ou choisissez un groupe existant. + + + MESH_STANDALONE_GRP_CHOSEN + Un groupe lié à la géométrie est choisi: %1. +Voulez-vous le convertir en un groupe autonome ? + + + NODE_ID + ID du nœud + + + NON_SMESH_OBJECTS_SELECTED + Certains objets sélectionnés n'appartiennent pas au composant %1. + + + PREVIEW + Prévisualiser + + + SKEW_ELEMENTS + Inclinaison d'angle + + + SMESHGUI_INVALID_PARAMETERS + Les paramètres spécifiés ne sont pas corrects. +Merci de les corriger, puis essayez de nouveau + + + SMESH_ADD_ALGORITHM + Algorithmes + + + SMESH_ADD_ALGORITHM_TITLE + Attribution des algorithmes + + + SMESH_ADD_ELEM0D + Ajouter un élément 0D + + + SMESH_ADD_ELEM0D_TITLE + Ajouter un élément 0D + + + SMESH_ADD_BALL + Ajouter un élément particulaire + + + SMESH_ADD_BALL_TITLE + Ajouter un élément particulaire + + + SMESH_ADD_EDGE + Ajouter une arête + + + SMESH_ADD_EDGE_TITLE + Ajouter une arête + + + SMESH_ADD_HEXAS + Ajouter un hexaèdre + + + SMESH_ADD_HEXAS_TITLE + Ajouter un hexaèdre + + + SMESH_ADD_HYPOTHESIS + Hypothèse + + + SMESH_ADD_HYPOTHESIS_TITLE + Attribution d'une hypothèse + + + SMESH_ADD_HYP_WRN + "%1" est attribué, mais: + + + + SMESH_ADD_OCTA + Ajouter un prisme hexagonale + + + SMESH_ADD_OCTA_TITLE + Ajouter un prisme hexagonale + + + SMESH_ADD_POLYGON + Ajouter un polygone + + + SMESH_ADD_POLYGON_TITLE + Ajouter un polygone + + + SMESH_ADD_PENTA + Ajouter un pentaèdre + + + SMESH_ADD_PENTA_TITLE + Ajouter un pentaèdre + + + SMESH_ADD_PYRAMID + Ajouter une pyramide + + + SMESH_ADD_PYRAMID_TITLE + Ajouter une pyramide + + + SMESH_ADD_QUADRANGLE + Ajouter un quadrangle + + + SMESH_ADD_QUADRANGLE_TITLE + Ajouter un quadrangle + + + SMESH_ADD_QUADRATIC_EDGE_TITLE + Ajouter une arête quadratique + + + SMESH_ADD_QUADRATIC_HEXAHEDRON_TITLE + Ajouter un hexaèdre quadratique + + + SMESH_ADD_TRIQUADRATIC_HEXAHEDRON_TITLE + Ajouter un hexaèdre triquadratique + + + SMESH_ADD_QUADRATIC_PENTAHEDRON_TITLE + Ajouter un pentaèdre quadratique + + + SMESH_ADD_QUADRATIC_PYRAMID_TITLE + Ajouter une pyramide quadratique + + + SMESH_ADD_BIQUADRATIC_QUADRANGLE_TITLE + Ajouter un quadrangle biquadratique + + + SMESH_ADD_QUADRATIC_QUADRANGLE_TITLE + Ajouter un quadrangle quadratique + + + SMESH_ADD_QUADRATIC_TETRAHEDRON_TITLE + Ajouter un tétraèdre quadratique + + + SMESH_ADD_QUADRATIC_TRIANGLE_TITLE + Ajouter un triangle quadratique + + + SMESH_ADD_SUBMESH + Construction d'un sous-maillage + + + SMESH_ADD_TETRAS + Ajouter un tétraèdre + + + SMESH_ADD_TETRAS_TITLE + Ajouter un tétraèdre + + + SMESH_ADD_TO_GROUP + Ajouter à un groupe + + + SMESH_ADD_TRIANGLE + Ajouter un triangle + + + SMESH_ADD_TRIANGLE_TITLE + Ajouter un triangle + + + SMESH_ANGLE + Angle + + + SMESH_ARGUMENTS + Arguments + + + SMESH_AUTO_GROUPS + Créer les groupes automatiquement + + + SMESH_AVAILABLE + Disponible + + + SMESH_AVAILABLE_ALGORITHMS + Algorithmes disponibles + + + SMESH_AVAILABLE_HYPOTHESES + Hypothèses disponibles + + + SMESH_AXIS + Axe + + + SMESH_BAD_SELECTION + Pas de sélection valide + + + SMESH_BAD_MESH_SELECTION + La sélection du maillage n'est pas valide + + + SMESH_BOUNDARYEDGES + Arêtes frontières + + + SMESH_BUILD_COMPOUND_TITLE + Créer un assemblage + + + SMESH_COPY_MESH_TITLE + Copier le maillage + + + SMESH_KEEP_IDS + Conserver les IDs des éléments + + + SMESH_BUT_ADD + A&jouter + + + SMESH_BUT_APPLY + A&ppliquer + + + SMESH_BUT_CANCEL + A&nnuler + + + SMESH_BUT_CLOSE + &Fermer + + + SMESH_BUT_CREATE + &Créer + + + SMESH_BUT_DELETE + Eff&acer + + + SMESH_BUT_FILTER + Définir les fil&tres + + + SMESH_BUT_HELP + Ai&de + + + SMESH_BUT_NEW + Nou&veau + + + SMESH_BUT_NO + &Non + + + SMESH_BUT_OK + &Ok + + + SMESH_BUT_OVERWRITE + Réécr&ire + + + SMESH_BUT_APPLY_AND_CLOSE + App&liquer et fermer + + + SMESH_BUT_REMOVE + S&upprimer + + + SMESH_BUT_SORT + &Trier la liste + + + SMESH_BUT_YES + &Oui + + + SMESH_CANT_ADD_HYP + Impossible d'attribuer "%1": + + + + SMESH_CANT_RM_HYP + Impossible de désassigner "%1": + + + + SMESH_CHECK_COLOR + Couleur + + + SMESH_CLIPPING_FROM + De <--- + + + SMESH_CLIPPING_INTO + ---> En + + + SMESH_CLIPPING_TITLE + Changer le plan de coupe + + + SMESH_COMPUTE_SUCCEED + Le calcul du maillage a réussi + + + SMESH_EVALUATE_SUCCEED + L'évaluation du maillage a réussi + + + SMESH_CONTENT + Contenu + + + SMESH_CONTINUE_MESH_VISUALIZATION + La système semble manquer de mémoire pour visualiser le maillage, +ce qui peut faire planter l'application. Voulez-vous continuer la visualisation ? + + + SMESH_COORDINATES + Coordonnées + + + SMESH_COPY_ELEMENTS + Copier les éléments + + + SMESH_COPY_GROUPS + Copier les groupes + + + SMESH_CREATE_ALGORITHMS + Créer les algorithmes + + + SMESH_CREATE_COPY + Créer une copie + + + SMESH_CREATE_GROUP_TITLE + Créer un groupe + + + SMESH_CREATE_GEO_GROUP + Créer des groupes liés à la géométrie + + + SMESH_CREATE_HYPOTHESES + Créer une hypothèse + + + SMESH_CREATE_MESH + Créer un nouveau maillage + + + SMESH_CREATE_POLYHEDRAL_VOLUME_TITLE + Créer un volume polyèdrique + + + SMESH_DIAGONAL + Inversion de diagonale + + + SMESH_DIAGONAL_INVERSION_TITLE + Inversion de diagonale + + + SMESH_DISTANCE + Distance + + + SMESH_DRS_1 + Le fichier MED contient pas de maillage avec ce nom + + + SMESH_DRS_2 + Le fichier MED contient des rangées de nombre d'éléments superposées, donc les nombres de ce fichier ne sont pas pris en compte + + + SMESH_DRS_3 + Quelques éléments ont été omis à cause du fichier de données incorrect + + + SMESH_DRS_4 + Le fichier n'est pas correct, des données sont manquantes + + + SMESH_DRS_EMPTY + Le fichier est vide, il n'y a rien à publier + + + SMESH_DX + dX + + + SMESH_DY + dY + + + SMESH_DZ + dZ + + + SMESH_ELEM0D + Elément 0D + + + SMESH_ELEMS0D + Eléments 0D + + + SMESH_BALL_ELEM + Particulaire + + + SMESH_BALL + Particulaire + + + SMESH_BALLS + Particulaires + + + SMESH_EDGE + Arête + + + SMESH_EDGES + Arêtes + + + SMESH_EDGES_CONNECTIVITY_TITLE + Connectivité des arêtes + + + SMESH_EDIT_GROUP_TITLE + Editer un groupe + + + SMESH_EDIT_GEOMGROUP_AS_GROUP_TITLE + Editer un groupe en tant qu'autonome + + + SMESH_EDIT_HYPOTHESES + Attribuer les hypothèses + + + SMESH_EDIT_USED + Utilisé + + + SMESH_ELEMENTS + Eléments + + + SMESH_ELEMENTS_COLOR + Couleur des éléments du maillage + + + SMESH_ELEMENTS_TYPE + Type des éléments + + + SMESH_ELEMENT_TYPE + Type de l'élément + + + SMESH_ERROR + Erreur + + + SMESH_ERR_SCALARBAR_PARAMS + Avertissement! Les paramètres ne sont pas corrects + + + SMESH_EXPORT_FAILED + Impossible d'exporter le maillage. +Vérifiez l'espace disponible sur le disque. + + + SMESH_EXPORT_MED_DUPLICATED_GRP + Il y a des noms de groupes dupliqués dans le maillage "%1". +Vous pouvez annuler l'exportation et les renommer, +si non des noms de groupes au fichier MED résultant +ne correspondront pas aux noms de l'étude. +Voulez-vous continuer ? + + + SMESH_EXPORT_MED_DUPLICATED_MESH_NAMES + Il y a des maillages avec les mêmes noms dans la sélection. +Il est possible que le fichier résultant soit incorrect. +Voulez-vous continuer ? + + + SMESH_EXPORT_MED_V2_1 + Les éléments polygonaux et polyèdriques seront omis +dans le cas d'exportation du maillage "%1" à MED 2.1 +Utilisez MED 2.2 pour l'exportation correcte. +Voulez-vous effectuer l'exportation à MED 2.1 ? + + + SMESH_EXPORT_MED_VERSION_COLLISION + La version MED du fichier "%1" n'est pas connue +ou ne correspond pas à la version choisie. +Réécrire le fichier ? + + + SMESH_EXPORT_MED_MESH_NAMES_COLLISION + Le fichier choisi contient déjà +les maillages avec les noms suivants: %1 +Il est possible que le fichier résultant ne soit pas correct. +Réécrire le fichier ? + + + SMESH_EXPORT_STL1 + Le maillage - "%1" ne contient pas de triangles + + + SMESH_EXPORT_STL2 + Le maillage - "%1" contient d'autres éléments que les triangles, ils ne seront donc pas enregistrés dans le fichier STL + + + SMESH_EXPORT_UNV + Les éléments pyramides seront omis au cours de l'exportation +du maillage "%1" dans le fichier UNV + + + EXPORT_NOT_SUPPORTED + A l'export du maillage avec le nom "%1" vers %2 +%3 sera ignoré. +Voulez-vous continuer ? + + + SMESH_EXTRUSION + Extrusion + + + SMESH_EXTRUSION_TO_DISTANCE + Distance de l'extrusion + + + SMESH_EXTRUSION_ALONG_VECTOR + Extrusion le long du vecteur + + + SMESH_FACE + Face + + + SMESH_FEATUREANGLE + Montrer l'angle + + + SMESH_FEATUREEDGES + Montrer les arêtes + + + SMESH_FILE_EXISTS + Le fichier "%1" existe déjà. +Voulez-vous le réécrire ou y ajouter +les données exportées ? + + + SMESH_FONT_ARIAL + Arial + + + SMESH_FONT_BOLD + Gras + + + SMESH_FONT_COURIER + Courrier + + + SMESH_FONT_ITALIC + Italique + + + SMESH_FONT_SCALARBAR + Police + + + SMESH_FONT_SHADOW + Ombrage + + + SMESH_FONT_TIMES + Times + + + SMESH_GEOM_GROUP + Groupe géométrique + + + SMESH_GROUP + Groupe + + + SMESH_GROUP_GEOMETRY + Groupe lié à une géométrie + + + SMESH_GROUP_FILTER + Groupe lié à un filtre + + + SMESH_GROUP_SELECTED + %1 groupes + + + SMESH_GROUP_STANDALONE + Groupe autonome + + + SMESH_GROUP_TYPE + Type du groupe + + + SMESH_HEIGHT + Hauteur : + + + SMESH_HEXAS + Hexaèdre + + + SMESH_HEXAHEDRA + Hexaèdres + + + SMESH_HILIGHT_COLOR + Couleur de sélection + + + SMESH_HORIZONTAL + Horizontale + + + SMESH_HYPOTHESES + Hypothèses + + + SMESH_HYP_1 + Il manque une hypothèse à l'algorithme + + + SMESH_HYP_10 + L'hypothèse ne correspond pas aux dimensions du sous-maillage + + + SMESH_HYP_11 + La géométrie n'est ni la géométrie principale, ni un de ses sous-objets, ni un groupe valide + + + SMESH_HYP_12 + La géométrie ne correspond pas à l'algorithme +Référez-vous à la documentation sur l'algorithme et la géométrie supportée + + + SMESH_HYP_13 + L'algorithme ne peut pas opérer sans géométrie + + + SMESH_HYP_2 + Il y a des hypothèses concurrentes sur la géométrie + + + SMESH_HYP_3 + L'hypothèse contient une valeur de paramètre incorrecte + + + SMESH_HYP_4 + Le sous-maillage n'est pas pris en compte parce qu'il y a un algorithme de dimension supérieure pour générer les éléments %1D + + + SMESH_HYP_5 + L'algorithme l'emporte sur les algorithme(s) de dimensions inférieures en générant les éléments de toutes les dimensions + + + SMESH_HYP_6 + Erreur critique inconnue lors de la définition de l'hypothèse + + + SMESH_HYP_7 + L'hypothèse ne correspond pas à la situation actuelle + + + SMESH_HYP_8 + Un maillage non-conforme a été produit avec les hypothèses appliquées + + + SMESH_HYP_9 + L'hypothèse d'une telle dimension est déjà attribuée à la géométrie + + + SMESH_ID_DIAGONAL + IDs des arêtes + + + SMESH_ID_ELEMENTS + IDs des éléments + + + SMESH_ID_FACES + IDs des faces + + + SMESH_ID_NODES + IDs des nœuds + + + SMESH_INCORRECT_INPUT + Les données d'entrée ne sont pas correctes + + + SMESH_INFORMATION + Information + + + SMESH_INIT + Maillage + + + SMESH_INIT_MESH + Construction du maillage + + + SMESH_INSUFFICIENT_DATA + La valeur d'entrée n'est pas suffisante + + + SMESH_LABELS + Etiquettes : + + + SMESH_LABELS_COLORS_SCALARBAR + Couleurs && étiquettes + + + SMESH_LENGTH + Longueur + + + SMESH_MAKE_GROUPS + Générer les groupes + + + SMESH_MANIFOLDEDGES + Arêtes partagées + + + SMESH_MAX + Max + + + SMESH_MEN_ALGORITHMS + Algorithmes + + + SMESH_MEN_APPLIED_ALGORIHTMS + Algorithmes appliqués + + + SMESH_MEN_APPLIED_HYPOTHESIS + Hypothèses appliquées + + + SMESH_MEN_COMPONENT + SMESH + + + SMESH_MEN_HYPOTHESIS + Hypothèses + + + SMESH_MEN_SubMeshesOnCompound + Sous-maillages sur un assemblage + + + SMESH_MEN_SubMeshesOnEdge + Sous-maillages sur une arête + + + SMESH_MEN_SubMeshesOnFace + Sous-maillages sur une face + + + SMESH_MEN_SubMeshesOnSolid + Sous-maillages sur un solide + + + SMESH_MEN_SubMeshesOnVertex + Sous-maillages sur un point + + + SMESH_AUTOMATIC + Automatique + + + SMESH_MANUAL + Manuel + + + SMESH_MERGE_ELEMENTS + Fusionner les éléments + + + SMESH_MODE + Mode + + + SMESH_MERGED_ELEMENTS + %1 éléments fusionnés avec succès + + + SMESH_MERGED_NODES + %1 nœuds fusionnés avec succès + + + SMESH_NO_ELEMENTS_DETECTED + Il n'y a aucun élément à fusionner. + + + SMESH_NO_NODES_DETECTED + Il n'y a aucun nœud à fusionner + + + SMESH_MERGE_NODES + Fusionner les nœuds + + + SMESH_MESH + Maillage + + + SMESH_MESHINFO_0DELEMS + Eléments 0D + + + SMESH_MESHINFO_BALLS + Particulaires + + + SMESH_MESHINFO_ALL_TYPES + Hétérogène + + + SMESH_MESHINFO_EDGES + Arêtes + + + SMESH_MESHINFO_ELEMENTS + Eléments + + + SMESH_MESHINFO_ENTITIES + Entités + + + SMESH_MESHINFO_FACES + Faces + + + SMESH_MESHINFO_HEXAS + Hexaèdres + + + SMESH_MESHINFO_NAME + Nom + + + SMESH_MESHINFO_NODES + Nœuds + + + SMESH_MESHINFO_ORDER0 + Total + + + SMESH_MESHINFO_ORDER1 + Linéaire + + + SMESH_MESHINFO_ORDER2 + Quadratique + + + SMESH_MESHINFO_HEXAPRISM + Prismes hexagonaux + + + SMESH_MESHINFO_POLYEDRES + Polyèdres + + + SMESH_MESHINFO_POLYGONES + Polygones + + + SMESH_MESHINFO_PRISMS + Prismes + + + SMESH_MESHINFO_PYRAS + Pyramides + + + SMESH_MESHINFO_QUADRANGLES + Quadrangles + + + SMESH_MESHINFO_TETRAS + Tétraèdres + + + SMESH_MESHINFO_TITLE + Informations sur le maillage + + + SMESH_MESHINFO_TOTAL + Total + + + SMESH_MESHINFO_TRIANGLES + Triangles + + + SMESH_MESHINFO_TYPE + Type + + + SMESH_MESHINFO_VOLUMES + Volumes + + + SMESH_MIN + Min + + + SMESH_MOVE + Déplacer + + + SMESH_MOVE_ELEMENTS + Déplacer les éléments + + + SMESH_MOVE_NODES_TITLE + Déplacer un nœud + + + SMESH_NAME + Nom + + + SMESH_NODES + Nœuds + + + SMESH_NONMANIFOLDEDGES + Arêtes non-partagées + + + SMESH_NORMAL + Normal + + + SMESH_NO_MESH_VISUALIZATION + Il n'y a pas assez de mémoire pour visualiser le maillage + + + SMESH_NUMBEROFCOLORS + Nombre de couleurs : + + + SMESH_NUMBEROFLABELS + Nombre d'étiquettes : + + + SMESH_NUMBEROFSTEPS + Nombre de pas : + + + SMESH_OBJECTS_SELECTED + %1_objets + + + SMESH_OBJECT_ALGORITHM + Algorithme + + + SMESH_OBJECT_GEOM + Objet géométrique + + + SMESH_OBJECT_HYPOTHESIS + Hypothèse + + + SMESH_OBJECT_MESH + Maillage + + + SMESH_OBJECT_MESHorSUBMESH + Maillage ou sous-maillage + + + SMESH_OPERATION_FAILED + L'opération n'a pas abouti + + + SMESH_OCTA + Prisme Octogonal + + + SMESH_OCTAHEDRA + Prismes octogonaux + + + TOP_OCTA + Prisme hexagonal + + + MEN_OCTA + Prisme hexagonal + + + STB_OCTA + Prisme hexagonal + + + SMESH_ORIENTATION + Orientation + + + SMESH_ORIENTATION_ELEMENTS_TITLE + Changer l'orientation + + + SMESH_OUTLINE_COLOR + Couleur de l'objet maillage + + + SMESH_PARAMETERS + Paramètres + + + SMESH_PENTA + Pentaèdre + + + SMESH_PENTAHEDRA + Pentaèdres + + + TOP_PENTA + Pentaèdre + + + MEN_PENTA + Pentaèdre + + + STB_PENTA + Pentaèdre + + + SMESH_PLANE + Plan + + + SMESH_POINT + Point + + + SMESH_POINT_1 + Point 1 + + + SMESH_POINT_2 + Point 2 + + + SMESH_BASE_POINT + Point de base + + + SMESH_POLYEDRE_CREATE_ERROR + Erreur de création du polyèdre + + + SMESH_POLYEDRON + Polyèdre + + + SMESH_POLYEDRONS + Polyèdres + + + SMESH_QUADRATIC_POLYEDRON + Polyèdre quadratique + + + SMESH_QUADRATIC_POLYEDRONS + Polyèdres quadratiques + + + SMESH_POLYGON + Polygone + + + SMESH_POLYGONS + Polygones + + + SMESH_QUADRATIC_POLYGON + Polygone quadratique + + + SMESH_QUADRATIC_POLYGONS + Polygones quadratiques + + + SMESH_POSITION_SIZE_SCALARBAR + Origine && Taille + + + SMESH_DISTRIBUTION_SCALARBAR + Distribution + + + SMESH_SHOW_DISTRIBUTION_SCALARBAR + Afficher la distribution + + + SMESH_PRECISION + Précision + + + SMESH_PREFERENCES_SCALARBAR + Préférences de la barre d'échelle + + + SMESH_PREF_SELECTION + Préférences - Sélection + + + SMESH_PRESELECTION + Présélection + + + SMESH_PRISM + Prisme + + + SMESH_PROPERTIES_SCALARBAR + Propriétés de la barre d'échelle + + + SMESH_PYRAMID + Pyramide + + + SMESH_PYRAMIDS + Pyramides + + + MEN_PYRAMID + Pyramide + + + TOP_PYRAMID + Pyramide + + + STB_PYRAMID + Pyramide + + + SMESH_QUADRANGLE + Quadrangle + + + SMESH_QUADRANGLES + Quadrangles + + + SMESH_QUADRATIC_EDGE + Arête quadratique + + + SMESH_QUADRATIC_EDGES + Arêtes quadratiques + + + SMESH_QUADRATIC_HEXAHEDRON + Hexaèdre quadratique + + + SMESH_QUADRATIC_HEXAHEDRONS + Hexaèdres quadratiques + + + SMESH_TRIQUADRATIC_HEXAHEDRON + Hexaèdre triquadratique + + + SMESH_TRIQUADRATIC_HEXAHEDRONS + Hexaèdres triquadratiques + + + TOP_TRIQUADRATIC_HEXAHEDRON + Hexaèdre triquadratique + + + MEN_TRIQUADRATIC_HEXAHEDRON + Hexaèdre triquadratique + + + STB_TRIQUADRATIC_HEXAHEDRON + Hexaèdre triquadratique + + + SMESH_QUADRATIC_PENTAHEDRON + Pentaèdre quadratique + + + SMESH_QUADRATIC_PENTAHEDRONS + Pentaèdres quadratiques + + + SMESH_QUADRATIC_PYRAMID + Pyramide quadratique + + + SMESH_QUADRATIC_PYRAMIDS + Pyramides quadratiques + + + SMESH_QUADRATIC_QUADRANGLE + Quadrangle quadratique + + + SMESH_QUADRATIC_QUADRANGLES + Quadrangles quadratiques + + + SMESH_BIQUADRATIC_QUADRANGLE + Quadrangle biquadratique + + + SMESH_BIQUADRATIC_QUADRANGLES + Quadrangles biquadratiques + + + MEN_BIQUADRATIC_QUADRANGLE + Quadrangle biquadratique + + + TOP_BIQUADRATIC_QUADRANGLE + Quadrangle biquadratique + + + STB_BIQUADRATIC_QUADRANGLE + Quadrangle biquadratique + + + SMESH_QUADRATIC_TETRAHEDRON + Tétraèdre quadratique + + + SMESH_QUADRATIC_TETRAHEDRONS + Tétraèdres quadratiques + + + SMESH_QUADRATIC_TRIANGLE + Triangle quadratique + + + SMESH_QUADRATIC_TRIANGLES + Triangles quadratiques + + + SMESH_RANGE_MAX + Valeur maximale : + + + SMESH_RANGE_MIN + Valeur minimale : + + + SMESH_RANGE_SCALARBAR + Echelle de valeurs + + + SMESH_REALLY_DELETE + Voulez-vous vraiment supprimer ces %1 objets? : %2 + + + SMESH_REMOVE + Supprimer + + + SMESH_REMOVE_ELEMENTS_TITLE + Supprimer les éléments + + + SMESH_REMOVE_NODES_TITLE + Supprimer les nœuds + + + SMESH_RENUMBERING + Renuméroter + + + SMESH_RENUMBERING_ELEMENTS_TITLE + Renuméroter les éléments + + + SMESH_RENUMBERING_NODES_TITLE + Renuméroter les nœuds + + + SMESH_REVERSE + Inverser + + + SMESH_REVOLUTION + Révolution + + + SMESH_RM_HYP_WRN + "%1" n'est pas attribué, mais: + + + + SMESH_ROTATION + Rotation + + + SMESH_ROTATION_TITLE + Rotation autour d'un axe + + + SMESH_SCALARBAR + Barre d'échelle + + + SMESH_SEGMENTS + Segments + + + SMESH_SELECTION + Sélection + + + SMESH_SELECT_FROM + Sélectionner à partir de + + + SMESH_SELECT_WHOLE_MESH + Choisir un maillage entier, un sous-maillage ou un groupe + + + SMESH_SET_COLOR + Groupe de couleur + + + SMESH_SEWING + Couture + + + SMESH_SMOOTHING + Lissage + + + SMESH_STANDARD_MESHINFO_TITLE + Information de maillage + + + SMESH_SUBMESH + Sous-maillage + + + SMESH_SUBMESH_SELECTED + %1 sous-maillages + + + SMESH_SYMMETRY + Symétrie + + + SMESH_TETRAS + Tétraèdre + + + SMESH_TETRAHEDRA + Tétraèdres + + + SMESH_TITLE + Titre : + + + SMESH_TOLERANCE + Tolérance + + + SMESH_TRANSLATION + Translation + + + SMESH_SCALE_TITLE + Transformation d'échelle + + + SMESH_DUPLICATE_TITLE + Dupliquer les nœuds + + + SMESH_SCALE + Echelle + + + SMESH_SCALE_FACTOR + Facteur d'échelle : + + + SMESH_SCALE_FACTOR_X + Facteur d'échelle X : + + + SMESH_SCALE_FACTOR_Y + Facteur d'échelle Y : + + + SMESH_SCALE_FACTOR_Z + Facteur d'échelle Z : + + + SMESH_TRANSPARENCY_OPAQUE + Opaque + + + SMESH_TRANSPARENCY_TITLE + Changer la transparence + + + SMESH_TRANSPARENCY_TRANSPARENT + Transparent + + + SMESH_TRIANGLE + Triangle + + + SMESH_TRIANGLES + Triangles + + + SMESH_UPDATEVIEW + Mettre à jour la vue + + + SMESH_VALUE + Valeur + + + SMESH_VECTOR + Vecteur + + + SMESH_VERTICAL + Verticale + + + SMESH_DISTRIBUTION_COLORING_TYPE + Type de coloration + + + SMESH_MONOCOLOR + Monocouleur + + + SMESH_DISTRIBUTION_COLOR + Couleur de la distribution: + + + SMESH_MULTICOLOR + Multicouleur + + + SMESH_VISU_PROBLEM + Impossible de visualiser le maillage, probablement à cause d'un manque de mémoire + + + SMESH_VISU_PROBLEM_CLEAR + Impossible de visualiser le maillage, pas assez de la mémoire pour montrer le message, +donc toutes les données visuelles ont été supprimées pour ne pas planter l'application. +Enregistrez votre travail avant que l'application se plante + + + SMESH_VOLUME + Volume + + + SMESH_WARNING + Avertissement + + + SMESH_WHAT_IS_TITLE + Information sur un élément de maillage + + + SMESH_WIDTH + Largeur : + + + SMESH_WRN_ALGORITHM_ALREADYEXIST + L'algorithme existe déjà + + + SMESH_WRN_COMPUTE_FAILED + Impossible de calculer le maillage + + + SMESH_WRN_EVALUATE_FAILED + Impossible d'évaluer le maillage + + + SMESH_WRN_EMPTY_NAME + Un nom vide n'est pas valide + + + SMESH_WRN_HYPOTHESIS_ALREADYEXIST + L'hypothèse existe déjà + + + SMESH_WRN_HYPOTHESIS_NOTEXIST + L'hypothèse ou l'algorithme n'existent pas + + + SMESH_WRN_MISSING_PARAMETERS + Paramètres manquants + + + SMESH_WRN_NO_AVAILABLE_DATA + Pas de données disponibles dans la sélection + + + SMESH_WRN_SELECTIONMODE_DIAGONAL + Activer le mode de sélection des références + + + SMESH_WRN_SELECTIONMODE_ELEMENTS + Activer le mode de sélection des éléments + + + SMESH_WRN_SELECTIONMODE_NODES + Activer le mode de sélection des nœuds + + + SMESH_WRN_VIEWER_VTK + Il faut ouvrir la scène dans le visualisateur VTK + + + SMESH_WRN_SIZE_LIMIT_EXCEEDED + La présentation n'a pas été mise à jour automatiquement: la nouvelle taille du maillage (%1 éléments) dépasse la limite de taille actuelle (%2 éléments). +Vérifiez la limite dans les préférences du module Mesh. + + + + SMESH_WRN_WARNING + Avertissement + + + SMESH_X + X + + + SMESH_X_SCALARBAR + X : + + + SMESH_Y + Y + + + SMESH_Y_SCALARBAR + Y : + + + SMESH_Z + Z + + + STATE_ALGO_MISSING + Il manque l'algorithme %3 %2D + + + STATE_HYP_BAD_GEOMETRY + L'algorithme %3 %2D "%1" est attribué à une géométrie qui ne convient pas + + + STATE_HYP_BAD_PARAMETER + Il y a un paramètre incorrect dans l'hypothèse %3 %2D de l'algorithme "%1" + + + STATE_HYP_MISSING + L'hypothèse %4D manque à l'algorithme %3 %2D "%1" + + + STATE_HYP_NOTCONFORM + L'algorithme %3 %2D "%1" produit un maillage non-conforme: l'hypothèse globale "Maillage non conforme autorisé" doit être cochée + + + STB_ADV_INFO + Monter l'information de base sur le maillage + + + STB_ALL + Tous + + + STB_AREA + Aire + + + STB_ASPECT + Rapport de forme + + + STB_ASPECT_3D + Rapport de forme 3D + + + STB_AUTO_COLOR + Couleur automatique + + + STB_AUTO_UPD + Mise à jour automatique + + + STB_BUILD_COMPOUND + Construire un maillage assemblé + + + STB_COPY_MESH + Copie le maillage + + + STB_CLIP + Pan de coupe + + + STB_COLORS + Paramètres d'affichage + + + STB_COMPUTE + Calculer + + + STB_PRECOMPUTE + Prévisualiser + + + STB_EVALUATE + Evaluer + + + STB_CONNECTION + Frontières sur connexion multiples + + + STB_CONNECTION_2D + Frontières sur connexion multiples 2D + + + STB_CONSTRUCT_GROUP + Construire un groupe + + + STB_CONV_TO_QUAD + Convertir vers/de quadratique + + + STB_2D_FROM_3D + Créer les éléments de frontière + + + STB_MESH_ORDER + Changer la priorité des sous-maillages + + + STB_CREATE_GROUP + Créer un groupe + + + STB_CREATE_GEO_GROUP + Créer des groupes à partir de la géométrie + + + STB_CREATE_MESH + Créer un maillage + + + STB_CREATE_SUBMESH + Créer un sous-maillage + + + STB_CUT + Découpe des quadrangles + + + STB_CUT_GROUP + Découper les groupes + + + STB_IMPORT_DAT + Importer un fichier DAT + + + STB_DAT + Exporter un fichier DAT + + + STB_DELETE + Supprimer + + + STB_DEL_GROUP + Supprimer les groupes et leur contenu + + + STB_FACE_ORIENTATION + Orientation des faces + + + STB_DISABLE_AUTO_COLOR + Désactiver la couleur automatique + + + STB_DISPLAY_ONLY + Afficher uniquement + + + STB_DISP_ENT + Visualiser une entité + + + STB_ELEM0D + Elément 0D + + + STB_ELEMS0D + Eléments 0D + + + STB_BALLS + Eléments particulaires + + + STB_BALL + Elément particulaire + + + STB_EDGE + Arête + + + STB_EDGES + Arêtes + + + STB_EDIT_GROUP + Editer un groupe + + + STB_EDIT_GEOMGROUP_AS_GROUP + Editer un groupe en tant qu'autonome + + + STB_EDIT_HYPO + Editer une hypothèse + + + STB_EDIT_MESHSUBMESH + Editer un maillage/sous-maillage + + + STB_EXPORT_DAT + Exporter au format DAT + + + STB_EXPORT_MED + Exporter au format MED + + + STB_EXPORT_CGNS + Exporter au format CGNS + + + STB_EXPORT_SAUV + Exporter au format SAUV (ASCII) + + + STB_EXPORT_STL + Exporter au format STL + + + STB_EXPORT_UNV + Exporter au format UNV + + + STB_EXTRUSION + Extrusion + + + STB_EXTRUSION_ALONG + Extrusion suivant un chemin + + + STB_FACES + Faces + + + STB_BARE_BORDER_VOLUME + Volumes avec éléments de peau 2D manquants + + + STB_BARE_BORDER_FACE + Faces avec éléments de peau 1D manquants + + + STB_OVER_CONSTRAINED_VOLUME + Volumes sur-contraints + + + STB_OVER_CONSTRAINED_FACE + Faces sur-contraintes + + + STB_FREE_BORDER + Frontières libres + + + STB_FREE_EDGE + Arêtes libres + + + STB_FREE_NODE + Nœuds libres + + + STB_FREE_FACES + Faces libres + + + STB_GLOBAL_HYPO + Hypothèse globale + + + STB_HEXA + Hexaèdre + + + STB_HIDE + Cacher + + + STB_INT_GROUP + Intersection des groupes + + + STB_INV + Inversion de diagonale + + + STB_LENGTH + Longueur + + + STB_LENGTH_2D + Longueur 2D + + + STB_MAP + Projection de motif + + + STB_MAX_ELEMENT_LENGTH_2D + Diamètre de l'élément 2D + + + STB_MAX_ELEMENT_LENGTH_3D + Diamètre de l'élément 3D + + + STB_IMPORT_MED + Importer un fichier MED + + + STB_MED + Exporter un fichier MED + + + STB_IMPORT_STL + Importer un fichier STL + + + STB_STL + Exporter un fichier STL + + + STB_IMPORT_CGNS + Importer un fichier CGNS + + + STB_CGNS + Exporter un fichier CGNS + + + STB_IMPORT_SAUV + Importer un fichier SAUV (ASCII) + + + STB_SAUV + Exporter un fichier SAUV (ASCII) + + + STB_MERGE + Fusionner les nœuds + + + STB_MERGE_ELEMENTS + Fusionner les éléments + + + STB_MESH_THROU_POINT + Déplacer un nœud + + + STB_MIN_ANG + Angle minimal + + + STB_MOVE + Déplacer un nœud + + + STB_NODE + Nœud + + + STB_NODES + Nœuds + + + STB_NUM_ELEMENTS + Visualiser les éléments + + + STB_NUM_NODES + Visualiser les nœuds + + + STB_ORIENT + Orientation + + + STB_POLYGON + Polygone + + + STB_POLYHEDRON + Polyèdre + + + STB_PRECISION + Précision + + + STB_QUAD + Quadrangle + + + STB_QUADRATIC_EDGE + Arête quadratique + + + STB_QUADRATIC_HEXAHEDRON + Hexaèdre quadratique + + + STB_QUADRATIC_PENTAHEDRON + Pentaèdre quadratique + + + STB_QUADRATIC_PYRAMID + Pyramide quadratique + + + STB_QUADRATIC_QUADRANGLE + Quadrangle quadratique + + + STB_QUADRATIC_TETRAHEDRON + Tétraèdre quadratique + + + STB_QUADRATIC_TRIANGLE + Triangle quadratique + + + STB_REMOVE_ELEMENTS + Supprimer les éléments + + + STB_REMOVE_NODES + Supprimer les nœuds + + + STB_REMOVE_ORPHAN_NODES + Supprimer les nœuds orphelins + + + STB_RENAME + Renommer + + + STB_RENUM_ELEMENTS + Renuméroter les éléments + + + STB_RENUM_NODES + Renuméroter les nœuds + + + STB_RESET + Restaurer + + + STB_SAVE_DISTRIBUTION + Enregistrer la distribution dans un fichier + + + STB_SHOW_DISTRIBUTION + Afficher la distribution + + + STB_REVOLUTION + Révolution + + + STB_ROT + Rotation + + + STB_SCALAR_BAR + Barre d'échelle + + + STB_SCALAR_BAR_PROP + Propriétés de la barre d'échelle + + + STB_SELECTION + Sélection + + + STB_SEL_FILTER_LIB + Librairie des filtres de sélection + + + STB_SEW + Couture + + + STB_SHADE + Ombrage + + + STB_SHOW + Afficher + + + STB_SHRINK + Contraction + + + STB_SKEW + Inclinaison d'angle + + + STB_SMOOTH + Lissage + + + STB_STD_INFO + Informations sur le maillage + + + STB_SYM + Symétrie + + + STB_TAPER + Cône + + + STB_TETRA + Tétraèdre + + + STB_TRANS + Translation + + + STB_SCALE + Mise à l'échelle + + + STB_DUPLICATE_NODES + Dupliquer les nœuds + + + STB_TRANSP + Transparence + + + STB_TRIANGLE + Triangle + + + STB_UNASSIGN + Désassocier + + + STB_UNION + Union des triangles + + + STB_UNION2 + Union de deux triangles + + + STB_IMPORT_UNV + Importer un fichier UNV + + + STB_UNV + Exporter un fichier UNV + + + STB_UN_GROUP + Union des groupes + + + STB_UNDERLYING_ELEMS + Créer les groupes d'entités à partir des groupes existants de dimensions supérieures + + + STB_UPDATE + Mettre à jour + + + STB_VOLUMES + Volumes + + + STB_VOLUME_3D + Volume + + + STB_WARP + Angle de déformation + + + STB_WHAT_IS + Information sur un élément ou un nœud de maillage + + + STB_WIRE + Contour + + + TAPER_ELEMENTS + Cône + + + TB_ADD_REMOVE + Ajouter/supprimer la barre d'outils + + + TB_CTRL + Barre d'outils des contrôles + + + TB_DISP_MODE + Barre du mode de visualisation + + + TB_HYPO + Barre d'hypothèses + + + TB_MESH + Barre de maillage + + + TB_MODIFY + Barre des modifications + + + TOP_ADV_INFO + Informations sur le maillage + + + TOP_ALL + Tous + + + TOP_AREA + Aire + + + TOP_ASPECT + Rapport de forme + + + TOP_ASPECT_3D + Rapport de forme 3D + + + TOP_AUTO_COLOR + Couleur automatique + + + TOP_AUTO_UPD + Mise à jour automatique + + + TOP_BUILD_COMPOUND + Construire un maillage assemblé + + + TOP_COPY_MESH + Copier le maillage + + + TOP_CLIP + Plan de coupe + + + TOP_COLORS + Paramètres d'affichage + + + TOP_COMPUTE + Calculer + + + TOP_PRECOMPUTE + Prévisualiser + + + TOP_EVALUATE + Evaluer + + + TOP_CONNECTION + Frontières sur connexions multiples + + + TOP_CONNECTION_2D + Frontières sur connexions multiples 2D + + + TOP_CONSTRUCT_GROUP + Construire un groupe + + + TOP_CONV_TO_QUAD + Convertir vers/de quadratique + + + TOP_2D_FROM_3D + Créer les éléments de frontière + + + TOP_MESH_ORDER + Changer la priorité des sous-maillages + + + TOP_CREATE_GROUP + Créer un groupe + + + TOP_CREATE_GEO_GROUP + Créer des groupes liés à la géométrie + + + TOP_CREATE_MESH + Créer un maillage + + + TOP_CREATE_SUBMESH + Créer un sous-maillage + + + TOP_CUT + Découpe des quadrangles + + + TOP_CUT_GROUP + Découper les groupes + + + TOP_IMPORT_DAT + Importer un fichier DAT + + + TOP_DAT + Exporter un fichier DAT + + + TOP_DELETE + Supprimer + + + TOP_DEL_GROUP + Supprimer les groupes et leur contenu + + + TOP_FACE_ORIENTATION + Orientation des faces + + + TOP_DISABLE_AUTO_COLOR + Désactiver la couleur automatique + + + TOP_DISPLAY_ONLY + Afficher uniquement + + + TOP_DISP_ENT + Visualiser une entité + + + TOP_ELEM0D + Elément 0D + + + TOP_ELEMS0D + Eléments 0D + + + TOP_BALL + Particulaire + + + TOP_BALLS + Particulaires + + + TOP_EDGE + Arête + + + TOP_EDGES + Arêtes + + + TOP_EDIT_GROUP + Editer un groupe + + + TOP_EDIT_GEOMGROUP_AS_GROUP + Editer un groupe en tant qu'autonome + + + TOP_EDIT_HYPO + Editer l'hypothèse + + + TOP_EDIT_MESHSUBMESH + Editer un maillage/sous-maillage + + + TOP_EXPORT_DAT + Exporter au format DAT + + + TOP_EXPORT_MED + Exporter au format MED + + + TOP_EXPORT_SAUV + Exporter au format SAUV (ASCII) + + + TOP_EXPORT_STL + Exporter au format STL + + + TOP_EXPORT_UNV + Exporter au format UNV + + + TOP_EXTRUSION + Extrusion + + + TOP_EXTRUSION_ALONG + Extrusion suivant un chemin + + + TOP_FACES + Faces + + + TOP_BARE_BORDER_VOLUME + Volumes avec éléments de peau 2D manquants + + + TOP_BARE_BORDER_FACE + Faces avec éléments de peau 1D manquants + + + TOP_OVER_CONSTRAINED_VOLUME + Volumes sur-contraints + + + TOP_OVER_CONSTRAINED_FACE + Faces sur-contraintes + + + TOP_FREE_BORDER + Frontières libres + + + TOP_FREE_EDGE + Arêtes libres + + + TOP_FREE_NODE + Nœuds libres + + + TOP_FREE_FACES + Faces libres + + + TOP_GLOBAL_HYPO + Hypothèse globale + + + TOP_HEXA + Hexaèdre + + + TOP_HIDE + Cacher + + + TOP_INT_GROUP + Intersection de groupes + + + TOP_INV + Inversion de diagonale + + + TOP_LENGTH + Longueur + + + TOP_LENGTH_2D + Longueur 2D + + + TOP_MAP + Projection de motif + + + TOP_MAX_ELEMENT_LENGTH_2D + Diamètre des éléments 2D + + + TOP_MAX_ELEMENT_LENGTH_3D + Diamètre des éléments 3D + + + TOP_IMPORT_MED + Importer un fichier MED + + + TOP_MED + Exporter un fichier MED + + + TOP_IMPORT_STL + Importer un fichier STL + + + TOP_STL + Exporter un fichier STL + + + TOP_IMPORT_CGNS + Importer un fichier CGNS + + + TOP_CGNS + Exporter un fichier CGNS + + + TOP_IMPORT_SAUV + Importer un fichier SAUV (ASCII) + + + TOP_SAUV + Exporter un fichier SAUV (ASCII) + + + TOP_MERGE + Fusionner les nœuds + + + TOP_MERGE_ELEMENTS + Fusionner les éléments + + + TOP_MESH_THROU_POINT + Déplacer un nœud + + + TOP_MIN_ANG + Angle minimal + + + TOP_MOVE + Déplacer un nœud + + + TOP_NODE + Nœud + + + TOP_NODES + Nœuds + + + TOP_NUM_ELEMENTS + Visualiser les éléments + + + TOP_NUM_NODES + Visualiser les nœuds + + + TOP_ORIENT + Orientation + + + TOP_POLYGON + Polygone + + + TOP_POLYHEDRON + Polyèdre + + + TOP_PRECISION + Précision + + + TOP_QUAD + Quadrangle + + + TOP_QUADRATIC_EDGE + Arête quadratique + + + TOP_QUADRATIC_HEXAHEDRON + Hexaèdre quadratique + + + TOP_QUADRATIC_PENTAHEDRON + Pentaèdre quadratique + + + TOP_QUADRATIC_PYRAMID + Pyramide quadratique + + + TOP_QUADRATIC_QUADRANGLE + Quadrangle quadratique + + + TOP_QUADRATIC_TETRAHEDRON + Tétraèdre quadratique + + + TOP_QUADRATIC_TRIANGLE + Triangle quadratique + + + TOP_REMOVE_ELEMENTS + Supprimer les éléments + + + TOP_REMOVE_NODES + Supprimer les nœuds + + + TOP_REMOVE_ORPHAN_NODES + Supprimer les nœuds orphelins + + + TOP_RENAME + Renommer + + + TOP_RENUM_ELEMENTS + Renuméroter les éléments + + + TOP_RENUM_NODES + Renuméroter les nœuds + + + TOP_RESET + Restaurer + + + TOP_SAVE_DISTRIBUTION + Exporter la distribution + + + TOP_SHOW_DISTRIBUTION + Afficher la distribution + + + TOP_REVOLUTION + Révolution + + + TOP_ROT + Rotation + + + TOP_SCALAR_BAR + Barre d'échelle + + + TOP_SCALAR_BAR_PROP + Propriétés de la barre d'échelle + + + TOP_SELECTION + Sélection + + + TOP_SEL_FILTER_LIB + Librairie des filtres de sélection + + + TOP_SEW + Couture + + + TOP_SHADE + Ombrage + + + TOP_SHOW + Afficher + + + TOP_SHRINK + Contraction + + + TOP_SKEW + Inclinaison d'angle + + + TOP_SMOOTH + Lissage + + + TOP_STD_INFO + Informations sur le maillage + + + TOP_SYM + Symétrie + + + TOP_TAPER + Cône + + + TOP_TETRA + Tétraèdre + + + TOP_TRANS + Translation + + + TOP_SCALE + Mise à l'échelle + + + TOP_DUPLICATE_NODES + Dupliquer les nœuds + + + TOP_TRANSP + Transparence + + + TOP_TRIANGLE + Triangle + + + TOP_UNASSIGN + Désassocier + + + TOP_UNION + Union des triangles + + + TOP_UNION2 + Union de deux triangles + + + TOP_IMPORT_UNV + Importer un fichier UNV + + + TOP_UNV + Exporter un fichier UNV + + + TOP_UN_GROUP + Union des groupes + + + TOP_UNDERLYING_ELEMS + Créer les groupes d'entités à partir des groupes existants de dimensions supérieures + + + TOP_UPDATE + Mettre à jour + + + TOP_VOLUMES + Volumes + + + TOP_VOLUME_3D + Volume + + + TOP_WARP + Angle de déformation + + + TOP_WHAT_IS + Information sur l'élément de maillage + + + TOP_WIRE + Contours + + + UNKNOWN_CONTROL + Inconnu + + + VOLUME_3D_ELEMENTS + Volume + + + WARP_ELEMENTS + Déformation + + + MEN_FILE_INFO + Information du fichier MED + + + SMESH_WRN_NO_APPROPRIATE_SELECTION + Aucun objet sélectionné ne convient + + + MEN_CLEAR_MESH + Effacer les données du maillage + + + TOP_CLEAR_MESH + Effacer les données du maillage + + + STB_CLEAR_MESH + Effacer les données du maillage + + + SMESH_IMPORT_MESH + Importer les donnés du maillage à partir des fichiers + + + SMESH_ERR_NOT_SUPPORTED_FORMAT + Le format de fichier n'est pas supporté + + + SMESH_ERR_UNKNOWN_IMPORT_ERROR + Erreur inconnue + + + SMESH_IMPORT_ERRORS + L'importation s'est terminée avec des erreurs + + + SMESH_DRS_SOME_EMPTY + Un ou plusieurs fichiers de maillage sont vides, les données n'ont pas été publiées + + + NO_MESH_SELECTED + Aucun maillage sélectionné + + + SMESH_PREF_def_precision + Précision par défaut + + + SMESH_PREF_length_precision + Précision de la longueur + + + SMESH_PREF_angle_precision + Précision angulaire + + + SMESH_PREF_len_tol_precision + Précision de tolérance de la longueur + + + SMESH_PREF_parametric_precision + Précision paramétrique + + + SMESH_PREF_area_precision + Précision de l'aire + + + FULL_RECOMPUTE_QUESTION + +Le maillage a été édité après le dernier calcul complet, +ceci peut empêcher un calcul correct. +Voulez-vous recalculer le maillage entier pour rejeter les modifications ? + + + + SMESH_PREF_vol_precision + Précision du volume + + + SMESH_PRECISION_HINT + +Il est possible de modifier la précision de la valeur d'entrée +avec le paramètre '%1' des préférences du module Mesh. + + + REMOVE_ORPHAN_NODES_QUESTION + Voulez-vous supprimer tous les nœuds orphelins ? + + + NB_NODES_REMOVED + %1 nœud(s) supprimés. + + + SMESH_SAVE_DISTRIBUTION + Exporter la distribution + + + SMESH_PLUGINS_OTHER + Extensions SMESH + + + MESH_LOADING_MSG + Chargement du maillage %0 en cours, veuillez patienter... + + + MESH_LOADING_MSG_FINISHED + Chargement du maillage %0 terminé + + + BALL_DIAMETER + Diamètre + + + BALL_ELEMENT + Particulaire + + + DEP_OBJECT + L'objet sélectionné a été utilisé pour en créer un autre. +Il ne peut pas être supprimé. + + + + SMESHGUI_GroupDlg + + ALLOW_ELEM_LIST_MODIF + Edition manuelle + + + SELECT_ALL + Sélectionner tout + + + + SMESHGUI + + NOT_A_VTK_VIEWER + Cette commande n'est disponible qu'à partir d'une fenêtre VTK. +Ouvrez une fenêtre VTK et essayez de nouveau + + + PREF_AUTO_GROUPS + Créer les groupes automatiquement pour l'export MED + + + PREF_GROUP_SEGMENT_LENGTH + Paramètres automatiques + + + PREF_SEGMENT_LENGTH + Ratio de la diagonale de la boîte englobante / taille maximale + + + PREF_NB_SEGMENTS + Nombre de segments par défaut + + + SMESH_PREF_MESH_LOADING + Chargement de maillage + + + PREF_FORGET_MESH_AT_HYP_MODIF + Ne pas charger le maillage du fichier d'étude à la modification d'une hypothèse + + + PREF_AUTO_UPDATE + Mettre à jour automatiquement + + + PREF_UPDATE_LIMIT + Limite de taille (nombre d'éléments) + + + PREF_UPDATE_LIMIT_NOLIMIT + Sans limite + + + PREF_BACKFACE + Face arrière + + + PREF_WIREFRAME + Couleur de contour + + + PREF_SELECTION + Couleur de sélection + + + PREF_PRE_SELECTION + Couleur de pré-sélection + + + PREF_COLOR + Couleur + + + PREF_ORIENTATION_COLOR + Couleur + + + PREF_ORIENTATION_3D_VECTORS + Vecteurs 3D + + + PREF_ORIENTATION_SCALE + Echelle + + + PREF_DISPLAY_ENTITY + Eléments à visualiser + + + QUADRATIC_REPRESENT_MODE + Représentation des éléments quadratiques 2D + + + MAX_ARC_ANGLE + Angle maximal + + + PREF_DISPLAY_MODE + Mode de visualisation + + + PREF_ELEMENTS + Eléments + + + PREF_ELEMENT_COLOR + Couleur d'élément + + + PREF_FILL + Remplir + + + PREF_NOTIFY_MODE + Montrer la notification sur le résultat de calcul + + + PREF_NOTIFY_NEVER + Jamais + + + PREF_NOTIFY_ERROR + Erreurs uniquement + + + PREF_NOTIFY_ALWAYS + Toujours + + + PREF_ELEM_INFO + Information des éléments du maillage + + + PREF_ELEM_INFO_SIMPLE + Simple + + + PREF_ELEM_INFO_TREE + Arbre + + + PREF_GPP_NODES_LIMIT + Calcul automatique du nombre de nœuds: limite + + + SMESH_PREF_GROUP_PRECISION + Précision des champs d'entrée + + + SMESH_PREF_GROUP_PREVIEW + Prévisualisation + + + PREF_GROUP_ELEMENTS + Eléments + + + PREF_GROUP_EXPORT + Exporter un maillage + + + PREF_GROUP_FACES_ORIENTATION + Orientation des faces + + + PREF_GROUP_COMPUTE + Calculer le maillage + + + PREF_GROUP_NODES + Nœuds + + + PREF_GROUP_GROUPS + Groupes + + + PREF_GRP_NAMES + Couleur des noms + + + PREF_GROUP_PRECISION + Précision + + + PREF_GROUP_PRESELECTION + Présélection + + + PREF_GROUP_QUALITY + Contrôles de qualité + + + PREF_GROUP_SELECTION + Sélection + + + PREF_GROUP_INFO + Information du maillage + + + PREF_HIGHLIGHT_COLOR + Couleur de sélection + + + PREF_LABELS_COLOR + Couleur des étiquettes + + + PREF_MARKER_SCALE + Echelle du marqueur + + + PREF_NODES + Nœuds + + + PREF_OBJECTS + Objets + + + PREF_OBJECT_COLOR + Couleur d'objet + + + PREF_OUTLINE + Silhouette + + + PREF_PRECISION_USE + Utiliser la précision + + + PREF_PRECISION_VALUE + Nombre de chiffres après la virgule + + + PREF_EQUAL_NODES_TOL + Tolérance des nœuds doubles + + + PREF_RENUMBER + Renuméroter automatiquement + + + PREF_SHRINK_COEFF + Coefficient de contraction + + + PREF_PYTHON_DUMP + Dump Python + + + PREF_HISTORICAL_PYTHON_DUMP + Dump Python historique + + + PREF_TAB_GENERAL + Général + + + PREF_TAB_MESH + Maillage + + + PREF_TAB_SELECTION + Sélection + + + PREF_TITLE_COLOR + Couleur du titre + + + PREF_TYPE_OF_MARKER + Type de marqueur + + + PREF_COLOR_0D + Eléments 0D + + + PREF_SIZE_0D + Taille des éléments 0D + + + PREF_BALL_COLOR + Particulaires + + + PREF_BALL_SIZE + Taille des éléments particulaires + + + PREF_WIDTH + Epaisseur + + + PREF_PREVIEW_CHUNK_SIZE + Taille des blocs pour la prévisualisation des sous-shapes + + + + SMESHGUI_AddQuadraticElementDlg + + SMESH_ADD_QUADRATIC_EDGE + Ajouter une arête quadratique + + + SMESH_ADD_QUADRATIC_HEXAHEDRON + Ajouter un hexaèdre quadratique + + + SMESH_ADD_TRIQUADRATIC_HEXAHEDRON + Ajouter un hexaèdre triquadratique + + + SMESH_ADD_QUADRATIC_PENTAHEDRON + Ajouter un pentaèdre quadratique + + + SMESH_ADD_QUADRATIC_PYRAMID + Ajouter une pyramide quadratique + + + SMESH_ADD_QUADRATIC_QUADRANGLE + Ajouter un quadrangle quadratique + + + SMESH_ADD_BIQUADRATIC_QUADRANGLE + Ajouter un quadrangle biquadratique + + + SMESH_ADD_QUADRATIC_TETRAHEDRON + Ajouter un tétraèdre quadratique + + + SMESH_ADD_QUADRATIC_TRIANGLE + Ajouter un triangle quadratique + + + SMESH_CORNER_NODES + Nœuds angulaires: + + + SMESH_MIDFACE_NODES + Nœuds à mi-face : + + + SMESH_CENTER_NODE + Nœud central: + + + SMESH_FIRST + Premier + + + SMESH_LAST + Dernier + + + SMESH_MIDDLE + Milieu + + + + SMESHGUI_BuildCompoundDlg + + COMPOUND + Assemblage + + + COMPOUND_MESH + Maillage d'assemblage + + + CREATE_COMMON_GROUPS + Créer des groupes communs pour les maillages initiaux + + + MERGE_NODES_AND_ELEMENTS + Fusionner les nœuds et les éléments coïncidents + + + MESHES + Maillages + + + PROCESSING_IDENTICAL_GROUPS + Traitement des groupes identiques + + + RENAME + Renommer + + + RESULT_NAME + Nom du résultat + + + UNITE + Réunir + + + + SMESHGUI_ChangeOrientationDlg + + CAPTION + Modifier l'orientation + + + + SMESHGUI_ComputeDlg + + CAPTION + Le calcul du maillage a échoué + + + CONSTRUCTOR + Calculer le maillage + + + EVAL_DLG + Evaluer le maillage + + + ERRORS + Erreurs + + + MEMORY_LACK + Problème d'allocation de mémoire + + + COMPUTE_WARNING + Le maillage semble correct mais des erreurs sont apparues + + + PUBLISH_SHAPE + Publier un sous-objet + + + SHOW_SHAPE + Montrer un sous-objet + + + SHOW_BAD_MESH + Montrer le maillage incorrect + + + + SMESHGUI_PrecomputeDlg + + CAPTION + Prévisualiser et calculer le maillage + + + PREVIEW + Prévisualiser + + + PREVIEW_1 + Maillage 1D + + + PREVIEW_2 + Maillage 2D + + + COMPUTE + Calculer + + + + SMESHGUI_PrecomputeOp + + CLEAR_SUBMESH_QUESTION + Des sous-maillages temporaires ont été créés sur la géométrie sélectionnée +au cours de l'opération de prévisualisation. +Voulez-vous supprimer toutes ces sous-maillages ? + + + SMESH_WRN_NOTHING_PREVIEW + La prévisualisation du maillage n'est pas disponible + + + SMESH_REJECT_MESH_ORDER + La priorité des sous-maillages a été changée au cours de la prévisualisation. +Voulez-vous restaurer la priorité initiale ? + + + + SMESHGUI_ConvToQuadDlg + + CAPTION + Convertir vers/de quadratique + + + MEDIUMNDS + Nœuds milieux sur la géométrie + + + MESH + Maillage ou sous-maillage + + + RADIOBTN_1 + Convertir en éléments quadratiques + + + RADIOBTN_2 + Convertir à partir d'éléments quadratiques + + + NON_CONFORM_WARNING + Attention: le maillage généré sera a priori non-conforme + + + + SMESHGUI_ConvToQuadOp + + MESH_IS_NOT_SELECTED + Le maillage n'est pas sélectionné +Indiquez-le et essayez de nouveau + + + REF_IS_NULL + Aucun maillage valide n'est sélectionné + + + + SMESHGUI_CreatePatternDlg + + CAPTION + Projection de motif + + + DEFAULT_2D + Motif_2d + + + DEFAULT_3D + Motif_3d + + + ERROR_OF_CREATION + Une erreur interne s'est produite au cours de la création du motif +Vérifiez la validité des informations données + + + ERROR_OF_SAVING + Une erreur interne s'est produite au cours de l'enregistrement du motif. +Vérifiez l'espace de disque disponible et vos droits d'écriture dans ce fichier + + + ERR_LOADF_CANT_PROJECT + Impossible d'appliquer la projection des nœuds vers la face + + + ERR_LOADF_CLOSED_FACE + Impossible de créer un motif à partir d'une face avec une arête de couture + + + ERR_LOADF_NARROW_FACE + Impossible de créer un motif à partir d'une face étroite + + + ERR_LOADV_BAD_SHAPE + Il n'est possible de créer un motif que d'une coque fermée ou d'un solide avec 6 faces + + + ERR_LOADV_COMPUTE_PARAMS + Il est impossible de calculer les paramètres du point + + + ERR_LOAD_EMPTY_SUBMESH + Il n'y a pas d'éléments pour créer de motif + + + MESH_OR_SUBMESH + Maillage ou sous-maillage + + + PATTERN + Motif + + + PATTERN_FILT + Fichiers de motif (*.smp) + + + PATTERN_NAME + Nom du motif + + + PATTERN_TYPE + Type du motif + + + PROJECT + Projeter les nœuds sur la face + + + SAVE + Sauvegarder... + + + SAVE_PATTERN + Sauvegarder le motif + + + + SMESHGUI_CreatePolyhedralVolumeDlg + + FACES_BY_NODES + Faces par nœuds + + + SMESH_POLYEDRE_CREATE_ERROR + Erreur de création du polyèdre. + + + SMESH_POLYEDRE_PREVIEW + Prévisualiser le polyèdre + + + + SMESHGUI_CuttingOfQuadsDlg + + CAPTION + Découpe des quadrangles + + + + SMESHGUI_DeleteGroupDlg + + CAPTION + Supprimer les groupes et leur contenu + + + NO_SELECTED_GROUPS + Il n'y a aucun groupe sélectionné +Choisissez un groupe et essayez de nouveau + + + SELECTED_GROUPS + Groupes sélectionnés + + + + SMESHGUI_MergeDlg + + COINCIDENT_ELEMENTS + Eléments coïncidents + + + COINCIDENT_NODES + Nœuds coïncidents + + + DETECT + Détecter + + + EDIT_SELECTED_GROUP + Editer le groupe sélectionné + + + SELECT_ALL + Tout sélectionner + + + EXCLUDE_GROUPS + Exclure les groupes + + + + SMESHGUI_ExtrusionAlongPathDlg + + BAD_SHAPE_TYPE + La géométrie choisie en tant que chemin n'est pas une arête + + + CANT_GET_TANGENT + Impossible d'obtenir la tangente pour un des nœuds du chemin + + + EXTRUSION_0D + Extrusion des éléments 0D + + + EXTRUSION_1D + Extrusion des éléments 1D + + + EXTRUSION_2D + Extrusion des éléments 2D + + + EXTRUSION_ALONG_PATH + Extrusion suivant un chemin + + + EXTR_BAD_STARTING_NODE + Nœud de départ du chemin incorrect + + + LINEAR_ANGLES + Variation linéaire des angles + + + NO_ELEMENTS_SELECTED + Aucun élément de maillage n'est sélectionné pour l'extrusion + + + SELECTED_PATH_IS_NOT_EDGE + Le maillage du chemin doit être du type arête + + + SMESH_ANGLES + Angles de rotation + + + SMESH_BASE_POINT + Point de base + + + SMESH_PATH + Chemin + + + SMESH_PATH_MESH + Maillage ou sous-maillage + + + SMESH_PATH_SHAPE + Géométrie (arête) + + + SMESH_PATH_START + Nœud de début + + + SMESH_USE_ANGLES + Utiliser les angles + + + SMESH_USE_BASE_POINT + Utiliser le point de base + + + WRONG_ANGLES_NUMBER + Le nombre d'angles doit correspondre au nombre des nœuds du chemin + + + + SMESHGUI_ExtrusionDlg + + EXTRUSION_0D + Extrusion de noeuds + + + EXTRUSION_1D + Extrusion des éléments 1D + + + EXTRUSION_2D + Extrusion des éléments 2D + + + EXTRUSION_ALONG_LINE + Extrusion suivant une ligne + + + + SMESHGUI_FilterDlg + + BAD_SHAPE_NAME + Il n'y a pas d'objet géométrique "%1" dans l'étude actuelle +Sélectionnez un objet valide et essayez de nouveau + + + CURRENT_DIALOG + Groupe actuel + + + EDGES_TLT + Filtre d'arêtes + + + FACES_TLT + Filtre de faces + + + MESH + Maillage + + + NODES_TLT + Filtre de nœuds + + + SELECTION + Sélection initiale + + + SET_IN_VIEWER + Insérer le filtre dans la fenêtre 3D + + + SHAPE_IS_NOT_A_CYLINDER + "%1" n'est pas une face cylindrique +Sélectionnez une face cylindrique et essayez de nouveau + + + SHAPE_IS_NOT_A_FACE + "%1" n'est pas une face +Sélectionnez une face et essayez de nouveau + + + SHAPE_IS_NOT_A_PLANE + "%1" n'est pas un plan +Sélectionnez un plan et essayez de nouveau + + + FACE_ID_NOT_SELECTED + Aucune face de maillage n'est sélectionnée. +Indiquez-la et essayez de nouveau + + + NOT_FACE_ID + "%1" ne correspond à aucun ID valide d'une face du maillage. +Sélectionnez une face et essayez de nouveau + + + SOURCE + Source + + + TLT + Filtre de sélection + + + VOLUMES_TLT + Filtre de volumes + + + + SMESHGUI_FilterLibraryDlg + + ADD + Ajouter + + + ADD_TO_TLT + Ajouter le filtre de sélection à la librairie + + + ALL_FILES_FILTER + Tous les fichiers (*.*) + + + ASSIGN_NEW_NAME + La librairie déjà contient un filtre avec le nom "%1" +Le nouveau nom "%2" est attribué au filtre ajouté + + + COPY_FROM_TLT + Copier le filtre de la sélection de la librairie + + + DELETE + Supprimer + + + EDGE + Arête + + + EDIT_LIB_TLT + Librairie des filtres de sélection + + + ELEMENT + Elément + + + EMPTY_FILTER_NAME + Le nom du filtre est vide +Indiquez un nom non-vide + + + ERROR_FILTER_NAME + Le nom du filtre n'est pas unique +Indiquez un autre nom + + + ERROR_LOAD + Il est impossible de charger la librairie +Vérifiez le nom du fichier de la librairie et ses propriétés + + + ERROR_OF_ADDING + Une erreur interne s'est produite à l'addition d'un nouveau filtre dans la librairie. +Vérifiez la validité des informations données + + + ERROR_OF_COPYING + Une erreur interne s'est produite à la copie d'un filtre depuis la librairie. +Vérifiez la validité des informations données + + + ERROR_OF_DELETING + Une erreur interne s'est produite lors de la suppression d'un filtre depuis la librairie. +Vérifiez la validité des informations données + + + ERROR_OF_EDITING + Une erreur interne s'est produite à l'édition d'un filtre dans la librairie. +Vérifiez la validité des informations données + + + ERROR_OF_SAVING + Une erreur s'est produite à la sauvegarde de la librairie des filtres. +Vérifiez la validité des informations données + + + FACE + Face + + + FILTER + Filtre + + + FILTER_NAME + Nom du filtre + + + FILTER_NAMES + Noms des filtres + + + LIBRARY_FILE + Nom du fichier de librairie + + + LIBRARY_IS_NOT_LOADED + La librairie n'est pas ouverte. Ouvrez la librairie et essayez de nouveau + + + LIB_NAME + FilterLib.xml + + + NODE + Nœud + + + NO_PERMISSION + Vous n'avez pas la permission d'écrire dans ce fichier + + + OPEN_LIBRARY + Ouvrir la librairie + + + SELECTION + Sélection + + + VOLUME + Volume + + + XML_FILT + Fichiers XML (*.xml) + + + + SMESHGUI_FilterTable + + ADD + Ajouter + + + ADDITIONAL_PARAMETERS + Paramètres supplémentaires + + + ADD_TO + Ajouter à... + + + AND + Et + + + AREA + Aire + + + ASPECT_RATIO + Rapport de forme + + + ASPECT_RATIO_3D + Rapport de forme 3D + + + BAD_ORIENTED_VOLUME + Volume mal orienté + + + BARE_BORDER_VOLUME + Volumes avec éléments de peau 2D manquants + + + BARE_BORDER_FACE + Faces avec éléments de peau 1D manquants + + + OVER_CONSTRAINED_VOLUME + Volumes sur-contraints + + + OVER_CONSTRAINED_FACE + Faces sur-contraintes + + + BELONG_TO_CYLINDER + Appartient au cylindre + + + BELONG_TO_GENSURFACE + Appartient à la surface + + + BELONG_TO_GEOM + Appartient à la géométrie + + + BELONG_TO_PLANE + Appartient au plan + + + BINARY + Opérateur logique + + + CLEAR + Effacer + + + COMPARE + Comparer + + + COPLANAR_FACES + Faces coplanaires + + + COPY_FROM + Copier de... + + + CRITERION + Critère + + + BALLS + Particulaires + + + EDGES + Arêtes + + + ENTITY_TYPE + Type de l'entité + + + EQUAL_TO + Egal à + + + ERROR + La valeur du seuil n'est pas valide. +Entrez une valeur correcte et essayez de nouveau + + + FACES + Faces + + + FILTER + Filtre + + + FREE_BORDERS + Bords libres + + + FREE_EDGES + Arêtes libres + + + FREE_NODES + Nœuds isolés + + + FREE_FACES + Faces libres + + + ID + ID + + + INSERT + Insérer + + + LENGTH + Longueur + + + LENGTH2D + Longueur 2D + + + LESS_THAN + Inférieur à ... + + + LYING_ON_GEOM + Repose sur la géométrie + + + MAX_ELEMENT_LENGTH_2D + Diamètre d'éléments 2D + + + MAX_ELEMENT_LENGTH_3D + Diamètre d'éléments 3D + + + MINIMUM_ANGLE + Angle minimal + + + MORE_THAN + Supérieur à ... + + + MULTIEDGES_ERROR + La valeur de seuil des bords multi-connectés ne peut pas être égal à 1 +Entrez une valeur correcte et essayez de nouveau + + + GROUPCOLOR_ERROR + Impossible d'identifier la couleur du groupe +Entrez une valeur correcte et essayez de nouveau + + + MULTI_BORDERS + Bords multi-connectés + + + NODES + Nœuds + + + NOT + Non + + + OR + Ou + + + RANGE_OF_IDS + Liste d'IDs + + + REMOVE + Supprimer + + + SKEW + Inclinaison + + + TAPER + Cône + + + THRESHOLD_VALUE + Valeur du seuil + + + UNARY + Négation + + + VOLUMES + Volumes + + + VOLUME_3D + Volume + + + WARPING + Déformation + + + LINEAR + Linéaire + + + GROUP_COLOR + Couleur du groupe + + + ELEMENTS + Eléments + + + GEOM_TYPE + Type de géométrie + + + GEOM_TYPE_0 + Point + + + GEOM_TYPE_1 + Arête + + + GEOM_TYPE_2 + Triangle + + + GEOM_TYPE_3 + Quadrangle + + + GEOM_TYPE_4 + Polygone + + + GEOM_TYPE_5 + Tétraèdre + + + GEOM_TYPE_6 + Pyramide + + + GEOM_TYPE_7 + Hexaèdre + + + GEOM_TYPE_8 + Pentaèdre + + + GEOM_TYPE_9 + Prisme hexagonal + + + GEOM_TYPE_10 + Polyèdre + + + GEOM_TYPE_11 + Particulaire + + + + SMESHGUI_GroupOpDlg + + ARGUMENTS + Arguments + + + DIFF_MESHES + Les arguments de l'opération ne sont pas indiqués correctement +Les groupes correspondent à des maillages différents +Donnez des arguments valides et essayez de nouveau + + + DIFF_TYPES + Les arguments de l'opération ne sont pas indiqués correctement +Les groupes contiennent des éléments de types différents +Donnez des arguments valides et essayez de nouveau + + + EMPTY_NAME + Le nom du groupe est invalide +Indiquez un nom non-vide et essayez de nouveau + + + INCORRECT_ARGUMENTS + Les arguments de l'opération ne sont pas indiqués +Indiquez-les et essayez de nouveau + + + NAME + Nom + + + OBJECT_1 + Objet 1 + + + OBJECT_2 + Objet 2 + + + RESULT_NAME + Nom du résultat + + + TOOL_OBJECT + Outil + + + UNION_OF_TWO_GROUPS + Union de deux groupes + + + + SMESHGUI_UnionGroupsDlg + + UNION_OF_GROUPS + Union de groupes + + + + SMESHGUI_DimGroupDlg + + CREATE_GROUP_OF_UNDERLYING_ELEMS + Créer un groupe d'entités sous-jacentes + + + ELEMENTS_TYPE + Type d'éléments + + + NODE + Nœud + + + EDGE + Arête + + + FACE + Face + + + VOLUME + Volume + + + + SMESHGUI_IntersectGroupsDlg + + INTERSECTION_OF_GROUPS + Intersection de groupes + + + + SMESHGUI_CutGroupsDlg + + CUT_OF_GROUPS + Différence de groupes + + + MAIN_OBJECT + Objet principal + + + TOOL_OBJECT + Objet outil + + + + SMESHGUI_MakeNodeAtPointDlg + + AUTO_SEARCH + Trouver le nœud le plus proche de la destination + + + CAPTION + Déplacer un nœud + + + DESTINATION + Destination + + + MOVE_NODE + Déplacer un nœud + + + METHOD + Méthode + + + NODE_2MOVE + Nœud à déplacer + + + NODE_2MOVE_ID + ID + + + + SMESHGUI_MakeNodeAtPointOp + + INVALID_ID + L'ID du nœud est invalide + + + INVALID_MESH + Le maillage à modifier n'est pas sélectionné + + + + SMESHGUI_FindElemByPointDlg + + MESH_GROUP + Maillage ou groupe + + + CAPTION + Trouver un élément par un point + + + CREATE_NEW_METHOD + Créer un nœud + + + MESH_PASS_THROUGH_POINT + Créer un nœud au point + + + METHOD + Méthode + + + MOVE_EXISTING_METHOD + Déplacer un nœud + + + NODE_2MOVE + Nœud à déplacer + + + NODE_2MOVE_ID + ID + + + + SMESHGUI_MeshDlg + + CREATE_MESH + Créer un maillage + + + CREATE_SUBMESH + Créer un sous-maillage + + + DIM_0D + 0D + + + DIM_1D + 1D + + + DIM_2D + 2D + + + DIM_3D + 3D + + + EDIT_MESH_SUBMESH + Editer un maillage/sous-maillage + + + GEOMETRY + Géométrie + + + HYPOTHESES_SETS + Attribuer un jeu d'hypothèses + + + MESH + Maillage + + + NAME + Nom + + + + SMESHGUI_MeshOp + + ALGORITHM_WITHOUT_HYPOTHESIS + L'algorithme pour la dimension %1 est défini mais l'hypothèse ne l'est pas + + + EDIT_SUBMESH_QUESTION + Un sous-maillage existe déjà sur la géométrie choisie +Voulez-vous éditer ce sous-maillage? + + + SUBMESH_NOT_ALLOWED + Créer un sous-maillage ignoré par l'algorithme global n'a pas de sens "%1" + + + GEOMETRY_OBJECT_IS_NOT_DEFINED + L'objet géométrique n'est pas défini +Indiquez-le et essayez de nouveau + + + GEOMETRY_OBJECT_IS_NULL + L'objet géométrique est nul + + + HYPOTHESES_AND_ALGORITHMS_ARE_NOT_DEFINED + Les hypothèses et les algorithmes ne sont pas définis + + + HYPOTHESIS_WITHOUT_ALGORITHM + L'hypothèse est définie pour la dimension %1 mais l'algorithme n'est pas défini + + + IMPORTED_MESH + Le maillage n'est pas construit sur une géométrie + + + INVALID_SUBSHAPE + L'objet géométrique n'est pas un sous-objet de l'objet maillé + + + MESH_IS_NOT_DEFINED + Le maillage n'est pas défini +Spécifiez-le et essayez de nouveau + + + MESH_IS_NULL + Le maillage est nul + + + NAME_OF_MESH_IS_EMPTY + Le nom du maillage est vide +Indiquez un nom valide et essayez de nouveau + + + NAME_OF_SUBMESH_IS_EMPTY + Le nom du sous-maillage est vide +Indiquez un nom valide et essayez de nouveau + + + THERE_IS_NO_OBJECT_FOR_EDITING + Il n'y a pas d'objet à éditer. +Sélectionnez un maillage ou un sous-maillage et essayez de nouveau + + + + SMESHGUI_MeshPatternDlg + + 3D_BLOCK + Bloc 3D + + + CAPTION + Projection de motif + + + CREATE_POLYEDRS_NEAR_BOUNDARY + Créer des polyèdres près de la frontière + + + CREATE_POLYGONS_NEAR_BOUNDARY + Créer des polygones près de la frontière + + + ERROR_OF_LOADING + Impossible de charger le motif. +Il est probable que le fichier est corrompu ou contient un autre type de motif + + + ERROR_OF_OPENING + Il est impossible d'ouvrir le fichier. +Vérifiez s'il existe et si vous avez l'autorisation + + + ERROR_OF_READING + Il est impossible de charger le motif +Vérifiez le contenu du fichier + + + ERR_READ_3D_COORD + Il est impossible de charger le motif +Les coordonnées des points 3D sont en dehors de l'intervalle [0,1] + + + ERR_READ_BAD_INDEX + Il est impossible de charger le motif +Un index de point invalide a été detecté + + + ERR_READ_BAD_KEY_POINT + Il est impossible de charger le motif +Le point-clef n'est pas situé sur la frontière + + + ERR_READ_ELEM_POINTS + Il est impossible de charger le motif +Le nombre de points de l'élément est invalide + + + ERR_READ_NB_POINTS + Il est impossible de charger le motif +Il est impossible de lire le nombre de points dans le fichier + + + ERR_READ_NO_ELEMS + Il est impossible de charger le motif +Il ne contient pas d'éléments + + + ERR_READ_NO_KEYPOINT + Il est impossible de charger le motif +Le motif 2D n'a pas de point-clef + + + ERR_READ_POINT_COORDS + Il est impossible de charger le motif +Il est impossible de lire les coordonnées des points dans le fichier + + + ERR_READ_TOO_FEW_POINTS + Il est impossible de charger le motif. +Il y a trop peu de points dans le fichier + + + FACE + Face + + + LOAD_PATTERN + Charger un motif + + + MESH_FACES + Faces du maillage + + + MESH_VOLUMES + Volumes du maillage + + + NEW + Nouveau... + + + NODE_1 + Nœud 1 + + + NODE_2 + Nœud 2 + + + PATTERN + Motif + + + PATTERN_FILT + Fichiers de motif (*.smp) + + + PATTERN_TYPE + Type de motif + + + PREVIEW + Prévisualiser + + + REFINE + Raffiner les éléments de maillage sélectionnés + + + REVERSE + Inverser l'ordre des points-clefs + + + VERTEX + Sommet... + + + VERTEX1 + Sommet 1 + + + VERTEX2 + Sommet 2 + + + + SMESHGUI_MeshTab + + ADD_HYPOTHESIS + Ajouter l'hypothèse + + + ALGORITHM + Algorithme + + + HYPOTHESIS + Hypothèse + + + NONE + <None> + + + + SMESHGUI_MultiEditDlg + + ADD + Ajouter + + + FILTER + Filtre + + + REMOVE + Supprimer + + + SELECT_FROM + Sélectionner à partir de + + + SORT_LIST + Trier la liste + + + SPLIT_JOIN_CRITERION + Critère + + + TO_ALL + Appliquer à tous + + + USE_DIAGONAL_1_3 + Utiliser la diagonale 1-3 + + + USE_DIAGONAL_2_4 + Utiliser la diagonale 2-4 + + + USE_NUMERIC_FUNC + Utiliser le facteur numérique + + + + SMESHGUI_CuttingIntoTetraDlg + + CAPTION + Diviser les volumes en tétraèdres + + + SPLIT_METHOD + Diviser l'hexaèdre + + + SPLIT_HEX_TO_5_TETRA + En 5 tétraèdres + + + SPLIT_HEX_TO_6_TETRA + En 6 tétraèdres + + + SPLIT_HEX_TO_24_TETRA + En 24 tétraèdres + + + + SMESHGUI_PrecisionDlg + + CAPTION + Précision pour les contrôles de qualité du maillage + + + NOT_USE + Ne pas utiliser! + + + PRECISION + Nombre de chiffres après la virgule + + + + SMESHGUI_RevolutionDlg + + ANGLE_BY_STEP + Angle par pas + + + PREVIEW + Prévisualiser + + + REVOLUTION_1D + Révolution des éléments 1D + + + REVOLUTION_2D + Révolution des éléments 2D + + + REVOLUTION_AROUND_AXIS + Révolution autour d'un axe + + + TOTAL_ANGLE + Angle total + + + MEN_POINT_SELECT + De l'origine au point sélectionner + + + MEN_FACE_SELECT + Normale de la face sélectionnée + + + + SMESHGUI_SewingDlg + + BORDER + Frontière + + + BORDER_1 + Frontière 1 + + + BORDER_2 + Frontière 2 + + + CREATE_POLYEDRS_NEAR_BOUNDARY + Remplacer les volumes concernés par des polyèdres + + + CREATE_POLYGONS_INSTEAD_SPLITTING + Créer des polygones au lieu de redécouper + + + ERROR_1 + La frontière Libre1 n'est pas trouvée avec les nœuds sélectionnés + + + ERROR_2 + La frontière Libre2 n'est pas trouvée avec les nœuds sélectionnés + + + ERROR_3 + Les frontières Libres 1 et 2 n'ont pas été trouvées avec les nœuds sélectionnés + + + ERROR_4 + Aucun chemin du premier au dernier nœud de la frontière n'est trouvé + + + ERROR_5 + Il n'est pas permis de découper les volumes de bord! + + + ERROR_6 + Le nombre d'éléments sélectionnés est différent de chaque côté + + + ERROR_7 + Les jeux d'éléments sont topologiquement différents ou les nœuds ne conviennent pas + + + ERROR_8 + Les nœuds du côté 1 soit ne sont pas connectés soit ne sont pas situés à la frontière du jeu d'éléments + + + ERROR_9 + Les nœuds du côté 2 soit ne sont pas connectés soit ne sont pas situés à la frontière de l'élément + + + FIRST_NODE_ID + ID du premier nœud + + + LAST_NODE_ID + ID du dernier nœud + + + MERGE_EQUAL_ELEMENTS + Fusionner les éléments égaux + + + NODE1_TO_MERGE + Nœud 1 à fusionner + + + NODE2_TO_MERGE + Nœud 2 à fusionner + + + SECOND_NODE_ID + ID du deuxième nœud + + + SEW_BORDER_TO_SIDE + Coudre la frontière au côté + + + SEW_CONFORM_FREE_BORDERS + Coudre les frontières libres conformes + + + SEW_FREE_BORDERS + Coudre les frontières libres + + + SEW_SIDE_ELEMENTS + Coudre les éléments de bord + + + SIDE + Bord + + + SIDE_1 + Bord 1 + + + SIDE_2 + Bord 2 + + + + SMESHGUI_ShapeByMeshDlg + + CAPTION + Trouver la géométrie par le maillage + + + + SMESHGUI_SingleEditDlg + + EDGE_BETWEEN + Arête entre des triangles voisins + + + + SMESHGUI_SmoothingDlg + + CENTROIDAL + Centroïdal + + + FIXED_NODES_IDS + IDs des nœuds fixes + + + IS_PARAMETRIC + dans l'espace paramétrique + + + ITERATION_LIMIT + Limite d'Itération + + + LAPLACIAN + Laplacien + + + MAX_ASPECT_RATIO + Rapport de forme maximal + + + METHOD + Méthode + + + + SMESHGUI_TrianglesInversionDlg + + CAPTION + Inversion de diagonale + + + + SMESHGUI_UnionOfTrianglesDlg + + CAPTION + Union des triangles + + + MAXIMUM_ANGLE + Angle maximal de pliage + + + + SMESHGUI_UnionOfTwoTrianglesDlg + + CAPTION + Union de deux triangles + + + + SMESHGUI_FileInfoDlg + + CAPTION + Informations sur le fichier + + + FILE_NAME + Nom du fichier + + + FILE_SIZE + Taille du fichier (bytes) + + + MED_VERSION + Version MED + + + + SMESHGUI_GroupOnShapeDlg + + SMESH_CREATE_GROUP_FROM_GEOM + Créer des groupes à partir de la géométrie + + + + SMESHGUI_MeshOrderDlg + + SMESH_MESHORDER_TITLE + Ordre des sous-maillages dans la procédure de maillage + + + + SMESHGUI_MeshOrderOp + + SMESH_NO_CONCURENT_MESH + Pas de sous-maillages concurrents détectés + + + + SMESHGUI_ClippingDlg + + CLIP_PLANES + Plans de découpe + + + MESHES_SUBMESHES_GROUPS + Maillages, sous-maillages et groupes + + + SELECT_ALL + Tout sélectionner + + + ROTATION_AROUND_X_Y2Z + Rotation autour de X (Y à Z): + + + ROTATION_AROUND_Y_X2Z + Rotation autour de Y (X à Z): + + + ROTATION_AROUND_Z_Y2X + Rotation autour de Z (Y à X): + + + ROTATION_AROUND_X_Z2Y + Rotation autour de X (Z à Y): + + + ROTATION_AROUND_Y_Z2X + Rotation autour de Y (Z à X): + + + ROTATION_AROUND_Z_X2Y + Rotation autour de Z (X à Y): + + + SHOW_PREVIEW + Prévisualiser + + + AUTO_APPLY + Appliquer automatiquement + + + ALONG_XY + || X-Y + + + ALONG_YZ + || Y-Z + + + ALONG_ZX + || Z-X + + + PLANE_NUM + Plan# %1 + + + NO_PLANES + Pas de plans + + + + SMESHGUI_DuplicateNodesDlg + + DUPLICATION_MODE + Mode de duplication + + + DUPLICATION_WITHOUT_ELEMS + Sans duplication des éléments de frontière + + + GROUP_NODES_TO_DUPLICATE + Groupe des nœuds à dupliquer + + + GROUP_NODES_TO_REPLACE + Groupe des éléments dont les nœuds sont à remplacer + + + DUPLICATION_WITH_ELEMS + Avec duplication des éléments de frontière + + + GROUP_ELEMS_TO_DUPLICATE + Groupe des éléments à dupliquer + + + GROUP_NODES_NOT_DUPLICATE + Groupe des nœuds à ne pas dupliquer + + + GROUP_ELEMS_TO_REPLACE + Groupe des éléments dont les nœuds sont à remplacer + + + CONSTRUCT_NEW_GROUP_NODES + Construire un groupe avec les nœuds nouvellement créés + + + CONSTRUCT_NEW_GROUP_ELEMENTS + Construire un groupe avec les éléments nouvellement créés + + + + SMESHGUI_Make2DFrom3DDlg + + CAPTION + Créer les éléments de frontière + + + Groups + Groupes 2D + + + MODE + Mode + + + 2D_FROM_3D + 2D à partir de 3D + + + 1D_FROM_3D + 1D à partir de groupes 2D + + + 1D_FROM_2D + 1D à partir de 2D + + + TARGET + Cible + + + THIS_MESH + Ce maillage + + + NEW_MESH + Nouveau maillage + + + COPY_SRC + Copier le maillage source + + + MISSING_ONLY + Copier seulement les éléments manquants + + + CREATE_GROUP + Créer un groupe + + + + SMESHGUI_Make2DFrom3DOp + + NB_ADDED + %1 éléments de bord ont été ajoutés + + + WRONG_GROUPS + Les groupes suivants n'ont pas été traités +en raison de leurs types incompatibles: +%1 + + + SMESH_ERR_NO_INPUT_MESH + Aucun maillage, sous-maillage ou groupe source n'est indiqué + + + SMESH_TOO_MANY_MESHES + Un seul maillage à la fois peut être traité + + + SMESH_NOT_ONLY_GROUPS + Impossible de traiter à la fois des maillages et des groupes + + + SMESH_ERR_NO_3D_ELEMENTS + L'objet source ne contient pas d'éléments 3D + + + SMESH_ERR_NO_2D_ELEMENTS + L'objet source ne contient pas d'éléments 2D + + + SMESH_ERR_MESH_NAME_NOT_SPECIFIED + Le nom du nouveau maillage n'est pas indiqué + + + SMESH_ERR_GRP_NAME_NOT_SPECIFIED + Le nom du groupe n'est pas indiqué + + + + SMESHGUI_MeshInfo + + NAME_LAB + Nom: + + + OBJECT_LAB + Objet: + + + NODES_LAB + Nœuds: + + + ELEMENTS_LAB + Eléments: + + + TOTAL_LAB + Total + + + LINEAR_LAB + Linéaire + + + QUADRATIC_LAB + Quadratique + + + 0D_LAB + 0D: + + + BALL_LAB + Particulaires: + + + 1D_LAB + 1D (arêtes): + + + 2D_LAB + 2D (faces): + + + TRIANGLES_LAB + Triangles: + + + QUADRANGLES_LAB + Quadrangles: + + + POLYGONS_LAB + Polygones: + + + 3D_LAB + 3D (volumes): + + + TETRAHEDRONS_LAB + Tétraèdres: + + + HEXAHEDONRS_LAB + Hexaèdres: + + + PYRAMIDS_LAB + Pyramides: + + + PRISMS_LAB + Prismes: + + + HEX_PRISMS_LAB + Prismes hexagonaux: + + + POLYHEDRONS_LAB + Polyèdres: + + + OBJECT_MESH + Maillage + + + OBJECT_SUBMESH + Sous-maillage + + + OBJECT_GROUP + Groupe + + + OBJECT_GROUP_NODES + Groupe de nœuds + + + OBJECT_GROUP_EDGES + Groupe d'arêtes + + + OBJECT_GROUP_FACES + Groupe de faces + + + OBJECT_GROUP_VOLUMES + Groupe de volumes + + + OBJECT_GROUP_0DELEMS + Groupe d'éléments 0D + + + OBJECT_GROUP_BALLS + Groupe d'éléments particulaires + + + BUT_LOAD_MESH + Charger un maillage depuis un serveur + + + + SMESHGUI_MeshInfoDlg + + MESH_INFO + Informations de maillage + + + BASE_INFO + Informations de base + + + ELEM_INFO + Infos sur les éléments + + + ADDITIONAL_INFO + Infos détaillées + + + NODE_MODE + Nœud + + + ELEM_MODE + Elément + + + X_FROM_Y_ITEMS_SHOWN + %1-%2 sur %3 éléments affichés + + + + SMESHGUI_ElemInfo + + COORDINATES + COORDONNÉES + + + CONNECTIVITY + CONNECTIVITÉ + + + GRAVITY_CENTER + CENTRE DE GRAVITÉ + + + NODE + Nœud + + + 0D_ELEMENT + ELÉMENTS 0D + + + 0D_ELEMENTS + ELÉMENTS 0D + + + BALL_ELEMENT + ELEMENT PARTICULAIRE + + + BALL_ELEMENTS + ELEMENTS PARTICULAIRES + + + EDGE + ARÊTE + + + EDGES + ARÊTES + + + FACE + FACE + + + FACES + FACES + + + VOLUME + VOLUME + + + VOLUMES + VOLUMES + + + FREE_NODE + Nœud libre (pas de connectivité) + + + TYPE + TYPE + + + TRIANGLE + Triangle + + + QUADRANGLE + Quadrangle + + + POLYGON + Polygone + + + TETRAHEDRON + Tétraèdre + + + HEXAHEDRON + Hexaèdre + + + PYRAMID + Pyramide + + + PRISM + Prisme + + + HEX_PRISM + Prisme hexagonal + + + POLYHEDRON + Polyèdre + + + QUADRATIC + QUADRATIQUE + + + YES + Oui + + + NO + Non + + + PROPERTY + Propriété + + + VALUE + Valeur + + + + SMESHGUI_AddInfo + + NAME + Nom + + + GROUPS + Groupes + + + GROUPS_1 + Nœuds + + + GROUPS_2 + Arêtes + + + GROUPS_3 + Faces + + + GROUPS_4 + Volumes + + + GROUPS_5 + Eléments 0D + + + GROUPS_6 + Eléments particulaires + + + PARENT_MESH + Maillage parent + + + TYPE + Type + + + STANDALONE_GROUP + Groupe autonome + + + GROUP_ON_GEOMETRY + Groupe lié à une géométrie + + + GROUP_ON_FILTER + Groupe lié à un filtre + + + GEOM_OBJECT + Shape + + + NODE + Nœud + + + EDGE + Arête + + + FACE + Face + + + VOLUME + Volume + + + 0DELEM + Elément 0D + + + BALL_ELEMENT + Elément particulaire + + + UNKNOWN + Inconnu + + + ENTITY_TYPE + Type de maille + + + SIZE + Taille + + + COLOR + Couleur + + + NB_NODES + Nœuds sous-jacents + + + COMPUTE + Calculer + + + LOAD + Charger + + + MESH_ON_GEOMETRY + Lié à une géométrie + + + MESH_FROM_FILE + Importé + + + FILE_NAME + Nom du fichier + + + STANDALONE_MESH + Autonome + + + SUBMESHES + Sous-maillages + + + SUBMESHES_0 + Assemblage + + + SUBMESHES_2 + Solide + + + SUBMESHES_3 + Coque + + + SUBMESHES_4 + Face + + + SUBMESHES_5 + Contour + + + SUBMESHES_6 + Arêtes + + + SUBMESHES_7 + Point + + + + SMESHGUI_MinDistance + + FIRST_TARGET + Premier élément + + + SECOND_TARGET + Deuxième élément + + + NODE + Nœud + + + ELEMENT + Elément + + + OBJECT + Objet + + + ORIGIN + Origine + + + COMPUTE + Calculer + + + RESULT + Distance entre les éléments + + + DISTANCE + Distance + + + + SMESHGUI_CopyMeshDlg + + OBJECT_NAME + Objet source + + + ELEM_IDS + IDs des éléments sources + + + NEW_NAME + Nom du nouveau maillage + + + + SMESHGUI_MeasureDlg + + MEASUREMENTS + Outils de mesure + + + MIN_DIST + Distance minimale + + + BND_BOX + Boîte englobante + + + + SMESHGUI_BoundingBox + + SOURCE + Source + + + OBJECTS + Objets + + + NODES + Nœuds + + + ELEMENTS + Eléments + + + COMPUTE + Calculer + + + RESULT + Boîte englobante + + + SELECTED_NB_OBJ + %1 %2 sélectionnés + + + NB_NODES + nœuds + + + NB_ELEMENTS + éléments + + + + SMESHGUI_MeshLoadingProgress + + MESH_LOAD_PROGRESS_TITLE + Chargement du maillage en cours + + + + StdMeshersGUI_SubShapeSelectorWdg + + X_FROM_Y_ITEMS_SHOWN + %1-%2 sur %3 éléments affichés + + + + SMESHGUI_Preferences_ColorDlg + + DIALOG_TITLE + Propriétés (couleur, épaisseur des traits, taille des éléments réduits, ...) + + + GRP_ELEMENTS + Eléments + + + SURFACE_COLOR_LBL + Couleur de surface + + + BACKSURFACE_COLOR_LBL + Couleur arrière + + + OUTLINE_COLOR_LBL + Couleur de contour + + + WIREFRAME_COLOR_LBL + Couleur en mode fil de fer + + + 0D_ELEMENTS_COLOR_LBL + Eléments 0D + + + 0D_ELEMENTS_SIZE_LBL + Taille des éléments 0D + + + BALL_ELEMENTS_COLOR_LBL + Eléments particulaires + + + BALL_ELEMENTS_SIZE_LBL + Taille des éléments particulaires + + + LINE_WIDTH_LBL + Epaisseur des traits + + + SHRINK_COEF_LBL + Coef. de contraction + + + GRP_NODES + Nœuds + + + NODES_COLOR_LBL + Couleur + + + NODES_MARKER_LBL + Marqueur + + + GRP_ORIENTATION + Orientation des faces + + + ORIENTATION_COLOR_LBL + Couleur + + + ORIENTATION_SCALE_LBL + Facteur d'échelle + + + 3D_VECTORS_LBL + Vecteurs 3D + + + GRP_SELECTION + Sélection + + + SELECTION_COLOR_LBL + Couleur de sélection + + + PRESELECTION_COLOR_LBL + Couleur de pré-sélection + + + + SMESHGUI_ReorientFacesDlg + + CAPTION + Réorienter des faces selon un vector + + + REORIENT_FACES + Réorienter + + + DIRECTION + Direction + + + OBJECT + Objet + + + POINT + Point + + + FACE + Face + + + FACES + Source des faces + + + ORIENTATION + Orientation + + + + SMESHGUI_ReorientFacesOp + + NO_OBJECT_SELECTED + Aucun objet sélectionné + + + NO_FACES + L'objet ne contient pas de faces + + + ZERO_SIZE_VECTOR + Vecteur de taille nulle + + + INVALID_FACE + Face non valide + + + NB_REORIENTED + %1 face(s) inversée(s) + + + diff --git a/src/SMESHUtils/Makefile.am b/src/SMESHUtils/Makefile.am new file mode 100644 index 000000000..0e5f87624 --- /dev/null +++ b/src/SMESHUtils/Makefile.am @@ -0,0 +1,57 @@ +# Copyright (C) 2007-2012 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 +# + +# File : Makefile.in +# Module : SMESH +# +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +# header files +salomeinclude_HEADERS = \ + SMESH_Block.hxx \ + SMESH_TypeDefs.hxx \ + SMESH_Octree.hxx \ + SMESH_OctreeNode.hxx \ + SMESH_Comment.hxx \ + SMESH_ComputeError.hxx \ + SMESH_File.hxx \ + SMESH_Utils.hxx + +# Libraries targets + +lib_LTLIBRARIES = libSMESHUtils.la + +dist_libSMESHUtils_la_SOURCES = \ + SMESH_Block.cxx \ + SMESH_Octree.cxx \ + SMESH_OctreeNode.cxx \ + SMESH_File.cxx + +# additionnal information to compile and link file +libSMESHUtils_la_CPPFLAGS = \ + $(KERNEL_CXXFLAGS) \ + $(CAS_CPPFLAGS) \ + $(VTK_INCLUDES) \ + $(BOOST_CPPFLAGS) \ + -I$(srcdir)/../SMDS \ + -I$(srcdir)/../SMESHDS + +libSMESHUtils_la_LDFLAGS = \ + ../SMESHDS/libSMESHDS.la \ + $(CAS_LDPATH) -lTKShHealing -lTKPrim -lTKG2d -lTKG3d -lTKGeomBase diff --git a/src/SMESH/SMESH_Block.cxx b/src/SMESHUtils/SMESH_Block.cxx similarity index 94% rename from src/SMESH/SMESH_Block.cxx rename to src/SMESHUtils/SMESH_Block.cxx index 0fd49966a..b28dc7d67 100644 --- a/src/SMESH/SMESH_Block.cxx +++ b/src/SMESHUtils/SMESH_Block.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Pattern.hxx // Created : Mon Aug 2 10:30:00 2004 // Author : Edward AGAPOV (eap) @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +67,7 @@ using namespace std; //================================================================================ /*! * \brief Set edge data - * \param edgeID - block subshape ID + * \param edgeID - block sub-shape ID * \param curve - edge geometry * \param isForward - is curve orientation coincides with edge orientation in the block */ @@ -85,7 +87,7 @@ void SMESH_Block::TEdge::Set( const int edgeID, Adaptor3d_Curve* curve, const bo //================================================================================ /*! * \brief Set coordinates of nodes at edge ends to work with mesh block - * \param edgeID - block subshape ID + * \param edgeID - block sub-shape ID * \param node1 - coordinates of node with lower ID * \param node2 - coordinates of node with upper ID */ @@ -141,7 +143,7 @@ SMESH_Block::TEdge::~TEdge() //================================================================================ /*! * \brief Set face data - * \param faceID - block subshape ID + * \param faceID - block sub-shape ID * \param S - face surface geometry * \param c2d - 4 pcurves in the order as returned by GetFaceEdgesIDs(faceID) * \param isForward - orientation of pcurves comparing with block edge direction @@ -178,7 +180,7 @@ void SMESH_Block::TFace::Set( const int faceID, //================================================================================ /*! * \brief Set face data to work with mesh block - * \param faceID - block subshape ID + * \param faceID - block sub-shape ID * \param edgeU0 - filled data of edge u0 = GetFaceEdgesIDs(faceID)[ 0 ] * \param edgeU1 - filled data of edge u1 = GetFaceEdgesIDs(faceID)[ 1 ] */ @@ -371,7 +373,7 @@ bool SMESH_Block::ShellPoint( const gp_XYZ& theParams, gp_XYZ& thePoint ) const //======================================================================= //function : ShellPoint //purpose : computes coordinates of a point in shell by points on sub-shapes; -// thePointOnShape[ subShapeID ] must be a point on a subShape +// thePointOnShape[ subShapeID ] must be a point on a sub-shape //======================================================================= bool SMESH_Block::ShellPoint(const gp_XYZ& theParams, @@ -381,8 +383,8 @@ bool SMESH_Block::ShellPoint(const gp_XYZ& theParams, if ( thePointOnShape.size() < ID_F1yz ) return false; - double x = theParams.X(), y = theParams.Y(), z = theParams.Z(); - double x1 = 1. - x, y1 = 1. - y, z1 = 1. - z; + const double x = theParams.X(), y = theParams.Y(), z = theParams.Z(); + const double x1 = 1. - x, y1 = 1. - y, z1 = 1. - z; const vector& p = thePointOnShape; thePoint = @@ -951,26 +953,45 @@ int SMESH_Block::GetShapeIDByParams ( const gp_XYZ& theCoord ) return id + 1; // shape ids start at 1 } -//======================================================================= -//function : GetOrderedEdges -//purpose : return nb wires and a list of oredered edges -//======================================================================= +//================================================================================ +/*! + * \brief Return number of wires and a list of oredered edges. + * \param theFace - the face to process + * \param theFirstVertex - the vertex of the outer wire to set first in the returned + * list ( theFirstVertex may be NULL ) + * \param theEdges - all ordered edges of theFace (outer edges goes first). + * \param theNbEdgesInWires - nb of edges (== nb of vertices in closed wire) in each wire + * \param theShapeAnalysisAlgo - if true, ShapeAnalysis::OuterWire() is used to find + * the outer wire else BRepTools::OuterWire() is used. + * \retval int - nb of wires + * + * Always try to set a seam edge first. + * BRepTools::OuterWire() fails e.g. in the case of issue 0020184, + * ShapeAnalysis::OuterWire() fails in the case of issue 0020452 + */ +//================================================================================ int SMESH_Block::GetOrderedEdges (const TopoDS_Face& theFace, TopoDS_Vertex theFirstVertex, list< TopoDS_Edge >& theEdges, - list< int > & theNbVertexInWires) + list< int > & theNbEdgesInWires, + const bool theShapeAnalysisAlgo) { // put wires in a list, so that an outer wire comes first list aWireList; - TopoDS_Wire anOuterWire = BRepTools::OuterWire( theFace ); - aWireList.push_back( anOuterWire ); + TopoDS_Wire anOuterWire = + theShapeAnalysisAlgo ? ShapeAnalysis::OuterWire( theFace ) : BRepTools::OuterWire( theFace ); for ( TopoDS_Iterator wIt (theFace); wIt.More(); wIt.Next() ) - if ( !anOuterWire.IsSame( wIt.Value() )) - aWireList.push_back( TopoDS::Wire( wIt.Value() )); + if ( wIt.Value().ShapeType() == TopAbs_WIRE ) // it can be internal vertex! + { + if ( !anOuterWire.IsSame( wIt.Value() )) + aWireList.push_back( TopoDS::Wire( wIt.Value() )); + else + aWireList.push_front( TopoDS::Wire( wIt.Value() )); + } // loop on edges of wires - theNbVertexInWires.clear(); + theNbEdgesInWires.clear(); list::iterator wlIt = aWireList.begin(); for ( ; wlIt != aWireList.end(); wlIt++ ) { @@ -979,10 +1000,16 @@ int SMESH_Block::GetOrderedEdges (const TopoDS_Face& theFace, for ( iE = 0; wExp.More(); wExp.Next(), iE++ ) { TopoDS_Edge edge = wExp.Current(); - edge = TopoDS::Edge( edge.Oriented( wExp.Orientation() )); + // commented for issue 0020557, other related ones: 0020526, PAL19080 + // edge = TopoDS::Edge( edge.Oriented( wExp.Orientation() )); theEdges.push_back( edge ); } - theNbVertexInWires.push_back( iE ); + if ( iE == 0 ) // wExp returns nothing if e.g. the wire contains one internal edge + { // Issue 0020676 + for ( TopoDS_Iterator e( *wlIt ); e.More(); e.Next(), ++iE ) + theEdges.push_back( TopoDS::Edge( e.Value() )); + } + theNbEdgesInWires.push_back( iE ); iE = 0; if ( wlIt == aWireList.begin() && theEdges.size() > 1 ) { // the outer wire // orient closed edges @@ -1020,7 +1047,7 @@ int SMESH_Block::GetOrderedEdges (const TopoDS_Face& theFace, theEdges.splice(theEdges.end(), theEdges, theEdges.begin(), ++theEdges.begin()); TopExp::Vertices( theEdges.front(), vv[0], vv[1], true ); - if ( iE++ > theNbVertexInWires.back() ) { + if ( iE++ > theNbEdgesInWires.back() ) { #ifdef _DEBUG_ gp_Pnt p = BRep_Tool::Pnt( theFirstVertex ); MESSAGE ( " : Warning : vertex "<< theFirstVertex.TShape().operator->() @@ -1478,7 +1505,7 @@ bool SMESH_Block::FindBlockShapes(const TopoDS_Shell& theShell, //================================================================================ /*! * \brief Initialize block geometry with shapes from theShapeIDMap - * \param theShapeIDMap - map of block subshapes + * \param theShapeIDMap - map of block sub-shapes * \retval bool - is a success */ //================================================================================ @@ -1527,11 +1554,11 @@ bool SMESH_Block::LoadBlockShapes(const TopTools_IndexedMapOfOrientedShape& theS * \brief Load face geometry * \param theFace - face * \param theFaceID - face in-block ID - * \param theShapeIDMap - map of block subshapes + * \param theShapeIDMap - map of block sub-shapes * \retval bool - is a success * * It is enough to compute params or coordinates on the face. - * Face subshapes must be loaded into theShapeIDMap before + * Face sub-shapes must be loaded into theShapeIDMap before */ //================================================================================ @@ -1563,7 +1590,7 @@ bool SMESH_Block::LoadFace(const TopoDS_Face& theFace, * \brief/ Insert theShape into theShapeIDMap with theShapeID * \param theShape - shape to insert * \param theShapeID - shape in-block ID - * \param theShapeIDMap - map of block subshapes + * \param theShapeIDMap - map of block sub-shapes */ //================================================================================ diff --git a/src/SMESH/SMESH_Block.hxx b/src/SMESHUtils/SMESH_Block.hxx similarity index 90% rename from src/SMESH/SMESH_Block.hxx rename to src/SMESHUtils/SMESH_Block.hxx index 20c18163b..4971db99a 100644 --- a/src/SMESH/SMESH_Block.hxx +++ b/src/SMESHUtils/SMESH_Block.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_Block.hxx // Created : Tue Nov 30 12:42:18 2004 // Author : Edward AGAPOV (eap) @@ -26,7 +27,7 @@ #ifndef SMESH_Block_HeaderFile #define SMESH_Block_HeaderFile -#include "SMESH_SMESH.hxx" +#include "SMESH_Utils.hxx" //#include //#include @@ -58,7 +59,7 @@ class gp_Pnt; // parameters inside the block and vice versa // ========================================================= -class SMESH_EXPORT SMESH_Block: public math_FunctionSetWithDerivatives +class SMESHUtils_EXPORT SMESH_Block: public math_FunctionSetWithDerivatives { public: enum TShapeID { @@ -276,11 +277,14 @@ public: static int GetOrderedEdges (const TopoDS_Face& theFace, TopoDS_Vertex theFirstVertex, std::list< TopoDS_Edge >& theEdges, - std::list< int > & theNbVertexInWires); + std::list< int > & theNbEdgesInWires, + const bool theShapeAnalysisAlgo=false); // Return nb wires and a list of oredered edges. // It is used to assign indices to subshapes. // theFirstVertex may be NULL. // Always try to set a seam edge first + // if (theShapeAnalysisAlgo) then ShapeAnalysis::OuterWire() is used to find the outer + // wire else BRepTools::OuterWire() is used public: // ----------------------------------------------------------- @@ -307,7 +311,7 @@ public: // Note 2: curve adaptors need to have only Value(double), FirstParameter() and // LastParameter() defined to be used by Block algoritms - class SMESH_EXPORT TEdge { + class SMESHUtils_EXPORT TEdge { int myCoordInd; double myFirst; double myLast; @@ -327,7 +331,7 @@ public: ~TEdge(); }; - class SMESH_EXPORT TFace { + class SMESHUtils_EXPORT TFace { // 4 edges in the order u0, u1, 0v, 1v int myCoordInd[ 4 ]; double myFirst [ 4 ]; diff --git a/src/SMESHUtils/SMESH_Comment.hxx b/src/SMESHUtils/SMESH_Comment.hxx new file mode 100644 index 000000000..0aa156511 --- /dev/null +++ b/src/SMESHUtils/SMESH_Comment.hxx @@ -0,0 +1,76 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : SMESH_Comment.hxx +// Created : Wed Mar 14 18:28:45 2007 +// Author : Edward AGAPOV (eap) +// Module : SMESH +// $Header: +// +#ifndef SMESH_Comment_HeaderFile +#define SMESH_Comment_HeaderFile + +# include +# include + +using namespace std; + +/*! + * \brief Class to generate string from any type + */ +class SMESH_Comment : public string +{ + ostringstream _s ; + +public : + + SMESH_Comment():string("") {} + + SMESH_Comment(const SMESH_Comment& c):string() { + _s << c.c_str() ; + this->string::operator=( _s.str() ); + } + + SMESH_Comment & operator=(const SMESH_Comment& c) { + _s << c.c_str() ; + this->string::operator=( _s.str() ); + return *this; + } + + template + SMESH_Comment( const T &anything ) { + _s << anything ; + this->string::operator=( _s.str() ); + } + + template + SMESH_Comment & operator<<( const T &anything ) { + _s << anything ; + this->string::operator=( _s.str() ); + return *this ; + } + + operator char*() const { + return (char*)c_str(); + } +}; + + +#endif diff --git a/src/SMESH/SMESH_ComputeError.hxx b/src/SMESHUtils/SMESH_ComputeError.hxx similarity index 66% rename from src/SMESH/SMESH_ComputeError.hxx rename to src/SMESHUtils/SMESH_ComputeError.hxx index a764f0a3b..204427f08 100644 --- a/src/SMESH/SMESH_ComputeError.hxx +++ b/src/SMESHUtils/SMESH_ComputeError.hxx @@ -1,29 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 // -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : SMESH_Hypothesis.hxx // Author : Edward AGAPOV (eap) // Module : SMESH -// $Header: // #ifndef SMESH_ComputeError_HeaderFile #define SMESH_ComputeError_HeaderFile @@ -52,12 +48,14 @@ enum SMESH_ComputeErrorName COMPERR_EXCEPTION = -6, //!< other exception raised COMPERR_MEMORY_PB = -7, //!< std::bad_alloc exception COMPERR_ALGO_FAILED = -8, //!< algo failed for some reason - COMPERR_BAD_SHAPE = -9 //!< bad geometry + COMPERR_BAD_SHAPE = -9, //!< bad geometry + COMPERR_WARNING = -10, //!< algo reports error but sub-mesh is computed anyway + COMPERR_CANCELED = -11 //!< compute canceled }; // ============================================================= /*! - * \brief Contains algorithm and description of occured error + * \brief Contains an algorithm and description of an occured error */ // ============================================================= @@ -80,6 +78,7 @@ struct SMESH_ComputeError :myName(error),myComment(comment),myAlgo(algo) {} bool IsOK() { return myName == COMPERR_OK; } + bool IsKO() { return myName != COMPERR_OK && myName != COMPERR_WARNING; } bool IsCommon() { return myName < 0; } inline std::string CommonName() const; @@ -98,6 +97,9 @@ std::string SMESH_ComputeError::CommonName() const _case2char(COMPERR_EXCEPTION ); _case2char(COMPERR_MEMORY_PB ); _case2char(COMPERR_ALGO_FAILED ); + _case2char(COMPERR_BAD_SHAPE ); + _case2char(COMPERR_WARNING ); + _case2char(COMPERR_CANCELED ); default:; } return ""; diff --git a/src/SMESHUtils/SMESH_File.cxx b/src/SMESHUtils/SMESH_File.cxx new file mode 100644 index 000000000..8a69d1cc0 --- /dev/null +++ b/src/SMESHUtils/SMESH_File.cxx @@ -0,0 +1,240 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESH_File.cxx +// Created : Wed Mar 10 11:23:25 2010 +// Author : Edward AGAPOV (eap) +// +#include "SMESH_File.hxx" +#include "utilities.h" + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WIN32 +#include +#else +#include +#include +#endif + +//================================================================================ +/*! + * \brief Creator opening the file for reading by default + */ +//================================================================================ + +SMESH_File::SMESH_File(const std::string& name, bool open) + :_name(name), _size(-1), _file(0), _map(0), _pos(0), _end(0) +{ + if ( open ) this->open(); +} + +//================================================================================ +/*! + * \brief Destructor closing the file + */ +//================================================================================ + +SMESH_File::~SMESH_File() +{ + close(); +} + +//================================================================================ +/*! + * \brief Open file for reading. Return true if there is something to read + */ +//================================================================================ + +bool SMESH_File::open() +{ + int length = size(); + if ( !_map && length > 0 ) + { +#ifdef WNT + _file = CreateFile(_name.data(), GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + bool ok = ( _file != INVALID_HANDLE_VALUE ); +#else + _file = ::open(_name.data(), O_RDONLY ); + bool ok = ( _file > 0 ); +#endif + if ( ok ) + { +#ifdef WNT + _mapObj = CreateFileMapping(_file, NULL, PAGE_READONLY, 0, (DWORD)length, NULL); + _map = (void*) MapViewOfFile( _mapObj, FILE_MAP_READ, 0, 0, 0 ); +#else + _map = ::mmap(0,length,PROT_READ,MAP_PRIVATE,_file,0); + if ( _map == MAP_FAILED ) _map = NULL; +#endif + if ( _map != NULL ) + { + _size = length; + _pos = (char*) _map; + _end = _pos + _size; + } + else + { +#ifdef WNT + CloseHandle(_mapObj); + CloseHandle(_file); +#else + ::close(_file); +#endif + } + } + } + return _pos; +} + +//================================================================================ +/*! + * \brief Close the file + */ +//================================================================================ + +void SMESH_File::close() +{ + if ( _map != NULL ) + { +#ifdef WNT + UnmapViewOfFile(_map); + CloseHandle(_mapObj); + CloseHandle(_file); +#else + ::munmap(_map, _size); + ::close(_file); +#endif + _map = NULL; + _pos = _end = 0; + _size = -1; + } +} + +//================================================================================ +/*! + * \brief Remove the file + */ +//================================================================================ + +bool SMESH_File::remove() +{ + close(); + try { + OSD_Path filePath(TCollection_AsciiString((char*)_name.data())); + OSD_File(filePath).Remove(); + } + catch ( Standard_ProgramError ) { + MESSAGE("Can't remove file: " << _name << " ; file does not exist or permission denied"); + return false; + } + return true; +} + +//================================================================================ +/*! + * \brief Return file size + */ +//================================================================================ + +int SMESH_File::size() const +{ + if ( _size >= 0 ) return _size; // size of open file + + int size = -1; + int file = ::open( _name.data(), O_RDONLY ); + if ( file > 0 ) + { + struct stat status; + int err = fstat( file, &status); + if ( !err ) + size = status.st_size; + ::close( file ); + } + return size; +} + +//================================================================================ +/*! + * \brief Set cursor to the given position + */ +//================================================================================ + +void SMESH_File::setPos(const char* pos) +{ + if ( pos > (const char*)_map && pos < _end ) + _pos = (char*) pos; +} + +//================================================================================ +/*! + * \brief Skip till current line end and return the skipped string + */ +//================================================================================ + +std::string SMESH_File::getLine() +{ + std::string line; + const char* p = _pos; + while ( !eof() ) + if ( *(++_pos) == '\n' ) + break; + line.append( p, _pos ); + if ( !eof() ) _pos++; + return line; +} + +//================================================================================ +/*! + * \brief Move cursor to the file beginning + */ +//================================================================================ + +void SMESH_File::rewind() +{ + _pos = (char*) _map; +} + +//================================================================================ +/*! + * \brief Fill vector by reading out integers from file. Vector size gives number + * of integers to read + */ +//================================================================================ + +bool SMESH_File::getInts(std::vector& ints) +{ + int i = 0; + while ( i < ints.size() ) + { + while ( !isdigit( *_pos ) && !eof()) ++_pos; + if ( eof() ) break; + if ( _pos[-1] == '-' ) --_pos; + ints[ i++ ] = strtol( _pos, (char**)&_pos, 10 ); + } + return ( i == ints.size() ); +} diff --git a/src/SMESHUtils/SMESH_File.hxx b/src/SMESHUtils/SMESH_File.hxx new file mode 100644 index 000000000..3734e96d6 --- /dev/null +++ b/src/SMESHUtils/SMESH_File.hxx @@ -0,0 +1,95 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESH_File.hxx +// Created : Wed Mar 10 10:33:04 2010 +// Author : Edward AGAPOV (eap) +// +#ifndef __SMESH_File_HXX__ +#define __SMESH_File_HXX__ + +#include "SMESH_Utils.hxx" + +#include +#include + +#ifdef WNT +#include +#else +#include +#endif + +/*! + * \brief High level util for effective file reading and other file operations + */ +class SMESHUtils_EXPORT SMESH_File +{ +public: + + SMESH_File(const std::string& name, bool open=true); + + ~SMESH_File(); + + std::string getName() const { return _name; } + + bool open(); + + void close(); + + bool remove(); + + int size() const; + + // ------------------------ + // Access to file contents + // ------------------------ + + operator const char*() const { return _pos; } + + bool operator++() { return ++_pos < _end; } + + void operator +=(int posDelta) { _pos+=posDelta; } + + bool eof() const { return _pos >= _end; } + + const char* getPos() const { return _pos; } + + void setPos(const char* pos); + + std::string getLine(); + + void rewind(); + + bool getInts(std::vector& ids); + +private: + + std::string _name; //!< file name + int _size; //!< file size +#ifdef WNT + HANDLE _file, _mapObj; +#else + int _file; +#endif + void* _map; + const char* _pos; //!< current position + const char* _end; //!< position after file end +}; + +#endif diff --git a/src/SMESHUtils/SMESH_Octree.cxx b/src/SMESHUtils/SMESH_Octree.cxx new file mode 100644 index 000000000..00778a729 --- /dev/null +++ b/src/SMESHUtils/SMESH_Octree.cxx @@ -0,0 +1,211 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_Octree : global Octree implementation +// File : SMESH_Octree.cxx +// Created : Tue Jan 16 16:00:00 2007 +// Author : Nicolas Geimer & Aurélien Motteux(OCC) +// Module : SMESH +// +#include "SMESH_Octree.hxx" + +//=========================================================================== +/*! + * Constructor. limit must be provided at tree root construction. + * limit will be deleted by SMESH_Octree. + */ +//=========================================================================== + +SMESH_Octree::SMESH_Octree (SMESH_Octree::Limit* limit): + myChildren(NULL), + myFather(NULL), + myIsLeaf( false ), + myLimit( limit ), + myLevel(0), + myBox(NULL) +{ +} + +//================================================================================ +/*! + * \brief Compute the Octree + */ +//================================================================================ + +void SMESH_Octree::compute() +{ + if ( myLevel==0 ) + { + myBox = buildRootBox(); + if ( myLimit->myMinBoxSize > 0. && maxSize() <= myLimit->myMinBoxSize ) + myIsLeaf = true; + else + buildChildren(); + } +} + +//====================================== +/*! + * \brief SMESH_Octree Destructor + */ +//====================================== + +SMESH_Octree::~SMESH_Octree () +{ + if(myChildren != NULL) + { + if(!isLeaf()) + { + for(int i = 0; i<8; i++) + delete myChildren[i]; + delete[] myChildren; + myChildren = 0; + } + } + if ( myBox ) + delete myBox; + myBox = 0; + if ( level() == 0 ) + delete myLimit; + myLimit = 0; +} + +//================================================================= +/*! + * \brief Build the 8 children boxes and call buildChildrenData() + */ +//================================================================= + +void SMESH_Octree::buildChildren() +{ + if ( isLeaf() ) return; + + myChildren = new SMESH_Octree*[8]; + + gp_XYZ min = myBox->CornerMin(); + gp_XYZ max = myBox->CornerMax(); + gp_XYZ HSize = (max - min)/2.; + gp_XYZ mid = min + HSize; + gp_XYZ childHsize = HSize/2.; + + // get the whole model size + double rootSize = 0; + { + SMESH_Octree* root = this; + while ( root->myLevel > 0 ) + root = root->myFather; + rootSize = root->maxSize(); + } + Standard_Real XminChild, YminChild, ZminChild; + gp_XYZ minChild; + for (int i = 0; i < 8; i++) + { + // We build the eight boxes, we need 2 points to do that: + // Min and Mid + // In binary, we can write i from 0 to 7 + // For instance : + // 5 is 101, it corresponds here in coordinates to ZYX + // If coordinate is 0 in Y-> box from Ymin to Ymid + // If coordinate is 1 in Y-> box from Ymid to Ymax + // Same scheme for X and Z + // I need the minChild to build the Bnd_B3d box. + + XminChild = (i%2==0)?min.X():mid.X(); + YminChild = ((i%4)/2==0)?min.Y():mid.Y(); + ZminChild = (i<4)?min.Z():mid.Z(); + minChild.SetCoord(XminChild, YminChild, ZminChild); + + // The child is of the same type than its father (For instance, a SMESH_OctreeNode) + // We allocate the memory we need for the child + myChildren[i] = allocateOctreeChild(); + // and we assign to him its box. + myChildren[i]->myFather = this; + myChildren[i]->myLimit = myLimit; + myChildren[i]->myLevel = myLevel + 1; + myChildren[i]->myBox = new Bnd_B3d(minChild+childHsize,childHsize); + myChildren[i]->myBox->Enlarge( rootSize * 1e-10 ); + if ( myLimit->myMinBoxSize > 0. && myChildren[i]->maxSize() <= myLimit->myMinBoxSize ) + myChildren[i]->myIsLeaf = true; + } + + // After building the 8 boxes, we put the data into the children. + buildChildrenData(); + + //After we pass to the next level of the Octree + for (int i = 0; i<8; i++) + myChildren[i]->buildChildren(); +} + +//================================================================================ +/*! + * \brief Tell if Octree is a leaf or not + * An inheriting class can influence it via myIsLeaf protected field + */ +//================================================================================ + +bool SMESH_Octree::isLeaf() const +{ + return myIsLeaf || ((myLimit->myMaxLevel > 0) ? (level() >= myLimit->myMaxLevel) : false ); +} + +//=========================================================================== +/*! + * \brief Compute the bigger dimension of my box + */ +//=========================================================================== + +double SMESH_Octree::maxSize() const +{ + if ( myBox && !myBox->IsVoid() ) + { + gp_XYZ min = myBox->CornerMin(); + gp_XYZ max = myBox->CornerMax(); + gp_XYZ Size = (max - min); + double returnVal = (Size.X()>Size.Y())?Size.X():Size.Y(); + return (returnVal>Size.Z())?returnVal:Size.Z(); + } + return 0.; +} + +//================================================================================ +/*! + * \brief Return height of the tree, full or from this level to topest leaf + */ +//================================================================================ + +int SMESH_Octree::getHeight(const bool full) const +{ + if ( full && myFather ) + return myFather->getHeight( true ); + + if ( isLeaf() ) + return 1; + + int heigth = 0; + for (int i = 0; i<8; i++) + { + int h = myChildren[i]->getHeight( false ); + if ( h > heigth ) + heigth = h; + } + return heigth + 1; +} diff --git a/src/SMESHUtils/SMESH_Octree.hxx b/src/SMESHUtils/SMESH_Octree.hxx new file mode 100644 index 000000000..97d767a60 --- /dev/null +++ b/src/SMESHUtils/SMESH_Octree.hxx @@ -0,0 +1,127 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_Octree : global Octree implementation +// File : SMESH_Octree.hxx +// Created : Tue Jan 16 16:00:00 2007 +// Author : Nicolas Geimer & Aurélien Motteux (OCC) +// Module : SMESH +// +#ifndef _SMESH_OCTREE_HXX_ +#define _SMESH_OCTREE_HXX_ + +#include "SMESH_Utils.hxx" +#include + +class SMESHUtils_EXPORT SMESH_Octree { + +public: + + // Data limiting the tree height + struct Limit { + // MaxLevel of the Octree + int myMaxLevel; + // Minimal size of the Box + double myMinBoxSize; + + // Default: + // maxLevel-> 8^8 = 16777216 terminal trees + // minSize -> box size not checked + Limit(int maxLevel=8, double minSize=0.):myMaxLevel(maxLevel),myMinBoxSize(minSize) {} + virtual ~Limit() {} // it can be inherited + }; + + // Constructor. limit must be provided at tree root construction. + // limit will be deleted by SMESH_Octree + SMESH_Octree (Limit* limit=0); + + // Destructor + virtual ~SMESH_Octree (); + + // Compute the Octree. Must be called by constructor of inheriting class + void compute(); + + // Tell if Octree is a leaf or not. + // An inheriting class can influence it via myIsLeaf protected field + bool isLeaf() const; + + // Return its level + int level() const { return myLevel; } + + // Get box to the 3d Bounding Box of the Octree + const Bnd_B3d& getBox() const { return *myBox; } + + // Compute the bigger dimension of my box + double maxSize() const; + + // Return index of a child the given point is in + inline int getChildIndex(double x, double y, double z, const gp_XYZ& boxMiddle)const; + + // Return height of the tree, full or from this level to topest leaf + int getHeight(const bool full=true) const; + +protected: + // Return box of the whole tree + virtual Bnd_B3d* buildRootBox() = 0; + + // Constructor for children + virtual SMESH_Octree* allocateOctreeChild() const = 0; + + // Build the data in the 8 children + virtual void buildChildrenData() = 0; + + // members + + // Array of 8 Octree children + SMESH_Octree** myChildren; + + // Point the father, set to NULL for the level 0 + SMESH_Octree* myFather; + + // Tell us if the Octree is a leaf or not + bool myIsLeaf; + + // Tree limit + const Limit* myLimit; + +private: + // Build the 8 children boxes recursively + void buildChildren(); + + // Level of the Octree + int myLevel; + + Bnd_B3d* myBox; +}; + +//================================================================================ +/*! + * \brief Return index of a child the given point is in + */ +//================================================================================ + +inline int SMESH_Octree::getChildIndex(double x, double y, double z, const gp_XYZ& mid) const +{ + return (x > mid.X()) + ( y > mid.Y())*2 + (z > mid.Z())*4; +} + +#endif diff --git a/src/SMESHUtils/SMESH_OctreeNode.cxx b/src/SMESHUtils/SMESH_OctreeNode.cxx new file mode 100644 index 000000000..d14a50a24 --- /dev/null +++ b/src/SMESHUtils/SMESH_OctreeNode.cxx @@ -0,0 +1,437 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_OctreeNode : Octree with Nodes set +// inherites global class SMESH_Octree +// File : SMESH_OctreeNode.cxx +// Created : Tue Jan 16 16:00:00 2007 +// Author : Nicolas Geimer & Aurelien Motteux (OCC) +// Module : SMESH +// +#include "SMESH_OctreeNode.hxx" + +#include "SMDS_SetIterator.hxx" +#include + +using namespace std; + +//=============================================================== +/*! + * \brief Constructor : Build all the Octree using Compute() + * \param theNodes - Set of nodes, the Octree is built from this nodes + * \param maxLevel - Maximum level for the leaves + * \param maxNbNodes - Maximum number of nodes, a leaf can contain + * \param minBoxSize - Minimal size of the Octree Box + */ +//================================================================ +SMESH_OctreeNode::SMESH_OctreeNode (const TIDSortedNodeSet & theNodes, const int maxLevel, + const int maxNbNodes , const double minBoxSize ) + :SMESH_Octree( new SMESH_Octree::Limit( maxLevel,minBoxSize)), + myMaxNbNodes(maxNbNodes), + myNodes(theNodes) +{ + compute(); +} + +//================================================================================ +/*! + * \brief Constructor used to allocate a child + */ +//================================================================================ + +SMESH_OctreeNode::SMESH_OctreeNode (int maxNbNodes): + SMESH_Octree(), myMaxNbNodes(maxNbNodes) +{ +} + +//================================================================================== +/*! + * \brief Construct an empty SMESH_OctreeNode used by SMESH_Octree::buildChildren() + */ +//================================================================================== + +SMESH_Octree* SMESH_OctreeNode::allocateOctreeChild() const +{ + return new SMESH_OctreeNode(myMaxNbNodes); +} + +//====================================== +/*! + * \brief Compute the first bounding box + * + * We take the max/min coord of the nodes + */ +//====================================== + +Bnd_B3d* SMESH_OctreeNode::buildRootBox() +{ + Bnd_B3d* box = new Bnd_B3d; + TIDSortedNodeSet::iterator it = myNodes.begin(); + for (; it != myNodes.end(); it++) { + const SMDS_MeshNode* n1 = *it; + gp_XYZ p1( n1->X(), n1->Y(), n1->Z() ); + box->Add(p1); + } + if ( myNodes.size() <= myMaxNbNodes ) + myIsLeaf = true; + + return box; +} + +//==================================================================================== +/*! + * \brief Tells us if Node is inside the current box with the precision "precision" + * \param Node - Node + * \param precision - The box is enlarged with this precision + * \retval bool - True if Node is in the box within precision + */ +//==================================================================================== + +const bool SMESH_OctreeNode::isInside (const gp_XYZ& p, const double precision) +{ + if (precision <= 0.) + return !(getBox().IsOut(p)); + Bnd_B3d BoxWithPrecision = getBox(); + BoxWithPrecision.Enlarge(precision); + return ! BoxWithPrecision.IsOut(p); +} + +//================================================ +/*! + * \brief Set the data of the children + * Shares the father's data with each of his child + */ +//================================================ +void SMESH_OctreeNode::buildChildrenData() +{ + gp_XYZ min = getBox().CornerMin(); + gp_XYZ max = getBox().CornerMax(); + gp_XYZ mid = (min + max)/2.; + + TIDSortedNodeSet::iterator it = myNodes.begin(); + while (it != myNodes.end()) + { + const SMDS_MeshNode* n1 = *it; + int ChildBoxNum = getChildIndex( n1->X(), n1->Y(), n1->Z(), mid ); + SMESH_OctreeNode* myChild = dynamic_cast (myChildren[ChildBoxNum]); + myChild->myNodes.insert(myChild->myNodes.end(),n1); + myNodes.erase( it ); + it = myNodes.begin(); + } + for (int i = 0; i < 8; i++) + { + SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); + if ( myChild->myNodes.size() <= myMaxNbNodes ) + myChild->myIsLeaf = true; + } +} + +//=================================================================== +/*! + * \brief Return in Result a list of Nodes potentials to be near Node + * \param Node - Node + * \param precision - precision used + * \param Result - list of Nodes potentials to be near Node + */ +//==================================================================== +void SMESH_OctreeNode::NodesAround (const SMDS_MeshNode * Node, + list* Result, + const double precision) +{ + gp_XYZ p(Node->X(), Node->Y(), Node->Z()); + if (isInside(p, precision)) + { + if (isLeaf()) + { + Result->insert(Result->end(), myNodes.begin(), myNodes.end()); + } + else + { + for (int i = 0; i < 8; i++) + { + SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); + myChild->NodesAround(Node, Result, precision); + } + } + } +} + +//================================================================================ +/*! + * \brief Return in dist2Nodes nodes mapped to their square distance from Node + * \param node - node to find nodes closest to + * \param dist2Nodes - map of found nodes and their distances + * \param precision - radius of a sphere to check nodes inside + * \retval bool - true if an exact overlapping found + */ +//================================================================================ + +bool SMESH_OctreeNode::NodesAround(const gp_XYZ &node, + map& dist2Nodes, + double precision) +{ + if ( !dist2Nodes.empty() ) + precision = min ( precision, sqrt( dist2Nodes.begin()->first )); + else if ( precision == 0. ) + precision = maxSize() / 2; + + //gp_XYZ p(node->X(), node->Y(), node->Z()); + if (isInside(node, precision)) + { + if (!isLeaf()) + { + // first check a child containing node + gp_XYZ mid = (getBox().CornerMin() + getBox().CornerMax()) / 2.; + int nodeChild = getChildIndex( node.X(), node.Y(), node.Z(), mid ); + if ( ((SMESH_OctreeNode*) myChildren[nodeChild])->NodesAround(node, dist2Nodes, precision)) + return true; + + for (int i = 0; i < 8; i++) + if ( i != nodeChild ) + if (((SMESH_OctreeNode*) myChildren[i])->NodesAround(node, dist2Nodes, precision)) + return true; + } + else if ( NbNodes() > 0 ) + { + double minDist = precision * precision; + gp_Pnt p1 ( node.X(), node.Y(), node.Z() ); + TIDSortedNodeSet::iterator nIt = myNodes.begin(); + for ( ; nIt != myNodes.end(); ++nIt ) + { + gp_Pnt p2 ( (*nIt)->X(), (*nIt)->Y(), (*nIt)->Z() ); + double dist2 = p1.SquareDistance( p2 ); + if ( dist2 < minDist ) + dist2Nodes.insert( make_pair( minDist = dist2, *nIt )); + } +// if ( dist2Nodes.size() > 1 ) // leave only closest node in dist2Nodes +// dist2Nodes.erase( ++dist2Nodes.begin(), dist2Nodes.end()); + + return ( sqrt( minDist) <= precision * 1e-12 ); + } + } + return false; +} + +//============================= +/*! + * \brief Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance + * Search for all the nodes in theSetOfNodes + * Static Method : no need to create an SMESH_OctreeNode + * \param theSetOfNodes - set of nodes we look at, modified during research + * \param theGroupsOfNodes - list of nodes closed to each other returned + * \param theTolerance - Precision used, default value is 0.00001 + * \param maxLevel - Maximum level for SMESH_OctreeNode constructed, default value is -1 (Infinite) + * \param maxNbNodes - maximum Nodes in a Leaf of the SMESH_OctreeNode constructed, default value is 5 + */ +//============================= +void SMESH_OctreeNode::FindCoincidentNodes (TIDSortedNodeSet& theSetOfNodes, + list< list< const SMDS_MeshNode*> >* theGroupsOfNodes, + const double theTolerance, + const int maxLevel, + const int maxNbNodes) +{ + // VSR 14/10/2011: limit max number of the levels in order to avoid endless recursing + const int MAX_LEVEL = 10; + SMESH_OctreeNode theOctreeNode(theSetOfNodes, maxLevel < 0 ? MAX_LEVEL : maxLevel, maxNbNodes, theTolerance); + theOctreeNode.FindCoincidentNodes (&theSetOfNodes, theTolerance, theGroupsOfNodes); +} + +//============================= +/*! + * \brief Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance + * Search for all the nodes in theSetOfNodes + * \note The Octree itself is also modified by this method + * \param theSetOfNodes - set of nodes we look at, modified during research + * \param theTolerance - Precision used + * \param theGroupsOfNodes - list of nodes closed to each other returned + */ +//============================= +void SMESH_OctreeNode::FindCoincidentNodes ( TIDSortedNodeSet* theSetOfNodes, + const double theTolerance, + list< list< const SMDS_MeshNode*> >* theGroupsOfNodes) +{ + TIDSortedNodeSet::iterator it1 = theSetOfNodes->begin(); + list::iterator it2; + + while (it1 != theSetOfNodes->end()) + { + const SMDS_MeshNode * n1 = *it1; + + list ListOfCoincidentNodes;// Initialize the lists via a declaration, it's enough + + list * groupPtr = 0; + + // Searching for Nodes around n1 and put them in ListofCoincidentNodes. + // Found nodes are also erased from theSetOfNodes + FindCoincidentNodes(n1, theSetOfNodes, &ListOfCoincidentNodes, theTolerance); + + // We build a list {n1 + his neigbours} and add this list in theGroupsOfNodes + for (it2 = ListOfCoincidentNodes.begin(); it2 != ListOfCoincidentNodes.end(); it2++) + { + const SMDS_MeshNode* n2 = *it2; + if ( !groupPtr ) + { + theGroupsOfNodes->push_back( list() ); + groupPtr = & theGroupsOfNodes->back(); + groupPtr->push_back( n1 ); + } + if (groupPtr->front() > n2) + groupPtr->push_front( n2 ); + else + groupPtr->push_back( n2 ); + } + if (groupPtr != 0) + groupPtr->sort(); + + theSetOfNodes->erase(it1); + it1 = theSetOfNodes->begin(); + } +} + +//====================================================================================== +/*! + * \brief Return a list of nodes closed to Node and remove it from SetOfNodes + * \note The Octree itself is also modified by this method + * \param Node - We're searching the nodes next to him. + * \param SetOfNodes - set of nodes in which we erase the found nodes + * \param Result - list of nodes closed to Node + * \param precision - Precision used + */ +//====================================================================================== +void SMESH_OctreeNode::FindCoincidentNodes (const SMDS_MeshNode * Node, + TIDSortedNodeSet* SetOfNodes, + list* Result, + const double precision) +{ + gp_XYZ p(Node->X(), Node->Y(), Node->Z()); + bool isInsideBool = isInside(p, precision); + + if (isInsideBool) + { + // I'm only looking in the leaves, since all the nodes are stored there. + if (isLeaf()) + { + gp_Pnt p1 (Node->X(), Node->Y(), Node->Z()); + + TIDSortedNodeSet myNodesCopy = myNodes; + TIDSortedNodeSet::iterator it = myNodesCopy.begin(); + double tol2 = precision * precision; + bool squareBool; + + while (it != myNodesCopy.end()) + { + const SMDS_MeshNode* n2 = *it; + // We're only looking at nodes with a superior Id. + // JFA: Why? + //if (Node->GetID() < n2->GetID()) + if (Node->GetID() != n2->GetID()) // JFA: for bug 0020185 + { + gp_Pnt p2 (n2->X(), n2->Y(), n2->Z()); + // Distance optimized computation + squareBool = (p1.SquareDistance( p2 ) <= tol2); + + // If n2 inside the SquareDistance, we add it in Result and remove it from SetOfNodes and myNodes + if (squareBool) + { + Result->insert(Result->begin(), n2); + SetOfNodes->erase( n2 ); + myNodes.erase( n2 ); + } + } + //myNodesCopy.erase( it ); + //it = myNodesCopy.begin(); + it++; + } + if (Result->size() > 0) + myNodes.erase(Node); // JFA: for bug 0020185 + } + else + { + // If I'm not a leaf, I'm going to see my children ! + for (int i = 0; i < 8; i++) + { + SMESH_OctreeNode* myChild = dynamic_cast (myChildren[i]); + myChild->FindCoincidentNodes(Node, SetOfNodes, Result, precision); + } + } + } +} + +//================================================================================ +/*! + * \brief Update data according to node movement + */ +//================================================================================ + +void SMESH_OctreeNode::UpdateByMoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt ) +{ + if ( isLeaf() ) + { + TIDSortedNodeSet::iterator pNode = myNodes.find( node ); + bool nodeInMe = ( pNode != myNodes.end() ); + + bool pointInMe = isInside( toPnt.Coord(), 1e-10 ); + + if ( pointInMe != nodeInMe ) + { + if ( pointInMe ) + myNodes.insert( node ); + else + myNodes.erase( node ); + } + } + else if ( myChildren ) + { + gp_XYZ mid = (getBox().CornerMin() + getBox().CornerMax()) / 2.; + int nodeChild = getChildIndex( node->X(), node->Y(), node->Z(), mid ); + int pointChild = getChildIndex( toPnt.X(), toPnt.Y(), toPnt.Z(), mid ); + if ( nodeChild != pointChild ) + { + ((SMESH_OctreeNode*) myChildren[ nodeChild ])->UpdateByMoveNode( node, toPnt ); + ((SMESH_OctreeNode*) myChildren[ pointChild ])->UpdateByMoveNode( node, toPnt ); + } + } +} + +//================================================================================ +/*! + * \brief Return iterator over children + */ +//================================================================================ +SMESH_OctreeNodeIteratorPtr SMESH_OctreeNode::GetChildrenIterator() +{ + return SMESH_OctreeNodeIteratorPtr + ( new SMDS_SetIterator< SMESH_OctreeNode*, SMESH_Octree** > + ( myChildren, (( isLeaf() || !myChildren ) ? myChildren : &myChildren[ 8 ] ))); +} + +//================================================================================ +/*! + * \brief Return nodes iterator + */ +//================================================================================ +SMDS_NodeIteratorPtr SMESH_OctreeNode::GetNodeIterator() +{ + return SMDS_NodeIteratorPtr + ( new SMDS_SetIterator< SMDS_pNode, TIDSortedNodeSet::const_iterator > + ( myNodes.begin(), myNodes.size() ? myNodes.end() : myNodes.begin())); +} diff --git a/src/SMESHUtils/SMESH_OctreeNode.hxx b/src/SMESHUtils/SMESH_OctreeNode.hxx new file mode 100644 index 000000000..cc66a275b --- /dev/null +++ b/src/SMESHUtils/SMESH_OctreeNode.hxx @@ -0,0 +1,137 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_OctreeNode : Octree with Nodes set +// inherites global class SMESH_Octree +// File : SMESH_OctreeNode.hxx +// Created : Tue Jan 16 16:00:00 2007 +// Author : Nicolas Geimer & Aurelien Motteux (OCC) +// Module : SMESH +// +#ifndef _SMESH_OCTREENODE_HXX_ +#define _SMESH_OCTREENODE_HXX_ + +#include "SMESH_Utils.hxx" +#include "SMESH_Octree.hxx" +#include +#include "SMDS_MeshNode.hxx" + +#include +#include +#include + +#include "SMDS_ElemIterator.hxx" + +//forward declaration +class SMDS_MeshNode; +class SMESH_OctreeNode; + +typedef SMDS_Iterator SMESH_OctreeNodeIterator; +typedef boost::shared_ptr SMESH_OctreeNodeIteratorPtr; +typedef std::set< const SMDS_MeshNode*, TIDCompare > TIDSortedNodeSet; + +class SMESHUtils_EXPORT SMESH_OctreeNode : public SMESH_Octree { + +public: + + // Constructor + SMESH_OctreeNode (const TIDSortedNodeSet& theNodes, const int maxLevel = 8, + const int maxNbNodes = 5, const double minBoxSize = 0.); + +//============================= +/*! + * \brief Empty destructor + */ +//============================= + virtual ~SMESH_OctreeNode () {}; + + // Tells us if Node is inside the current box with the precision "precision" + virtual const bool isInside(const gp_XYZ& p, const double precision = 0.); + + // Return in Result a list of Nodes potentials to be near Node + void NodesAround(const SMDS_MeshNode * Node, + std::list* Result, + const double precision = 0.); + + // Return in dist2Nodes nodes mapped to their square distance from Node + bool NodesAround(const gp_XYZ& node, + std::map& dist2Nodes, + double precision); + + // Return in theGroupsOfNodes a list of group of nodes close to each other within theTolerance + // Search for all the nodes in nodes + void FindCoincidentNodes ( TIDSortedNodeSet* nodes, + const double theTolerance, + std::list< std::list< const SMDS_MeshNode*> >* theGroupsOfNodes); + + // Static method that return in theGroupsOfNodes a list of group of nodes close to each other within + // theTolerance search for all the nodes in nodes + static void FindCoincidentNodes ( TIDSortedNodeSet& nodes, + std::list< std::list< const SMDS_MeshNode*> >* theGroupsOfNodes, + const double theTolerance = 0.00001, + const int maxLevel = -1, + const int maxNbNodes = 5); + /*! + * \brief Update data according to node movement + */ + void UpdateByMoveNode( const SMDS_MeshNode* node, const gp_Pnt& toPnt ); + /*! + * \brief Return iterator over children + */ + SMESH_OctreeNodeIteratorPtr GetChildrenIterator(); + /*! + * \brief Return nodes iterator + */ + SMDS_NodeIteratorPtr GetNodeIterator(); + /*! + * \brief Return nb nodes in a tree + */ + int NbNodes() const { return myNodes.size(); } + +protected: + + SMESH_OctreeNode (int maxNbNodes ); + + // Compute the bounding box of the whole set of nodes myNodes + virtual Bnd_B3d* buildRootBox(); + + // Shares the father's data with each of his child + virtual void buildChildrenData(); + + // Construct an empty SMESH_OctreeNode used by SMESH_Octree::buildChildren() + virtual SMESH_Octree* allocateOctreeChild() const; + + // Return in result a list of nodes closed to Node and remove it from SetOfNodes + void FindCoincidentNodes( const SMDS_MeshNode * Node, + TIDSortedNodeSet* SetOfNodes, + std::list* Result, + const double precision); + + // The max number of nodes a leaf box can contain + int myMaxNbNodes; + + // The set of nodes inside the box of the Octree (Empty if Octree is not a leaf) + TIDSortedNodeSet myNodes; + +}; + +#endif diff --git a/src/SMESHUtils/SMESH_TypeDefs.hxx b/src/SMESHUtils/SMESH_TypeDefs.hxx new file mode 100644 index 000000000..edb0f247f --- /dev/null +++ b/src/SMESHUtils/SMESH_TypeDefs.hxx @@ -0,0 +1,184 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_TypeDefs.hxx +// Created : Thu Jan 27 18:38:33 2011 +// Author : Edward AGAPOV (eap) + + +#ifndef __SMESH_TypeDefs_HXX__ +#define __SMESH_TypeDefs_HXX__ + +#include "SMESH_Utils.hxx" + +#include + +#include + +#include +#include +#include +#include + +typedef std::map > TElemOfElemListMap; +typedef std::map TNodeNodeMap; + +//!< Set of elements sorted by ID, to be used to assure predictability of edition +typedef std::set< const SMDS_MeshElement*, TIDCompare > TIDSortedElemSet; +typedef std::set< const SMDS_MeshNode*, TIDCompare > TIDSortedNodeSet; + +typedef std::pair< const SMDS_MeshNode*, const SMDS_MeshNode* > NLink; + + +namespace SMESHUtils +{ + /*! + * \brief Enforce freeing memory allocated by std::vector + */ + template + void FreeVector(TVECTOR& vec) + { + TVECTOR v2; + vec.swap( v2 ); + } + template + void CompactVector(TVECTOR& vec) + { + TVECTOR v2( vec ); + vec.swap( v2 ); + } +} + +//======================================================================= +/*! + * \brief A sorted pair of nodes + */ +//======================================================================= + +struct SMESH_TLink: public NLink +{ + SMESH_TLink(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 ):NLink( n1, n2 ) + { if ( n1->GetID() < n2->GetID() ) std::swap( first, second ); } + SMESH_TLink(const NLink& link ):NLink( link ) + { if ( first->GetID() < second->GetID() ) std::swap( first, second ); } + const SMDS_MeshNode* node1() const { return first; } + const SMDS_MeshNode* node2() const { return second; } +}; + +//======================================================================= +/*! + * \brief SMESH_TLink knowing its orientation + */ +//======================================================================= + +struct SMESH_OrientedLink: public SMESH_TLink +{ + bool _reversed; + SMESH_OrientedLink(const SMDS_MeshNode* n1, const SMDS_MeshNode* n2 ) + : SMESH_TLink( n1, n2 ), _reversed( n1 != node1() ) {} +}; + +//------------------------------------------ +/*! + * \brief SMDS_MeshNode -> gp_XYZ convertor + */ +//------------------------------------------ +struct SMESH_TNodeXYZ : public gp_XYZ +{ + const SMDS_MeshNode* _node; + double _xyz[3]; + SMESH_TNodeXYZ( const SMDS_MeshElement* e):gp_XYZ(0,0,0),_node(0) { + if (e) { + assert( e->GetType() == SMDSAbs_Node ); + _node = static_cast(e); + _node->GetXYZ(_xyz); // - thread safe getting coords + SetCoord( _xyz[0], _xyz[1], _xyz[2] ); + } + } + double Distance(const SMDS_MeshNode* n) const { return (SMESH_TNodeXYZ( n )-*this).Modulus(); } + double SquareDistance(const SMDS_MeshNode* n) const { return (SMESH_TNodeXYZ( n )-*this).SquareModulus(); } + bool operator==(const SMESH_TNodeXYZ& other) const { return _node == other._node; } +}; + +// -------------------------------------------------------------------------------- +// class SMESH_SequenceOfElemPtr +#include + +class SMDS_MeshElement; + +typedef const SMDS_MeshElement* SMDS_MeshElementPtr; + +DEFINE_BASECOLLECTION (SMESH_BaseCollectionElemPtr, SMDS_MeshElementPtr) +DEFINE_SEQUENCE (SMESH_SequenceOfElemPtr, SMESH_BaseCollectionElemPtr, SMDS_MeshElementPtr) + + +// -------------------------------------------------------------------------------- +// class SMESH_SequenceOfNode +typedef const SMDS_MeshNode* SMDS_MeshNodePtr; + +DEFINE_BASECOLLECTION (SMESH_BaseCollectionNodePtr, SMDS_MeshNodePtr) +DEFINE_SEQUENCE(SMESH_SequenceOfNode, + SMESH_BaseCollectionNodePtr, SMDS_MeshNodePtr) + +// -------------------------------------------------------------------------------- +// #include "SMESHDS_DataMapOfShape.hxx" + +// #include + +// #include + +/// Class SMESH_IndexedMapOfShape + +// DEFINE_BASECOLLECTION (SMESH_BaseCollectionShape, TopoDS_Shape) +// DEFINE_INDEXEDMAP (SMESH_IndexedMapOfShape, SMESH_BaseCollectionShape, TopoDS_Shape) + +/// Class SMESH_IndexedDataMapOfShapeIndexedMapOfShape + +// DEFINE_BASECOLLECTION (SMESH_BaseCollectionIndexedMapOfShape, SMESH_IndexedMapOfShape) +// DEFINE_INDEXEDDATAMAP (SMESH_IndexedDataMapOfShapeIndexedMapOfShape, +// SMESH_BaseCollectionIndexedMapOfShape, TopoDS_Shape, +// SMESH_IndexedMapOfShape) + +// -------------------------------------------------------------------------------- +// class SMESH_DataMapOfElemPtrSequenceOfElemPtr + +// SMESHUtils_EXPORT +// inline Standard_Integer HashCode(SMDS_MeshElementPtr theElem, +// const Standard_Integer theUpper) +// { +// void* anElem = (void*) theElem; +// return HashCode(anElem,theUpper); +// } + +// SMESHUtils_EXPORT +// inline Standard_Boolean IsEqual(SMDS_MeshElementPtr theOne, +// SMDS_MeshElementPtr theTwo) +// { +// return theOne == theTwo; +// } + +// DEFINE_BASECOLLECTION (SMESH_BaseCollectionSequenceOfElemPtr, SMESH_SequenceOfElemPtr) +// DEFINE_DATAMAP (SMESH_DataMapOfElemPtrSequenceOfElemPtr, +// SMESH_BaseCollectionSequenceOfElemPtr, +// SMDS_MeshElementPtr, SMESH_SequenceOfElemPtr) + +#endif diff --git a/src/SMESHUtils/SMESH_Utils.hxx b/src/SMESHUtils/SMESH_Utils.hxx new file mode 100755 index 000000000..45c4a2c52 --- /dev/null +++ b/src/SMESHUtils/SMESH_Utils.hxx @@ -0,0 +1,40 @@ +// Copyright (C) 2007-2012 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 +// + +// File : SMESH_Utils.hxx +// Author : Alexander A. BORODIN +// Module : SMESH +// +#ifndef _SMESH_Utils_hxx_ +#define _SMESH_Utils_hxx_ + +#ifdef WNT + #if defined SMESHUtils_EXPORTS + #define SMESHUtils_EXPORT __declspec( dllexport ) + #else + #define SMESHUtils_EXPORT __declspec( dllimport ) + #endif +#else + #define SMESHUtils_EXPORT +#endif + +#endif diff --git a/src/SMESH_I/Makefile.am b/src/SMESH_I/Makefile.am index 2a15a44df..2f748f7da 100644 --- a/src/SMESH_I/Makefile.am +++ b/src/SMESH_I/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses # File : Makefile.in # Author : Paul RASCLE, EDF @@ -48,6 +46,8 @@ salomeinclude_HEADERS = \ SMESH_Pattern_i.hxx \ SMESH_2smeshpy.hxx \ SMESH_NoteBook.hxx \ + SMESH_Measurements_i.hxx \ + SMESH_PreMeshInfo.hxx \ SMESH.hxx # Scripts to be installed. @@ -78,7 +78,9 @@ dist_libSMESHEngine_la_SOURCES = \ SMESH_Group_i.cxx \ SMESH_Pattern_i.cxx \ SMESH_2smeshpy.cxx \ - SMESH_NoteBook.cxx + SMESH_NoteBook.cxx \ + SMESH_Measurements_i.cxx \ + SMESH_PreMeshInfo.cxx # Executables targets bin_PROGRAMS = SMESHEngine @@ -92,6 +94,7 @@ libSMESHEngine_la_CPPFLAGS = \ $(CORBA_INCLUDES) \ $(CAS_CPPFLAGS) \ @HDF5_INCLUDES@ \ + $(VTK_INCLUDES) \ $(BOOST_CPPFLAGS) \ $(KERNEL_CXXFLAGS) \ $(GUI_CXXFLAGS) \ @@ -102,14 +105,16 @@ libSMESHEngine_la_CPPFLAGS = \ -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../Driver \ -I$(srcdir)/../DriverMED \ + -I$(srcdir)/../DriverCGNS \ -I$(srcdir)/../SMESH \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix - + -I$(srcdir)/../SMESHUtils \ + -I$(top_builddir)/idl libSMESHEngine_la_LDFLAGS = \ ../../idl/libSalomeIDLSMESH.la \ ../SMESH/libSMESHimpl.la \ + ../SMDS/libSMDS.la \ + ../SMESHDS/libSMESHDS.la \ ../Controls/libSMESHControls.la \ $(KERNEL_LDFLAGS) \ -lSalomeContainer \ @@ -119,7 +124,10 @@ libSMESHEngine_la_LDFLAGS = \ -lSalomeLifeCycleCORBA \ -lTOOLSDS \ -lSalomeGenericObj \ + -lSalomeIDLKernel \ + -lSALOMELocalTrace \ $(MED_LDFLAGS) \ + -lMEDWrapper \ -lMEDWrapper_V2_2 \ -lSalomeIDLMED \ $(CAS_LDPATH) \ @@ -127,7 +135,8 @@ libSMESHEngine_la_LDFLAGS = \ -lTKBO \ -lTKShHealing \ $(GEOM_LDFLAGS) \ - -lGEOMClient + -lGEOMClient \ + -lSalomeIDLGEOM SMESHEngine_CPPFLAGS = \ $(libSMESHEngine_la_CPPFLAGS) diff --git a/src/SMESH_I/SMESH.hxx b/src/SMESH_I/SMESH.hxx index 0bd99b692..579ae9127 100644 --- a/src/SMESH_I/SMESH.hxx +++ b/src/SMESH_I/SMESH.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : // File : SMESH.hxx // Author : Michael ZORIN @@ -29,7 +30,7 @@ #define _SMESH_I_SMESH_HXX_ #ifdef WNT - #if defined SMESH_I_EXPORTS + #if defined SMESH_I_EXPORTS || defined SMESHEngine_EXPORTS #define SMESH_I_EXPORT __declspec( dllexport ) #else #define SMESH_I_EXPORT __declspec( dllimport ) diff --git a/src/SMESH_I/SMESHEngine.cxx b/src/SMESH_I/SMESHEngine.cxx index 0c8d3bd60..5034d2cdd 100644 --- a/src/SMESH_I/SMESHEngine.cxx +++ b/src/SMESH_I/SMESHEngine.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + int main(int argc, char** argv) { return 1; diff --git a/src/SMESH_I/SMESH_0D_Algo_i.cxx b/src/SMESH_I/SMESH_0D_Algo_i.cxx index cd8247ba9..84f9067d5 100644 --- a/src/SMESH_I/SMESH_0D_Algo_i.cxx +++ b/src/SMESH_I/SMESH_0D_Algo_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_0D_Algo_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_0D_Algo_i.hxx b/src/SMESH_I/SMESH_0D_Algo_i.hxx index 401feed17..dd23a1c1c 100644 --- a/src/SMESH_I/SMESH_0D_Algo_i.hxx +++ b/src/SMESH_I/SMESH_0D_Algo_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_0D_Algo_i.hxx // Module : SMESH diff --git a/src/SMESH_I/SMESH_1D_Algo_i.cxx b/src/SMESH_I/SMESH_1D_Algo_i.cxx index e5da53f79..9dcf746ac 100644 --- a/src/SMESH_I/SMESH_1D_Algo_i.cxx +++ b/src/SMESH_I/SMESH_1D_Algo_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_1D_Algo_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_1D_Algo_i.hxx b/src/SMESH_I/SMESH_1D_Algo_i.hxx index 4ae7b0ea5..b8c912e62 100644 --- a/src/SMESH_I/SMESH_1D_Algo_i.hxx +++ b/src/SMESH_I/SMESH_1D_Algo_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_1D_Algo_i.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_2D_Algo_i.cxx b/src/SMESH_I/SMESH_2D_Algo_i.cxx index 7fcf5a86d..37d228f76 100644 --- a/src/SMESH_I/SMESH_2D_Algo_i.cxx +++ b/src/SMESH_I/SMESH_2D_Algo_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_2D_Algo_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_2D_Algo_i.hxx b/src/SMESH_I/SMESH_2D_Algo_i.hxx index 2579278fb..43277ccf5 100644 --- a/src/SMESH_I/SMESH_2D_Algo_i.hxx +++ b/src/SMESH_I/SMESH_2D_Algo_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_2D_Algo_i.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_2smeshpy.cxx b/src/SMESH_I/SMESH_2smeshpy.cxx index be494755b..88e7ba182 100644 --- a/src/SMESH_I/SMESH_2smeshpy.cxx +++ b/src/SMESH_I/SMESH_2smeshpy.cxx @@ -1,26 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_2smeshpy.cxx // Created : Fri Nov 18 13:20:10 2005 // Author : Edward AGAPOV (eap) @@ -30,34 +29,52 @@ #include "utilities.h" #include "SMESH_PythonDump.hxx" #include "SMESH_NoteBook.hxx" -#include "Resource_DataMapOfAsciiStringAsciiString.hxx" +#include "SMESH_Filter_i.hxx" + +#include +#include #include "SMESH_Gen_i.hxx" -/* SALOME headers that include CORBA headers that include windows.h +/* SALOME headers that include CORBA headers that include windows.h * that defines GetObject symbol as GetObjectA should stand before SALOME headers * that declare methods named GetObject - to apply the same rules of GetObject renaming * and thus to avoid mess with GetObject symbol on Windows */ +#include + +#ifdef WNT +#include +#else +#include +#endif + + IMPLEMENT_STANDARD_HANDLE (_pyObject ,Standard_Transient); IMPLEMENT_STANDARD_HANDLE (_pyCommand ,Standard_Transient); +IMPLEMENT_STANDARD_HANDLE (_pyHypothesisReader,Standard_Transient); IMPLEMENT_STANDARD_HANDLE (_pyGen ,_pyObject); IMPLEMENT_STANDARD_HANDLE (_pyMesh ,_pyObject); IMPLEMENT_STANDARD_HANDLE (_pySubMesh ,_pyObject); IMPLEMENT_STANDARD_HANDLE (_pyMeshEditor ,_pyObject); IMPLEMENT_STANDARD_HANDLE (_pyHypothesis ,_pyObject); -IMPLEMENT_STANDARD_HANDLE (_pyFilterManager ,_pyObject); +IMPLEMENT_STANDARD_HANDLE (_pySelfEraser ,_pyObject); +IMPLEMENT_STANDARD_HANDLE (_pyGroup ,_pyObject); +IMPLEMENT_STANDARD_HANDLE (_pyFilter ,_pyObject); IMPLEMENT_STANDARD_HANDLE (_pyAlgorithm ,_pyHypothesis); IMPLEMENT_STANDARD_HANDLE (_pyComplexParamHypo,_pyHypothesis); IMPLEMENT_STANDARD_HANDLE (_pyNumberOfSegmentsHyp,_pyHypothesis); IMPLEMENT_STANDARD_RTTIEXT(_pyObject ,Standard_Transient); IMPLEMENT_STANDARD_RTTIEXT(_pyCommand ,Standard_Transient); +IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesisReader,Standard_Transient); IMPLEMENT_STANDARD_RTTIEXT(_pyGen ,_pyObject); IMPLEMENT_STANDARD_RTTIEXT(_pyMesh ,_pyObject); IMPLEMENT_STANDARD_RTTIEXT(_pySubMesh ,_pyObject); IMPLEMENT_STANDARD_RTTIEXT(_pyMeshEditor ,_pyObject); IMPLEMENT_STANDARD_RTTIEXT(_pyHypothesis ,_pyObject); -IMPLEMENT_STANDARD_RTTIEXT(_pyFilterManager ,_pyObject); +IMPLEMENT_STANDARD_RTTIEXT(_pySelfEraser ,_pyObject); +IMPLEMENT_STANDARD_RTTIEXT(_pyGroup ,_pyObject); +IMPLEMENT_STANDARD_RTTIEXT(_pyFilter ,_pyObject); IMPLEMENT_STANDARD_RTTIEXT(_pyAlgorithm ,_pyHypothesis); IMPLEMENT_STANDARD_RTTIEXT(_pyComplexParamHypo,_pyHypothesis); IMPLEMENT_STANDARD_RTTIEXT(_pyNumberOfSegmentsHyp,_pyHypothesis); @@ -106,6 +123,166 @@ namespace { return find( name ) != end(); } }; + + //================================================================================ + /*! + * \brief Returns a mesh by object + */ + //================================================================================ + + Handle(_pyMesh) ObjectToMesh( const Handle( _pyObject )& obj ) + { + if ( !obj.IsNull() ) + { + if ( obj->IsKind( STANDARD_TYPE( _pyMesh ))) + return Handle(_pyMesh)::DownCast( obj ); + else if ( obj->IsKind( STANDARD_TYPE( _pySubMesh ))) + return Handle(_pySubMesh)::DownCast( obj )->GetMesh(); + else if ( obj->IsKind( STANDARD_TYPE( _pyGroup ))) + return Handle(_pyGroup)::DownCast( obj )->GetMesh(); + } + return Handle(_pyMesh)(); + } + + //================================================================================ + /*! + * \brief Check if objects used as args have been created by previous commands + */ + //================================================================================ + + void CheckObjectPresence( const Handle(_pyCommand)& cmd, set<_pyID> & presentObjects) + { + for ( int iArg = cmd->GetNbArgs(); iArg; --iArg ) + { + const _pyID& arg = cmd->GetArg( iArg ); + if ( arg.IsEmpty() || arg.Value( 1 ) == '"' || arg.Value( 1 ) == '\'' ) + continue; + list< _pyID > idList = cmd->GetStudyEntries( arg ); + list< _pyID >::iterator id = idList.begin(); + for ( ; id != idList.end(); ++id ) + if ( !theGen->IsGeomObject( *id ) && !presentObjects.count( *id )) + { + cmd->Comment(); + cmd->GetString() += " ### " ; + cmd->GetString() += *id + " has not been yet created"; + return; + } + } + const _pyID& obj = cmd->GetObject(); + if ( !obj.IsEmpty() && cmd->IsStudyEntry( obj ) && !presentObjects.count( obj )) + { + cmd->Comment(); + cmd->GetString() += " ### not created object" ; + } + const _pyID& result = cmd->GetResultValue(); + if ( result.IsEmpty() || result.Value( 1 ) == '"' || result.Value( 1 ) == '\'' ) + return; + list< _pyID > idList = cmd->GetStudyEntries( result ); + list< _pyID >::iterator id = idList.begin(); + for ( ; id != idList.end(); ++id ) + presentObjects.insert( *id ); + } + + //================================================================================ + /*! + * \brief Fix SMESH::FunctorType arguments of SMESH::Filter::Criterion() + */ + //================================================================================ + + void fixFunctorType( TCollection_AsciiString& Type, + TCollection_AsciiString& Compare, + TCollection_AsciiString& UnaryOp, + TCollection_AsciiString& BinaryOp ) + { + // The problem is that dumps of old studies created using filters becomes invalid + // when new items are inserted in the enum SMESH::FunctorType since values + // of this enum are dumped as integer values. + // This function corrects enum values of old studies given as args (Type,Compare,...) + // We can find out how to correct them by value of BinaryOp which can have only two + // values: FT_Undefined or FT_LogicalNOT. + // Hereafter is the history of the enum SMESH::FunctorType since v3.0.0 + // where PythonDump appeared + // v 3.0.0: FT_Undefined == 25 + // v 3.1.0: FT_Undefined == 26, new items: + // - FT_Volume3D = 7 + // v 4.1.2: FT_Undefined == 27, new items: + // - FT_BelongToGenSurface = 17 + // v 5.1.1: FT_Undefined == 32, new items: + // - FT_FreeNodes = 10 + // - FT_FreeFaces = 11 + // - FT_LinearOrQuadratic = 23 + // - FT_GroupColor = 24 + // - FT_ElemGeomType = 25 + // v 5.1.5: FT_Undefined == 33, new items: + // - FT_CoplanarFaces = 26 + // v 6.2.0: FT_Undefined == 39, new items: + // - FT_MaxElementLength2D = 8 + // - FT_MaxElementLength3D = 9 + // - FT_BareBorderVolume = 25 + // - FT_BareBorderFace = 26 + // - FT_OverConstrainedVolume = 27 + // - FT_OverConstrainedFace = 28 + // v 6.5.0: FT_Undefined == 43, new items: + // - FT_EqualNodes = 14 + // - FT_EqualEdges = 15 + // - FT_EqualFaces = 16 + // - FT_EqualVolumes = 17 + // v 6.6.0: FT_Undefined == 44, new items: + // - FT_BallDiameter = 37 + // + // It's necessary to continue recording this history and to fill + // undef2newItems (see below) accordingly. + + typedef map< int, vector< int > > TUndef2newItems; + static TUndef2newItems undef2newItems; + if ( undef2newItems.empty() ) + { + undef2newItems[ 26 ].push_back( 7 ); + undef2newItems[ 27 ].push_back( 17 ); + { int items[] = { 10, 11, 23, 24, 25 }; + undef2newItems[ 32 ].assign( items, items+5 ); } + undef2newItems[ 33 ].push_back( 26 ); + { int items[] = { 8, 9, 25, 26, 27, 28 }; + undef2newItems[ 39 ].assign( items, items+6 ); } + { int items[] = { 14, 15, 16, 17 }; + undef2newItems[ 43 ].assign( items, items+4 ); } + { int items[] = { 37 }; + undef2newItems[ 44 ].assign( items, items+1 ); } + } + + int iType = Type.IntegerValue(); + int iCompare = Compare.IntegerValue(); + int iUnaryOp = UnaryOp.IntegerValue(); + int iBinaryOp = BinaryOp.IntegerValue(); + + // find out integer value of FT_Undefined at the moment of dump + int oldUndefined = iBinaryOp; + if ( iBinaryOp < iUnaryOp ) // BinaryOp was FT_LogicalNOT + oldUndefined += 3; + + // apply history to args + TUndef2newItems::const_iterator undef_items = + undef2newItems.upper_bound( oldUndefined ); + if ( undef_items != undef2newItems.end() ) + { + int* pArg[4] = { &iType, &iCompare, &iUnaryOp, &iBinaryOp }; + for ( ; undef_items != undef2newItems.end(); ++undef_items ) + { + const vector< int > & addedItems = undef_items->second; + for ( size_t i = 0; i < addedItems.size(); ++i ) + for ( int iArg = 0; iArg < 4; ++iArg ) + { + int& arg = *pArg[iArg]; + if ( arg >= addedItems[i] ) + arg++; + } + } + Type = TCollection_AsciiString( iType ); + Compare = TCollection_AsciiString( iCompare ); + UnaryOp = TCollection_AsciiString( iUnaryOp ); + BinaryOp = TCollection_AsciiString( iBinaryOp ); + } + } } //================================================================================ @@ -113,22 +290,26 @@ namespace { * \brief Convert python script using commands of smesh.py * \param theScript - Input script * \retval TCollection_AsciiString - Convertion result + * \param theToKeepAllCommands - to keep all commands or + * to exclude commands relating to objects removed from study * * Class SMESH_2smeshpy declared in SMESH_PythonDump.hxx */ //================================================================================ TCollection_AsciiString -SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, +SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod, - Resource_DataMapOfAsciiStringAsciiString& theObjectNames) + Resource_DataMapOfAsciiStringAsciiString& theObjectNames, + SALOMEDS::Study_ptr& theStudy, + const bool theToKeepAllCommands) { - theGen = new _pyGen( theEntry2AccessorMethod, theObjectNames ); + theGen = new _pyGen( theEntry2AccessorMethod, theObjectNames, theStudy, theToKeepAllCommands ); // split theScript into separate commands SMESH_NoteBook * aNoteBook = new SMESH_NoteBook(); - + int from = 1, end = theScript.Length(), to; while ( from < end && ( to = theScript.Location( "\n", from, end ))) { @@ -137,13 +318,13 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, aNoteBook->AddCommand( theScript.SubString( from, to - 1 )); from = to + 1; } - + aNoteBook->ReplaceVariables(); TCollection_AsciiString aNoteScript = aNoteBook->GetResultScript(); delete aNoteBook; aNoteBook = 0; - + // split theScript into separate commands from = 1, end = aNoteScript.Length(); while ( from < end && ( to = aNoteScript.Location( "\n", from, end ))) @@ -160,6 +341,9 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, MESSAGE_BEGIN ( std::endl << " ######## RESULT ######## " << std::endl<< std::endl ); #endif + // clean commmands of removed objects depending on myIsPublished flag + theGen->ClearCommands(); + // reorder commands after conversion list< Handle(_pyCommand) >::iterator cmd; bool orderChanges; @@ -169,21 +353,25 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, if ( (*cmd)->SetDependentCmdsAfter() ) orderChanges = true; } while ( orderChanges ); - + // concat commands back into a script - TCollection_AsciiString aScript; + TCollection_AsciiString aScript, aPrevCmd; + set<_pyID> createdObjects; for ( cmd = theGen->GetCommands().begin(); cmd != theGen->GetCommands().end(); ++cmd ) { #ifdef DUMP_CONVERSION MESSAGE_ADD ( "## COM " << (*cmd)->GetOrderNb() << ": "<< (*cmd)->GetString() << std::endl ); #endif - if ( !(*cmd)->IsEmpty() ) { + if ( !(*cmd)->IsEmpty() && aPrevCmd != (*cmd)->GetString()) { + CheckObjectPresence( *cmd, createdObjects ); + aPrevCmd = (*cmd)->GetString(); aScript += "\n"; - aScript += (*cmd)->GetString(); + aScript += aPrevCmd; } } aScript += "\n"; + theGen->Free(); theGen.Nullify(); return aScript; @@ -196,15 +384,49 @@ SMESH_2smeshpy::ConvertScript(const TCollection_AsciiString& theScript, //================================================================================ _pyGen::_pyGen(Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod, - Resource_DataMapOfAsciiStringAsciiString& theObjectNames) - : _pyObject( new _pyCommand( TPythonDump::SMESHGenName(), 0 )), + Resource_DataMapOfAsciiStringAsciiString& theObjectNames, + SALOMEDS::Study_ptr& theStudy, + const bool theToKeepAllCommands) + : _pyObject( new _pyCommand( "", 0 )), + myNbCommands( 0 ), myID2AccessorMethod( theEntry2AccessorMethod ), - myObjectNames( theObjectNames ) + myObjectNames( theObjectNames ), + myNbFilters( 0 ), + myToKeepAllCommands( theToKeepAllCommands ), + myStudy( SALOMEDS::Study::_duplicate( theStudy )), + myGeomIDNb(0), myGeomIDIndex(-1) { - myNbCommands = 0; - myHasPattern = false; // make that GetID() to return TPythonDump::SMESHGenName() + GetCreationCmd()->Clear(); + GetCreationCmd()->GetString() = TPythonDump::SMESHGenName(); GetCreationCmd()->GetString() += "="; + + // Find 1st digit of study entry by which a GEOM object differs from a SMESH object + if ( !theObjectNames.IsEmpty() && !CORBA::is_nil( theStudy )) + { + // find a GEOM entry + _pyID geomID; + SALOMEDS::SComponent_var geomComp = theStudy->FindComponent("GEOM"); + if ( geomComp->_is_nil() ) return; + CORBA::String_var entry = geomComp->GetID(); + geomID = entry.in(); + + // find a SMESH entry + _pyID smeshID; + Resource_DataMapIteratorOfDataMapOfAsciiStringAsciiString e2n( theObjectNames ); + for ( ; e2n.More() && smeshID.IsEmpty(); e2n.Next() ) + if ( _pyCommand::IsStudyEntry( e2n.Key() )) + smeshID = e2n.Key(); + + // find 1st difference between smeshID and geomID + if ( !geomID.IsEmpty() && !smeshID.IsEmpty() ) + for ( int i = 1; i <= geomID.Length() && i <= smeshID.Length(); ++i ) + if ( geomID.Value( i ) != smeshID.Value( i )) + { + myGeomIDNb = geomID.Value( i ); + myGeomIDIndex = i; + } + } } //================================================================================ @@ -235,74 +457,146 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand MESSAGE ( "## COM " << myNbCommands << ": "<< aCommand->GetString() ); #endif - _pyID objID = aCommand->GetObject(); + const _pyID& objID = aCommand->GetObject(); if ( objID.IsEmpty() ) return aCommand; + // Prevent moving a command creating a sub-mesh to the end of the script + // if the sub-mesh is used in theCommand as argument + if ( _pySubMesh::CanBeArgOfMethod( aCommand->GetMethod() )) + { + PlaceSubmeshAfterItsCreation( aCommand ); + } + + // Find an object to process theCommand + // SMESH_Gen method? - if ( objID == this->GetID() ) { + if ( objID == this->GetID() || objID == SMESH_2smeshpy::GenName()) + { this->Process( aCommand ); return aCommand; } - - // SMESH_subMesh method? - map< _pyID, Handle(_pySubMesh) >::iterator id_subMesh = mySubMeshes.find( objID ); - if ( id_subMesh != mySubMeshes.end() ) { - id_subMesh->second->Process( aCommand ); - return aCommand; - } // SMESH_Mesh method? map< _pyID, Handle(_pyMesh) >::iterator id_mesh = myMeshes.find( objID ); - if ( id_mesh != myMeshes.end() ) { + if ( id_mesh != myMeshes.end() ) + { + //id_mesh->second->AddProcessedCmd( aCommand ); + // check for mesh editor object if ( aCommand->GetMethod() == "GetMeshEditor" ) { // MeshEditor creation _pyID editorID = aCommand->GetResultValue(); Handle(_pyMeshEditor) editor = new _pyMeshEditor( aCommand ); myMeshEditors.insert( make_pair( editorID, editor )); return aCommand; - } + } // check for SubMesh objects else if ( aCommand->GetMethod() == "GetSubMesh" ) { // SubMesh creation _pyID subMeshID = aCommand->GetResultValue(); Handle(_pySubMesh) subMesh = new _pySubMesh( aCommand ); - mySubMeshes.insert( make_pair( subMeshID, subMesh )); + myObjects.insert( make_pair( subMeshID, subMesh )); } - id_mesh->second->Process( aCommand ); - return aCommand; - } - //SMESH_FilterManager method? - if ( theCommand.Search( "aFilterManager" ) != -1 ) { - if ( theCommand.Search( "CreateFilterManager" ) != -1 ) - myFilterManager = new _pyFilterManager( aCommand ); - else if ( !myFilterManager.IsNull() ) - myFilterManager->Process( aCommand ); + id_mesh->second->Process( aCommand ); + id_mesh->second->AddProcessedCmd( aCommand ); return aCommand; } // SMESH_MeshEditor method? map< _pyID, Handle(_pyMeshEditor) >::iterator id_editor = myMeshEditors.find( objID ); - if ( id_editor != myMeshEditors.end() ) { + if ( id_editor != myMeshEditors.end() ) + { + const TCollection_AsciiString& method = aCommand->GetMethod(); + + // some commands of SMESH_MeshEditor create meshes and groups + _pyID meshID, groups; + if ( method.Search("MakeMesh") != -1 ) + meshID = aCommand->GetResultValue(); + else if ( method == "MakeBoundaryMesh") + meshID = aCommand->GetResultValue(1); + else if ( method == "MakeBoundaryElements") + meshID = aCommand->GetResultValue(2); + + if ( method.Search("MakeGroups") != -1 || + method == "ExtrusionAlongPathX" || + method == "ExtrusionAlongPathObjX" || + method == "DoubleNodeGroupNew" || + method == "DoubleNodeGroupsNew" || + method == "DoubleNodeElemGroupNew" || + method == "DoubleNodeElemGroupsNew"|| + method == "DoubleNodeElemGroup2New"|| + method == "DoubleNodeElemGroups2New" + ) + groups = aCommand->GetResultValue(); + else if ( method == "MakeBoundaryMesh" ) + groups = aCommand->GetResultValue(2); + else if ( method == "MakeBoundaryElements") + groups = aCommand->GetResultValue(3); + id_editor->second->Process( aCommand ); - TCollection_AsciiString processedCommand = aCommand->GetString(); - // some commands of SMESH_MeshEditor create meshes - if ( aCommand->GetMethod().Search("MakeMesh") != -1 ) { - Handle(_pyMesh) mesh = new _pyMesh( aCommand, aCommand->GetResultValue() ); + id_editor->second->AddProcessedCmd( aCommand ); + + if ( !meshID.IsEmpty() && + !myMeshes.count( meshID ) && + aCommand->IsStudyEntry( meshID )) + { + TCollection_AsciiString processedCommand = aCommand->GetString(); + Handle(_pyMesh) mesh = new _pyMesh( aCommand, meshID ); + myMeshes.insert( make_pair( meshID, mesh )); + aCommand->Clear(); aCommand->GetString() = processedCommand; // discard changes made by _pyMesh - myMeshes.insert( make_pair( mesh->GetID(), mesh )); + } + if ( !groups.IsEmpty() ) + { + if ( !aCommand->IsStudyEntry( meshID )) + meshID = id_editor->second->GetMesh(); + Handle(_pyMesh) mesh = myMeshes[ meshID ]; + + list< _pyID > idList = aCommand->GetStudyEntries( groups ); + list< _pyID >::iterator grID = idList.begin(); + for ( ; grID != idList.end(); ++grID ) + if ( !myObjects.count( *grID )) + { + Handle(_pyGroup) group = new _pyGroup( aCommand, *grID ); + AddObject( group ); + if ( !mesh.IsNull() ) mesh->AddGroup( group ); + } } return aCommand; - } + } // SMESH_MeshEditor methods + // SMESH_Hypothesis method? list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); for ( ; hyp != myHypos.end(); ++hyp ) if ( !(*hyp)->IsAlgo() && objID == (*hyp)->GetID() ) { (*hyp)->Process( aCommand ); + (*hyp)->AddProcessedCmd( aCommand ); return aCommand; } + // aFilterManager.CreateFilter() ? + if ( aCommand->GetMethod() == "CreateFilter" ) + { + // Set a more human readable name to a filter + // aFilter0x7fbf6c71cfb0 -> aFilter_nb + _pyID newID, filterID = aCommand->GetResultValue(); + int pos = filterID.Search( "0x" ); + if ( pos > 1 ) + newID = (filterID.SubString(1,pos-1) + "_") + _pyID( ++myNbFilters ); + + Handle(_pyObject) filter( new _pyFilter( aCommand, newID )); + AddObject( filter ); + } + + // other object method? + map< _pyID, Handle(_pyObject) >::iterator id_obj = myObjects.find( objID ); + if ( id_obj != myObjects.end() ) { + id_obj->second->Process( aCommand ); + id_obj->second->AddProcessedCmd( aCommand ); + return aCommand; + } + // Add access to a wrapped mesh AddMeshAccessorMethod( aCommand ); @@ -312,7 +606,7 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand // PAL12227. PythonDump was not updated at proper time; result is // aCriteria.append(SMESH.Filter.Criterion(17,26,0,'L1',26,25,1e-07,SMESH.EDGE,-1)) // TypeError: __init__() takes exactly 11 arguments (10 given) - char wrongCommand[] = "SMESH.Filter.Criterion("; + const char wrongCommand[] = "SMESH.Filter.Criterion("; if ( int beg = theCommand.Location( wrongCommand, 1, theCommand.Length() )) { _pyCommand tmpCmd( theCommand.SubString( beg, theCommand.Length() ), -1); @@ -326,13 +620,86 @@ Handle(_pyCommand) _pyGen::AddCommand( const TCollection_AsciiString& theCommand aCommand->GetString().Trunc( beg - 1 ); aCommand->GetString() += tmpCmd.GetString(); } + // IMP issue 0021014 + // set GetCriterion(elementType,CritType,Compare,Treshold,UnaryOp,BinaryOp,Tolerance) + // 1 2 3 4 5 6 7 + // instead of "SMESH.Filter.Criterion( + // Type,Compare,Threshold,ThresholdStr,ThresholdID,UnaryOp,BinaryOp,Tolerance,TypeOfElement,Precision) + // 1 2 3 4 5 6 7 8 9 10 + // in order to avoid the problem of type mismatch of long and FunctorType + const TCollection_AsciiString + SMESH("SMESH."), dfltFunctor = "SMESH.FT_Undefined", dftlTol = "1e-07", dftlPreci = "-1"; + TCollection_AsciiString + Type = aCommand->GetArg(1), // long + Compare = aCommand->GetArg(2), // long + Threshold = aCommand->GetArg(3), // double + ThresholdStr = aCommand->GetArg(4), // string + ThresholdID = aCommand->GetArg(5), // string + UnaryOp = aCommand->GetArg(6), // long + BinaryOp = aCommand->GetArg(7), // long + Tolerance = aCommand->GetArg(8), // double + TypeOfElement = aCommand->GetArg(9), // ElementType + Precision = aCommand->GetArg(10); // long + fixFunctorType( Type, Compare, UnaryOp, BinaryOp ); + Type = SMESH + SMESH::FunctorTypeToString( SMESH::FunctorType( Type.IntegerValue() )); + Compare = SMESH + SMESH::FunctorTypeToString( SMESH::FunctorType( Compare.IntegerValue() )); + UnaryOp = SMESH + SMESH::FunctorTypeToString( SMESH::FunctorType( UnaryOp.IntegerValue() )); + BinaryOp = SMESH + SMESH::FunctorTypeToString( SMESH::FunctorType( BinaryOp.IntegerValue() )); + + aCommand->RemoveArgs(); + aCommand->SetObject( SMESH_2smeshpy::GenName() ); + aCommand->SetMethod( "GetCriterion" ); + + aCommand->SetArg( 1, TypeOfElement ); + aCommand->SetArg( 2, Type ); + aCommand->SetArg( 3, Compare ); + + if ( Type == "SMESH.FT_ElemGeomType" && Threshold.IsIntegerValue() ) + { + // set SMESH.GeometryType instead of a numerical Threshold + const char* types[SMESH::Geom_BALL+1] = { + "Geom_POINT", "Geom_EDGE", "Geom_TRIANGLE", "Geom_QUADRANGLE", "Geom_POLYGON", + "Geom_TETRA", "Geom_PYRAMID", "Geom_HEXA", "Geom_PENTA", "Geom_HEXAGONAL_PRISM", + "Geom_POLYHEDRA", "Geom_BALL" + }; + int iGeom = Threshold.IntegerValue(); + if ( -1 < iGeom && iGeom < SMESH::Geom_POLYHEDRA+1 ) + Threshold = SMESH + types[ iGeom ]; + } + if ( ThresholdID.Length() != 2 && ThresholdStr.Length() != 2) // not '' or "" + aCommand->SetArg( 4, ThresholdID.SubString( 2, ThresholdID.Length()-1 )); // shape entry + else if ( ThresholdStr.Length() != 2 ) + aCommand->SetArg( 4, ThresholdStr ); + else if ( ThresholdID.Length() != 2 ) + aCommand->SetArg( 4, ThresholdID ); + else + aCommand->SetArg( 4, Threshold ); + // find the last not default arg + int lastDefault = 8; + if ( Tolerance == dftlTol ) { + lastDefault = 7; + if ( BinaryOp == dfltFunctor ) { + lastDefault = 6; + if ( UnaryOp == dfltFunctor ) + lastDefault = 5; + } + } + if ( 5 < lastDefault ) aCommand->SetArg( 5, UnaryOp ); + if ( 6 < lastDefault ) aCommand->SetArg( 6, BinaryOp ); + if ( 7 < lastDefault ) aCommand->SetArg( 7, Tolerance ); + if ( Precision != dftlPreci ) + { + TCollection_AsciiString crit = aCommand->GetResultValue(); + aCommand->GetString() += "; "; + aCommand->GetString() += crit + ".Precision = " + Precision; + } } return aCommand; } //================================================================================ /*! - * \brief Convert the command or remember it for later conversion + * \brief Convert the command or remember it for later conversion * \param theCommand - The python command calling a method of SMESH_Gen */ //================================================================================ @@ -344,6 +711,7 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand ) // Concatenate( [mesh1, ...], ... ) // CreateHypothesis( theHypType, theLibName ) // Compute( mesh, geom ) + // Evaluate( mesh, geom ) // mesh creation TCollection_AsciiString method = theCommand->GetMethod(); @@ -353,18 +721,23 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand ) myMeshes.insert( make_pair( mesh->GetID(), mesh )); return; } - if ( method == "CreateMeshesFromUNV" || method == "CreateMeshesFromSTL") + if ( method == "CreateMeshesFromUNV" || + method == "CreateMeshesFromSTL" || + method == "CreateMeshesFromCGNS" || + method == "CopyMesh" ) { Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue() ); myMeshes.insert( make_pair( mesh->GetID(), mesh )); return; } - if( method == "CreateMeshesFromMED") + if( method == "CreateMeshesFromMED" || method == "CreateMeshesFromSAUV") { for(int ind = 0;indGetNbResultValues();ind++) { - Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue(ind)); - myMeshes.insert( make_pair( theCommand->GetResultValue(ind), mesh )); + _pyID meshID = theCommand->GetResultValue(ind+1); + if ( !theCommand->IsStudyEntry( meshID ) ) continue; + Handle(_pyMesh) mesh = new _pyMesh( theCommand, theCommand->GetResultValue(ind+1)); + myMeshes.insert( make_pair( mesh->GetID(), mesh )); } } @@ -392,22 +765,38 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand ) if ( id_mesh != myMeshes.end() ) { theCommand->SetObject( meshID ); theCommand->RemoveArgs(); - id_mesh->second->Flush(); + id_mesh->second->Process( theCommand ); + id_mesh->second->AddProcessedCmd( theCommand ); return; } } - // leave only one smeshgen.GetPattern() in the script - if ( method == "GetPattern" ) { - if ( myHasPattern ) { - theCommand->Clear(); + // smeshgen.Evaluate( mesh, geom ) --> mesh.Evaluate(geom) + if ( method == "Evaluate" ) + { + const _pyID& meshID = theCommand->GetArg( 1 ); + map< _pyID, Handle(_pyMesh) >::iterator id_mesh = myMeshes.find( meshID ); + if ( id_mesh != myMeshes.end() ) { + theCommand->SetObject( meshID ); + _pyID geom = theCommand->GetArg( 2 ); + theCommand->RemoveArgs(); + theCommand->SetArg( 1, geom ); + id_mesh->second->AddProcessedCmd( theCommand ); return; } - myHasPattern = true; } + // objects erasing creation command if no more it's commands invoked: + // SMESH_Pattern, FilterManager + if ( method == "GetPattern" || + method == "CreateFilterManager" || + method == "CreateMeasurements" ) { + Handle(_pyObject) obj = new _pySelfEraser( theCommand ); + if ( !myObjects.insert( make_pair( obj->GetID(), obj )).second ) + theCommand->Clear(); // already created + } // Concatenate( [mesh1, ...], ... ) - if ( method == "Concatenate" || method == "ConcatenateWithGroups") + else if ( method == "Concatenate" || method == "ConcatenateWithGroups") { if ( method == "ConcatenateWithGroups" ) { theCommand->SetMethod( "Concatenate" ); @@ -417,6 +806,14 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand ) myMeshes.insert( make_pair( mesh->GetID(), mesh )); AddMeshAccessorMethod( theCommand ); } + else if ( method == "SetName" ) // SetName(obj,name) + { + // store theCommand as one of object commands to erase it along with the object + const _pyID& objID = theCommand->GetArg( 1 ); + Handle(_pyObject) obj = FindObject( objID ); + if ( !obj.IsNull() ) + obj->AddProcessedCmd( theCommand ); + } // Replace name of SMESH_Gen @@ -445,19 +842,45 @@ void _pyGen::Process( const Handle(_pyCommand)& theCommand ) void _pyGen::Flush() { - // create empty command + // create an empty command myLastCommand = new _pyCommand(); - if ( !myFilterManager.IsNull() ) - myFilterManager->Flush(); + map< _pyID, Handle(_pyMesh) >::iterator id_mesh; + map< _pyID, Handle(_pyObject) >::iterator id_obj; + list< Handle(_pyHypothesis) >::iterator hyp; - map< _pyID, Handle(_pyMesh) >::iterator id_mesh = myMeshes.begin(); - for ( ; id_mesh != myMeshes.end(); ++id_mesh ) + if ( IsToKeepAllCommands() ) // historical dump + { + // set myIsPublished = true to all objects + for ( id_mesh = myMeshes.begin(); id_mesh != myMeshes.end(); ++id_mesh ) + id_mesh->second->SetRemovedFromStudy( false ); + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) + (*hyp)->SetRemovedFromStudy( false ); + for ( id_obj = myObjects.begin(); id_obj != myObjects.end(); ++id_obj ) + id_obj->second->SetRemovedFromStudy( false ); + } + else + { + // let hypotheses find referred objects in order to prevent clearing + // not published referred hyps (it's needed for hyps like "LayerDistribution") + list< Handle(_pyMesh) > fatherMeshes; + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) + if ( !hyp->IsNull() ) + (*hyp)->GetReferredMeshesAndGeom( fatherMeshes ); + } + // set myIsPublished = false to all objects depending on + // meshes built on a removed geometry + for ( id_mesh = myMeshes.begin(); id_mesh != myMeshes.end(); ++id_mesh ) + if ( id_mesh->second->IsNotGeomPublished() ) + id_mesh->second->SetRemovedFromStudy( true ); + + // Flush meshes + for ( id_mesh = myMeshes.begin(); id_mesh != myMeshes.end(); ++id_mesh ) if ( ! id_mesh->second.IsNull() ) id_mesh->second->Flush(); - list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); - for ( ; hyp != myHypos.end(); ++hyp ) + // Flush hyps + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) if ( !hyp->IsNull() ) { (*hyp)->Flush(); // smeshgen.CreateHypothesis() --> smesh.smesh.CreateHypothesis() @@ -465,15 +888,97 @@ void _pyGen::Flush() (*hyp)->GetCreationCmd()->SetObject( SMESH_2smeshpy::GenName() ); } - map< _pyID, Handle(_pySubMesh) >::iterator id_subMesh = mySubMeshes.begin(); - for ( ; id_subMesh != mySubMeshes.end(); ++id_subMesh ) - if ( ! id_subMesh->second.IsNull() ) - id_subMesh->second->Flush(); + // Flush other objects + for ( id_obj = myObjects.begin(); id_obj != myObjects.end(); ++id_obj ) + if ( ! id_obj->second.IsNull() ) + id_obj->second->Flush(); myLastCommand->SetOrderNb( ++myNbCommands ); myCommands.push_back( myLastCommand ); } +//================================================================================ +/*! + * \brief Prevent moving a command creating a sub-mesh to the end of the script + * if the sub-mesh is used in theCmdUsingSubmesh as argument + */ +//================================================================================ + +void _pyGen::PlaceSubmeshAfterItsCreation( Handle(_pyCommand) theCmdUsingSubmesh ) const +{ + map< _pyID, Handle(_pyObject) >::const_iterator id_obj = myObjects.begin(); + for ( ; id_obj != myObjects.end(); ++id_obj ) + { + if ( !id_obj->second->IsKind( STANDARD_TYPE( _pySubMesh ))) continue; + for ( int iArg = theCmdUsingSubmesh->GetNbArgs(); iArg; --iArg ) + { + const _pyID& arg = theCmdUsingSubmesh->GetArg( iArg ); + if ( arg.IsEmpty() || arg.Value( 1 ) == '"' || arg.Value( 1 ) == '\'' ) + continue; + list< _pyID > idList = theCmdUsingSubmesh->GetStudyEntries( arg ); + list< _pyID >::iterator id = idList.begin(); + for ( ; id != idList.end(); ++id ) + if ( id_obj->first == *id ) + // _pySubMesh::Process() does what we need + Handle(_pySubMesh)::DownCast( id_obj->second )->Process( theCmdUsingSubmesh ); + } + } +} + +//================================================================================ +/*! + * \brief Clean commmands of removed objects depending on myIsPublished flag + */ +//================================================================================ + +void _pyGen::ClearCommands() +{ + map< _pyID, Handle(_pyMesh) >::iterator id_mesh = myMeshes.begin(); + for ( ; id_mesh != myMeshes.end(); ++id_mesh ) + id_mesh->second->ClearCommands(); + + list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); + for ( ; hyp != myHypos.end(); ++hyp ) + if ( !hyp->IsNull() ) + (*hyp)->ClearCommands(); + + map< _pyID, Handle(_pyObject) >::iterator id_obj = myObjects.begin(); + for ( ; id_obj != myObjects.end(); ++id_obj ) + id_obj->second->ClearCommands(); +} + +//================================================================================ +/*! + * \brief Release mutual handles of objects + */ +//================================================================================ + +void _pyGen::Free() +{ + map< _pyID, Handle(_pyMesh) >::iterator id_mesh = myMeshes.begin(); + for ( ; id_mesh != myMeshes.end(); ++id_mesh ) + id_mesh->second->Free(); + myMeshes.clear(); + + map< _pyID, Handle(_pyMeshEditor) >::iterator id_ed = myMeshEditors.begin(); + for ( ; id_ed != myMeshEditors.end(); ++id_ed ) + id_ed->second->Free(); + myMeshEditors.clear(); + + map< _pyID, Handle(_pyObject) >::iterator id_obj = myObjects.begin(); + for ( ; id_obj != myObjects.end(); ++id_obj ) + id_obj->second->Free(); + myObjects.clear(); + + list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); + for ( ; hyp != myHypos.end(); ++hyp ) + if ( !hyp->IsNull() ) + (*hyp)->Free(); + myHypos.clear(); + + myFile2ExportedMesh.clear(); +} + //================================================================================ /*! * \brief Add access method to mesh that is an argument @@ -548,7 +1053,7 @@ Handle(_pyHypothesis) _pyGen::FindAlgo( const _pyID& theGeom, const _pyID& theMe if ( !hyp->IsNull() && (*hyp)->IsAlgo() && theHypothesis->CanBeCreatedBy( (*hyp)->GetAlgoType() ) && - (*hyp)->GetGeom() == theGeom && + (*hyp)->GetGeom() == theGeom && (*hyp)->GetMesh() == theMesh ) return *hyp; return 0; @@ -564,12 +1069,9 @@ Handle(_pyHypothesis) _pyGen::FindAlgo( const _pyID& theGeom, const _pyID& theMe Handle(_pySubMesh) _pyGen::FindSubMesh( const _pyID& theSubMeshID ) { - map< _pyID, Handle(_pySubMesh) >::iterator id_subMesh = mySubMeshes.begin(); - for ( ; id_subMesh != mySubMeshes.end(); ++id_subMesh ) { - Handle(_pySubMesh) sm = id_subMesh->second; - if ( !id_subMesh->second.IsNull() && theSubMeshID == id_subMesh->second->GetID() ) - return sm; - } + map< _pyID, Handle(_pyObject) >::iterator id_subMesh = myObjects.find(theSubMeshID); + if ( id_subMesh != myObjects.end() ) + return Handle(_pySubMesh)::DownCast( id_subMesh->second ); return Handle(_pySubMesh)(); } @@ -634,8 +1136,8 @@ void _pyGen::SetCommandBefore( Handle(_pyCommand) theCmd, Handle(_pyCommand) the //================================================================================ void _pyGen::setNeighbourCommand( Handle(_pyCommand)& theCmd, - Handle(_pyCommand)& theOtherCmd, - const bool theIsAfter ) + Handle(_pyCommand)& theOtherCmd, + const bool theIsAfter ) { list< Handle(_pyCommand) >::iterator pos; pos = find( myCommands.begin(), myCommands.end(), theCmd ); @@ -688,114 +1190,216 @@ _pyID _pyGen::GenerateNewID( const _pyID& theID ) aNewID = theID + _pyID( ":" ) + _pyID( index++ ); } while ( myObjectNames.IsBound( aNewID ) ); - - myObjectNames.Bind( aNewID, myObjectNames.IsBound( theID ) - ? (myObjectNames.Find( theID ) + _pyID( "_" ) + _pyID( index-1 )) - : _pyID( "A" ) + aNewID ); + + myObjectNames.Bind( aNewID, myObjectNames.IsBound( theID ) + ? (myObjectNames.Find( theID ) + _pyID( "_" ) + _pyID( index-1 )) + : _pyID( "A" ) + aNewID ); return aNewID; } //================================================================================ /*! - * \brief Find out type of geom group - * \param grpID - The geom group entry - * \retval int - The type - */ -//================================================================================ - -static bool sameGroupType( const _pyID& grpID, - const TCollection_AsciiString& theType) -{ - // define group type as smesh.Mesh.Group() does - int type = -1; - SALOMEDS::Study_var study = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); - SALOMEDS::SObject_var aSObj = study->FindObjectID( grpID.ToCString() ); - if ( !aSObj->_is_nil() ) { - GEOM::GEOM_Object_var aGeomObj = GEOM::GEOM_Object::_narrow( aSObj->GetObject() ); - if ( !aGeomObj->_is_nil() ) { - switch ( aGeomObj->GetShapeType() ) { - case GEOM::VERTEX: type = SMESH::NODE; break; - case GEOM::EDGE: type = SMESH::EDGE; break; - case GEOM::FACE: type = SMESH::FACE; break; - case GEOM::SOLID: - case GEOM::SHELL: type = SMESH::VOLUME; break; - case GEOM::COMPOUND: { - GEOM::GEOM_Gen_ptr aGeomGen = SMESH_Gen_i::GetSMESHGen()->GetGeomEngine(); - if ( !aGeomGen->_is_nil() ) { - GEOM::GEOM_IGroupOperations_var aGrpOp = - aGeomGen->GetIGroupOperations( study->StudyId() ); - if ( !aGrpOp->_is_nil() ) { - switch ( aGrpOp->GetType( aGeomObj )) { - case TopAbs_VERTEX: type = SMESH::NODE; break; - case TopAbs_EDGE: type = SMESH::EDGE; break; - case TopAbs_FACE: type = SMESH::FACE; break; - case TopAbs_SOLID: type = SMESH::VOLUME; break; - default:; - } - } - } - } - default:; - } - } + * \brief Stores theObj in myObjects + */ +//================================================================================ + +void _pyGen::AddObject( Handle(_pyObject)& theObj ) +{ + if ( theObj.IsNull() ) return; + + if ( theObj->IsKind( STANDARD_TYPE( _pyMesh ))) + myMeshes.insert( make_pair( theObj->GetID(), Handle(_pyMesh)::DownCast( theObj ))); + + else if ( theObj->IsKind( STANDARD_TYPE( _pyMeshEditor ))) + myMeshEditors.insert( make_pair( theObj->GetID(), Handle(_pyMeshEditor)::DownCast( theObj ))); + + else + myObjects.insert( make_pair( theObj->GetID(), theObj )); +} + +//================================================================================ +/*! + * \brief Re-register an object with other ID to make it Process() commands of + * other object having this ID + */ +//================================================================================ + +void _pyGen::SetProxyObject( const _pyID& theID, Handle(_pyObject)& theObj ) +{ + if ( theObj.IsNull() ) return; + + if ( theObj->IsKind( STANDARD_TYPE( _pyMesh ))) + myMeshes.insert( make_pair( theID, Handle(_pyMesh)::DownCast( theObj ))); + + else if ( theObj->IsKind( STANDARD_TYPE( _pyMeshEditor ))) + myMeshEditors.insert( make_pair( theID, Handle(_pyMeshEditor)::DownCast( theObj ))); + + else + myObjects.insert( make_pair( theID, theObj )); +} + +//================================================================================ +/*! + * \brief Finds a _pyObject by ID + */ +//================================================================================ + +Handle(_pyObject) _pyGen::FindObject( const _pyID& theObjID ) const +{ + { + map< _pyID, Handle(_pyObject) >::const_iterator id_obj = myObjects.find( theObjID ); + if ( id_obj != myObjects.end() ) + return id_obj->second; } - if ( type < 0 ) { - MESSAGE("Type of the group " << grpID << " not found"); - return false; + { + map< _pyID, Handle(_pyMesh) >::const_iterator id_obj = myMeshes.find( theObjID ); + if ( id_obj != myMeshes.end() ) + return id_obj->second; } - if ( theType.IsIntegerValue() ) - return type == theType.IntegerValue(); + // { + // map< _pyID, Handle(_pyMeshEditor) >::const_iterator id_obj = myMeshEditors.find( theObjID ); + // if ( id_obj != myMeshEditors.end() ) + // return id_obj->second; + // } + return Handle(_pyObject)(); +} + +//================================================================================ +/*! + * \brief Check if a study entry is under GEOM component + */ +//================================================================================ - switch ( type ) { - case SMESH::NODE: return theType.Location( "NODE", 1, theType.Length() ); - case SMESH::EDGE: return theType.Location( "EDGE", 1, theType.Length() ); - case SMESH::FACE: return theType.Location( "FACE", 1, theType.Length() ); - case SMESH::VOLUME: return theType.Location( "VOLUME", 1, theType.Length() ); - default:; +bool _pyGen::IsGeomObject(const _pyID& theObjID) const +{ + if ( myGeomIDNb ) + { + return ( myGeomIDIndex <= theObjID.Length() && + int( theObjID.Value( myGeomIDIndex )) == myGeomIDNb && + _pyCommand::IsStudyEntry( theObjID )); } return false; } //================================================================================ /*! - * \brief - * \param theCreationCmd - + * \brief Returns true if an object is not present in a study + */ +//================================================================================ + +bool _pyGen::IsNotPublished(const _pyID& theObjID) const +{ + if ( theObjID.IsEmpty() ) return false; + + if ( myObjectNames.IsBound( theObjID )) + return false; // SMESH object is in study + + // either the SMESH object is not in study or it is a GEOM object + if ( IsGeomObject( theObjID )) + { + SALOMEDS::SObject_var so = myStudy->FindObjectID( theObjID.ToCString() ); + if ( so->_is_nil() ) return true; + CORBA::Object_var obj = so->GetObject(); + return CORBA::is_nil( obj ); + } + return true; // SMESH object not in study +} + +//================================================================================ +/*! + * \brief Return reader of hypotheses of plugins + */ +//================================================================================ + +Handle( _pyHypothesisReader ) _pyGen::GetHypothesisReader() const +{ + if (myHypReader.IsNull() ) + ((_pyGen*) this)->myHypReader = new _pyHypothesisReader; + + return myHypReader; +} + + +//================================================================================ +/*! + * \brief Mesh created by SMESH_Gen */ //================================================================================ _pyMesh::_pyMesh(const Handle(_pyCommand) theCreationCmd) - : _pyObject(theCreationCmd), myHasEditor(false) + : _pyObject( theCreationCmd ), myGeomNotInStudy( false ) { - // convert my creation command + if ( theCreationCmd->GetMethod() == "CreateMesh" && theGen->IsNotPublished( GetGeom() )) + myGeomNotInStudy = true; + + // convert my creation command --> smeshpy.Mesh(...) Handle(_pyCommand) creationCmd = GetCreationCmd(); - //TCollection_AsciiString str = creationCmd->GetMethod(); -// if(str != "CreateMeshesFromUNV" && -// str != "CreateMeshesFromMED" && -// str != "CreateMeshesFromSTL") - creationCmd->SetObject( SMESH_2smeshpy::SmeshpyName() ); + creationCmd->SetObject( SMESH_2smeshpy::SmeshpyName() ); creationCmd->SetMethod( "Mesh" ); - - theGen->SetAccessorMethod( GetID(), "GetMesh()" ); + theGen->SetAccessorMethod( GetID(), _pyMesh::AccessorMethod() ); } //================================================================================ /*! - * \brief - * \param theCreationCmd - + * \brief Mesh created by SMESH_MeshEditor */ //================================================================================ -_pyMesh::_pyMesh(const Handle(_pyCommand) theCreationCmd, const TCollection_AsciiString& id): - _pyObject(theCreationCmd), myHasEditor(false) + +_pyMesh::_pyMesh(const Handle(_pyCommand) theCreationCmd, const _pyID& meshId): + _pyObject(theCreationCmd,meshId), myGeomNotInStudy(false ) { + if ( theCreationCmd->MethodStartsFrom( "CreateMeshesFrom" )) + { + // this mesh depends on the exported mesh + const TCollection_AsciiString& file = theCreationCmd->GetArg( 1 ); + if ( !file.IsEmpty() ) + { + ExportedMeshData& exportData = theGen->FindExportedMesh( file ); + addFatherMesh( exportData.myMesh ); + if ( !exportData.myLastComputeCmd.IsNull() ) + { + // restore cleared Compute() by which the exported mesh was generated + exportData.myLastComputeCmd->GetString() = exportData.myLastComputeCmdString; + // protect that Compute() cmd from clearing + if ( exportData.myMesh->myLastComputeCmd == exportData.myLastComputeCmd ) + exportData.myMesh->myLastComputeCmd.Nullify(); + } + } + } + else if ( theCreationCmd->MethodStartsFrom( "Concatenate" )) + { + // this mesh depends on concatenated meshes + const TCollection_AsciiString& meshIDs = theCreationCmd->GetArg( 1 ); + list< _pyID > idList = theCreationCmd->GetStudyEntries( meshIDs ); + list< _pyID >::iterator meshID = idList.begin(); + for ( ; meshID != idList.end(); ++meshID ) + addFatherMesh( *meshID ); + } + else if ( theCreationCmd->GetMethod() == "CopyMesh" ) + { + // this mesh depends on a copied IdSource + const _pyID& objID = theCreationCmd->GetArg( 1 ); + addFatherMesh( objID ); + } + else if ( theCreationCmd->GetMethod().Search("MakeMesh") != -1 || + theCreationCmd->GetMethod() == "MakeBoundaryMesh" || + theCreationCmd->GetMethod() == "MakeBoundaryElements" ) + { + // this mesh depends on a source mesh + // (theCreationCmd is already Process()ed by _pyMeshEditor) + const _pyID& meshID = theCreationCmd->GetObject(); + addFatherMesh( meshID ); + } + // convert my creation command Handle(_pyCommand) creationCmd = GetCreationCmd(); - creationCmd->SetObject( SMESH_2smeshpy::SmeshpyName() ); - theGen->SetAccessorMethod( id, "GetMesh()" ); + creationCmd->SetObject( SMESH_2smeshpy::SmeshpyName() ); + theGen->SetAccessorMethod( meshId, _pyMesh::AccessorMethod() ); } //================================================================================ /*! - * \brief Convert a IDL API command of SMESH::Mesh to a method call of python Mesh + * \brief Convert an IDL API command of SMESH::SMESH_Mesh to a method call of python Mesh * \param theCommand - Engine method called for this mesh */ //================================================================================ @@ -815,15 +1419,69 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand ) // --> in Mesh.ExportMED( f, auto_groups, version ) // 5. etc - const TCollection_AsciiString method = theCommand->GetMethod(); - // ---------------------------------------------------------------------- - if ( method == "GetSubMesh" ) { - Handle(_pySubMesh) subMesh = theGen->FindSubMesh( theCommand->GetResultValue() ); - if ( !subMesh.IsNull() ) { - subMesh->SetCreator( this ); - mySubmeshes.push_back( subMesh ); - } - } + const TCollection_AsciiString& method = theCommand->GetMethod(); + // ---------------------------------------------------------------------- + if ( method == "Compute" ) // in snapshot mode, clear the previous Compute() + { + if ( !theGen->IsToKeepAllCommands() ) // !historical + { + list< Handle(_pyHypothesis) >::iterator hyp; + if ( !myLastComputeCmd.IsNull() ) + { + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) + (*hyp)->ComputeDiscarded( myLastComputeCmd ); + + myLastComputeCmd->Clear(); + } + myLastComputeCmd = theCommand; + + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) + (*hyp)->MeshComputed( myLastComputeCmd ); + } + Flush(); + } + // ---------------------------------------------------------------------- + else if ( method == "Clear" ) // in snapshot mode, clear all previous commands + { + if ( !theGen->IsToKeepAllCommands() ) // !historical + { + int untilCmdNb = + myChildMeshes.empty() ? 0 : myChildMeshes.back()->GetCreationCmd()->GetOrderNb(); + // list< Handle(_pyCommand) >::reverse_iterator cmd = myProcessedCmds.rbegin(); + // for ( ; cmd != myProcessedCmds.rend() && (*cmd)->GetOrderNb() > untilCmdNb; ++cmd ) + // (*cmd)->Clear(); + if ( !myLastComputeCmd.IsNull() ) + { + list< Handle(_pyHypothesis) >::iterator hyp; + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) + (*hyp)->ComputeDiscarded( myLastComputeCmd ); + + myLastComputeCmd->Clear(); + } + + list< Handle(_pyMeshEditor)>::iterator e = myEditors.begin(); + for ( ; e != myEditors.end(); ++e ) + { + list< Handle(_pyCommand)>& cmds = (*e)->GetProcessedCmds(); + list< Handle(_pyCommand) >::reverse_iterator cmd = cmds.rbegin(); + for ( ; cmd != cmds.rend() && (*cmd)->GetOrderNb() > untilCmdNb; ++cmd ) + if ( !(*cmd)->IsEmpty() ) + { + if ( (*cmd)->GetStudyEntries( (*cmd)->GetResultValue() ).empty() ) // no object created + (*cmd)->Clear(); + } + } + myLastComputeCmd = theCommand; // to clear Clear() the same way as Compute() + } + } + // ---------------------------------------------------------------------- + else if ( method == "GetSubMesh" ) { // collect submeshes of the mesh + Handle(_pySubMesh) subMesh = theGen->FindSubMesh( theCommand->GetResultValue() ); + if ( !subMesh.IsNull() ) { + subMesh->SetCreator( this ); + mySubmeshes.push_back( subMesh ); + } + } // ---------------------------------------------------------------------- else if ( method == "AddHypothesis" ) { // mesh.AddHypothesis(geom, HYPO ) myAddHypCmds.push_back( theCommand ); @@ -837,30 +1495,46 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand ) } } // ---------------------------------------------------------------------- - else if ( method == "CreateGroupFromGEOM" ) {// (type, name, grp) - _pyID grp = theCommand->GetArg( 3 ); - if ( sameGroupType( grp, theCommand->GetArg( 1 )) ) { // --> Group(grp) - theCommand->SetMethod( "Group" ); - theCommand->RemoveArgs(); - theCommand->SetArg( 1, grp ); - } - else { - _pyID type = theCommand->GetArg( 1 ); - _pyID name = theCommand->GetArg( 2 ); - theCommand->SetMethod( "GroupOnGeom" ); - theCommand->RemoveArgs(); - theCommand->SetArg( 1, grp ); - theCommand->SetArg( 2, name ); - theCommand->SetArg( 3, type ); - } - } - // ---------------------------------------------------------------------- - else if ( method == "ExportToMED" ) { // ExportToMED() --> ExportMED() - theCommand->SetMethod( "ExportMED" ); + else if ( method == "CreateGroup" || + method == "CreateGroupFromGEOM" || + method == "CreateGroupFromFilter" ) + { + Handle(_pyGroup) group = new _pyGroup( theCommand ); + myGroups.push_back( group ); + theGen->AddObject( group ); } // ---------------------------------------------------------------------- - else if ( method == "CreateGroup" ) { // CreateGroup() --> CreateEmptyGroup() - theCommand->SetMethod( "CreateEmptyGroup" ); + else if ( theCommand->MethodStartsFrom( "Export" )) + { + if ( method == "ExportToMED" || // ExportToMED() --> ExportMED() + method == "ExportToMEDX" ) { // ExportToMEDX() --> ExportMED() + theCommand->SetMethod( "ExportMED" ); + } + else if ( method == "ExportCGNS" ) + { // ExportCGNS(part, ...) -> ExportCGNS(..., part) + _pyID partID = theCommand->GetArg( 1 ); + int nbArgs = theCommand->GetNbArgs(); + for ( int i = 2; i <= nbArgs; ++i ) + theCommand->SetArg( i-1, theCommand->GetArg( i )); + theCommand->SetArg( nbArgs, partID ); + } + else if ( theCommand->MethodStartsFrom( "ExportPartTo" )) + { // ExportPartTo*(part, ...) -> Export*(..., part) + // + // remove "PartTo" from the method + TCollection_AsciiString newMethod = method; + newMethod.Remove( 7, 6 ); + theCommand->SetMethod( newMethod ); + // make the 1st arg be the last one + _pyID partID = theCommand->GetArg( 1 ); + int nbArgs = theCommand->GetNbArgs(); + for ( int i = 2; i <= nbArgs; ++i ) + theCommand->SetArg( i-1, theCommand->GetArg( i )); + theCommand->SetArg( nbArgs, partID ); + } + // remember file name + theGen->AddExportedMesh( theCommand->GetArg( 1 ), + ExportedMeshData( this, myLastComputeCmd )); } // ---------------------------------------------------------------------- else if ( method == "RemoveHypothesis" ) // (geom, hyp) @@ -895,6 +1569,40 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand ) // remove hyp from myHypos myHypos.remove( hyp ); } + // check for SubMesh order commands + else if ( method == "GetMeshOrder" || method == "SetMeshOrder" ) + { + // make commands GetSubMesh() returning sub-meshes be before using sub-meshes + // by GetMeshOrder() and SetMeshOrder(), since by defalut GetSubMesh() + // commands are moved at the end of the script + TCollection_AsciiString subIDs = + ( method == "SetMeshOrder" ) ? theCommand->GetArg(1) : theCommand->GetResultValue(); + list< _pyID > idList = theCommand->GetStudyEntries( subIDs ); + list< _pyID >::iterator subID = idList.begin(); + for ( ; subID != idList.end(); ++subID ) + { + Handle(_pySubMesh) subMesh = theGen->FindSubMesh( *subID ); + if ( !subMesh.IsNull() ) + subMesh->Process( theCommand ); // it moves GetSubMesh() before theCommand + } + } + // update list of groups + else if ( method == "GetGroups" ) + { + TCollection_AsciiString grIDs = theCommand->GetResultValue(); + list< _pyID > idList = theCommand->GetStudyEntries( grIDs ); + list< _pyID >::iterator grID = idList.begin(); + for ( ; grID != idList.end(); ++grID ) + { + Handle(_pyObject) obj = theGen->FindObject( *grID ); + if ( obj.IsNull() ) + { + Handle(_pyGroup) group = new _pyGroup( theCommand, *grID ); + theGen->AddObject( group ); + myGroups.push_back( group ); + } + } + } // add accessor method if necessary else { @@ -912,12 +1620,12 @@ void _pyMesh::Process( const Handle(_pyCommand)& theCommand ) bool _pyMesh::NeedMeshAccess( const Handle(_pyCommand)& theCommand ) { - // names of SMESH_Mesh methods fully equal to methods of class Mesh, so - // no conversion is needed for them at all: + // names of SMESH_Mesh methods fully equal to methods of python class Mesh, + // so no conversion is needed for them at all: static TStringSet sameMethods; if ( sameMethods.empty() ) { const char * names[] = - { "ExportDAT","ExportUNV","ExportSTL", "RemoveGroup","RemoveGroupWithContents", + { "ExportDAT","ExportUNV","ExportSTL","ExportSAUV", "RemoveGroup","RemoveGroupWithContents", "GetGroups","UnionGroups","IntersectGroups","CutGroups","GetLog","GetId","ClearLog", "GetStudyId","HasDuplicatedGroupNamesMED","GetMEDMesh","NbNodes","NbElements", "NbEdges","NbEdgesOfOrder","NbFaces","NbFacesOfOrder","NbTriangles", @@ -929,7 +1637,7 @@ bool _pyMesh::NeedMeshAccess( const Handle(_pyCommand)& theCommand ) "GetNodeInverseElements","GetShapeID","GetShapeIDForElem","GetElemNbNodes", "GetElemNode","IsMediumNode","IsMediumNodeOfAnyElem","ElemNbEdges","ElemNbFaces", "IsPoly","IsQuadratic","BaryCenter","GetHypothesisList", "SetAutoColor", "GetAutoColor", - "Clear", "ConvertToStandalone" + "Clear", "ConvertToStandalone", "GetMeshOrder", "SetMeshOrder" ,"" }; // <- mark of end sameMethods.Insert( names ); } @@ -945,6 +1653,23 @@ bool _pyMesh::NeedMeshAccess( const Handle(_pyCommand)& theCommand ) void _pyMesh::Flush() { + { + // get the meshes this mesh depends on via hypotheses + list< Handle(_pyMesh) > fatherMeshes; + list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); + for ( ; hyp != myHypos.end(); ++hyp ) + if ( ! (*hyp)->GetReferredMeshesAndGeom( fatherMeshes )) + myGeomNotInStudy = true; + + list< Handle(_pyMesh) >::iterator m = fatherMeshes.begin(); + for ( ; m != fatherMeshes.end(); ++m ) + addFatherMesh( *m ); + // if ( removedGeom ) + // SetRemovedFromStudy(); // as reffered geometry not in study + } + if ( myGeomNotInStudy ) + return; + list < Handle(_pyCommand) >::iterator cmd; // try to convert algo addition like this: @@ -961,30 +1686,34 @@ void _pyMesh::Flush() // check and create new algorithm instance if it is already wrapped if ( algo->IsWrapped() ) { _pyID localAlgoID = theGen->GenerateNewID( algoID ); - TCollection_AsciiString aNewCmdStr = localAlgoID + - TCollection_AsciiString( " = " ) + theGen->GetID() + - TCollection_AsciiString( ".CreateHypothesis( \"" ) + algo->GetAlgoType() + - TCollection_AsciiString( "\" )" ); - + TCollection_AsciiString aNewCmdStr = addCmd->GetIndentation() + localAlgoID + + TCollection_AsciiString( " = " ) + theGen->GetID() + + TCollection_AsciiString( ".CreateHypothesis( \"" ) + algo->GetAlgoType() + + TCollection_AsciiString( "\" )" ); + Handle(_pyCommand) newCmd = theGen->AddCommand( aNewCmdStr ); Handle(_pyAlgorithm) newAlgo = Handle(_pyAlgorithm)::DownCast(theGen->FindHyp( localAlgoID )); if ( !newAlgo.IsNull() ) { - newAlgo->Assign( algo, this->GetID() ); - newAlgo->SetCreationCmd( newCmd ); - algo = newAlgo; - // set algorithm creation - theGen->SetCommandBefore( newCmd, addCmd ); + newAlgo->Assign( algo, this->GetID() ); + newAlgo->SetCreationCmd( newCmd ); + algo = newAlgo; + // set algorithm creation + theGen->SetCommandBefore( newCmd, addCmd ); + myHypos.push_back( newAlgo ); + if ( !myLastComputeCmd.IsNull() && + newCmd->GetOrderNb() == myLastComputeCmd->GetOrderNb() + 1) + newAlgo->MeshComputed( myLastComputeCmd ); } else - newCmd->Clear(); + newCmd->Clear(); } _pyID geom = addCmd->GetArg( 1 ); bool isLocalAlgo = ( geom != GetGeom() ); - + // try to convert if ( algo->Addition2Creation( addCmd, this->GetID() )) // OK { - // wrapped algo is created atfer mesh creation + // wrapped algo is created after mesh creation GetCreationCmd()->AddDependantCmd( addCmd ); if ( isLocalAlgo ) { @@ -992,14 +1721,14 @@ void _pyMesh::Flush() addCmd->SetArg( addCmd->GetNbArgs() + 1, TCollection_AsciiString( "geom=" ) + geom ); // sm = mesh.GetSubMesh(geom, name) --> sm = ALGO.GetSubMesh() - list < Handle(_pySubMesh) >::iterator smIt; + list < Handle(_pySubMesh) >::iterator smIt; for ( smIt = mySubmeshes.begin(); smIt != mySubmeshes.end(); ++smIt ) { - Handle(_pySubMesh) subMesh = *smIt; + Handle(_pySubMesh) subMesh = *smIt; Handle(_pyCommand) subCmd = subMesh->GetCreationCmd(); if ( geom == subCmd->GetArg( 1 )) { subCmd->SetObject( algo->GetID() ); subCmd->RemoveArgs(); - subMesh->SetCreator( algo ); + subMesh->SetCreator( algo ); } } } @@ -1034,21 +1763,126 @@ void _pyMesh::Flush() } } - // sm = mesh.GetSubMesh(geom, name) --> sm = mesh.GetMesh().GetSubMesh(geom, name) -// for ( cmd = mySubmeshes.begin(); cmd != mySubmeshes.end(); ++cmd ) { -// Handle(_pyCommand) subCmd = *cmd; -// if ( subCmd->GetNbArgs() > 0 ) -// AddMeshAccess( subCmd ); -// } myAddHypCmds.clear(); mySubmeshes.clear(); // flush hypotheses list< Handle(_pyHypothesis) >::iterator hyp = myHypos.begin(); - for ( ; hyp != myHypos.end(); ++hyp ) + for ( hyp = myHypos.begin(); hyp != myHypos.end(); ++hyp ) (*hyp)->Flush(); } +//================================================================================ +/*! + * \brief Sets myIsPublished of me and of all objects depending on me. + */ +//================================================================================ + +void _pyMesh::SetRemovedFromStudy(const bool isRemoved) +{ + _pyObject::SetRemovedFromStudy(isRemoved); + + list< Handle(_pySubMesh) >::iterator sm = mySubmeshes.begin(); + for ( ; sm != mySubmeshes.end(); ++sm ) + (*sm)->SetRemovedFromStudy(isRemoved); + + list< Handle(_pyGroup) >::iterator gr = myGroups.begin(); + for ( ; gr != myGroups.end(); ++gr ) + (*gr)->SetRemovedFromStudy(isRemoved); + + list< Handle(_pyMesh) >::iterator m = myChildMeshes.begin(); + for ( ; m != myChildMeshes.end(); ++m ) + (*m)->SetRemovedFromStudy(isRemoved); + + list< Handle(_pyMeshEditor)>::iterator e = myEditors.begin(); + for ( ; e != myEditors.end(); ++e ) + (*e)->SetRemovedFromStudy(isRemoved); +} + +//================================================================================ +/*! + * \brief Return true if none of myChildMeshes is in study + */ +//================================================================================ + +bool _pyMesh::CanClear() +{ + if ( IsInStudy() ) + return false; + + list< Handle(_pyMesh) >::iterator m = myChildMeshes.begin(); + for ( ; m != myChildMeshes.end(); ++m ) + if ( !(*m)->CanClear() ) + return false; + + return true; +} + +//================================================================================ +/*! + * \brief Clear my commands and commands of mesh editor + */ +//================================================================================ + +void _pyMesh::ClearCommands() +{ + if ( !CanClear() ) + { + if ( !IsInStudy() ) + { + // mark all sub-objects as not removed, except child meshes + list< Handle(_pyMesh) > children; + children.swap( myChildMeshes ); + SetRemovedFromStudy( false ); + children.swap( myChildMeshes ); + } + return; + } + _pyObject::ClearCommands(); + + list< Handle(_pySubMesh) >::iterator sm = mySubmeshes.begin(); + for ( ; sm != mySubmeshes.end(); ++sm ) + (*sm)->ClearCommands(); + + list< Handle(_pyGroup) >::iterator gr = myGroups.begin(); + for ( ; gr != myGroups.end(); ++gr ) + (*gr)->ClearCommands(); + + list< Handle(_pyMeshEditor)>::iterator e = myEditors.begin(); + for ( ; e != myEditors.end(); ++e ) + (*e)->ClearCommands(); +} + +//================================================================================ +/*! + * \brief Add a father mesh by ID + */ +//================================================================================ + +void _pyMesh::addFatherMesh( const _pyID& meshID ) +{ + if ( !meshID.IsEmpty() ) + addFatherMesh( Handle(_pyMesh)::DownCast( theGen->FindObject( meshID ))); +} + +//================================================================================ +/*! + * \brief Add a father mesh + */ +//================================================================================ + +void _pyMesh::addFatherMesh( const Handle(_pyMesh)& mesh ) +{ + if ( !mesh.IsNull() ) + { + //myFatherMeshes.push_back( mesh ); + mesh->myChildMeshes.push_back( this ); + + // protect last Compute() from clearing by the next Compute() + mesh->myLastComputeCmd.Nullify(); + } +} + //================================================================================ /*! * \brief MeshEditor convert its commands to ones of mesh @@ -1061,6 +1895,10 @@ _pyMeshEditor::_pyMeshEditor(const Handle(_pyCommand)& theCreationCmd): myMesh = theCreationCmd->GetObject(); myCreationCmdStr = theCreationCmd->GetString(); theCreationCmd->Clear(); + + Handle(_pyMesh) mesh = ObjectToMesh( theGen->FindObject( myMesh )); + if ( !mesh.IsNull() ) + mesh->AddEditor( this ); } //================================================================================ @@ -1071,34 +1909,36 @@ _pyMeshEditor::_pyMeshEditor(const Handle(_pyCommand)& theCreationCmd): void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand) { - // names of SMESH_MeshEditor methods fully equal to methods of class Mesh, so + // names of SMESH_MeshEditor methods fully equal to methods of python class Mesh, so // commands calling this methods are converted to calls of methods of Mesh static TStringSet sameMethods; if ( sameMethods.empty() ) { const char * names[] = { - "RemoveElements","RemoveNodes","AddNode","AddEdge","AddFace","AddPolygonalFace", + "RemoveElements","RemoveNodes","RemoveOrphanNodes","AddNode","Add0DElement","AddEdge","AddFace","AddPolygonalFace","AddBall", "AddVolume","AddPolyhedralVolume","AddPolyhedralVolumeByFaces","MoveNode", "MoveClosestNodeToPoint", "InverseDiag","DeleteDiag","Reorient","ReorientObject","TriToQuad","SplitQuad","SplitQuadObject", "BestSplit","Smooth","SmoothObject","SmoothParametric","SmoothParametricObject", "ConvertToQuadratic","ConvertFromQuadratic","RenumberNodes","RenumberElements", "RotationSweep","RotationSweepObject","RotationSweepObject1D","RotationSweepObject2D", "ExtrusionSweep","AdvancedExtrusion","ExtrusionSweepObject","ExtrusionSweepObject1D","ExtrusionSweepObject2D", - "ExtrusionAlongPath","ExtrusionAlongPathObject","ExtrusionAlongPathObject1D","ExtrusionAlongPathObject2D", + "ExtrusionAlongPath","ExtrusionAlongPathObject","ExtrusionAlongPathX", + "ExtrusionAlongPathObject1D","ExtrusionAlongPathObject2D", "Mirror","MirrorObject","Translate","TranslateObject","Rotate","RotateObject", - "FindCoincidentNodes","FindCoincidentNodesOnPart","MergeNodes","FindEqualElements", + "FindCoincidentNodes",/*"FindCoincidentNodesOnPart",*/"MergeNodes","FindEqualElements", "MergeElements","MergeEqualElements","SewFreeBorders","SewConformFreeBorders", "SewBorderToSide","SewSideElements","ChangeElemNodes","GetLastCreatedNodes", "GetLastCreatedElems", "MirrorMakeMesh","MirrorObjectMakeMesh","TranslateMakeMesh", - "TranslateObjectMakeMesh","RotateMakeMesh","RotateObjectMakeMesh" + "TranslateObjectMakeMesh","RotateMakeMesh","RotateObjectMakeMesh","MakeBoundaryMesh", + "MakeBoundaryElements", "SplitVolumesIntoTetra" ,"" }; // <- mark of the end sameMethods.Insert( names ); } // names of SMESH_MeshEditor methods which differ from methods of class Mesh - // only last two arguments + // only by last two arguments static TStringSet diffLastTwoArgsMethods; - if (diffLastTwoArgsMethods.empty() ){ + if (diffLastTwoArgsMethods.empty() ) { const char * names[] = { "MirrorMakeGroups","MirrorObjectMakeGroups", "TranslateMakeGroups","TranslateObjectMakeGroups", @@ -1107,54 +1947,162 @@ void _pyMeshEditor::Process( const Handle(_pyCommand)& theCommand) diffLastTwoArgsMethods.Insert( names ); } - if ( sameMethods.Contains( theCommand->GetMethod() )) { - theCommand->SetObject( myMesh ); - - // meshes made by *MakeMesh() methods are not wrapped by _pyMesh, - // so let _pyMesh care of it (TMP?) -// if ( theCommand->GetMethod().Search("MakeMesh") != -1 ) -// _pyMesh( new _pyCommand( theCommand->GetString(), 0 )); // for theGen->SetAccessorMethod() - } - else { - - //Replace SMESH_MeshEditor "MakeGroups" functions on the Mesh + const TCollection_AsciiString & method = theCommand->GetMethod(); + bool isPyMeshMethod = sameMethods.Contains( method ); + if ( !isPyMeshMethod ) + { + //Replace SMESH_MeshEditor "MakeGroups" functions by the Mesh //functions with the flag "theMakeGroups = True" like: //SMESH_MeshEditor.CmdMakeGroups => Mesh.Cmd(...,True) - int pos = theCommand->GetMethod().Search("MakeGroups"); - if( pos != -1) { + int pos = method.Search("MakeGroups"); + if( pos != -1) + { + isPyMeshMethod = true; + bool is0DmethId = ( method == "ExtrusionSweepMakeGroups0D" ); + bool is0DmethObj = ( method == "ExtrusionSweepObject0DMakeGroups"); + // 1. Remove "MakeGroups" from the Command TCollection_AsciiString aMethod = theCommand->GetMethod(); int nbArgsToAdd = diffLastTwoArgsMethods.Contains(aMethod) ? 2 : 1; + + if(is0DmethObj) + pos = pos-2; //Remove "0D" from the Command too aMethod.Trunc(pos-1); theCommand->SetMethod(aMethod); - // 2. Set Mesh object instead of SMESH_MeshEditor - theCommand->SetObject( myMesh ); - - // 3. And add last "True" argument + // 2. And add last "True" argument(s) while(nbArgsToAdd--) - theCommand->SetArg(theCommand->GetNbArgs()+1,"True "); + theCommand->SetArg(theCommand->GetNbArgs()+1,"True"); + if( is0DmethId || is0DmethObj ) + theCommand->SetArg(theCommand->GetNbArgs()+1,"True"); } - else { - // editor creation command is needed only if any editor function is called - theGen->AddMeshAccessorMethod( theCommand ); // for *Object() - if ( !myCreationCmdStr.IsEmpty() ) { - GetCreationCmd()->GetString() = myCreationCmdStr; - myCreationCmdStr.Clear(); - } + } + + // ExtrusionSweep0D() -> ExtrusionSweep() + // ExtrusionSweepObject0D() -> ExtrusionSweepObject() + if ( !isPyMeshMethod && ( method == "ExtrusionSweep0D" || + method == "ExtrusionSweepObject0D" )) + { + isPyMeshMethod = true; + theCommand->SetMethod( method.SubString( 1, method.Length()-2)); + theCommand->SetArg(theCommand->GetNbArgs()+1,"False"); //sets flag "MakeGroups = False" + theCommand->SetArg(theCommand->GetNbArgs()+1,"True"); //sets flag "IsNode = True" + } + // set "ExtrusionAlongPathX()" instead of "ExtrusionAlongPathObjX()" + if ( !isPyMeshMethod && method == "ExtrusionAlongPathObjX") + { + isPyMeshMethod = true; + theCommand->SetMethod("ExtrusionAlongPathX"); + } + + // set "FindCoincidentNodesOnPart()" instead of "FindCoincidentNodesOnPartBut()" + if ( !isPyMeshMethod && method == "FindCoincidentNodesOnPartBut") + { + isPyMeshMethod = true; + theCommand->SetMethod("FindCoincidentNodesOnPart"); + } + // DoubleNode...New(...) -> DoubleNode...(...,True) + if ( !isPyMeshMethod && ( method == "DoubleNodeElemGroupNew" || + method == "DoubleNodeElemGroupsNew" || + method == "DoubleNodeGroupNew" || + method == "DoubleNodeGroupsNew" || + method == "DoubleNodeElemGroup2New" || + method == "DoubleNodeElemGroups2New")) + { + isPyMeshMethod = true; + const int excessLen = 3 + int( method.Value( method.Length()-3 ) == '2' ); + theCommand->SetMethod( method.SubString( 1, method.Length()-excessLen)); + if ( excessLen == 3 ) + { + theCommand->SetArg(theCommand->GetNbArgs()+1,"True"); + } + else if ( theCommand->GetArg(4) == "0" || + theCommand->GetArg(5) == "0" ) + { + // [ nothing, Group ] = DoubleNodeGroup2New(,,,False, True) -> + // Group = DoubleNodeGroup2New(,,,False, True) + _pyID groupID = theCommand->GetResultValue( 1 + int( theCommand->GetArg(4) == "0")); + theCommand->SetResultValue( groupID ); + } + } + // ConvertToQuadraticObject(bool,obj) -> ConvertToQuadratic(bool,obj) + // ConvertFromQuadraticObject(obj) -> ConvertFromQuadratic(obj) + if ( !isPyMeshMethod && ( method == "ConvertToQuadraticObject" || + method == "ConvertFromQuadraticObject" )) + { + isPyMeshMethod = true; + theCommand->SetMethod( method.SubString( 1, method.Length()-6)); + } + // FindAmongElementsByPoint(meshPart, x, y, z, elementType) -> + // FindElementsByPoint(x, y, z, elementType, meshPart) + if ( !isPyMeshMethod && method == "FindAmongElementsByPoint" ) + { + isPyMeshMethod = true; + theCommand->SetMethod( "FindElementsByPoint" ); + // make the 1st arg be the last one + _pyID partID = theCommand->GetArg( 1 ); + int nbArgs = theCommand->GetNbArgs(); + for ( int i = 2; i <= nbArgs; ++i ) + theCommand->SetArg( i-1, theCommand->GetArg( i )); + theCommand->SetArg( nbArgs, partID ); + } + // Reorient2D( mesh, dir, face, point ) -> Reorient2D( mesh, dir, faceORpoint ) + if ( !isPyMeshMethod && method == "Reorient2D" ) + { + isPyMeshMethod = true; + _AString mesh = theCommand->GetArg( 1 ); + _AString dir = theCommand->GetArg( 2 ); + _AString face = theCommand->GetArg( 3 ); + _AString point = theCommand->GetArg( 4 ); + theCommand->RemoveArgs(); + theCommand->SetArg( 1, mesh ); + theCommand->SetArg( 2, dir ); + if ( face.Value(1) == '-' || face.Value(1) == '0' ) // invalid: face <= 0 + theCommand->SetArg( 3, point ); + else + theCommand->SetArg( 3, face ); + } + + // meshes made by *MakeMesh() methods are not wrapped by _pyMesh, + // so let _pyMesh care of it (TMP?) + // if ( theCommand->GetMethod().Search("MakeMesh") != -1 ) + // _pyMesh( new _pyCommand( theCommand->GetString(), 0 )); // for theGen->SetAccessorMethod() + if ( isPyMeshMethod ) + { + theCommand->SetObject( myMesh ); + } + else + { + // editor creation command is needed only if any editor function is called + theGen->AddMeshAccessorMethod( theCommand ); // for *Object() + if ( !myCreationCmdStr.IsEmpty() ) { + GetCreationCmd()->GetString() = myCreationCmdStr; + myCreationCmdStr.Clear(); } } } +//================================================================================ +/*! + * \brief Return true if my mesh can be removed + */ +//================================================================================ + +bool _pyMeshEditor::CanClear() +{ + Handle(_pyMesh) mesh = ObjectToMesh( theGen->FindObject( myMesh )); + return mesh.IsNull() ? true : mesh->CanClear(); +} + //================================================================================ /*! * \brief _pyHypothesis constructor - * \param theCreationCmd - + * \param theCreationCmd - */ //================================================================================ _pyHypothesis::_pyHypothesis(const Handle(_pyCommand)& theCreationCmd): - _pyObject( theCreationCmd ) + _pyObject( theCreationCmd ), myCurCrMethod(0) { myIsAlgo = myIsWrapped = /*myIsConverted = myIsLocal = myDim = */false; } @@ -1185,178 +2133,66 @@ Handle(_pyHypothesis) _pyHypothesis::NewHypothesis( const Handle(_pyCommand)& th algo = new _pyAlgorithm( theCreationCmd ); hyp = new _pyHypothesis( theCreationCmd ); - // 1D Regular_1D ---------- - if ( hypType == "Regular_1D" ) { - // set mesh's method creating algo, - // i.e. convertion result will be "regular1d = Mesh.Segment()", - // and set hypType by which algo creating a hypothesis is searched for - algo->SetConvMethodAndType("Segment", hypType.ToCString()); - } - else if ( hypType == "CompositeSegment_1D" ) { - algo->SetConvMethodAndType("Segment", "Regular_1D"); - algo->myArgs.Append( "algo=smesh.COMPOSITE"); - } - else if ( hypType == "LocalLength" ) { - // set algo's method creating hyp, and algo type - hyp->SetConvMethodAndType( "LocalLength", "Regular_1D"); - // set method whose 1 arg will become the 1-st arg of hyp creation command - // i.e. convertion result will be "locallength = regular1d.LocalLength()" - hyp->AddArgMethod( "SetLength" ); - } - else if ( hypType == "MaxLength" ) { - // set algo's method creating hyp, and algo type - hyp->SetConvMethodAndType( "MaxSize", "Regular_1D"); - // set method whose 1 arg will become the 1-st arg of hyp creation command - // i.e. convertion result will be "maxsize = regular1d.MaxSize()" - hyp->AddArgMethod( "SetLength" ); - } - else if ( hypType == "NumberOfSegments" ) { + if ( hypType == "NumberOfSegments" ) { hyp = new _pyNumberOfSegmentsHyp( theCreationCmd ); hyp->SetConvMethodAndType( "NumberOfSegments", "Regular_1D"); // arg of SetNumberOfSegments() will become the 1-st arg of hyp creation command hyp->AddArgMethod( "SetNumberOfSegments" ); // arg of SetScaleFactor() will become the 2-nd arg of hyp creation command hyp->AddArgMethod( "SetScaleFactor" ); - } - else if ( hypType == "Arithmetic1D" ) { - hyp = new _pyComplexParamHypo( theCreationCmd ); - hyp->SetConvMethodAndType( "Arithmetic1D", "Regular_1D"); - } - else if ( hypType == "StartEndLength" ) { - hyp = new _pyComplexParamHypo( theCreationCmd ); - hyp->SetConvMethodAndType( "StartEndLength", "Regular_1D"); - } - else if ( hypType == "Deflection1D" ) { - hyp->SetConvMethodAndType( "Deflection1D", "Regular_1D"); - hyp->AddArgMethod( "SetDeflection" ); - } - else if ( hypType == "Propagation" ) { - hyp->SetConvMethodAndType( "Propagation", "Regular_1D"); - } - else if ( hypType == "QuadraticMesh" ) { - hyp->SetConvMethodAndType( "QuadraticMesh", "Regular_1D"); - } - else if ( hypType == "AutomaticLength" ) { - hyp->SetConvMethodAndType( "AutomaticLength", "Regular_1D"); - hyp->AddArgMethod( "SetFineness"); + hyp->AddArgMethod( "SetReversedEdges" ); + // same for ""CompositeSegment_1D: + hyp->SetConvMethodAndType( "NumberOfSegments", "CompositeSegment_1D"); + hyp->AddArgMethod( "SetNumberOfSegments" ); + hyp->AddArgMethod( "SetScaleFactor" ); + hyp->AddArgMethod( "SetReversedEdges" ); } else if ( hypType == "SegmentLengthAroundVertex" ) { hyp = new _pySegmentLengthAroundVertexHyp( theCreationCmd ); hyp->SetConvMethodAndType( "LengthNearVertex", "Regular_1D" ); hyp->AddArgMethod( "SetLength" ); + // same for ""CompositeSegment_1D: + hyp->SetConvMethodAndType( "LengthNearVertex", "CompositeSegment_1D"); + hyp->AddArgMethod( "SetLength" ); } - // 1D Python_1D ---------- - else if ( hypType == "Python_1D" ) { - algo->SetConvMethodAndType( "Segment", hypType.ToCString()); - algo->myArgs.Append( "algo=smesh.PYTHON"); - } - else if ( hypType == "PythonSplit1D" ) { - hyp->SetConvMethodAndType( "PythonSplit1D", "Python_1D"); - hyp->AddArgMethod( "SetNumberOfSegments"); - hyp->AddArgMethod( "SetPythonLog10RatioFunction"); - } - // MEFISTO_2D ---------- - else if ( hypType == "MEFISTO_2D" ) { // MEFISTO_2D - algo->SetConvMethodAndType( "Triangle", hypType.ToCString()); - } - else if ( hypType == "MaxElementArea" ) { - hyp->SetConvMethodAndType( "MaxElementArea", "MEFISTO_2D"); - hyp->SetConvMethodAndType( "MaxElementArea", "NETGEN_2D_ONLY"); - hyp->AddArgMethod( "SetMaxElementArea"); - } - else if ( hypType == "LengthFromEdges" ) { - hyp->SetConvMethodAndType( "LengthFromEdges", "MEFISTO_2D"); - hyp->SetConvMethodAndType( "LengthFromEdges", "NETGEN_2D_ONLY"); - } - // Quadrangle_2D ---------- - else if ( hypType == "Quadrangle_2D" ) { - algo->SetConvMethodAndType( "Quadrangle" , hypType.ToCString()); - } - else if ( hypType == "QuadranglePreference" ) { - hyp->SetConvMethodAndType( "QuadranglePreference", "Quadrangle_2D"); - hyp->SetConvMethodAndType( "QuadranglePreference", "NETGEN_2D_ONLY"); - } - else if ( hypType == "TrianglePreference" ) { - hyp->SetConvMethodAndType( "TrianglePreference", "Quadrangle_2D"); - } - // NETGEN ---------- -// else if ( hypType == "NETGEN_2D") { // 1D-2D -// algo->SetConvMethodAndType( "Triangle" , hypType.ToCString()); -// algo->myArgs.Append( "algo=smesh.NETGEN" ); -// } - else if ( hypType == "NETGEN_2D_ONLY") { // 2D - algo->SetConvMethodAndType( "Triangle" , hypType.ToCString()); - algo->myArgs.Append( "algo=smesh.NETGEN_2D" ); - } - else if ( hypType == "NETGEN_3D") { // 3D - algo->SetConvMethodAndType( "Tetrahedron" , hypType.ToCString()); - algo->myArgs.Append( "algo=smesh.NETGEN" ); - } - else if ( hypType == "MaxElementVolume") { - hyp->SetConvMethodAndType( "MaxElementVolume", "NETGEN_3D"); - hyp->AddArgMethod( "SetMaxElementVolume" ); - } - // GHS3D_3D ---------- - else if ( hypType == "GHS3D_3D" ) { - algo->SetConvMethodAndType( "Tetrahedron", hypType.ToCString()); - algo->myArgs.Append( "algo=smesh.GHS3D" ); - } - // Hexa_3D --------- - else if ( hypType == "Hexa_3D" ) { - algo->SetConvMethodAndType( "Hexahedron", hypType.ToCString()); - } - // Repetitive Projection_1D --------- - else if ( hypType == "Projection_1D" ) { - algo->SetConvMethodAndType( "Projection1D", hypType.ToCString()); - } - else if ( hypType == "ProjectionSource1D" ) { - hyp->SetConvMethodAndType( "SourceEdge", "Projection_1D"); - hyp->AddArgMethod( "SetSourceEdge"); - hyp->AddArgMethod( "SetSourceMesh"); - // 2 args of SetVertexAssociation() will become the 3-th and 4-th args of hyp creation command - hyp->AddArgMethod( "SetVertexAssociation", 2 ); - } - // Projection_2D --------- - else if ( hypType == "Projection_2D" ) { - algo->SetConvMethodAndType( "Projection2D", hypType.ToCString()); - } - else if ( hypType == "ProjectionSource2D" ) { - hyp->SetConvMethodAndType( "SourceFace", "Projection_2D"); - hyp->AddArgMethod( "SetSourceFace"); - hyp->AddArgMethod( "SetSourceMesh"); - hyp->AddArgMethod( "SetVertexAssociation", 4 ); - } - // Projection_3D --------- - else if ( hypType == "Projection_3D" ) { - algo->SetConvMethodAndType( "Projection3D", hypType.ToCString()); - } - else if ( hypType == "ProjectionSource3D" ) { - hyp->SetConvMethodAndType( "SourceShape3D", "Projection_3D"); - hyp->AddArgMethod( "SetSource3DShape"); - hyp->AddArgMethod( "SetSourceMesh"); - hyp->AddArgMethod( "SetVertexAssociation", 4 ); - } - // Prism_3D --------- - else if ( hypType == "Prism_3D" ) { - algo->SetConvMethodAndType( "Prism", hypType.ToCString()); - } - // RadialPrism_3D --------- - else if ( hypType == "RadialPrism_3D" ) { - algo->SetConvMethodAndType( "Prism", hypType.ToCString()); - } - else if ( hypType == "NumberOfLayers" ) { - hyp->SetConvMethodAndType( "NumberOfLayers", "RadialPrism_3D"); - hyp->AddArgMethod( "SetNumberOfLayers" ); + else if ( hypType == "LayerDistribution2D" ) { + hyp = new _pyLayerDistributionHypo( theCreationCmd, "Get2DHypothesis" ); + hyp->SetConvMethodAndType( "LayerDistribution", "RadialQuadrangle_1D2D"); } else if ( hypType == "LayerDistribution" ) { - hyp = new _pyLayerDistributionHypo( theCreationCmd ); + hyp = new _pyLayerDistributionHypo( theCreationCmd, "Get3DHypothesis" ); hyp->SetConvMethodAndType( "LayerDistribution", "RadialPrism_3D"); } + else if ( hypType == "CartesianParameters3D" ) { + hyp = new _pyComplexParamHypo( theCreationCmd ); + hyp->SetConvMethodAndType( "SetGrid", "Cartesian_3D"); + for ( int iArg = 0; iArg < 4; ++iArg ) + hyp->setCreationArg( iArg+1, "[]"); + } + else + { + hyp = theGen->GetHypothesisReader()->GetHypothesis( hypType, theCreationCmd ); + } + + return algo->IsValid() ? algo : hyp; +} - if ( algo->IsValid() ) { - return algo; +//================================================================================ +/*! + * \brief Returns true if addition of this hypothesis to a given mesh can be + * wrapped into hypothesis creation + */ +//================================================================================ + +bool _pyHypothesis::IsWrappable(const _pyID& theMesh) const +{ + if ( !myIsWrapped && myMesh == theMesh && IsInStudy() ) + { + Handle(_pyObject) pyMesh = theGen->FindObject( myMesh ); + if ( !pyMesh.IsNull() && pyMesh->IsInStudy() ) + return true; } - return hyp; + return false; } //================================================================================ @@ -1394,20 +2230,20 @@ bool _pyHypothesis::Addition2Creation( const Handle(_pyCommand)& theCmd, theCmd->SetResultValue( GetID() ); theCmd->SetObject( IsAlgo() ? theMesh : algo->GetID()); theCmd->SetMethod( IsAlgo() ? GetAlgoCreationMethod() : GetCreationMethod( algo->GetAlgoType() )); - // set args + // set args (geom will be set by _pyMesh calling this method) theCmd->RemoveArgs(); - for ( int i = 1; i <= myArgs.Length(); ++i ) { - if ( !myArgs( i ).IsEmpty() ) - theCmd->SetArg( i, myArgs( i )); + for ( size_t i = 0; i < myCurCrMethod->myArgs.size(); ++i ) { + if ( !myCurCrMethod->myArgs[ i ].IsEmpty() ) + theCmd->SetArg( i+1, myCurCrMethod->myArgs[ i ]); else - theCmd->SetArg( i, "[]"); + theCmd->SetArg( i+1, "[]"); } // set a new creation command GetCreationCmd()->Clear(); // replace creation command by wrapped instance // please note, that hypothesis attaches to algo creation command (see upper) SetCreationCmd( theCmd ); - + // clear commands setting arg values list < Handle(_pyCommand) >::iterator argCmd = myArgCommands.begin(); @@ -1416,8 +2252,8 @@ bool _pyHypothesis::Addition2Creation( const Handle(_pyCommand)& theCmd, // set unknown arg commands after hypo creation Handle(_pyCommand) afterCmd = myIsWrapped ? theCmd : GetCreationCmd(); - list::iterator cmd = myUnknownCommands.begin(); - for ( ; cmd != myUnknownCommands.end(); ++cmd ) { + list::iterator cmd = myUnusedCommands.begin(); + for ( ; cmd != myUnusedCommands.end(); ++cmd ) { afterCmd->AddDependantCmd( *cmd ); } @@ -1434,20 +2270,27 @@ bool _pyHypothesis::Addition2Creation( const Handle(_pyCommand)& theCmd, void _pyHypothesis::Process( const Handle(_pyCommand)& theCommand) { ASSERT( !myIsAlgo ); + if ( !theGen->IsToKeepAllCommands() ) + rememberCmdOfParameter( theCommand ); // set args - int nbArgs = 0; - for ( int i = 1; i <= myArgMethods.Length(); ++i ) { - if ( myArgMethods( i ) == theCommand->GetMethod() ) { - while ( myArgs.Length() < nbArgs + myNbArgsByMethod( i )) - myArgs.Append( "[]" ); - for ( int iArg = 1; iArg <= myNbArgsByMethod( i ); ++iArg ) - myArgs( nbArgs + iArg ) = theCommand->GetArg( iArg ); // arg value - myArgCommands.push_back( theCommand ); - return; + bool usedCommand = false; + TType2CrMethod::iterator type2meth = myAlgoType2CreationMethod.begin(); + for ( ; type2meth != myAlgoType2CreationMethod.end(); ++type2meth ) + { + CreationMethod& crMethod = type2meth->second; + for ( size_t i = 0; i < crMethod.myArgMethods.size(); ++i ) { + if ( crMethod.myArgMethods[ i ] == theCommand->GetMethod() ) { + if ( !usedCommand ) + myArgCommands.push_back( theCommand ); + usedCommand = true; + while ( crMethod.myArgs.size() < i+1 ) + crMethod.myArgs.push_back( "[]" ); + crMethod.myArgs[ i ] = theCommand->GetArg( crMethod.myArgNb[i] ); + } } - nbArgs += myNbArgsByMethod( i ); } - myUnknownCommands.push_back( theCommand ); + if ( !usedCommand ) + myUnusedCommands.push_back( theCommand ); } //================================================================================ @@ -1458,9 +2301,8 @@ void _pyHypothesis::Process( const Handle(_pyCommand)& theCommand) void _pyHypothesis::Flush() { - if ( IsWrapped() ) { - } - else { + if ( !IsAlgo() ) + { list < Handle(_pyCommand) >::iterator cmd = myArgCommands.begin(); for ( ; cmd != myArgCommands.end(); ++cmd ) { // Add access to a wrapped mesh @@ -1468,8 +2310,8 @@ void _pyHypothesis::Flush() // Add access to a wrapped algorithm theGen->AddAlgoAccessorMethod( *cmd ); } - cmd = myUnknownCommands.begin(); - for ( ; cmd != myUnknownCommands.end(); ++cmd ) { + cmd = myUnusedCommands.begin(); + for ( ; cmd != myUnusedCommands.end(); ++cmd ) { // Add access to a wrapped mesh theGen->AddMeshAccessorMethod( *cmd ); // Add access to a wrapped algorithm @@ -1478,7 +2320,7 @@ void _pyHypothesis::Flush() } // forget previous hypothesis modifications myArgCommands.clear(); - myUnknownCommands.clear(); + myUnusedCommands.clear(); } //================================================================================ @@ -1493,8 +2335,8 @@ void _pyHypothesis::ClearAllCommands() list::iterator cmd = myArgCommands.begin(); for ( ; cmd != myArgCommands.end(); ++cmd ) ( *cmd )->Clear(); - cmd = myUnknownCommands.begin(); - for ( ; cmd != myUnknownCommands.end(); ++cmd ) + cmd = myUnusedCommands.begin(); + for ( ; cmd != myUnusedCommands.end(); ++cmd ) ( *cmd )->Clear(); } @@ -1506,144 +2348,426 @@ void _pyHypothesis::ClearAllCommands() //================================================================================ void _pyHypothesis::Assign( const Handle(_pyHypothesis)& theOther, - const _pyID& theMesh ) + const _pyID& theMesh ) { - myIsWrapped = false; - myMesh = theMesh; - // myCreationCmd = theOther->myCreationCmd; - myIsAlgo = theOther->myIsAlgo; - myGeom = theOther->myGeom; - myType2CreationMethod = theOther->myType2CreationMethod; - myArgs = theOther->myArgs; - myArgMethods = theOther->myArgMethods; - myNbArgsByMethod = theOther->myNbArgsByMethod; - myArgCommands = theOther->myArgCommands; - myUnknownCommands = theOther->myUnknownCommands; + myIsAlgo = theOther->myIsAlgo; + myIsWrapped = false; + myGeom = theOther->myGeom; + myMesh = theMesh; + myAlgoType2CreationMethod = theOther->myAlgoType2CreationMethod; + //myArgCommands = theOther->myArgCommands; + //myUnusedCommands = theOther->myUnusedCommands; + // init myCurCrMethod + GetCreationMethod( theOther->GetAlgoType() ); } //================================================================================ /*! - * \brief Remember hypothesis parameter values - * \param theCommand - The called hypothesis method + * \brief Analyze my erasability depending on myReferredObjs */ //================================================================================ -void _pyComplexParamHypo::Process( const Handle(_pyCommand)& theCommand) +bool _pyHypothesis::CanClear() { - // ex: hyp.SetLength(start, 1) - // hyp.SetLength(end, 0) - ASSERT(( theCommand->GetMethod() == "SetLength" )); - ASSERT(( theCommand->GetArg( 2 ).IsIntegerValue() )); - int i = 2 - theCommand->GetArg( 2 ).IntegerValue(); - while ( myArgs.Length() < i ) - myArgs.Append( "[]" ); - myArgs( i ) = theCommand->GetArg( 1 ); // arg value - myArgCommands.push_back( theCommand ); + if ( IsInStudy() ) + { + list< Handle(_pyObject) >::iterator obj = myReferredObjs.begin(); + for ( ; obj != myReferredObjs.end(); ++obj ) + if ( (*obj)->CanClear() ) + return true; + return false; + } + return true; } //================================================================================ /*! - * \brief Convert methods of 1D hypotheses to my own methods - * \param theCommand - The called hypothesis method + * \brief Clear my commands depending on usage by meshes */ //================================================================================ -void _pyLayerDistributionHypo::Process( const Handle(_pyCommand)& theCommand) +void _pyHypothesis::ClearCommands() { - if ( theCommand->GetMethod() != "SetLayerDistribution" ) - return; + // if ( !theGen->IsToKeepAllCommands() ) + // { + // bool isUsed = false; + // int lastComputeOrder = 0; + // list::iterator cmd = myComputeCmds.begin(); + // for ( ; cmd != myComputeCmds.end(); ++cmd ) + // if ( ! (*cmd)->IsEmpty() ) + // { + // isUsed = true; + // if ( (*cmd)->GetOrderNb() > lastComputeOrder ) + // lastComputeOrder = (*cmd)->GetOrderNb(); + // } + // if ( !isUsed ) + // { + // SetRemovedFromStudy( true ); + // } + // else + // { + // // clear my commands invoked after lastComputeOrder + // // map >::iterator m2c; + // // for ( m2c = myMeth2Commands.begin(); m2c != myMeth2Commands.end(); ++m2c ) + // // { + // // list< Handle(_pyCommand)> & cmds = m2c->second; + // // if ( !cmds.empty() && cmds.back()->GetOrderNb() > lastComputeOrder ) + // // cmds.back()->Clear(); + // // } + // } + // } + _pyObject::ClearCommands(); +} - _pyID newName; // name for 1D hyp = "HypType" + "_Distribution" +//================================================================================ +/*! + * \brief Find arguments that are objects like mesh, group, geometry + * \param meshes - referred meshes (directly or indirrectly) + * \retval bool - false if a referred geometry is not in the study + */ +//================================================================================ - const _pyID& hyp1dID = theCommand->GetArg( 1 ); - Handle(_pyHypothesis) hyp1d = theGen->FindHyp( hyp1dID ); - if ( hyp1d.IsNull() ) // apparently hypId changed at study restoration - hyp1d = my1dHyp; - else if ( !my1dHyp.IsNull() && hyp1dID != my1dHyp->GetID() ) { - // 1D hypo is already set, so distribution changes and the old - // 1D hypo is thrown away - my1dHyp->ClearAllCommands(); - } - my1dHyp = hyp1d; - if ( my1dHyp.IsNull() ) - return; // something wrong :( +bool _pyHypothesis::GetReferredMeshesAndGeom( list< Handle(_pyMesh) >& meshes ) +{ + if ( IsAlgo() ) return true; - // make a new name for 1D hyp = "HypType" + "_Distribution" - if ( my1dHyp->GetCreationCmd()->GetMethod() == "CreateHypothesis" ) { - // not yet converted creation cmd - TCollection_AsciiString hypTypeQuoted = my1dHyp->GetCreationCmd()->GetArg(1); - TCollection_AsciiString hypType = hypTypeQuoted.SubString( 2, hypTypeQuoted.Length() - 1 ); - newName = hypType + "_Distribution"; - my1dHyp->GetCreationCmd()->SetResultValue( newName ); + bool geomPublished = true; + vector< _AString > args; + TType2CrMethod::iterator type2meth = myAlgoType2CreationMethod.begin(); + for ( ; type2meth != myAlgoType2CreationMethod.end(); ++type2meth ) + { + CreationMethod& crMethod = type2meth->second; + args.insert( args.end(), crMethod.myArgs.begin(), crMethod.myArgs.end()); } - else { - // already converted creation cmd - newName = my1dHyp->GetCreationCmd()->GetResultValue(); + list::iterator cmd = myUnusedCommands.begin(); + for ( ; cmd != myUnusedCommands.end(); ++cmd ) { + for ( int nb = (*cmd)->GetNbArgs(); nb; --nb ) + args.push_back( (*cmd)->GetArg( nb )); } - // as creation of 1D hyp was written later then it's edition, - // we need to find all it's edition calls and process them - list< Handle(_pyCommand) >& cmds = theGen->GetCommands(); - list< Handle(_pyCommand) >::iterator cmdIt = cmds.begin(); - for ( ; cmdIt != cmds.end(); ++cmdIt ) { - const _pyID& objID = (*cmdIt)->GetObject(); - if ( objID == hyp1dID ) { - my1dHyp->Process( *cmdIt ); - my1dHyp->GetCreationCmd()->AddDependantCmd( *cmdIt ); - ( *cmdIt )->SetObject( newName ); - } - } - if ( !myArgCommands.empty() ) - myArgCommands.front()->Clear(); - theCommand->SetArg( 1, newName ); - myArgCommands.push_back( theCommand ); - // copy hyp1d's creation method and args -// myCreationMethod = hyp1d->GetCreationMethod(); -// myArgs = hyp1d->GetArgs(); -// // make them cleared at conversion -// myArgCommands = hyp1d->GetArgCommands(); - -// // to be cleared at convertion only -// myArgCommands.push_back( theCommand ); + for ( size_t i = 0; i < args.size(); ++i ) + { + list< _pyID > idList = _pyCommand::GetStudyEntries( args[ i ]); + if ( idList.empty() && !args[ i ].IsEmpty() ) + idList.push_back( args[ i ]); + list< _pyID >::iterator id = idList.begin(); + for ( ; id != idList.end(); ++id ) + { + Handle(_pyObject) obj = theGen->FindObject( *id ); + if ( obj.IsNull() ) obj = theGen->FindHyp( *id ); + if ( obj.IsNull() ) + { + if ( theGen->IsGeomObject( *id ) && theGen->IsNotPublished( *id )) + geomPublished = false; + } + else + { + myReferredObjs.push_back( obj ); + Handle(_pyMesh) mesh = ObjectToMesh( obj ); + if ( !mesh.IsNull() ) + meshes.push_back( mesh ); + // prevent clearing not published hyps referred e.g. by "LayerDistribution" + else if ( obj->IsKind( STANDARD_TYPE( _pyHypothesis )) && this->IsInStudy() ) + obj->SetRemovedFromStudy( false ); + } + } + } + return geomPublished; } //================================================================================ /*! - * \brief - * \param theAdditionCmd - command to be converted - * \param theMesh - mesh instance - * \retval bool - status + * \brief Remember theCommand setting a parameter */ //================================================================================ -bool _pyLayerDistributionHypo::Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, - const _pyID& theMesh) +void _pyHypothesis::rememberCmdOfParameter( const Handle(_pyCommand) & theCommand ) { - myIsWrapped = false; + // parameters are discriminated by method name + TCollection_AsciiString method = theCommand->GetMethod(); - if ( my1dHyp.IsNull() ) - return false; + // discriminate commands setting different parameters via one method + // by passing parameter names like e.g. SetOption("size", "0.2") + if ( theCommand->GetString().FirstLocationInSet( "'\"", 1, theCommand->Length() ) && + theCommand->GetNbArgs() > 1 ) + { + // mangle method by appending a 1st textual arg + for ( int iArg = 1; iArg <= theCommand->GetNbArgs(); ++iArg ) + { + const TCollection_AsciiString& arg = theCommand->GetArg( iArg ); + if ( arg.Value(1) != '\"' && arg.Value(1) != '\'' ) continue; + if ( !isalpha( arg.Value(2))) continue; + method += arg; + break; + } + } + // parameters are discriminated by method name + list< Handle(_pyCommand)>& cmds = myMeth2Commands[ theCommand->GetMethod() ]; + if ( !cmds.empty() && !isCmdUsedForCompute( cmds.back() )) + { + cmds.back()->Clear(); // previous parameter value has not been used + cmds.back() = theCommand; + } + else + { + cmds.push_back( theCommand ); + } +} - // set "SetLayerDistribution()" after addition cmd - theAdditionCmd->AddDependantCmd( myArgCommands.front() ); +//================================================================================ +/*! + * \brief Return true if a setting parameter command ha been used to compute mesh + */ +//================================================================================ - _pyID geom = theAdditionCmd->GetArg( 1 ); +bool _pyHypothesis::isCmdUsedForCompute( const Handle(_pyCommand) & cmd, + _pyCommand::TAddr avoidComputeAddr ) const +{ + bool isUsed = false; + map< _pyCommand::TAddr, list >::const_iterator addr2cmds = + myComputeAddr2Cmds.begin(); + for ( ; addr2cmds != myComputeAddr2Cmds.end() && !isUsed; ++addr2cmds ) + { + if ( addr2cmds->first == avoidComputeAddr ) continue; + const list & cmds = addr2cmds->second; + isUsed = ( std::find( cmds.begin(), cmds.end(), cmd ) != cmds.end() ); + } + return isUsed; +} + +//================================================================================ +/*! + * \brief Save commands setting parameters as they are used for a mesh computation + */ +//================================================================================ + +void _pyHypothesis::MeshComputed( const Handle(_pyCommand)& theComputeCmd ) +{ + myComputeCmds.push_back( theComputeCmd ); + list& savedCmds = myComputeAddr2Cmds[ theComputeCmd->GetAddress() ]; + + map >::iterator m2c; + for ( m2c = myMeth2Commands.begin(); m2c != myMeth2Commands.end(); ++m2c ) + savedCmds.push_back( m2c->second.back() ); +} + +//================================================================================ +/*! + * \brief Clear commands setting parameters as a mesh computed using them is cleared + */ +//================================================================================ + +void _pyHypothesis::ComputeDiscarded( const Handle(_pyCommand)& theComputeCmd ) +{ + list& savedCmds = myComputeAddr2Cmds[ theComputeCmd->GetAddress() ]; + + list::iterator cmd = savedCmds.begin(); + for ( ; cmd != savedCmds.end(); ++cmd ) + { + // check if a cmd has been used to compute another mesh + if ( isCmdUsedForCompute( *cmd, theComputeCmd->GetAddress() )) + continue; + // check if a cmd is a sole command setting its parameter; + // don't use method name for search as it can change + map >::iterator + m2cmds = myMeth2Commands.begin(); + for ( ; m2cmds != myMeth2Commands.end(); ++m2cmds ) + { + list< Handle(_pyCommand)>& cmds = m2cmds->second; + list< Handle(_pyCommand)>::iterator cmdIt = std::find( cmds.begin(), cmds.end(), *cmd ); + if ( cmdIt != cmds.end() ) + { + if ( cmds.back() != *cmd ) + { + cmds.erase( cmdIt ); + (*cmd)->Clear(); + } + break; + } + } + } + myComputeAddr2Cmds.erase( theComputeCmd->GetAddress() ); +} + +//================================================================================ +/*! + * \brief Sets an argNb-th argument of current creation command + * \param argNb - argument index countered from 1 + */ +//================================================================================ + +void _pyHypothesis::setCreationArg( const int argNb, const _AString& arg ) +{ + if ( myCurCrMethod ) + { + while ( myCurCrMethod->myArgs.size() < argNb ) + myCurCrMethod->myArgs.push_back( "None" ); + if ( arg.IsEmpty() ) + myCurCrMethod->myArgs[ argNb-1 ] = "None"; + else + myCurCrMethod->myArgs[ argNb-1 ] = arg; + } +} + + +//================================================================================ +/*! + * \brief Remember hypothesis parameter values + * \param theCommand - The called hypothesis method + */ +//================================================================================ + +void _pyComplexParamHypo::Process( const Handle(_pyCommand)& theCommand) +{ + if ( GetAlgoType() == "Cartesian_3D" ) + { + // CartesianParameters3D hyp + + if ( theCommand->GetMethod() == "SetSizeThreshold" ) + { + setCreationArg( 4, theCommand->GetArg( 1 )); + myArgCommands.push_back( theCommand ); + return; + } + if ( theCommand->GetMethod() == "SetGrid" || + theCommand->GetMethod() == "SetGridSpacing" ) + { + TCollection_AsciiString axis = theCommand->GetArg( theCommand->GetNbArgs() ); + int iArg = axis.Value(1) - '0'; + if ( theCommand->GetMethod() == "SetGrid" ) + { + setCreationArg( 1+iArg, theCommand->GetArg( 1 )); + } + else + { + myCurCrMethod->myArgs[ iArg ] = "[ "; + myCurCrMethod->myArgs[ iArg ] += theCommand->GetArg( 1 ); + myCurCrMethod->myArgs[ iArg ] += ", "; + myCurCrMethod->myArgs[ iArg ] += theCommand->GetArg( 2 ); + myCurCrMethod->myArgs[ iArg ] += "]"; + } + myArgCommands.push_back( theCommand ); + rememberCmdOfParameter( theCommand ); + return; + } + } + + if( theCommand->GetMethod() == "SetLength" ) + { + // NOW it is OBSOLETE + // ex: hyp.SetLength(start, 1) + // hyp.SetLength(end, 0) + ASSERT(( theCommand->GetArg( 2 ).IsIntegerValue() )); + int i = 1 - theCommand->GetArg( 2 ).IntegerValue(); + TType2CrMethod::iterator type2meth = myAlgoType2CreationMethod.begin(); + for ( ; type2meth != myAlgoType2CreationMethod.end(); ++type2meth ) + { + CreationMethod& crMethod = type2meth->second; + while ( crMethod.myArgs.size() < i+1 ) + crMethod.myArgs.push_back( "[]" ); + crMethod.myArgs[ i ] = theCommand->GetArg( 1 ); // arg value + } + myArgCommands.push_back( theCommand ); + } + else + { + _pyHypothesis::Process( theCommand ); + } +} +//================================================================================ +/*! + * \brief Clear SetObjectEntry() as it is called by methods of Mesh_Segment + */ +//================================================================================ + +void _pyComplexParamHypo::Flush() +{ + if ( IsWrapped() ) + { + list < Handle(_pyCommand) >::iterator cmd = myUnusedCommands.begin(); + for ( ; cmd != myUnusedCommands.end(); ++cmd ) + if ((*cmd)->GetMethod() == "SetObjectEntry" ) + (*cmd)->Clear(); + } +} + +//================================================================================ +/*! + * \brief Convert methods of 1D hypotheses to my own methods + * \param theCommand - The called hypothesis method + */ +//================================================================================ + +void _pyLayerDistributionHypo::Process( const Handle(_pyCommand)& theCommand) +{ + if ( theCommand->GetMethod() != "SetLayerDistribution" ) + return; + + const _pyID& hyp1dID = theCommand->GetArg( 1 ); + // Handle(_pyHypothesis) hyp1d = theGen->FindHyp( hyp1dID ); + // if ( hyp1d.IsNull() && ! my1dHyp.IsNull()) // apparently hypId changed at study restoration + // { + // TCollection_AsciiString cmd = + // my1dHyp->GetCreationCmd()->GetIndentation() + hyp1dID + " = " + my1dHyp->GetID(); + // Handle(_pyCommand) newCmd = theGen->AddCommand( cmd ); + // theGen->SetCommandAfter( newCmd, my1dHyp->GetCreationCmd() ); + // hyp1d = my1dHyp; + // } + // else if ( !my1dHyp.IsNull() && hyp1dID != my1dHyp->GetID() ) + // { + // // 1D hypo is already set, so distribution changes and the old + // // 1D hypo is thrown away + // my1dHyp->ClearAllCommands(); + // } + // my1dHyp = hyp1d; + // //my1dHyp->SetRemovedFromStudy( false ); + + // if ( !myArgCommands.empty() ) + // myArgCommands.back()->Clear(); + myCurCrMethod->myArgs.push_back( hyp1dID ); + myArgCommands.push_back( theCommand ); +} + +//================================================================================ +/*! + * \brief + * \param theAdditionCmd - command to be converted + * \param theMesh - mesh instance + * \retval bool - status + */ +//================================================================================ - my1dHyp->SetMesh( theMesh ); - if ( !my1dHyp->Addition2Creation( theAdditionCmd, theMesh )) +bool _pyLayerDistributionHypo::Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, + const _pyID& theMesh) +{ + myIsWrapped = false; + + if ( my1dHyp.IsNull() ) return false; - // clear "SetLayerDistribution()" cmd - myArgCommands.front()->Clear(); + // set "SetLayerDistribution()" after addition cmd + theAdditionCmd->AddDependantCmd( myArgCommands.front() ); - // Convert my creation => me = RadialPrismAlgo.Get3DHypothesis() + _pyID geom = theAdditionCmd->GetArg( 1 ); - // find RadialPrism algo created on for theMesh Handle(_pyHypothesis) algo = theGen->FindAlgo( geom, theMesh, this ); - if ( !algo.IsNull() ) { + if ( !algo.IsNull() ) + { + my1dHyp->SetMesh( theMesh ); + my1dHyp->SetConvMethodAndType(my1dHyp->GetAlgoCreationMethod().ToCString(), + algo->GetAlgoType().ToCString()); + if ( !my1dHyp->Addition2Creation( theAdditionCmd, theMesh )) + return false; + + // clear "SetLayerDistribution()" cmd + myArgCommands.back()->Clear(); + + // Convert my creation => me = RadialPrismAlgo.Get3DHypothesis() + + // find RadialPrism algo created on for theMesh GetCreationCmd()->SetObject( algo->GetID() ); - GetCreationCmd()->SetMethod( "Get3DHypothesis" ); + GetCreationCmd()->SetMethod( myAlgoMethod ); GetCreationCmd()->RemoveArgs(); theAdditionCmd->AddDependantCmd( GetCreationCmd() ); myIsWrapped = true; @@ -1653,14 +2777,60 @@ bool _pyLayerDistributionHypo::Addition2Creation( const Handle(_pyCommand)& theA //================================================================================ /*! - * \brief + * \brief */ //================================================================================ void _pyLayerDistributionHypo::Flush() { - //my1dHyp.Nullify(); - //_pyHypothesis::Flush(); + // as creation of 1D hyp was written later then it's edition, + // we need to find all it's edition calls and process them + list< Handle(_pyCommand) >::iterator cmd = myArgCommands.begin(); + _pyID prevNewName; + for ( cmd = myArgCommands.begin(); cmd != myArgCommands.end(); ++cmd ) + { + const _pyID& hyp1dID = (*cmd)->GetArg( 1 ); + if ( hyp1dID.IsEmpty() ) continue; + + Handle(_pyHypothesis) hyp1d = theGen->FindHyp( hyp1dID ); + + // make a new name for 1D hyp = "HypType" + "_Distribution" + _pyID newName; + if ( hyp1d.IsNull() ) // apparently hypId changed at study restoration + { + if ( prevNewName.IsEmpty() ) continue; + newName = prevNewName; + } + else + { + if ( hyp1d->IsWrapped() ) { + newName = hyp1d->GetCreationCmd()->GetMethod(); + } + else { + TCollection_AsciiString hypTypeQuoted = hyp1d->GetCreationCmd()->GetArg(1); + newName = hypTypeQuoted.SubString( 2, hypTypeQuoted.Length() - 1 ); + } + newName += "_Distribution"; + prevNewName = newName; + + hyp1d->GetCreationCmd()->SetResultValue( newName ); + } + list< Handle(_pyCommand) >& cmds = theGen->GetCommands(); + list< Handle(_pyCommand) >::iterator cmdIt = cmds.begin(); + for ( ; cmdIt != cmds.end(); ++cmdIt ) { + const _pyID& objID = (*cmdIt)->GetObject(); + if ( objID == hyp1dID ) { + if ( !hyp1d.IsNull() ) + { + hyp1d->Process( *cmdIt ); + hyp1d->GetCreationCmd()->AddDependantCmd( *cmdIt ); + } + ( *cmdIt )->SetObject( newName ); + } + } + // Set new hyp name to SetLayerDistribution(hyp1dID) cmd + (*cmd)->SetArg( 1, newName ); + } } //================================================================================ @@ -1675,11 +2845,11 @@ void _pyLayerDistributionHypo::Flush() bool _pyNumberOfSegmentsHyp::Addition2Creation( const Handle(_pyCommand)& theCmd, const _pyID& theMesh) { - if ( IsWrappable( theMesh ) && myArgs.Length() > 1 ) { + if ( IsWrappable( theMesh ) && myCurCrMethod->myArgs.size() > 1 ) { // scale factor (2-nd arg) is provided: clear SetDistrType(1) command bool scaleDistrType = false; - list::reverse_iterator cmd = myUnknownCommands.rbegin(); - for ( ; cmd != myUnknownCommands.rend(); ++cmd ) { + list::reverse_iterator cmd = myUnusedCommands.rbegin(); + for ( ; cmd != myUnusedCommands.rend(); ++cmd ) { if ( (*cmd)->GetMethod() == "SetDistrType" ) { if ( (*cmd)->GetArg( 1 ) == "1" ) { scaleDistrType = true; @@ -1687,7 +2857,13 @@ bool _pyNumberOfSegmentsHyp::Addition2Creation( const Handle(_pyCommand)& theCmd } else if ( !scaleDistrType ) { // distribution type changed: remove scale factor from args - myArgs.Remove( 2, myArgs.Length() ); + TType2CrMethod::iterator type2meth = myAlgoType2CreationMethod.begin(); + for ( ; type2meth != myAlgoType2CreationMethod.end(); ++type2meth ) + { + CreationMethod& crMethod = type2meth->second; + if ( crMethod.myArgs.size() == 2 ) + crMethod.myArgs.pop_back(); + } break; } } @@ -1705,14 +2881,16 @@ bool _pyNumberOfSegmentsHyp::Addition2Creation( const Handle(_pyCommand)& theCmd void _pyNumberOfSegmentsHyp::Flush() { // find number of the last SetDistrType() command - list::reverse_iterator cmd = myUnknownCommands.rbegin(); + list::reverse_iterator cmd = myUnusedCommands.rbegin(); int distrTypeNb = 0; - for ( ; !distrTypeNb && cmd != myUnknownCommands.rend(); ++cmd ) + for ( ; !distrTypeNb && cmd != myUnusedCommands.rend(); ++cmd ) if ( (*cmd)->GetMethod() == "SetDistrType" ) distrTypeNb = (*cmd)->GetOrderNb(); + else if (IsWrapped() && (*cmd)->GetMethod() == "SetObjectEntry" ) + (*cmd)->Clear(); // clear commands before the last SetDistrType() - list * cmds[2] = { &myArgCommands, &myUnknownCommands }; + list * cmds[2] = { &myArgCommands, &myUnusedCommands }; for ( int i = 0; i < 2; ++i ) { set uniqueMethods; list & cmdList = *cmds[i]; @@ -1740,7 +2918,7 @@ void _pyNumberOfSegmentsHyp::Flush() * \retval bool - false if the command cant be converted */ //================================================================================ - + bool _pySegmentLengthAroundVertexHyp::Addition2Creation( const Handle(_pyCommand)& theCmd, const _pyID& theMeshID) { @@ -1764,8 +2942,8 @@ bool _pySegmentLengthAroundVertexHyp::Addition2Creation( const Handle(_pyCommand theCmd->SetArg( 1, geom ); // set vertex as a second arg - if ( myArgs.Length() < 1) myArgs.Append( "1" ); // :( - myArgs.Append( vertex ); + if ( myCurCrMethod->myArgs.size() < 1) setCreationArg( 1, "1" ); // :( + setCreationArg( 2, vertex ); // mesh.AddHypothesis(vertex, SegmentLengthAroundVertex) --> // theMeshID.LengthNearVertex( length, vertex ) @@ -1791,11 +2969,11 @@ _pyAlgorithm::_pyAlgorithm(const Handle(_pyCommand)& theCreationCmd) /*! * \brief Convert the command adding an algorithm to mesh * \param theCmd - The command like mesh.AddHypothesis( geom, algo ) - * \param theMesh - The mesh needing this algo + * \param theMesh - The mesh needing this algo * \retval bool - false if the command cant be converted */ //================================================================================ - + bool _pyAlgorithm::Addition2Creation( const Handle(_pyCommand)& theCmd, const _pyID& theMeshID) { @@ -1867,12 +3045,20 @@ const TCollection_AsciiString & _pyCommand::GetResultValue() { if ( GetBegPos( RESULT_IND ) == UNKNOWN ) { - int begPos = myString.Location( "=", 1, Length() ); - if ( begPos ) - myRes = GetWord( myString, begPos, false ); - else - begPos = EMPTY; - SetBegPos( RESULT_IND, begPos ); + SetBegPos( RESULT_IND, EMPTY ); + int begPos, endPos = myString.Location( "=", 1, Length() ); + if ( endPos ) + { + begPos = 1; + while ( begPos < endPos && isspace( myString.Value( begPos ))) ++begPos; + if ( begPos < endPos ) + { + SetBegPos( RESULT_IND, begPos ); + --endPos; + while ( begPos < endPos && isspace( myString.Value( endPos ))) --endPos; + myRes = myString.SubString( begPos, endPos ); + } + } } return myRes; } @@ -1902,22 +3088,25 @@ const int _pyCommand::GetNbResultValues() //================================================================================ /*! * \brief Return substring of python command looking like - * ResultValue1 , ResultValue1,... = Obj.Meth() with res index + * ResultValue1 , ResultValue2,... = Obj.Meth() with res index * \retval const TCollection_AsciiString & - ResultValue with res index substring */ //================================================================================ -const TCollection_AsciiString & _pyCommand::GetResultValue(int res) +TCollection_AsciiString _pyCommand::GetResultValue(int res) { int begPos = 1; - int Nb=0; + if ( SkipSpaces( myString, begPos ) && myString.Value( begPos ) == '[' ) + ++begPos; // skip [, else the whole list is returned int endPos = myString.Location( "=", 1, Length() ); + int Nb=0; while ( begPos < endPos) { - myRes = GetWord( myString, begPos, true ); - begPos = begPos + myRes.Length(); + _AString result = GetWord( myString, begPos, true ); + begPos = begPos + result.Length(); Nb++; - if(res == Nb){ - myRes.RemoveAll('[');myRes.RemoveAll(']'); - return myRes; + if(res == Nb) { + result.RemoveAll('['); + result.RemoveAll(']'); + return result; } if(Nb>res) break; @@ -1944,18 +3133,38 @@ const TCollection_AsciiString & _pyCommand::GetObject() int nb1 = 0; // number of ' character at the left of = int nb2 = 0; // number of " character at the left of = for ( int i = 1; i < begPos-1; i++ ) { - if ( IsEqual(myString.Value( i ), "'" ) ) - nb1 += 1; - else if ( IsEqual( myString.Value( i ), '"' ) ) - nb2 += 1; + if ( myString.Value( i )=='\'' ) + nb1 += 1; + else if ( myString.Value( i )=='"' ) + nb2 += 1; } // if number of ' or " is not divisible by 2, // then get an object at the start of the command if ( nb1 % 2 != 0 || nb2 % 2 != 0 ) - begPos = 1; + begPos = 1; } - // store myObj = GetWord( myString, begPos, true ); + // check if object is complex, + // so far consider case like "smesh.smesh.Method()" + if ( int bracketPos = myString.Location( "(", begPos, Length() )) { + //if ( bracketPos==0 ) bracketPos = Length(); + int dotPos = begPos+myObj.Length(); + while ( dotPos+1 < bracketPos ) { + if ( int pos = myString.Location( ".", dotPos+1, bracketPos )) + dotPos = pos; + else + break; + } + if ( dotPos > begPos+myObj.Length() ) + myObj = myString.SubString( begPos, dotPos-1 ); + } + // 1st word after '=' is an object + // else // no method -> no object + // { + // myObj.Clear(); + // begPos = EMPTY; + // } + // store SetBegPos( OBJECT_IND, begPos ); } //SCRUTE(myObj); @@ -1999,32 +3208,71 @@ const TCollection_AsciiString & _pyCommand::GetArg( int index ) { if ( GetBegPos( ARG1_IND ) == UNKNOWN ) { - // find all args - int begPos = GetBegPos( METHOD_IND ) + myMeth.Length(); - if ( begPos < 1 ) - begPos = myString.Location( "(", 1, Length() ) + 1; - - int i = 0, prevLen = 0, nbNestings = 0; - while ( begPos != EMPTY ) { - begPos += prevLen; - if( myString.Value( begPos ) == '(' ) - nbNestings++; - // check if we are looking at the closing parenthesis - while ( begPos <= Length() && isspace( myString.Value( begPos ))) - ++begPos; - if ( begPos > Length() ) - break; - if ( myString.Value( begPos ) == ')' ) { - nbNestings--; - if( nbNestings == 0 ) - break; + // Find all args + + int pos = GetBegPos( METHOD_IND ) + myMeth.Length(); + if ( pos < 1 ) + pos = myString.Location( "(", 1, Length() ); + else + --pos; + + // we are at or before '(', skip it if present + if ( pos > 0 ) { + while ( pos <= Length() && myString.Value( pos ) != '(' ) ++pos; + if ( pos > Length() ) + pos = 0; + } + if ( pos < 1 ) { + SetBegPos( ARG1_IND, 0 ); // even no '(' + return theEmptyString; + } + ++pos; + + list< TCollection_AsciiString > separatorStack( 1, ",)"); + bool ignoreNesting = false; + int prevPos = pos; + while ( pos <= Length() ) + { + const char chr = myString.Value( pos ); + + if ( separatorStack.back().Location( chr, 1, separatorStack.back().Length())) + { + if ( separatorStack.size() == 1 ) // a comma dividing args or a terminal ')' found + { + while ( pos-1 >= prevPos && isspace( myString.Value( prevPos ))) + ++prevPos; + TCollection_AsciiString arg; + if ( pos-1 >= prevPos ) { + arg = myString.SubString( prevPos, pos-1 ); + arg.RightAdjust(); // remove spaces + arg.LeftAdjust(); + } + if ( !arg.IsEmpty() || chr == ',' ) + { + SetBegPos( ARG1_IND + myArgs.Length(), prevPos ); + myArgs.Append( arg ); + } + if ( chr == ')' ) + break; + prevPos = pos+1; + } + else // end of nesting args found + { + separatorStack.pop_back(); + ignoreNesting = false; + } } - myArgs.Append( GetWord( myString, begPos, true, true )); - SetBegPos( ARG1_IND + i, begPos ); - prevLen = myArgs.Last().Length(); - if ( prevLen == 0 ) - myArgs.Remove( myArgs.Length() ); // no more args - i++; + else if ( !ignoreNesting ) + { + switch ( chr ) { + case '(' : separatorStack.push_back(")"); break; + case '[' : separatorStack.push_back("]"); break; + case '\'': separatorStack.push_back("'"); ignoreNesting=true; break; + case '"' : separatorStack.push_back("\""); ignoreNesting=true; break; + default:; + } + } + ++pos; } } if ( myArgs.Length() < index ) @@ -2074,9 +3322,19 @@ TCollection_AsciiString _pyCommand::GetWord( const TCollection_AsciiString & the return theEmptyString; // no word found // end end = beg + 1; - while ( end <= theString.Length() && isWord( theString.Value( end ), dotIsWord)) - ++end; - --end; + char begChar = theString.Value( beg ); + if ( begChar == '"' || begChar == '\'' || begChar == '[') { + char endChar = ( begChar == '[' ) ? ']' : begChar; + // end is at the corresponding quoting mark or bracket + while ( end < theString.Length() && + ( theString.Value( end ) != endChar || theString.Value( end-1 ) == '\\')) + ++end; + } + else { + while ( end <= theString.Length() && isWord( theString.Value( end ), dotIsWord)) + ++end; + --end; + } } else { // search backward // end @@ -2085,23 +3343,77 @@ TCollection_AsciiString _pyCommand::GetWord( const TCollection_AsciiString & the if ( end == 0 ) return theEmptyString; // no word found beg = end - 1; - while ( beg > 0 && isWord( theString.Value( beg ), dotIsWord)) - --beg; - ++beg; + char endChar = theString.Value( end ); + if ( endChar == '"' || endChar == '\'' || endChar == ']') { + char begChar = ( endChar == ']' ) ? '[' : endChar; + // beg is at the corresponding quoting mark + while ( beg > 1 && + ( theString.Value( beg ) != begChar || theString.Value( beg-1 ) == '\\')) + --beg; + } + else { + while ( beg > 0 && isWord( theString.Value( beg ), dotIsWord)) + --beg; + ++beg; + } } theStartPos = beg; //cout << theString << " ---- " << beg << " - " << end << endl; return theString.SubString( beg, end ); } +//================================================================================ +/*! + * \brief Returns true if the string looks like a study entry + */ +//================================================================================ + +bool _pyCommand::IsStudyEntry( const TCollection_AsciiString& str ) +{ + if ( str.Length() < 5 ) return false; + + int nbColons = 0, isColon; + for ( int i = 1; i <= str.Length(); ++i ) + { + char c = str.Value(i); + if (!( isColon = (c == ':')) && ( c < '0' || c > '9' )) + return false; + nbColons += isColon; + } + return nbColons > 2 && str.Length()-nbColons > 2; +} + +//================================================================================ +/*! + * \brief Finds entries in a sting + */ +//================================================================================ + +std::list< _pyID > _pyCommand::GetStudyEntries( const TCollection_AsciiString& str ) +{ + std::list< _pyID > resList; + int pos = 0; + while ( ++pos <= str.Length() ) + { + if ( !isdigit( str.Value( pos ))) continue; + if ( pos != 1 && ( isalpha( str.Value( pos-1 ) || str.Value( pos-1 ) == ':'))) continue; + + int end = pos; + while ( ++end <= str.Length() && ( isdigit( str.Value( end )) || str.Value( end ) == ':' )); + _pyID entry = str.SubString( pos, end-1 ); + pos = end; + if ( IsStudyEntry( entry )) + resList.push_back( entry ); + } + return resList; +} + //================================================================================ /*! * \brief Look for position where not space char is - * \param theString - The string + * \param theString - The string * \param thePos - The position to search from and which returns result * \retval bool - false if there are only space after thePos in theString - * - * */ //================================================================================ @@ -2141,7 +3453,7 @@ void _pyCommand::SetPart(int thePartIndex, const TCollection_AsciiString& thePar case METHOD_IND: seperator = "()"; break; default:; } - } + } myString.Remove( pos, theOldPart.Length() ); if ( !seperator.IsEmpty() ) myString.Insert( pos , seperator ); @@ -2215,6 +3527,30 @@ void _pyCommand::RemoveArgs() myBegPos.Remove( ARG1_IND, myBegPos.Length() ); } +//================================================================================ +/*! + * \brief Comment a python command + */ +//================================================================================ + +void _pyCommand::Comment() +{ + if ( IsEmpty() ) return; + + int i = 1; + while ( i <= Length() && isspace( myString.Value(i) )) ++i; + if ( i <= Length() ) + { + myString.Insert( i, "#" ); + for ( int iPart = 0; iPart < myBegPos.Length(); ++iPart ) + { + int begPos = GetBegPos( iPart ); + if ( begPos != UNKNOWN ) + SetBegPos( iPart, begPos + 1 ); + } + } +} + //================================================================================ /*! * \brief Set dependent commands after this one @@ -2280,6 +3616,49 @@ bool _pyCommand::AddAccessorMethod( _pyID theObjectID, const char* theAcsMethod return added; } +//================================================================================ +/*! + * \brief Creates pyObject + */ +//================================================================================ + +_pyObject::_pyObject(const Handle(_pyCommand)& theCreationCmd, const _pyID& theID) + : myID(theID), myCreationCmd(theCreationCmd), myIsPublished(false) +{ + setID( theID ); +} + +//================================================================================ +/*! + * \brief Set up myID and myIsPublished + */ +//================================================================================ + +void _pyObject::setID(const _pyID& theID) +{ + myID = theID; + myIsPublished = !theGen->IsNotPublished( GetID() ); +} + +//================================================================================ +/*! + * \brief Clear myCreationCmd and myProcessedCmds + */ +//================================================================================ + +void _pyObject::ClearCommands() +{ + if ( !CanClear() ) + return; + + if ( !myCreationCmd.IsNull() ) + myCreationCmd->Clear(); + + list< Handle(_pyCommand) >::iterator cmd = myProcessedCmds.begin(); + for ( ; cmd != myProcessedCmds.end(); ++cmd ) + (*cmd)->Clear(); +} + //================================================================================ /*! * \brief Return method name giving access to an interaface object wrapped by python class @@ -2307,14 +3686,69 @@ _pyID _pyObject::FatherID(const _pyID & childID) //================================================================================ /*! - * \brief FilterManager creates only if at least one command invoked + * \brief SelfEraser erases creation command if no more it's commands invoked */ //================================================================================ -_pyFilterManager::_pyFilterManager(const Handle(_pyCommand)& theCreationCmd): - _pyObject( theCreationCmd ), - myCmdCount( 0 ) +void _pySelfEraser::Flush() { + if ( GetNbCalls() == 0 ) + GetCreationCmd()->Clear(); +} + +//================================================================================ +/*! + * \brief _pySubMesh constructor + */ +//================================================================================ + +_pySubMesh::_pySubMesh(const Handle(_pyCommand)& theCreationCmd): + _pyObject(theCreationCmd) +{ + myMesh = ObjectToMesh( theGen->FindObject( theCreationCmd->GetObject() )); +} + +//================================================================================ +/*! + * \brief Return true if a sub-mesh can be used as argument of the given method + */ +//================================================================================ + +bool _pySubMesh::CanBeArgOfMethod(const _AString& theMethodName) +{ + // names of all methods where a sub-mesh can be used as argument + static TStringSet methods; + if ( methods.empty() ) { + const char * names[] = { + // methods of SMESH_Gen + "CopyMesh", + // methods of SMESH_Group + "AddFrom", + // methods of SMESH_Measurements + "MinDistance", + // methods of SMESH_Mesh + "ExportPartToMED","ExportCGNS","ExportPartToDAT","ExportPartToUNV","ExportPartToSTL", + "RemoveSubMesh", + // methods of SMESH_MeshEditor + "ReorientObject","Reorient2D","TriToQuadObject","QuadToTriObject","SplitQuadObject", + "SplitVolumesIntoTetra","SmoothObject","SmoothParametricObject","ConvertFromQuadraticObject", + "RotationSweepObject","RotationSweepObjectMakeGroups","RotationSweepObject1D", + "RotationSweepObject1DMakeGroups","RotationSweepObject2D","RotationSweepObject2DMakeGroups", + "ExtrusionSweepObject","ExtrusionSweepObjectMakeGroups","ExtrusionSweepObject0D", + "ExtrusionSweepObject0DMakeGroups","ExtrusionSweepObject1D","ExtrusionSweepObject2D", + "ExtrusionSweepObject1DMakeGroups","ExtrusionSweepObject2DMakeGroups", + "ExtrusionAlongPathObjX","ExtrusionAlongPathObject","ExtrusionAlongPathObjectMakeGroups", + "ExtrusionAlongPathObject1D","ExtrusionAlongPathObject1DMakeGroups", + "ExtrusionAlongPathObject2D","ExtrusionAlongPathObject2DMakeGroups","MirrorObject", + "MirrorObjectMakeGroups","MirrorObjectMakeMesh","TranslateObject","Scale", + "TranslateObjectMakeGroups","TranslateObjectMakeMesh","ScaleMakeGroups","ScaleMakeMesh", + "RotateObject","RotateObjectMakeGroups","RotateObjectMakeMesh","FindCoincidentNodesOnPart", + "FindCoincidentNodesOnPartBut","FindEqualElements","FindAmongElementsByPoint", + "MakeBoundaryMesh", + "" }; // <- mark of end + methods.Insert( names ); + } + return methods.Contains( theMethodName ); } //================================================================================ @@ -2323,59 +3757,452 @@ _pyFilterManager::_pyFilterManager(const Handle(_pyCommand)& theCreationCmd): */ //================================================================================ -void _pyFilterManager::Process( const Handle(_pyCommand)& /*theCommand*/) +void _pySubMesh::Process( const Handle(_pyCommand)& theCommand ) +{ + _pyObject::Process(theCommand); // count calls of Process() + GetCreationCmd()->AddDependantCmd( theCommand ); +} + +//================================================================================ +/*! + * \brief Move creation command depending on invoked commands + */ +//================================================================================ + +void _pySubMesh::Flush() +{ + if ( GetNbCalls() == 0 ) // move to the end of all commands + theGen->GetLastCommand()->AddDependantCmd( GetCreationCmd() ); + else if ( !myCreator.IsNull() ) + // move to be just after creator + myCreator->GetCreationCmd()->AddDependantCmd( GetCreationCmd() ); +} + +//================================================================================ +/*! + * \brief Creates _pyGroup + */ +//================================================================================ + +_pyGroup::_pyGroup(const Handle(_pyCommand)& theCreationCmd, const _pyID & id) + :_pySubMesh(theCreationCmd) { - myCmdCount++; + if ( !id.IsEmpty() ) + setID( id ); + + myCanClearCreationCmd = true; + + const _AString& method = theCreationCmd->GetMethod(); + if ( method == "CreateGroup" ) // CreateGroup() --> CreateEmptyGroup() + { + theCreationCmd->SetMethod( "CreateEmptyGroup" ); + } + // ---------------------------------------------------------------------- + else if ( method == "CreateGroupFromGEOM" ) // (type, name, grp) + { + _pyID geom = theCreationCmd->GetArg( 3 ); + // VSR 24/12/2010. PAL21106: always use GroupOnGeom() function on dump + // next if(){...} section is commented + //if ( sameGroupType( geom, theCreationCmd->GetArg( 1 )) ) { // --> Group(geom) + // theCreationCmd->SetMethod( "Group" ); + // theCreationCmd->RemoveArgs(); + // theCreationCmd->SetArg( 1, geom ); + //} + //else { + // ------------------------->>>>> GroupOnGeom( geom, name, typ ) + _pyID type = theCreationCmd->GetArg( 1 ); + _pyID name = theCreationCmd->GetArg( 2 ); + theCreationCmd->SetMethod( "GroupOnGeom" ); + theCreationCmd->RemoveArgs(); + theCreationCmd->SetArg( 1, geom ); + theCreationCmd->SetArg( 2, name ); + theCreationCmd->SetArg( 3, type ); + //} + } + else if ( method == "CreateGroupFromFilter" ) + { + // -> GroupOnFilter(typ, name, aFilter0x4743dc0 -> aFilter_1) + theCreationCmd->SetMethod( "GroupOnFilter" ); + + _pyID filterID = theCreationCmd->GetArg(3); + Handle(_pyFilter) filter = Handle(_pyFilter)::DownCast( theGen->FindObject( filterID )); + if ( !filter.IsNull()) + { + if ( !filter->GetNewID().IsEmpty() ) + theCreationCmd->SetArg( 3, filter->GetNewID() ); + filter->AddUser( this ); + } + myFilter = filter; + } + else + { + // theCreationCmd does something else apart from creation of this group + // and thus it can't be cleared if this group is removed + myCanClearCreationCmd = false; + } } //================================================================================ /*! - * \brief Clear creatin command if no commands invoked + * \brief To convert creation of a group by filter */ //================================================================================ -void _pyFilterManager::Flush() +void _pyGroup::Process( const Handle(_pyCommand)& theCommand) { - if ( !myCmdCount ) + // Convert the following set of commands into mesh.MakeGroupByFilter(groupName, theFilter) + // group = mesh.CreateEmptyGroup( elemType, groupName ) + // aFilter.SetMesh(mesh) + // nbAdd = group.AddFrom( aFilter ) + Handle(_pyFilter) filter; + if ( theCommand->GetMethod() == "AddFrom" ) + { + _pyID idSource = theCommand->GetArg(1); + // check if idSource is a filter + filter = Handle(_pyFilter)::DownCast( theGen->FindObject( idSource )); + if ( !filter.IsNull() ) + { + // find aFilter.SetMesh(mesh) to clear it, it should be just before theCommand + list< Handle(_pyCommand) >::reverse_iterator cmdIt = theGen->GetCommands().rbegin(); + while ( *cmdIt != theCommand ) ++cmdIt; + while ( (*cmdIt)->GetOrderNb() != 1 ) + { + const Handle(_pyCommand)& setMeshCmd = *(++cmdIt); + if ((setMeshCmd->GetObject() == idSource || + setMeshCmd->GetObject() == filter->GetNewID() ) + && + setMeshCmd->GetMethod() == "SetMesh") + { + setMeshCmd->Clear(); + break; + } + } + // replace 3 commands by one + theCommand->Clear(); + const Handle(_pyCommand)& makeGroupCmd = GetCreationCmd(); + TCollection_AsciiString name = makeGroupCmd->GetArg( 2 ); + if ( !filter->GetNewID().IsEmpty() ) + idSource = filter->GetNewID(); + makeGroupCmd->SetMethod( "MakeGroupByFilter" ); + makeGroupCmd->SetArg( 1, name ); + makeGroupCmd->SetArg( 2, idSource ); + } + } + else if ( theCommand->GetMethod() == "SetFilter" ) + { + // set new name of a filter or clear the command if the same filter is set + _pyID filterID = theCommand->GetArg(1); + filter = Handle(_pyFilter)::DownCast( theGen->FindObject( filterID )); + if ( !myFilter.IsNull() && filter == myFilter ) + theCommand->Clear(); + else if ( !filter.IsNull() && !filter->GetNewID().IsEmpty() ) + theCommand->SetArg( 1, filter->GetNewID() ); + myFilter = filter; + } + else if ( theCommand->GetMethod() == "GetFilter" ) + { + // GetFilter() returns a filter with other ID, make myFilter process + // calls of the returned filter + if ( !myFilter.IsNull() ) + { + theGen->SetProxyObject( theCommand->GetResultValue(), myFilter ); + theCommand->Clear(); + } + } + + if ( !filter.IsNull() ) + filter->AddUser( this ); + + theGen->AddMeshAccessorMethod( theCommand ); +} + +//================================================================================ +/*! + * \brief Prevent clearing "DoubleNode...() command if a group created by it is removed + * + * + */ +//================================================================================ + +void _pyGroup::Flush() +{ + if ( !theGen->IsToKeepAllCommands() && + myCreationCmd && !myCanClearCreationCmd ) + { + myCreationCmd.Nullify(); // this way myCreationCmd won't be cleared + } +} + +//================================================================================ +/*! + * \brief Constructor of _pyFilter + */ +//================================================================================ + +_pyFilter::_pyFilter(const Handle(_pyCommand)& theCreationCmd, const _pyID& newID/*=""*/) + :_pyObject(theCreationCmd), myNewID( newID ) +{ +} + +//================================================================================ +/*! + * \brief To convert creation of a filter by criteria and + * to replace an old name by a new one + */ +//================================================================================ + +void _pyFilter::Process( const Handle(_pyCommand)& theCommand) +{ + if ( theCommand->GetObject() == GetID() ) + _pyObject::Process(theCommand); // count commands + + if ( !myNewID.IsEmpty() ) + theCommand->SetObject( myNewID ); + + // Convert the following set of commands into smesh.GetFilterFromCriteria(criteria) + // aFilter0x2aaab0487080 = aFilterManager.CreateFilter() + // aFilter0x2aaab0487080.SetCriteria(aCriteria) + if ( GetNbCalls() == 1 && // none method was called before this SetCriteria() call + theCommand->GetMethod() == "SetCriteria") + { + // aFilter.SetCriteria(aCriteria) -> + // aFilter = smesh.GetFilterFromCriteria(criteria) + if ( myNewID.IsEmpty() ) + theCommand->SetResultValue( GetID() ); + else + theCommand->SetResultValue( myNewID ); + theCommand->SetObject( SMESH_2smeshpy::GenName() ); + theCommand->SetMethod( "GetFilterFromCriteria" ); + + // Clear aFilterManager.CreateFilter() GetCreationCmd()->Clear(); + } + else if ( theCommand->GetMethod() == "SetMesh" ) + { + if ( myMesh == theCommand->GetArg( 1 )) + theCommand->Clear(); + else + myMesh = theCommand->GetArg( 1 ); + theGen->AddMeshAccessorMethod( theCommand ); + } } +//================================================================================ +/*! + * \brief Set new filter name to the creation command + */ +//================================================================================ + +void _pyFilter::Flush() +{ + if ( !myNewID.IsEmpty() && !GetCreationCmd()->IsEmpty() ) + GetCreationCmd()->SetResultValue( myNewID ); +} //================================================================================ /*! - * \brief SubMesh creation can be moved to the end of engine commands + * \brief Return true if all my users can be cleared */ //================================================================================ -_pySubMesh::_pySubMesh(const Handle(_pyCommand)& theCreationCmd): - _pyObject( theCreationCmd ), - myCmdCount( 0 ) +bool _pyFilter::CanClear() { + list< Handle(_pyObject) >::iterator obj = myUsers.begin(); + for ( ; obj != myUsers.end(); ++obj ) + if ( !(*obj)->CanClear() ) + return false; + + return true; } //================================================================================ /*! - * \brief count invoked commands + * \brief Reads _pyHypothesis'es from resource files of mesher Plugins */ //================================================================================ -void _pySubMesh::Process( const Handle(_pyCommand)& theCommand ) +_pyHypothesisReader::_pyHypothesisReader() { - myCmdCount++; - GetCreationCmd()->AddDependantCmd( theCommand ); + // Get paths to xml files of plugins + vector< string > xmlPaths; + string sep; + if ( const char* meshersList = getenv("SMESH_MeshersList") ) + { + string meshers = meshersList, plugin; + string::size_type from = 0, pos; + while ( from < meshers.size() ) + { + // cut off plugin name + pos = meshers.find( ':', from ); + if ( pos != string::npos ) + plugin = meshers.substr( from, pos-from ); + else + plugin = meshers.substr( from ), pos = meshers.size(); + from = pos + 1; + + // get PLUGIN_ROOT_DIR path + string rootDirVar, pluginSubDir = plugin; + if ( plugin == "StdMeshers" ) + rootDirVar = "SMESH", pluginSubDir = "smesh"; + else + for ( pos = 0; pos < plugin.size(); ++pos ) + rootDirVar += toupper( plugin[pos] ); + rootDirVar += "_ROOT_DIR"; + + const char* rootDir = getenv( rootDirVar.c_str() ); + if ( !rootDir || strlen(rootDir) == 0 ) + { + rootDirVar = plugin + "_ROOT_DIR"; // HexoticPLUGIN_ROOT_DIR + rootDir = getenv( rootDirVar.c_str() ); + if ( !rootDir || strlen(rootDir) == 0 ) continue; + } + + // get a separator from rootDir + for ( pos = strlen( rootDir )-1; pos >= 0 && sep.empty(); --pos ) + if ( rootDir[pos] == '/' || rootDir[pos] == '\\' ) + { + sep = rootDir[pos]; + break; + } +#ifdef WNT + if (sep.empty() ) sep = "\\"; +#else + if (sep.empty() ) sep = "/"; +#endif + + // get a path to resource file + string xmlPath = rootDir; + if ( xmlPath[ xmlPath.size()-1 ] != sep[0] ) + xmlPath += sep; + xmlPath += "share" + sep + "salome" + sep + "resources" + sep; + for ( pos = 0; pos < pluginSubDir.size(); ++pos ) + xmlPath += tolower( pluginSubDir[pos] ); + xmlPath += sep + plugin + ".xml"; + bool fileOK; +#ifdef WNT + fileOK = (GetFileAttributes(xmlPath.c_str()) != INVALID_FILE_ATTRIBUTES); +#else + fileOK = (access(xmlPath.c_str(), F_OK) == 0); +#endif + if ( fileOK ) + xmlPaths.push_back( xmlPath ); + } + } + + // Read xml files + LDOMParser xmlParser; + for ( size_t i = 0; i < xmlPaths.size(); ++i ) + { + bool error = xmlParser.parse( xmlPaths[i].c_str() ); + if ( error ) + { + _AString data; + INFOS( xmlParser.GetError(data) ); + continue; + } + // + // + // Regular_1D=Segment() + // LocalLength=LocalLength(SetLength(1),,SetPrecision(1)) + // + LDOM_Document xmlDoc = xmlParser.getDocument(); + LDOM_NodeList algoNodeList = xmlDoc.getElementsByTagName( "algorithm" ); + for ( int i = 0; i < algoNodeList.getLength(); ++i ) + { + LDOM_Node algoNode = algoNodeList.item( i ); + LDOM_Element& algoElem = (LDOM_Element&) algoNode; + LDOM_NodeList pyAlgoNodeList = algoElem.getElementsByTagName( "algo" ); + if ( pyAlgoNodeList.getLength() < 1 ) continue; + + _AString text, algoType, method, arg; + for ( int iA = 0; iA < pyAlgoNodeList.getLength(); ++iA ) + { + LDOM_Node pyAlgoNode = pyAlgoNodeList.item( iA ); + LDOM_Node textNode = pyAlgoNode.getFirstChild(); + text = textNode.getNodeValue(); + Handle(_pyCommand) algoCmd = new _pyCommand( text ); + algoType = algoCmd->GetResultValue(); + method = algoCmd->GetMethod(); + arg = algoCmd->GetArg(1); + if ( !algoType.IsEmpty() && !method.IsEmpty() ) + { + Handle(_pyAlgorithm) algo = new _pyAlgorithm( algoCmd ); + algo->SetConvMethodAndType( method, algoType ); + if ( !arg.IsEmpty() ) + algo->setCreationArg( 1, arg ); + + myType2Hyp[ algoType ] = algo; + break; + } + } + if ( algoType.IsEmpty() ) continue; + + LDOM_NodeList pyHypoNodeList = algoElem.getElementsByTagName( "hypo" ); + _AString hypType; + Handle( _pyHypothesis ) hyp; + for ( int iH = 0; iH < pyHypoNodeList.getLength(); ++iH ) + { + LDOM_Node pyHypoNode = pyHypoNodeList.item( iH ); + LDOM_Node textNode = pyHypoNode.getFirstChild(); + text = textNode.getNodeValue(); + Handle(_pyCommand) hypoCmd = new _pyCommand( text ); + hypType = hypoCmd->GetResultValue(); + method = hypoCmd->GetMethod(); + if ( !hypType.IsEmpty() && !method.IsEmpty() ) + { + map<_AString, Handle(_pyHypothesis)>::iterator type2hyp = myType2Hyp.find( hypType ); + if ( type2hyp == myType2Hyp.end() ) + hyp = new _pyHypothesis( hypoCmd ); + else + hyp = type2hyp->second; + hyp->SetConvMethodAndType( method, algoType ); + for ( int iArg = 1; iArg <= hypoCmd->GetNbArgs(); ++iArg ) + { + _pyCommand argCmd( hypoCmd->GetArg( iArg )); + _AString argMethod = argCmd.GetMethod(); + _AString argNbText = argCmd.GetArg( 1 ); + if ( argMethod.IsEmpty() && !argCmd.IsEmpty() ) + hyp->setCreationArg( 1, argCmd.GetString() ); // e.g. Parameters(smesh.SIMPLE) + else + hyp->AddArgMethod( argMethod, + argNbText.IsIntegerValue() ? argNbText.IntegerValue() : 1 ); + } + myType2Hyp[ hypType ] = hyp; + } + } + } + } } //================================================================================ /*! - * \brief Clear creatin command if no commands invoked + * \brief Returns a new hypothesis initialized according to the read information */ //================================================================================ -void _pySubMesh::Flush() +Handle(_pyHypothesis) +_pyHypothesisReader::GetHypothesis(const _AString& hypType, + const Handle(_pyCommand)& creationCmd) const { - if ( !myCmdCount ) // move to the end of all commands - theGen->GetLastCommand()->AddDependantCmd( GetCreationCmd() ); - else if ( !myCreator.IsNull() ) - // move to be just after creator - myCreator->GetCreationCmd()->AddDependantCmd( GetCreationCmd() ); + Handle(_pyHypothesis) resHyp, sampleHyp; + + map<_AString, Handle(_pyHypothesis)>::const_iterator type2hyp = myType2Hyp.find( hypType ); + if ( type2hyp != myType2Hyp.end() ) + sampleHyp = type2hyp->second; + + if ( sampleHyp.IsNull() ) + { + resHyp = new _pyHypothesis(creationCmd); + } + else + { + if ( sampleHyp->IsAlgo() ) + resHyp = new _pyAlgorithm( creationCmd ); + else + resHyp = new _pyHypothesis(creationCmd); + resHyp->Assign( sampleHyp, _pyID() ); + } + return resHyp; } diff --git a/src/SMESH_I/SMESH_2smeshpy.hxx b/src/SMESH_I/SMESH_2smeshpy.hxx index eac87deca..f92c285b4 100644 --- a/src/SMESH_I/SMESH_2smeshpy.hxx +++ b/src/SMESH_I/SMESH_2smeshpy.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_smesh.hxx // Created : Fri Nov 18 12:05:18 2005 // Author : Edward AGAPOV (eap) @@ -35,6 +36,10 @@ #include #include +#include + +#include +#include CORBA_CLIENT_HEADER(SALOMEDS) // =========================================================================================== /*! @@ -47,9 +52,6 @@ * * Everything here is for internal usage by SMESH_2smeshpy::ConvertScript() * declared in SMESH_PythonDump.hxx - * - * See comments to _pyHypothesis class to know how to assure convertion of a new - * type of hypothesis */ // =========================================================================================== @@ -68,19 +70,21 @@ class _pyMesh; class _pySubMesh; class _pyHypothesis; class _pyAlgorithm; -class _pyFilterManager; - -DEFINE_STANDARD_HANDLE (_pyCommand ,Standard_Transient); -DEFINE_STANDARD_HANDLE (_pyObject ,Standard_Transient); -DEFINE_STANDARD_HANDLE (_pyGen ,_pyObject); -DEFINE_STANDARD_HANDLE (_pyMesh ,_pyObject); -DEFINE_STANDARD_HANDLE (_pySubMesh ,_pyObject); -DEFINE_STANDARD_HANDLE (_pyMeshEditor,_pyObject); -DEFINE_STANDARD_HANDLE (_pyHypothesis,_pyObject); -DEFINE_STANDARD_HANDLE (_pyFilterManager,_pyObject); -DEFINE_STANDARD_HANDLE (_pyAlgorithm ,_pyHypothesis); +class _pyHypothesisReader; + +DEFINE_STANDARD_HANDLE (_pyCommand ,Standard_Transient); +DEFINE_STANDARD_HANDLE (_pyObject ,Standard_Transient); +DEFINE_STANDARD_HANDLE (_pyHypothesisReader,Standard_Transient); +DEFINE_STANDARD_HANDLE (_pyGen ,_pyObject); +DEFINE_STANDARD_HANDLE (_pyMesh ,_pyObject); +DEFINE_STANDARD_HANDLE (_pySubMesh ,_pyObject); +DEFINE_STANDARD_HANDLE (_pyGroup ,_pySubMesh); +DEFINE_STANDARD_HANDLE (_pyMeshEditor ,_pyObject); +DEFINE_STANDARD_HANDLE (_pyHypothesis ,_pyObject); +DEFINE_STANDARD_HANDLE (_pyAlgorithm ,_pyHypothesis); typedef TCollection_AsciiString _pyID; +typedef TCollection_AsciiString _AString; // =========================================================== /*! @@ -92,50 +96,54 @@ typedef TCollection_AsciiString _pyID; class _pyCommand: public Standard_Transient { int myOrderNb; //!< position within the script - TCollection_AsciiString myString; //!< command text - TCollection_AsciiString myRes, myObj, myMeth; //!< found parts of command + _AString myString; //!< command text + _AString myRes, myObj, myMeth; //!< found parts of command TColStd_SequenceOfAsciiString myArgs; //!< found arguments TColStd_SequenceOfInteger myBegPos; //!< where myRes, myObj, ... begin std::list< Handle(_pyCommand) > myDependentCmds; //!< commands that sould follow me in the script enum { UNKNOWN=-1, EMPTY=0, RESULT_IND, OBJECT_IND, METHOD_IND, ARG1_IND }; - int GetBegPos( int thePartIndex ); + int GetBegPos( int thePartIndex ); void SetBegPos( int thePartIndex, int thePosition ); - void SetPart( int thePartIndex, const TCollection_AsciiString& theNewPart, - TCollection_AsciiString& theOldPart); + void SetPart( int thePartIndex, const _AString& theNewPart, _AString& theOldPart); void FindAllArgs() { GetArg(1); } public: _pyCommand() {}; - _pyCommand( const TCollection_AsciiString& theString, int theNb ) + _pyCommand( const _AString& theString, int theNb=-1 ) : myString( theString ), myOrderNb( theNb ) {}; - TCollection_AsciiString & GetString() { return myString; } + _AString & GetString() { return myString; } int GetOrderNb() const { return myOrderNb; } void SetOrderNb( int theNb ) { myOrderNb = theNb; } + typedef void* TAddr; + TAddr GetAddress() const { return (void*) this; } int Length() { return myString.Length(); } - void Clear() { myString.Clear(); myBegPos.Clear(); } + void Clear() { myString.Clear(); myBegPos.Clear(); myArgs.Clear(); } bool IsEmpty() const { return myString.IsEmpty(); } - TCollection_AsciiString GetIndentation(); - const TCollection_AsciiString & GetResultValue(); + _AString GetIndentation(); + const _AString & GetResultValue(); const int GetNbResultValues(); - const TCollection_AsciiString & GetResultValue(int res); - const TCollection_AsciiString & GetObject(); - const TCollection_AsciiString & GetMethod(); - const TCollection_AsciiString & GetArg( int index ); + _AString GetResultValue(int res); + const _AString & GetObject(); + const _AString & GetMethod(); + const _AString & GetArg( int index ); int GetNbArgs() { FindAllArgs(); return myArgs.Length(); } - //Handle(TColStd_HSequenceOfAsciiString) GetArgs(); - void SetResultValue( const TCollection_AsciiString& theResult ) + bool MethodStartsFrom(const _AString& beg) + { GetMethod(); return ( myMeth.Location( beg, 1, myMeth.Length() ) == 1 ); } + void SetResultValue( const _AString& theResult ) { GetResultValue(); SetPart( RESULT_IND, theResult, myRes ); } - void SetObject(const TCollection_AsciiString& theObject) + void SetObject(const _AString& theObject) { GetObject(); SetPart( OBJECT_IND, theObject, myObj ); } - void SetMethod(const TCollection_AsciiString& theMethod) + void SetMethod(const _AString& theMethod) { GetMethod(); SetPart( METHOD_IND, theMethod, myMeth ); } - void SetArg( int index, const TCollection_AsciiString& theArg); + void SetArg( int index, const _AString& theArg); void RemoveArgs(); - static bool SkipSpaces( const TCollection_AsciiString & theSring, int & thePos ); - static TCollection_AsciiString GetWord( const TCollection_AsciiString & theSring, - int & theStartPos, const bool theForward, - const bool dotIsWord = false); + void Comment(); + static bool SkipSpaces( const _AString & theSring, int & thePos ); + static _AString GetWord( const _AString & theSring, int & theStartPos, + const bool theForward, const bool dotIsWord = false); + static bool IsStudyEntry( const _AString& str ); + static std::list< _pyID > GetStudyEntries( const _AString& str ); void AddDependantCmd( Handle(_pyCommand) cmd, bool prepend = false) { if (prepend) myDependentCmds.push_front( cmd ); else myDependentCmds.push_back( cmd ); } bool SetDependentCmdsAfter() const; @@ -147,27 +155,62 @@ public: // ------------------------------------------------------------------------------------- /*! - * \brief Root of all objects + * \brief Root of all objects. It counts calls of Process() */ // ------------------------------------------------------------------------------------- class _pyObject: public Standard_Transient { +protected: + _pyID myID; Handle(_pyCommand) myCreationCmd; + std::list< Handle(_pyCommand) > myProcessedCmds; + bool myIsPublished; + + void setID(const _pyID& theID); public: - _pyObject(const Handle(_pyCommand)& theCreationCmd): myCreationCmd(theCreationCmd) {} - const _pyID& GetID() { return myCreationCmd->GetResultValue(); } + _pyObject(const Handle(_pyCommand)& theCreationCmd, const _pyID& theID=_pyID()); + const _pyID& GetID() { return myID.IsEmpty() ? myCreationCmd->GetResultValue() : myID; } static _pyID FatherID(const _pyID & childID); const Handle(_pyCommand)& GetCreationCmd() { return myCreationCmd; } - void SetCreationCmd( Handle(_pyCommand) cmd ) { myCreationCmd = cmd; } + int GetNbCalls() const { return myProcessedCmds.size(); } + bool IsInStudy() const { return myIsPublished; } + virtual void SetRemovedFromStudy(const bool isRemoved) { myIsPublished = !isRemoved; } + void SetCreationCmd( Handle(_pyCommand) cmd ) { myCreationCmd = cmd; } int GetCommandNb() { return myCreationCmd->GetOrderNb(); } - virtual void Process(const Handle(_pyCommand) & theCommand) = 0; + void AddProcessedCmd( const Handle(_pyCommand) & cmd ) + { if (myProcessedCmds.empty() || myProcessedCmds.back()!=cmd) myProcessedCmds.push_back( cmd );} + std::list< Handle(_pyCommand) >& GetProcessedCmds() { return myProcessedCmds; } + virtual void Process(const Handle(_pyCommand) & cmd) { AddProcessedCmd(cmd); } virtual void Flush() = 0; virtual const char* AccessorMethod() const; + virtual bool CanClear() { return !myIsPublished; } + virtual void ClearCommands(); + virtual void Free() {} DEFINE_STANDARD_RTTI (_pyObject) }; +// ------------------------------------------------------------------------------------- +/*! + * \brief Data used to restore cleared Compute() command of an exported mesh + * when an imported mesh is created + */ +// ------------------------------------------------------------------------------------- +struct ExportedMeshData +{ + Handle(_pyMesh) myMesh; + Handle(_pyCommand) myLastComputeCmd; + _AString myLastComputeCmdString; + ExportedMeshData() {} + ExportedMeshData( const Handle(_pyMesh)& mesh, Handle(_pyCommand) computeCmd): + myMesh( mesh ), myLastComputeCmd( computeCmd ) + { + if ( !myLastComputeCmd.IsNull()) + myLastComputeCmdString = myLastComputeCmd->GetString(); + } +}; + // ------------------------------------------------------------------------------------- /*! * \brief Class corresponding to SMESH_Gen. It holds info on existing @@ -178,25 +221,45 @@ class _pyGen: public _pyObject { public: _pyGen(Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod, - Resource_DataMapOfAsciiStringAsciiString& theObjectNames); - //~_pyGen(); - Handle(_pyCommand) AddCommand( const TCollection_AsciiString& theCommand ); - void Process( const Handle(_pyCommand)& theCommand ); - void Flush(); - Handle(_pyHypothesis) FindHyp( const _pyID& theHypID ); - Handle(_pyHypothesis) FindAlgo( const _pyID& theGeom, const _pyID& theMesh, - const Handle(_pyHypothesis)& theHypothesis); - Handle(_pySubMesh) FindSubMesh( const _pyID& theSubMeshID ); + Resource_DataMapOfAsciiStringAsciiString& theObjectNames, + SALOMEDS::Study_ptr& theStudy, + const bool theToKeepAllCommands); + Handle(_pyCommand) AddCommand( const _AString& theCommand ); void ExchangeCommands( Handle(_pyCommand) theCmd1, Handle(_pyCommand) theCmd2 ); void SetCommandAfter( Handle(_pyCommand) theCmd, Handle(_pyCommand) theAfterCmd ); void SetCommandBefore( Handle(_pyCommand) theCmd, Handle(_pyCommand) theBeforeCmd ); Handle(_pyCommand)& GetLastCommand(); std::list< Handle(_pyCommand) >& GetCommands() { return myCommands; } + void PlaceSubmeshAfterItsCreation( Handle(_pyCommand) theCmdUsingSubmesh ) const; + + _pyID GenerateNewID( const _pyID& theID ); + void AddObject( Handle(_pyObject)& theObj ); + void SetProxyObject( const _pyID& theID, Handle(_pyObject)& theObj ); + Handle(_pyObject) FindObject( const _pyID& theObjID ) const; + Handle(_pySubMesh) FindSubMesh( const _pyID& theSubMeshID ); + Handle(_pyHypothesis) FindHyp( const _pyID& theHypID ); + Handle(_pyHypothesis) FindAlgo( const _pyID& theGeom, const _pyID& theMesh, + const Handle(_pyHypothesis)& theHypothesis); + void SetAccessorMethod(const _pyID& theID, const char* theMethod ); bool AddMeshAccessorMethod( Handle(_pyCommand) theCmd ) const; bool AddAlgoAccessorMethod( Handle(_pyCommand) theCmd ) const; - const char* AccessorMethod() const; - _pyID GenerateNewID( const _pyID& theID ); + virtual const char* AccessorMethod() const; + + bool IsGeomObject(const _pyID& theObjID) const; + bool IsNotPublished(const _pyID& theObjID) const; + bool IsToKeepAllCommands() const { return myToKeepAllCommands; } + void AddExportedMesh(const _AString& file, const ExportedMeshData& mesh ) + { myFile2ExportedMesh[ file ] = mesh; } + ExportedMeshData& FindExportedMesh( const _AString& file ) + { return myFile2ExportedMesh[ file ]; } + + virtual void Process( const Handle(_pyCommand)& theCommand ); + virtual void Flush(); + virtual void ClearCommands(); + virtual void Free(); + + Handle( _pyHypothesisReader ) GetHypothesisReader() const; private: void setNeighbourCommand( Handle(_pyCommand)& theCmd, @@ -205,16 +268,20 @@ private: private: std::map< _pyID, Handle(_pyMesh) > myMeshes; - std::map< _pyID, Handle(_pySubMesh) > mySubMeshes; std::map< _pyID, Handle(_pyMeshEditor) > myMeshEditors; + std::map< _pyID, Handle(_pyObject) > myObjects; std::list< Handle(_pyHypothesis) > myHypos; std::list< Handle(_pyCommand) > myCommands; int myNbCommands; - bool myHasPattern; Resource_DataMapOfAsciiStringAsciiString& myID2AccessorMethod; Resource_DataMapOfAsciiStringAsciiString& myObjectNames; Handle(_pyCommand) myLastCommand; - Handle(_pyFilterManager) myFilterManager; + int myNbFilters; + bool myToKeepAllCommands; + SALOMEDS::Study_var myStudy; + int myGeomIDNb, myGeomIDIndex; + std::map< _AString, ExportedMeshData > myFile2ExportedMesh; + Handle( _pyHypothesisReader ) myHypReader; DEFINE_STANDARD_RTTI (_pyGen) }; @@ -228,22 +295,35 @@ private: class _pyMesh: public _pyObject { std::list< Handle(_pyHypothesis) > myHypos; - std::list< Handle(_pyCommand) > myAddHypCmds; - std::list< Handle(_pySubMesh) > mySubmeshes; - bool myHasEditor; + std::list< Handle(_pyCommand) > myAddHypCmds; + std::list< Handle(_pySubMesh) > mySubmeshes; + std::list< Handle(_pyGroup) > myGroups; + std::list< Handle(_pyMeshEditor)> myEditors; + //d::list< Handle(_pyMesh) > myFatherMeshes; // this mesh depends on + std::list< Handle(_pyMesh) > myChildMeshes; // depending on me + bool myGeomNotInStudy; + Handle(_pyCommand) myLastComputeCmd; public: _pyMesh(const Handle(_pyCommand) creationCmd); - _pyMesh(const Handle(_pyCommand) theCreationCmd, const TCollection_AsciiString & id); + _pyMesh(const Handle(_pyCommand) theCreationCmd, const _pyID & id); const _pyID& GetGeom() { return GetCreationCmd()->GetArg(1); } - void Process( const Handle(_pyCommand)& theCommand); - void Flush(); - const char* AccessorMethod() const { return _pyMesh_ACCESS_METHOD; } + void AddGroup( const Handle(_pyGroup)& g ) { myGroups.push_back( g ); } + void AddEditor( const Handle(_pyMeshEditor)& e ) { myEditors.push_back( e ); } + bool IsNotGeomPublished() { return myGeomNotInStudy; } + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); + virtual void SetRemovedFromStudy(const bool isRemoved); + virtual bool CanClear(); + virtual void ClearCommands(); + virtual void Free() { /*myFatherMeshes.clear();*/ myChildMeshes.clear(); } + virtual const char* AccessorMethod() const { return _pyMesh_ACCESS_METHOD; } private: + void addFatherMesh( const Handle(_pyMesh)& mesh ); + void addFatherMesh( const _pyID& meshID ); static bool NeedMeshAccess( const Handle(_pyCommand)& theCommand ); static void AddMeshAccess( const Handle(_pyCommand)& theCommand ) { theCommand->SetObject( theCommand->GetObject() + "." _pyMesh_ACCESS_METHOD ); } - //friend class _pyMeshEditor; DEFINE_STANDARD_RTTI (_pyMesh) }; #undef _pyMesh_ACCESS_METHOD @@ -255,12 +335,14 @@ private: // ------------------------------------------------------------------------------------- class _pyMeshEditor: public _pyObject { - _pyID myMesh; - TCollection_AsciiString myCreationCmdStr; + _pyID myMesh; + _AString myCreationCmdStr; public: _pyMeshEditor(const Handle(_pyCommand)& theCreationCmd); - void Process( const Handle(_pyCommand)& theCommand); + _pyID GetMesh() const { return myMesh; } + virtual void Process( const Handle(_pyCommand)& theCommand); virtual void Flush() {} + virtual bool CanClear(); DEFINE_STANDARD_RTTI (_pyMesh) }; @@ -268,64 +350,80 @@ public: // ------------------------------------------------------------------------------------- /*! * \brief Root class for hypothesis - * - * HOWTO assure convertion of a new type of hypothesis - * In _pyHypothesis::NewHypothesis(): - * 1. add a case for the name of the new hypothesis - * 2. use SetConvMethodAndType() to set - * . for algo: algorithm name and method of Mesh creating the algo - * . for hypo: name of the algorithm and method creating the hypothesis - * 3. append to myArgMethods interface methods setting param values in the - * order they are used when creation method is called. If arguments of - * the creation method can't be easily got from calls of hypothesis methods, you are - * to derive a specific class from _pyHypothesis that would redefine Process(), - * see _pyComplexParamHypo for example */ // ------------------------------------------------------------------------------------- class _pyHypothesis: public _pyObject { + friend class _pyHypothesisReader; protected: bool myIsAlgo, myIsWrapped; _pyID myGeom, myMesh; - // a hypothesis can be used and created by different algos by different methods - std::map myType2CreationMethod; - //TCollection_AsciiString myCreationMethod, myType; - TColStd_SequenceOfAsciiString myArgs; // creation arguments - TColStd_SequenceOfAsciiString myArgMethods; // hypo methods setting myArgs - TColStd_SequenceOfInteger myNbArgsByMethod; // nb args set by each method - std::list myArgCommands; - std::list myUnknownCommands; + struct CreationMethod { + _AString myMethod; // method of algo or mesh creating a hyp + // myArgNb(i)-th arg of myArgMethods(i) of hyp becomes an i-th arg of myAlgoMethod + std::vector<_AString> myArgMethods; + std::vector myArgNb; // arg nb countered from 1 + std::vector<_AString> myArgs; // creation arguments + }; + void setCreationArg( const int argNb, const _AString& arg ); + // a hypothesis can be created by different algos by different methods + typedef std::map<_AString, CreationMethod > TType2CrMethod; + TType2CrMethod myAlgoType2CreationMethod; + CreationMethod* myCurCrMethod; // used for adding to myAlgoType2CreationMethod + std::list myArgCommands; + std::list myUnusedCommands; + std::list myReferredObjs; + // maps used to clear commands setting parameters if result of setting is + // discared (e.g. by mesh.Clear()) + std::map<_AString, std::list > myMeth2Commands; + std::map< _pyCommand::TAddr, std::list > myComputeAddr2Cmds; + std::list myComputeCmds; + void rememberCmdOfParameter( const Handle(_pyCommand) & cmd ); + bool isCmdUsedForCompute( const Handle(_pyCommand) & cmd, + _pyCommand::TAddr avoidComputeAddr=NULL ) const; public: _pyHypothesis(const Handle(_pyCommand)& theCreationCmd); - void SetConvMethodAndType(const char* creationMethod, const char* type) - { myType2CreationMethod[ (char*)type ] = (char*)creationMethod; } - void AddArgMethod(const char* method, const int nbArgs = 1) - { myArgMethods.Append( (char*)method ); myNbArgsByMethod.Append( nbArgs ); } - const TColStd_SequenceOfAsciiString& GetArgs() const { return myArgs; } + void SetConvMethodAndType(const _AString& creationMethod, const _AString& type) + { myCurCrMethod = &myAlgoType2CreationMethod[ type ]; + myCurCrMethod->myMethod = creationMethod; } + void AddArgMethod(const _AString& method, const int argNb = 1) + { myCurCrMethod->myArgMethods.push_back( method ); + myCurCrMethod->myArgNb.push_back( argNb ); } + //const TColStd_SequenceOfAsciiString& GetArgs() const { return myArgs; } const std::list& GetArgCommands() const { return myArgCommands; } void ClearAllCommands(); virtual bool IsAlgo() const { return myIsAlgo; } - bool IsValid() const { return !myType2CreationMethod.empty(); } + bool IsValid() const { return !myAlgoType2CreationMethod.empty(); } bool IsWrapped() const { return myIsWrapped; } const _pyID & GetGeom() const { return myGeom; } void SetMesh( const _pyID& theMeshId) { if ( myMesh.IsEmpty() ) myMesh = theMeshId; } const _pyID & GetMesh() const { return myMesh; } - const TCollection_AsciiString& GetAlgoType() const - { return myType2CreationMethod.begin()->first; } - const TCollection_AsciiString& GetAlgoCreationMethod() const - { return myType2CreationMethod.begin()->second; } - bool CanBeCreatedBy(const TCollection_AsciiString& algoType ) const - { return myType2CreationMethod.find( algoType ) != myType2CreationMethod.end(); } - const TCollection_AsciiString& GetCreationMethod(const TCollection_AsciiString& algoType) const - { return myType2CreationMethod.find( algoType )->second; } - virtual bool IsWrappable(const _pyID& theMesh) { return !myIsWrapped && myMesh == theMesh; } + const _AString& GetAlgoType() const + { return myAlgoType2CreationMethod.begin()->first; } + const _AString& GetAlgoCreationMethod() const + { return myAlgoType2CreationMethod.begin()->second.myMethod; } + bool CanBeCreatedBy(const _AString& algoType ) const + { return myAlgoType2CreationMethod.find( algoType ) != myAlgoType2CreationMethod.end(); } + const _AString& GetCreationMethod(const _AString& algoType) + { return ( myCurCrMethod = & myAlgoType2CreationMethod[ algoType ])->myMethod; } + static Handle(_pyHypothesis) NewHypothesis( const Handle(_pyCommand)& theCreationCmd); + + virtual bool IsWrappable(const _pyID& theMesh) const; virtual bool Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, const _pyID& theMesh); - static Handle(_pyHypothesis) NewHypothesis( const Handle(_pyCommand)& theCreationCmd); - void Process( const Handle(_pyCommand)& theCommand); - void Flush(); + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); + virtual void Free() { myReferredObjs.clear(); } virtual void Assign( const Handle(_pyHypothesis)& theOther, - const _pyID& theMesh ); + const _pyID& theMesh ); + virtual bool CanClear(); + virtual void ClearCommands(); + virtual bool GetReferredMeshesAndGeom( std::list< Handle(_pyMesh) >& meshes ); + + void MeshComputed ( const Handle(_pyCommand)& theComputeCommand ); + void ComputeDiscarded( const Handle(_pyCommand)& theComputeCommand ); + //void ComputeSaved ( const Handle(_pyCommand)& theComputeCommand ); + DEFINE_STANDARD_RTTI (_pyHypothesis) }; @@ -341,7 +439,7 @@ public: _pyAlgorithm(const Handle(_pyCommand)& theCreationCmd); virtual bool Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, const _pyID& theMesh); - const char* AccessorMethod() const { return "GetAlgorithm()"; } + virtual const char* AccessorMethod() const { return "GetAlgorithm()"; } virtual bool IsWrappable(const _pyID& theMesh) { return !myIsWrapped; } DEFINE_STANDARD_RTTI (_pyAlgorithm) @@ -356,7 +454,8 @@ class _pyComplexParamHypo: public _pyHypothesis { public: _pyComplexParamHypo(const Handle(_pyCommand)& theCreationCmd): _pyHypothesis(theCreationCmd) {} - void Process( const Handle(_pyCommand)& theCommand); + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); DEFINE_STANDARD_RTTI (_pyComplexParamHypo) }; @@ -370,13 +469,15 @@ DEFINE_STANDARD_HANDLE (_pyComplexParamHypo, _pyHypothesis); class _pyLayerDistributionHypo: public _pyHypothesis { Handle(_pyHypothesis) my1dHyp; + _AString myAlgoMethod; public: - _pyLayerDistributionHypo(const Handle(_pyCommand)& theCreationCmd): - _pyHypothesis(theCreationCmd) {} - void Process( const Handle(_pyCommand)& theCommand); - void Flush(); - bool Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, - const _pyID& theMesh); + _pyLayerDistributionHypo(const Handle(_pyCommand)& theCreationCmd, const char* algoMethod): + _pyHypothesis(theCreationCmd), myAlgoMethod((char*)algoMethod) {} + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); + virtual bool Addition2Creation( const Handle(_pyCommand)& theAdditionCmd, + const _pyID& theMesh); + virtual void Free() { my1dHyp.Nullify(); } DEFINE_STANDARD_RTTI (_pyLayerDistributionHypo) }; @@ -416,20 +517,19 @@ DEFINE_STANDARD_HANDLE (_pySegmentLengthAroundVertexHyp, _pyHypothesis); // ------------------------------------------------------------------------------------- /*! - * \brief FilterManager creates only if at least one command invoked + * \brief SelfEraser erases creation command if no more it's commands invoked */ // ------------------------------------------------------------------------------------- -class _pyFilterManager: public _pyObject +class _pySelfEraser: public _pyObject { public: - _pyFilterManager(const Handle(_pyCommand)& theCreationCmd); - void Process( const Handle(_pyCommand)& theCommand); + _pySelfEraser(const Handle(_pyCommand)& theCreationCmd) + :_pyObject(theCreationCmd) { myIsPublished = true; } virtual void Flush(); - DEFINE_STANDARD_RTTI (_pyFilterManager) -private: - int myCmdCount; + DEFINE_STANDARD_RTTI (_pySelfEraser) }; +DEFINE_STANDARD_HANDLE (_pySelfEraser, _pyObject); // ------------------------------------------------------------------------------------- /*! @@ -438,16 +538,72 @@ private: // ------------------------------------------------------------------------------------- class _pySubMesh: public _pyObject { + Handle(_pyObject) myCreator; + Handle(_pyMesh) myMesh; public: _pySubMesh(const Handle(_pyCommand)& theCreationCmd); - void Process( const Handle(_pyCommand)& theCommand); + virtual void Process( const Handle(_pyCommand)& theCommand); virtual void Flush(); + virtual Handle(_pyMesh) GetMesh() { return myMesh; } + virtual void Free() { myCreator.Nullify(); myMesh.Nullify(); } void SetCreator( const Handle(_pyObject)& theCreator ) { myCreator = theCreator; } + static bool CanBeArgOfMethod(const _AString& theMethodName); - DEFINE_STANDARD_RTTI (_pyFilterManager) -private: - int myCmdCount; - Handle(_pyObject) myCreator; + DEFINE_STANDARD_RTTI (_pySubMesh) +}; +// ------------------------------------------------------------------------------------- +/*! + * \brief A filter sets a human readable name to self + */ +// ------------------------------------------------------------------------------------- +class _pyFilter: public _pyObject +{ + _pyID myNewID, myMesh; + std::list< Handle(_pyObject) > myUsers; +public: + _pyFilter(const Handle(_pyCommand)& theCreationCmd, const _pyID& newID=""); + void AddUser( const Handle(_pyObject)& user) { myUsers.push_back( user ); } + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); + virtual bool CanClear(); + virtual void Free() { myUsers.clear(); } + const _pyID& GetNewID() const { return myNewID; } + + DEFINE_STANDARD_RTTI (_pyFilter) +}; +DEFINE_STANDARD_HANDLE (_pyFilter, _pyObject); + +// ------------------------------------------------------------------------------------- +/*! + * \brief To convert creation of a group by filter + */ +// ------------------------------------------------------------------------------------- +class _pyGroup: public _pySubMesh +{ + Handle(_pyFilter) myFilter; + bool myCanClearCreationCmd; +public: + _pyGroup(const Handle(_pyCommand)& theCreationCmd, const _pyID & id=_pyID()); + virtual void Process( const Handle(_pyCommand)& theCommand); + virtual void Flush(); + virtual void Free() { myFilter.Nullify(); } + + DEFINE_STANDARD_RTTI (_pyGroup) +}; + +// ------------------------------------------------------------------------------------- +/*! + * \brief Class reading _pyHypothesis'es from resource files of mesher Plugins + */ +// ------------------------------------------------------------------------------------- +class _pyHypothesisReader: public Standard_Transient +{ + std::map<_AString, Handle(_pyHypothesis)> myType2Hyp; +public: + _pyHypothesisReader(); + Handle(_pyHypothesis) GetHypothesis(const _AString& hypType, + const Handle(_pyCommand)& creationCmd) const; + DEFINE_STANDARD_RTTI (_pyHypothesisReader) }; #endif diff --git a/src/SMESH_I/SMESH_3D_Algo_i.cxx b/src/SMESH_I/SMESH_3D_Algo_i.cxx index 8aa242ba6..9a73a538f 100644 --- a/src/SMESH_I/SMESH_3D_Algo_i.cxx +++ b/src/SMESH_I/SMESH_3D_Algo_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_3D_Algo_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_3D_Algo_i.hxx b/src/SMESH_I/SMESH_3D_Algo_i.hxx index 4352e7296..d798b9e41 100644 --- a/src/SMESH_I/SMESH_3D_Algo_i.hxx +++ b/src/SMESH_I/SMESH_3D_Algo_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_3D_Algo_i.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_Algo_i.cxx b/src/SMESH_I/SMESH_Algo_i.cxx index e567a15d6..13caf98e0 100644 --- a/src/SMESH_I/SMESH_Algo_i.cxx +++ b/src/SMESH_I/SMESH_Algo_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Algo_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_Algo_i.hxx b/src/SMESH_I/SMESH_Algo_i.hxx index cfcfc5347..34491582d 100644 --- a/src/SMESH_I/SMESH_Algo_i.hxx +++ b/src/SMESH_I/SMESH_Algo_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Algo_i.hxx // Author : Paul RASCLE, EDF diff --git a/src/SMESH_I/SMESH_DumpPython.cxx b/src/SMESH_I/SMESH_DumpPython.cxx index ad409b786..0544cb091 100644 --- a/src/SMESH_I/SMESH_DumpPython.cxx +++ b/src/SMESH_I/SMESH_DumpPython.cxx @@ -1,30 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// File : SMESH_Gen_i_DumpPython.cxx -// Created : Thu Mar 24 17:17:59 2005 -// Author : Julia DOROVSKIKH -// Module : SMESH -// $Header : $ +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESH_DumpPython.cxx +// Created : Thu Mar 24 17:17:59 2005 +// Author : Julia DOROVSKIKH +// Module : SMESH + #include "SMESH_PythonDump.hxx" #include "SMESH_Gen_i.hxx" #include "SMESH_Filter_i.hxx" @@ -33,6 +32,7 @@ #include #include +#include #ifdef _DEBUG_ @@ -51,8 +51,17 @@ namespace SMESH size_t TPythonDump::myCounter = 0; + TVar::TVar(CORBA::Double value):myVals(1) { myVals[0] = SMESH_Comment(value); } + TVar::TVar(CORBA::Long value):myVals(1) { myVals[0] = SMESH_Comment(value); } + TVar::TVar(CORBA::Short value):myVals(1) { myVals[0] = SMESH_Comment(value); } + TVar::TVar(const SMESH::double_array& value):myVals(value.length()) + { + for ( size_t i = 0; i < value.length(); i++) + myVals[i] = SMESH_Comment(value[i]); + } + TPythonDump:: - TPythonDump() + TPythonDump():myVarsCounter(0) { ++myCounter; } @@ -65,48 +74,82 @@ namespace SMESH TCollection_AsciiString aCollection(Standard_CString(aString.c_str())); SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); if(!aStudy->_is_nil() && !aCollection.IsEmpty()){ - aSMESHGen->AddToPythonScript(aStudy->StudyId(),aCollection); - if(MYDEBUG) MESSAGE(aString); + aSMESHGen->AddToPythonScript(aStudy->StudyId(),aCollection); + if(MYDEBUG) MESSAGE(aString); + aSMESHGen->UpdateParameters(""); // prevent misuse of already treated variables } } } - TPythonDump& + TPythonDump& //!< to store a variable value + TPythonDump:: + operator<<(const TVar& theVarValue) + { + if ( theVarValue.myVals.empty() ) return *this; + + const std::vector< std::string >& varNames = SMESH_Gen_i::GetSMESHGen()->GetLastParameters(); + if ( theVarValue.myVals.size() > 1 ) + { + myStream << "[ "; + for ( size_t i = 1; i <= theVarValue.myVals.size(); ++i ) + { + if ( myVarsCounter < varNames.size() && !varNames[ myVarsCounter ].empty() ) + myStream << TVar::Quote() << varNames[ myVarsCounter ] << TVar::Quote(); + else + myStream << theVarValue.myVals[i-1]; + if ( i < theVarValue.myVals.size() ) + myStream << ", "; + ++myVarsCounter; + } + myStream << " ]"; + } + else + { + if ( myVarsCounter < varNames.size() && !varNames[ myVarsCounter ].empty() ) + myStream << TVar::Quote() << varNames[ myVarsCounter ] << TVar::Quote(); + else + myStream << theVarValue.myVals[0]; + ++myVarsCounter; + } + return *this; + } + + TPythonDump& TPythonDump:: operator<<(long int theArg){ myStream< - void DumpArray(const TArray& theArray, std::ostringstream & theStream) + void DumpArray(const TArray& theArray, TPythonDump & theStream) { theStream << "[ "; for (int i = 1; i <= theArray.length(); i++) { @@ -141,21 +209,34 @@ namespace SMESH theStream << " ]"; } - TPythonDump& + TPythonDump& TPythonDump::operator<<(const SMESH::long_array& theArg) { - DumpArray( theArg, myStream ); + DumpArray( theArg, *this ); return *this; } - TPythonDump& + TPythonDump& TPythonDump::operator<<(const SMESH::double_array& theArg) { - DumpArray( theArg, myStream ); + DumpArray( theArg, *this ); return *this; } - TPythonDump& + TPythonDump& + TPythonDump::operator<<(const SMESH::string_array& theArray) + { + myStream << "[ "; + for (int i = 1; i <= theArray.length(); i++) { + myStream << "'" << theArray[i-1] << "'"; + if ( i < theArray.length() ) + myStream << ", "; + } + myStream << " ]"; + return *this; + } + + TPythonDump& TPythonDump:: operator<<(SALOMEDS::SObject_ptr aSObject) { @@ -166,15 +247,16 @@ namespace SMESH return *this; } - TPythonDump& + TPythonDump& TPythonDump:: operator<<(CORBA::Object_ptr theArg) { SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); + SALOMEDS::Study_var aStudy = aSMESHGen->GetCurrentStudy(); SALOMEDS::SObject_var aSObject = SMESH_Gen_i::ObjectToSObject(aStudy,theArg); if(!aSObject->_is_nil()) { - myStream << aSObject->GetID(); + CORBA::String_var id = aSObject->GetID(); + myStream << id; } else if ( !CORBA::is_nil(theArg)) { if ( aSMESHGen->CanPublishInStudy( theArg )) // not published SMESH object myStream << "smeshObj_" << size_t(theArg); @@ -186,7 +268,44 @@ namespace SMESH return *this; } - TPythonDump& + TPythonDump& + TPythonDump:: + operator<<(SMESH::SMESH_Hypothesis_ptr theArg) + { + SALOMEDS::Study_var aStudy = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); + SALOMEDS::SObject_var aSObject = SMESH_Gen_i::ObjectToSObject(aStudy,theArg); + if(aSObject->_is_nil() && !CORBA::is_nil(theArg)) + myStream << "hyp_" << theArg->GetId(); + else + *this << CORBA::Object_ptr( theArg ); + return *this; + } + + TPythonDump& + TPythonDump:: + operator<<(SMESH::SMESH_IDSource_ptr theArg) + { + if ( CORBA::is_nil( theArg ) ) + return *this << "None"; + SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var aStudy = aSMESHGen->GetCurrentStudy(); + SALOMEDS::SObject_var aSObject = SMESH_Gen_i::ObjectToSObject(aStudy,theArg); + if(!aSObject->_is_nil()) + return *this << aSObject; + if ( SMESH::Filter_i* filter = SMESH::DownCast( theArg )) + return *this << filter; + SMESH::SMESH_Mesh_var mesh = theArg->GetMesh(); + if ( !theArg->_is_equivalent( mesh )) + { + SMESH::long_array_var anElementsId = theArg->GetIDs(); + SMESH::array_of_ElementType_var types = theArg->GetTypes(); + SMESH::ElementType type = types->length() ? types[0] : SMESH::ALL; + return *this << mesh << ".GetIDSource(" << anElementsId << ", " << type << ")"; + } + return *this; + } + + TPythonDump& TPythonDump:: operator<<(SMESH::FilterLibrary_i* theArg) { @@ -194,7 +313,7 @@ namespace SMESH return *this; } - TPythonDump& + TPythonDump& TPythonDump:: operator<<(SMESH::FilterManager_i* theArg) { @@ -202,7 +321,7 @@ namespace SMESH return *this; } - TPythonDump& + TPythonDump& TPythonDump:: operator<<(SMESH::Filter_i* theArg) { @@ -210,47 +329,69 @@ namespace SMESH return *this; } - TPythonDump& + TPythonDump& TPythonDump:: operator<<(SMESH::Functor_i* theArg) { if ( theArg ) { FunctorType aFunctorType = theArg->GetFunctorType(); switch(aFunctorType){ - case FT_AspectRatio: myStream<< "anAspectRatio"; break; - case FT_AspectRatio3D: myStream<< "anAspectRatio3D"; break; - case FT_Warping: myStream<< "aWarping"; break; - case FT_MinimumAngle: myStream<< "aMinimumAngle"; break; - case FT_Taper: myStream<< "aTaper"; break; - case FT_Skew: myStream<< "aSkew"; break; - case FT_Area: myStream<< "aArea"; break; - case FT_FreeBorders: myStream<< "aFreeBorders"; break; - case FT_FreeEdges: myStream<< "aFreeEdges"; break; - case FT_FreeNodes: myStream<< "aFreeNodes"; break; - case FT_MultiConnection: myStream<< "aMultiConnection"; break; - case FT_MultiConnection2D:myStream<< "aMultiConnection2D";break; - case FT_Length: myStream<< "aLength"; break; - case FT_Length2D: myStream<< "aLength"; break; - case FT_BelongToGeom: myStream<< "aBelongToGeom"; break; - case FT_BelongToPlane: myStream<< "aBelongToPlane"; break; - case FT_BelongToCylinder: myStream<< "aBelongToCylinder"; break; - case FT_BelongToGenSurface:myStream<<"aBelongToGenSurface";break; - case FT_LyingOnGeom: myStream<< "aLyingOnGeom"; break; - case FT_RangeOfIds: myStream<< "aRangeOfIds"; break; - case FT_BadOrientedVolume:myStream<< "aBadOrientedVolume";break; - case FT_LessThan: myStream<< "aLessThan"; break; - case FT_MoreThan: myStream<< "aMoreThan"; break; - case FT_EqualTo: myStream<< "anEqualTo"; break; - case FT_LogicalNOT: myStream<< "aLogicalNOT"; break; - case FT_LogicalAND: myStream<< "aLogicalAND"; break; - case FT_LogicalOR: myStream<< "aLogicalOR"; break; - case FT_Undefined: myStream<< "anUndefined"; break; + case FT_AspectRatio: myStream<< "anAspectRatio"; break; + case FT_AspectRatio3D: myStream<< "anAspectRatio3D"; break; + case FT_Warping: myStream<< "aWarping"; break; + case FT_MinimumAngle: myStream<< "aMinimumAngle"; break; + case FT_Taper: myStream<< "aTaper"; break; + case FT_Skew: myStream<< "aSkew"; break; + case FT_Area: myStream<< "aArea"; break; + case FT_Volume3D: myStream<< "aVolume3D"; break; + case FT_MaxElementLength2D: myStream<< "aMaxElementLength2D"; break; + case FT_MaxElementLength3D: myStream<< "aMaxElementLength3D"; break; + case FT_FreeBorders: myStream<< "aFreeBorders"; break; + case FT_FreeEdges: myStream<< "aFreeEdges"; break; + case FT_FreeNodes: myStream<< "aFreeNodes"; break; + case FT_FreeFaces: myStream<< "aFreeFaces"; break; + case FT_MultiConnection: myStream<< "aMultiConnection"; break; + case FT_MultiConnection2D: myStream<< "aMultiConnection2D"; break; + case FT_Length: myStream<< "aLength"; break; + case FT_Length2D: myStream<< "aLength2D"; break; + case FT_BelongToGeom: myStream<< "aBelongToGeom"; break; + case FT_BelongToPlane: myStream<< "aBelongToPlane"; break; + case FT_BelongToCylinder: myStream<< "aBelongToCylinder"; break; + case FT_BelongToGenSurface: myStream<< "aBelongToGenSurface"; break; + case FT_LyingOnGeom: myStream<< "aLyingOnGeom"; break; + case FT_CoplanarFaces: myStream<< "aCoplanarFaces"; break; + case FT_RangeOfIds: myStream<< "aRangeOfIds"; break; + case FT_BadOrientedVolume: myStream<< "aBadOrientedVolume"; break; + case FT_BareBorderVolume: myStream<< "aBareBorderVolume"; break; + case FT_BareBorderFace: myStream<< "aBareBorderFace"; break; + case FT_OverConstrainedVolume: myStream<< "aOverConstrainedVolume"; break; + case FT_OverConstrainedFace: myStream<< "aOverConstrainedFace"; break; + case FT_LinearOrQuadratic: myStream<< "aLinearOrQuadratic"; break; + case FT_GroupColor: myStream<< "aGroupColor"; break; + case FT_ElemGeomType: myStream<< "anElemGeomType"; break; + case FT_LessThan: myStream<< "aLessThan"; break; + case FT_MoreThan: myStream<< "aMoreThan"; break; + case FT_EqualTo: myStream<< "anEqualTo"; break; + case FT_LogicalNOT: myStream<< "aLogicalNOT"; break; + case FT_LogicalAND: myStream<< "aLogicalAND"; break; + case FT_LogicalOR: myStream<< "aLogicalOR"; break; + case FT_Undefined: + default: myStream<< "anUndefined"; break; } myStream<length() > 0 ) { - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); - myStream << "["; - int aListLen = theList->length(); - for(int i = 0 ; i < aListLen; i++){ - SALOMEDS::SObject_var aSObject = SMESH_Gen_i::ObjectToSObject(aStudy,(*theList)[i]); - if(!aSObject->_is_nil()) { - myStream << aSObject->GetID(); - i < (aListLen - 1) ? myStream<<", " : myStream<<"]"; - } - - } - } + TPythonDump& TPythonDump::operator<<(const SMESH::PointStruct & P) + { + *this << "SMESH.PointStruct ( " + << TVar( P.x ) << ", " + << TVar( P.y ) << ", " + << TVar( P.z ) << " )"; + return *this; + } + + TPythonDump& TPythonDump::operator<<(const SMESH::ListOfGroups& theList) + { + DumpArray( theList, *this ); + return *this; + } + TPythonDump& TPythonDump::operator<<(const SMESH::ListOfGroups * theList) + { + DumpArray( *theList, *this ); + return *this; + } + TPythonDump& TPythonDump::operator<<(const SMESH::ListOfIDSources& theList) + { + DumpArray( theList, *this ); return *this; } @@ -322,10 +470,10 @@ namespace SMESH //================================================================================ /*! - * \brief Return marker of long string literal beginning - * \param type - a name of functionality producing the string literal - * \retval TCollection_AsciiString - the marker string to be written into - * a raw python script + * \brief Return marker of long string literal beginning + * \param type - a name of functionality producing the string literal + * \retval TCollection_AsciiString - the marker string to be written into + * a raw python script */ //================================================================================ @@ -359,7 +507,7 @@ namespace SMESH * \param theLongString - the retrieved literal * \param theStringType - a name of functionality produced the literal * \retval bool - true if a string literal found - * + * * The literal is removed from theText; theFrom points position right after * the removed literal */ @@ -413,11 +561,31 @@ namespace SMESH } //======================================================================= -//function : DumpPython +//function : RemoveTabulation //purpose : //======================================================================= +void RemoveTabulation( TCollection_AsciiString& theScript ) +{ + std::string aString( theScript.ToCString() ); + std::string::size_type aPos = 0; + while( aPos < aString.length() ) + { + aPos = aString.find( "\n\t", aPos ); + if( aPos == std::string::npos ) + break; + aString.replace( aPos, 2, "\n" ); + aPos++; + } + theScript = aString.c_str(); +} + +//======================================================================= +//function : DumpPython +//purpose : +//======================================================================= Engines::TMPFile* SMESH_Gen_i::DumpPython (CORBA::Object_ptr theStudy, CORBA::Boolean isPublished, + CORBA::Boolean isMultiFile, CORBA::Boolean& isValidScript) { SALOMEDS::Study_var aStudy = SALOMEDS::Study::_narrow(theStudy); @@ -456,22 +624,21 @@ Engines::TMPFile* SMESH_Gen_i::DumpPython (CORBA::Object_ptr theStudy, TCollection_AsciiString aSavedTrace (oldValue); // Add trace of API methods calls and replace study entries by names - TCollection_AsciiString aScript = - "### This file is generated by SALOME automatically by dump python functionality of SMESH component\n\n"; - aScript += DumpPython_impl(aStudy, aMap, aMapNames, - isPublished, isValidScript, aSavedTrace); + TCollection_AsciiString aScript; + aScript += DumpPython_impl(aStudy, aMap, aMapNames, isPublished, isMultiFile, + myIsHistoricalPythonDump, isValidScript, aSavedTrace); - int aLen = aScript.Length(); + int aLen = aScript.Length(); unsigned char* aBuffer = new unsigned char[aLen+1]; strcpy((char*)aBuffer, aScript.ToCString()); CORBA::Octet* anOctetBuf = (CORBA::Octet*)aBuffer; - Engines::TMPFile_var aStreamFile = new Engines::TMPFile(aLen+1, aLen+1, anOctetBuf, 1); + Engines::TMPFile_var aStreamFile = new Engines::TMPFile(aLen+1, aLen+1, anOctetBuf, 1); bool hasNotPublishedObjects = aScript.Location( NotPublishedObjectName(), 1, aLen); isValidScript = isValidScript && !hasNotPublishedObjects; - return aStreamFile._retn(); + return aStreamFile._retn(); } //============================================================================= @@ -502,7 +669,7 @@ void SMESH_Gen_i::RemoveLastFromPythonScript (int theStudyID) //======================================================================= //function : SavePython -//purpose : +//purpose : //======================================================================= void SMESH_Gen_i::SavePython (SALOMEDS::Study_ptr theStudy) { @@ -555,11 +722,11 @@ Handle(TColStd_HSequenceOfInteger) FindEntries (TCollection_AsciiString& theStri int c = (int)arr[i]; j = i+1; if ( isdigit( c )) { //Is digit? - + isFound = Standard_False; while((j < aLen) && ( isdigit(c) || c == ':' )) { //Check if it is an entry - c = (int)arr[j++]; - if(c == ':') isFound = Standard_True; + c = (int)arr[j++]; + if(c == ':') isFound = Standard_True; } if (isFound) { @@ -595,20 +762,28 @@ namespace { const TCollection_AsciiString allowedChars = "qwertyuioplkjhgfdsazxcvbnmQWERTYUIOPLKJHGFDSAZXCVBNM0987654321_"; bool isValidName = true; - int p=1; // replace not allowed chars with underscore + int nbUnderscore = 0; + int p=1; // replace not allowed chars by underscore while (p <= aName.Length() && (p = aName.FirstLocationNotInSet(allowedChars, p, aName.Length()))) { if ( p == 1 || p == aName.Length() || aName.Value(p-1) == '_') - aName.Remove( p, 1 ); // remove double _ and from the start and the end + aName.Remove( p, 1 ); // remove double _ from the start and the end else - aName.SetValue(p, '_'); + aName.SetValue(p, '_'), nbUnderscore++; isValidName = false; } if ( aName.IsIntegerValue() ) { // aName must not start with a digit aName.Insert( 1, 'a' ); isValidName = false; } + // shorten names like CartesianParameters3D_400_400_400_1000000_1 + if ( aName.Length() > 20 && nbUnderscore > 2 ) + { + p = aName.Location( "_", 20, aName.Length()); + if ( p > 1 ) + aName.Trunc( p-1 ); + } return isValidName; } } @@ -619,10 +794,12 @@ namespace { */ //============================================================================= TCollection_AsciiString SMESH_Gen_i::DumpPython_impl - (SALOMEDS::Study_ptr theStudy, + (SALOMEDS::Study_ptr theStudy, Resource_DataMapOfAsciiStringAsciiString& theObjectNames, Resource_DataMapOfAsciiStringAsciiString& theNames, - bool isPublished, + bool isPublished, + bool isMultiFile, + bool isHistoricalDump, bool& aValidScript, const TCollection_AsciiString& theSavedTrace) { @@ -634,8 +811,11 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl TCollection_AsciiString anOldGen( SMESH::TPythonDump::SMESHGenName() ); TCollection_AsciiString aScript; - aScript = "def RebuildData(theStudy):\n\t"; + if( isMultiFile ) + aScript += "def RebuildData(theStudy):"; + aScript += "\n\t"; aScript += helper + "aFilterManager = " + aSMESHGen + ".CreateFilterManager()\n\t"; + aScript += helper + "aMeasurements = " + aSMESHGen + ".CreateMeasurements()\n\t"; if ( isPublished ) aScript += aSMESHGen + ".SetCurrentStudy(theStudy)"; else @@ -687,13 +867,22 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl // Some objects are wrapped with python classes and // Resource_DataMapOfAsciiStringAsciiString holds methods returning wrapped objects Resource_DataMapOfAsciiStringAsciiString anEntry2AccessorMethod; - aScript = SMESH_2smeshpy::ConvertScript( aScript, anEntry2AccessorMethod, theObjectNames ); + if ( !getenv("NO_2smeshpy_conversion")) + aScript = SMESH_2smeshpy::ConvertScript( aScript, anEntry2AccessorMethod, + theObjectNames, theStudy, isHistoricalDump ); + + // Replace characters used instead of quote marks to quote notebook variables + { + int pos = 1; + while (( pos = aScript.Location( 1, SMESH::TVar::Quote(), pos, aScript.Length() ))) + aScript.SetValue( pos, '"' ); + } // Find entries to be replaced by names Handle(TColStd_HSequenceOfInteger) aSeq = FindEntries(aScript); Standard_Integer aLen = aSeq->Length(); - if (aLen == 0) + if (aLen == 0 && isMultiFile) return aScript; // Replace entries by the names @@ -703,7 +892,7 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl Standard_Integer objectCounter = 0, aStart = 1, aScriptLength = aScript.Length(); TCollection_AsciiString anUpdatedScript, anEntry, aName, aBaseName("smeshObj_"); - // Collect names of GEOM objects to exclude same names for SMESH objects + // Collect names of GEOM objects to exclude same names of SMESH objects GEOM::string_array_var aGeomNames = geom->GetAllDumpNames(); int ign = 0, nbgn = aGeomNames->length(); for (; ign < nbgn; ign++) { @@ -757,67 +946,23 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl } // set initial part of aSript - TCollection_AsciiString initPart = "import salome, SMESH\n"; - initPart += helper + "import " + aSmeshpy + "\n\n"; - if ( importGeom ) + TCollection_AsciiString initPart = "import "; + if ( isMultiFile ) + initPart += helper + "salome, "; + initPart += aSmeshpy + ", SMESH, SALOMEDS\n"; + if ( importGeom && isMultiFile ) { - initPart += ("## import GEOM dump file ## \n" + initPart += ("\n## import GEOM dump file ## \n" "import string, os, sys, re\n" "sys.path.insert( 0, os.path.dirname(__file__) )\n" - "exec(\"from \"+re.sub(\"SMESH$\",\"GEOM\",__name__)+\" import *\")\n\n"); + "exec(\"from \"+re.sub(\"SMESH$\",\"GEOM\",__name__)+\" import *\")\n"); } anUpdatedScript.Insert ( 1, initPart ); // add final part of aScript - if (aSeq->Value(aLen) < aScriptLength) + if (aLen && aSeq->Value(aLen) < aScriptLength) anUpdatedScript += aScript.SubString(aSeq->Value(aLen) + 1, aScriptLength); - - //SMESH_Gen_i* aSMESHGenI = SMESH_Gen_i::GetSMESHGen(); - if( !CORBA::is_nil(theStudy) ) - { - SALOMEDS::SObject_var aComp = theStudy->FindComponent(ComponentDataType()); - if( !CORBA::is_nil(aComp) ) - { - SALOMEDS::ChildIterator_var Itr = theStudy->NewChildIterator(aComp); - for( Itr->InitEx(true); Itr->More(); Itr->Next() ) - { - SALOMEDS::SObject_var aSObj = Itr->Value(); - CORBA::String_var aName = aSObj->GetName(); - - SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow( SMESH_Gen_i::SObjectToObject( aSObj ) ); - if( !CORBA::is_nil(aMesh) ) - { - bool isAutoColor = aMesh->GetAutoColor(); - if( isAutoColor ) - { - anUpdatedScript += "\n\t"; - anUpdatedScript += (char*)aName.in(); - anUpdatedScript += ".SetAutoColor(1)"; - } - } - - SMESH::SMESH_GroupBase_var aGroup = SMESH::SMESH_GroupBase::_narrow( SMESH_Gen_i::SObjectToObject( aSObj ) ); - if( !CORBA::is_nil(aGroup) ) - { - SALOMEDS::Color aColor = aGroup->GetColor(); - if ( aColor.R > 0 || aColor.G > 0 || aColor.B > 0 ) - { - anUpdatedScript += "\n\t"; - anUpdatedScript += (char*)aName.in(); - anUpdatedScript += ".SetColor(SALOMEDS.Color("; - anUpdatedScript += aColor.R; - anUpdatedScript += ","; - anUpdatedScript += aColor.G; - anUpdatedScript += ","; - anUpdatedScript += aColor.B; - anUpdatedScript += "))"; - } - } - } - } - } - // Remove removed objects if ( seqRemoved.Length() > 0 ) { anUpdatedScript += "\n\t## some objects were removed"; @@ -859,8 +1004,10 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl anUpdatedScript += helper + ", '" + aGUIName + "')"; } } - anUpdatedScript += "\n\tif salome.sg.hasDesktop():"; - anUpdatedScript += "\n\t\tsalome.sg.updateObjBrowser(0)"; + + // Issue 0021249: removed (a similar block is dumped by SALOMEDSImpl_Study) + //anUpdatedScript += "\n\tif salome.sg.hasDesktop():"; + //anUpdatedScript += "\n\t\tsalome.sg.updateObjBrowser(0)"; // ----------------------------------------------------------------- // store visual properties of displayed objects @@ -876,7 +1023,13 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl CORBA::string_free(script); } } - anUpdatedScript += "\n\n\tpass\n"; + + if( isMultiFile ) + anUpdatedScript += "\n\tpass"; + anUpdatedScript += "\n"; + + if( !isMultiFile ) // remove unnecessary tabulation + RemoveTabulation( anUpdatedScript ); // ----------------------------------------------------------------- // put string literals describing patterns into separate functions @@ -916,7 +1069,18 @@ TCollection_AsciiString SMESH_Gen_i::DumpPython_impl do functionName = aFunctionType + "_" + ( nb++ ) + "()"; while ( !functionNameSet.insert( functionName.ToCString() ).second ); - anUpdatedScript += helper + "\n\ndef " + functionName + aLongString; // define function + // define function + TCollection_AsciiString funDef = helper + "def " + functionName + aLongString; + if ( isMultiFile ) + { + anUpdatedScript += helper + "\n\n" + funDef; + } + else + { + funDef += "\n\n"; + anUpdatedScript.Insert( 1, funDef); + where += funDef.Length(); + } } anUpdatedScript.InsertBefore( where, functionName ); // call function } diff --git a/src/SMESH_I/SMESH_Filter_i.cxx b/src/SMESH_I/SMESH_Filter_i.cxx index 189e6cbcc..fc5314b8c 100644 --- a/src/SMESH_I/SMESH_Filter_i.cxx +++ b/src/SMESH_I/SMESH_Filter_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Filter_i.cxx // Author : Alexey Petrov, OCC @@ -32,6 +33,7 @@ #include "SMDS_Mesh.hxx" #include "SMDS_MeshNode.hxx" #include "SMDS_MeshElement.hxx" +#include "SMDS_ElemIterator.hxx" #include "SMESHDS_Mesh.hxx" @@ -122,7 +124,7 @@ void Controls::BelongToGeom::init() { if (!myMeshDS || myShape.IsNull()) return; - // is subshape of main shape? + // is sub-shape of main shape? TopoDS_Shape aMainShape = myMeshDS->ShapeToMesh(); if (aMainShape.IsNull()) { myIsSubshape = false; @@ -278,7 +280,7 @@ void Controls::LyingOnGeom::init() { if (!myMeshDS || myShape.IsNull()) return; - // is subshape of main shape? + // is sub-shape of main shape? TopoDS_Shape aMainShape = myMeshDS->ShapeToMesh(); if (aMainShape.IsNull()) { myIsSubshape = false; @@ -485,21 +487,16 @@ static TopoDS_Shape getShapeByName( const char* theName ) { if ( theName != 0 ) { - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); - if (!CORBA::is_nil(aStudy)) + SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var aStudy = aSMESHGen->GetCurrentStudy(); + if ( !aStudy->_is_nil() ) { - SALOMEDS::Study::ListOfSObject_var aList = - aStudy->FindObjectByName( theName, "GEOM" ); + SALOMEDS::Study::ListOfSObject_var aList = aStudy->FindObjectByName( theName, "GEOM" ); if ( aList->length() > 0 ) { - GEOM::GEOM_Object_var aGeomObj = GEOM::GEOM_Object::_narrow( aList[ 0 ]->GetObject() ); - if ( !aGeomObj->_is_nil() ) - { - GEOM::GEOM_Gen_ptr aGEOMGen = SMESH_Gen_i::GetGeomEngine(); - TopoDS_Shape aLocShape = aSMESHGen->GetShapeReader()->GetShape( aGEOMGen, aGeomObj ); - return aLocShape; - } + CORBA::Object_var anObj = aList[ 0 ]->GetObject(); + GEOM::GEOM_Object_var aGeomObj = GEOM::GEOM_Object::_narrow( anObj ); + return aSMESHGen->GeomObjectToShape( aGeomObj ); } } } @@ -508,48 +505,35 @@ static TopoDS_Shape getShapeByName( const char* theName ) static TopoDS_Shape getShapeByID (const char* theID) { - if (theID != 0 && theID != "") { - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); - if (aStudy != 0) { + if ( theID && strlen( theID ) > 0 ) { + SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var aStudy = aSMESHGen->GetCurrentStudy(); + if ( !aStudy->_is_nil() ) { SALOMEDS::SObject_var aSObj = aStudy->FindObjectID(theID); - SALOMEDS::GenericAttribute_var anAttr; - if (!aSObj->_is_nil() && aSObj->FindAttribute(anAttr, "AttributeIOR")) { - SALOMEDS::AttributeIOR_var anIOR = SALOMEDS::AttributeIOR::_narrow(anAttr); - CORBA::String_var aVal = anIOR->Value(); - CORBA::Object_var obj = aStudy->ConvertIORToObject(aVal); + if ( !aSObj->_is_nil() ) { + CORBA::Object_var obj = aSObj->GetObject(); GEOM::GEOM_Object_var aGeomObj = GEOM::GEOM_Object::_narrow(obj); - - if (!aGeomObj->_is_nil()) { - GEOM::GEOM_Gen_ptr aGEOMGen = SMESH_Gen_i::GetGeomEngine(); - TopoDS_Shape aLocShape = aSMESHGen->GetShapeReader()->GetShape( aGEOMGen, aGeomObj ); - return aLocShape; - } + return aSMESHGen->GeomObjectToShape( aGeomObj ); } } } return TopoDS_Shape(); } -static char* getShapeNameByID (const char* theID) +static std::string getShapeNameByID (const char* theID) { - char* aName = ""; - - if (theID != 0 && theID != "") { - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - SALOMEDS::Study_ptr aStudy = aSMESHGen->GetCurrentStudy(); - if (aStudy != 0) { - //SALOMEDS::SObject_var aSObj = aStudy->FindObjectIOR( theID ); + if ( theID && strlen( theID ) > 0 ) { + SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var aStudy = aSMESHGen->GetCurrentStudy(); + if ( !aStudy->_is_nil() ) { SALOMEDS::SObject_var aSObj = aStudy->FindObjectID(theID); - SALOMEDS::GenericAttribute_var anAttr; - if (!aSObj->_is_nil() && aSObj->FindAttribute(anAttr, "AttributeName")) { - SALOMEDS::AttributeName_var aNameAttr = SALOMEDS::AttributeName::_narrow(anAttr); - aName = aNameAttr->Value(); + if ( !aSObj->_is_nil() ) { + CORBA::String_var name = aSObj->GetName(); + return name.in(); } } } - - return aName; + return ""; } /* @@ -570,7 +554,7 @@ Functor_i::Functor_i(): Functor_i::~Functor_i() { - //TPythonDump()<GetValue( theId ); } +SMESH::Histogram* NumericalFunctor_i::GetHistogram(CORBA::Short nbIntervals) +{ + std::vector nbEvents; + std::vector funValues; + std::vector elements; + myNumericalFunctorPtr->GetHistogram(nbIntervals,nbEvents,funValues,elements); + +#ifdef WIN32 + nbIntervals = CORBA::Short( min( nbEvents.size(), funValues.size() - 1)); +#else + nbIntervals = CORBA::Short( std::min( nbEvents.size(), funValues.size() - 1)); +#endif + SMESH::Histogram_var histogram = new SMESH::Histogram; + if ( nbIntervals > 0 ) + { + histogram->length( nbIntervals ); + for ( int i = 0; i < nbIntervals; ++i ) + { + HistogramRectangle& rect = histogram[i]; + rect.nbEvents = nbEvents[i]; + rect.min = funValues[i]; + rect.max = funValues[i+1]; + } + } + return histogram._retn(); +} + void NumericalFunctor_i::SetPrecision( CORBA::Long thePrecision ) { myNumericalFunctorPtr->SetPrecision( thePrecision ); @@ -736,6 +747,36 @@ FunctorType Volume3D_i::GetFunctorType() return SMESH::FT_Volume3D; } +/* + Class : MaxElementLength2D_i + Description : Functor for calculating maximum length of 2D element +*/ +MaxElementLength2D_i::MaxElementLength2D_i() +{ + myNumericalFunctorPtr.reset( new Controls::MaxElementLength2D() ); + myFunctorPtr = myNumericalFunctorPtr; +} + +FunctorType MaxElementLength2D_i::GetFunctorType() +{ + return SMESH::FT_MaxElementLength2D; +} + +/* + Class : MaxElementLength3D_i + Description : Functor for calculating maximum length of 3D element +*/ +MaxElementLength3D_i::MaxElementLength3D_i() +{ + myNumericalFunctorPtr.reset( new Controls::MaxElementLength3D() ); + myFunctorPtr = myNumericalFunctorPtr; +} + +FunctorType MaxElementLength3D_i::GetFunctorType() +{ + return SMESH::FT_MaxElementLength3D; +} + /* Class : Length_i Description : Functor for calculating length off edge @@ -770,11 +811,12 @@ SMESH::Length2D::Values* Length2D_i::GetValues() { INFOS("Length2D_i::GetValues"); SMESH::Controls::Length2D::TValues aValues; - myLength2DPtr->GetValues( aValues ); + (dynamic_cast(myFunctorPtr.get()))->GetValues( aValues ); long i = 0, iEnd = aValues.size(); SMESH::Length2D::Values_var aResult = new SMESH::Length2D::Values(iEnd); + aResult->length(iEnd); SMESH::Controls::Length2D::TValues::const_iterator anIter; for ( anIter = aValues.begin() ; anIter != aValues.end(); anIter++, i++ ) @@ -806,6 +848,21 @@ FunctorType MultiConnection_i::GetFunctorType() return SMESH::FT_MultiConnection; } +/* + Class : BallDiameter_i + Description : Functor returning diameter of a ball element +*/ +BallDiameter_i::BallDiameter_i() +{ + myNumericalFunctorPtr.reset( new Controls::BallDiameter() ); + myFunctorPtr = myNumericalFunctorPtr; +} + +FunctorType BallDiameter_i::GetFunctorType() +{ + return SMESH::FT_BallDiameter; +} + /* Class : MultiConnection2D_i Description : Functor for calculating number of faces conneted to the edge @@ -825,11 +882,12 @@ SMESH::MultiConnection2D::Values* MultiConnection2D_i::GetValues() { INFOS("MultiConnection2D_i::GetValues"); SMESH::Controls::MultiConnection2D::MValues aValues; - myMulticonnection2DPtr->GetValues( aValues ); - + (dynamic_cast(myFunctorPtr.get()))->GetValues( aValues ); + long i = 0, iEnd = aValues.size(); SMESH::MultiConnection2D::Values_var aResult = new SMESH::MultiConnection2D::Values(iEnd); + aResult->length(iEnd); SMESH::Controls::MultiConnection2D::MValues::const_iterator anIter; for ( anIter = aValues.begin() ; anIter != aValues.end(); anIter++, i++ ) @@ -881,6 +939,66 @@ FunctorType BadOrientedVolume_i::GetFunctorType() return SMESH::FT_BadOrientedVolume; } +/* + Class : BareBorderVolume_i + Description : Verify whether a mesh volume has a free facet without a face on it +*/ +BareBorderVolume_i::BareBorderVolume_i() +{ + Controls::PredicatePtr control( new Controls::BareBorderVolume() ); + myFunctorPtr = myPredicatePtr = control; +}; + +FunctorType BareBorderVolume_i::GetFunctorType() +{ + return SMESH::FT_BareBorderVolume; +} + +/* + Class : BareBorderFace_i + Description : Verify whether a mesh face has a free border without an edge on it +*/ +BareBorderFace_i::BareBorderFace_i() +{ + Controls::PredicatePtr control( new Controls::BareBorderFace() ); + myFunctorPtr = myPredicatePtr = control; +}; + +FunctorType BareBorderFace_i::GetFunctorType() +{ + return SMESH::FT_BareBorderFace; +} + +/* + Class : OverConstrainedVolume_i + Description : Verify whether a mesh volume has only one facet shared with other volumes +*/ +OverConstrainedVolume_i::OverConstrainedVolume_i() +{ + Controls::PredicatePtr control( new Controls::OverConstrainedVolume() ); + myFunctorPtr = myPredicatePtr = control; +}; + +FunctorType OverConstrainedVolume_i::GetFunctorType() +{ + return SMESH::FT_OverConstrainedVolume; +} + +/* + Class : OverConstrainedFace_i + Description : Verify whether a mesh face has only one border shared with other faces +*/ +OverConstrainedFace_i::OverConstrainedFace_i() +{ + Controls::PredicatePtr control( new Controls::OverConstrainedFace() ); + myFunctorPtr = myPredicatePtr = control; +}; + +FunctorType OverConstrainedFace_i::GetFunctorType() +{ + return SMESH::FT_OverConstrainedFace; +} + /* Class : BelongToGeom_i Description : Predicate for selection on geometrical support @@ -943,7 +1061,7 @@ void BelongToGeom_i::SetShape( const char* theID, const char* theName ) else myShapeID = 0; - if ( myShapeID && strcmp(myShapeName, getShapeNameByID(myShapeID)) == 0 ) + if ( myShapeID && myShapeName == getShapeNameByID(myShapeID)) myBelongToGeomPtr->SetGeom( getShapeByID(myShapeID) ); else myBelongToGeomPtr->SetGeom( getShapeByName( myShapeName ) ); @@ -1028,7 +1146,7 @@ void BelongToSurface_i::SetShape( const char* theID, const char* theName, Eleme else myShapeID = 0; - if ( myShapeID && strcmp(myShapeName, getShapeNameByID(myShapeID)) == 0 ) + if ( myShapeID && myShapeName == getShapeNameByID(myShapeID)) myElementsOnSurfacePtr->SetSurface( getShapeByID(myShapeID), (SMDSAbs_ElementType)theType ); else myElementsOnSurfacePtr->SetSurface( getShapeByName( myShapeName ), (SMDSAbs_ElementType)theType ); @@ -1198,7 +1316,7 @@ void LyingOnGeom_i::SetShape( const char* theID, const char* theName ) else myShapeID = 0; - if ( myShapeID && strcmp(myShapeName, getShapeNameByID(myShapeID)) == 0 ) + if ( myShapeID && myShapeName == getShapeNameByID(myShapeID)) myLyingOnGeomPtr->SetGeom( getShapeByID(myShapeID) ); else myLyingOnGeomPtr->SetGeom( getShapeByName( myShapeName ) ); @@ -1311,6 +1429,77 @@ FunctorType FreeNodes_i::GetFunctorType() return SMESH::FT_FreeNodes; } +/* + Class : EqualNodes_i + Description : Predicate for Equal nodes +*/ +EqualNodes_i::EqualNodes_i() +{ + myCoincidentNodesPtr.reset(new Controls::CoincidentNodes()); + myFunctorPtr = myPredicatePtr = myCoincidentNodesPtr; +} + +FunctorType EqualNodes_i::GetFunctorType() +{ + return SMESH::FT_EqualNodes; +} + +void EqualNodes_i::SetTolerance( double tol ) +{ + myCoincidentNodesPtr->SetTolerance( tol ); +} + +double EqualNodes_i::GetTolerance() +{ + return myCoincidentNodesPtr->GetTolerance(); +} + +/* + Class : EqualEdges_i + Description : Predicate for Equal Edges +*/ +EqualEdges_i::EqualEdges_i() +{ + myPredicatePtr.reset(new Controls::CoincidentElements1D()); + myFunctorPtr = myPredicatePtr; +} + +FunctorType EqualEdges_i::GetFunctorType() +{ + return SMESH::FT_EqualEdges; +} + +/* + Class : EqualFaces_i + Description : Predicate for Equal Faces +*/ +EqualFaces_i::EqualFaces_i() +{ + myPredicatePtr.reset(new Controls::CoincidentElements2D()); + myFunctorPtr = myPredicatePtr; +} + +FunctorType EqualFaces_i::GetFunctorType() +{ + return SMESH::FT_EqualFaces; +} + +/* + Class : EqualVolumes_i + Description : Predicate for Equal Volumes +*/ +EqualVolumes_i::EqualVolumes_i() +{ + myPredicatePtr.reset(new Controls::CoincidentElements3D()); + myFunctorPtr = myPredicatePtr; +} + +FunctorType EqualVolumes_i::GetFunctorType() +{ + return SMESH::FT_EqualVolumes; +} + + /* Class : RangeOfIds_i Description : Predicate for Range of Ids. @@ -1439,7 +1628,7 @@ void ElemGeomType_i::SetGeometryType(GeometryType theType) GeometryType ElemGeomType_i::GetGeometryType() const { - return (GeometryType)myElemGeomTypePtr->GetGeomType();; + return (GeometryType)myElemGeomTypePtr->GetGeomType(); } FunctorType ElemGeomType_i::GetFunctorType() @@ -1447,6 +1636,49 @@ FunctorType ElemGeomType_i::GetFunctorType() return SMESH::FT_ElemGeomType; } +/* + Class : CoplanarFaces_i + Description : Returns true if a mesh face is a coplanar neighbour to a given one +*/ +CoplanarFaces_i::CoplanarFaces_i() +{ + myCoplanarFacesPtr.reset(new Controls::CoplanarFaces()); + myFunctorPtr = myPredicatePtr = myCoplanarFacesPtr; +} + +void CoplanarFaces_i::SetFace ( CORBA::Long theFaceID ) +{ + myCoplanarFacesPtr->SetFace(theFaceID); + TPythonDump()<SetTolerance(theToler); + TPythonDump()<GetFace(); +} + +char* CoplanarFaces_i::GetFaceAsString () const +{ + TCollection_AsciiString str(Standard_Integer(myCoplanarFacesPtr->GetFace())); + return CORBA::string_dup( str.ToCString() ); +} + +CORBA::Double CoplanarFaces_i::GetTolerance() const +{ + return myCoplanarFacesPtr->GetTolerance(); +} + +FunctorType CoplanarFaces_i::GetFunctorType() +{ + return SMESH::FT_CoplanarFaces; +} + /* Class : Comparator_i Description : Base class for comparators @@ -1458,7 +1690,7 @@ Comparator_i::Comparator_i(): Comparator_i::~Comparator_i() { if ( myNumericalFunctor ) - myNumericalFunctor->Destroy(); + myNumericalFunctor->UnRegister(); } void Comparator_i::SetMargin( CORBA::Double theValue ) @@ -1475,7 +1707,7 @@ CORBA::Double Comparator_i::GetMargin() void Comparator_i::SetNumFunctor( NumericalFunctor_ptr theFunct ) { if ( myNumericalFunctor ) - myNumericalFunctor->Destroy(); + myNumericalFunctor->UnRegister(); myNumericalFunctor = DownCast(theFunct); @@ -1570,13 +1802,13 @@ LogicalNOT_i::LogicalNOT_i() LogicalNOT_i::~LogicalNOT_i() { if ( myPredicate ) - myPredicate->Destroy(); + myPredicate->UnRegister(); } void LogicalNOT_i::SetPredicate( Predicate_ptr thePredicate ) { if ( myPredicate ) - myPredicate->Destroy(); + myPredicate->UnRegister(); myPredicate = SMESH::GetPredicate(thePredicate); @@ -1610,10 +1842,10 @@ LogicalBinary_i::LogicalBinary_i() LogicalBinary_i::~LogicalBinary_i() { if ( myPredicate1 ) - myPredicate1->Destroy(); + myPredicate1->UnRegister(); if ( myPredicate2 ) - myPredicate2->Destroy(); + myPredicate2->UnRegister(); } void LogicalBinary_i::SetMesh( SMESH_Mesh_ptr theMesh ) @@ -1628,7 +1860,7 @@ void LogicalBinary_i::SetMesh( SMESH_Mesh_ptr theMesh ) void LogicalBinary_i::SetPredicate1( Predicate_ptr thePredicate ) { if ( myPredicate1 ) - myPredicate1->Destroy(); + myPredicate1->UnRegister(); myPredicate1 = SMESH::GetPredicate(thePredicate); @@ -1642,7 +1874,7 @@ void LogicalBinary_i::SetPredicate1( Predicate_ptr thePredicate ) void LogicalBinary_i::SetPredicate2( Predicate_ptr thePredicate ) { if ( myPredicate2 ) - myPredicate2->Destroy(); + myPredicate2->UnRegister(); myPredicate2 = SMESH::GetPredicate(thePredicate); @@ -1715,7 +1947,7 @@ FilterManager_i::FilterManager_i() FilterManager_i::~FilterManager_i() { - //TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); + TPythonDump()<_this(); - TPythonDump()<Destroy(); + myPredicate->UnRegister(); if(!CORBA::is_nil(myMesh)) - myMesh->Destroy(); + myMesh->UnRegister(); - //TPythonDump()<Destroy(); + myPredicate->UnRegister(); myPredicate = SMESH::GetPredicate(thePredicate); @@ -2071,8 +2399,13 @@ void Filter_i::SetPredicate( Predicate_ptr thePredicate ) { myFilter.SetPredicate( myPredicate->GetPredicate() ); myPredicate->Register(); + if ( const SMDS_Mesh* aMesh = MeshPtr2SMDSMesh(myMesh)) + myPredicate->GetPredicate()->SetMesh( aMesh ); TPythonDump()<::iterator i = myWaiters.begin(); + for ( ; i != myWaiters.end(); ++i ) + (*i)->PredicateChanged(); } //======================================================================= @@ -2096,10 +2429,14 @@ SetMesh( SMESH_Mesh_ptr theMesh ) theMesh->Register(); if(!CORBA::is_nil(myMesh)) - myMesh->Destroy(); + myMesh->UnRegister(); - myMesh = theMesh; + myMesh = SMESH_Mesh::_duplicate( theMesh ); TPythonDump()<GetPredicate()->SetMesh( aMesh ); } SMESH::long_array* @@ -2150,6 +2487,119 @@ GetElementsId( SMESH_Mesh_ptr theMesh ) return anArray._retn(); } +template +static void collectMeshInfo(const TIterator& theItr, + TPredicate& thePred, + SMESH::long_array& theRes) +{ + if (!theItr) + return; + while (theItr->more()) { + const SMDS_MeshElement* anElem = theItr->next(); + if ( thePred->IsSatisfy( anElem->GetID() ) ) + theRes[ anElem->GetEntityType() ]++; + } +} + +//============================================================================= +/*! + * \brief Returns statistic of mesh elements + */ +//============================================================================= +SMESH::long_array* ::Filter_i::GetMeshInfo() +{ + SMESH::long_array_var aRes = new SMESH::long_array(); + aRes->length(SMESH::Entity_Last); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = 0; + + if(!CORBA::is_nil(myMesh) && myPredicate) { + const SMDS_Mesh* aMesh = MeshPtr2SMDSMesh(myMesh); + SMDS_ElemIteratorPtr it; + switch( GetElementType() ) + { + case SMDSAbs_Node: + collectMeshInfo(aMesh->nodesIterator(),myPredicate,aRes); + break; + case SMDSAbs_Edge: + collectMeshInfo(aMesh->edgesIterator(),myPredicate,aRes); + break; + case SMDSAbs_Face: + collectMeshInfo(aMesh->facesIterator(),myPredicate,aRes); + break; + case SMDSAbs_Volume: + collectMeshInfo(aMesh->volumesIterator(),myPredicate,aRes); + break; + case SMDSAbs_All: + default: + collectMeshInfo(aMesh->elementsIterator(),myPredicate,aRes); + break; + } + } + + return aRes._retn(); +} + +//================================================================================ +/*! + * \brief Return GetElementType() within an array + * Implement SMESH_IDSource interface + */ +//================================================================================ + +SMESH::array_of_ElementType* Filter_i::GetTypes() +{ + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + + // check if any element passes through the filter + if ( !CORBA::is_nil(myMesh) && myPredicate ) + { + const SMDS_Mesh* aMesh = MeshPtr2SMDSMesh(myMesh); + SMDS_ElemIteratorPtr it = aMesh->elementsIterator( SMDSAbs_ElementType( GetElementType() )); + bool satisfies = false; + while ( !satisfies && it->more() ) + satisfies = myPredicate->IsSatisfy( it->next()->GetID() ); + if ( satisfies ) { + types->length( 1 ); + types[0] = GetElementType(); + } + } + return types._retn(); +} + +//======================================================================= +//function : GetMesh +//purpose : Returns mesh +//======================================================================= + +SMESH::SMESH_Mesh_ptr Filter_i::GetMesh() +{ + return SMESH_Mesh::_duplicate( myMesh ); +} + +//================================================================================ +/*! + * \brief Stores an object to be notified on change of predicate + */ +//================================================================================ + +void Filter_i::AddWaiter( TPredicateChangeWaiter* waiter ) +{ + if ( waiter ) + myWaiters.push_back( waiter ); +} + +//================================================================================ +/*! + * \brief Removes an object to be notified on change of predicate + */ +//================================================================================ + +void Filter_i::RemoveWaiter( TPredicateChangeWaiter* waiter ) +{ + myWaiters.remove( waiter ); +} + //======================================================================= // name : getCriteria // Purpose : Retrieve criterions from predicate @@ -2166,6 +2616,14 @@ static inline bool getCriteria( Predicate_i* thePred, case FT_FreeFaces: case FT_LinearOrQuadratic: case FT_FreeNodes: + case FT_EqualEdges: + case FT_EqualFaces: + case FT_EqualVolumes: + case FT_BadOrientedVolume: + case FT_BareBorderVolume: + case FT_BareBorderFace: + case FT_OverConstrainedVolume: + case FT_OverConstrainedFace: { CORBA::ULong i = theCriteria->length(); theCriteria->length( i + 1 ); @@ -2189,6 +2647,7 @@ static inline bool getCriteria( Predicate_i* thePred, theCriteria[ i ].ThresholdStr = aPred->GetShapeName(); theCriteria[ i ].ThresholdID = aPred->GetShapeID(); theCriteria[ i ].TypeOfElement = aPred->GetElementType(); + theCriteria[ i ].Tolerance = aPred->GetTolerance(); return true; } @@ -2224,34 +2683,51 @@ static inline bool getCriteria( Predicate_i* thePred, theCriteria[ i ].ThresholdStr = aPred->GetShapeName(); theCriteria[ i ].ThresholdID = aPred->GetShapeID(); theCriteria[ i ].TypeOfElement = aPred->GetElementType(); + theCriteria[ i ].Tolerance = aPred->GetTolerance(); return true; } - case FT_RangeOfIds: + case FT_CoplanarFaces: { - RangeOfIds_i* aPred = dynamic_cast( thePred ); + CoplanarFaces_i* aPred = dynamic_cast( thePred ); CORBA::ULong i = theCriteria->length(); theCriteria->length( i + 1 ); theCriteria[ i ] = createCriterion(); + CORBA::String_var faceId = aPred->GetFaceAsString(); - theCriteria[ i ].Type = FT_RangeOfIds; - theCriteria[ i ].ThresholdStr = aPred->GetRangeStr(); - theCriteria[ i ].TypeOfElement = aPred->GetElementType(); + theCriteria[ i ].Type = FT_CoplanarFaces; + theCriteria[ i ].ThresholdID = faceId; + theCriteria[ i ].Tolerance = aPred->GetTolerance(); return true; } - case FT_BadOrientedVolume: + case FT_EqualNodes: + { + EqualNodes_i* aPred = dynamic_cast( thePred ); + + CORBA::ULong i = theCriteria->length(); + theCriteria->length( i + 1 ); + + theCriteria[ i ] = createCriterion(); + + theCriteria[ i ].Type = FT_EqualNodes; + theCriteria[ i ].Tolerance = aPred->GetTolerance(); + + return true; + } + case FT_RangeOfIds: { - BadOrientedVolume_i* aPred = dynamic_cast( thePred ); + RangeOfIds_i* aPred = dynamic_cast( thePred ); CORBA::ULong i = theCriteria->length(); theCriteria->length( i + 1 ); theCriteria[ i ] = createCriterion(); - theCriteria[ i ].Type = FT_BadOrientedVolume; + theCriteria[ i ].Type = FT_RangeOfIds; + theCriteria[ i ].ThresholdStr = aPred->GetRangeStr(); theCriteria[ i ].TypeOfElement = aPred->GetElementType(); return true; @@ -2350,7 +2826,7 @@ CORBA::Boolean Filter_i::GetCriteria( SMESH::Filter::Criteria_out theCriteria ) CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria ) { if ( myPredicate != 0 ) - myPredicate->Destroy(); + myPredicate->UnRegister(); SMESH::FilterManager_i* aFilter = new SMESH::FilterManager_i(); FilterManager_ptr aFilterMgr = aFilter->_this(); @@ -2376,16 +2852,20 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria { TPythonDump pd; - pd << "aCriterion = SMESH.Filter.Criterion(" << aCriterion << "," << aCompare - << "," << aThreshold << ",'" << aThresholdStr; - if (aThresholdID) - pd << "',salome.ObjectToID(" << aThresholdID - << ")," << aUnary << "," << aBinary << "," << aTolerance - << "," << aTypeOfElem << "," << aPrecision << ")"; - else - pd << "',''," << aUnary << "," << aBinary << "," << aTolerance - << "," << aTypeOfElem << "," << aPrecision << ")"; + pd << "aCriterion = SMESH.Filter.Criterion(" + << aCriterion << ", " + << aCompare << ", " + << aThreshold << ", '" + << aThresholdStr << "', '"; + if (aThresholdID) pd << aThresholdID; + pd << "', " + << aUnary << ", " + << aBinary << ", " + << aTolerance << ", " + << aTypeOfElem << ", " + << aPrecision << ")"; } + TPythonDump pd; SMESH::Predicate_ptr aPredicate = SMESH::Predicate::_nil(); SMESH::NumericalFunctor_ptr aFunctor = SMESH::NumericalFunctor::_nil(); @@ -2430,6 +2910,15 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria case SMESH::FT_Volume3D: aFunctor = aFilterMgr->CreateVolume3D(); break; + case SMESH::FT_MaxElementLength2D: + aFunctor = aFilterMgr->CreateMaxElementLength2D(); + break; + case SMESH::FT_MaxElementLength3D: + aFunctor = aFilterMgr->CreateMaxElementLength3D(); + break; + case SMESH::FT_BallDiameter: + aFunctor = aFilterMgr->CreateBallDiameter(); + break; // Predicates @@ -2445,11 +2934,28 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria case SMESH::FT_FreeNodes: aPredicate = aFilterMgr->CreateFreeNodes(); break; + case SMESH::FT_EqualNodes: + { + SMESH::EqualNodes_ptr pred = aFilterMgr->CreateEqualNodes(); + pred->SetTolerance( aTolerance ); + aPredicate = pred; + break; + } + case SMESH::FT_EqualEdges: + aPredicate = aFilterMgr->CreateEqualEdges(); + break; + case SMESH::FT_EqualFaces: + aPredicate = aFilterMgr->CreateEqualFaces(); + break; + case SMESH::FT_EqualVolumes: + aPredicate = aFilterMgr->CreateEqualVolumes(); + break; case SMESH::FT_BelongToGeom: { SMESH::BelongToGeom_ptr tmpPred = aFilterMgr->CreateBelongToGeom(); tmpPred->SetElementType( aTypeOfElem ); tmpPred->SetShape( aThresholdID, aThresholdStr ); + tmpPred->SetTolerance( aTolerance ); aPredicate = tmpPred; } break; @@ -2475,7 +2981,8 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria { SMESH::LyingOnGeom_ptr tmpPred = aFilterMgr->CreateLyingOnGeom(); tmpPred->SetElementType( aTypeOfElem ); - tmpPred->SetShape( aThresholdID, aThresholdStr ); + tmpPred->SetShape( aThresholdID, aThresholdStr ); + tmpPred->SetTolerance( aTolerance ); aPredicate = tmpPred; } break; @@ -2492,6 +2999,26 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria aPredicate = aFilterMgr->CreateBadOrientedVolume(); } break; + case SMESH::FT_BareBorderVolume: + { + aPredicate = aFilterMgr->CreateBareBorderVolume(); + } + break; + case SMESH::FT_BareBorderFace: + { + aPredicate = aFilterMgr->CreateBareBorderFace(); + } + break; + case SMESH::FT_OverConstrainedVolume: + { + aPredicate = aFilterMgr->CreateOverConstrainedVolume(); + } + break; + case SMESH::FT_OverConstrainedFace: + { + aPredicate = aFilterMgr->CreateOverConstrainedFace(); + } + break; case SMESH::FT_LinearOrQuadratic: { SMESH::LinearOrQuadratic_ptr tmpPred = aFilterMgr->CreateLinearOrQuadratic(); @@ -2511,7 +3038,15 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria { SMESH::ElemGeomType_ptr tmpPred = aFilterMgr->CreateElemGeomType(); tmpPred->SetElementType( aTypeOfElem ); - tmpPred->SetGeometryType( (GeometryType)(aThreshold + 0.5) ); + tmpPred->SetGeometryType( (GeometryType)(int)(aThreshold + 0.5) ); + aPredicate = tmpPred; + break; + } + case SMESH::FT_CoplanarFaces: + { + SMESH::CoplanarFaces_ptr tmpPred = aFilterMgr->CreateCoplanarFaces(); + tmpPred->SetFace( atol (aThresholdID )); + tmpPred->SetTolerance( aTolerance ); aPredicate = tmpPred; break; } @@ -2559,10 +3094,10 @@ CORBA::Boolean Filter_i::SetCriteria( const SMESH::Filter::Criteria& theCriteria // logical op aPredicates.push_back( aPredicate ); aBinaries.push_back( aBinary ); - TPythonDump()<<"aCriteria.append(aCriterion)"; + pd <<"aCriteria.append(aCriterion)"; } // end of for - TPythonDump()<_this(); + // if ( SMESH::Functor_i* fun = SMESH::DownCast( anObj )) + // TPythonDump() << fun << " = " << this << ".GetPredicate()"; return anObj._retn(); } } @@ -2717,19 +3254,29 @@ static inline LDOMString toString( CORBA::Long theType ) case FT_Skew : return "Skew"; case FT_Area : return "Area"; case FT_Volume3D : return "Volume3D"; + case FT_MaxElementLength2D: return "Max element length 2D"; + case FT_MaxElementLength3D: return "Max element length 3D"; case FT_BelongToGeom : return "Belong to Geom"; case FT_BelongToPlane : return "Belong to Plane"; case FT_BelongToCylinder: return "Belong to Cylinder"; case FT_BelongToGenSurface: return "Belong to Generic Surface"; case FT_LyingOnGeom : return "Lying on Geom"; - case FT_BadOrientedVolume: return "Bad Oriented Volume"; + case FT_BadOrientedVolume:return "Bad Oriented Volume"; + case FT_BareBorderVolume: return "Volumes with bare border"; + case FT_BareBorderFace : return "Faces with bare border"; + case FT_OverConstrainedVolume: return "Over-constrained Volumes"; + case FT_OverConstrainedFace : return "Over-constrained Faces"; case FT_RangeOfIds : return "Range of IDs"; case FT_FreeBorders : return "Free borders"; case FT_FreeEdges : return "Free edges"; case FT_FreeFaces : return "Free faces"; case FT_FreeNodes : return "Free nodes"; + case FT_EqualNodes : return "Equal nodes"; + case FT_EqualEdges : return "Equal edges"; + case FT_EqualFaces : return "Equal faces"; + case FT_EqualVolumes : return "Equal volumes"; case FT_MultiConnection : return "Borders at multi-connections"; - case FT_MultiConnection2D: return "Borders at multi-connections 2D"; + case FT_MultiConnection2D:return "Borders at multi-connections 2D"; case FT_Length : return "Length"; case FT_Length2D : return "Length 2D"; case FT_LessThan : return "Less than"; @@ -2759,6 +3306,8 @@ static inline SMESH::FunctorType toFunctorType( const LDOMString& theStr ) else if ( theStr.equals( "Skew" ) ) return FT_Skew; else if ( theStr.equals( "Area" ) ) return FT_Area; else if ( theStr.equals( "Volume3D" ) ) return FT_Volume3D; + else if ( theStr.equals( "Max element length 2D" ) ) return FT_MaxElementLength2D; + else if ( theStr.equals( "Max element length 3D" ) ) return FT_MaxElementLength3D; else if ( theStr.equals( "Belong to Geom" ) ) return FT_BelongToGeom; else if ( theStr.equals( "Belong to Plane" ) ) return FT_BelongToPlane; else if ( theStr.equals( "Belong to Cylinder" ) ) return FT_BelongToCylinder; @@ -2768,12 +3317,20 @@ static inline SMESH::FunctorType toFunctorType( const LDOMString& theStr ) else if ( theStr.equals( "Free edges" ) ) return FT_FreeEdges; else if ( theStr.equals( "Free faces" ) ) return FT_FreeFaces; else if ( theStr.equals( "Free nodes" ) ) return FT_FreeNodes; + else if ( theStr.equals( "Equal nodes" ) ) return FT_EqualNodes; + else if ( theStr.equals( "Equal edges" ) ) return FT_EqualEdges; + else if ( theStr.equals( "Equal faces" ) ) return FT_EqualFaces; + else if ( theStr.equals( "Equal volumes" ) ) return FT_EqualVolumes; else if ( theStr.equals( "Borders at multi-connections" ) ) return FT_MultiConnection; // else if ( theStr.equals( "Borders at multi-connections 2D" ) ) return FT_MultiConnection2D; else if ( theStr.equals( "Length" ) ) return FT_Length; // else if ( theStr.equals( "Length2D" ) ) return FT_Length2D; else if ( theStr.equals( "Range of IDs" ) ) return FT_RangeOfIds; else if ( theStr.equals( "Bad Oriented Volume" ) ) return FT_BadOrientedVolume; + else if ( theStr.equals( "Volumes with bare border" ) ) return FT_BareBorderVolume; + else if ( theStr.equals( "Faces with bare border" ) ) return FT_BareBorderFace; + else if ( theStr.equals( "Over-constrained Volumes" ) ) return FT_OverConstrainedVolume; + else if ( theStr.equals( "Over-constrained Faces" ) ) return FT_OverConstrainedFace; else if ( theStr.equals( "Less than" ) ) return FT_LessThan; else if ( theStr.equals( "More than" ) ) return FT_MoreThan; else if ( theStr.equals( "Equal to" ) ) return FT_EqualTo; @@ -3000,7 +3557,7 @@ FilterLibrary_i::FilterLibrary_i() FilterLibrary_i::~FilterLibrary_i() { delete myFileName; - //TPythonDump()<(theFilter)) - TPythonDump()< SMESH::FT_Undefined ) + return "FT_Undefined"; + return getFunctNames()[ ft ]; +} + +//================================================================================ +/*! + * \brief Converts a string to FunctorType. This is reverse of FunctorTypeToString() + */ +//================================================================================ + +SMESH::FunctorType SMESH::StringToFunctorType(const char* str) +{ + std::string name( str + 3 ); // skip "FT_" + const char** functNames = getFunctNames(); + int ft = 0; + for ( ; ft < SMESH::FT_Undefined; ++ft ) + if ( name == ( functNames[ft] + 3 )) + break; + + //ASSERT( strcmp( str, FunctorTypeToString( SMESH::FunctorType( ft ))) == 0 ); + + return SMESH::FunctorType( ft ); +} diff --git a/src/SMESH_I/SMESH_Filter_i.hxx b/src/SMESH_I/SMESH_Filter_i.hxx index 3b654e880..cb82aa6d5 100644 --- a/src/SMESH_I/SMESH_Filter_i.hxx +++ b/src/SMESH_I/SMESH_Filter_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Filter_i.hxx // Author : Alexey Petrov, OCC @@ -38,11 +39,14 @@ #include "SALOME_GenericObj_i.hh" #include "SMESH_ControlsDef.hxx" +#include + class SMESHDS_Mesh; namespace SMESH { + // ================================================================================ namespace Controls { @@ -107,10 +111,10 @@ namespace SMESH double GetTolerance(); virtual bool Contains( const SMESHDS_Mesh* theMeshDS, - const TopoDS_Shape& theShape, - const SMDS_MeshElement* theElem, - TopAbs_ShapeEnum theFindShapeEnum, - TopAbs_ShapeEnum theAvoidShapeEnum = TopAbs_SHAPE ); + const TopoDS_Shape& theShape, + const SMDS_MeshElement* theElem, + TopAbs_ShapeEnum theFindShapeEnum, + TopAbs_ShapeEnum theAvoidShapeEnum = TopAbs_SHAPE ); private: virtual void init(); @@ -122,8 +126,10 @@ namespace SMESH Controls::ElementsOnShapePtr myElementsOnShapePtr; // only if myIsSubshape == false }; typedef boost::shared_ptr LyingOnGeomPtr; - } - + + } // namespace Controls + + // ================================================================================ /* FUNCTORS */ @@ -133,7 +139,7 @@ namespace SMESH Description : An abstact class for all functors */ class SMESH_I_EXPORT Functor_i: public virtual POA_SMESH::Functor, - public virtual SALOME::GenericObj_i + public virtual SALOME::GenericObj_i { public: void SetMesh( SMESH_Mesh_ptr theMesh ); @@ -152,10 +158,11 @@ namespace SMESH Description : Base class for numerical functors */ class SMESH_I_EXPORT NumericalFunctor_i: public virtual POA_SMESH::NumericalFunctor, - public virtual Functor_i + public virtual Functor_i { public: CORBA::Double GetValue( CORBA::Long theElementId ); + SMESH::Histogram* GetHistogram(CORBA::Short nbIntervals); void SetPrecision( CORBA::Long thePrecision ); CORBA::Long GetPrecision(); Controls::NumericalFunctorPtr GetNumericalFunctor(); @@ -170,7 +177,7 @@ namespace SMESH Description : Functor for calculation of minimum angle */ class SMESH_I_EXPORT MinimumAngle_i: public virtual POA_SMESH::MinimumAngle, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: MinimumAngle_i(); @@ -183,7 +190,7 @@ namespace SMESH Description : Functor for calculating aspect ratio */ class SMESH_I_EXPORT AspectRatio_i: public virtual POA_SMESH::AspectRatio, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: AspectRatio_i(); @@ -196,7 +203,7 @@ namespace SMESH Description : Functor for calculating aspect ratio for 3D */ class SMESH_I_EXPORT AspectRatio3D_i: public virtual POA_SMESH::AspectRatio3D, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: AspectRatio3D_i(); @@ -209,7 +216,7 @@ namespace SMESH Description : Functor for calculating warping */ class SMESH_I_EXPORT Warping_i: public virtual POA_SMESH::Warping, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Warping_i(); @@ -222,7 +229,7 @@ namespace SMESH Description : Functor for calculating taper */ class SMESH_I_EXPORT Taper_i: public virtual POA_SMESH::Taper, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Taper_i(); @@ -235,7 +242,7 @@ namespace SMESH Description : Functor for calculating skew in degrees */ class SMESH_I_EXPORT Skew_i: public virtual POA_SMESH::Skew, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Skew_i(); @@ -248,7 +255,7 @@ namespace SMESH Description : Functor for calculating area */ class SMESH_I_EXPORT Area_i: public virtual POA_SMESH::Area, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Area_i(); @@ -261,7 +268,7 @@ namespace SMESH Description : Functor for calculating volume of 3D element */ class SMESH_I_EXPORT Volume3D_i: public virtual POA_SMESH::Volume3D, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Volume3D_i(); @@ -269,12 +276,38 @@ namespace SMESH }; + /* + Class : MaxElementLength2D_i + Description : Functor for calculating maximum length of 2D element + */ + class SMESH_I_EXPORT MaxElementLength2D_i: public virtual POA_SMESH::MaxElementLength2D, + public virtual NumericalFunctor_i + { + public: + MaxElementLength2D_i(); + FunctorType GetFunctorType(); + }; + + + /* + Class : MaxElementLength3D_i + Description : Functor for calculating maximum length of 3D element + */ + class SMESH_I_EXPORT MaxElementLength3D_i: public virtual POA_SMESH::MaxElementLength3D, + public virtual NumericalFunctor_i + { + public: + MaxElementLength3D_i(); + FunctorType GetFunctorType(); + }; + + /* Class : Length_i Description : Functor for calculating length of edge */ class SMESH_I_EXPORT Length_i: public virtual POA_SMESH::Length, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Length_i(); @@ -286,7 +319,7 @@ namespace SMESH Description : Functor for calculating length of edge */ class SMESH_I_EXPORT Length2D_i: public virtual POA_SMESH::Length2D, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: Length2D_i(); @@ -303,7 +336,7 @@ namespace SMESH Description : Functor for calculating number of faces conneted to the edge */ class SMESH_I_EXPORT MultiConnection_i: public virtual POA_SMESH::MultiConnection, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: MultiConnection_i(); @@ -315,7 +348,7 @@ namespace SMESH Description : Functor for calculating number of faces conneted to the edge */ class SMESH_I_EXPORT MultiConnection2D_i: public virtual POA_SMESH::MultiConnection2D, - public virtual NumericalFunctor_i + public virtual NumericalFunctor_i { public: MultiConnection2D_i(); @@ -326,6 +359,18 @@ namespace SMESH Controls::MultiConnection2DPtr myMulticonnection2DPtr; }; + /* + Class : BallDiameter_i + Description : Functor returning diameter of a ball element + */ + class SMESH_I_EXPORT BallDiameter_i: public virtual POA_SMESH::BallDiameter, + public virtual NumericalFunctor_i + { + public: + BallDiameter_i(); + FunctorType GetFunctorType(); + }; + /* PREDICATES @@ -335,7 +380,7 @@ namespace SMESH Description : Base class for all predicates */ class SMESH_I_EXPORT Predicate_i: public virtual POA_SMESH::Predicate, - public virtual Functor_i + public virtual Functor_i { public: CORBA::Boolean IsSatisfy( CORBA::Long theElementId ); @@ -352,19 +397,67 @@ namespace SMESH the point of view of MED convention */ class SMESH_I_EXPORT BadOrientedVolume_i: public virtual POA_SMESH::BadOrientedVolume, - public virtual Predicate_i + public virtual Predicate_i { public: BadOrientedVolume_i(); FunctorType GetFunctorType(); }; + /* + Class : BareBorderVolume_i + Description : Verify whether a mesh volume has a free facet without a face on it + */ + class SMESH_I_EXPORT BareBorderVolume_i: public virtual POA_SMESH::BareBorderVolume, + public virtual Predicate_i + { + public: + BareBorderVolume_i(); + FunctorType GetFunctorType(); + }; + + /* + Class : BareBorderFace_i + Description : Verify whether a mesh face has a free border without an edge on it + */ + class SMESH_I_EXPORT BareBorderFace_i: public virtual POA_SMESH::BareBorderFace, + public virtual Predicate_i + { + public: + BareBorderFace_i(); + FunctorType GetFunctorType(); + }; + + /* + Class : OverConstrainedVolume_i + Description : Verify whether a mesh volume has only one facet shared with other volumes + */ + class SMESH_I_EXPORT OverConstrainedVolume_i: public virtual POA_SMESH::OverConstrainedVolume, + public virtual Predicate_i + { + public: + OverConstrainedVolume_i(); + FunctorType GetFunctorType(); + }; + + /* + Class : OverConstrainedFace_i + Description : Verify whether a mesh face has only one border shared with other faces + */ + class SMESH_I_EXPORT OverConstrainedFace_i: public virtual POA_SMESH::OverConstrainedFace, + public virtual Predicate_i + { + public: + OverConstrainedFace_i(); + FunctorType GetFunctorType(); + }; + /* Class : BelongToGeom_i Description : Predicate for selection on geometrical support */ class SMESH_I_EXPORT BelongToGeom_i: public virtual POA_SMESH::BelongToGeom, - public virtual Predicate_i + public virtual Predicate_i { public: BelongToGeom_i(); @@ -395,7 +488,7 @@ namespace SMESH Description : Verify whether mesh element lie in pointed Geom planar object */ class SMESH_I_EXPORT BelongToSurface_i: public virtual POA_SMESH::BelongToSurface, - public virtual Predicate_i + public virtual Predicate_i { public: BelongToSurface_i( const Handle(Standard_Type)& ); @@ -426,7 +519,7 @@ namespace SMESH Description : Verify whether mesh element lie in pointed Geom planar object */ class SMESH_I_EXPORT BelongToPlane_i: public virtual POA_SMESH::BelongToPlane, - public virtual BelongToSurface_i + public virtual BelongToSurface_i { public: BelongToPlane_i(); @@ -439,7 +532,7 @@ namespace SMESH Description : Verify whether mesh element lie in pointed Geom cylindrical object */ class SMESH_I_EXPORT BelongToCylinder_i: public virtual POA_SMESH::BelongToCylinder, - public virtual BelongToSurface_i + public virtual BelongToSurface_i { public: BelongToCylinder_i(); @@ -465,7 +558,7 @@ namespace SMESH Description : Predicate for selection on geometrical support(lying or partially lying) */ class SMESH_I_EXPORT LyingOnGeom_i: public virtual POA_SMESH::LyingOnGeom, - public virtual Predicate_i + public virtual Predicate_i { public: LyingOnGeom_i(); @@ -496,7 +589,7 @@ namespace SMESH Description : Predicate for free borders */ class SMESH_I_EXPORT FreeBorders_i: public virtual POA_SMESH::FreeBorders, - public virtual Predicate_i + public virtual Predicate_i { public: FreeBorders_i(); @@ -509,7 +602,7 @@ namespace SMESH Description : Predicate for free edges */ class SMESH_I_EXPORT FreeEdges_i: public virtual POA_SMESH::FreeEdges, - public virtual Predicate_i + public virtual Predicate_i { public: FreeEdges_i(); @@ -526,7 +619,7 @@ namespace SMESH Description : Predicate for free faces */ class SMESH_I_EXPORT FreeFaces_i: public virtual POA_SMESH::FreeFaces, - public virtual Predicate_i + public virtual Predicate_i { public: FreeFaces_i(); @@ -539,7 +632,7 @@ namespace SMESH Description : Predicate for free nodes */ class SMESH_I_EXPORT FreeNodes_i: public virtual POA_SMESH::FreeNodes, - public virtual Predicate_i + public virtual Predicate_i { public: FreeNodes_i(); @@ -547,12 +640,63 @@ namespace SMESH }; + /* + Class : EqualNodes_i + Description : Predicate for equal nodes + */ + class SMESH_I_EXPORT EqualNodes_i: public virtual POA_SMESH::EqualNodes, + public virtual Predicate_i + { + public: + EqualNodes_i(); + FunctorType GetFunctorType(); + void SetTolerance( double ); + double GetTolerance(); + + private: + Controls::CoincidentNodesPtr myCoincidentNodesPtr; + }; + /* + Class : EqualEdges_i + Description : Predicate for equal edges + */ + class SMESH_I_EXPORT EqualEdges_i: public virtual POA_SMESH::EqualEdges, + public virtual Predicate_i + { + public: + EqualEdges_i(); + FunctorType GetFunctorType(); + }; + /* + Class : EqualFaces_i + Description : Predicate for equal Faces + */ + class SMESH_I_EXPORT EqualFaces_i: public virtual POA_SMESH::EqualFaces, + public virtual Predicate_i + { + public: + EqualFaces_i(); + FunctorType GetFunctorType(); + }; + /* + Class : EqualVolumes_i + Description : Predicate for equal Volumes + */ + class SMESH_I_EXPORT EqualVolumes_i: public virtual POA_SMESH::EqualVolumes, + public virtual Predicate_i + { + public: + EqualVolumes_i(); + FunctorType GetFunctorType(); + }; + + /* Class : RangeOfIds_i Description : Predicate for Range of Ids */ class SMESH_I_EXPORT RangeOfIds_i: public virtual POA_SMESH::RangeOfIds, - public virtual Predicate_i + public virtual Predicate_i { public: RangeOfIds_i(); @@ -572,7 +716,7 @@ namespace SMESH Description : Verify whether a mesh element is linear */ class SMESH_I_EXPORT LinearOrQuadratic_i: public virtual POA_SMESH::LinearOrQuadratic, - public virtual Predicate_i + public virtual Predicate_i { public: LinearOrQuadratic_i(); @@ -588,7 +732,7 @@ namespace SMESH Description : Functor for check color of group to whic mesh element belongs to */ class SMESH_I_EXPORT GroupColor_i: public virtual POA_SMESH::GroupColor, - public virtual Predicate_i + public virtual Predicate_i { public: GroupColor_i(); @@ -607,7 +751,7 @@ namespace SMESH Description : Functor for check element geometry type */ class SMESH_I_EXPORT ElemGeomType_i: public virtual POA_SMESH::ElemGeomType, - public virtual Predicate_i + public virtual Predicate_i { public: ElemGeomType_i(); @@ -621,12 +765,32 @@ namespace SMESH Controls::ElemGeomTypePtr myElemGeomTypePtr; }; + /* + Class : CoplanarFaces_i + Description : Returns true if a mesh face is a coplanar neighbour to a given one + */ + class SMESH_I_EXPORT CoplanarFaces_i: public virtual POA_SMESH::CoplanarFaces, + public virtual Predicate_i + { + public: + CoplanarFaces_i(); + FunctorType GetFunctorType(); + + void SetFace ( CORBA::Long theFaceID ); + void SetTolerance( CORBA::Double theToler ); + char* GetFaceAsString () const; + CORBA::Long GetFace () const; + CORBA::Double GetTolerance () const; + private: + Controls::CoplanarFacesPtr myCoplanarFacesPtr; + }; + /* Class : Comparator_i Description : Base class for comparators */ class SMESH_I_EXPORT Comparator_i: public virtual POA_SMESH::Comparator, - public virtual Predicate_i + public virtual Predicate_i { public: virtual ~Comparator_i(); @@ -651,7 +815,7 @@ namespace SMESH Description : Comparator "<" */ class SMESH_I_EXPORT LessThan_i: public virtual POA_SMESH::LessThan, - public virtual Comparator_i + public virtual Comparator_i { public: LessThan_i(); @@ -664,7 +828,7 @@ namespace SMESH Description : Comparator ">" */ class SMESH_I_EXPORT MoreThan_i: public virtual POA_SMESH::MoreThan, - public virtual Comparator_i + public virtual Comparator_i { public: MoreThan_i(); @@ -677,7 +841,7 @@ namespace SMESH Description : Comparator "=" */ class SMESH_I_EXPORT EqualTo_i: public virtual POA_SMESH::EqualTo, - public virtual Comparator_i + public virtual Comparator_i { public: EqualTo_i(); @@ -695,7 +859,7 @@ namespace SMESH Description : Logical NOT predicate */ class SMESH_I_EXPORT LogicalNOT_i: public virtual POA_SMESH::LogicalNOT, - public virtual Predicate_i + public virtual Predicate_i { public: LogicalNOT_i(); @@ -716,7 +880,7 @@ namespace SMESH Description : Base class for binary logical predicate */ class SMESH_I_EXPORT LogicalBinary_i: public virtual POA_SMESH::LogicalBinary, - public virtual Predicate_i + public virtual Predicate_i { public: virtual ~LogicalBinary_i(); @@ -742,7 +906,7 @@ namespace SMESH Description : Logical AND */ class SMESH_I_EXPORT LogicalAND_i: public virtual POA_SMESH::LogicalAND, - public virtual LogicalBinary_i + public virtual LogicalBinary_i { public: LogicalAND_i(); @@ -755,7 +919,7 @@ namespace SMESH Description : Logical OR */ class SMESH_I_EXPORT LogicalOR_i: public virtual POA_SMESH::LogicalOR, - public virtual LogicalBinary_i + public virtual LogicalBinary_i { public: LogicalOR_i(); @@ -767,7 +931,7 @@ namespace SMESH FILTER */ class SMESH_I_EXPORT Filter_i: public virtual POA_SMESH::Filter, - public virtual SALOME::GenericObj_i + public virtual SALOME::GenericObj_i { public: Filter_i(); @@ -781,20 +945,16 @@ namespace SMESH void SetMesh( SMESH_Mesh_ptr ); - virtual - SMESH::long_array* - GetIDs(); - static void GetElementsId( Predicate_i*, - const SMDS_Mesh*, - Controls::Filter::TIdSequence& ); + const SMDS_Mesh*, + Controls::Filter::TIdSequence& ); static void GetElementsId( Predicate_i*, - SMESH_Mesh_ptr, - Controls::Filter::TIdSequence& ); + SMESH_Mesh_ptr, + Controls::Filter::TIdSequence& ); virtual long_array* @@ -818,10 +978,31 @@ namespace SMESH Predicate_i* GetPredicate_i(); + // ========================= + // SMESH_IDSource interface + // ========================= + virtual SMESH::long_array* GetIDs(); + virtual SMESH::long_array* GetMeshInfo(); + virtual SMESH::array_of_ElementType* GetTypes(); + virtual SMESH::SMESH_Mesh_ptr GetMesh(); + virtual bool IsMeshInfoCorrect() { return true; } + + /*! + * \brief Object notified on change of predicate + */ + struct TPredicateChangeWaiter + { + virtual void PredicateChanged() = 0; + }; + void AddWaiter( TPredicateChangeWaiter* waiter ); + void RemoveWaiter( TPredicateChangeWaiter* waiter ); + private: Controls::Filter myFilter; Predicate_i* myPredicate; SMESH_Mesh_var myMesh; + + std::list myWaiters; }; @@ -829,7 +1010,7 @@ namespace SMESH FILTER LIBRARY */ class SMESH_I_EXPORT FilterLibrary_i: public virtual POA_SMESH::FilterLibrary, - public virtual SALOME::GenericObj_i + public virtual SALOME::GenericObj_i { public: FilterLibrary_i( const char* theFileName ); @@ -842,8 +1023,8 @@ namespace SMESH CORBA::Boolean AddEmpty( const char* theFilterName, ElementType theType ); CORBA::Boolean Delete ( const char* theFilterName ); CORBA::Boolean Replace ( const char* theFilterName, - const char* theNewName, - Filter_ptr theFilter ); + const char* theNewName, + Filter_ptr theFilter ); CORBA::Boolean Save(); CORBA::Boolean SaveAs( const char* aFileName ); @@ -867,7 +1048,7 @@ namespace SMESH */ class SMESH_I_EXPORT FilterManager_i: public virtual POA_SMESH::FilterManager, - public virtual SALOME::GenericObj_i + public virtual SALOME::GenericObj_i { public: FilterManager_i(); @@ -881,10 +1062,13 @@ namespace SMESH Skew_ptr CreateSkew(); Area_ptr CreateArea(); Volume3D_ptr CreateVolume3D(); + MaxElementLength2D_ptr CreateMaxElementLength2D(); + MaxElementLength3D_ptr CreateMaxElementLength3D(); Length_ptr CreateLength(); Length2D_ptr CreateLength2D(); MultiConnection_ptr CreateMultiConnection(); MultiConnection2D_ptr CreateMultiConnection2D(); + BallDiameter_ptr CreateBallDiameter(); BelongToGeom_ptr CreateBelongToGeom(); BelongToPlane_ptr CreateBelongToPlane(); @@ -897,15 +1081,22 @@ namespace SMESH FreeEdges_ptr CreateFreeEdges(); FreeNodes_ptr CreateFreeNodes(); FreeFaces_ptr CreateFreeFaces(); - + + EqualNodes_ptr CreateEqualNodes(); + EqualEdges_ptr CreateEqualEdges(); + EqualFaces_ptr CreateEqualFaces(); + EqualVolumes_ptr CreateEqualVolumes(); + RangeOfIds_ptr CreateRangeOfIds(); - BadOrientedVolume_ptr CreateBadOrientedVolume(); + BareBorderFace_ptr CreateBareBorderFace(); + BareBorderVolume_ptr CreateBareBorderVolume(); + OverConstrainedFace_ptr CreateOverConstrainedFace(); + OverConstrainedVolume_ptr CreateOverConstrainedVolume(); LinearOrQuadratic_ptr CreateLinearOrQuadratic(); - GroupColor_ptr CreateGroupColor(); - ElemGeomType_ptr CreateElemGeomType(); + CoplanarFaces_ptr CreateCoplanarFaces(); LessThan_ptr CreateLessThan(); MoreThan_ptr CreateMoreThan(); @@ -925,6 +1116,9 @@ namespace SMESH Predicate_i* GetPredicate( SMESH::Predicate_ptr thePredicate ); + + const char* FunctorTypeToString(SMESH::FunctorType ft); + SMESH::FunctorType StringToFunctorType(const char* str); } diff --git a/src/SMESH_I/SMESH_Gen_i.cxx b/src/SMESH_I/SMESH_Gen_i.cxx index e90e690f7..24b30bb62 100644 --- a/src/SMESH_I/SMESH_Gen_i.cxx +++ b/src/SMESH_I/SMESH_Gen_i.cxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Gen_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// + #include #include #include @@ -46,14 +45,9 @@ #include #include -#include "Utils_CorbaException.hxx" - -#include "utilities.h" -#include -#include - #ifdef WNT #include + #include #else #include #endif @@ -70,28 +64,28 @@ #define UnLoadLib( handle ) dlclose( handle ); #endif -#include - #include "SMESH_Gen_i.hxx" -#include "SMESH_Mesh_i.hxx" -#include "SMESH_Hypothesis_i.hxx" -#include "SMESH_Algo_i.hxx" -#include "SMESH_Group_i.hxx" -#include "SMESH_PythonDump.hxx" +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" +#include "SMDS_PolyhedralVolumeOfNodes.hxx" +#include "SMDS_SetIterator.hxx" +#include "SMDS_SpacePosition.hxx" +#include "SMDS_VertexPosition.hxx" #include "SMESHDS_Document.hxx" #include "SMESHDS_Group.hxx" #include "SMESHDS_GroupOnGeom.hxx" -#include "SMESH_Mesh.hxx" -#include "SMESH_Hypothesis.hxx" +#include "SMESH_Algo_i.hxx" +#include "SMESH_File.hxx" #include "SMESH_Group.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_Hypothesis.hxx" +#include "SMESH_Hypothesis_i.hxx" +#include "SMESH_Mesh.hxx" #include "SMESH_MeshEditor.hxx" - -#include "SMDS_EdgePosition.hxx" -#include "SMDS_FacePosition.hxx" -#include "SMDS_VertexPosition.hxx" -#include "SMDS_SpacePosition.hxx" -#include "SMDS_PolyhedralVolumeOfNodes.hxx" +#include "SMESH_Mesh_i.hxx" +#include "SMESH_PreMeshInfo.hxx" +#include "SMESH_PythonDump.hxx" #include CORBA_SERVER_HEADER(SMESH_Group) #include CORBA_SERVER_HEADER(SMESH_Filter) @@ -99,22 +93,35 @@ #include "DriverMED_W_SMESHDS_Mesh.h" #include "DriverMED_R_SMESHDS_Mesh.h" +#ifdef WITH_CGNS +#include "DriverCGNS_Read.hxx" +#endif +#include "memoire.h" -#include "SALOMEDS_Tool.hxx" -#include "SALOME_NamingService.hxx" -#include "SALOME_LifeCycleCORBA.hxx" -#include "Utils_SINGLETON.hxx" -#include "OpUtil.hxx" +#include -#include CORBA_CLIENT_HEADER(SALOME_ModuleCatalog) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "GEOM_Client.hxx" -#include "Utils_ExceptHandlers.hxx" +#include CORBA_CLIENT_HEADER(SALOME_ModuleCatalog) +#include CORBA_CLIENT_HEADER(SALOME_Session) #include +#include +#include using namespace std; using SMESH::TPythonDump; +using SMESH::TVar; #define NUM_TMP_FILES 2 @@ -150,9 +157,9 @@ PortableServer::ServantBase_var SMESH_Gen_i::GetServant( CORBA::Object_ptr theOb try { PortableServer::Servant aServant = GetPOA()->reference_to_servant( theObject ); return aServant; - } + } catch (...) { - INFOS( "GetServant - Unknown exception was caught!!!" ); + INFOS( "GetServant - Unknown exception was caught!!!" ); return NULL; } } @@ -172,11 +179,11 @@ CORBA::Object_var SMESH_Gen_i::SObjectToObject( SALOMEDS::SObject_ptr theSObject if ( !theSObject->_is_nil() ) { try { if( theSObject->FindAttribute( anAttr, "AttributeIOR" ) ) { - SALOMEDS::AttributeIOR_var anIOR = SALOMEDS::AttributeIOR::_narrow( anAttr ); - CORBA::String_var aValue = anIOR->Value(); - if( strcmp( aValue, "" ) != 0 ) - anObj = GetORB()->string_to_object( aValue ); - } + SALOMEDS::AttributeIOR_var anIOR = SALOMEDS::AttributeIOR::_narrow( anAttr ); + CORBA::String_var aValue = anIOR->Value(); + if( strcmp( aValue, "" ) != 0 ) + anObj = GetORB()->string_to_object( aValue ); + } } catch( ... ) { INFOS( "SObjectToObject - Unknown exception was caught!!!" ); @@ -189,7 +196,7 @@ CORBA::Object_var SMESH_Gen_i::SObjectToObject( SALOMEDS::SObject_ptr theSObject /*! * GetNS [ static ] * - * Get SALOME_NamingService object + * Get SALOME_NamingService object */ //============================================================================= @@ -209,7 +216,7 @@ SALOME_NamingService* SMESH_Gen_i::GetNS() * * Get SALOME_LifeCycleCORBA object */ -//============================================================================= +//============================================================================= SALOME_LifeCycleCORBA* SMESH_Gen_i::GetLCC() { if ( myLCC == NULL ) { myLCC = new SALOME_LifeCycleCORBA( GetNS() ); @@ -224,14 +231,14 @@ SALOME_LifeCycleCORBA* SMESH_Gen_i::GetLCC() { * * Get GEOM::GEOM_Gen reference */ -//============================================================================= +//============================================================================= GEOM::GEOM_Gen_ptr SMESH_Gen_i::GetGeomEngine() { //CCRT GEOM::GEOM_Gen_var aGeomEngine = //CCRT GEOM::GEOM_Gen::_narrow( GetLCC()->FindOrLoad_Component("FactoryServer","GEOM") ); //CCRT return aGeomEngine._retn(); if(CORBA::is_nil(myGeomGen)) { - Engines::Component_ptr temp=GetLCC()->FindOrLoad_Component("FactoryServer","GEOM"); + Engines::EngineComponent_ptr temp=GetLCC()->FindOrLoad_Component("FactoryServer","GEOM"); myGeomGen=GEOM::GEOM_Gen::_narrow(temp); } return myGeomGen; @@ -252,16 +259,16 @@ SMESH_Gen_i::SMESH_Gen_i() //============================================================================= /*! - * SMESH_Gen_i::SMESH_Gen_i + * SMESH_Gen_i::SMESH_Gen_i * * Standard constructor, used with Container */ //============================================================================= SMESH_Gen_i::SMESH_Gen_i( CORBA::ORB_ptr orb, - PortableServer::POA_ptr poa, - PortableServer::ObjectId* contId, - const char* instanceName, + PortableServer::POA_ptr poa, + PortableServer::ObjectId* contId, + const char* instanceName, const char* interfaceName ) : Engines_Component_i( orb, poa, contId, instanceName, interfaceName ) { @@ -269,16 +276,40 @@ SMESH_Gen_i::SMESH_Gen_i( CORBA::ORB_ptr orb, myOrb = CORBA::ORB::_duplicate(orb); myPoa = PortableServer::POA::_duplicate(poa); - + _thisObj = this ; _id = myPoa->activate_object( _thisObj ); - + myIsEmbeddedMode = false; myShapeReader = NULL; // shape reader mySMESHGen = this; + myIsHistoricalPythonDump = true; + myToForgetMeshDataOnHypModif = false; // set it in standalone mode only //OSD::SetSignal( true ); + + // 0020605: EDF 1190 SMESH: Display performance. 80 seconds for 52000 cells. + // find out mode (embedded or standalone) here else + // meshes created before calling SMESH_Client::GetSMESHGen(), which calls + // SMESH_Gen_i::SetEmbeddedMode(), have wrong IsEmbeddedMode flag + if ( SALOME_NamingService* ns = GetNS() ) + { + CORBA::Object_var obj = ns->Resolve( "/Kernel/Session" ); + SALOME::Session_var session = SALOME::Session::_narrow( obj ) ; + if ( !session->_is_nil() ) + { + CORBA::String_var str_host = session->getHostname(); + CORBA::Long s_pid = session->getPID(); + string my_host = Kernel_Utils::GetHostname(); +#ifdef WNT + long my_pid = (long)_getpid(); +#else + long my_pid = (long) getpid(); +#endif + SetEmbeddedMode( s_pid == my_pid && my_host == str_host.in() ); + } + } } //============================================================================= @@ -291,7 +322,7 @@ SMESH_Gen_i::SMESH_Gen_i( CORBA::ORB_ptr orb, SMESH_Gen_i::~SMESH_Gen_i() { - INFOS( "SMESH_Gen_i::~SMESH_Gen_i" ); + MESSAGE( "SMESH_Gen_i::~SMESH_Gen_i" ); // delete hypothesis creators map::iterator itHyp; @@ -308,10 +339,10 @@ SMESH_Gen_i::~SMESH_Gen_i() } myStudyContextMap.clear(); // delete shape reader - if ( !myShapeReader ) + if ( !myShapeReader ) delete myShapeReader; } - + //============================================================================= /*! * SMESH_Gen_i::createHypothesis @@ -349,7 +380,7 @@ SMESH::SMESH_Hypothesis_ptr SMESH_Gen_i::createHypothesis(const char* theHypName } else { - //try to use new format + //try to use new format #ifdef WNT aPlatformLibName = new char[ libNameLen + 5 ]; aPlatformLibName[0] = '\0'; @@ -435,7 +466,7 @@ SMESH::SMESH_Hypothesis_ptr SMESH_Gen_i::createHypothesis(const char* theHypName // activate the CORBA servant of hypothesis hypothesis_i = SMESH::SMESH_Hypothesis::_narrow( myHypothesis_i->_this() ); int nextId = RegisterObject( hypothesis_i ); - if(MYDEBUG) MESSAGE( "Add hypo to map with id = "<< nextId ); + if(MYDEBUG) MESSAGE( "Add hypo to map with id = "<< nextId ); return hypothesis_i._retn(); } @@ -458,6 +489,7 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::createMesh() // create a new mesh object servant, store it in a map in study context SMESH_Mesh_i* meshServant = new SMESH_Mesh_i( GetPOA(), this, GetCurrentStudyID() ); // create a new mesh object + MESSAGE("myIsEmbeddedMode " << myIsEmbeddedMode); meshServant->SetImpl( myGen.CreateMesh( GetCurrentStudyID(), myIsEmbeddedMode )); // activate the CORBA servant of Mesh @@ -482,7 +514,7 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::createMesh() GEOM_Client* SMESH_Gen_i::GetShapeReader() { // create shape reader if necessary - if ( !myShapeReader ) + if ( !myShapeReader ) myShapeReader = new GEOM_Client(GetContainerRef()); ASSERT( myShapeReader ); return myShapeReader; @@ -515,6 +547,7 @@ void SMESH_Gen_i::SetGeomEngine( GEOM::GEOM_Gen_ptr geomcompo ) void SMESH_Gen_i::SetEmbeddedMode( CORBA::Boolean theMode ) { myIsEmbeddedMode = theMode; + MESSAGE("myIsEmbeddedMode " << myIsEmbeddedMode); if ( !myIsEmbeddedMode ) { //PAL10867: disable signals catching with "noexcepthandler" option @@ -559,25 +592,43 @@ CORBA::Boolean SMESH_Gen_i::IsEmbeddedMode() void SMESH_Gen_i::SetCurrentStudy( SALOMEDS::Study_ptr theStudy ) { - //if(MYDEBUG) - //MESSAGE( "SMESH_Gen_i::SetCurrentStudy" ); + setCurrentStudy( theStudy ); +} + +void SMESH_Gen_i::setCurrentStudy( SALOMEDS::Study_ptr theStudy, + bool theStudyIsBeingClosed) +{ + int curStudyId = GetCurrentStudyID(); myCurrentStudy = SALOMEDS::Study::_duplicate( theStudy ); // create study context, if it doesn't exist and set current study int studyId = GetCurrentStudyID(); - if(MYDEBUG) MESSAGE( "SMESH_Gen_i::SetCurrentStudy: study Id = " << studyId ); if ( myStudyContextMap.find( studyId ) == myStudyContextMap.end() ) { - myStudyContextMap[ studyId ] = new StudyContext; + myStudyContextMap[ studyId ] = new StudyContext; } // myCurrentStudy may be nil - if ( !CORBA::is_nil( myCurrentStudy ) ) { - SALOMEDS::StudyBuilder_var aStudyBuilder = myCurrentStudy->NewBuilder(); + if ( !theStudyIsBeingClosed && !CORBA::is_nil( myCurrentStudy ) ) { + SALOMEDS::StudyBuilder_var aStudyBuilder = myCurrentStudy->NewBuilder(); if( !myCurrentStudy->FindComponent( "GEOM" )->_is_nil() ) aStudyBuilder->LoadWith( myCurrentStudy->FindComponent( "GEOM" ), GetGeomEngine() ); - // set current study for geom engine - //if ( !CORBA::is_nil( GetGeomEngine() ) ) - // GetGeomEngine()->GetCurrentStudy( myCurrentStudy->StudyId() ); + // NPAL16168, issue 0020210 + // Let meshes update their data depending on GEOM groups that could change + if ( curStudyId != studyId ) + { + //SALOMEDS::SComponent_var me = PublishComponent( myCurrentStudy ); + SALOMEDS::SComponent_var me = SALOMEDS::SComponent::_narrow + ( myCurrentStudy->FindComponent( ComponentDataType() ) ); + if ( !me->_is_nil() ) { + SALOMEDS::ChildIterator_var anIter = myCurrentStudy->NewChildIterator( me ); + for ( ; anIter->More(); anIter->Next() ) { + SALOMEDS::SObject_var so = anIter->Value(); + CORBA::Object_var ior = SObjectToObject( so ); + if ( SMESH_Mesh_i* mesh = SMESH::DownCast( ior )) + mesh->CheckGeomGroupModif(); + } + } + } } } @@ -597,7 +648,7 @@ SALOMEDS::Study_ptr SMESH_Gen_i::GetCurrentStudy() //============================================================================= /*! - * SMESH_Gen_i::GetCurrentStudyContext + * SMESH_Gen_i::GetCurrentStudyContext * * Get current study context */ @@ -613,7 +664,7 @@ StudyContext* SMESH_Gen_i::GetCurrentStudyContext() //============================================================================= /*! - * SMESH_Gen_i::CreateHypothesis + * SMESH_Gen_i::CreateHypothesis * * Create hypothesis/algorothm of given type and publish it in the study */ @@ -711,7 +762,7 @@ SMESH_Gen_i::GetHypothesisParameterValues (const char* theHypType, } } - // let the temporary hypothesis find out some how parameter values by mesh + // let the temporary hypothesis find out somehow parameter values by mesh if ( hyp->SetParametersByMesh( mesh, shape )) return SMESH::SMESH_Hypothesis::_duplicate( tmpHyp ); } @@ -724,6 +775,7 @@ SMESH_Gen_i::GetHypothesisParameterValues (const char* theHypType, ::SMESH_Hypothesis::TDefaults dflts; dflts._elemLength = diagonal / myGen.GetBoundaryBoxSegmentation(); dflts._nbSegments = myGen.GetDefaultNbSegments(); + dflts._shape = &shape; // let the temporary hypothesis initialize it's values if ( hyp->SetParametersByDefaults( dflts, mesh )) return SMESH::SMESH_Hypothesis::_duplicate( tmpHyp ); @@ -755,12 +807,69 @@ void SMESH_Gen_i::SetBoundaryBoxSegmentation( CORBA::Long theNbSegments ) void SMESH_Gen_i::SetDefaultNbSegments(CORBA::Long theNbSegments) throw ( SALOME::SALOME_Exception ) { - if ( theNbSegments ) + if ( theNbSegments > 0 ) myGen.SetDefaultNbSegments( int(theNbSegments) ); else THROW_SALOME_CORBA_EXCEPTION( "non-positive number of segments", SALOME::BAD_PARAM ); } +//============================================================================= +/*! + Set an option value +*/ +//============================================================================= + +void SMESH_Gen_i::SetOption(const char* name, const char* value) +{ + if ( name && value && strlen( value ) > 0 ) + { + string msgToGUI; + if ( strcmp(name, "historical_python_dump") == 0 ) + { + myIsHistoricalPythonDump = ( value[0] == '1' || toupper(value[0]) == 'T' ); // 1 || true + msgToGUI = "preferences/SMESH/historical_python_dump/"; + msgToGUI += myIsHistoricalPythonDump ? "true" : "false"; + } + else if ( strcmp(name, "forget_mesh_on_hyp_modif") == 0 ) + { + myToForgetMeshDataOnHypModif = ( value[0] == '1' || toupper(value[0]) == 'T' ); // 1 || true + msgToGUI = "preferences/SMESH/forget_mesh_on_hyp_modif/"; + msgToGUI += myToForgetMeshDataOnHypModif ? "true" : "false"; + } + + // update preferences in case if SetOption() is invoked from python console + if ( !msgToGUI.empty() ) + { + CORBA::Object_var obj = SMESH_Gen_i::GetNS()->Resolve( "/Kernel/Session" ); + SALOME::Session_var session = SALOME::Session::_narrow( obj ); + if ( !CORBA::is_nil( session ) ) + session->emitMessageOneWay(msgToGUI.c_str()); + } + } +} + +//============================================================================= +/*! + Return an option value +*/ +//============================================================================= + +char* SMESH_Gen_i::GetOption(const char* name) +{ + if ( name ) + { + if ( strcmp(name, "historical_python_dump") == 0 ) + { + return CORBA::string_dup( myIsHistoricalPythonDump ? "true" : "false" ); + } + if ( strcmp(name, "forget_mesh_on_hyp_modif") == 0 ) + { + return CORBA::string_dup( myToForgetMeshDataOnHypModif ? "true" : "false" ); + } + } + return CORBA::string_dup( "" ); +} + //============================================================================= /*! * SMESH_Gen_i::CreateMesh @@ -851,7 +960,7 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMeshesFromUNV( const char* theFileName aStudyBuilder->CommitCommand(); if ( !aSO->_is_nil() ) { // Update Python script - TPythonDump() << aSO << " = smeshgen.CreateMeshesFromUNV('" << theFileName << "')"; + TPythonDump() << aSO << " = smeshgen.CreateMeshesFromUNV(r'" << theFileName << "')"; } } @@ -860,8 +969,9 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMeshesFromUNV( const char* theFileName aServant->ImportUNVFile( theFileName ); // Dump creation of groups - aServant->GetGroups(); + SMESH::ListOfGroups_var groups = aServant->GetGroups(); + aServant->GetImpl().GetMeshDS()->Modified(); return aMesh._retn(); } @@ -873,13 +983,11 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMeshesFromUNV( const char* theFileName */ //============================================================================= -SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromMED( const char* theFileName, - SMESH::DriverMED_ReadStatus& theStatus) - throw ( SALOME::SALOME_Exception ) +SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromMEDorSAUV( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus, + const char* theCommandNameForPython, + const char* theFileNameForPython) { - Unexpect aCatch(SALOME_SalomeException); - if(MYDEBUG) MESSAGE( "SMESH_Gen_i::CreateMeshFromMED" ); - // Retrieve mesh names from the file DriverMED_R_SMESHDS_Mesh myReader; myReader.SetFile( theFileName ); @@ -894,23 +1002,21 @@ SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromMED( const char* theFileName, // Python Dump TPythonDump aPythonDump; aPythonDump << "(["; - //TCollection_AsciiString aStr ("(["); if (theStatus == SMESH::DRS_OK) { SALOMEDS::StudyBuilder_var aStudyBuilder = myCurrentStudy->NewBuilder(); aStudyBuilder->NewCommand(); // There is a transaction aResult->length( aNames.size() ); int i = 0; - + // Iterate through all meshes and create mesh objects for ( list::iterator it = aNames.begin(); it != aNames.end(); it++ ) { // Python Dump - //if (i > 0) aStr += ", "; if (i > 0) aPythonDump << ", "; // create mesh SMESH::SMESH_Mesh_var mesh = createMesh(); - + // publish mesh in the study SALOMEDS::SObject_var aSO; if ( CanPublishInStudy( mesh ) ) @@ -918,37 +1024,85 @@ SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromMED( const char* theFileName, if ( !aSO->_is_nil() ) { // Python Dump aPythonDump << aSO; - //aStr += aSO->GetID(); } else { // Python Dump aPythonDump << "mesh_" << i; -// aStr += "mesh_"; -// aStr += TCollection_AsciiString(i); } // Read mesh data (groups are published automatically by ImportMEDFile()) SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( mesh ).in() ); ASSERT( meshServant ); SMESH::DriverMED_ReadStatus status1 = - meshServant->ImportMEDFile( theFileName, (*it).c_str() ); + meshServant->ImportMEDFile( theFileName, (*it).c_str() ); if (status1 > theStatus) - theStatus = status1; + theStatus = status1; aResult[i++] = SMESH::SMESH_Mesh::_duplicate( mesh ); + meshServant->GetImpl().GetMeshDS()->Modified(); } aStudyBuilder->CommitCommand(); } // Update Python script - aPythonDump << "], status) = " << this << ".CreateMeshesFromMED('" << theFileName << "')"; + aPythonDump << "], status) = " << this << "." << theCommandNameForPython << "(r'" << theFileNameForPython << "')"; } // Dump creation of groups for ( int i = 0; i < aResult->length(); ++i ) - aResult[ i ]->GetGroups(); + SMESH::ListOfGroups_var groups = aResult[ i ]->GetGroups(); return aResult._retn(); } +SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromMED( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus) + throw ( SALOME::SALOME_Exception ) +{ + Unexpect aCatch(SALOME_SalomeException); + if(MYDEBUG) MESSAGE( "SMESH_Gen_i::CreateMeshFromMED" ); + SMESH::mesh_array* result = CreateMeshesFromMEDorSAUV(theFileName, theStatus, "CreateMeshesFromMED", theFileName); + return result; +} + +//============================================================================= +/*! + * SMESH_Gen_i::CreateMeshFromSAUV + * + * Create mesh and import data from SAUV file + */ +//============================================================================= + +SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromSAUV( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus) + throw ( SALOME::SALOME_Exception ) +{ + Unexpect aCatch(SALOME_SalomeException); + if(MYDEBUG) MESSAGE( "SMESH_Gen_i::CreateMeshFromSAUV" ); + std::string sauvfilename(theFileName); + std::string medfilename(theFileName); + medfilename += ".med"; + std::string cmd; +#ifdef WNT + cmd = "%PYTHONBIN% "; +#else + cmd = "python "; +#endif + cmd += "-c \""; + cmd += "from medutilities import convert ; convert(r'" + sauvfilename + "', 'GIBI', 'MED', 1, r'" + medfilename + "')"; + cmd += "\""; + system(cmd.c_str()); + SMESH::mesh_array* result = CreateMeshesFromMEDorSAUV(medfilename.c_str(), theStatus, "CreateMeshesFromSAUV", sauvfilename.c_str()); +#ifdef WNT + cmd = "%PYTHONBIN% "; +#else + cmd = "python "; +#endif + cmd += "-c \""; + cmd += "from medutilities import my_remove ; my_remove(r'" + medfilename + "')"; + cmd += "\""; + system(cmd.c_str()); + return result; +} + //============================================================================= /*! * SMESH_Gen_i::CreateMeshFromSTL @@ -974,16 +1128,100 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CreateMeshesFromSTL( const char* theFileName aStudyBuilder->CommitCommand(); if ( !aSO->_is_nil() ) { // Update Python script - TPythonDump() << aSO << " = " << this << ".CreateMeshesFromSTL('" << theFileName << "')"; + TPythonDump() << aSO << " = " << this << ".CreateMeshesFromSTL(r'" << theFileName << "')"; } } SMESH_Mesh_i* aServant = dynamic_cast( GetServant( aMesh ).in() ); ASSERT( aServant ); aServant->ImportSTLFile( theFileName ); + aServant->GetImpl().GetMeshDS()->Modified(); return aMesh._retn(); } +//================================================================================ +/*! + * \brief Create meshes and import data from the CGSN file + */ +//================================================================================ + +SMESH::mesh_array* SMESH_Gen_i::CreateMeshesFromCGNS( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus) + throw ( SALOME::SALOME_Exception ) +{ + Unexpect aCatch(SALOME_SalomeException); + + SMESH::mesh_array_var aResult = new SMESH::mesh_array(); + +#ifdef WITH_CGNS + // Retrieve nb meshes from the file + DriverCGNS_Read myReader; + myReader.SetFile( theFileName ); + Driver_Mesh::Status aStatus; + int nbMeshes = myReader.GetNbMeshes(aStatus); + theStatus = (SMESH::DriverMED_ReadStatus)aStatus; + + aResult->length( nbMeshes ); + + { // open a new scope to make aPythonDump die before PythonDump in SMESH_Mesh::GetGroups() + + // Python Dump + TPythonDump aPythonDump; + aPythonDump << "(["; + + if (theStatus == SMESH::DRS_OK) + { + SALOMEDS::StudyBuilder_var aStudyBuilder = myCurrentStudy->NewBuilder(); + aStudyBuilder->NewCommand(); // There is a transaction + + int i = 0; + + // Iterate through all meshes and create mesh objects + for ( ; i < nbMeshes; ++i ) + { + // Python Dump + if (i > 0) aPythonDump << ", "; + + // create mesh + SMESH::SMESH_Mesh_var mesh = createMesh(); + aResult[i] = SMESH::SMESH_Mesh::_duplicate( mesh ); + + // Read mesh data (groups are published automatically by ImportMEDFile()) + SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( mesh ).in() ); + ASSERT( meshServant ); + string meshName; + SMESH::DriverMED_ReadStatus status1 = + meshServant->ImportCGNSFile( theFileName, i, meshName ); + if (status1 > theStatus) + theStatus = status1; + + meshServant->GetImpl().GetMeshDS()->Modified(); + // publish mesh in the study + SALOMEDS::SObject_var aSO; + if ( CanPublishInStudy( mesh ) ) + aSO = PublishMesh( myCurrentStudy, mesh.in(), meshName.c_str() ); + + // Python Dump + if ( !aSO->_is_nil() ) + aPythonDump << aSO; + else + aPythonDump << "mesh_" << i; + } + aStudyBuilder->CommitCommand(); + } + + aPythonDump << "], status) = " << this << ".CreateMeshesFromCGNS(r'" << theFileName << "')"; + } + // Dump creation of groups + for ( int i = 0; i < aResult->length(); ++i ) + SMESH::ListOfGroups_var groups = aResult[ i ]->GetGroups(); +#else + THROW_SALOME_CORBA_EXCEPTION("CGNS library is unavailable", SALOME::INTERNAL_ERROR); +#endif + + return aResult._retn(); +} + //============================================================================= /*! * SMESH_Gen_i::IsReadyToCompute @@ -1000,7 +1238,7 @@ CORBA::Boolean SMESH_Gen_i::IsReadyToCompute( SMESH::SMESH_Mesh_ptr theMesh, if(MYDEBUG) MESSAGE( "SMESH_Gen_i::IsReadyToCompute" ); if ( CORBA::is_nil( theShapeObject ) ) - THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", + THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", SALOME::BAD_PARAM ); if ( CORBA::is_nil( theMesh ) ) @@ -1068,7 +1306,7 @@ SALOMEDS::SObject_ptr SMESH_Gen_i::GetAlgoSO(const ::SMESH_Algo* algo) */ //================================================================================ -SMESH::compute_error_array* SMESH_Gen_i::GetComputeErrors( SMESH::SMESH_Mesh_ptr theMesh, +SMESH::compute_error_array* SMESH_Gen_i::GetComputeErrors( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theSubObject ) throw ( SALOME::SALOME_Exception ) { @@ -1090,7 +1328,7 @@ SMESH::compute_error_array* SMESH_Gen_i::GetComputeErrors( SMESH::SMESH_Mesh_ptr shape = GeomObjectToShape( theSubObject ); else shape = SMESH_Mesh::PseudoShape(); - + ::SMESH_Mesh& mesh = meshServant->GetImpl(); error_array->length( mesh.GetMeshDS()->MaxShapeIndex() ); @@ -1130,7 +1368,7 @@ SMESH::compute_error_array* SMESH_Gen_i::GetComputeErrors( SMESH::SMESH_Mesh_ptr return error_array._retn(); } -// +// //================================================================================ /*! * \brief Return mesh elements preventing computation of a subshape @@ -1225,7 +1463,7 @@ SMESH_Gen_i::GetBadInputElements( SMESH::SMESH_Mesh_ptr theMesh, */ //================================================================================ -SMESH::algo_error_array* SMESH_Gen_i::GetAlgoState( SMESH::SMESH_Mesh_ptr theMesh, +SMESH::algo_error_array* SMESH_Gen_i::GetAlgoState( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theSubObject ) throw ( SALOME::SALOME_Exception ) { @@ -1248,7 +1486,7 @@ SMESH::algo_error_array* SMESH_Gen_i::GetAlgoState( SMESH::SMESH_Mesh_ptr theMes myLocShape = GeomObjectToShape( theSubObject ); else myLocShape = SMESH_Mesh::PseudoShape(); - + ::SMESH_Mesh& myLocMesh = meshServant->GetImpl(); list< ::SMESH_Gen::TAlgoStateError > error_list; list< ::SMESH_Gen::TAlgoStateError >::iterator error; @@ -1285,7 +1523,7 @@ SMESH::algo_error_array* SMESH_Gen_i::GetAlgoState( SMESH::SMESH_Mesh_ptr theMes //============================================================================= SMESH::long_array* SMESH_Gen_i::GetSubShapesId( GEOM::GEOM_Object_ptr theMainShapeObject, - const SMESH::object_array& theListOfSubShapeObject ) + const SMESH::object_array& theListOfSubShapeObject ) throw ( SALOME::SALOME_Exception ) { Unexpect aCatch(SALOME_SalomeException); @@ -1301,47 +1539,47 @@ SMESH::long_array* SMESH_Gen_i::GetSubShapesId( GEOM::GEOM_Object_ptr theMainSha try { TopoDS_Shape myMainShape = GeomObjectToShape(theMainShapeObject); - TopTools_IndexedMapOfShape myIndexToShape; + TopTools_IndexedMapOfShape myIndexToShape; TopExp::MapShapes(myMainShape,myIndexToShape); for ( int i = 0; i < theListOfSubShapeObject.length(); i++ ) - { - GEOM::GEOM_Object_var aShapeObject - = GEOM::GEOM_Object::_narrow(theListOfSubShapeObject[i]); - if ( CORBA::is_nil( aShapeObject ) ) - THROW_SALOME_CORBA_EXCEPTION ("bad shape object reference", \ - SALOME::BAD_PARAM ); - - TopoDS_Shape locShape = GeomObjectToShape(aShapeObject); - for (TopExp_Explorer exp(locShape,TopAbs_FACE); exp.More(); exp.Next()) - { - const TopoDS_Face& F = TopoDS::Face(exp.Current()); - setId.insert(myIndexToShape.FindIndex(F)); - if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(F)); - } - for (TopExp_Explorer exp(locShape,TopAbs_EDGE); exp.More(); exp.Next()) - { - const TopoDS_Edge& E = TopoDS::Edge(exp.Current()); - setId.insert(myIndexToShape.FindIndex(E)); - if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(E)); - } - for (TopExp_Explorer exp(locShape,TopAbs_VERTEX); exp.More(); exp.Next()) - { - const TopoDS_Vertex& V = TopoDS::Vertex(exp.Current()); - setId.insert(myIndexToShape.FindIndex(V)); - if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(V)); - } - } + { + GEOM::GEOM_Object_var aShapeObject + = GEOM::GEOM_Object::_narrow(theListOfSubShapeObject[i]); + if ( CORBA::is_nil( aShapeObject ) ) + THROW_SALOME_CORBA_EXCEPTION ("bad shape object reference", \ + SALOME::BAD_PARAM ); + + TopoDS_Shape locShape = GeomObjectToShape(aShapeObject); + for (TopExp_Explorer exp(locShape,TopAbs_FACE); exp.More(); exp.Next()) + { + const TopoDS_Face& F = TopoDS::Face(exp.Current()); + setId.insert(myIndexToShape.FindIndex(F)); + if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(F)); + } + for (TopExp_Explorer exp(locShape,TopAbs_EDGE); exp.More(); exp.Next()) + { + const TopoDS_Edge& E = TopoDS::Edge(exp.Current()); + setId.insert(myIndexToShape.FindIndex(E)); + if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(E)); + } + for (TopExp_Explorer exp(locShape,TopAbs_VERTEX); exp.More(); exp.Next()) + { + const TopoDS_Vertex& V = TopoDS::Vertex(exp.Current()); + setId.insert(myIndexToShape.FindIndex(V)); + if(MYDEBUG) SCRUTE(myIndexToShape.FindIndex(V)); + } + } shapesId->length(setId.size()); set::iterator iind; int i=0; for (iind = setId.begin(); iind != setId.end(); iind++) - { - if(MYDEBUG) SCRUTE((*iind)); - shapesId[i] = (*iind); - if(MYDEBUG) SCRUTE(shapesId[i]); - i++; - } + { + if(MYDEBUG) SCRUTE((*iind)); + shapesId[i] = (*iind); + if(MYDEBUG) SCRUTE(shapesId[i]); + i++; + } } catch (SALOME_Exception& S_ex) { @@ -1363,11 +1601,12 @@ CORBA::Boolean SMESH_Gen_i::Compute( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theShapeObject ) throw ( SALOME::SALOME_Exception ) { + MEMOSTAT; Unexpect aCatch(SALOME_SalomeException); if(MYDEBUG) MESSAGE( "SMESH_Gen_i::Compute" ); if ( CORBA::is_nil( theShapeObject ) && theMesh->HasShapeToMesh()) - THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", + THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", SALOME::BAD_PARAM ); if ( CORBA::is_nil( theMesh ) ) @@ -1381,6 +1620,7 @@ CORBA::Boolean SMESH_Gen_i::Compute( SMESH::SMESH_Mesh_ptr theMesh, try { // get mesh servant SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( theMesh ).in() ); + meshServant->Load(); ASSERT( meshServant ); if ( meshServant ) { // NPAL16168: "geometrical group edition from a submesh don't modifiy mesh computation" @@ -1393,7 +1633,13 @@ CORBA::Boolean SMESH_Gen_i::Compute( SMESH::SMESH_Mesh_ptr theMesh, myLocShape = SMESH_Mesh::PseudoShape(); // call implementation compute ::SMESH_Mesh& myLocMesh = meshServant->GetImpl(); - return myGen.Compute( myLocMesh, myLocShape); +#ifdef WITH_SMESH_CANCEL_COMPUTE + myGen.PrepareCompute( myLocMesh, myLocShape); +#endif + bool ok = myGen.Compute( myLocMesh, myLocShape); + meshServant->CreateGroupServants(); // algos can create groups (issue 0020918) + myLocMesh.GetMeshDS()->Modified(); + return ok; } } catch ( std::bad_alloc ) { @@ -1408,6 +1654,29 @@ CORBA::Boolean SMESH_Gen_i::Compute( SMESH::SMESH_Mesh_ptr theMesh, return false; } +//============================================================================= +/*! + * SMESH_Gen_i::CancelCompute + * + * Cancel Compute mesh on a shape + */ +//============================================================================= + +void SMESH_Gen_i::CancelCompute( SMESH::SMESH_Mesh_ptr theMesh, + GEOM::GEOM_Object_ptr theShapeObject ) +{ +#ifdef WITH_SMESH_CANCEL_COMPUTE + SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( theMesh ).in() ); + ::SMESH_Mesh& myLocMesh = meshServant->GetImpl(); + TopoDS_Shape myLocShape; + if(theMesh->HasShapeToMesh()) + myLocShape = GeomObjectToShape( theShapeObject ); + else + myLocShape = SMESH_Mesh::PseudoShape(); + myGen.CancelCompute( myLocMesh, myLocShape); +#endif +} + //============================================================================= /*! * SMESH_Gen_i::Precompute @@ -1417,16 +1686,16 @@ CORBA::Boolean SMESH_Gen_i::Compute( SMESH::SMESH_Mesh_ptr theMesh, //============================================================================= SMESH::MeshPreviewStruct* SMESH_Gen_i::Precompute( SMESH::SMESH_Mesh_ptr theMesh, - GEOM::GEOM_Object_ptr theShapeObject, - SMESH::Dimension theDimension, - SMESH::long_array& theShapesId) + GEOM::GEOM_Object_ptr theShapeObject, + SMESH::Dimension theDimension, + SMESH::long_array& theShapesId) throw ( SALOME::SALOME_Exception ) { Unexpect aCatch(SALOME_SalomeException); if(MYDEBUG) MESSAGE( "SMESH_Gen_i::Precompute" ); if ( CORBA::is_nil( theShapeObject ) && theMesh->HasShapeToMesh()) - THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", + THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", SALOME::BAD_PARAM ); if ( CORBA::is_nil( theMesh ) ) @@ -1437,6 +1706,7 @@ SMESH::MeshPreviewStruct* SMESH_Gen_i::Precompute( SMESH::SMESH_Mesh_ptr theMesh try { // get mesh servant SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( theMesh ).in() ); + meshServant->Load(); ASSERT( meshServant ); if ( meshServant ) { // NPAL16168: "geometrical group edition from a submesh don't modifiy mesh computation" @@ -1446,7 +1716,7 @@ SMESH::MeshPreviewStruct* SMESH_Gen_i::Precompute( SMESH::SMESH_Mesh_ptr theMesh if(theMesh->HasShapeToMesh()) myLocShape = GeomObjectToShape( theShapeObject ); else - return result._retn();; + return result._retn();; // call implementation compute ::SMESH_Mesh& myLocMesh = meshServant->GetImpl(); @@ -1454,132 +1724,132 @@ SMESH::MeshPreviewStruct* SMESH_Gen_i::Precompute( SMESH::SMESH_Mesh_ptr theMesh ::MeshDimension aDim = (MeshDimension)theDimension; if ( myGen.Compute( myLocMesh, myLocShape, false, aDim, &shapeIds ) ) { - int nbShapeId = shapeIds.size(); - theShapesId.length( nbShapeId ); - // iterates on shapes and collect mesh entities into mesh preview - TSetOfInt::const_iterator idIt = shapeIds.begin(); - TSetOfInt::const_iterator idEnd = shapeIds.end(); - std::map< int, int > mapOfShIdNb; - std::set< SMESH_TLink > setOfEdge; - std::list< SMDSAbs_ElementType > listOfElemType; - typedef map TNode2LocalIDMap; - typedef TNode2LocalIDMap::iterator TNodeLocalID; - TNode2LocalIDMap mapNode2LocalID; - list< TNodeLocalID > connectivity; - int i, nbConnNodes = 0; - std::set< const SMESH_subMesh* > setOfVSubMesh; - // iterates on shapes - for ( ; idIt != idEnd; idIt++ ) - { - if ( mapOfShIdNb.find( *idIt ) != mapOfShIdNb.end() ) - continue; - SMESH_subMesh* sm = myLocMesh.GetSubMeshContaining(*idIt); - if ( !sm || !sm->IsMeshComputed() ) - continue; - - const TopoDS_Shape& aSh = sm->GetSubShape(); - const int shDim = myGen.GetShapeDim( aSh ); - if ( shDim < 1 || shDim > theDimension ) - continue; - - mapOfShIdNb[ *idIt ] = 0; - theShapesId[ mapOfShIdNb.size() - 1 ] = *idIt; - - SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); - if ( !smDS ) continue; - - if ( theDimension == SMESH::DIM_2D ) - { - SMDS_ElemIteratorPtr faceIt = smDS->GetElements(); - while ( faceIt->more() ) - { - const SMDS_MeshElement* face = faceIt->next(); - int aNbNode = face->NbNodes(); - if ( aNbNode > 4 ) - aNbNode /= 2; // do not take into account additional middle nodes - - SMDS_MeshNode* node1 = (SMDS_MeshNode*)face->GetNode( 1 ); - for ( int nIndx = 1; nIndx <= aNbNode; nIndx++ ) - { - SMDS_MeshNode* node2 = (SMDS_MeshNode*)face->GetNode( nIndx < aNbNode ? nIndx+1 : 1 ); - if ( setOfEdge.insert( SMESH_TLink ( node1, node2 ) ).second ) - { - listOfElemType.push_back( SMDSAbs_Edge ); - connectivity.push_back - ( mapNode2LocalID.insert( make_pair( node1, ++nbConnNodes)).first ); - connectivity.push_back - ( mapNode2LocalID.insert( make_pair( node2, ++nbConnNodes)).first ); - } - node1 = node2; - } - } - } - else if ( theDimension == SMESH::DIM_1D ) - { - SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes(); - while ( nodeIt->more() ) - { - listOfElemType.push_back( SMDSAbs_Node ); - connectivity.push_back - ( mapNode2LocalID.insert( make_pair( nodeIt->next(), ++nbConnNodes)).first ); - } - // add corner nodes by first vertex from edge - SMESH_subMeshIteratorPtr edgeSmIt = - sm->getDependsOnIterator(/*includeSelf*/false, - /*complexShapeFirst*/false); - while ( edgeSmIt->more() ) - { - SMESH_subMesh* vertexSM = edgeSmIt->next(); - // check that vertex is not already treated - if ( !setOfVSubMesh.insert( vertexSM ).second ) - continue; - if ( vertexSM->GetSubShape().ShapeType() != TopAbs_VERTEX ) - continue; - - const SMESHDS_SubMesh* vertexSmDS = vertexSM->GetSubMeshDS(); - SMDS_NodeIteratorPtr nodeIt = vertexSmDS->GetNodes(); - while ( nodeIt->more() ) - { - listOfElemType.push_back( SMDSAbs_Node ); - connectivity.push_back - ( mapNode2LocalID.insert( make_pair( nodeIt->next(), ++nbConnNodes)).first ); - } - } - } - } - - // fill node coords and assign local ids to the nodes - int nbNodes = mapNode2LocalID.size(); - result->nodesXYZ.length( nbNodes ); - TNodeLocalID node2ID = mapNode2LocalID.begin(); - for ( i = 0; i < nbNodes; ++i, ++node2ID ) { - node2ID->second = i; - const SMDS_MeshNode* node = (const SMDS_MeshNode*) node2ID->first; - result->nodesXYZ[i].x = node->X(); - result->nodesXYZ[i].y = node->Y(); - result->nodesXYZ[i].z = node->Z(); - } - // fill connectivity - result->elementConnectivities.length( nbConnNodes ); - list< TNodeLocalID >::iterator connIt = connectivity.begin(); - for ( i = 0; i < nbConnNodes; ++i, ++connIt ) { - result->elementConnectivities[i] = (*connIt)->second; - } - - // fill element types - result->elementTypes.length( listOfElemType.size() ); - std::list< SMDSAbs_ElementType >::const_iterator typeIt = listOfElemType.begin(); - std::list< SMDSAbs_ElementType >::const_iterator typeEnd = listOfElemType.end(); - for ( i = 0; typeIt != typeEnd; ++i, ++typeIt ) + int nbShapeId = shapeIds.size(); + theShapesId.length( nbShapeId ); + // iterates on shapes and collect mesh entities into mesh preview + TSetOfInt::const_iterator idIt = shapeIds.begin(); + TSetOfInt::const_iterator idEnd = shapeIds.end(); + std::map< int, int > mapOfShIdNb; + std::set< SMESH_TLink > setOfEdge; + std::list< SMDSAbs_ElementType > listOfElemType; + typedef map TNode2LocalIDMap; + typedef TNode2LocalIDMap::iterator TNodeLocalID; + TNode2LocalIDMap mapNode2LocalID; + list< TNodeLocalID > connectivity; + int i, nbConnNodes = 0; + std::set< const SMESH_subMesh* > setOfVSubMesh; + // iterates on shapes + for ( ; idIt != idEnd; idIt++ ) + { + if ( mapOfShIdNb.find( *idIt ) != mapOfShIdNb.end() ) + continue; + SMESH_subMesh* sm = myLocMesh.GetSubMeshContaining(*idIt); + if ( !sm || !sm->IsMeshComputed() ) + continue; + + const TopoDS_Shape& aSh = sm->GetSubShape(); + const int shDim = myGen.GetShapeDim( aSh ); + if ( shDim < 1 || shDim > theDimension ) + continue; + + mapOfShIdNb[ *idIt ] = 0; + theShapesId[ mapOfShIdNb.size() - 1 ] = *idIt; + + SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); + if ( !smDS ) continue; + + if ( theDimension == SMESH::DIM_2D ) + { + SMDS_ElemIteratorPtr faceIt = smDS->GetElements(); + while ( faceIt->more() ) + { + const SMDS_MeshElement* face = faceIt->next(); + int aNbNode = face->NbNodes(); + if ( aNbNode > 4 ) + aNbNode /= 2; // do not take into account additional middle nodes + + SMDS_MeshNode* node1 = (SMDS_MeshNode*)face->GetNode( 0 ); + for ( int nIndx = 0; nIndx < aNbNode; nIndx++ ) + { + SMDS_MeshNode* node2 = (SMDS_MeshNode*)face->GetNode( nIndx+1 < aNbNode ? nIndx+1 : 0 ); + if ( setOfEdge.insert( SMESH_TLink ( node1, node2 ) ).second ) + { + listOfElemType.push_back( SMDSAbs_Edge ); + connectivity.push_back + ( mapNode2LocalID.insert( make_pair( node1, ++nbConnNodes)).first ); + connectivity.push_back + ( mapNode2LocalID.insert( make_pair( node2, ++nbConnNodes)).first ); + } + node1 = node2; + } + } + } + else if ( theDimension == SMESH::DIM_1D ) + { + SMDS_NodeIteratorPtr nodeIt = smDS->GetNodes(); + while ( nodeIt->more() ) + { + listOfElemType.push_back( SMDSAbs_Node ); + connectivity.push_back + ( mapNode2LocalID.insert( make_pair( nodeIt->next(), ++nbConnNodes)).first ); + } + // add corner nodes by first vertex from edge + SMESH_subMeshIteratorPtr edgeSmIt = + sm->getDependsOnIterator(/*includeSelf*/false, + /*complexShapeFirst*/false); + while ( edgeSmIt->more() ) + { + SMESH_subMesh* vertexSM = edgeSmIt->next(); + // check that vertex is not already treated + if ( !setOfVSubMesh.insert( vertexSM ).second ) + continue; + if ( vertexSM->GetSubShape().ShapeType() != TopAbs_VERTEX ) + continue; + + const SMESHDS_SubMesh* vertexSmDS = vertexSM->GetSubMeshDS(); + SMDS_NodeIteratorPtr nodeIt = vertexSmDS->GetNodes(); + while ( nodeIt->more() ) + { + listOfElemType.push_back( SMDSAbs_Node ); + connectivity.push_back + ( mapNode2LocalID.insert( make_pair( nodeIt->next(), ++nbConnNodes)).first ); + } + } + } + } + + // fill node coords and assign local ids to the nodes + int nbNodes = mapNode2LocalID.size(); + result->nodesXYZ.length( nbNodes ); + TNodeLocalID node2ID = mapNode2LocalID.begin(); + for ( i = 0; i < nbNodes; ++i, ++node2ID ) { + node2ID->second = i; + const SMDS_MeshNode* node = (const SMDS_MeshNode*) node2ID->first; + result->nodesXYZ[i].x = node->X(); + result->nodesXYZ[i].y = node->Y(); + result->nodesXYZ[i].z = node->Z(); + } + // fill connectivity + result->elementConnectivities.length( nbConnNodes ); + list< TNodeLocalID >::iterator connIt = connectivity.begin(); + for ( i = 0; i < nbConnNodes; ++i, ++connIt ) { + result->elementConnectivities[i] = (*connIt)->second; + } + + // fill element types + result->elementTypes.length( listOfElemType.size() ); + std::list< SMDSAbs_ElementType >::const_iterator typeIt = listOfElemType.begin(); + std::list< SMDSAbs_ElementType >::const_iterator typeEnd = listOfElemType.end(); + for ( i = 0; typeIt != typeEnd; ++i, ++typeIt ) { - SMDSAbs_ElementType elemType = *typeIt; - result->elementTypes[i].SMDS_ElementType = (SMESH::ElementType)elemType; - result->elementTypes[i].isPoly = false; - result->elementTypes[i].nbNodesInElement = elemType == SMDSAbs_Edge ? 2 : 1; - } - - // correct number of shapes - theShapesId.length( mapOfShIdNb.size() ); + SMDSAbs_ElementType elemType = *typeIt; + result->elementTypes[i].SMDS_ElementType = (SMESH::ElementType)elemType; + result->elementTypes[i].isPoly = false; + result->elementTypes[i].nbNodesInElement = elemType == SMDSAbs_Edge ? 2 : 1; + } + + // correct number of shapes + theShapesId.length( mapOfShIdNb.size() ); } } } @@ -1595,38 +1865,125 @@ SMESH::MeshPreviewStruct* SMESH_Gen_i::Precompute( SMESH::SMESH_Mesh_ptr theMesh return result._retn(); } -//================================================================================ + +//============================================================================= /*! - * \brief Return geometrical object the given element is built on - * \param theMesh - the mesh the element is in - * \param theElementID - the element ID - * \param theGeomName - the name of the result geom object if it is not yet published - * \retval GEOM::GEOM_Object_ptr - the found or just published geom object + * SMESH_Gen_i::Evaluate + * + * Evaluate mesh on a shape */ -//================================================================================ +//============================================================================= -GEOM::GEOM_Object_ptr -SMESH_Gen_i::GetGeometryByMeshElement( SMESH::SMESH_Mesh_ptr theMesh, - CORBA::Long theElementID, - const char* theGeomName) - throw ( SALOME::SALOME_Exception ) +SMESH::long_array* SMESH_Gen_i::Evaluate(SMESH::SMESH_Mesh_ptr theMesh, + GEOM::GEOM_Object_ptr theShapeObject) +// SMESH::long_array& theNbElems) + throw ( SALOME::SALOME_Exception ) { Unexpect aCatch(SALOME_SalomeException); - - GEOM::GEOM_Object_var geom = FindGeometryByMeshElement(theMesh, theElementID); - if ( !geom->_is_nil() ) { - GEOM::GEOM_Object_var mainShape = theMesh->GetShapeToMesh(); - GEOM::GEOM_Gen_ptr geomGen = GetGeomEngine(); + if(MYDEBUG) MESSAGE( "SMESH_Gen_i::Evaluate" ); - // try to find the corresponding SObject - SALOMEDS::SObject_var SObj = ObjectToSObject( myCurrentStudy, geom.in() ); - if ( SObj->_is_nil() ) // submesh can be not found even if published - { - // try to find published submesh - GEOM::ListOfLong_var list = geom->GetSubShapeIndices(); - if ( !geom->IsMainShape() && list->length() == 1 ) { - SALOMEDS::SObject_var mainSO = ObjectToSObject( myCurrentStudy, mainShape ); - SALOMEDS::ChildIterator_var it; + if ( CORBA::is_nil( theShapeObject ) && theMesh->HasShapeToMesh()) + THROW_SALOME_CORBA_EXCEPTION( "bad shape object reference", + SALOME::BAD_PARAM ); + + if ( CORBA::is_nil( theMesh ) ) + THROW_SALOME_CORBA_EXCEPTION( "bad Mesh reference", + SALOME::BAD_PARAM ); + + SMESH::long_array_var nbels = new SMESH::long_array; + nbels->length(SMESH::Entity_Last); + int i = SMESH::Entity_Node; + for (; i < SMESH::Entity_Last; i++) + nbels[i] = 0; + + // Update Python script + TPythonDump() << "theNbElems = " << this << ".Evaluate( " + << theMesh << ", " << theShapeObject << ")"; + + try { + // get mesh servant + SMESH_Mesh_i* meshServant = dynamic_cast( GetServant( theMesh ).in() ); + ASSERT( meshServant ); + if ( meshServant ) { + // NPAL16168: "geometrical group edition from a submesh don't modifiy mesh computation" + meshServant->CheckGeomGroupModif(); + // get local TopoDS_Shape + TopoDS_Shape myLocShape; + if(theMesh->HasShapeToMesh()) + myLocShape = GeomObjectToShape( theShapeObject ); + else + myLocShape = SMESH_Mesh::PseudoShape(); + // call implementation compute + ::SMESH_Mesh& myLocMesh = meshServant->GetImpl(); + MapShapeNbElems aResMap; + /*CORBA::Boolean ret =*/ myGen.Evaluate( myLocMesh, myLocShape, aResMap); + MapShapeNbElemsItr anIt = aResMap.begin(); + for(; anIt!=aResMap.end(); anIt++) { + const vector& aVec = (*anIt).second; + for(i = SMESH::Entity_Node; i < aVec.size(); i++) { + int nbElem = aVec[i]; + if ( nbElem < 0 ) // algo failed, check that it has reported a message + { + SMESH_subMesh* sm = anIt->first; + SMESH_ComputeErrorPtr& error = sm->GetComputeError(); + const SMESH_Algo* algo = myGen.GetAlgo( myLocMesh, sm->GetSubShape()); + if ( (algo && !error.get()) || error->IsOK() ) + error.reset( new SMESH_ComputeError( COMPERR_ALGO_FAILED,"Failed to evaluate",algo)); + } + else + { + nbels[i] += aVec[i]; + } + } + } + return nbels._retn(); + } + } + catch ( std::bad_alloc ) { + INFOS( "Evaluate(): lack of memory" ); + } + catch ( SALOME_Exception& S_ex ) { + INFOS( "Evaluate(): catch exception "<< S_ex.what() ); + } + catch ( ... ) { + INFOS( "Evaluate(): unknown exception " ); + } + + return nbels._retn(); +} + +//================================================================================ +/*! + * \brief Return geometrical object the given element is built on + * \param theMesh - the mesh the element is in + * \param theElementID - the element ID + * \param theGeomName - the name of the result geom object if it is not yet published + * \retval GEOM::GEOM_Object_ptr - the found or just published geom object + */ +//================================================================================ + +GEOM::GEOM_Object_ptr +SMESH_Gen_i::GetGeometryByMeshElement( SMESH::SMESH_Mesh_ptr theMesh, + CORBA::Long theElementID, + const char* theGeomName) + throw ( SALOME::SALOME_Exception ) +{ + Unexpect aCatch(SALOME_SalomeException); + + GEOM::GEOM_Object_var geom = FindGeometryByMeshElement(theMesh, theElementID); + if ( !geom->_is_nil() ) { + GEOM::GEOM_Object_var mainShape = theMesh->GetShapeToMesh(); + GEOM::GEOM_Gen_ptr geomGen = GetGeomEngine(); + + // try to find the corresponding SObject + SALOMEDS::SObject_var SObj = ObjectToSObject( myCurrentStudy, geom.in() ); + if ( SObj->_is_nil() ) // submesh can be not found even if published + { + // try to find published submesh + GEOM::ListOfLong_var list = geom->GetSubShapeIndices(); + if ( !geom->IsMainShape() && list->length() == 1 ) { + SALOMEDS::SObject_var mainSO = ObjectToSObject( myCurrentStudy, mainShape ); + SALOMEDS::ChildIterator_var it; if ( !mainSO->_is_nil() ) it = myCurrentStudy->NewChildIterator( mainSO ); if ( !it->_is_nil() ) { @@ -1716,7 +2073,7 @@ SMESH_Gen_i::FindGeometryByMeshElement( SMESH::SMESH_Mesh_ptr theMesh, } if ( !geom->_is_nil() ) { GeomObjectToShape( geom ); // let geom client remember the found shape - return geom._retn(); + return geom._retn(); } } } @@ -1733,16 +2090,16 @@ SMESH_Gen_i::FindGeometryByMeshElement( SMESH::SMESH_Mesh_ptr theMesh, //================================================================================ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::Concatenate(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, - CORBA::Double theMergeTolerance) + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, + CORBA::Double theMergeTolerance) throw ( SALOME::SALOME_Exception ) { return ConcatenateCommon(theMeshesArray, - theUniteIdenticalGroups, - theMergeNodesAndElements, - theMergeTolerance, - false); + theUniteIdenticalGroups, + theMergeNodesAndElements, + theMergeTolerance, + false); } //================================================================================ @@ -1756,8 +2113,8 @@ SMESH::SMESH_Mesh_ptr SMESH_Gen_i::Concatenate(const SMESH::mesh_array& theMeshe SMESH::SMESH_Mesh_ptr SMESH_Gen_i::ConcatenateWithGroups(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, CORBA::Double theMergeTolerance) throw ( SALOME::SALOME_Exception ) { @@ -1765,7 +2122,7 @@ SMESH_Gen_i::ConcatenateWithGroups(const SMESH::mesh_array& theMeshesArray, theUniteIdenticalGroups, theMergeNodesAndElements, theMergeTolerance, - true); + true); } //================================================================================ @@ -1778,10 +2135,10 @@ SMESH_Gen_i::ConcatenateWithGroups(const SMESH::mesh_array& theMeshesArray, SMESH::SMESH_Mesh_ptr SMESH_Gen_i::ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, CORBA::Double theMergeTolerance, - CORBA::Boolean theCommonGroups) + CORBA::Boolean theCommonGroups) throw ( SALOME::SALOME_Exception ) { typedef map TIDsMap; @@ -1789,16 +2146,18 @@ SMESH_Gen_i::ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, typedef map< pair, TListOfNewGroups > TGroupsMap; typedef std::set TGroups; - TPythonDump aPythonDump; // prevent dump of called methods + TPythonDump* pPythonDump = new TPythonDump; + TPythonDump& aPythonDump = *pPythonDump; // prevent dump of called methods // create mesh SMESH::SMESH_Mesh_var aNewMesh = CreateEmptyMesh(); - + + SMESHDS_Mesh* aNewMeshDS = 0; if ( !aNewMesh->_is_nil() ) { SMESH_Mesh_i* aNewImpl = dynamic_cast( GetServant( aNewMesh ).in() ); if ( aNewImpl ) { ::SMESH_Mesh& aLocMesh = aNewImpl->GetImpl(); - SMESHDS_Mesh* aNewMeshDS = aLocMesh.GetMeshDS(); + aNewMeshDS = aLocMesh.GetMeshDS(); TGroupsMap aGroupsMap; TListOfNewGroups aListOfNewGroups; @@ -1807,240 +2166,280 @@ SMESH_Gen_i::ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, // loop on meshes for ( int i = 0; i < theMeshesArray.length(); i++) { - SMESH::SMESH_Mesh_var anInitMesh = theMeshesArray[i]; - if ( !anInitMesh->_is_nil() ) { - SMESH_Mesh_i* anInitImpl = dynamic_cast( GetServant( anInitMesh ).in() ); - if ( anInitImpl ) { - ::SMESH_Mesh& aInitLocMesh = anInitImpl->GetImpl(); - SMESHDS_Mesh* anInitMeshDS = aInitLocMesh.GetMeshDS(); - - TIDsMap nodesMap; - TIDsMap elemsMap; - - // loop on elements of mesh - SMDS_ElemIteratorPtr itElems = anInitMeshDS->elementsIterator(); - const SMDS_MeshElement* anElem = 0; - const SMDS_MeshElement* aNewElem = 0; - int anElemNbNodes = 0; - - int anNbNodes = 0; - int anNbEdges = 0; - int anNbFaces = 0; - int anNbVolumes = 0; - - SMESH::long_array_var anIDsNodes = new SMESH::long_array(); - SMESH::long_array_var anIDsEdges = new SMESH::long_array(); - SMESH::long_array_var anIDsFaces = new SMESH::long_array(); - SMESH::long_array_var anIDsVolumes = new SMESH::long_array(); - - if( theCommonGroups ) { - anIDsNodes->length( anInitMeshDS->NbNodes() ); - anIDsEdges->length( anInitMeshDS->NbEdges() ); - anIDsFaces->length( anInitMeshDS->NbFaces() ); - anIDsVolumes->length( anInitMeshDS->NbVolumes() ); - } - - for ( int j = 0; itElems->more(); j++) { - anElem = itElems->next(); - SMDSAbs_ElementType anElemType = anElem->GetType(); - anElemNbNodes = anElem->NbNodes(); - std::vector aNodesArray (anElemNbNodes); - - // loop on nodes of element - const SMDS_MeshNode* aNode = 0; - const SMDS_MeshNode* aNewNode = 0; - SMDS_ElemIteratorPtr itNodes = anElem->nodesIterator(); - - for ( int k = 0; itNodes->more(); k++) { - aNode = static_cast(itNodes->next()); - if ( nodesMap.find(aNode->GetID()) == nodesMap.end() ) { - aNewNode = aNewMeshDS->AddNode(aNode->X(), aNode->Y(), aNode->Z()); - nodesMap.insert( make_pair(aNode->GetID(), aNewNode->GetID()) ); - if( theCommonGroups ) - anIDsNodes[anNbNodes++] = aNewNode->GetID(); - } - else - aNewNode = aNewMeshDS->FindNode( nodesMap.find(aNode->GetID())->second ); - aNodesArray[k] = aNewNode; - }//nodes loop - - // creates a corresponding element on existent nodes in new mesh - if ( anElem->IsPoly() && anElemType == SMDSAbs_Volume ) - { - const SMDS_PolyhedralVolumeOfNodes* aVolume = - dynamic_cast (anElem); - if ( aVolume ) { - aNewElem = aNewMeshDS->AddPolyhedralVolume(aNodesArray, - aVolume->GetQuanities()); - elemsMap.insert(make_pair(anElem->GetID(), aNewElem->GetID())); - if( theCommonGroups ) - anIDsVolumes[anNbVolumes++] = aNewElem->GetID(); - } - } - else { - - aNewElem = aNewEditor.AddElement(aNodesArray, - anElemType, - anElem->IsPoly()); - elemsMap.insert(make_pair(anElem->GetID(), aNewElem->GetID())); - if( theCommonGroups ) { - if( anElemType == SMDSAbs_Edge ) - anIDsEdges[anNbEdges++] = aNewElem->GetID(); - else if( anElemType == SMDSAbs_Face ) - anIDsFaces[anNbFaces++] = aNewElem->GetID(); - else if( anElemType == SMDSAbs_Volume ) - anIDsVolumes[anNbVolumes++] = aNewElem->GetID(); - } - } - }//elems loop - - aListOfGroups = anInitImpl->GetGroups(); - SMESH::SMESH_GroupBase_ptr aGroup; - - // loop on groups of mesh - SMESH::long_array_var anInitIDs = new SMESH::long_array(); - SMESH::long_array_var anNewIDs = new SMESH::long_array(); - SMESH::SMESH_Group_var aNewGroup; - - SMESH::ElementType aGroupType; - CORBA::String_var aGroupName; - if ( theCommonGroups ) { - for(aGroupType=SMESH::NODE;aGroupType<=SMESH::VOLUME;aGroupType=(SMESH::ElementType)(aGroupType+1)) { - string str = "Gr"; - SALOMEDS::SObject_var aMeshSObj = ObjectToSObject( myCurrentStudy, anInitMesh ); - if(aMeshSObj) - str += aMeshSObj->GetName(); - str += "_"; - - int anLen = 0; - - switch(aGroupType) { - case SMESH::NODE: - str += "Nodes"; - anIDsNodes->length(anNbNodes); - anLen = anNbNodes; - break; - case SMESH::EDGE: - str += "Edges"; - anIDsEdges->length(anNbEdges); - anLen = anNbEdges; - break; - case SMESH::FACE: - str += "Faces"; - anIDsFaces->length(anNbFaces); - anLen = anNbFaces; - break; - case SMESH::VOLUME: - str += "Volumes"; - anIDsVolumes->length(anNbVolumes); - anLen = anNbVolumes; - break; - default: - break; - } - - if(anLen) { - aGroupName = str.c_str(); - - // add a new group in the mesh - aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); - - switch(aGroupType) { - case SMESH::NODE: - aNewGroup->Add( anIDsNodes ); - break; - case SMESH::EDGE: - aNewGroup->Add( anIDsEdges ); - break; - case SMESH::FACE: - aNewGroup->Add( anIDsFaces ); - break; - case SMESH::VOLUME: - aNewGroup->Add( anIDsVolumes ); - break; - default: - break; - } - - aListOfNewGroups.clear(); - aListOfNewGroups.push_back(aNewGroup); - aGroupsMap.insert(make_pair( make_pair(aGroupName, aGroupType), aListOfNewGroups )); - } - } - } - - // check that current group name and type don't have identical ones in union mesh - for (int i = 0; i < aListOfGroups->length(); i++) { - aGroup = aListOfGroups[i]; - aListOfNewGroups.clear(); - aGroupType = aGroup->GetType(); - aGroupName = aGroup->GetName(); - - TGroupsMap::iterator anIter = aGroupsMap.find(make_pair(aGroupName, aGroupType)); - - // convert a list of IDs - anInitIDs = aGroup->GetListOfID(); - anNewIDs->length(anInitIDs->length()); - if ( aGroupType == SMESH::NODE ) - for (int j = 0; j < anInitIDs->length(); j++) { - anNewIDs[j] = nodesMap.find(anInitIDs[j])->second; - } - else - for (int j = 0; j < anInitIDs->length(); j++) { - anNewIDs[j] = elemsMap.find(anInitIDs[j])->second; - } - - // check that current group name and type don't have identical ones in union mesh - if ( anIter == aGroupsMap.end() ) { - // add a new group in the mesh - aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); - // add elements into new group - aNewGroup->Add( anNewIDs ); - - aListOfNewGroups.push_back(aNewGroup); - aGroupsMap.insert(make_pair( make_pair(aGroupName, aGroupType), aListOfNewGroups )); - } - - else if ( theUniteIdenticalGroups ) { - // unite identical groups - TListOfNewGroups& aNewGroups = anIter->second; - aNewGroups.front()->Add( anNewIDs ); - } - - else { - // rename identical groups - aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); - aNewGroup->Add( anNewIDs ); - - TListOfNewGroups& aNewGroups = anIter->second; - string aNewGroupName; - if (aNewGroups.size() == 1) { - aNewGroupName = string(aGroupName) + "_1"; - aNewGroups.front()->SetName(aNewGroupName.c_str()); - } - char aGroupNum[128]; - sprintf(aGroupNum, "%u", aNewGroups.size()+1); - aNewGroupName = string(aGroupName) + "_" + string(aGroupNum); - aNewGroup->SetName(aNewGroupName.c_str()); - aNewGroups.push_back(aNewGroup); - } - }//groups loop - } - } + SMESH::SMESH_Mesh_var anInitMesh = theMeshesArray[i]; + if ( !anInitMesh->_is_nil() ) { + SMESH_Mesh_i* anInitImpl = dynamic_cast( GetServant( anInitMesh ).in() ); + if ( anInitImpl ) { + ::SMESH_Mesh& aInitLocMesh = anInitImpl->GetImpl(); + SMESHDS_Mesh* anInitMeshDS = aInitLocMesh.GetMeshDS(); + + TIDsMap nodesMap; + TIDsMap elemsMap; + + // loop on elements of mesh + SMDS_ElemIteratorPtr itElems = anInitMeshDS->elementsIterator(); + const SMDS_MeshElement* anElem = 0; + const SMDS_MeshElement* aNewElem = 0; + int anElemNbNodes = 0; + + int anNbNodes = 0; + int anNbEdges = 0; + int anNbFaces = 0; + int anNbVolumes = 0; + int aNbBalls = 0; + + SMESH::long_array_var anIDsNodes = new SMESH::long_array(); + SMESH::long_array_var anIDsEdges = new SMESH::long_array(); + SMESH::long_array_var anIDsFaces = new SMESH::long_array(); + SMESH::long_array_var anIDsVolumes = new SMESH::long_array(); + SMESH::long_array_var anIDsBalls = new SMESH::long_array(); + + if( theCommonGroups ) { + anIDsNodes->length( anInitMeshDS->NbNodes() ); + anIDsEdges->length( anInitMeshDS->NbEdges() ); + anIDsFaces->length( anInitMeshDS->NbFaces() ); + anIDsVolumes->length( anInitMeshDS->NbVolumes() ); + anIDsBalls->length( anInitMeshDS->NbBalls() ); + } + + for ( int j = 0; itElems->more(); j++) { + anElem = itElems->next(); + SMDSAbs_ElementType anElemType = anElem->GetType(); + anElemNbNodes = anElem->NbNodes(); + std::vector aNodesArray (anElemNbNodes); + + // loop on nodes of element + const SMDS_MeshNode* aNode = 0; + const SMDS_MeshNode* aNewNode = 0; + SMDS_ElemIteratorPtr itNodes = anElem->nodesIterator(); + + for ( int k = 0; itNodes->more(); k++) { + aNode = static_cast(itNodes->next()); + if ( nodesMap.find(aNode->GetID()) == nodesMap.end() ) { + aNewNode = aNewMeshDS->AddNode(aNode->X(), aNode->Y(), aNode->Z()); + nodesMap.insert( make_pair(aNode->GetID(), aNewNode->GetID()) ); + if( theCommonGroups ) + anIDsNodes[anNbNodes++] = aNewNode->GetID(); + } + else + aNewNode = aNewMeshDS->FindNode( nodesMap.find(aNode->GetID())->second ); + aNodesArray[k] = aNewNode; + }//nodes loop + + // creates a corresponding element on existent nodes in new mesh + switch ( anElem->GetEntityType() ) { + case SMDSEntity_Polyhedra: + if ( const SMDS_VtkVolume* aVolume = + dynamic_cast (anElem)) + { + aNewElem = aNewMeshDS->AddPolyhedralVolume(aNodesArray, + aVolume->GetQuantities()); + elemsMap.insert(make_pair(anElem->GetID(), aNewElem->GetID())); + if( theCommonGroups ) + anIDsVolumes[anNbVolumes++] = aNewElem->GetID(); + } + break; + case SMDSEntity_Ball: + if ( const SMDS_BallElement* aBall = + dynamic_cast (anElem)) + { + aNewElem = aNewEditor.AddElement(aNodesArray, SMDSAbs_Ball, + /*isPoly=*/false, /*id=*/0, + aBall->GetDiameter() ); + elemsMap.insert(make_pair(anElem->GetID(), aNewElem->GetID())); + if( theCommonGroups ) + anIDsBalls[aNbBalls++] = aNewElem->GetID(); + } + break; + default: + { + aNewElem = aNewEditor.AddElement(aNodesArray, + anElemType, + anElem->IsPoly()); + elemsMap.insert(make_pair(anElem->GetID(), aNewElem->GetID())); + if( theCommonGroups ) { + if( anElemType == SMDSAbs_Edge ) + anIDsEdges[anNbEdges++] = aNewElem->GetID(); + else if( anElemType == SMDSAbs_Face ) + anIDsFaces[anNbFaces++] = aNewElem->GetID(); + else if( anElemType == SMDSAbs_Volume ) + anIDsVolumes[anNbVolumes++] = aNewElem->GetID(); + } + } + } + }//elems loop + + // copy orphan nodes + SMDS_NodeIteratorPtr itNodes = anInitMeshDS->nodesIterator(); + while ( itNodes->more() ) + { + const SMDS_MeshNode* aNode = itNodes->next(); + if ( aNode->NbInverseElements() == 0 ) + { + const SMDS_MeshNode* aNewNode = + aNewMeshDS->AddNode(aNode->X(), aNode->Y(), aNode->Z()); + nodesMap.insert( make_pair(aNode->GetID(), aNewNode->GetID()) ); + if( theCommonGroups ) + anIDsNodes[anNbNodes++] = aNewNode->GetID(); + } + } + + + aListOfGroups = anInitImpl->GetGroups(); + SMESH::SMESH_GroupBase_ptr aGroup; + + // loop on groups of mesh + SMESH::long_array_var anInitIDs = new SMESH::long_array(); + SMESH::long_array_var anNewIDs = new SMESH::long_array(); + SMESH::SMESH_Group_var aNewGroup; + + SMESH::ElementType aGroupType; + CORBA::String_var aGroupName; + if ( theCommonGroups ) { + for(aGroupType=SMESH::NODE;aGroupType<=SMESH::BALL;aGroupType=(SMESH::ElementType)(aGroupType+1)) { + string str = "Gr"; + SALOMEDS::SObject_var aMeshSObj = ObjectToSObject( myCurrentStudy, anInitMesh ); + if(aMeshSObj) + str += aMeshSObj->GetName(); + str += "_"; + + int anLen = 0; + + switch(aGroupType) { + case SMESH::NODE: + str += "Nodes"; + anIDsNodes->length(anNbNodes); + anLen = anNbNodes; + break; + case SMESH::EDGE: + str += "Edges"; + anIDsEdges->length(anNbEdges); + anLen = anNbEdges; + break; + case SMESH::FACE: + str += "Faces"; + anIDsFaces->length(anNbFaces); + anLen = anNbFaces; + break; + case SMESH::VOLUME: + str += "Volumes"; + anIDsVolumes->length(anNbVolumes); + anLen = anNbVolumes; + break; + case SMESH::BALL: + str += "Balls"; + anIDsBalls->length(aNbBalls); + anLen = aNbBalls; + break; + default: + break; + } + + if(anLen) { + aGroupName = str.c_str(); + + // add a new group in the mesh + aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); + + switch(aGroupType) { + case SMESH::NODE: + aNewGroup->Add( anIDsNodes ); + break; + case SMESH::EDGE: + aNewGroup->Add( anIDsEdges ); + break; + case SMESH::FACE: + aNewGroup->Add( anIDsFaces ); + break; + case SMESH::VOLUME: + aNewGroup->Add( anIDsVolumes ); + break; + case SMESH::BALL: + aNewGroup->Add( anIDsBalls ); + break; + default: + break; + } + + aListOfNewGroups.clear(); + aListOfNewGroups.push_back(aNewGroup); + aGroupsMap.insert(make_pair( make_pair(aGroupName, aGroupType), aListOfNewGroups )); + } + } + } + + // check that current group name and type don't have identical ones in union mesh + for (int i = 0; i < aListOfGroups->length(); i++) { + aGroup = aListOfGroups[i]; + aListOfNewGroups.clear(); + aGroupType = aGroup->GetType(); + aGroupName = aGroup->GetName(); + + TGroupsMap::iterator anIter = aGroupsMap.find(make_pair(aGroupName, aGroupType)); + + // convert a list of IDs + anInitIDs = aGroup->GetListOfID(); + anNewIDs->length(anInitIDs->length()); + if ( aGroupType == SMESH::NODE ) + for (int j = 0; j < anInitIDs->length(); j++) { + anNewIDs[j] = nodesMap.find(anInitIDs[j])->second; + } + else + for (int j = 0; j < anInitIDs->length(); j++) { + anNewIDs[j] = elemsMap.find(anInitIDs[j])->second; + } + + // check that current group name and type don't have identical ones in union mesh + if ( anIter == aGroupsMap.end() ) { + // add a new group in the mesh + aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); + // add elements into new group + aNewGroup->Add( anNewIDs ); + + aListOfNewGroups.push_back(aNewGroup); + aGroupsMap.insert(make_pair( make_pair(aGroupName, aGroupType), aListOfNewGroups )); + } + + else if ( theUniteIdenticalGroups ) { + // unite identical groups + TListOfNewGroups& aNewGroups = anIter->second; + aNewGroups.front()->Add( anNewIDs ); + } + + else { + // rename identical groups + aNewGroup = aNewImpl->CreateGroup(aGroupType, aGroupName); + aNewGroup->Add( anNewIDs ); + + TListOfNewGroups& aNewGroups = anIter->second; + string aNewGroupName; + if (aNewGroups.size() == 1) { + aNewGroupName = string(aGroupName) + "_1"; + aNewGroups.front()->SetName(aNewGroupName.c_str()); + } + char aGroupNum[128]; + sprintf(aGroupNum, "%u", aNewGroups.size()+1); + aNewGroupName = string(aGroupName) + "_" + string(aGroupNum); + aNewGroup->SetName(aNewGroupName.c_str()); + aNewGroups.push_back(aNewGroup); + } + }//groups loop + } + } }//meshes loop if (theMergeNodesAndElements) { - // merge nodes - set aMeshNodes; // no input nodes - SMESH_MeshEditor::TListOfListOfNodes aGroupsOfNodes; - aNewEditor.FindCoincidentNodes( aMeshNodes, theMergeTolerance, aGroupsOfNodes ); - aNewEditor.MergeNodes( aGroupsOfNodes ); - // merge elements - aNewEditor.MergeEqualElements(); + // merge nodes + TIDSortedNodeSet aMeshNodes; // no input nodes + SMESH_MeshEditor::TListOfListOfNodes aGroupsOfNodes; + aNewEditor.FindCoincidentNodes( aMeshNodes, theMergeTolerance, aGroupsOfNodes ); + aNewEditor.MergeNodes( aGroupsOfNodes ); + // merge elements + aNewEditor.MergeEqualElements(); } } } - + // Update Python script aPythonDump << aNewMesh << " = " << this; if( !theCommonGroups ) @@ -2054,12 +2453,309 @@ SMESH_Gen_i::ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, } aPythonDump << "], "; aPythonDump << theUniteIdenticalGroups << ", " - << theMergeNodesAndElements << ", " - << theMergeTolerance << ")"; + << theMergeNodesAndElements << ", " + << TVar( theMergeTolerance ) << ")"; + + delete pPythonDump; // enable python dump from GetGroups() + + // 0020577: EDF 1164 SMESH: Bad dump of concatenate with create common groups + if ( !aNewMesh->_is_nil() ) + { + SMESH::ListOfGroups_var groups = aNewMesh->GetGroups(); + } + // IPAL21468 Change icon of compound because it need not be computed. + SALOMEDS::SObject_var aMeshSObj = ObjectToSObject( myCurrentStudy, aNewMesh ); + SetPixMap( aMeshSObj, "ICON_SMESH_TREE_MESH" ); + + if (aNewMeshDS) + aNewMeshDS->Modified(); return aNewMesh._retn(); } +//================================================================================ +/*! + * \brief Create a mesh by copying a part of another mesh + * \param meshPart - a part of mesh to copy + * \param toCopyGroups - to create in the new mesh groups + * the copied elements belongs to + * \param toKeepIDs - to preserve IDs of the copied elements or not + * \retval SMESH::SMESH_Mesh_ptr - the new mesh + */ +//================================================================================ + +SMESH::SMESH_Mesh_ptr SMESH_Gen_i::CopyMesh(SMESH::SMESH_IDSource_ptr meshPart, + const char* meshName, + CORBA::Boolean toCopyGroups, + CORBA::Boolean toKeepIDs) +{ + Unexpect aCatch(SALOME_SalomeException); + + TPythonDump* pyDump = new TPythonDump; // prevent dump from CreateMesh() + + // 1. Get source mesh + + if ( CORBA::is_nil( meshPart )) + THROW_SALOME_CORBA_EXCEPTION( "bad IDSource", SALOME::BAD_PARAM ); + + SMESH::SMESH_Mesh_var srcMesh = meshPart->GetMesh(); + SMESH_Mesh_i* srcMesh_i = SMESH::DownCast( srcMesh ); + if ( !srcMesh_i ) + THROW_SALOME_CORBA_EXCEPTION( "bad mesh of IDSource", SALOME::BAD_PARAM ); + + SMESHDS_Mesh* srcMeshDS = srcMesh_i->GetImpl().GetMeshDS(); + + // 2. Make a new mesh + + SMESH::SMESH_Mesh_var newMesh = CreateMesh(GEOM::GEOM_Object::_nil()); + SMESH_Mesh_i* newMesh_i = SMESH::DownCast( newMesh ); + if ( !newMesh_i ) + THROW_SALOME_CORBA_EXCEPTION( "can't create a mesh", SALOME::INTERNAL_ERROR ); + SALOMEDS::SObject_var meshSO = ObjectToSObject(myCurrentStudy, newMesh ); + if ( !meshSO->_is_nil() ) + { + SetName( meshSO, meshName, "Mesh" ); + SetPixMap( meshSO, "ICON_SMESH_TREE_MESH_IMPORTED"); + } + SMESHDS_Mesh* newMeshDS = newMesh_i->GetImpl().GetMeshDS(); + ::SMESH_MeshEditor editor( &newMesh_i->GetImpl() ); + + // 3. Get elements to copy + + SMDS_ElemIteratorPtr srcElemIt; SMDS_NodeIteratorPtr srcNodeIt; + TIDSortedElemSet srcElems; + SMESH::array_of_ElementType_var srcElemTypes = meshPart->GetTypes(); + if ( SMESH::DownCast( meshPart )) + { + srcElemIt = srcMeshDS->elementsIterator(); + srcNodeIt = srcMeshDS->nodesIterator(); + } + else + { + SMESH::long_array_var ids = meshPart->GetIDs(); + if ( srcElemTypes->length() == 1 && srcElemTypes[0] == SMESH::NODE ) // group of nodes + { + for (int i=0; i < ids->length(); i++) + if ( const SMDS_MeshElement * elem = srcMeshDS->FindNode( ids[i] )) + srcElems.insert( elem ); + } + else + { + for (int i=0; i < ids->length(); i++) + if ( const SMDS_MeshElement * elem = srcMeshDS->FindElement( ids[i] )) + srcElems.insert( elem ); + } + if ( srcElems.empty() ) + return newMesh._retn(); + + typedef SMDS_SetIterator< SMDS_pElement, TIDSortedElemSet::const_iterator > ElIter; + srcElemIt = SMDS_ElemIteratorPtr( new ElIter( srcElems.begin(), srcElems.end() )); + } + + // 4. Copy elements + + typedef map TE2EMap; + TE2EMap e2eMapByType[ SMDSAbs_NbElementTypes ]; + TE2EMap& n2nMap = e2eMapByType[ SMDSAbs_Node ]; + int iN; + const SMDS_MeshNode *nSrc, *nTgt; + vector< const SMDS_MeshNode* > nodes; + while ( srcElemIt->more() ) + { + const SMDS_MeshElement * elem = srcElemIt->next(); + // find / add nodes + nodes.resize( elem->NbNodes()); + SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); + if ( toKeepIDs ) { + for ( iN = 0; nIt->more(); ++iN ) + { + nSrc = static_cast( nIt->next() ); + nTgt = newMeshDS->FindNode( nSrc->GetID()); + if ( !nTgt ) + nTgt = newMeshDS->AddNodeWithID( nSrc->X(), nSrc->Y(), nSrc->Z(), nSrc->GetID()); + nodes[ iN ] = nTgt; + } + } + else { + for ( iN = 0; nIt->more(); ++iN ) + { + nSrc = static_cast( nIt->next() ); + TE2EMap::iterator n2n = n2nMap.insert( make_pair( nSrc, SMDS_pNode(0) )).first; + if ( !n2n->second ) + n2n->second = newMeshDS->AddNode( nSrc->X(), nSrc->Y(), nSrc->Z() ); + nodes[ iN ] = (const SMDS_MeshNode*) n2n->second; + } + } + // add elements + if ( elem->GetType() != SMDSAbs_Node ) + { + int ID = toKeepIDs ? elem->GetID() : 0; + const SMDS_MeshElement * newElem; + switch ( elem->GetEntityType() ) { + case SMDSEntity_Polyhedra: + newElem = editor.GetMeshDS()-> + AddPolyhedralVolumeWithID( nodes, + static_cast(elem)->GetQuantities(), + ID); + break; + case SMDSEntity_Ball: + newElem = editor.AddElement( nodes, SMDSAbs_Ball, false, ID, + static_cast(elem)->GetDiameter()); + break; + default: + newElem = editor.AddElement( nodes,elem->GetType(),elem->IsPoly(),ID); + + if ( toCopyGroups && !toKeepIDs ) + e2eMapByType[ elem->GetType() ].insert( make_pair( elem, newElem )); + } + } + } // while ( srcElemIt->more() ) + + // 4(b). Copy free nodes + + if ( srcNodeIt && srcMeshDS->NbNodes() != newMeshDS->NbNodes() ) + { + while ( srcNodeIt->more() ) + { + nSrc = srcNodeIt->next(); + if ( nSrc->NbInverseElements() == 0 ) + { + if ( toKeepIDs ) + nTgt = newMeshDS->AddNodeWithID( nSrc->X(), nSrc->Y(), nSrc->Z(), nSrc->GetID()); + else + n2nMap[ nSrc ] = newMeshDS->AddNode( nSrc->X(), nSrc->Y(), nSrc->Z() ); + } + } + } + + // 5. Copy groups + + int nbNewGroups = 0; + if ( toCopyGroups ) + { + SMESH_Mesh::GroupIteratorPtr gIt = srcMesh_i->GetImpl().GetGroups(); + while ( gIt->more() ) + { + SMESH_Group* group = gIt->next(); + const SMESHDS_GroupBase* groupDS = group->GetGroupDS(); + + // Check group type. We copy nodal groups containing nodes of copied element + SMDSAbs_ElementType groupType = groupDS->GetType(); + if ( groupType != SMDSAbs_Node && + newMeshDS->GetMeshInfo().NbElements( groupType ) == 0 ) + continue; // group type differs from types of meshPart + + // Find copied elements in the group + vector< const SMDS_MeshElement* > groupElems; + SMDS_ElemIteratorPtr eIt = groupDS->GetElements(); + if ( toKeepIDs ) + { + const SMDS_MeshElement* foundElem; + if ( groupType == SMDSAbs_Node ) + { + while ( eIt->more() ) + if (( foundElem = newMeshDS->FindNode( eIt->next()->GetID() ))) + groupElems.push_back( foundElem ); + } + else + { + while ( eIt->more() ) + if (( foundElem = newMeshDS->FindElement( eIt->next()->GetID() ))) + groupElems.push_back( foundElem ); + } + } + else + { + TE2EMap & e2eMap = e2eMapByType[ groupDS->GetType() ]; + if ( e2eMap.empty() ) continue; + int minID = e2eMap.begin()->first->GetID(); + int maxID = e2eMap.rbegin()->first->GetID(); + TE2EMap::iterator e2e; + while ( eIt->more() && groupElems.size() < e2eMap.size()) + { + const SMDS_MeshElement* e = eIt->next(); + if ( e->GetID() < minID || e->GetID() > maxID ) continue; + if ((e2e = e2eMap.find( e )) != e2eMap.end()) + groupElems.push_back( e2e->second ); + } + } + // Make a new group + if ( !groupElems.empty() ) + { + SMESH::SMESH_Group_var newGroupObj = + newMesh->CreateGroup( SMESH::ElementType(groupType), group->GetName() ); + if ( SMESH_GroupBase_i* newGroup_i = SMESH::DownCast( newGroupObj)) + { + SMESHDS_GroupBase * newGroupDS = newGroup_i->GetGroupDS(); + SMDS_MeshGroup& smdsGroup = ((SMESHDS_Group*)newGroupDS)->SMDSGroup(); + for ( unsigned i = 0; i < groupElems.size(); ++i ) + smdsGroup.Add( groupElems[i] ); + + nbNewGroups++; + } + } + } + } + + newMeshDS->Modified(); + + *pyDump << newMesh << " = " << this + << ".CopyMesh( " << meshPart << ", " + << "'" << meshName << "', " + << toCopyGroups << ", " + << toKeepIDs << ")"; + + delete pyDump; pyDump = 0; // allow dump in GetGroups() + + if ( nbNewGroups > 0 ) // dump created groups + SMESH::ListOfGroups_var groups = newMesh->GetGroups(); + + return newMesh._retn(); +} + +//================================================================================ +/*! + * SMESH_Gen_i::GetMEDVersion + * + * Get MED version of the file by its name + */ +//================================================================================ +CORBA::Boolean SMESH_Gen_i::GetMEDVersion(const char* theFileName, + SMESH::MED_VERSION& theVersion) +{ + theVersion = SMESH::MED_V2_1; + MED::EVersion aVersion = MED::GetVersionId( theFileName ); + switch( aVersion ) { + case MED::eV2_1 : theVersion = SMESH::MED_V2_1; return true; + case MED::eV2_2 : theVersion = SMESH::MED_V2_2; return true; + case MED::eVUnknown : return false; + } + return false; +} + +//================================================================================ +/*! + * SMESH_Gen_i::GetMeshNames + * + * Get names of meshes defined in file with the specified name + */ +//================================================================================ +SMESH::string_array* SMESH_Gen_i::GetMeshNames(const char* theFileName) +{ + SMESH::string_array_var aResult = new SMESH::string_array(); + MED::PWrapper aMed = MED::CrWrapper( theFileName ); + MED::TErr anErr; + MED::TInt aNbMeshes = aMed->GetNbMeshes( &anErr ); + if( anErr >= 0 ) { + aResult->length( aNbMeshes ); + for( MED::TInt i = 0; i < aNbMeshes; i++ ) { + MED::PMeshInfo aMeshInfo = aMed->GetPMeshInfo( i+1 ); + aResult[i] = CORBA::string_dup( aMeshInfo->GetName().c_str() ); + } + } + return aResult._retn(); +} + //============================================================================= /*! * SMESH_Gen_i::Save @@ -2076,7 +2772,7 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, // ASSERT( theComponent->GetStudy()->StudyId() == myCurrentStudy->StudyId() ) // san -- in case differs from theComponent's study, // use that of the component - if ( myCurrentStudy->_is_nil() || + if ( myCurrentStudy->_is_nil() || theComponent->GetStudy()->StudyId() != myCurrentStudy->StudyId() ) SetCurrentStudy( theComponent->GetStudy() ); @@ -2097,7 +2793,7 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, aFileSeq->length( NUM_TMP_FILES ); TCollection_AsciiString aStudyName( "" ); - if ( isMultiFile ) + if ( isMultiFile ) aStudyName = ( (char*)SALOMEDS_Tool::GetNameFromPath( myCurrentStudy->URL() ).c_str() ); // Set names of temporary files @@ -2136,6 +2832,41 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, DriverMED_W_SMESHDS_Mesh myWriter; myWriter.SetFile( meshfile.ToCString() ); + // IMP issue 20918 + // SetStoreName() to groups before storing hypotheses to let them refer to + // groups using "store name", which is "Group " + { + SALOMEDS::ChildIterator_var itBig = myCurrentStudy->NewChildIterator( theComponent ); + for ( ; itBig->More(); itBig->Next() ) { + SALOMEDS::SObject_var gotBranch = itBig->Value(); + if ( gotBranch->Tag() > GetAlgorithmsRootTag() ) { + CORBA::Object_var anObject = SObjectToObject( gotBranch ); + if ( !CORBA::is_nil( anObject ) ) { + SMESH::SMESH_Mesh_var myMesh = SMESH::SMESH_Mesh::_narrow( anObject ) ; + if ( !myMesh->_is_nil() ) { + myMesh->Load(); // load from study file if not yet done + TPythonDump pd; // not to dump GetGroups() + SMESH::ListOfGroups_var groups = myMesh->GetGroups(); + pd << ""; // to avoid optimizing pd out + for ( int i = 0; i < groups->length(); ++i ) + { + SMESH_GroupBase_i* grImpl = SMESH::DownCast( groups[i]); + if ( grImpl ) + { + CORBA::String_var objStr = GetORB()->object_to_string( grImpl->_this() ); + int anId = myStudyContext->findId( string( objStr.in() ) ); + char grpName[ 30 ]; + sprintf( grpName, "Group %d", anId ); + SMESHDS_GroupBase* aGrpBaseDS = grImpl->GetGroupDS(); + aGrpBaseDS->SetStoreName( grpName ); + } + } + } + } + } + } + } + // Write data // ---> create HDF file aFile = new HDFfile( (char*) filename.ToCString() ); @@ -2319,17 +3050,33 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, aDataset->WriteOnDisk( ( char* )( strHasData.c_str() ) ); aDataset->CloseOnDisk(); - // ouv : NPAL12872 + // ouv : NPAL12872 // for each mesh open the HDF group basing on its auto color parameter - char meshAutoColorName[ 30 ]; - sprintf( meshAutoColorName, "AutoColorMesh %d", id ); - int anAutoColor[1]; - anAutoColor[0] = myImpl->GetAutoColor(); - aSize[ 0 ] = 1; - aDataset = new HDFdataset( meshAutoColorName, aTopGroup, HDF_INT32, aSize, 1 ); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( anAutoColor ); - aDataset->CloseOnDisk(); + char meshAutoColorName[ 30 ]; + sprintf( meshAutoColorName, "AutoColorMesh %d", id ); + int anAutoColor[1]; + anAutoColor[0] = myImpl->GetAutoColor(); + aSize[ 0 ] = 1; + aDataset = new HDFdataset( meshAutoColorName, aTopGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( anAutoColor ); + aDataset->CloseOnDisk(); + + // issue 0020693. Store _isModified flag + int isModified = myLocMesh.GetIsModified(); + aSize[ 0 ] = 1; + aDataset = new HDFdataset( "_isModified", aTopGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( &isModified ); + aDataset->CloseOnDisk(); + + // issue 20918. Store Persistent Id of SMESHDS_Mesh + int meshPersistentId = mySMESHDSMesh->GetPersistentId(); + aSize[ 0 ] = 1; + aDataset = new HDFdataset( "meshPersistentId", aTopGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( &meshPersistentId ); + aDataset->CloseOnDisk(); // write reference on a shape if exists SALOMEDS::SObject_var myRef; @@ -2370,7 +3117,7 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, if ( ok ) { // san - it is impossible to recover applied hypotheses // using their entries within Load() method, - // for there are no AttributeIORs in the study when Load() is working. + // for there are no AttributeIORs in the study when Load() is working. // Hence, it is better to store persistent IDs of hypotheses as references to them //string myRefOnObject = myRefOnHyp->GetID(); @@ -2413,7 +3160,7 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, if ( ok ) { // san - it is impossible to recover applied algorithms // using their entries within Load() method, - // for there are no AttributeIORs in the study when Load() is working. + // for there are no AttributeIORs in the study when Load() is working. // Hence, it is better to store persistent IDs of algorithms as references to them //string myRefOnObject = myRefOnAlgo->GetID(); @@ -2563,382 +3310,430 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, //} } } - aSubSubGroup->CloseOnDisk(); - } + aSubSubGroup->CloseOnDisk(); + } + + // write applied algorithms if exist + SALOMEDS::SObject_var mySubAlgoBranch; + found = mySObject->FindSubObject( GetRefOnAppliedAlgorithmsTag(), mySubAlgoBranch ); + if ( found ) { + aSubSubGroup = new HDFgroup( "Applied Algorithms", aSubGroup ); + aSubSubGroup->CreateOnDisk(); + + SALOMEDS::ChildIterator_var it = myCurrentStudy->NewChildIterator( mySubAlgoBranch ); + int algoNb = 0; + for ( ; it->More(); it->Next() ) { + SALOMEDS::SObject_var mySubSObject = it->Value(); + SALOMEDS::SObject_var myRefOnAlgo; + bool ok = mySubSObject->ReferencedObject( myRefOnAlgo ); + if ( ok ) { + //string myRefOnObject = myRefOnAlgo->GetID(); + CORBA::Object_var anObject = SObjectToObject( myRefOnAlgo ); + CORBA::String_var objStr = GetORB()->object_to_string( anObject ); + int id = myStudyContext->findId( string( objStr.in() ) ); + //if ( myRefOnObject.length() > 0 ) { + //aSize[ 0 ] = myRefOnObject.length() + 1; + char algoName[ 30 ], algoId[ 30 ]; + sprintf( algoName, "Algo %d", ++algoNb ); + sprintf( algoId, "%d", id ); + aSize[ 0 ] = strlen( algoId ) + 1; + aDataset = new HDFdataset( algoName, aSubSubGroup, HDF_STRING, aSize, 1 ); + aDataset->CreateOnDisk(); + //aDataset->WriteOnDisk( ( char* )( myRefOnObject.c_str() ) ); + aDataset->WriteOnDisk( algoId ); + aDataset->CloseOnDisk(); + //} + } + } + aSubSubGroup->CloseOnDisk(); + } + // close submesh HDF group + aSubGroup->CloseOnDisk(); + } + } + // close container of submeshes by type HDF group + aGroup->CloseOnDisk(); + } + } + // All sub-meshes will be stored in MED file + // .. will NOT (PAL 12992) + //if ( shapeRefFound ) + //myWriter.AddAllSubMeshes(); + + // store submesh order if any + const TListOfListOfInt& theOrderIds = myLocMesh.GetMeshOrder(); + if ( theOrderIds.size() ) { + char order_list[ 30 ]; + strcpy( order_list, "Mesh Order" ); + // count number of submesh ids + int nbIDs = 0; + TListOfListOfInt::const_iterator idIt = theOrderIds.begin(); + for ( ; idIt != theOrderIds.end(); idIt++ ) + nbIDs += (*idIt).size(); + // number of values = number of IDs + + // number of lists (for separators) - 1 + int* smIDs = new int [ nbIDs + theOrderIds.size() - 1 ]; + idIt = theOrderIds.begin(); + for ( int i = 0; idIt != theOrderIds.end(); idIt++ ) { + const TListOfInt& idList = *idIt; + if (idIt != theOrderIds.begin()) // not first list + smIDs[ i++ ] = -1/* *idList.size()*/; // separator between lists + // dump submesh ids from current list + TListOfInt::const_iterator id_smId = idList.begin(); + for( ; id_smId != idList.end(); id_smId++ ) + smIDs[ i++ ] = *id_smId; + } + // write HDF group + aSize[ 0 ] = nbIDs + theOrderIds.size() - 1; + + aDataset = new HDFdataset( order_list, aTopGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( smIDs ); + aDataset->CloseOnDisk(); + // + delete[] smIDs; + } + + // groups root sub-branch + SALOMEDS::SObject_var myGroupsBranch; + for ( int i = GetNodeGroupsTag(); i <= GetBallElementsGroupsTag(); i++ ) { + found = gotBranch->FindSubObject( i, myGroupsBranch ); + if ( found ) { + char name_group[ 30 ]; + if ( i == GetNodeGroupsTag() ) + strcpy( name_group, "Groups of Nodes" ); + else if ( i == GetEdgeGroupsTag() ) + strcpy( name_group, "Groups of Edges" ); + else if ( i == GetFaceGroupsTag() ) + strcpy( name_group, "Groups of Faces" ); + else if ( i == GetVolumeGroupsTag() ) + strcpy( name_group, "Groups of Volumes" ); + else if ( i == Get0DElementsGroupsTag() ) + strcpy( name_group, "Groups of 0D Elements" ); + else if ( i == GetBallElementsGroupsTag() ) + strcpy( name_group, "Groups of Balls" ); + + aGroup = new HDFgroup( name_group, aTopGroup ); + aGroup->CreateOnDisk(); + + SALOMEDS::ChildIterator_var it = myCurrentStudy->NewChildIterator( myGroupsBranch ); + for ( ; it->More(); it->Next() ) { + SALOMEDS::SObject_var mySObject = it->Value(); + CORBA::Object_var aSubObject = SObjectToObject( mySObject ); + if ( !CORBA::is_nil( aSubObject ) ) { + SMESH_GroupBase_i* myGroupImpl = + dynamic_cast( GetServant( aSubObject ).in() ); + if ( !myGroupImpl ) + continue; + SMESHDS_GroupBase* aGrpBaseDS = myGroupImpl->GetGroupDS(); + if ( !aGrpBaseDS ) + continue; + + CORBA::String_var objStr = GetORB()->object_to_string( aSubObject ); + int anId = myStudyContext->findId( string( objStr.in() ) ); + + // For each group, create a dataset named "Group " + // and store the group's user name into it + const char* grpName = aGrpBaseDS->GetStoreName(); + char* aUserName = myGroupImpl->GetName(); + aSize[ 0 ] = strlen( aUserName ) + 1; + + aDataset = new HDFdataset( grpName, aGroup, HDF_STRING, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( aUserName ); + aDataset->CloseOnDisk(); + + // ouv : NPAL12872 + // For each group, create a dataset named "Group Color" + // and store the group's color into it + char grpColorName[ 30 ]; + sprintf( grpColorName, "ColorGroup %d", anId ); + SALOMEDS::Color aColor = myGroupImpl->GetColor(); + double anRGB[3]; + anRGB[ 0 ] = aColor.R; + anRGB[ 1 ] = aColor.G; + anRGB[ 2 ] = aColor.B; + aSize[ 0 ] = 3; + aDataset = new HDFdataset( grpColorName, aGroup, HDF_FLOAT64, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( anRGB ); + aDataset->CloseOnDisk(); + + // Pass SMESHDS_Group to MED writer + SMESHDS_Group* aGrpDS = dynamic_cast( aGrpBaseDS ); + if ( aGrpDS ) + myWriter.AddGroup( aGrpDS ); + + // write reference on a shape if exists + SMESHDS_GroupOnGeom* aGeomGrp = + dynamic_cast( aGrpBaseDS ); + if ( aGeomGrp ) { + SALOMEDS::SObject_var mySubRef, myShape; + if (mySObject->FindSubObject( GetRefOnShapeTag(), mySubRef ) && + mySubRef->ReferencedObject( myShape ) && + !CORBA::is_nil( myShape->GetObject() )) + { + string myRefOnObject = myShape->GetID(); + if ( myRefOnObject.length() > 0 ) { + char aRefName[ 30 ]; + sprintf( aRefName, "Ref on shape %d", anId); + aSize[ 0 ] = myRefOnObject.length() + 1; + aDataset = new HDFdataset(aRefName, aGroup, HDF_STRING, aSize, 1); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( ( char* )( myRefOnObject.c_str() ) ); + aDataset->CloseOnDisk(); + } + } + else // shape ref is invalid: + { + // save a group on geometry as ordinary group + myWriter.AddGroup( aGeomGrp ); + } + } + else if ( SMESH_GroupOnFilter_i* aFilterGrp_i = + dynamic_cast( myGroupImpl )) + { + std::string str = aFilterGrp_i->FilterToString(); + std::string hdfGrpName = "Filter " + SMESH_Comment(anId); + aSize[ 0 ] = str.length() + 1; + aDataset = new HDFdataset( hdfGrpName.c_str(), aGroup, HDF_STRING, aSize, 1); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( ( char* )( str.c_str() ) ); + aDataset->CloseOnDisk(); + } + } + } + aGroup->CloseOnDisk(); + } + } // loop on groups + + if ( strcmp( strHasData.c_str(), "1" ) == 0 ) + { + // Flush current mesh information into MED file + myWriter.Perform(); + + // save info on nb of elements + SMESH_PreMeshInfo::SaveToFile( myImpl, id, aFile ); + + // maybe a shape was deleted in the study + if ( !shapeRefFound && !mySMESHDSMesh->ShapeToMesh().IsNull() && hasShape) { + TopoDS_Shape nullShape; + myLocMesh.ShapeToMesh( nullShape ); // remove shape referring data + } + + if ( !mySMESHDSMesh->SubMeshes().empty() ) + { + // Store submeshes + // ---------------- + aGroup = new HDFgroup( "Submeshes", aTopGroup ); + aGroup->CreateOnDisk(); + + // each element belongs to one or none submesh, + // so for each node/element, we store a submesh ID + + // Make maps of submesh IDs of elements sorted by element IDs + typedef int TElemID; + typedef int TSubMID; + map< TElemID, TSubMID > eId2smId, nId2smId; + map< TElemID, TSubMID >::iterator hint; // insertion to map is done before hint + const map& aSubMeshes = mySMESHDSMesh->SubMeshes(); + map::const_iterator itSubM ( aSubMeshes.begin() ); + SMDS_NodeIteratorPtr itNode; + SMDS_ElemIteratorPtr itElem; + for ( itSubM = aSubMeshes.begin(); itSubM != aSubMeshes.end() ; itSubM++ ) + { + TSubMID aSubMeID = itSubM->first; + SMESHDS_SubMesh* aSubMesh = itSubM->second; + if ( aSubMesh->IsComplexSubmesh() ) + continue; // submesh containing other submeshs + // nodes + hint = nId2smId.begin(); // optimize insertion basing on increasing order of elem Ids in submesh + for ( itNode = aSubMesh->GetNodes(); itNode->more(); ++hint) + hint = nId2smId.insert( hint, make_pair( itNode->next()->GetID(), aSubMeID )); + // elements + hint = eId2smId.begin(); + for ( itElem = aSubMesh->GetElements(); itElem->more(); ++hint) + hint = eId2smId.insert( hint, make_pair( itElem->next()->GetID(), aSubMeID )); + } + + // Care of elements that are not on submeshes + if ( mySMESHDSMesh->NbNodes() != nId2smId.size() ) { + for ( itNode = mySMESHDSMesh->nodesIterator(); itNode->more(); ) + /* --- stl_map.h says : */ + /* A %map relies on unique keys and thus a %pair is only inserted if its */ + /* first element (the key) is not already present in the %map. */ + nId2smId.insert( make_pair( itNode->next()->GetID(), 0 )); + } + int nbElems = mySMESHDSMesh->NbEdges() + mySMESHDSMesh->NbFaces() + mySMESHDSMesh->NbVolumes(); + if ( nbElems != eId2smId.size() ) { + for ( itElem = mySMESHDSMesh->elementsIterator(); itElem->more(); ) + eId2smId.insert( make_pair( itElem->next()->GetID(), 0 )); + } + + // Store submesh IDs + for ( int isNode = 0; isNode < 2; ++isNode ) + { + map< TElemID, TSubMID >& id2smId = isNode ? nId2smId : eId2smId; + if ( id2smId.empty() ) continue; + map< TElemID, TSubMID >::const_iterator id_smId = id2smId.begin(); + // make and fill array of submesh IDs + int* smIDs = new int [ id2smId.size() ]; + for ( int i = 0; id_smId != id2smId.end(); ++id_smId, ++i ) + smIDs[ i ] = id_smId->second; + // write HDF group + aSize[ 0 ] = id2smId.size(); + string aDSName( isNode ? "Node Submeshes" : "Element Submeshes"); + aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( smIDs ); + aDataset->CloseOnDisk(); + // + delete[] smIDs; + } + + aGroup->CloseOnDisk(); + + // Store node positions on sub-shapes (SMDS_Position): + // ---------------------------------------------------- + + aGroup = new HDFgroup( "Node Positions", aTopGroup ); + aGroup->CreateOnDisk(); + + // in aGroup, create 5 datasets to contain: + // "Nodes on Edges" - ID of node on edge + // "Edge positions" - U parameter on node on edge + // "Nodes on Faces" - ID of node on face + // "Face U positions" - U parameter of node on face + // "Face V positions" - V parameter of node on face + + // Find out nb of nodes on edges and faces + // Collect corresponing sub-meshes + int nbEdgeNodes = 0, nbFaceNodes = 0; + list aEdgeSM, aFaceSM; + // loop on SMESHDS_SubMesh'es + for ( itSubM = aSubMeshes.begin(); itSubM != aSubMeshes.end() ; itSubM++ ) + { + SMESHDS_SubMesh* aSubMesh = (*itSubM).second; + if ( aSubMesh->IsComplexSubmesh() ) + continue; // submesh containing other submeshs + int nbNodes = aSubMesh->NbNodes(); + if ( nbNodes == 0 ) continue; + + int aShapeID = (*itSubM).first; + if ( aShapeID < 1 || aShapeID > mySMESHDSMesh->MaxShapeIndex() ) + continue; + int aShapeType = mySMESHDSMesh->IndexToShape( aShapeID ).ShapeType(); + // write only SMDS_FacePosition and SMDS_EdgePosition + switch ( aShapeType ) { + case TopAbs_FACE: + nbFaceNodes += nbNodes; + aFaceSM.push_back( aSubMesh ); + break; + case TopAbs_EDGE: + nbEdgeNodes += nbNodes; + aEdgeSM.push_back( aSubMesh ); + break; + default: + continue; + } + } + // Treat positions on edges or faces + for ( int onFace = 0; onFace < 2; onFace++ ) + { + // Create arrays to store in datasets + int iNode = 0, nbNodes = ( onFace ? nbFaceNodes : nbEdgeNodes ); + if (!nbNodes) continue; + int* aNodeIDs = new int [ nbNodes ]; + double* aUPos = new double [ nbNodes ]; + double* aVPos = ( onFace ? new double[ nbNodes ] : 0 ); + + // Fill arrays + // loop on sub-meshes + list * pListSM = ( onFace ? &aFaceSM : &aEdgeSM ); + list::iterator itSM = pListSM->begin(); + for ( ; itSM != pListSM->end(); itSM++ ) + { + SMESHDS_SubMesh* aSubMesh = (*itSM); + + SMDS_NodeIteratorPtr itNode = aSubMesh->GetNodes(); + // loop on nodes in aSubMesh + while ( itNode->more() ) + { + //node ID + const SMDS_MeshNode* node = itNode->next(); + aNodeIDs [ iNode ] = node->GetID(); + + // Position + const SMDS_PositionPtr pos = node->GetPosition(); + if ( onFace ) { // on FACE + const SMDS_FacePosition* fPos = + dynamic_cast( pos ); + if ( fPos ) { + aUPos[ iNode ] = fPos->GetUParameter(); + aVPos[ iNode ] = fPos->GetVParameter(); + iNode++; + } + else + nbNodes--; + } + else { // on EDGE + const SMDS_EdgePosition* ePos = + dynamic_cast( pos ); + if ( ePos ) { + aUPos[ iNode ] = ePos->GetUParameter(); + iNode++; + } + else + nbNodes--; + } + } // loop on nodes in aSubMesh + } // loop on sub-meshes - // write applied algorithms if exist - SALOMEDS::SObject_var mySubAlgoBranch; - found = mySObject->FindSubObject( GetRefOnAppliedAlgorithmsTag(), mySubAlgoBranch ); - if ( found ) { - aSubSubGroup = new HDFgroup( "Applied Algorithms", aSubGroup ); - aSubSubGroup->CreateOnDisk(); + // Write datasets + if ( nbNodes ) + { + aSize[ 0 ] = nbNodes; + // IDS + string aDSName( onFace ? "Nodes on Faces" : "Nodes on Edges"); + aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_INT32, aSize, 1 ); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( aNodeIDs ); + aDataset->CloseOnDisk(); - SALOMEDS::ChildIterator_var it = myCurrentStudy->NewChildIterator( mySubAlgoBranch ); - int algoNb = 0; - for ( ; it->More(); it->Next() ) { - SALOMEDS::SObject_var mySubSObject = it->Value(); - SALOMEDS::SObject_var myRefOnAlgo; - bool ok = mySubSObject->ReferencedObject( myRefOnAlgo ); - if ( ok ) { - //string myRefOnObject = myRefOnAlgo->GetID(); - CORBA::Object_var anObject = SObjectToObject( myRefOnAlgo ); - CORBA::String_var objStr = GetORB()->object_to_string( anObject ); - int id = myStudyContext->findId( string( objStr.in() ) ); - //if ( myRefOnObject.length() > 0 ) { - //aSize[ 0 ] = myRefOnObject.length() + 1; - char algoName[ 30 ], algoId[ 30 ]; - sprintf( algoName, "Algo %d", ++algoNb ); - sprintf( algoId, "%d", id ); - aSize[ 0 ] = strlen( algoId ) + 1; - aDataset = new HDFdataset( algoName, aSubSubGroup, HDF_STRING, aSize, 1 ); - aDataset->CreateOnDisk(); - //aDataset->WriteOnDisk( ( char* )( myRefOnObject.c_str() ) ); - aDataset->WriteOnDisk( algoId ); - aDataset->CloseOnDisk(); - //} - } - } - aSubSubGroup->CloseOnDisk(); + // U Positions + aDSName = ( onFace ? "Face U positions" : "Edge positions"); + aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_FLOAT64, aSize, 1); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( aUPos ); + aDataset->CloseOnDisk(); + // V Positions + if ( onFace ) { + aDataset = new HDFdataset( "Face V positions", aGroup, HDF_FLOAT64, aSize, 1); + aDataset->CreateOnDisk(); + aDataset->WriteOnDisk( aVPos ); + aDataset->CloseOnDisk(); } - // close submesh HDF group - aSubGroup->CloseOnDisk(); } - } - // close container of submeshes by type HDF group + delete [] aNodeIDs; + delete [] aUPos; + if ( aVPos ) delete [] aVPos; + + } // treat positions on edges or faces + + // close "Node Positions" group aGroup->CloseOnDisk(); - } - } - // All sub-meshes will be stored in MED file - // .. will NOT (PAL 12992) - //if ( shapeRefFound ) - //myWriter.AddAllSubMeshes(); - // groups root sub-branch - SALOMEDS::SObject_var myGroupsBranch; - for ( int i = GetNodeGroupsTag(); i <= GetVolumeGroupsTag(); i++ ) { - found = gotBranch->FindSubObject( i, myGroupsBranch ); - if ( found ) { - char name_group[ 30 ]; - if ( i == GetNodeGroupsTag() ) - strcpy( name_group, "Groups of Nodes" ); - else if ( i == GetEdgeGroupsTag() ) - strcpy( name_group, "Groups of Edges" ); - else if ( i == GetFaceGroupsTag() ) - strcpy( name_group, "Groups of Faces" ); - else if ( i == GetVolumeGroupsTag() ) - strcpy( name_group, "Groups of Volumes" ); - - aGroup = new HDFgroup( name_group, aTopGroup ); - aGroup->CreateOnDisk(); - - SALOMEDS::ChildIterator_var it = myCurrentStudy->NewChildIterator( myGroupsBranch ); - for ( ; it->More(); it->Next() ) { - SALOMEDS::SObject_var mySObject = it->Value(); - CORBA::Object_var aSubObject = SObjectToObject( mySObject ); - if ( !CORBA::is_nil( aSubObject ) ) { - SMESH_GroupBase_i* myGroupImpl = - dynamic_cast( GetServant( aSubObject ).in() ); - if ( !myGroupImpl ) - continue; - - CORBA::String_var objStr = GetORB()->object_to_string( aSubObject ); - int anId = myStudyContext->findId( string( objStr.in() ) ); - - // For each group, create a dataset named "Group " - // and store the group's user name into it - char grpName[ 30 ]; - sprintf( grpName, "Group %d", anId ); - char* aUserName = myGroupImpl->GetName(); - aSize[ 0 ] = strlen( aUserName ) + 1; - - aDataset = new HDFdataset( grpName, aGroup, HDF_STRING, aSize, 1 ); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( aUserName ); - aDataset->CloseOnDisk(); - - // ouv : NPAL12872 - // For each group, create a dataset named "Group Color" - // and store the group's color into it - char grpColorName[ 30 ]; - sprintf( grpColorName, "ColorGroup %d", anId ); - SALOMEDS::Color aColor = myGroupImpl->GetColor(); - double anRGB[3]; - anRGB[ 0 ] = aColor.R; - anRGB[ 1 ] = aColor.G; - anRGB[ 2 ] = aColor.B; - aSize[ 0 ] = 3; - aDataset = new HDFdataset( grpColorName, aGroup, HDF_FLOAT64, aSize, 1 ); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( anRGB ); - aDataset->CloseOnDisk(); - - // Store the group contents into MED file - if ( myLocMesh.GetGroup( myGroupImpl->GetLocalID() ) ) { - - if(MYDEBUG) MESSAGE( "VSR - SMESH_Gen_i::Save(): saving group with StoreName = " - << grpName << " to MED file" ); - SMESHDS_GroupBase* aGrpBaseDS = - myLocMesh.GetGroup( myGroupImpl->GetLocalID() )->GetGroupDS(); - aGrpBaseDS->SetStoreName( grpName ); - - // Pass SMESHDS_Group to MED writer - SMESHDS_Group* aGrpDS = dynamic_cast( aGrpBaseDS ); - if ( aGrpDS ) - myWriter.AddGroup( aGrpDS ); - - // write reference on a shape if exists - SMESHDS_GroupOnGeom* aGeomGrp = - dynamic_cast( aGrpBaseDS ); - if ( aGeomGrp ) { - SALOMEDS::SObject_var mySubRef, myShape; - if (mySObject->FindSubObject( GetRefOnShapeTag(), mySubRef ) && - mySubRef->ReferencedObject( myShape ) && - !CORBA::is_nil( myShape->GetObject() )) - { - string myRefOnObject = myShape->GetID(); - if ( myRefOnObject.length() > 0 ) { - char aRefName[ 30 ]; - sprintf( aRefName, "Ref on shape %d", anId); - aSize[ 0 ] = myRefOnObject.length() + 1; - aDataset = new HDFdataset(aRefName, aGroup, HDF_STRING, aSize, 1); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( ( char* )( myRefOnObject.c_str() ) ); - aDataset->CloseOnDisk(); - } - } - else // shape ref is invalid: - { - // save a group on geometry as ordinary group - myWriter.AddGroup( aGeomGrp ); - } - } - } - } - } - aGroup->CloseOnDisk(); - } - } // loop on groups - - if ( strcmp( strHasData.c_str(), "1" ) == 0 ) - { - // Flush current mesh information into MED file - myWriter.Perform(); - - // maybe a shape was deleted in the study - if ( !shapeRefFound && !mySMESHDSMesh->ShapeToMesh().IsNull() && hasShape) { - TopoDS_Shape nullShape; - myLocMesh.ShapeToMesh( nullShape ); // remove shape referring data - } - - if ( !mySMESHDSMesh->SubMeshes().empty() ) - { - // Store submeshes - // ---------------- - aGroup = new HDFgroup( "Submeshes", aTopGroup ); - aGroup->CreateOnDisk(); - - // each element belongs to one or none submesh, - // so for each node/element, we store a submesh ID - - // Make maps of submesh IDs of elements sorted by element IDs - typedef int TElemID; - typedef int TSubMID; - map< TElemID, TSubMID > eId2smId, nId2smId; - map< TElemID, TSubMID >::iterator hint; // insertion to map is done before hint - const map& aSubMeshes = mySMESHDSMesh->SubMeshes(); - map::const_iterator itSubM ( aSubMeshes.begin() ); - SMDS_NodeIteratorPtr itNode; - SMDS_ElemIteratorPtr itElem; - for ( itSubM = aSubMeshes.begin(); itSubM != aSubMeshes.end() ; itSubM++ ) - { - TSubMID aSubMeID = itSubM->first; - SMESHDS_SubMesh* aSubMesh = itSubM->second; - if ( aSubMesh->IsComplexSubmesh() ) - continue; // submesh containing other submeshs - // nodes - hint = nId2smId.begin(); // optimize insertion basing on increasing order of elem Ids in submesh - for ( itNode = aSubMesh->GetNodes(); itNode->more(); ++hint) - hint = nId2smId.insert( hint, make_pair( itNode->next()->GetID(), aSubMeID )); - // elements - hint = eId2smId.begin(); - for ( itElem = aSubMesh->GetElements(); itElem->more(); ++hint) - hint = eId2smId.insert( hint, make_pair( itElem->next()->GetID(), aSubMeID )); - } - - // Care of elements that are not on submeshes - if ( mySMESHDSMesh->NbNodes() != nId2smId.size() ) { - for ( itNode = mySMESHDSMesh->nodesIterator(); itNode->more(); ) - /* --- stl_map.h says : */ - /* A %map relies on unique keys and thus a %pair is only inserted if its */ - /* first element (the key) is not already present in the %map. */ - nId2smId.insert( make_pair( itNode->next()->GetID(), 0 )); - } - int nbElems = mySMESHDSMesh->NbEdges() + mySMESHDSMesh->NbFaces() + mySMESHDSMesh->NbVolumes(); - if ( nbElems != eId2smId.size() ) { - for ( itElem = mySMESHDSMesh->elementsIterator(); itElem->more(); ) - eId2smId.insert( make_pair( itElem->next()->GetID(), 0 )); - } - - // Store submesh IDs - for ( int isNode = 0; isNode < 2; ++isNode ) - { - map< TElemID, TSubMID >& id2smId = isNode ? nId2smId : eId2smId; - if ( id2smId.empty() ) continue; - map< TElemID, TSubMID >::const_iterator id_smId = id2smId.begin(); - // make and fill array of submesh IDs - int* smIDs = new int [ id2smId.size() ]; - for ( int i = 0; id_smId != id2smId.end(); ++id_smId, ++i ) - smIDs[ i ] = id_smId->second; - // write HDF group - aSize[ 0 ] = id2smId.size(); - string aDSName( isNode ? "Node Submeshes" : "Element Submeshes"); - aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_INT32, aSize, 1 ); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( smIDs ); - aDataset->CloseOnDisk(); - // - delete smIDs; - } - - // Store node positions on sub-shapes (SMDS_Position): - // ---------------------------------------------------- - - aGroup = new HDFgroup( "Node Positions", aTopGroup ); - aGroup->CreateOnDisk(); - - // in aGroup, create 5 datasets to contain: - // "Nodes on Edges" - ID of node on edge - // "Edge positions" - U parameter on node on edge - // "Nodes on Faces" - ID of node on face - // "Face U positions" - U parameter of node on face - // "Face V positions" - V parameter of node on face - - // Find out nb of nodes on edges and faces - // Collect corresponing sub-meshes - int nbEdgeNodes = 0, nbFaceNodes = 0; - list aEdgeSM, aFaceSM; - // loop on SMESHDS_SubMesh'es - for ( itSubM = aSubMeshes.begin(); itSubM != aSubMeshes.end() ; itSubM++ ) - { - SMESHDS_SubMesh* aSubMesh = (*itSubM).second; - if ( aSubMesh->IsComplexSubmesh() ) - continue; // submesh containing other submeshs - int nbNodes = aSubMesh->NbNodes(); - if ( nbNodes == 0 ) continue; - - int aShapeID = (*itSubM).first; - int aShapeType = mySMESHDSMesh->IndexToShape( aShapeID ).ShapeType(); - // write only SMDS_FacePosition and SMDS_EdgePosition - switch ( aShapeType ) { - case TopAbs_FACE: - nbFaceNodes += nbNodes; - aFaceSM.push_back( aSubMesh ); - break; - case TopAbs_EDGE: - nbEdgeNodes += nbNodes; - aEdgeSM.push_back( aSubMesh ); - break; - default: - continue; - } - } - // Treat positions on edges or faces - for ( int onFace = 0; onFace < 2; onFace++ ) - { - // Create arrays to store in datasets - int iNode = 0, nbNodes = ( onFace ? nbFaceNodes : nbEdgeNodes ); - if (!nbNodes) continue; - int* aNodeIDs = new int [ nbNodes ]; - double* aUPos = new double [ nbNodes ]; - double* aVPos = ( onFace ? new double[ nbNodes ] : 0 ); - - // Fill arrays - // loop on sub-meshes - list * pListSM = ( onFace ? &aFaceSM : &aEdgeSM ); - list::iterator itSM = pListSM->begin(); - for ( ; itSM != pListSM->end(); itSM++ ) - { - SMESHDS_SubMesh* aSubMesh = (*itSM); - - SMDS_NodeIteratorPtr itNode = aSubMesh->GetNodes(); - // loop on nodes in aSubMesh - while ( itNode->more() ) - { - //node ID - const SMDS_MeshNode* node = itNode->next(); - aNodeIDs [ iNode ] = node->GetID(); - - // Position - const SMDS_PositionPtr pos = node->GetPosition(); - if ( onFace ) { // on FACE - const SMDS_FacePosition* fPos = - dynamic_cast( pos.get() ); - if ( fPos ) { - aUPos[ iNode ] = fPos->GetUParameter(); - aVPos[ iNode ] = fPos->GetVParameter(); - iNode++; - } - else - nbNodes--; - } - else { // on EDGE - const SMDS_EdgePosition* ePos = - dynamic_cast( pos.get() ); - if ( ePos ) { - aUPos[ iNode ] = ePos->GetUParameter(); - iNode++; - } - else - nbNodes--; - } - } // loop on nodes in aSubMesh - } // loop on sub-meshes - - // Write datasets - if ( nbNodes ) - { - aSize[ 0 ] = nbNodes; - // IDS - string aDSName( onFace ? "Nodes on Faces" : "Nodes on Edges"); - aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_INT32, aSize, 1 ); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( aNodeIDs ); - aDataset->CloseOnDisk(); - - // U Positions - aDSName = ( onFace ? "Face U positions" : "Edge positions"); - aDataset = new HDFdataset( (char*)aDSName.c_str(), aGroup, HDF_FLOAT64, aSize, 1); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( aUPos ); - aDataset->CloseOnDisk(); - // V Positions - if ( onFace ) { - aDataset = new HDFdataset( "Face V positions", aGroup, HDF_FLOAT64, aSize, 1); - aDataset->CreateOnDisk(); - aDataset->WriteOnDisk( aVPos ); - aDataset->CloseOnDisk(); - } - } - delete [] aNodeIDs; - delete [] aUPos; - if ( aVPos ) delete [] aVPos; - - } // treat positions on edges or faces - - // close "Node Positions" group - aGroup->CloseOnDisk(); - - } // if ( there are submeshes in SMESHDS_Mesh ) - } // if ( hasData ) - - // close mesh HDF group - aTopGroup->CloseOnDisk(); - } - } + } // if ( there are submeshes in SMESHDS_Mesh ) + } // if ( hasData ) + + // close mesh HDF group + aTopGroup->CloseOnDisk(); + } + } } } } - + // close HDF file aFile->CloseOnDisk(); delete aFile; @@ -2947,7 +3742,7 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, aStreamFile = SALOMEDS_Tool::PutFilesToStream( tmpDir.ToCString(), aFileSeq.in(), isMultiFile ); // Remove temporary files and directory - if ( !isMultiFile ) + if ( !isMultiFile ) SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.ToCString(), aFileSeq.in(), true ); INFOS( "SMESH_Gen_i::Save() completed" ); @@ -2963,8 +3758,8 @@ SALOMEDS::TMPFile* SMESH_Gen_i::Save( SALOMEDS::SComponent_ptr theComponent, //============================================================================= SALOMEDS::TMPFile* SMESH_Gen_i::SaveASCII( SALOMEDS::SComponent_ptr theComponent, - const char* theURL, - bool isMultiFile ) { + const char* theURL, + bool isMultiFile ) { if(MYDEBUG) MESSAGE( "SMESH_Gen_i::SaveASCII" ); SALOMEDS::TMPFile_var aStreamFile = Save( theComponent, theURL, isMultiFile ); return aStreamFile._retn(); @@ -2974,12 +3769,12 @@ SALOMEDS::TMPFile* SMESH_Gen_i::SaveASCII( SALOMEDS::SComponent_ptr theComponent int size = aStreamFile.in().length(); _CORBA_Octet* buffer = new _CORBA_Octet[size*3+1]; for ( int i = 0; i < size; i++ ) - sprintf( (char*)&(buffer[i*3]), "|%02x", (char*)(aStreamFile[i]) ); + sprintf( (char*)&(buffer[i*3]), "|%02x", aStreamFile[i] ); buffer[size * 3] = '\0'; SALOMEDS::TMPFile_var anAsciiStreamFile = new SALOMEDS::TMPFile(size*3, size*3, buffer, 1); - + return anAsciiStreamFile._retn(); } @@ -3000,34 +3795,9 @@ void SMESH_Gen_i::loadGeomData( SALOMEDS::SComponent_ptr theCompRoot ) if ( aStudy->_is_nil() ) return; - SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); + SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); aStudyBuilder->LoadWith( theCompRoot, GetGeomEngine() ); } -//============================================================================= -/*! - * \brief Creates SMDS_Position according to shape type - */ -//============================================================================= - -class PositionCreator { -public: - SMDS_PositionPtr MakePosition(const TopAbs_ShapeEnum type) { - return (this->*myFuncTable[ type ])(); - } - PositionCreator() { - myFuncTable.resize( (size_t) TopAbs_SHAPE, & PositionCreator::defaultPosition ); - myFuncTable[ TopAbs_FACE ] = & PositionCreator::facePosition; - myFuncTable[ TopAbs_EDGE ] = & PositionCreator::edgePosition; - myFuncTable[ TopAbs_VERTEX ] = & PositionCreator::vertexPosition; - } -private: - SMDS_PositionPtr edgePosition() const { return SMDS_PositionPtr( new SMDS_EdgePosition ); } - SMDS_PositionPtr facePosition() const { return SMDS_PositionPtr( new SMDS_FacePosition ); } - SMDS_PositionPtr vertexPosition() const { return SMDS_PositionPtr( new SMDS_VertexPosition); } - SMDS_PositionPtr defaultPosition() const { return SMDS_SpacePosition::originSpacePosition(); } - typedef SMDS_PositionPtr (PositionCreator:: * FmakePos)() const; - vector myFuncTable; -}; //============================================================================= /*! @@ -3038,13 +3808,13 @@ private: //============================================================================= bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, - const SALOMEDS::TMPFile& theStream, - const char* theURL, - bool isMultiFile ) + const SALOMEDS::TMPFile& theStream, + const char* theURL, + bool isMultiFile ) { INFOS( "SMESH_Gen_i::Load" ); - if ( myCurrentStudy->_is_nil() || + if ( myCurrentStudy->_is_nil() || theComponent->GetStudy()->StudyId() != myCurrentStudy->StudyId() ) SetCurrentStudy( theComponent->GetStudy() ); @@ -3060,7 +3830,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, // Get temporary files location TCollection_AsciiString tmpDir = isMultiFile ? TCollection_AsciiString( ( char* )theURL ) : ( char* )SALOMEDS_Tool::GetTmpDir().c_str(); - + INFOS( "THE URL++++++++++++++" ) INFOS( theURL ); INFOS( "THE TMP PATH+++++++++" ); @@ -3069,9 +3839,9 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, // Convert the stream into sequence of files to process SALOMEDS::ListOfFileNames_var aFileSeq = SALOMEDS_Tool::PutStreamToFiles( theStream, tmpDir.ToCString(), - isMultiFile ); + isMultiFile ); TCollection_AsciiString aStudyName( "" ); - if ( isMultiFile ) + if ( isMultiFile ) aStudyName = ( (char*)SALOMEDS_Tool::GetNameFromPath( myCurrentStudy->URL() ).c_str() ); // Set names of temporary files @@ -3097,8 +3867,10 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, return false; } - DriverMED_R_SMESHDS_Mesh myReader; - myReader.SetFile( meshfile.ToCString() ); + TPythonDump pd; // prevent dump during loading + + // DriverMED_R_SMESHDS_Mesh myReader; + // myReader.SetFile( meshfile.ToCString() ); // For PAL13473 ("Repetitive mesh") implementation. // New dependencies between SMESH objects are established: @@ -3114,99 +3886,99 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, list< pair< SMESH_Mesh_i*, HDFgroup* > > meshGroupList; // get total number of top-level groups - int aNbGroups = aFile->nInternalObjects(); + int aNbGroups = aFile->nInternalObjects(); if ( aNbGroups > 0 ) { // --> in first turn we should read&create hypotheses if ( aFile->ExistInternalObject( "Hypotheses" ) ) { // open hypotheses root HDF group - aTopGroup = new HDFgroup( "Hypotheses", aFile ); + aTopGroup = new HDFgroup( "Hypotheses", aFile ); aTopGroup->OpenOnDisk(); // get number of hypotheses - int aNbObjects = aTopGroup->nInternalObjects(); + int aNbObjects = aTopGroup->nInternalObjects(); for ( int j = 0; j < aNbObjects; j++ ) { - // try to identify hypothesis - char hypGrpName[ HDF_NAME_MAX_LEN+1 ]; + // try to identify hypothesis + char hypGrpName[ HDF_NAME_MAX_LEN+1 ]; aTopGroup->InternalObjectIndentify( j, hypGrpName ); - if ( string( hypGrpName ).substr( 0, 10 ) == string( "Hypothesis" ) ) { - // open hypothesis group - aGroup = new HDFgroup( hypGrpName, aTopGroup ); - aGroup->OpenOnDisk(); - - // --> get hypothesis id - int id = atoi( string( hypGrpName ).substr( 10 ).c_str() ); - string hypname; - string libname; - string hypdata; - - // get number of datasets - int aNbSubObjects = aGroup->nInternalObjects(); - for ( int k = 0; k < aNbSubObjects; k++ ) { - // identify dataset - char name_of_subgroup[ HDF_NAME_MAX_LEN+1 ]; - aGroup->InternalObjectIndentify( k, name_of_subgroup ); - // --> get hypothesis name - if ( strcmp( name_of_subgroup, "Name" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* hypname_str = new char[ size ]; - aDataset->ReadFromDisk( hypname_str ); - hypname = string( hypname_str ); - delete [] hypname_str; - aDataset->CloseOnDisk(); - } - // --> get hypothesis plugin library name - if ( strcmp( name_of_subgroup, "LibName" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* libname_str = new char[ size ]; - aDataset->ReadFromDisk( libname_str ); - if(MYDEBUG) SCRUTE( libname_str ); - libname = string( libname_str ); - delete [] libname_str; - aDataset->CloseOnDisk(); - } - // --> get hypothesis data - if ( strcmp( name_of_subgroup, "Data" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* hypdata_str = new char[ size ]; - aDataset->ReadFromDisk( hypdata_str ); - hypdata = string( hypdata_str ); - delete [] hypdata_str; - aDataset->CloseOnDisk(); - } - } - // close hypothesis HDF group - aGroup->CloseOnDisk(); - - // --> restore hypothesis from data - if ( id > 0 && !hypname.empty()/* && !hypdata.empty()*/ ) { // VSR : persistent data can be empty - if(MYDEBUG) MESSAGE("VSR - load hypothesis : id = " << id << + if ( string( hypGrpName ).substr( 0, 10 ) == string( "Hypothesis" ) ) { + // open hypothesis group + aGroup = new HDFgroup( hypGrpName, aTopGroup ); + aGroup->OpenOnDisk(); + + // --> get hypothesis id + int id = atoi( string( hypGrpName ).substr( 10 ).c_str() ); + string hypname; + string libname; + string hypdata; + + // get number of datasets + int aNbSubObjects = aGroup->nInternalObjects(); + for ( int k = 0; k < aNbSubObjects; k++ ) { + // identify dataset + char name_of_subgroup[ HDF_NAME_MAX_LEN+1 ]; + aGroup->InternalObjectIndentify( k, name_of_subgroup ); + // --> get hypothesis name + if ( strcmp( name_of_subgroup, "Name" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* hypname_str = new char[ size ]; + aDataset->ReadFromDisk( hypname_str ); + hypname = string( hypname_str ); + delete [] hypname_str; + aDataset->CloseOnDisk(); + } + // --> get hypothesis plugin library name + if ( strcmp( name_of_subgroup, "LibName" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* libname_str = new char[ size ]; + aDataset->ReadFromDisk( libname_str ); + if(MYDEBUG) SCRUTE( libname_str ); + libname = string( libname_str ); + delete [] libname_str; + aDataset->CloseOnDisk(); + } + // --> get hypothesis data + if ( strcmp( name_of_subgroup, "Data" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* hypdata_str = new char[ size ]; + aDataset->ReadFromDisk( hypdata_str ); + hypdata = string( hypdata_str ); + delete [] hypdata_str; + aDataset->CloseOnDisk(); + } + } + // close hypothesis HDF group + aGroup->CloseOnDisk(); + + // --> restore hypothesis from data + if ( id > 0 && !hypname.empty()/* && !hypdata.empty()*/ ) { // VSR : persistent data can be empty + if(MYDEBUG) MESSAGE("VSR - load hypothesis : id = " << id << ", name = " << hypname.c_str() << ", persistent string = " << hypdata.c_str()); SMESH::SMESH_Hypothesis_var myHyp; - try { // protect persistence mechanism against exceptions - myHyp = this->createHypothesis( hypname.c_str(), libname.c_str() ); - } - catch (...) { - INFOS( "Exception during hypothesis creation" ); - } + try { // protect persistence mechanism against exceptions + myHyp = this->createHypothesis( hypname.c_str(), libname.c_str() ); + } + catch (...) { + INFOS( "Exception during hypothesis creation" ); + } - SMESH_Hypothesis_i* myImpl = dynamic_cast( GetServant( myHyp ).in() ); - if ( myImpl ) { - // myImpl->LoadFrom( hypdata.c_str() ); + SMESH_Hypothesis_i* myImpl = dynamic_cast( GetServant( myHyp ).in() ); + if ( myImpl ) { + // myImpl->LoadFrom( hypdata.c_str() ); hypDataList.push_back( make_pair( myImpl, hypdata )); - string iorString = GetORB()->object_to_string( myHyp ); - int newId = myStudyContext->findId( iorString ); - myStudyContext->mapOldToNew( id, newId ); - } - else - if(MYDEBUG) MESSAGE( "VSR - SMESH_Gen::Load - can't get servant" ); + string iorString = GetORB()->object_to_string( myHyp ); + int newId = myStudyContext->findId( iorString ); + myStudyContext->mapOldToNew( id, newId ); + } + else + if(MYDEBUG) MESSAGE( "VSR - SMESH_Gen::Load - can't get servant" ); } } } @@ -3218,95 +3990,95 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, // --> then we should read&create algorithms if ( aFile->ExistInternalObject( "Algorithms" ) ) { // open algorithms root HDF group - aTopGroup = new HDFgroup( "Algorithms", aFile ); + aTopGroup = new HDFgroup( "Algorithms", aFile ); aTopGroup->OpenOnDisk(); // get number of algorithms - int aNbObjects = aTopGroup->nInternalObjects(); + int aNbObjects = aTopGroup->nInternalObjects(); for ( int j = 0; j < aNbObjects; j++ ) { - // try to identify algorithm - char hypGrpName[ HDF_NAME_MAX_LEN+1 ]; + // try to identify algorithm + char hypGrpName[ HDF_NAME_MAX_LEN+1 ]; aTopGroup->InternalObjectIndentify( j, hypGrpName ); - if ( string( hypGrpName ).substr( 0, 9 ) == string( "Algorithm" ) ) { - // open algorithm group - aGroup = new HDFgroup( hypGrpName, aTopGroup ); - aGroup->OpenOnDisk(); - - // --> get algorithm id - int id = atoi( string( hypGrpName ).substr( 9 ).c_str() ); - string hypname; - string libname; - string hypdata; - - // get number of datasets - int aNbSubObjects = aGroup->nInternalObjects(); - for ( int k = 0; k < aNbSubObjects; k++ ) { - // identify dataset - char name_of_subgroup[ HDF_NAME_MAX_LEN+1 ]; - aGroup->InternalObjectIndentify( k, name_of_subgroup ); - // --> get algorithm name - if ( strcmp( name_of_subgroup, "Name" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* hypname_str = new char[ size ]; - aDataset->ReadFromDisk( hypname_str ); - hypname = string( hypname_str ); - delete [] hypname_str; - aDataset->CloseOnDisk(); - } - // --> get algorithm plugin library name - if ( strcmp( name_of_subgroup, "LibName" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* libname_str = new char[ size ]; - aDataset->ReadFromDisk( libname_str ); - if(MYDEBUG) SCRUTE( libname_str ); - libname = string( libname_str ); - delete [] libname_str; - aDataset->CloseOnDisk(); - } - // --> get algorithm data - if ( strcmp( name_of_subgroup, "Data" ) == 0 ) { - aDataset = new HDFdataset( name_of_subgroup, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - char* hypdata_str = new char[ size ]; - aDataset->ReadFromDisk( hypdata_str ); - if(MYDEBUG) SCRUTE( hypdata_str ); - hypdata = string( hypdata_str ); - delete [] hypdata_str; - aDataset->CloseOnDisk(); - } - } - // close algorithm HDF group - aGroup->CloseOnDisk(); - - // --> restore algorithm from data - if ( id > 0 && !hypname.empty()/* && !hypdata.empty()*/ ) { // VSR : persistent data can be empty - if(MYDEBUG) MESSAGE("VSR - load algo : id = " << id << + if ( string( hypGrpName ).substr( 0, 9 ) == string( "Algorithm" ) ) { + // open algorithm group + aGroup = new HDFgroup( hypGrpName, aTopGroup ); + aGroup->OpenOnDisk(); + + // --> get algorithm id + int id = atoi( string( hypGrpName ).substr( 9 ).c_str() ); + string hypname; + string libname; + string hypdata; + + // get number of datasets + int aNbSubObjects = aGroup->nInternalObjects(); + for ( int k = 0; k < aNbSubObjects; k++ ) { + // identify dataset + char name_of_subgroup[ HDF_NAME_MAX_LEN+1 ]; + aGroup->InternalObjectIndentify( k, name_of_subgroup ); + // --> get algorithm name + if ( strcmp( name_of_subgroup, "Name" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* hypname_str = new char[ size ]; + aDataset->ReadFromDisk( hypname_str ); + hypname = string( hypname_str ); + delete [] hypname_str; + aDataset->CloseOnDisk(); + } + // --> get algorithm plugin library name + if ( strcmp( name_of_subgroup, "LibName" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* libname_str = new char[ size ]; + aDataset->ReadFromDisk( libname_str ); + if(MYDEBUG) SCRUTE( libname_str ); + libname = string( libname_str ); + delete [] libname_str; + aDataset->CloseOnDisk(); + } + // --> get algorithm data + if ( strcmp( name_of_subgroup, "Data" ) == 0 ) { + aDataset = new HDFdataset( name_of_subgroup, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* hypdata_str = new char[ size ]; + aDataset->ReadFromDisk( hypdata_str ); + if(MYDEBUG) SCRUTE( hypdata_str ); + hypdata = string( hypdata_str ); + delete [] hypdata_str; + aDataset->CloseOnDisk(); + } + } + // close algorithm HDF group + aGroup->CloseOnDisk(); + + // --> restore algorithm from data + if ( id > 0 && !hypname.empty()/* && !hypdata.empty()*/ ) { // VSR : persistent data can be empty + if(MYDEBUG) MESSAGE("VSR - load algo : id = " << id << ", name = " << hypname.c_str() << ", persistent string = " << hypdata.c_str()); SMESH::SMESH_Hypothesis_var myHyp; - try { // protect persistence mechanism against exceptions - myHyp = this->createHypothesis( hypname.c_str(), libname.c_str() ); - } - catch (...) { - INFOS( "Exception during hypothesis creation" ); - } + try { // protect persistence mechanism against exceptions + myHyp = this->createHypothesis( hypname.c_str(), libname.c_str() ); + } + catch (...) { + INFOS( "Exception during hypothesis creation" ); + } - SMESH_Hypothesis_i* myImpl = dynamic_cast( GetServant( myHyp ).in() ); - if ( myImpl ) { - //myImpl->LoadFrom( hypdata.c_str() ); + SMESH_Hypothesis_i* myImpl = dynamic_cast( GetServant( myHyp ).in() ); + if ( myImpl ) { + //myImpl->LoadFrom( hypdata.c_str() ); hypDataList.push_back( make_pair( myImpl, hypdata )); - string iorString = GetORB()->object_to_string( myHyp ); - int newId = myStudyContext->findId( iorString ); - myStudyContext->mapOldToNew( id, newId ); - } - else - if(MYDEBUG) MESSAGE( "VSR - SMESH_Gen::Load - can't get servant" ); + string iorString = GetORB()->object_to_string( myHyp ); + int newId = myStudyContext->findId( iorString ); + myStudyContext->mapOldToNew( id, newId ); + } + else + if(MYDEBUG) MESSAGE( "VSR - SMESH_Gen::Load - can't get servant" ); } } } @@ -3322,44 +4094,44 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aFile->InternalObjectIndentify( i, meshName ); if ( string( meshName ).substr( 0, 4 ) == string( "Mesh" ) ) { - // --> get mesh id - int id = atoi( string( meshName ).substr( 4 ).c_str() ); - if ( id <= 0 ) - continue; - - // open mesh HDF group - aTopGroup = new HDFgroup( meshName, aFile ); - aTopGroup->OpenOnDisk(); - - // get number of child HDF objects - int aNbObjects = aTopGroup->nInternalObjects(); - if ( aNbObjects > 0 ) { - // create mesh - if(MYDEBUG) MESSAGE( "VSR - load mesh : id = " << id ); - SMESH::SMESH_Mesh_var myNewMesh = this->createMesh(); - SMESH_Mesh_i* myNewMeshImpl = dynamic_cast( GetServant( myNewMesh ).in() ); + // --> get mesh id + int id = atoi( string( meshName ).substr( 4 ).c_str() ); + if ( id <= 0 ) + continue; + + // open mesh HDF group + aTopGroup = new HDFgroup( meshName, aFile ); + aTopGroup->OpenOnDisk(); + + // get number of child HDF objects + int aNbObjects = aTopGroup->nInternalObjects(); + if ( aNbObjects > 0 ) { + // create mesh + if(MYDEBUG) MESSAGE( "VSR - load mesh : id = " << id ); + SMESH::SMESH_Mesh_var myNewMesh = this->createMesh(); + SMESH_Mesh_i* myNewMeshImpl = dynamic_cast( GetServant( myNewMesh ).in() ); if ( !myNewMeshImpl ) - continue; + continue; meshGroupList.push_back( make_pair( myNewMeshImpl, aTopGroup )); - string iorString = GetORB()->object_to_string( myNewMesh ); - int newId = myStudyContext->findId( iorString ); - myStudyContext->mapOldToNew( id, newId ); - - // ouv : NPAL12872 - // try to read and set auto color flag - char aMeshAutoColorName[ 30 ]; - sprintf( aMeshAutoColorName, "AutoColorMesh %d", id); - if( aTopGroup->ExistInternalObject( aMeshAutoColorName ) ) - { - aDataset = new HDFdataset( aMeshAutoColorName, aTopGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - int* anAutoColor = new int[ size ]; - aDataset->ReadFromDisk( anAutoColor ); - aDataset->CloseOnDisk(); - myNewMeshImpl->SetAutoColor( (bool)anAutoColor[0] ); - } + string iorString = GetORB()->object_to_string( myNewMesh ); + int newId = myStudyContext->findId( iorString ); + myStudyContext->mapOldToNew( id, newId ); + + // ouv : NPAL12872 + // try to read and set auto color flag + char aMeshAutoColorName[ 30 ]; + sprintf( aMeshAutoColorName, "AutoColorMesh %d", id); + if( aTopGroup->ExistInternalObject( aMeshAutoColorName ) ) + { + aDataset = new HDFdataset( aMeshAutoColorName, aTopGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + int* anAutoColor = new int[ size ]; + aDataset->ReadFromDisk( anAutoColor ); + aDataset->CloseOnDisk(); + myNewMeshImpl->GetImpl().SetAutoColor( (bool)anAutoColor[0] ); + } // try to read and set reference to shape GEOM::GEOM_Object_var aShapeObject; @@ -3386,9 +4158,32 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, } } + // issue 0020693. Restore _isModified flag + if( aTopGroup->ExistInternalObject( "_isModified" ) ) + { + aDataset = new HDFdataset( "_isModified", aTopGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + int* isModified = new int[ size ]; + aDataset->ReadFromDisk( isModified ); + aDataset->CloseOnDisk(); + myNewMeshImpl->GetImpl().SetIsModified( bool(*isModified)); + } + + // issue 20918. Restore Persistent Id of SMESHDS_Mesh + if( aTopGroup->ExistInternalObject( "meshPersistentId" ) ) + { + aDataset = new HDFdataset( "meshPersistentId", aTopGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + int* meshPersistentId = new int[ size ]; + aDataset->ReadFromDisk( meshPersistentId ); + aDataset->CloseOnDisk(); + myNewMeshImpl->GetImpl().GetMeshDS()->SetPersistentId( *meshPersistentId ); + } } } - } + } // reading MESHes // As all object that can be referred by hypothesis are created, // we can restore hypothesis data @@ -3408,8 +4203,8 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, { aTopGroup = meshi_group->second; SMESH_Mesh_i* myNewMeshImpl = meshi_group->first; - ::SMESH_Mesh& myLocMesh = myNewMeshImpl->GetImpl(); - SMESHDS_Mesh* mySMESHDSMesh = myLocMesh.GetMeshDS(); + //::SMESH_Mesh& myLocMesh = myNewMeshImpl->GetImpl(); + //SMESHDS_Mesh* mySMESHDSMesh = myLocMesh.GetMeshDS(); GEOM::GEOM_Object_var aShapeObject = myNewMeshImpl->GetShapeToMesh(); bool hasData = false; @@ -3430,19 +4225,20 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aDataset->CloseOnDisk(); if ( strcmp( strHasData, "1") == 0 ) { // read mesh data from MED file - myReader.SetMesh( mySMESHDSMesh ); - myReader.SetMeshId( id ); - myReader.Perform(); + // myReader.SetMesh( mySMESHDSMesh ); + // myReader.SetMeshId( id ); + // myReader.Perform(); hasData = true; } } - // try to get applied algorithms + // Try to get applied ALGORITHMS (mesh is not cleared by algo addition because + // nodes and elements are not yet put into sub-meshes) if ( aTopGroup->ExistInternalObject( "Applied Algorithms" ) ) { aGroup = new HDFgroup( "Applied Algorithms", aTopGroup ); aGroup->OpenOnDisk(); // get number of applied algorithms - int aNbSubObjects = aGroup->nInternalObjects(); + int aNbSubObjects = aGroup->nInternalObjects(); if(MYDEBUG) MESSAGE( "VSR - number of applied algos " << aNbSubObjects ); for ( int j = 0; j < aNbSubObjects; j++ ) { char name_dataset[ HDF_NAME_MAX_LEN+1 ]; @@ -3455,9 +4251,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, char* refFromFile = new char[ size ]; aDataset->ReadFromDisk( refFromFile ); aDataset->CloseOnDisk(); - // san - it is impossible to recover applied algorithms using their entries within Load() method - //SALOMEDS::SObject_var hypSO = myCurrentStudy->FindObjectID( refFromFile ); //CORBA::Object_var hypObject = SObjectToObject( hypSO ); int id = atoi( refFromFile ); @@ -3481,7 +4275,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aGroup = new HDFgroup( "Applied Hypotheses", aTopGroup ); aGroup->OpenOnDisk(); // get number of applied hypotheses - int aNbSubObjects = aGroup->nInternalObjects(); + int aNbSubObjects = aGroup->nInternalObjects(); for ( int j = 0; j < aNbSubObjects; j++ ) { char name_dataset[ HDF_NAME_MAX_LEN+1 ]; aGroup->InternalObjectIndentify( j, name_dataset ); @@ -3493,9 +4287,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, char* refFromFile = new char[ size ]; aDataset->ReadFromDisk( refFromFile ); aDataset->CloseOnDisk(); - // san - it is impossible to recover applied hypotheses using their entries within Load() method - //SALOMEDS::SObject_var hypSO = myCurrentStudy->FindObjectID( refFromFile ); //CORBA::Object_var hypObject = SObjectToObject( hypSO ); int id = atoi( refFromFile ); @@ -3514,7 +4306,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aGroup->CloseOnDisk(); } - // --> try to find submeshes containers for each type of submesh + // --> try to find SUB-MESHES containers for each type of submesh for ( int j = GetSubMeshOnVertexTag(); j <= GetSubMeshOnCompoundTag(); j++ ) { char name_meshgroup[ 30 ]; if ( j == GetSubMeshOnVertexTag() ) @@ -3539,7 +4331,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aGroup->OpenOnDisk(); // get number of submeshes - int aNbSubMeshes = aGroup->nInternalObjects(); + int aNbSubMeshes = aGroup->nInternalObjects(); for ( int k = 0; k < aNbSubMeshes; k++ ) { // identify submesh char name_submeshgroup[ HDF_NAME_MAX_LEN+1 ]; @@ -3585,28 +4377,13 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, if ( aSubMesh->_is_nil() ) continue; - // VSR: Get submesh data from MED convertor - // int anInternalSubmeshId = aSubMesh->GetId(); // this is not a persistent ID, it's an internal one computed from sub-shape - // if (myNewMeshImpl->_mapSubMesh.find(anInternalSubmeshId) != myNewMeshImpl->_mapSubMesh.end()) { - // if(MYDEBUG) MESSAGE("VSR - SMESH_Gen_i::Load(): loading from MED file submesh with ID = " << - // subid << " for subshape # " << anInternalSubmeshId); - // SMESHDS_SubMesh* aSubMeshDS = - // myNewMeshImpl->_mapSubMesh[anInternalSubmeshId]->CreateSubMeshDS(); - // if ( !aSubMeshDS ) { - // if(MYDEBUG) MESSAGE("VSR - SMESH_Gen_i::Load(): FAILED to create a submesh for subshape # " << - // anInternalSubmeshId << " in current mesh!"); - // } - // else - // myReader.GetSubMesh( aSubMeshDS, subid ); - // } - // try to get applied algorithms if ( aSubGroup->ExistInternalObject( "Applied Algorithms" ) ) { // open "applied algorithms" HDF group aSubSubGroup = new HDFgroup( "Applied Algorithms", aSubGroup ); aSubSubGroup->OpenOnDisk(); // get number of applied algorithms - int aNbSubObjects = aSubSubGroup->nInternalObjects(); + int aNbSubObjects = aSubSubGroup->nInternalObjects(); for ( int l = 0; l < aNbSubObjects; l++ ) { char name_dataset[ HDF_NAME_MAX_LEN+1 ]; aSubSubGroup->InternalObjectIndentify( l, name_dataset ); @@ -3619,8 +4396,6 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aDataset->ReadFromDisk( refFromFile ); aDataset->CloseOnDisk(); - //SALOMEDS::SObject_var hypSO = myCurrentStudy->FindObjectID( refFromFile ); - //CORBA::Object_var hypObject = SObjectToObject( hypSO ); int id = atoi( refFromFile ); string anIOR = myStudyContext->getIORbyOldId( id ); if ( !anIOR.empty() ) { @@ -3643,7 +4418,7 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aSubSubGroup = new HDFgroup( "Applied Hypotheses", aSubGroup ); aSubSubGroup->OpenOnDisk(); // get number of applied hypotheses - int aNbSubObjects = aSubSubGroup->nInternalObjects(); + int aNbSubObjects = aSubSubGroup->nInternalObjects(); for ( int l = 0; l < aNbSubObjects; l++ ) { char name_dataset[ HDF_NAME_MAX_LEN+1 ]; aSubSubGroup->InternalObjectIndentify( l, name_dataset ); @@ -3656,8 +4431,6 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, aDataset->ReadFromDisk( refFromFile ); aDataset->CloseOnDisk(); - //SALOMEDS::SObject_var hypSO = myCurrentStudy->FindObjectID( refFromFile ); - //CORBA::Object_var hypObject = SObjectToObject( hypSO ); int id = atoi( refFromFile ); string anIOR = myStudyContext->getIORbyOldId( id ); if ( !anIOR.empty() ) { @@ -3670,222 +4443,21 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, } } } - // close "applied hypotheses" HDF group + // close "APPLIED HYPOTHESES" hdf group aSubSubGroup->CloseOnDisk(); } - // close submesh HDF group + // close SUB-MESH hdf group aSubGroup->CloseOnDisk(); } } - // close submeshes containers HDF group + // close SUB-MESHES containers hdf group aGroup->CloseOnDisk(); } } - if(hasData) { - - // Read sub-meshes from MED - // ------------------------- - if(MYDEBUG) MESSAGE("Create all sub-meshes"); - bool submeshesInFamilies = ( ! aTopGroup->ExistInternalObject( "Submeshes" )); - if ( submeshesInFamilies ) - { - // old way working before fix of PAL 12992 - myReader.CreateAllSubMeshes(); - } - else - { - // open a group - aGroup = new HDFgroup( "Submeshes", aTopGroup ); - aGroup->OpenOnDisk(); - - int maxID = mySMESHDSMesh->MaxShapeIndex(); - vector< SMESHDS_SubMesh * > subMeshes( maxID + 1, (SMESHDS_SubMesh*) 0 ); - vector< TopAbs_ShapeEnum > smType ( maxID + 1, TopAbs_SHAPE ); - - PositionCreator aPositionCreator; - - SMDS_NodeIteratorPtr nIt = mySMESHDSMesh->nodesIterator(); - SMDS_ElemIteratorPtr eIt = mySMESHDSMesh->elementsIterator(); - for ( int isNode = 0; isNode < 2; ++isNode ) - { - string aDSName( isNode ? "Node Submeshes" : "Element Submeshes"); - if ( aGroup->ExistInternalObject( (char*) aDSName.c_str() )) - { - aDataset = new HDFdataset( (char*) aDSName.c_str(), aGroup ); - aDataset->OpenOnDisk(); - // read submesh IDs for all elements sorted by ID - int nbElems = aDataset->GetSize(); - int* smIDs = new int [ nbElems ]; - aDataset->ReadFromDisk( smIDs ); - aDataset->CloseOnDisk(); - - // get elements sorted by ID - TIDSortedElemSet elemSet; - if ( isNode ) - while ( nIt->more() ) elemSet.insert( nIt->next() ); - else - while ( eIt->more() ) elemSet.insert( eIt->next() ); - ASSERT( elemSet.size() == nbElems ); - - // add elements to submeshes - TIDSortedElemSet::iterator iE = elemSet.begin(); - for ( int i = 0; i < nbElems; ++i, ++iE ) - { - int smID = smIDs[ i ]; - if ( smID == 0 ) continue; - ASSERT( smID <= maxID ); - const SMDS_MeshElement* elem = *iE; - // get or create submesh - SMESHDS_SubMesh* & sm = subMeshes[ smID ]; - if ( ! sm ) { - sm = mySMESHDSMesh->NewSubMesh( smID ); - smType[ smID ] = mySMESHDSMesh->IndexToShape( smID ).ShapeType(); - } - // add - if ( isNode ) { - SMDS_PositionPtr pos = aPositionCreator.MakePosition( smType[ smID ]); - pos->SetShapeId( smID ); - SMDS_MeshNode* node = const_cast( static_cast( elem )); - node->SetPosition( pos ); - sm->AddNode( node ); - } else { - sm->AddElement( elem ); - } - } - delete [] smIDs; - } - } - } // end reading submeshes - - // Read node positions on sub-shapes (SMDS_Position) - - if ( aTopGroup->ExistInternalObject( "Node Positions" )) - { - // There are 5 datasets to read: - // "Nodes on Edges" - ID of node on edge - // "Edge positions" - U parameter on node on edge - // "Nodes on Faces" - ID of node on face - // "Face U positions" - U parameter of node on face - // "Face V positions" - V parameter of node on face - const char* aEid_DSName = "Nodes on Edges"; - const char* aEu_DSName = "Edge positions"; - const char* aFu_DSName = "Face U positions"; - //char* aFid_DSName = "Nodes on Faces"; - //char* aFv_DSName = "Face V positions"; - - // data to retrieve - int nbEids = 0, nbFids = 0; - int *aEids = 0, *aFids = 0; - double *aEpos = 0, *aFupos = 0, *aFvpos = 0; - - // open a group - aGroup = new HDFgroup( "Node Positions", aTopGroup ); - aGroup->OpenOnDisk(); - - // loop on 5 data sets - int aNbObjects = aGroup->nInternalObjects(); - for ( int i = 0; i < aNbObjects; i++ ) - { - // identify dataset - char aDSName[ HDF_NAME_MAX_LEN+1 ]; - aGroup->InternalObjectIndentify( i, aDSName ); - // read data - aDataset = new HDFdataset( aDSName, aGroup ); - aDataset->OpenOnDisk(); - if ( aDataset->GetType() == HDF_FLOAT64 ) // Positions - { - double* pos = new double [ aDataset->GetSize() ]; - aDataset->ReadFromDisk( pos ); - // which one? - if ( strncmp( aDSName, aEu_DSName, strlen( aEu_DSName )) == 0 ) - aEpos = pos; - else if ( strncmp( aDSName, aFu_DSName, strlen( aFu_DSName )) == 0 ) - aFupos = pos; - else - aFvpos = pos; - } - else // NODE IDS - { - int aSize = aDataset->GetSize(); - - // for reading files, created from 18.07.2005 till 10.10.2005 - if (aDataset->GetType() == HDF_STRING) - aSize /= sizeof(int); - - int* ids = new int [aSize]; - aDataset->ReadFromDisk( ids ); - // on face or nodes? - if ( strncmp( aDSName, aEid_DSName, strlen( aEid_DSName )) == 0 ) { - aEids = ids; - nbEids = aSize; - } - else { - aFids = ids; - nbFids = aSize; - } - } - aDataset->CloseOnDisk(); - } // loop on 5 datasets - - // Set node positions on edges or faces - for ( int onFace = 0; onFace < 2; onFace++ ) - { - int nbNodes = ( onFace ? nbFids : nbEids ); - if ( nbNodes == 0 ) continue; - int* aNodeIDs = ( onFace ? aFids : aEids ); - double* aUPos = ( onFace ? aFupos : aEpos ); - double* aVPos = ( onFace ? aFvpos : 0 ); - // loop on node IDs - for ( int iNode = 0; iNode < nbNodes; iNode++ ) - { - const SMDS_MeshNode* node = mySMESHDSMesh->FindNode( aNodeIDs[ iNode ]); - ASSERT( node ); - SMDS_PositionPtr aPos = node->GetPosition(); - ASSERT( aPos ) - if ( onFace ) { - ASSERT( aPos->GetTypeOfPosition() == SMDS_TOP_FACE ); - SMDS_FacePosition* fPos = const_cast - ( static_cast( aPos.get() )); - fPos->SetUParameter( aUPos[ iNode ]); - fPos->SetVParameter( aVPos[ iNode ]); - } - else { - ASSERT( aPos->GetTypeOfPosition() == SMDS_TOP_EDGE ); - SMDS_EdgePosition* fPos = const_cast - ( static_cast( aPos.get() )); - fPos->SetUParameter( aUPos[ iNode ]); - } - } - } - if ( aEids ) delete [] aEids; - if ( aFids ) delete [] aFids; - if ( aEpos ) delete [] aEpos; - if ( aFupos ) delete [] aFupos; - if ( aFvpos ) delete [] aFvpos; - - aGroup->CloseOnDisk(); - - } // if ( aTopGroup->ExistInternalObject( "Node Positions" ) ) - } // if ( hasData ) - - // Recompute State (as computed sub-meshes are restored from MED) - if ( !aShapeObject->_is_nil() || !myNewMeshImpl->HasShapeToMesh()) { - MESSAGE("Compute State Engine ..."); - TopoDS_Shape myLocShape; - if(myNewMeshImpl->HasShapeToMesh()) - myLocShape = GeomObjectToShape( aShapeObject ); - else - myLocShape = SMESH_Mesh::PseudoShape(); - - myNewMeshImpl->GetImpl().GetSubMesh(myLocShape)->ComputeStateEngine - (SMESH_subMesh::SUBMESH_RESTORED); - MESSAGE("Compute State Engine finished"); - } - - // try to get groups - for ( int ii = GetNodeGroupsTag(); ii <= GetVolumeGroupsTag(); ii++ ) { + // try to get GROUPS + for ( int ii = GetNodeGroupsTag(); ii <= GetBallElementsGroupsTag(); ii++ ) { char name_group[ 30 ]; if ( ii == GetNodeGroupsTag() ) strcpy( name_group, "Groups of Nodes" ); @@ -3895,12 +4467,16 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, strcpy( name_group, "Groups of Faces" ); else if ( ii == GetVolumeGroupsTag() ) strcpy( name_group, "Groups of Volumes" ); + else if ( ii == Get0DElementsGroupsTag() ) + strcpy( name_group, "Groups of 0D Elements" ); + else if ( ii == GetBallElementsGroupsTag() ) + strcpy( name_group, "Groups of Balls" ); if ( aTopGroup->ExistInternalObject( name_group ) ) { aGroup = new HDFgroup( name_group, aTopGroup ); aGroup->OpenOnDisk(); // get number of groups - int aNbSubObjects = aGroup->nInternalObjects(); + int aNbSubObjects = aGroup->nInternalObjects(); for ( int j = 0; j < aNbSubObjects; j++ ) { char name_dataset[ HDF_NAME_MAX_LEN+1 ]; aGroup->InternalObjectIndentify( j, name_dataset ); @@ -3941,11 +4517,29 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, } } } + // Try to read a filter of SMESH_GroupOnFilter + SMESH::Filter_var filter; + SMESH_PredicatePtr predicate; + std::string hdfGrpName = "Filter " + SMESH_Comment(subid); + if ( aGroup->ExistInternalObject( hdfGrpName.c_str() )) + { + aDataset = new HDFdataset( hdfGrpName.c_str(), aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + char* persistStr = new char[ size ]; + aDataset->ReadFromDisk( persistStr ); + aDataset->CloseOnDisk(); + if ( strlen( persistStr ) > 0 ) { + filter = SMESH_GroupOnFilter_i::StringToFilter( persistStr ); + predicate = SMESH_GroupOnFilter_i::GetPredicate( filter ); + } + } + // Create group servant SMESH::ElementType type = (SMESH::ElementType)(ii - GetNodeGroupsTag() + 1); SMESH::SMESH_GroupBase_var aNewGroup = SMESH::SMESH_GroupBase::_duplicate - ( myNewMeshImpl->createGroup( type, nameFromFile, aShape ) ); - // Obtain a SMESHDS_Group object + ( myNewMeshImpl->createGroup( type, nameFromFile, aShape, predicate ) ); + // Obtain a SMESHDS_Group object if ( aNewGroup->_is_nil() ) continue; @@ -3953,55 +4547,126 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, int newSubId = myStudyContext->findId( iorSubString ); myStudyContext->mapOldToNew( subid, newSubId ); - SMESH_GroupBase_i* aGroupImpl = - dynamic_cast( GetServant( aNewGroup ).in() ); + SMESH_GroupBase_i* aGroupImpl = SMESH::DownCast< SMESH_GroupBase_i*>( aNewGroup ); if ( !aGroupImpl ) continue; - SMESH_Group* aLocalGroup = myLocMesh.GetGroup( aGroupImpl->GetLocalID() ); - if ( !aLocalGroup ) + if ( SMESH_GroupOnFilter_i* aFilterGroup = + dynamic_cast< SMESH_GroupOnFilter_i*>( aGroupImpl )) + aFilterGroup->SetFilter( filter ); + + SMESHDS_GroupBase* aGroupBaseDS = aGroupImpl->GetGroupDS(); + if ( !aGroupBaseDS ) continue; - SMESHDS_GroupBase* aGroupBaseDS = aLocalGroup->GetGroupDS(); aGroupBaseDS->SetStoreName( name_dataset ); - // ouv : NPAL12872 - // Read color of the group + // ouv : NPAL12872 + // Read color of the group char aGroupColorName[ 30 ]; sprintf( aGroupColorName, "ColorGroup %d", subid); if ( aGroup->ExistInternalObject( aGroupColorName ) ) - { - aDataset = new HDFdataset( aGroupColorName, aGroup ); - aDataset->OpenOnDisk(); - size = aDataset->GetSize(); - double* anRGB = new double[ size ]; - aDataset->ReadFromDisk( anRGB ); - aDataset->CloseOnDisk(); - Quantity_Color aColor( anRGB[0], anRGB[1], anRGB[2], Quantity_TOC_RGB ); - aGroupBaseDS->SetColor( aColor ); - } + { + aDataset = new HDFdataset( aGroupColorName, aGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + double* anRGB = new double[ size ]; + aDataset->ReadFromDisk( anRGB ); + aDataset->CloseOnDisk(); + Quantity_Color aColor( anRGB[0], anRGB[1], anRGB[2], Quantity_TOC_RGB ); + aGroupBaseDS->SetColor( aColor ); + } // Fill group with contents from MED file - SMESHDS_Group* aGrp = dynamic_cast( aGroupBaseDS ); - if ( aGrp ) - myReader.GetGroup( aGrp ); + // SMESHDS_Group* aGrp = dynamic_cast( aGroupBaseDS ); + // if ( aGrp ) + // myReader.GetGroup( aGrp ); } } aGroup->CloseOnDisk(); } + } // reading GROUPs + + // instead of reading mesh data, we read only brief information of all + // objects: mesh, groups, sub-meshes (issue 0021208 ) + if ( hasData ) + { + SMESH_PreMeshInfo::LoadFromFile( myNewMeshImpl, id, + meshfile.ToCString(), filename.ToCString(), + !isMultiFile ); + } + + // read Sub-Mesh ORDER if any + if( aTopGroup->ExistInternalObject( "Mesh Order" ) ) { + aDataset = new HDFdataset( "Mesh Order", aTopGroup ); + aDataset->OpenOnDisk(); + size = aDataset->GetSize(); + int* smIDs = new int[ size ]; + aDataset->ReadFromDisk( smIDs ); + aDataset->CloseOnDisk(); + TListOfListOfInt anOrderIds; + anOrderIds.push_back( TListOfInt() ); + for ( int i = 0; i < size; i++ ) + if ( smIDs[ i ] < 0 ) // is separator + anOrderIds.push_back( TListOfInt() ); + else + anOrderIds.back().push_back(smIDs[ i ]); + + myNewMeshImpl->GetImpl().SetMeshOrder( anOrderIds ); } + } // loop on meshes + + // update hyps needing full mesh data restored (issue 20918) + for ( hyp_data = hypDataList.begin(); hyp_data != hypDataList.end(); ++hyp_data ) + { + SMESH_Hypothesis_i* hyp = hyp_data->first; + hyp->UpdateAsMeshesRestored(); + } + + // notify algos on completed restoration to set sub-mesh event listeners + for ( meshi_group = meshGroupList.begin(); meshi_group != meshGroupList.end(); ++meshi_group ) + { + SMESH_Mesh_i* myNewMeshImpl = meshi_group->first; + ::SMESH_Mesh& myLocMesh = myNewMeshImpl->GetImpl(); + + TopoDS_Shape myLocShape; + if(myLocMesh.HasShapeToMesh()) + myLocShape = myLocMesh.GetShapeToMesh(); + else + myLocShape = SMESH_Mesh::PseudoShape(); + + myLocMesh.GetSubMesh(myLocShape)-> + ComputeStateEngine (SMESH_subMesh::SUBMESH_RESTORED); } + // close mesh group if(aTopGroup) - aTopGroup->CloseOnDisk(); + aTopGroup->CloseOnDisk(); } // close HDF file aFile->CloseOnDisk(); delete aFile; // Remove temporary files created from the stream - if ( !isMultiFile ) - SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.ToCString(), aFileSeq.in(), true ); + if ( !isMultiFile ) + { + SMESH_File meshFile( meshfile.ToCString() ); + if ( !meshFile ) // no meshfile exists + { + SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.ToCString(), aFileSeq.in(), true ); + } + else + { + Engines::Container_var container = GetContainerRef(); + if ( Engines_Container_i* container_i = SMESH::DownCast( container )) + { + container_i->registerTemporaryFile( filename.ToCString() ); + container_i->registerTemporaryFile( meshfile.ToCString() ); + container_i->registerTemporaryFile( tmpDir.ToCString() ); + } + } + } + pd << ""; // prevent optimizing pd out INFOS( "SMESH_Gen_i::Load completed" ); return true; @@ -4016,9 +4681,9 @@ bool SMESH_Gen_i::Load( SALOMEDS::SComponent_ptr theComponent, //============================================================================= bool SMESH_Gen_i::LoadASCII( SALOMEDS::SComponent_ptr theComponent, - const SALOMEDS::TMPFile& theStream, - const char* theURL, - bool isMultiFile ) { + const SALOMEDS::TMPFile& theStream, + const char* theURL, + bool isMultiFile ) { if(MYDEBUG) MESSAGE( "SMESH_Gen_i::LoadASCII" ); return Load( theComponent, theStream, theURL, isMultiFile ); @@ -4042,7 +4707,7 @@ bool SMESH_Gen_i::LoadASCII( SALOMEDS::SComponent_ptr theComponent, } SALOMEDS::TMPFile_var aRealStreamFile = new SALOMEDS::TMPFile(real_size, real_size, buffer, 1); - + return Load( theComponent, *(aRealStreamFile._retn()), theURL, isMultiFile ); } @@ -4059,8 +4724,9 @@ void SMESH_Gen_i::Close( SALOMEDS::SComponent_ptr theComponent ) if(MYDEBUG) MESSAGE( "SMESH_Gen_i::Close" ); // set correct current study - if (theComponent->GetStudy()->StudyId() != GetCurrentStudyID()) - SetCurrentStudy(theComponent->GetStudy()); + SALOMEDS::Study_var study = theComponent->GetStudy(); + if ( study->StudyId() != GetCurrentStudyID()) + setCurrentStudy( study, /*IsBeingClosed=*/true ); // Clear study contexts data int studyId = GetCurrentStudyID(); @@ -4078,7 +4744,7 @@ void SMESH_Gen_i::Close( SALOMEDS::SComponent_ptr theComponent ) // printf( "--------------------------- SMESH_Gen_i::Close, delete aGroup = %p \n", i_mesh->second ); // delete i_mesh->second; // } - + // delete SMESHDS_Mesh's // it's too long on big meshes @@ -4086,14 +4752,18 @@ void SMESH_Gen_i::Close( SALOMEDS::SComponent_ptr theComponent ) // delete context->myDocument; // context->myDocument = 0; // } - + + // remove the tmp files meshes are loaded from + SMESH_PreMeshInfo::RemoveStudyFiles_TMP_METHOD( theComponent ); + + myCurrentStudy = SALOMEDS::Study::_nil(); return; } //============================================================================= /*! * SMESH_Gen_i::ComponentDataType - * + * * Get component data type */ //============================================================================= @@ -4104,23 +4774,23 @@ char* SMESH_Gen_i::ComponentDataType() return CORBA::string_dup( "SMESH" ); } - + //============================================================================= /*! * SMESH_Gen_i::IORToLocalPersistentID - * + * * Transform data from transient form to persistent */ //============================================================================= char* SMESH_Gen_i::IORToLocalPersistentID( SALOMEDS::SObject_ptr /*theSObject*/, - const char* IORString, - CORBA::Boolean /*isMultiFile*/, - CORBA::Boolean /*isASCII*/ ) + const char* IORString, + CORBA::Boolean /*isMultiFile*/, + CORBA::Boolean /*isASCII*/ ) { if(MYDEBUG) MESSAGE( "SMESH_Gen_i::IORToLocalPersistentID" ); StudyContext* myStudyContext = GetCurrentStudyContext(); - + if ( myStudyContext && strcmp( IORString, "" ) != 0 ) { int anId = myStudyContext->findId( IORString ); if ( anId ) { @@ -4142,9 +4812,9 @@ char* SMESH_Gen_i::IORToLocalPersistentID( SALOMEDS::SObject_ptr /*theSObject*/, //============================================================================= char* SMESH_Gen_i::LocalPersistentIDToIOR( SALOMEDS::SObject_ptr /*theSObject*/, - const char* aLocalPersistentID, - CORBA::Boolean /*isMultiFile*/, - CORBA::Boolean /*isASCII*/ ) + const char* aLocalPersistentID, + CORBA::Boolean /*isMultiFile*/, + CORBA::Boolean /*isASCII*/ ) { if(MYDEBUG) MESSAGE( "SMESH_Gen_i::LocalPersistentIDToIOR(): id = " << aLocalPersistentID ); StudyContext* myStudyContext = GetCurrentStudyContext(); @@ -4158,7 +4828,7 @@ char* SMESH_Gen_i::LocalPersistentIDToIOR( SALOMEDS::SObject_ptr /*theSObject*/, //======================================================================= //function : RegisterObject -//purpose : +//purpose : //======================================================================= int SMESH_Gen_i::RegisterObject(CORBA::Object_ptr theObject) @@ -4208,21 +4878,26 @@ void SMESH_Gen_i::SetName(const char* theIOR, } } +int SMESH_Gen_i::GetCurrentStudyID() +{ + return myCurrentStudy->_is_nil() || myCurrentStudy->_non_existent() ? -1 : myCurrentStudy->StudyId(); +} + //============================================================================= -/*! +/*! * SMESHEngine_factory * - * C factory, accessible with dlsym, after dlopen + * C factory, accessible with dlsym, after dlopen */ //============================================================================= extern "C" { SMESH_I_EXPORT PortableServer::ObjectId* SMESHEngine_factory( CORBA::ORB_ptr orb, - PortableServer::POA_ptr poa, - PortableServer::ObjectId* contId, - const char* instanceName, - const char* interfaceName ) + PortableServer::POA_ptr poa, + PortableServer::ObjectId* contId, + const char* instanceName, + const char* interfaceName ) { if(MYDEBUG) MESSAGE( "PortableServer::ObjectId* SMESHEngine_factory()" ); if(MYDEBUG) SCRUTE(interfaceName); diff --git a/src/SMESH_I/SMESH_Gen_i.hxx b/src/SMESH_I/SMESH_Gen_i.hxx index 836e585c7..a72c466f3 100644 --- a/src/SMESH_I/SMESH_Gen_i.hxx +++ b/src/SMESH_I/SMESH_Gen_i.hxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Gen_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// + #ifndef _SMESH_GEN_I_HXX_ #define _SMESH_GEN_I_HXX_ @@ -64,8 +63,8 @@ public: // constructor StudyContext() {} // destructor - ~StudyContext() - { + ~StudyContext() + { mapIdToIOR.clear(); mapIdToId.clear(); } @@ -113,7 +112,7 @@ public: } return 0; } - + private: // get next free object identifier int getNextId() @@ -133,7 +132,7 @@ private: // ========================================================== class SMESH_I_EXPORT SMESH_Gen_i: public virtual POA_SMESH::SMESH_Gen, - public virtual Engines_Component_i + public virtual Engines_Component_i { public: // Get last created instance of the class @@ -159,14 +158,14 @@ public: GEOM::GEOM_Object_ptr ShapeToGeomObject (const TopoDS_Shape& theShape ); // Get TopoDS_Shape correspoding to GEOM_Object TopoDS_Shape GeomObjectToShape(GEOM::GEOM_Object_ptr theGeomObject); - + // Default constructor SMESH_Gen_i(); // Standard constructor SMESH_Gen_i( CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, - PortableServer::ObjectId* contId, - const char* instanceName, + PortableServer::ObjectId* contId, + const char* instanceName, const char* interfaceName ); // Destructor virtual ~SMESH_Gen_i(); @@ -176,7 +175,7 @@ public: // ***************************************** // Set a new Mesh object name void SetName(const char* theIOR, - const char* theName); + const char* theName); //GEOM::GEOM_Gen_ptr SetGeomEngine( const char* containerLoc ); void SetGeomEngine( GEOM::GEOM_Gen_ptr geomcompo ); @@ -195,7 +194,7 @@ public: SMESH::SMESH_Hypothesis_ptr CreateHypothesis (const char* theHypType, const char* theLibName) throw ( SALOME::SALOME_Exception ); - + // Return hypothesis of given type holding parameter values of the existing mesh SMESH::SMESH_Hypothesis_ptr GetHypothesisParameterValues (const char* theHypType, const char* theLibName, @@ -203,7 +202,9 @@ public: GEOM::GEOM_Object_ptr theGeom, CORBA::Boolean byMesh) throw ( SALOME::SALOME_Exception ); - + + // Preferences + // ------------ /*! * Sets number of segments per diagonal of boundary box of geometry by which * default segment length of appropriate 1D hypotheses is defined @@ -214,6 +215,21 @@ public: */ void SetDefaultNbSegments(CORBA::Long theNbSegments) throw ( SALOME::SALOME_Exception ); + /*! + Set an option value + */ + virtual void SetOption(const char*, const char*); + /*! + Return an option value + */ + virtual char* GetOption(const char*); + + /*! + * To load full mesh data from study at hyp modification or not + */ + bool ToForgetMeshDataOnHypModif() const { return myToForgetMeshDataOnHypModif; } + + // Create empty mesh on a shape SMESH::SMESH_Mesh_ptr CreateMesh( GEOM::GEOM_Object_ptr theShapeObject ) throw ( SALOME::SALOME_Exception ); @@ -222,7 +238,7 @@ public: SMESH::SMESH_Mesh_ptr CreateEmptyMesh() throw ( SALOME::SALOME_Exception ); - // Create mesh(es) and import data from UNV file + // Create mesh(es) and import data from UNV fileter SMESH::SMESH_Mesh_ptr CreateMeshesFromUNV( const char* theFileName ) throw ( SALOME::SALOME_Exception ); @@ -231,14 +247,35 @@ public: SMESH::DriverMED_ReadStatus& theStatus ) throw ( SALOME::SALOME_Exception ); + // Create mesh(es) and import data from MED file + SMESH::mesh_array* CreateMeshesFromSAUV( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus ) + throw ( SALOME::SALOME_Exception ); + // Create mesh(es) and import data from STL file SMESH::SMESH_Mesh_ptr CreateMeshesFromSTL( const char* theFileName ) throw ( SALOME::SALOME_Exception ); + // Create mesh(es) and import data from CGNS file + SMESH::mesh_array* CreateMeshesFromCGNS( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus ) + throw ( SALOME::SALOME_Exception ); + + // Copy a part of mesh + SMESH::SMESH_Mesh_ptr CopyMesh(SMESH::SMESH_IDSource_ptr meshPart, + const char* meshName, + CORBA::Boolean toCopyGroups, + CORBA::Boolean toKeepIDs); + // Compute mesh on a shape CORBA::Boolean Compute( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theShapeObject ) throw ( SALOME::SALOME_Exception ); + + // Cancel Compute mesh on a shape + void CancelCompute( SMESH::SMESH_Mesh_ptr theMesh, + GEOM::GEOM_Object_ptr theShapeObject ); + /*! * \brief Return errors of mesh computation */ @@ -246,24 +283,33 @@ public: GEOM::GEOM_Object_ptr theShapeObject ) throw ( SALOME::SALOME_Exception ); + /*! + * Evaluate mesh on a shape and + * returns statistic of mesh elements + * Result array of number enityties + */ + SMESH::long_array* Evaluate(SMESH::SMESH_Mesh_ptr theMesh, + GEOM::GEOM_Object_ptr theShapeObject) + throw ( SALOME::SALOME_Exception ); + // Returns true if mesh contains enough data to be computed CORBA::Boolean IsReadyToCompute( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theShapeObject ) throw ( SALOME::SALOME_Exception ); - + /*! * Calculate Mesh as preview till indicated dimension on shape * First, verify list of hypothesis associated with the subShape. * Return mesh preview structure */ SMESH::MeshPreviewStruct* Precompute( SMESH::SMESH_Mesh_ptr theMesh, - GEOM::GEOM_Object_ptr theSubObject, - SMESH::Dimension theDimension, - SMESH::long_array& theShapesId ) + GEOM::GEOM_Object_ptr theSubObject, + SMESH::Dimension theDimension, + SMESH::long_array& theShapesId ) throw ( SALOME::SALOME_Exception ); // Returns errors of hypotheses definintion - SMESH::algo_error_array* GetAlgoState( SMESH::SMESH_Mesh_ptr theMesh, + SMESH::algo_error_array* GetAlgoState( SMESH::SMESH_Mesh_ptr theMesh, GEOM::GEOM_Object_ptr theSubObject ) throw ( SALOME::SALOME_Exception ); @@ -285,54 +331,61 @@ public: // Return geometrical object the given element is built on. Don't publish it in study. GEOM::GEOM_Object_ptr FindGeometryByMeshElement( SMESH::SMESH_Mesh_ptr theMesh, - CORBA::Long theElementID) + CORBA::Long theElementID) throw ( SALOME::SALOME_Exception ); // Concatenate the given meshes into one mesh - SMESH::SMESH_Mesh_ptr ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, - CORBA::Double theMergeTolerance, - CORBA::Boolean theCommonGroups) + SMESH::SMESH_Mesh_ptr ConcatenateCommon(const SMESH::mesh_array& theMeshesArray, + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, + CORBA::Double theMergeTolerance, + CORBA::Boolean theCommonGroups) throw ( SALOME::SALOME_Exception ); // Concatenate the given meshes into one mesh - SMESH::SMESH_Mesh_ptr Concatenate(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, - CORBA::Double theMergeTolerance) + SMESH::SMESH_Mesh_ptr Concatenate(const SMESH::mesh_array& theMeshesArray, + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, + CORBA::Double theMergeTolerance) throw ( SALOME::SALOME_Exception ); // Concatenate the given meshes into one mesh // Create the groups of all elements from initial meshes - SMESH::SMESH_Mesh_ptr ConcatenateWithGroups(const SMESH::mesh_array& theMeshesArray, - CORBA::Boolean theUniteIdenticalGroups, - CORBA::Boolean theMergeNodesAndElements, - CORBA::Double theMergeTolerance) + SMESH::SMESH_Mesh_ptr ConcatenateWithGroups(const SMESH::mesh_array& theMeshesArray, + CORBA::Boolean theUniteIdenticalGroups, + CORBA::Boolean theMergeNodesAndElements, + CORBA::Double theMergeTolerance) throw ( SALOME::SALOME_Exception ); + // Get MED version of the file by its name + CORBA::Boolean GetMEDVersion(const char* theFileName, + SMESH::MED_VERSION& theVersion); + + // Get names of meshes defined in file with the specified name + SMESH::string_array* GetMeshNames(const char* theFileName); + // **************************************************** // Interface inherited methods (from SALOMEDS::Driver) // **************************************************** // Save SMESH data SALOMEDS::TMPFile* Save( SALOMEDS::SComponent_ptr theComponent, - const char* theURL, - bool isMultiFile ); + const char* theURL, + bool isMultiFile ); // Load SMESH data bool Load( SALOMEDS::SComponent_ptr theComponent, - const SALOMEDS::TMPFile& theStream, - const char* theURL, - bool isMultiFile ); + const SALOMEDS::TMPFile& theStream, + const char* theURL, + bool isMultiFile ); // Save SMESH data in ASCII format SALOMEDS::TMPFile* SaveASCII( SALOMEDS::SComponent_ptr theComponent, - const char* theURL, - bool isMultiFile ); + const char* theURL, + bool isMultiFile ); // Load SMESH data in ASCII format bool LoadASCII( SALOMEDS::SComponent_ptr theComponent, - const SALOMEDS::TMPFile& theStream, - const char* theURL, - bool isMultiFile ); + const SALOMEDS::TMPFile& theStream, + const char* theURL, + bool isMultiFile ); // Create filter manager SMESH::FilterManager_ptr CreateFilterManager(); @@ -340,30 +393,33 @@ public: // Return a pattern mesher SMESH::SMESH_Pattern_ptr GetPattern(); + // Create measurement instance + SMESH::Measurements_ptr CreateMeasurements(); + // Clears study-connected data when it is closed void Close( SALOMEDS::SComponent_ptr theComponent ); - + // Get component data type char* ComponentDataType(); - + // Transform data from transient form to persistent char* IORToLocalPersistentID( SALOMEDS::SObject_ptr theSObject, - const char* IORString, - CORBA::Boolean isMultiFile, - CORBA::Boolean isASCII ); + const char* IORString, + CORBA::Boolean isMultiFile, + CORBA::Boolean isASCII ); // Transform data from persistent form to transient char* LocalPersistentIDToIOR( SALOMEDS::SObject_ptr theSObject, - const char* aLocalPersistentID, - CORBA::Boolean isMultiFile, - CORBA::Boolean isASCII ); + const char* aLocalPersistentID, + CORBA::Boolean isMultiFile, + CORBA::Boolean isASCII ); // Returns true if object can be published in the study bool CanPublishInStudy( CORBA::Object_ptr theIOR ); // Publish object in the study SALOMEDS::SObject_ptr PublishInStudy( SALOMEDS::Study_ptr theStudy, - SALOMEDS::SObject_ptr theSObject, - CORBA::Object_ptr theObject, - const char* theName ) + SALOMEDS::SObject_ptr theSObject, + CORBA::Object_ptr theObject, + const char* theName ) throw ( SALOME::SALOME_Exception ); // Copy-paste methods - returns true if object can be copied to the clipboard @@ -374,8 +430,8 @@ public: CORBA::Boolean CanPaste( const char* theComponentName, CORBA::Long theObjectID ) { return false; } // Copy-paste methods - paste object from the clipboard SALOMEDS::SObject_ptr PasteInto( const SALOMEDS::TMPFile& theStream, - CORBA::Long theObjectID, - SALOMEDS::SObject_ptr theObject ) { + CORBA::Long theObjectID, + SALOMEDS::SObject_ptr theObject ) { SALOMEDS::SObject_var aResultSO; return aResultSO._retn(); } @@ -384,9 +440,10 @@ public: // Dump python // ============ - virtual Engines::TMPFile* DumpPython(CORBA::Object_ptr theStudy, - CORBA::Boolean isPublished, - CORBA::Boolean& isValidScript); + virtual Engines::TMPFile* DumpPython(CORBA::Object_ptr theStudy, + CORBA::Boolean isPublished, + CORBA::Boolean isMultiFile, + CORBA::Boolean& isValidScript); void AddToPythonScript (int theStudyID, const TCollection_AsciiString& theString); @@ -394,10 +451,12 @@ public: void SavePython (SALOMEDS::Study_ptr theStudy); - TCollection_AsciiString DumpPython_impl (SALOMEDS::Study_ptr theStudy, + TCollection_AsciiString DumpPython_impl (SALOMEDS::Study_ptr theStudy, Resource_DataMapOfAsciiStringAsciiString& theObjectNames, Resource_DataMapOfAsciiStringAsciiString& theNames, - bool isPublished, + bool isPublished, + bool isMultiFile, + bool isHistoricalDump, bool& aValidScript, const TCollection_AsciiString& theSavedTrace); @@ -413,7 +472,7 @@ public: // Get shape reader GEOM_Client* GetShapeReader(); - // Tags definition + // Tags definition static long GetHypothesisRootTag(); static long GetAlgorithmsRootTag(); static long GetRefOnShapeTag(); @@ -430,6 +489,8 @@ public: static long GetEdgeGroupsTag(); static long GetFaceGroupsTag(); static long GetVolumeGroupsTag(); + static long Get0DElementsGroupsTag(); + static long GetBallElementsGroupsTag(); // publishing methods SALOMEDS::SComponent_ptr PublishComponent(SALOMEDS::Study_ptr theStudy); @@ -443,7 +504,7 @@ public: SMESH::SMESH_Mesh_ptr theMesh, SMESH::SMESH_subMesh_ptr theSubMesh, GEOM::GEOM_Object_ptr theShapeObject, - const char* theName = 0); + const char* theName = 0); SALOMEDS::SObject_ptr PublishGroup (SALOMEDS::Study_ptr theStudy, SMESH::SMESH_Mesh_ptr theMesh, SMESH::SMESH_GroupBase_ptr theGroup, @@ -464,6 +525,9 @@ public: const char* theName, const char* theDefaultName = 0); + static void SetPixMap(SALOMEDS::SObject_ptr theSObject, + const char* thePixMap); + // Get study context StudyContext* GetCurrentStudyContext(); @@ -474,7 +538,7 @@ public: CORBA::Long GetObjectId(CORBA::Object_ptr theObject); // Return an object that previously had an oldID - template + template typename TInterface::_var_type GetObjectByOldId( const int oldID ) { if ( StudyContext* myStudyContext = GetCurrentStudyContext() ) { @@ -486,31 +550,38 @@ public: } // Get current study ID - int GetCurrentStudyID() - { return myCurrentStudy->_is_nil() ? -1 : myCurrentStudy->StudyId(); } + int GetCurrentStudyID(); /*! * \brief Find SObject for an algo */ SALOMEDS::SObject_ptr GetAlgoSO(const ::SMESH_Algo* algo); - void UpdateParameters(CORBA::Object_ptr theObject, const char* theParameters); + void UpdateParameters(/*CORBA::Object_ptr theObject,*/ const char* theParameters); char* GetParameters(CORBA::Object_ptr theObject); char* ParseParameters(const char* theParameters); - - + const std::vector< std::string >& GetLastParameters() const { return myLastParameters; } + private: // Create hypothesis of given type SMESH::SMESH_Hypothesis_ptr createHypothesis( const char* theHypName, const char* theLibName) throw ( SALOME::SALOME_Exception ); - + // Create empty mesh on shape SMESH::SMESH_Mesh_ptr createMesh() throw ( SALOME::SALOME_Exception ); static void loadGeomData( SALOMEDS::SComponent_ptr theCompRoot ); - + + SMESH::mesh_array* CreateMeshesFromMEDorSAUV( const char* theFileName, + SMESH::DriverMED_ReadStatus& theStatus, + const char* theCommandNameForPython, + const char* theFileNameForPython); + + void setCurrentStudy( SALOMEDS::Study_ptr theStudy, + bool theStudyIsBeingClosed=false); + private: static GEOM::GEOM_Gen_var myGeomGen; static CORBA::ORB_var myOrb; // ORB reference @@ -518,7 +589,7 @@ private: static SALOME_NamingService* myNS; // Naming Service static SALOME_LifeCycleCORBA* myLCC; // Life Cycle CORBA static SMESH_Gen_i* mySMESHGen; // Point to last created instance of the class - ::SMESH_Gen myGen; // SMESH_Gen local implementation + ::SMESH_Gen myGen; // SMESH_Gen local implementation // hypotheses managing map myHypCreatorMap; @@ -529,8 +600,13 @@ private: SALOMEDS::Study_var myCurrentStudy; // Current study CORBA::Boolean myIsEmbeddedMode; // Current mode + // To load full mesh data from study at hyp modification or not + bool myToForgetMeshDataOnHypModif; + // Dump Python: trace of API methods calls std::map < int, Handle(TColStd_HSequenceOfAsciiString) > myPythonScripts; + bool myIsHistoricalPythonDump; + std::vector< std::string > myLastParameters; }; diff --git a/src/SMESH_I/SMESH_Gen_i_1.cxx b/src/SMESH_I/SMESH_Gen_i_1.cxx index b0701ea59..1638083b5 100644 --- a/src/SMESH_I/SMESH_Gen_i_1.cxx +++ b/src/SMESH_I/SMESH_Gen_i_1.cxx @@ -1,31 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses -// File : SMESH_Gen_i_1.cxx -// Created : Thu Oct 21 17:24:06 2004 -// Author : Edward AGAPOV (eap) -// Module : SMESH -// $Header: +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : SMESH_Gen_i_1.cxx +// Created : Thu Oct 21 17:24:06 2004 +// Author : Edward AGAPOV (eap) +// Module : SMESH + #include "SMESH_Gen_i.hxx" #include "SMESH_Mesh_i.hxx" @@ -40,13 +38,14 @@ #include "Utils_ExceptHandlers.hxx" #include +#include #ifdef _DEBUG_ static int MYDEBUG = 0; -static int VARIABLE_DEBUG = 0; +//static int VARIABLE_DEBUG = 0; #else static int MYDEBUG = 0; -static int VARIABLE_DEBUG = 0; +//static int VARIABLE_DEBUG = 0; #endif //============================================================================= @@ -137,6 +136,16 @@ long SMESH_Gen_i::GetVolumeGroupsTag() return SMESH::Tag_VolumeGroups; } +long SMESH_Gen_i::Get0DElementsGroupsTag() +{ + return SMESH::Tag_0DElementsGroups; +} + +long SMESH_Gen_i::GetBallElementsGroupsTag() +{ + return SMESH::Tag_BallElementsGroups; +} + //============================================================================= /*! * SMESH_Gen_i::CanPublishInStudy @@ -210,8 +219,10 @@ GEOM::GEOM_Object_ptr SMESH_Gen_i::ShapeToGeomObject (const TopoDS_Shape& theSha GEOM_Client* aClient = GetShapeReader(); TCollection_AsciiString IOR; if ( aClient && aClient->Find( theShape, IOR )) - aShapeObj = GEOM::GEOM_Object::_narrow - ( GetORB()->string_to_object( IOR.ToCString() ) ); + { + CORBA::Object_var obj = GetORB()->string_to_object( IOR.ToCString() ); + aShapeObj = GEOM::GEOM_Object::_narrow ( obj ); + } } return aShapeObj._retn(); } @@ -262,7 +273,8 @@ static SALOMEDS::SObject_ptr publish(SALOMEDS::Study_ptr theStudy, } if ( thePixMap ) { anAttr = aStudyBuilder->FindOrCreateAttribute( SO, "AttributePixMap" ); - SALOMEDS::AttributePixMap::_narrow( anAttr )->SetPixMap( thePixMap ); + SALOMEDS::AttributePixMap_var pm = SALOMEDS::AttributePixMap::_narrow( anAttr ); + pm->SetPixMap( thePixMap ); } if ( !theSelectable ) { anAttr = aStudyBuilder->FindOrCreateAttribute( SO, "AttributeSelectable" ); @@ -298,6 +310,25 @@ void SMESH_Gen_i::SetName(SALOMEDS::SObject_ptr theSObject, } } +//======================================================================= +//function : SetPixMap +//purpose : +//======================================================================= + +void SMESH_Gen_i::SetPixMap(SALOMEDS::SObject_ptr theSObject, + const char* thePixMap) +{ + if ( !theSObject->_is_nil() && thePixMap && strlen( thePixMap )) + { + SALOMEDS::Study_var aStudy = theSObject->GetStudy(); + SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); + SALOMEDS::GenericAttribute_var anAttr = + aStudyBuilder->FindOrCreateAttribute( theSObject, "AttributePixMap" ); + SALOMEDS::AttributePixMap_var aPMAttr = SALOMEDS::AttributePixMap::_narrow( anAttr ); + aPMAttr->SetPixMap( thePixMap ); + } +} + //======================================================================= //function : addReference //purpose : @@ -349,9 +380,9 @@ static void addReference (SALOMEDS::Study_ptr theStudy, //============================================================================= SALOMEDS::SObject_ptr SMESH_Gen_i::PublishInStudy(SALOMEDS::Study_ptr theStudy, - SALOMEDS::SObject_ptr theSObject, - CORBA::Object_ptr theIOR, - const char* theName) + SALOMEDS::SObject_ptr theSObject, + CORBA::Object_ptr theIOR, + const char* theName) throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); @@ -446,9 +477,9 @@ static long findMaxChildTag( SALOMEDS::SObject_ptr theSObject ) if ( !aStudy->_is_nil() ) { SALOMEDS::ChildIterator_var anIter = aStudy->NewChildIterator( theSObject ); for ( ; anIter->More(); anIter->Next() ) { - long nTag = anIter->Value()->Tag(); - if ( nTag > aTag ) - aTag = nTag; + long nTag = anIter->Value()->Tag(); + if ( nTag > aTag ) + aTag = nTag; } } } @@ -499,13 +530,12 @@ SALOMEDS::SObject_ptr SMESH_Gen_i::PublishMesh (SALOMEDS::Study_ptr theStudy, // Publish global hypotheses - SMESH::ListOfHypothesis * hypList = theMesh->GetHypothesisList( aShapeObject ); - if ( hypList ) - for ( int i = 0; i < hypList->length(); i++ ) { - SMESH::SMESH_Hypothesis_var aHyp = SMESH::SMESH_Hypothesis::_narrow( (*hypList)[ i ]); - PublishHypothesis( theStudy, aHyp ); - AddHypothesisToShape( theStudy, theMesh, aShapeObject, aHyp ); - } + SMESH::ListOfHypothesis_var hypList = theMesh->GetHypothesisList( aShapeObject ); + for ( int i = 0; i < hypList->length(); i++ ) { + SMESH::SMESH_Hypothesis_var aHyp = SMESH::SMESH_Hypothesis::_narrow( hypList[ i ]); + PublishHypothesis( theStudy, aHyp ); + AddHypothesisToShape( theStudy, theMesh, aShapeObject, aHyp ); + } } // Publish submeshes @@ -569,7 +599,7 @@ SALOMEDS::SObject_ptr SMESH_Gen_i::PublishSubMesh (SALOMEDS::Study_ptr theS } // Find submesh sub-tree tag long aRootTag; - char* aRootName = ""; + const char* aRootName = ""; switch ( theShapeObject->GetShapeType() ) { case GEOM::VERTEX: aRootTag = GetSubMeshOnVertexTag(); @@ -607,7 +637,10 @@ SALOMEDS::SObject_ptr SMESH_Gen_i::PublishSubMesh (SALOMEDS::Study_ptr theS SetName( aRootSO, aRootName ); // Add new submesh to corresponding sub-tree - aSubMeshSO = publish (theStudy, theSubMesh, aRootSO, 0, "ICON_SMESH_TREE_MESH_WARN"); + SMESH::array_of_ElementType_var elemTypes = theSubMesh->GetTypes(); + const int isEmpty = ( elemTypes->length() == 0 ); + const char* pm[2] = { "ICON_SMESH_TREE_MESH", "ICON_SMESH_TREE_MESH_WARN" }; + aSubMeshSO = publish (theStudy, theSubMesh, aRootSO, 0, pm[isEmpty] ); if ( aSubMeshSO->_is_nil() ) return aSubMeshSO._retn(); } @@ -655,21 +688,36 @@ SALOMEDS::SObject_ptr SMESH_Gen_i::PublishGroup (SALOMEDS::Study_ptr theStudy } int aType = (int)theGroup->GetType(); const char* aRootNames[] = { - "Compound Groups", "Groups of Nodes", - "Groups of Edges", "Groups of Faces", "Groups of Volumes" }; + "Compound Groups", "Groups of Nodes", "Groups of Edges", + "Groups of Faces", "Groups of Volumes", "Groups of 0D Elements", + "Groups of Balls" }; // Currently, groups with heterogenous content are not supported - if ( aType != SMESH::ALL ) { + if ( aType != SMESH::ALL ) + { long aRootTag = GetNodeGroupsTag() + aType - 1; // Find or create groups root SALOMEDS::SObject_var aRootSO = publish (theStudy, CORBA::Object::_nil(), aMeshSO, aRootTag, 0, false ); - if ( aType < 5 ) + if ( aType < sizeof(aRootNames)/sizeof(char*) ) SetName( aRootSO, aRootNames[aType] ); // Add new group to corresponding sub-tree - aGroupSO = publish (theStudy, theGroup, aRootSO, 0, "ICON_SMESH_TREE_GROUP" ); + SMESH::array_of_ElementType_var elemTypes = theGroup->GetTypes(); + int isEmpty = ( elemTypes->length() == 0 ); + std::string pm[2] = { "ICON_SMESH_TREE_GROUP", "ICON_SMESH_TREE_MESH_WARN" }; + if ( SMESH::DownCast< SMESH_GroupOnFilter_i* > ( theGroup )) + { + pm[0] = "ICON_SMESH_TREE_GROUP_ON_FILTER"; + } + else if ( SMESH::DownCast< SMESH_Group_i* > ( theGroup )) + { + SMESH::array_of_ElementType_var allElemTypes = theMesh->GetTypes(); + for ( size_t i =0; i < allElemTypes->length() && isEmpty; ++i ) + isEmpty = ( allElemTypes[i] != theGroup->GetType() ); + } + aGroupSO = publish (theStudy, theGroup, aRootSO, 0, pm[isEmpty].c_str() ); } if ( aGroupSO->_is_nil() ) return aGroupSO._retn(); @@ -869,71 +917,146 @@ bool SMESH_Gen_i::RemoveHypothesisFromShape(SALOMEDS::Study_ptr theStudy //function : UpdateParameters //purpose : //======================================================================= -void SMESH_Gen_i::UpdateParameters(CORBA::Object_ptr theObject, const char* theParameters) +void SMESH_Gen_i::UpdateParameters(/*CORBA::Object_ptr theObject,*/ const char* theParameters) { - - if(VARIABLE_DEBUG) - cout<<"UpdateParameters : "<_is_nil() || CORBA::is_nil(theObject)) - return; - - SALOMEDS::SObject_var aSObj = ObjectToSObject(aStudy,theObject); - if(aSObj->_is_nil()) + if ( aStudy->_is_nil() ) return; - - SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); - - SALOMEDS::GenericAttribute_var aFindAttr; - bool hasAttr = aSObj->FindAttribute(aFindAttr, "AttributeString"); - if(VARIABLE_DEBUG) - cout<<"Find Attribute "<FindOrCreateAttribute( aSObj, "AttributeString"); - SALOMEDS::AttributeString_var aStringAttr = SALOMEDS::AttributeString::_narrow(anAttr); - - TCollection_AsciiString aNewParams; - TCollection_AsciiString aOldParameters(aStringAttr->Value()); - TCollection_AsciiString anInputParams(ParseParameters(theParameters)); - - if(!hasAttr) - aNewParams = anInputParams; - else - aNewParams = aOldParameters+"|"+anInputParams; - - if(VARIABLE_DEBUG) + myLastParameters.clear(); + int pos = 0, prevPos = 0, len = strlen( theParameters ); + if ( len == 0 ) return; + while ( pos <= len ) + { + if ( pos == len || theParameters[pos] == ':' ) { - cout<<"Input Parameters : "<IsVariable( val.c_str() )) + val.clear(); + myLastParameters.push_back( val ); + } + else + { + myLastParameters.push_back(""); + } + prevPos = pos+1; } - - - aStringAttr->SetValue( aNewParams.ToCString() ); + ++pos; + } + return; + + // OLD VARIANT + + // if(VARIABLE_DEBUG) + // cout<<"UpdateParameters : "<_is_nil() || CORBA::is_nil(theObject)) + // return; + + // SALOMEDS::SObject_var aSObj = ObjectToSObject(aStudy,theObject); + // if(aSObj->_is_nil()) + // return; + + // SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); + + // SALOMEDS::GenericAttribute_var aFindAttr; + // bool hasAttr = aSObj->FindAttribute(aFindAttr, "AttributeString"); + // if(VARIABLE_DEBUG) + // cout<<"Find Attribute "<FindOrCreateAttribute( aSObj, "AttributeString"); + // SALOMEDS::AttributeString_var aStringAttr = SALOMEDS::AttributeString::_narrow(anAttr); + + // CORBA::String_var oldparVar = aStringAttr->Value(); + // CORBA::String_var inpparVar = ParseParameters(theParameters); + // TCollection_AsciiString aNewParams; + // TCollection_AsciiString aOldParameters(oldparVar.inout()); + // TCollection_AsciiString anInputParams(inpparVar.inout()); + // if(!hasAttr) + // aNewParams = anInputParams; + // else + // { + // int pos = aOldParameters.SearchFromEnd("|"); + // if(pos==-1) pos = 0; + // TCollection_AsciiString previousParamFull(aOldParameters.Split(pos)); + // TCollection_AsciiString previousParam(previousParamFull); + // TCollection_AsciiString theRepet("1"); + // pos = previousParam.SearchFromEnd(";*="); + // if(pos >= 0) + // { + // theRepet = previousParam.Split(pos+2); + // pos = pos-1; + // if(pos==-1) pos = 0; + // previousParam.Split(pos); + // } + // if(previousParam == anInputParams) + // { + // theRepet = theRepet.IntegerValue()+1; + // aNewParams = aOldParameters + previousParam + ";*=" + theRepet; + // } + // else + // { + // aNewParams = aOldParameters + previousParamFull + "|" + anInputParams; + // } + // } + + // if(VARIABLE_DEBUG) + // { + // cout<<"Input Parameters : "<SetValue( aNewParams.ToCString() ); } //======================================================================= //function : ParseParameters -//purpose : +//purpose : Replace variables by their values //======================================================================= char* SMESH_Gen_i::ParseParameters(const char* theParameters) { - const char* aParameters = CORBA::string_dup(theParameters); + //const char* aParameters = theParameters; +// const char* aParameters = CORBA::string_dup(theParameters); TCollection_AsciiString anInputParams; - SALOMEDS::Study_ptr aStudy = GetCurrentStudy(); + SALOMEDS::Study_var aStudy = GetCurrentStudy(); if( !aStudy->_is_nil() ) { - SALOMEDS::ListOfListOfStrings_var aSections = aStudy->ParseVariables(aParameters); - for(int j=0;jlength();j++) { - SALOMEDS::ListOfStrings aVars= aSections[j]; - for(int i=0;iIsVariable(aVars[i].in()) ? - TCollection_AsciiString(aVars[i].in()) : TCollection_AsciiString(""); - if(i != aVars.length()-1) - anInputParams+=":"; +// SALOMEDS::ListOfListOfStrings_var aSections = aStudy->ParseVariables(theParameters); +// for(int j=0;jlength();j++) { +// SALOMEDS::ListOfStrings aVars= aSections[j]; +// for(int i=0;iIsVariable(aVars[i].in()) ? +// TCollection_AsciiString(aVars[i].in()) : TCollection_AsciiString(""); +// if(i != aVars.length()-1) +// anInputParams+=":"; +// } +// if(j!=aSections->length()-1) +// anInputParams+="|"; +// } + TCollection_AsciiString paramStr( theParameters ); + static TCollection_AsciiString separators(":|"); + int beg = 0, end; + char sep, *pParams = (char*)paramStr.ToCString(); + while ( beg < paramStr.Length() ) + { + end = beg-1; + while ( ++end < paramStr.Length() ) + if ( pParams[end] == ':' || pParams[end] == '|') + break; + if ( end < paramStr.Length()) + { + sep = pParams[end]; + pParams[end] = '\0'; } - if(j!=aSections->length()-1) - anInputParams+="|"; + if ( aStudy->IsVariable( pParams+beg )) + anInputParams += pParams+beg; + if ( end < paramStr.Length() ) + anInputParams += sep; + else + break; + beg = end + 1; } } return CORBA::string_dup(anInputParams.ToCString()); diff --git a/src/SMESH_I/SMESH_Group_i.cxx b/src/SMESH_I/SMESH_Group_i.cxx index 045e691b9..1b99653c6 100644 --- a/src/SMESH_I/SMESH_Group_i.cxx +++ b/src/SMESH_I/SMESH_Group_i.cxx @@ -1,39 +1,45 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : SMESH_Group_i.cxx // Author : Sergey ANIKIN, OCC // Module : SMESH // #include "SMESH_Group_i.hxx" -#include "SMESH_Mesh_i.hxx" -#include "SMESH_Gen_i.hxx" -#include "SMESH_Group.hxx" + +#include "SMDSAbs_ElementType.hxx" #include "SMESHDS_Group.hxx" +#include "SMESHDS_GroupOnFilter.hxx" #include "SMESHDS_GroupOnGeom.hxx" -#include "SMDSAbs_ElementType.hxx" - +#include "SMESH_Comment.hxx" #include "SMESH_Filter_i.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_Mesh_i.hxx" #include "SMESH_PythonDump.hxx" +#include "SMESH_PreMeshInfo.hxx" + +#include CORBA_SERVER_HEADER(SMESH_Filter) #include "utilities.h" @@ -45,30 +51,48 @@ using namespace SMESH; */ //============================================================================= -SMESH_GroupBase_i::SMESH_GroupBase_i( PortableServer::POA_ptr thePOA, SMESH_Mesh_i* theMeshServant, const int theLocalID ) +SMESH_GroupBase_i::SMESH_GroupBase_i( PortableServer::POA_ptr thePOA, + SMESH_Mesh_i* theMeshServant, + const int theLocalID ) : SALOME::GenericObj_i( thePOA ), myMeshServant( theMeshServant ), - myLocalID( theLocalID ) + myLocalID( theLocalID ), + myNbNodes(-1), + myGroupDSTic(0), + myPreMeshInfo(NULL) { // PAL7962: san -- To ensure correct mapping of servant and correct reference counting in GenericObj_i, // servant activation is performed by SMESH_Mesh_i::createGroup() // thePOA->activate_object( this ); } -SMESH_Group_i::SMESH_Group_i( PortableServer::POA_ptr thePOA, SMESH_Mesh_i* theMeshServant, const int theLocalID ) +SMESH_Group_i::SMESH_Group_i( PortableServer::POA_ptr thePOA, + SMESH_Mesh_i* theMeshServant, + const int theLocalID ) : SALOME::GenericObj_i( thePOA ), SMESH_GroupBase_i( thePOA, theMeshServant, theLocalID ) { //MESSAGE("SMESH_Group_i; this = "<FullLoadFromFile(); + SMESHDS_GroupBase* aGroupDS = GetGroupDS(); if (aGroupDS) return aGroupDS->Contains(theID); @@ -251,6 +291,9 @@ CORBA::Boolean SMESH_GroupBase_i::Contains( CORBA::Long theID ) CORBA::Long SMESH_Group_i::Add( const SMESH::long_array& theIDs ) { + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + // Update Python script TPythonDump() << "nbAdd = " << _this() << ".Add( " << theIDs << " )"; @@ -277,6 +320,9 @@ CORBA::Long SMESH_Group_i::Add( const SMESH::long_array& theIDs ) CORBA::Long SMESH_Group_i::Remove( const SMESH::long_array& theIDs ) { + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + // Update Python script TPythonDump() << "nbDel = " << _this() << ".Remove( " << theIDs << " )"; @@ -305,8 +351,8 @@ typedef bool (SMESHDS_Group::*TFunChangeGroup)(const int); CORBA::Long ChangeByPredicate( SMESH::Predicate_i* thePredicate, - SMESHDS_GroupBase* theGroupBase, - TFunChangeGroup theFun) + SMESHDS_GroupBase* theGroupBase, + TFunChangeGroup theFun) { CORBA::Long aNb = 0; if(SMESHDS_Group* aGroupDS = dynamic_cast(theGroupBase)){ @@ -317,7 +363,7 @@ ChangeByPredicate( SMESH::Predicate_i* thePredicate, CORBA::Long i = 0, iEnd = aSequence.size(); for(; i < iEnd; i++) if((aGroupDS->*theFun)(aSequence[i])) - aNb++; + aNb++; return aNb; } return aNb; @@ -327,6 +373,9 @@ CORBA::Long SMESH_Group_i:: AddByPredicate( SMESH::Predicate_ptr thePredicate ) { + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + if(SMESH::Predicate_i* aPredicate = SMESH::GetPredicate(thePredicate)){ TPythonDump()<<_this()<<".AddByPredicate("<FullLoadFromFile(); + if(SMESH::Predicate_i* aPredicate = SMESH::GetPredicate(thePredicate)){ TPythonDump()<<_this()<<".RemoveByPredicate("<FullLoadFromFile(); + + TPythonDump pd; + long nbAdd = 0; + SMESHDS_Group* aGroupDS = dynamic_cast( GetGroupDS() ); + if (aGroupDS) { + SMESH::long_array_var anIds; + SMESH::SMESH_GroupBase_var group = SMESH::SMESH_GroupBase::_narrow(theSource); + SMESH::SMESH_Mesh_var mesh = SMESH::SMESH_Mesh::_narrow(theSource); + SMESH::SMESH_subMesh_var submesh = SMESH::SMESH_subMesh::_narrow(theSource); + SMESH::Filter_var filter = SMESH::Filter::_narrow(theSource); + if ( !group->_is_nil()) + anIds = group->GetType()==GetType() ? theSource->GetIDs() : new SMESH::long_array(); + else if ( !mesh->_is_nil() ) + anIds = mesh->GetElementsByType( GetType() ); + else if ( !submesh->_is_nil()) + anIds = submesh->GetElementsByType( GetType() ); + else if ( !filter->_is_nil() ) { + filter->SetMesh( GetMeshServant()->_this() ); + anIds = filter->GetElementType()==GetType() ? theSource->GetIDs() : new SMESH::long_array(); + } + else + anIds = theSource->GetIDs(); + for ( int i = 0, total = anIds->length(); i < total; i++ ) { + if ( aGroupDS->Add((int)anIds[i]) ) nbAdd++; + } + } + + // Update Python script + pd << "nbAdd = " << _this() << ".AddFrom( " << theSource << " )"; + + return nbAdd; +} + //============================================================================= /*! * @@ -353,6 +442,9 @@ RemoveByPredicate( SMESH::Predicate_ptr thePredicate ) CORBA::Long SMESH_GroupBase_i::GetID( CORBA::Long theIndex ) { + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + SMESHDS_GroupBase* aGroupDS = GetGroupDS(); if (aGroupDS) return aGroupDS->GetID(theIndex); @@ -368,30 +460,113 @@ CORBA::Long SMESH_GroupBase_i::GetID( CORBA::Long theIndex ) SMESH::long_array* SMESH_GroupBase_i::GetListOfID() { + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aRes = new SMESH::long_array(); SMESHDS_GroupBase* aGroupDS = GetGroupDS(); - if (aGroupDS) { + if (aGroupDS) + { int aSize = aGroupDS->Extent(); aRes->length(aSize); for (int i = 0; i < aSize; i++) aRes[i] = aGroupDS->GetID(i+1); - return aRes._retn(); + + if ( 0 < aSize && aSize < 100 ) // for comfortable testing ;) + std::sort( &aRes[0], &aRes[0]+aSize ); } MESSAGE("get list of IDs of a vague group"); return aRes._retn(); } -//============================================================================= +namespace +{ + //================================================================================ + /*! + * \brief return nodes of elements pointered by iterator + */ + //================================================================================ + + void getNodesOfElements(SMDS_ElemIteratorPtr elemIt, + set& nodes) + { + while ( elemIt->more() ) + { + const SMDS_MeshElement* e = elemIt->next(); + nodes.insert( e->begin_nodes(), e->end_nodes() ); + } + } +} + +//================================================================================ /*! - * + * \brief return the number of nodes of cells included to the group */ -//============================================================================= -SMESH::SMESH_Mesh_ptr SMESH_GroupBase_i::GetMesh() +//================================================================================ + +CORBA::Long SMESH_GroupBase_i::GetNumberOfNodes() { - SMESH::SMESH_Mesh_var aMesh; - if ( myMeshServant ) - aMesh = SMESH::SMESH_Mesh::_narrow( myMeshServant->_this() ); - return aMesh._retn(); + if ( GetType() == SMESH::NODE ) + return Size(); + + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + + if ( SMESHDS_GroupBase* g = GetGroupDS()) + { + if ( myNbNodes < 0 || g->GetTic() != myGroupDSTic ) + { + set nodes; + getNodesOfElements( g->GetElements(), nodes ); + myNbNodes = nodes.size(); + myGroupDSTic = g->GetTic(); + } + } + return myNbNodes; +} + +//================================================================================ +/*! + * \brief Return true if GetNumberOfNodes() won't take a long time for computation + */ +//================================================================================ + +CORBA::Boolean SMESH_GroupBase_i::IsNodeInfoAvailable() +{ + if ( GetType() == SMESH::NODE/* || Size() < 100000 */) + return true; + if ( myPreMeshInfo ) + return false; + if ( SMESHDS_GroupBase* g = GetGroupDS()) + return ( myNbNodes > -1 && g->GetTic() == myGroupDSTic); + return false; +} + +//================================================================================ +/*! + * \brief Return IDs of nodes of cells included to the group + */ +//================================================================================ + +SMESH::long_array* SMESH_GroupBase_i::GetNodeIDs() +{ + if ( GetType() == SMESH::NODE ) + return GetListOfID(); + + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + + SMESH::long_array_var aRes = new SMESH::long_array(); + if ( SMESHDS_GroupBase* g = GetGroupDS()) + { + set nodes; + getNodesOfElements( g->GetElements(), nodes ); + aRes->length( nodes.size() ); + set::iterator nIt = nodes.begin(), nEnd = nodes.end(); + for ( int i = 0; nIt != nEnd; ++nIt, ++i ) + aRes[i] = (*nIt)->GetID(); + } + return aRes._retn(); } //============================================================================= @@ -399,10 +574,12 @@ SMESH::SMESH_Mesh_ptr SMESH_GroupBase_i::GetMesh() * */ //============================================================================= -SMESH::long_array* SMESH_GroupBase_i::GetIDs() +SMESH::SMESH_Mesh_ptr SMESH_GroupBase_i::GetMesh() { - SMESH::long_array_var aResult = GetListOfID(); - return aResult._retn(); + SMESH::SMESH_Mesh_var aMesh; + if ( myMeshServant ) + aMesh = SMESH::SMESH_Mesh::_narrow( myMeshServant->_this() ); + return aMesh._retn(); } //======================================================================= @@ -454,10 +631,13 @@ void SMESH_GroupBase_i::SetColor(const SALOMEDS::Color& color) if (aGroupDS) { Quantity_Color aQColor( color.R, color.G, color.B, Quantity_TOC_RGB ); - return aGroupDS->SetColor(aQColor); + Quantity_Color oldColor = aGroupDS->GetColor(); + if ( oldColor != aQColor ) + { + aGroupDS->SetColor(aQColor); + TPythonDump()<<_this()<<".SetColor( SALOMEDS.Color( "<SetColorGroup(color); + { + aGroupDS->SetColorGroup(color); + TPythonDump()<<_this()<<".SetColorNumber( "<GetMeshInfo(); + + SMESH::long_array_var aRes = new SMESH::long_array(); + aRes->length(SMESH::Entity_Last); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = 0; + + if ( SMESHDS_GroupBase* g = GetGroupDS()) + { + if ( g->GetType() == SMDSAbs_Node || ( myNbNodes > -1 && g->GetTic() == myGroupDSTic)) + aRes[ SMDSEntity_Node ] = GetNumberOfNodes(); + + if ( g->GetType() != SMDSAbs_Node ) + SMESH_Mesh_i::CollectMeshInfo( g->GetElements(), aRes); + } + + return aRes._retn(); +} + +//======================================================================= +//function : GetIDs +//purpose : Returns ids of members +//======================================================================= + +SMESH::long_array* SMESH_GroupBase_i::GetIDs() +{ + return GetListOfID(); +} + +//======================================================================= +//function : GetTypes +//purpose : Returns types of elements it contains +//======================================================================= + +SMESH::array_of_ElementType* SMESH_GroupBase_i::GetTypes() +{ + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + if ( !IsEmpty() ) + { + types->length( 1 ); + types[0] = GetType(); + } + return types._retn(); +} + +//======================================================================= +//function : IsMeshInfoCorrect +//purpose : * Returns false if GetMeshInfo() returns incorrect information that may +// * happen if mesh data is not yet fully loaded from the file of study. +//======================================================================= + +bool SMESH_GroupBase_i::IsMeshInfoCorrect() +{ + return myPreMeshInfo ? myPreMeshInfo->IsMeshInfoCorrect() : true; +} + +//================================================================================ +/*! + * \brief Retrieves the predicate from the filter + */ +//================================================================================ + +SMESH_PredicatePtr SMESH_GroupOnFilter_i::GetPredicate( SMESH::Filter_ptr filter ) +{ + SMESH_PredicatePtr predicate; + + if ( SMESH::Filter_i* filt_i = SMESH::DownCast< SMESH::Filter_i* >( filter )) + if ( SMESH::Predicate_i* predic_i= filt_i->GetPredicate_i() ) + predicate = predic_i->GetPredicate(); + + return predicate; +} + +//================================================================================ +/*! + * \brief Sets the filter defining group contents + */ +//================================================================================ + +void SMESH_GroupOnFilter_i::SetFilter(SMESH::Filter_ptr theFilter) +{ + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + + if ( ! myFilter->_is_nil() ) + myFilter->UnRegister(); + + myFilter = SMESH::Filter::_duplicate( theFilter ); + + if ( SMESHDS_GroupOnFilter* grDS = dynamic_cast< SMESHDS_GroupOnFilter*>( GetGroupDS() )) + grDS->SetPredicate( GetPredicate( myFilter )); + + TPythonDump()<< _this() <<".SetFilter( "<Register(); + SMESH::DownCast< SMESH::Filter_i* >( myFilter )->AddWaiter( this ); + } +} + +//================================================================================ +/*! + * \brief Returns the filter defining group contents + */ +//================================================================================ + +SMESH::Filter_ptr SMESH_GroupOnFilter_i::GetFilter() +{ + SMESH::Filter_var f = myFilter; + TPythonDump() << f << " = " << _this() << ".GetFilter()"; + return f._retn(); +} + +#define SEPAR '^' + +//================================================================================ +/*! + * \brief Return a string to be used to store group definition in the study + */ +//================================================================================ + +std::string SMESH_GroupOnFilter_i::FilterToString() const +{ + SMESH_Comment result; + SMESH::Filter::Criteria_var criteria; + if ( !myFilter->_is_nil() && myFilter->GetCriteria( criteria.out() )) + { + result << criteria->length() << SEPAR; + for ( unsigned i = 0; i < criteria->length(); ++i ) + { + // write FunctorType as string but not as number to assure correct + // persistence if enum FunctorType is modified by insertion in the middle + SMESH::Filter::Criterion& crit = criteria[ i ]; + result << SMESH::FunctorTypeToString( SMESH::FunctorType( crit.Type )) << SEPAR; + result << SMESH::FunctorTypeToString( SMESH::FunctorType( crit.Compare )) << SEPAR; + result << crit.Threshold << SEPAR; + result << crit.ThresholdStr << SEPAR; + result << crit.ThresholdID << SEPAR; + result << SMESH::FunctorTypeToString( SMESH::FunctorType( crit.UnaryOp )) << SEPAR; + result << SMESH::FunctorTypeToString( SMESH::FunctorType( crit.BinaryOp ))<< SEPAR; + result << crit.Tolerance << SEPAR; + result << crit.TypeOfElement << SEPAR; + result << crit.Precision << SEPAR; + } + } + return result; +} + +//================================================================================ +/*! + * \brief Restore the filter by the persistent string + */ +//================================================================================ + +SMESH::Filter_ptr SMESH_GroupOnFilter_i::StringToFilter(const std::string& thePersistStr ) +{ + SMESH::Filter_var filter; + + // divide thePersistStr into sub-strings + std::vector< std::string > strVec; + std::string::size_type from = 0, to; + while ( from < thePersistStr.size() ) + { + to = thePersistStr.find( SEPAR, from ); + if ( to == std::string::npos ) + break; + strVec.push_back( thePersistStr.substr( from, to-from )); + from = to+1; + } + if ( strVec.empty() || strVec[0] == "0" ) + return filter._retn(); +#undef SEPAR + + // create Criteria + int nbCrit = atoi( strVec[0].c_str() ); + SMESH::Filter::Criteria_var criteria = new SMESH::Filter::Criteria; + criteria->length( nbCrit ); + int nbStrPerCrit = ( strVec.size() - 1 ) / nbCrit; + for ( int i = 0; i < nbCrit; ++i ) + { + SMESH::Filter::Criterion& crit = criteria[ i ]; + int iStr = 1 + i * nbStrPerCrit; + crit.Type = SMESH::StringToFunctorType( strVec[ iStr++ ].c_str() ); + crit.Compare = SMESH::StringToFunctorType( strVec[ iStr++ ].c_str() ); + crit.Threshold = atof( strVec[ iStr++ ].c_str() ); + crit.ThresholdStr = strVec[ iStr++ ].c_str(); + crit.ThresholdID = strVec[ iStr++ ].c_str(); + crit.UnaryOp = SMESH::StringToFunctorType( strVec[ iStr++ ].c_str() ); + crit.BinaryOp = SMESH::StringToFunctorType( strVec[ iStr++ ].c_str() ); + crit.Tolerance = atof( strVec[ iStr++ ].c_str() ); + crit.TypeOfElement= SMESH::ElementType( atoi( strVec[ iStr++ ].c_str() )); + crit.Precision = atoi( strVec[ iStr++ ].c_str() ); + } + + // create a filter + TPythonDump pd; + SMESH::FilterManager_i* aFilterMgr = new SMESH::FilterManager_i(); + filter = aFilterMgr->CreateFilter(); + filter->SetCriteria( criteria.inout() ); + + aFilterMgr->UnRegister(); + + pd << ""; // to avoid optimizing pd out + + return filter._retn(); +} + +//================================================================================ +/*! + * \brief Destructor of SMESH_GroupOnFilter_i + */ +//================================================================================ + +SMESH_GroupOnFilter_i::~SMESH_GroupOnFilter_i() +{ + if ( ! myFilter->_is_nil() ) + { + SMESH::DownCast< SMESH::Filter_i* >( myFilter )->RemoveWaiter( this ); + myFilter->UnRegister(); + } +} + +//================================================================================ +/*! + * \brief Method calleds when a predicate of myFilter changes + */ +//================================================================================ + +void SMESH_GroupOnFilter_i::PredicateChanged() +{ + if ( myPreMeshInfo ) + myPreMeshInfo->FullLoadFromFile(); + + if ( SMESHDS_GroupOnFilter* grDS = dynamic_cast< SMESHDS_GroupOnFilter*>( GetGroupDS() )) + grDS->SetPredicate( GetPredicate( myFilter )); +} diff --git a/src/SMESH_I/SMESH_Group_i.hxx b/src/SMESH_I/SMESH_Group_i.hxx index 8e6e33f9f..dcb441057 100644 --- a/src/SMESH_I/SMESH_Group_i.hxx +++ b/src/SMESH_I/SMESH_Group_i.hxx @@ -1,34 +1,36 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : SMESH_Group_i.hxx // Author : Sergey ANIKIN, OCC // Module : SMESH -// $Header$ // #ifndef SMESH_Group_i_HeaderFile #define SMESH_Group_i_HeaderFile #include "SMESH.hxx" +#include "SMESH_Mesh_i.hxx" +#include "SMESH_Filter_i.hxx" #include #include CORBA_SERVER_HEADER(SMESH_Group) @@ -37,9 +39,9 @@ #include "SALOME_GenericObj_i.hh" -class SMESH_Mesh_i; class SMESH_Group; class SMESHDS_GroupBase; +class SMESH_PreMeshInfo; // =========== // Group Base @@ -50,8 +52,8 @@ class SMESH_I_EXPORT SMESH_GroupBase_i: { public: SMESH_GroupBase_i(PortableServer::POA_ptr thePOA, - SMESH_Mesh_i* theMeshServant, - const int theLocalID ); + SMESH_Mesh_i* theMeshServant, + const int theLocalID ); virtual ~SMESH_GroupBase_i(); // CORBA interface implementation @@ -63,11 +65,33 @@ class SMESH_I_EXPORT SMESH_GroupBase_i: CORBA::Boolean Contains(CORBA::Long elem_id); CORBA::Long GetID(CORBA::Long elem_index); SMESH::long_array* GetListOfID(); - SMESH::SMESH_Mesh_ptr GetMesh(); + SMESH::long_array* GetNodeIDs(); + CORBA::Long GetNumberOfNodes(); + CORBA::Boolean IsNodeInfoAvailable(); // for gui + + virtual SMESH::SMESH_Mesh_ptr GetMesh(); + + /*! + * Returns statistic of mesh elements + * Result array of number enityties + * Inherited from SMESH_IDSource + */ + virtual SMESH::long_array* GetMeshInfo(); // Inherited from SMESH_IDSource interface virtual SMESH::long_array* GetIDs(); + /*! + * Returns types of elements it contains + * Inherited from SMESH_IDSource interface + */ + virtual SMESH::array_of_ElementType* GetTypes(); + /*! + * Returns false if GetMeshInfo() returns incorrect information that may + * happen if mesh data is not yet fully loaded from the file of study. + */ + virtual bool IsMeshInfoCorrect(); + // Internal C++ interface int GetLocalID() const { return myLocalID; } SMESH_Mesh_i* GetMeshServant() const { return myMeshServant; } @@ -80,9 +104,20 @@ class SMESH_I_EXPORT SMESH_GroupBase_i: void SetColorNumber(CORBA::Long color); CORBA::Long GetColorNumber(); +protected: + + SMESH_PreMeshInfo* & changePreMeshInfo() { return myPreMeshInfo; } + SMESH_PreMeshInfo* myPreMeshInfo; // mesh info before full loading from study file + friend class SMESH_PreMeshInfo; + private: SMESH_Mesh_i* myMeshServant; int myLocalID; + + void changeLocalId(int localId) { myLocalID = localId; } + friend class SMESH_Mesh_i; + + int myNbNodes, myGroupDSTic; }; // ====== @@ -94,8 +129,9 @@ class SMESH_I_EXPORT SMESH_Group_i: public SMESH_GroupBase_i { public: - SMESH_Group_i( PortableServer::POA_ptr thePOA, SMESH_Mesh_i* theMeshServant, const int theLocalID ); - + SMESH_Group_i( PortableServer::POA_ptr thePOA, + SMESH_Mesh_i* theMeshServant, + const int theLocalID ); // CORBA interface implementation void Clear(); CORBA::Long Add( const SMESH::long_array& theIDs ); @@ -103,6 +139,8 @@ class SMESH_I_EXPORT SMESH_Group_i: CORBA::Long AddByPredicate( SMESH::Predicate_ptr thePredicate ); CORBA::Long RemoveByPredicate( SMESH::Predicate_ptr thePredicate ); + + CORBA::Long AddFrom( SMESH::SMESH_IDSource_ptr theSource ); }; // ========================= @@ -114,9 +152,42 @@ class SMESH_I_EXPORT SMESH_GroupOnGeom_i: public SMESH_GroupBase_i { public: - SMESH_GroupOnGeom_i( PortableServer::POA_ptr thePOA, SMESH_Mesh_i* theMeshServant, const int theLocalID ); - + SMESH_GroupOnGeom_i( PortableServer::POA_ptr thePOA, + SMESH_Mesh_i* theMeshServant, + const int theLocalID ); // CORBA interface implementation GEOM::GEOM_Object_ptr GetShape(); }; + +// ========================= +// Group deined by filter +// ========================= + +class SMESH_I_EXPORT SMESH_GroupOnFilter_i: + public virtual POA_SMESH::SMESH_GroupOnFilter, + public SMESH_GroupBase_i, + public SMESH::Filter_i::TPredicateChangeWaiter +{ + public: + SMESH_GroupOnFilter_i( PortableServer::POA_ptr thePOA, + SMESH_Mesh_i* theMeshServant, + const int theLocalID ); + ~SMESH_GroupOnFilter_i(); + + // Persistence + static SMESH::Filter_ptr StringToFilter(const std::string& thePersistentString ); + std::string FilterToString() const; + + static SMESH_PredicatePtr GetPredicate( SMESH::Filter_ptr ); + + // CORBA interface implementation + void SetFilter(SMESH::Filter_ptr theFilter); + SMESH::Filter_ptr GetFilter(); + + // method of SMESH::Filter_i::TPredicateChangeWaiter + virtual void PredicateChanged(); + + private: + SMESH::Filter_var myFilter; +}; #endif diff --git a/src/SMESH_I/SMESH_Hypothesis_i.cxx b/src/SMESH_I/SMESH_Hypothesis_i.cxx index b677e647b..96131044b 100644 --- a/src/SMESH_I/SMESH_Hypothesis_i.cxx +++ b/src/SMESH_I/SMESH_Hypothesis_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Hypothesis_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include #include @@ -138,6 +138,78 @@ bool SMESH_Hypothesis_i::IsPublished(){ return res; } +//================================================================================ +/*! + * \brief Set the pramIndex-th parameter + */ +//================================================================================ + +void SMESH_Hypothesis_i::SetVarParameter (const char* theParameter, + const char* theMethod) +{ + if ( SMESH_Gen_i *gen = SMESH_Gen_i::GetSMESHGen() ) + { + gen->UpdateParameters(theParameter); + + const std::vector< std::string >& pars = gen->GetLastParameters(); + if ( !pars.empty() ) + myMethod2VarParams[ theMethod ] = pars[0]; + } +} + +//================================================================================ +/*! + * \brief Return the pramIndex-th variable parameter used for Hypothesis creation + */ +//================================================================================ + +char* SMESH_Hypothesis_i::GetVarParameter (const char* theMethod) +{ + if ( myMethod2VarParams.count("needs update by old study")) + { + // restore myMethod2VarParams by old study + myMethod2VarParams.clear(); + if ( SMESH_Gen_i* gen = SMESH_Gen_i::GetSMESHGen() ) + { + CORBA::String_var oldparVar = gen->GetParameters( _this() ); + setOldParameters( oldparVar.in() ); + } + } + std::map< std::string, std::string >::iterator meth_param = myMethod2VarParams.find( theMethod ); + if ( meth_param != myMethod2VarParams.end() ) + return CORBA::string_dup( meth_param->second.c_str() ); + + return CORBA::string_dup(""); +} + +//================================================================================ +/*! + * \brief Restore myMethod2VarParams by parameters stored in an old study + */ +//================================================================================ + +void SMESH_Hypothesis_i::setOldParameters (const char* theParameters) +{ + if ( SMESH_Gen_i* gen = SMESH_Gen_i::GetSMESHGen() ) + { + TCollection_AsciiString aOldParameters(theParameters); + int pos = aOldParameters.SearchFromEnd("|"); + if ( pos >= 0 ) aOldParameters = aOldParameters.Split(pos); + pos = aOldParameters.SearchFromEnd(";*="); + if ( pos >= 0 ) aOldParameters.Split(pos-1); + gen->UpdateParameters( aOldParameters.ToCString() ); + + myMethod2VarParams.clear(); + const std::vector< std::string >& pars = gen->GetLastParameters(); + for ( size_t i = 0; i < pars.size(); ++i ) + { + std::string meth = getMethodOfParameter( i, pars.size() ); + myMethod2VarParams[ meth ] = pars[i]; + } + gen->UpdateParameters(""); // clear params + } +} + //============================================================================= /*! * SMESH_Hypothesis_i::SetParameters() @@ -147,14 +219,15 @@ bool SMESH_Hypothesis_i::IsPublished(){ void SMESH_Hypothesis_i::SetParameters(const char* theParameters) { SMESH_Gen_i *gen = SMESH_Gen_i::GetSMESHGen(); - char * aParameters = CORBA::string_dup(theParameters); + //char * aParameters = CORBA::string_dup(theParameters); if(gen){ - if(IsPublished()) { - SMESH_Gen_i::GetSMESHGen()->UpdateParameters(SMESH::SMESH_Hypothesis::_narrow(_this()),aParameters); - } - else { - myBaseImpl->SetParameters(gen->ParseParameters(aParameters)); - } + gen->UpdateParameters(theParameters); + // if(IsPublished()) { + // SMESH_Gen_i::GetSMESHGen()->UpdateParameters(SMESH::SMESH_Hypothesis::_narrow(_this()),aParameters); + // } + // else { + // myBaseImpl->SetParameters(gen->ParseParameters(aParameters)); + // } } } @@ -230,9 +303,10 @@ void SMESH_Hypothesis_i::SetLastParameters(const char* theParameters) //============================================================================= void SMESH_Hypothesis_i::ClearParameters() { - if(!IsPublished()) { - myBaseImpl->ClearParameters(); - } + myMethod2VarParams.clear(); + // if(!IsPublished()) { + // myBaseImpl->ClearParameters(); + // } } //============================================================================= @@ -245,7 +319,6 @@ void SMESH_Hypothesis_i::ClearParameters() ::SMESH_Hypothesis* SMESH_Hypothesis_i::GetImpl() { - //MESSAGE( "SMESH_Hypothesis_i::GetImpl" ); return myBaseImpl; } @@ -259,8 +332,18 @@ void SMESH_Hypothesis_i::ClearParameters() char* SMESH_Hypothesis_i::SaveTo() { - MESSAGE( "SMESH_Hypothesis_i::SaveTo" ); std::ostringstream os; + + // assure that parameters are loaded from an old study + CORBA::String_var p = GetVarParameter(""); + + os << "VARS " << myMethod2VarParams.size() << " "; + std::map< std::string, std::string >::iterator meth_param = myMethod2VarParams.begin(); + for ( ; meth_param != myMethod2VarParams.end(); ++meth_param ) + os << meth_param->first << " " + << meth_param->second.size() << " " + << meth_param->second << " "; + myBaseImpl->SaveTo( os ); return CORBA::string_dup( os.str().c_str() ); } @@ -275,7 +358,44 @@ char* SMESH_Hypothesis_i::SaveTo() void SMESH_Hypothesis_i::LoadFrom( const char* theStream ) { - MESSAGE( "SMESH_Hypothesis_i::LoadFrom" ); std::istringstream is( theStream ); + if ( strncmp( theStream, "VARS", 4 ) == 0 ) + { + int nbVars, len; + char str[256]; + std::string meth; + is >> str >> nbVars; + for ( int i = 0; i < nbVars; ++i ) + { + is >> meth >> len; + if ( len < 256 ) + { + is.get( str, len + 2 ); // 2 - to read at least 1 white space + if ( len > 0 ) + myMethod2VarParams[ meth ] = std::string( str+1, len ); + } + } + } + else + { + // we can't restore myMethod2VarParams by old study here because SObject + // isn't yet bound to _this() + myMethod2VarParams["needs update by old study"] = "yes"; + } + myBaseImpl->LoadFrom( is ); + + // let listeners know about loading (issue 0020918) + myBaseImpl->NotifySubMeshesHypothesisModification(); +} + +//================================================================================ +/*! + * \brief This mesthod is called after completion of loading a study + */ +//================================================================================ + +void SMESH_Hypothesis_i::UpdateAsMeshesRestored() +{ + // for hyps needing full data restored } diff --git a/src/SMESH_I/SMESH_Hypothesis_i.hxx b/src/SMESH_I/SMESH_Hypothesis_i.hxx index 13bd7742f..593ed85ca 100644 --- a/src/SMESH_I/SMESH_Hypothesis_i.hxx +++ b/src/SMESH_I/SMESH_Hypothesis_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Hypothesis_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_HYPOTHESIS_I_HXX_ #define _SMESH_HYPOTHESIS_I_HXX_ @@ -38,6 +38,11 @@ #include "SMESH_Gen.hxx" +#include +#include + +class TCollection_AsciiString; + // ====================================================== // Generic hypothesis // ====================================================== @@ -65,6 +70,14 @@ public: // Get unique id of hypothesis CORBA::Long GetId(); + // Set the variable parameter; method is a name of method setting this parameter. + // This method must be called by the hypothesis creator just before calling hyp->method() + void SetVarParameter (const char* parameter, const char* method); + + // Return the variable parameter used for Hypothesis creation by name of method + // setting this parameter + char* GetVarParameter (const char* methodName); + // Set list of parameters separated by ":" symbol, used for Hypothesis creation void SetParameters (const char* theParameters); @@ -90,9 +103,27 @@ public: // Persistence virtual char* SaveTo(); virtual void LoadFrom( const char* theStream ); - + virtual void UpdateAsMeshesRestored(); // for hyps needing full data restored + protected: - ::SMESH_Hypothesis* myBaseImpl; // base hypothesis implementation + ::SMESH_Hypothesis* myBaseImpl; // base hypothesis implementation + + std::map< std::string, std::string > myMethod2VarParams; // variable parameters + + + public: + // Methods for backward compatibility of notebook variables + + // restore myMethod2VarParams by parameters stored in an old study + virtual void setOldParameters (const char* theParameters); + + // method used to convert variable parameters stored in an old study + // into myMethod2VarParams. It should return a method name for an index of + // variable parameters. Index is countered from zero + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const { return ""; } + + // method intended to remove explicit treatment of Netgen hypotheses from SMESH_NoteBook + virtual int getParamIndex(const TCollection_AsciiString& method, int nbVars) const { return -1; } }; // ====================================================== diff --git a/src/SMESH_I/SMESH_MEDFamily_i.cxx b/src/SMESH_I/SMESH_MEDFamily_i.cxx index 35bacddc3..3218fd486 100644 --- a/src/SMESH_I/SMESH_MEDFamily_i.cxx +++ b/src/SMESH_I/SMESH_MEDFamily_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDFamily_i.cxx // Module : SMESH @@ -77,7 +78,7 @@ SMESH_MEDFamily_i::~SMESH_MEDFamily_i() */ //============================================================================= SMESH_MEDFamily_i::SMESH_MEDFamily_i(int identifier, SMESH_subMesh_i* sm, - string name, string description, SALOME_MED::medEntityMesh entity): + string name, string description, SALOME_MED::medEntityMesh entity): SMESH_MEDSupport_i( sm, name, description, entity ), _subMesh_i(sm), @@ -101,9 +102,9 @@ SMESH_MEDFamily_i::SMESH_MEDFamily_i(int identifier, SMESH_subMesh_i* sm, CORBA::Long SMESH_MEDFamily_i::getIdentifier() throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); return _identifier; } @@ -115,9 +116,9 @@ throw (SALOME::SALOME_Exception) CORBA::Long SMESH_MEDFamily_i::getNumberOfAttributes() throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); return _numberOfAttribute; } //============================================================================= @@ -125,20 +126,20 @@ throw (SALOME::SALOME_Exception) * CORBA: Accessor for attributes identifiers */ //============================================================================= -SALOME_MED::long_array* SMESH_MEDFamily_i::getAttributesIdentifiers() +SALOME_TYPES::ListOfLong* SMESH_MEDFamily_i::getAttributesIdentifiers() throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); }; - SALOME_MED::long_array_var myseq= new SALOME_MED::long_array; + SALOME_TYPES::ListOfLong_var myseq= new SALOME_TYPES::ListOfLong; myseq->length(_numberOfAttribute); for (int i=0;i<_numberOfAttribute;i++) { @@ -155,17 +156,17 @@ throw (SALOME::SALOME_Exception) CORBA::Long SMESH_MEDFamily_i::getAttributeIdentifier(CORBA::Long i) throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); }; ASSERT (i <= _numberOfAttribute); return _attributeIdentifier[i]; @@ -175,21 +176,21 @@ CORBA::Long SMESH_MEDFamily_i::getAttributeIdentifier(CORBA::Long i) * CORBA: Accessor for attributes values */ //============================================================================= -SALOME_MED::long_array* SMESH_MEDFamily_i::getAttributesValues() +SALOME_TYPES::ListOfLong* SMESH_MEDFamily_i::getAttributesValues() throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); }; - SALOME_MED::long_array_var myseq= new SALOME_MED::long_array; + SALOME_TYPES::ListOfLong_var myseq= new SALOME_TYPES::ListOfLong; myseq->length(_numberOfAttribute); for (int i=0;i<_numberOfAttribute;i++) { @@ -205,14 +206,14 @@ SALOME_MED::long_array* SMESH_MEDFamily_i::getAttributesValues() CORBA::Long SMESH_MEDFamily_i::getAttributeValue(CORBA::Long i) throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); } ASSERT (i <= _numberOfAttribute); @@ -223,19 +224,19 @@ CORBA::Long SMESH_MEDFamily_i::getAttributeValue(CORBA::Long i) * CORBA: Accessor for attributes desriptions */ //============================================================================= -SALOME_MED::string_array * SMESH_MEDFamily_i::getAttributesDescriptions() +SALOME_TYPES::ListOfString * SMESH_MEDFamily_i::getAttributesDescriptions() throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); } - SALOME_MED::string_array_var myseq = new SALOME_MED::string_array; + SALOME_TYPES::ListOfString_var myseq = new SALOME_TYPES::ListOfString; for (int i=0;i<_numberOfAttribute;i++) { myseq[i]=CORBA::string_dup(_attributeDescription[i].c_str()); @@ -250,14 +251,14 @@ SALOME_MED::string_array * SMESH_MEDFamily_i::getAttributesDescriptions() char * SMESH_MEDFamily_i::getAttributeDescription( CORBA::Long i) throw (SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) + if (_subMesh==NULL) THROW_SALOME_CORBA_EXCEPTION("No associated Family",\ - SALOME::INTERNAL_ERROR); + SALOME::INTERNAL_ERROR); if (_numberOfAttribute == 0) { MESSAGE("Les familles SMESH n ont pas d attribut"); THROW_SALOME_CORBA_EXCEPTION("No attributes"\ - ,SALOME::BAD_PARAM); + ,SALOME::BAD_PARAM); } ASSERT (i <= _numberOfAttribute); return CORBA::string_dup(_attributeDescription[i].c_str()); @@ -291,7 +292,7 @@ char * SMESH_MEDFamily_i::getGroupName( CORBA::Long i) * CORBA: Accessor for all the groups name */ //============================================================================= -SALOME_MED::string_array* SMESH_MEDFamily_i::getGroupsNames() +SALOME_TYPES::ListOfString* SMESH_MEDFamily_i::getGroupsNames() throw (SALOME::SALOME_Exception) { MESSAGE("!!! NOT YET IMPLEMENTED !!!!"); diff --git a/src/SMESH_I/SMESH_MEDFamily_i.hxx b/src/SMESH_I/SMESH_MEDFamily_i.hxx index 2b850fc8a..c96df39c0 100644 --- a/src/SMESH_I/SMESH_MEDFamily_i.hxx +++ b/src/SMESH_I/SMESH_MEDFamily_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDFamily_i.hxx // Module : SMESH @@ -56,7 +57,7 @@ public : // Constructors and associated internal methods SMESH_MEDFamily_i(int identifier, SMESH_subMesh_i* sm, - std::string name, std::string description, SALOME_MED::medEntityMesh entity ); + std::string name, std::string description, SALOME_MED::medEntityMesh entity ); SMESH_MEDFamily_i(const SMESH_MEDFamily_i & f); // IDL Methods @@ -69,15 +70,15 @@ public : throw (SALOME::SALOME_Exception); CORBA::Long getNumberOfAttributes() throw (SALOME::SALOME_Exception); - SALOME_MED::long_array* getAttributesIdentifiers() + SALOME_TYPES::ListOfLong* getAttributesIdentifiers() throw (SALOME::SALOME_Exception); CORBA::Long getAttributeIdentifier(CORBA::Long i) throw (SALOME::SALOME_Exception); - SALOME_MED::long_array* getAttributesValues() + SALOME_TYPES::ListOfLong* getAttributesValues() throw (SALOME::SALOME_Exception); CORBA::Long getAttributeValue(CORBA::Long i) throw (SALOME::SALOME_Exception); - SALOME_MED::string_array* getAttributesDescriptions() + SALOME_TYPES::ListOfString* getAttributesDescriptions() throw (SALOME::SALOME_Exception); char* getAttributeDescription( CORBA::Long i) throw (SALOME::SALOME_Exception); @@ -85,7 +86,7 @@ public : throw (SALOME::SALOME_Exception); char * getGroupName( CORBA::Long i) throw (SALOME::SALOME_Exception); - SALOME_MED::string_array* getGroupsNames() + SALOME_TYPES::ListOfString* getGroupsNames() throw (SALOME::SALOME_Exception); }; #endif /* MED_FAMILY_I_HXX_ */ diff --git a/src/SMESH_I/SMESH_MEDMesh_i.cxx b/src/SMESH_I/SMESH_MEDMesh_i.cxx index 9ee46fda3..823c8a037 100644 --- a/src/SMESH_I/SMESH_MEDMesh_i.cxx +++ b/src/SMESH_I/SMESH_MEDMesh_i.cxx @@ -1,29 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDMesh_i.cxx // Module : SMESH // #include "SMESH_MEDMesh_i.hxx" +#include "SMESH_Gen_i.hxx" #include "SMESH_Mesh_i.hxx" #include "SMESHDS_Mesh.hxx" @@ -69,8 +71,8 @@ using namespace std; // PN Est-ce un const ? SMESH_MEDMesh_i::SMESH_MEDMesh_i() { - BEGIN_OF("Default Constructor SMESH_MEDMesh_i"); - END_OF("Default Constructor SMESH_MEDMesh_i"); + BEGIN_OF("Default Constructor SMESH_MEDMesh_i"); + END_OF("Default Constructor SMESH_MEDMesh_i"); } //============================================================================= @@ -88,18 +90,18 @@ SMESH_MEDMesh_i::~SMESH_MEDMesh_i() */ //============================================================================= SMESH_MEDMesh_i::SMESH_MEDMesh_i(::SMESH_Mesh_i * m_i):_meshId(""), - _compte(false), - _creeFamily(false), - _famIdent(0), - _indexElts(0), - _indexEnts(0) + _compte(false), + _creeFamily(false), + _famIdent(0), + _indexElts(0), + _indexEnts(0) { - BEGIN_OF("Constructor SMESH_MEDMesh_i"); + BEGIN_OF("Constructor SMESH_MEDMesh_i"); - _mesh_i = m_i; - _meshDS = _mesh_i->GetImpl().GetMeshDS(); + _mesh_i = m_i; + _meshDS = _mesh_i->GetImpl().GetMeshDS(); - END_OF("Constructor SMESH_MEDMesh_i"); + END_OF("Constructor SMESH_MEDMesh_i"); } //============================================================================= @@ -109,22 +111,28 @@ SMESH_MEDMesh_i::SMESH_MEDMesh_i(::SMESH_Mesh_i * m_i):_meshId(""), //============================================================================= char *SMESH_MEDMesh_i::getName() throw(SALOME::SALOME_Exception) { - if (_meshDS == NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - - try - { - // A COMPLETER PAR LE NOM DU MAILLAGE - //return CORBA::string_dup(_mesh_i->getName().c_str()); - return CORBA::string_dup("toto"); - } - catch(...) - { - MESSAGE("Exception en accedant au nom"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } + if (_meshDS == NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + + try + { + SMESH_Gen_i* gen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var study = gen->GetCurrentStudy(); + SALOMEDS::SObject_var meshSO = gen->ObjectToSObject( study, _mesh_i->_this()); + if ( meshSO->_is_nil() ) + return CORBA::string_dup("toto"); + + CORBA::String_var name = meshSO->GetName(); + return CORBA::string_dup( name.in() ); + } + catch(...) + { + MESSAGE("Exception en accedant au nom"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } + return 0; } //============================================================================= @@ -134,8 +142,8 @@ char *SMESH_MEDMesh_i::getName() throw(SALOME::SALOME_Exception) //============================================================================= CORBA::Long SMESH_MEDMesh_i::getCorbaIndex()throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); } //============================================================================= @@ -145,11 +153,11 @@ CORBA::Long SMESH_MEDMesh_i::getCorbaIndex()throw(SALOME::SALOME_Exception) //============================================================================= CORBA::Long SMESH_MEDMesh_i::getSpaceDimension()throw(SALOME::SALOME_Exception) { - // PN : Il semblerait que la dimension soit fixee a 3 - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - return 3; + // PN : Il semblerait que la dimension soit fixee a 3 + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + return 3; } //============================================================================= @@ -159,14 +167,14 @@ CORBA::Long SMESH_MEDMesh_i::getSpaceDimension()throw(SALOME::SALOME_Exception) //============================================================================= CORBA::Long SMESH_MEDMesh_i::getMeshDimension()throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - // PN : Il semblerait que la dimension soit fixee a 3 - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - return 3; + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + // PN : Il semblerait que la dimension soit fixee a 3 + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + return 3; } //============================================================================= /*! @@ -188,7 +196,7 @@ CORBA::Boolean SMESH_MEDMesh_i::getIsAGrid() throw (SALOME::SALOME_Exception) //============================================================================= CORBA::Boolean SMESH_MEDMesh_i::existConnectivity(SALOME_MED::medConnectivity connectivityType, - SALOME_MED::medEntityMesh entity) + SALOME_MED::medEntityMesh entity) throw (SALOME::SALOME_Exception) { MESSAGE("!!!!!! IMPLEMENTED BUT ONLY PARTIALLY !!!!!!"); @@ -218,21 +226,21 @@ CORBA::Double SMESH_MEDMesh_i::getCoordinate(CORBA::Long Number, CORBA::Long Axi //============================================================================= char *SMESH_MEDMesh_i::getCoordinatesSystem() throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - // PN : En dur. Non encore prevu - try - { - string systcoo = "CARTESIEN"; - return CORBA::string_dup(systcoo.c_str()); - } - catch(...) - { - MESSAGE("Exception en accedant au maillage"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + // PN : En dur. Non encore prevu + try + { + string systcoo = "CARTESIEN"; + return CORBA::string_dup(systcoo.c_str()); + } + catch(...) + { + MESSAGE("Exception en accedant au maillage"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } } //============================================================================= @@ -240,56 +248,56 @@ char *SMESH_MEDMesh_i::getCoordinatesSystem() throw(SALOME::SALOME_Exception) * CORBA: Accessor for Coordinates */ //============================================================================= -SALOME_MED::double_array * SMESH_MEDMesh_i::getCoordinates( - SALOME_MED::medModeSwitch typeSwitch) throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfDouble * SMESH_MEDMesh_i::getCoordinates +(SALOME_MED::medModeSwitch typeSwitch) throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - SALOME_MED::double_array_var myseq = new SALOME_MED::double_array; - try - { - // PN : En dur - int spaceDimension = 3; - int nbNodes = _meshDS->NbNodes(); - SCRUTE(nbNodes); - myseq->length(nbNodes * spaceDimension); - int i = 0; - - SMDS_NodeIteratorPtr itNodes=_meshDS->nodesIterator(); - while(itNodes->more()) - { - const SMDS_MeshNode* node = itNodes->next(); - - if (typeSwitch == SALOME_MED::MED_FULL_INTERLACE) - { - myseq[i * 3] = node->X(); - myseq[i * 3 + 1] = node->Y(); - myseq[i * 3 + 2] = node->Z(); - SCRUTE(myseq[i * 3]); - SCRUTE(myseq[i * 3 + 1]); - SCRUTE(myseq[i * 3 + 2]); - } - else - { - ASSERT(typeSwitch == SALOME_MED::MED_NO_INTERLACE); - myseq[i] = node->X(); - myseq[i + nbNodes] = node->Y(); - myseq[i + (nbNodes * 2)] = node->Z(); - SCRUTE(myseq[i]); - SCRUTE(myseq[i + nbNodes]); - SCRUTE(myseq[i + (nbNodes * 2)]); - } - i++; - } - } - catch(...) - { - MESSAGE("Exception en accedant aux coordonnees"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } - return myseq._retn(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + SALOME_TYPES::ListOfDouble_var myseq = new SALOME_TYPES::ListOfDouble; + try + { + // PN : En dur + int spaceDimension = 3; + int nbNodes = _meshDS->NbNodes(); + SCRUTE(nbNodes); + myseq->length(nbNodes * spaceDimension); + int i = 0; + + SMDS_NodeIteratorPtr itNodes=_meshDS->nodesIterator(); + while(itNodes->more()) + { + const SMDS_MeshNode* node = itNodes->next(); + + if (typeSwitch == SALOME_MED::MED_FULL_INTERLACE) + { + myseq[i * 3] = node->X(); + myseq[i * 3 + 1] = node->Y(); + myseq[i * 3 + 2] = node->Z(); + SCRUTE(myseq[i * 3]); + SCRUTE(myseq[i * 3 + 1]); + SCRUTE(myseq[i * 3 + 2]); + } + else + { + ASSERT(typeSwitch == SALOME_MED::MED_NO_INTERLACE); + myseq[i] = node->X(); + myseq[i + nbNodes] = node->Y(); + myseq[i + (nbNodes * 2)] = node->Z(); + SCRUTE(myseq[i]); + SCRUTE(myseq[i + nbNodes]); + SCRUTE(myseq[i + (nbNodes * 2)]); + } + i++; + } + } + catch(...) + { + MESSAGE("Exception en accedant aux coordonnees"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } + return myseq._retn(); } //============================================================================= @@ -297,29 +305,29 @@ SALOME_MED::double_array * SMESH_MEDMesh_i::getCoordinates( * CORBA: Accessor for Coordinates Names */ //============================================================================= -SALOME_MED::string_array * - SMESH_MEDMesh_i::getCoordinatesNames()throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfString * +SMESH_MEDMesh_i::getCoordinatesNames()throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - SALOME_MED::string_array_var myseq = new SALOME_MED::string_array; - try - { - // PN : en dur - int spaceDimension = 3; - myseq->length(spaceDimension); - myseq[0] = CORBA::string_dup("x"); - myseq[1] = CORBA::string_dup("y"); - myseq[2] = CORBA::string_dup("z"); - } - catch(...) - { - MESSAGE("Exception en accedant aux noms des coordonnees"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } - return myseq._retn(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + SALOME_TYPES::ListOfString_var myseq = new SALOME_TYPES::ListOfString; + try + { + // PN : en dur + int spaceDimension = 3; + myseq->length(spaceDimension); + myseq[0] = CORBA::string_dup("x"); + myseq[1] = CORBA::string_dup("y"); + myseq[2] = CORBA::string_dup("z"); + } + catch(...) + { + MESSAGE("Exception en accedant aux noms des coordonnees"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } + return myseq._retn(); } @@ -328,29 +336,29 @@ SALOME_MED::string_array * * CORBA: Accessor for Coordinates Units */ //============================================================================= -SALOME_MED::string_array * - SMESH_MEDMesh_i::getCoordinatesUnits()throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfString * +SMESH_MEDMesh_i::getCoordinatesUnits()throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - SALOME_MED::string_array_var myseq = new SALOME_MED::string_array; - try - { - // PN : en dur - int spaceDimension = 3; - myseq->length(spaceDimension); - myseq[0] = CORBA::string_dup("m"); - myseq[1] = CORBA::string_dup("m"); - myseq[2] = CORBA::string_dup("m"); - } - catch(...) - { - MESSAGE("Exception en accedant aux unites des coordonnees"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } - return myseq._retn(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + SALOME_TYPES::ListOfString_var myseq = new SALOME_TYPES::ListOfString; + try + { + // PN : en dur + int spaceDimension = 3; + myseq->length(spaceDimension); + myseq[0] = CORBA::string_dup("m"); + myseq[1] = CORBA::string_dup("m"); + myseq[2] = CORBA::string_dup("m"); + } + catch(...) + { + MESSAGE("Exception en accedant aux unites des coordonnees"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } + return myseq._retn(); } //============================================================================= @@ -360,19 +368,19 @@ SALOME_MED::string_array * //============================================================================= CORBA::Long SMESH_MEDMesh_i::getNumberOfNodes()throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - try - { - return _meshDS->NbNodes(); - } - catch(...) - { - MESSAGE("Exception en accedant au nombre de noeuds"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + try + { + return _meshDS->NbNodes(); + } + catch(...) + { + MESSAGE("Exception en accedant au nombre de noeuds"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } } //============================================================================= @@ -381,119 +389,119 @@ CORBA::Long SMESH_MEDMesh_i::getNumberOfNodes()throw(SALOME::SALOME_Exception) */ //============================================================================= CORBA::Long SMESH_MEDMesh_i::getNumberOfTypes(SALOME_MED::medEntityMesh entity) -throw(SALOME::SALOME_Exception) + throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - try - { - if (!_compte) - calculeNbElts(); - int retour = 0; - if (_mapNbTypes.find(entity) != _mapNbTypes.end()) - retour = _mapNbTypes[entity]; - return retour; - } - catch(...) - { - MESSAGE("Exception en accedant au nombre de Types"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + try + { + if (!_compte) + calculeNbElts(); + int retour = 0; + if (_mapNbTypes.find(entity) != _mapNbTypes.end()) + retour = _mapNbTypes[entity]; + return retour; + } + catch(...) + { + MESSAGE("Exception en accedant au nombre de Types"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } } //============================================================================= /*! * CORBA: Accessor for existing geometry element types - * Not implemented for MED_ALL_ENTITIES + * Not implemented for MED_ALL_ENTITIES */ //============================================================================= SALOME_MED::medGeometryElement_array * - SMESH_MEDMesh_i::getTypes(SALOME_MED::medEntityMesh entity) throw(SALOME:: - SALOME_Exception) +SMESH_MEDMesh_i::getTypes(SALOME_MED::medEntityMesh entity) throw(SALOME:: + SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - if (entity == SALOME_MED::MED_ALL_ENTITIES) - THROW_SALOME_CORBA_EXCEPTION("Not implemented for MED_ALL_ENTITIES", - SALOME::BAD_PARAM); - if (!_compte) - calculeNbElts(); - SALOME_MED::medGeometryElement_array_var myseq = - new SALOME_MED::medGeometryElement_array; - try - { - if (_mapNbTypes.find(entity) == _mapNbTypes.end()) - THROW_SALOME_CORBA_EXCEPTION("No Such Entity in the mesh", - SALOME::BAD_PARAM); - int nbTypes = _mapNbTypes[entity]; - - myseq->length(nbTypes); - - if (_mapIndToVectTypes.find(entity) == _mapIndToVectTypes.end()) - THROW_SALOME_CORBA_EXCEPTION("No Such Entity in the mesh", - SALOME::INTERNAL_ERROR); - - int index = _mapIndToVectTypes[entity]; - ASSERT(_TypesId[index].size() != 0); - int i = 0; - vector < SALOME_MED::medGeometryElement >::iterator it; - for (it = _TypesId[index].begin(); it != _TypesId[index].end(); it++) - { - myseq[i++] = *it; - }; - } - catch(...) - { - MESSAGE("Exception en accedant aux differents types"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } - return myseq._retn(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + if (entity == SALOME_MED::MED_ALL_ENTITIES) + THROW_SALOME_CORBA_EXCEPTION("Not implemented for MED_ALL_ENTITIES", + SALOME::BAD_PARAM); + if (!_compte) + calculeNbElts(); + SALOME_MED::medGeometryElement_array_var myseq = + new SALOME_MED::medGeometryElement_array; + try + { + if (_mapNbTypes.find(entity) == _mapNbTypes.end()) + THROW_SALOME_CORBA_EXCEPTION("No Such Entity in the mesh", + SALOME::BAD_PARAM); + int nbTypes = _mapNbTypes[entity]; + + myseq->length(nbTypes); + + if (_mapIndToVectTypes.find(entity) == _mapIndToVectTypes.end()) + THROW_SALOME_CORBA_EXCEPTION("No Such Entity in the mesh", + SALOME::INTERNAL_ERROR); + + int index = _mapIndToVectTypes[entity]; + ASSERT(_TypesId[index].size() != 0); + int i = 0; + vector < SALOME_MED::medGeometryElement >::iterator it; + for (it = _TypesId[index].begin(); it != _TypesId[index].end(); it++) + { + myseq[i++] = *it; + }; + } + catch(...) + { + MESSAGE("Exception en accedant aux differents types"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } + return myseq._retn(); } //============================================================================= /*! * CORBA: Returns number of elements of type medGeometryElement - * Not implemented for MED_ALL_ELEMENTS - * implemented for MED_ALL_ENTITIES + * Not implemented for MED_ALL_ELEMENTS + * implemented for MED_ALL_ENTITIES * * Dans cette implementation, il n est pas prevu de tenir compte du entity * qui ne doit pas pouvoir avoir deux valeurs differentes pour un geomElement */ //============================================================================= CORBA::Long SMESH_MEDMesh_i::getNumberOfElements(SALOME_MED:: - medEntityMesh entity, - SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) + medEntityMesh entity, + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - if (geomElement == SALOME_MED::MED_ALL_ELEMENTS) - THROW_SALOME_CORBA_EXCEPTION("Not implemented for MED_ALL_ELEMENTS", - SALOME::BAD_PARAM); - if (!_compte) - calculeNbElts(); - - try - { - int retour = 0; - if (_mapIndToSeqElts.find(geomElement) != _mapIndToSeqElts.end()) - { - int index = _mapIndToSeqElts[geomElement]; - - retour = _seq_elemId[index]->length(); - } - return retour; - } - catch(...) - { - MESSAGE("Exception en accedant au nombre d élements"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", - SALOME::INTERNAL_ERROR); - } + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + if (geomElement == SALOME_MED::MED_ALL_ELEMENTS) + THROW_SALOME_CORBA_EXCEPTION("Not implemented for MED_ALL_ELEMENTS", + SALOME::BAD_PARAM); + if (!_compte) + calculeNbElts(); + + try + { + int retour = 0; + if (_mapIndToSeqElts.find(geomElement) != _mapIndToSeqElts.end()) + { + int index = _mapIndToSeqElts[geomElement]; + + retour = _seq_elemId[index]->length(); + } + return retour; + } + catch(...) + { + MESSAGE("Exception en accedant au nombre d élements"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Mesh C++ Object", + SALOME::INTERNAL_ERROR); + } } //============================================================================= @@ -501,31 +509,30 @@ CORBA::Long SMESH_MEDMesh_i::getNumberOfElements(SALOME_MED:: * CORBA: Accessor for connectivities */ //============================================================================= -SALOME_MED::long_array * -SMESH_MEDMesh_i::getConnectivity(SALOME_MED::medModeSwitch typeSwitch, - SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity, - SALOME_MED::medGeometryElement geomElement) +SALOME_TYPES::ListOfLong * +SMESH_MEDMesh_i::getConnectivity(SALOME_MED::medConnectivity mode, + SALOME_MED::medEntityMesh entity, + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - if (mode != SALOME_MED::MED_NODAL) - THROW_SALOME_CORBA_EXCEPTION("Not Implemented", SALOME::BAD_PARAM); - if (typeSwitch == SALOME_MED::MED_NO_INTERLACE) - THROW_SALOME_CORBA_EXCEPTION("Not Yet Implemented", SALOME::BAD_PARAM); - if (!_compte) - calculeNbElts(); - - // Faut-il renvoyer un pointeur vide ??? - if (_mapIndToSeqElts.find(geomElement) != _mapIndToSeqElts.end()) - THROW_SALOME_CORBA_EXCEPTION("No Such Element in the mesh", - SALOME::BAD_PARAM); - - int index = _mapIndToSeqElts[geomElement]; - - return _seq_elemId[index]._retn(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + if (mode != SALOME_MED::MED_NODAL) + THROW_SALOME_CORBA_EXCEPTION("Not Implemented", SALOME::BAD_PARAM); + /*if (typeSwitch == SALOME_MED::MED_NO_INTERLACE) + THROW_SALOME_CORBA_EXCEPTION("Not Yet Implemented", SALOME::BAD_PARAM);*/ + if (!_compte) + calculeNbElts(); + + // Faut-il renvoyer un pointeur vide ??? + if (_mapIndToSeqElts.find(geomElement) != _mapIndToSeqElts.end()) + THROW_SALOME_CORBA_EXCEPTION("No Such Element in the mesh", + SALOME::BAD_PARAM); + + int index = _mapIndToSeqElts[geomElement]; + + return _seq_elemId[index]._retn(); } //============================================================================= @@ -533,14 +540,14 @@ SMESH_MEDMesh_i::getConnectivity(SALOME_MED::medModeSwitch typeSwitch, * CORBA: Accessor for connectivities */ //============================================================================= -SALOME_MED::long_array * +SALOME_TYPES::ListOfLong * SMESH_MEDMesh_i::getConnectivityIndex(SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity) + SALOME_MED::medEntityMesh entity) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -550,15 +557,15 @@ SMESH_MEDMesh_i::getConnectivityIndex(SALOME_MED::medConnectivity mode, //============================================================================= CORBA::Long SMESH_MEDMesh_i::getElementNumber(SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity, - SALOME_MED::medGeometryElement type, - const SALOME_MED::long_array & connectivity) + SALOME_MED::medEntityMesh entity, + SALOME_MED::medGeometryElement type, + const SALOME_TYPES::ListOfLong & connectivity) throw(SALOME::SALOME_Exception) { - const char *LOC = "getElementNumber "; - MESSAGE(LOC << "Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return -1; + const char *LOC = "getElementNumber "; + MESSAGE(LOC << "Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return -1; } //============================================================================= @@ -567,13 +574,13 @@ SMESH_MEDMesh_i::getElementNumber(SALOME_MED::medConnectivity mode, * not implemented for MED_ALL_ENTITIES and MED_MAILLE */ //============================================================================= -SALOME_MED::long_array * - SMESH_MEDMesh_i::getReverseConnectivity(SALOME_MED:: - medConnectivity mode) throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfLong * +SMESH_MEDMesh_i::getReverseConnectivity(SALOME_MED:: + medConnectivity mode) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -581,13 +588,13 @@ SALOME_MED::long_array * * CORBA: Accessor for connectivities */ //============================================================================= -SALOME_MED::long_array * - SMESH_MEDMesh_i::getReverseConnectivityIndex(SALOME_MED:: - medConnectivity mode) throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfLong * +SMESH_MEDMesh_i::getReverseConnectivityIndex(SALOME_MED:: + medConnectivity mode) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -596,14 +603,14 @@ SALOME_MED::long_array * */ //============================================================================= CORBA::Long SMESH_MEDMesh_i::getNumberOfFamilies(SALOME_MED:: - medEntityMesh entity) throw(SALOME::SALOME_Exception) + medEntityMesh entity) throw(SALOME::SALOME_Exception) { - if (_creeFamily == false) - createFamilies(); - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - return _families.size(); + if (_creeFamily == false) + createFamilies(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + return _families.size(); } //============================================================================= @@ -612,13 +619,13 @@ CORBA::Long SMESH_MEDMesh_i::getNumberOfFamilies(SALOME_MED:: */ //============================================================================= CORBA::Long SMESH_MEDMesh_i::getNumberOfGroups(SALOME_MED::medEntityMesh entity) -throw(SALOME::SALOME_Exception) + throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - MESSAGE(" Pas d implementation des groupes dans SMESH"); - return 0; + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + MESSAGE(" Pas d implementation des groupes dans SMESH"); + return 0; } //============================================================================= @@ -627,24 +634,24 @@ throw(SALOME::SALOME_Exception) */ //============================================================================= SALOME_MED::Family_array * - SMESH_MEDMesh_i::getFamilies(SALOME_MED:: - medEntityMesh entity) throw(SALOME::SALOME_Exception) +SMESH_MEDMesh_i::getFamilies(SALOME_MED:: + medEntityMesh entity) throw(SALOME::SALOME_Exception) { - if (_creeFamily == false) - createFamilies(); - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - SALOME_MED::Family_array_var myseq = new SALOME_MED::Family_array; - int nbfam = _families.size(); - myseq->length(nbfam); - int i = 0; - vector < SALOME_MED::FAMILY_ptr >::iterator it; - for (it = _families.begin(); it != _families.end(); it++) - { - myseq[i++] = *it; - }; - return myseq._retn(); + if (_creeFamily == false) + createFamilies(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + SALOME_MED::Family_array_var myseq = new SALOME_MED::Family_array; + int nbfam = _families.size(); + myseq->length(nbfam); + int i = 0; + vector < SALOME_MED::FAMILY_ptr >::iterator it; + for (it = _families.begin(); it != _families.end(); it++) + { + myseq[i++] = *it; + }; + return myseq._retn(); } //============================================================================= @@ -653,16 +660,16 @@ SALOME_MED::Family_array * */ //============================================================================= SALOME_MED::FAMILY_ptr SMESH_MEDMesh_i::getFamily(SALOME_MED:: - medEntityMesh entity, CORBA::Long i) throw(SALOME::SALOME_Exception) + medEntityMesh entity, CORBA::Long i) throw(SALOME::SALOME_Exception) { - if (_creeFamily == false) - createFamilies(); - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - - SCRUTE(_families[i]->getName()); - MESSAGE(" SMESH_MEDMesh_i::getFamily " << i) return _families[i]; + if (_creeFamily == false) + createFamilies(); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + + SCRUTE(_families[i]->getName()); + MESSAGE(" SMESH_MEDMesh_i::getFamily " << i) return _families[i]; } //============================================================================= @@ -671,14 +678,14 @@ SALOME_MED::FAMILY_ptr SMESH_MEDMesh_i::getFamily(SALOME_MED:: */ //============================================================================= SALOME_MED::Group_array * - SMESH_MEDMesh_i::getGroups(SALOME_MED::medEntityMesh entity) throw(SALOME:: - SALOME_Exception) +SMESH_MEDMesh_i::getGroups(SALOME_MED::medEntityMesh entity) throw(SALOME:: + SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - MESSAGE(" Pas d implementation des groupes dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("No group implementation", SALOME::BAD_PARAM); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + MESSAGE(" Pas d implementation des groupes dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("No group implementation", SALOME::BAD_PARAM); } //============================================================================= @@ -687,20 +694,20 @@ SALOME_MED::Group_array * */ //============================================================================= SALOME_MED::GROUP_ptr SMESH_MEDMesh_i::getGroup(SALOME_MED:: - medEntityMesh entity, CORBA::Long i) throw(SALOME::SALOME_Exception) + medEntityMesh entity, CORBA::Long i) throw(SALOME::SALOME_Exception) { - if (_mesh_i == 0) - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - MESSAGE(" Pas d implementation des groupes dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("No group implementation", SALOME::BAD_PARAM); + if (_mesh_i == 0) + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + MESSAGE(" Pas d implementation des groupes dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("No group implementation", SALOME::BAD_PARAM); } //============================================================================= /*! * CORBA: Returns references for the global numbering index */ //============================================================================= -SALOME_MED::long_array* +SALOME_TYPES::ListOfLong* SMESH_MEDMesh_i::getGlobalNumberingIndex(SALOME_MED::medEntityMesh entity) throw (SALOME::SALOME_Exception) { @@ -764,11 +771,11 @@ SMESH_MEDMesh_i::getSkin(SALOME_MED::SUPPORT_ptr mySupport3D) */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getVolume(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -777,11 +784,11 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getVolume(SALOME_MED:: */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getArea(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -790,11 +797,11 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getArea(SALOME_MED:: */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getLength(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -803,11 +810,11 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getLength(SALOME_MED:: */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getNormal(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -816,11 +823,11 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getNormal(SALOME_MED:: */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getBarycenter(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Pas Implemente dans SMESH"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Pas Implemente dans SMESH"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -829,11 +836,11 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getBarycenter(SALOME_MED:: */ //============================================================================= SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getNeighbourhood(SALOME_MED:: - SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) + SUPPORT_ptr mySupport) throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -843,57 +850,57 @@ SALOME_MED::FIELD_ptr SMESH_MEDMesh_i::getNeighbourhood(SALOME_MED:: */ //============================================================================= void SMESH_MEDMesh_i::addInStudy(SALOMEDS::Study_ptr myStudy, - SALOME_MED::MESH_ptr myIor) throw(SALOME::SALOME_Exception) + SALOME_MED::GMESH_ptr myIor) throw(SALOME::SALOME_Exception) { - BEGIN_OF("MED_Mesh_i::addInStudy"); - if (_meshId != "") - { - MESSAGE("Mesh already in Study"); - THROW_SALOME_CORBA_EXCEPTION("Mesh already in Study", - SALOME::BAD_PARAM); - }; - - /* - * SALOMEDS::StudyBuilder_var myBuilder = myStudy->NewBuilder(); - * - * // Create SComponent labelled 'MED' if it doesn't already exit - * SALOMEDS::SComponent_var medfather = myStudy->FindComponent("MED"); - * if ( CORBA::is_nil(medfather) ) - * { - * MESSAGE("Add Component MED"); - * medfather = myBuilder->NewComponent("MED"); - * //myBuilder->AddAttribute (medfather,SALOMEDS::Name,"MED"); - * SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow( - * myBuilder->FindOrCreateAttribute(medfather, "AttributeName")); - * aName->SetValue("MED"); - * - * myBuilder->DefineComponentInstance(medfather,myIor); - * - * } ; - * - * MESSAGE("Add a mesh Object under MED"); - * myBuilder->NewCommand(); - * SALOMEDS::SObject_var newObj = myBuilder->NewObject(medfather); - * - * ORB_INIT &init = *SINGLETON_::Instance() ; - * ASSERT(SINGLETON_::IsAlreadyExisting()) ; - * CORBA::ORB_var &orb = init(0,0); - * CORBA::String_var iorStr = orb->object_to_string(myIor); - * //myBuilder->AddAttribute(newObj,SALOMEDS::IOR,iorStr.in()); - * SALOMEDS::AttributeIOR_var aIOR = SALOMEDS::AttributeIOR::_narrow( - * myBuilder->FindOrCreateAttribute(newObj, "AttributeIOR")); - * aIOR->SetValue(iorStr.c_str()); - * - * //myBuilder->AddAttribute(newObj,SALOMEDS::Name,_mesh_i->getName().c_str()); - * SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow( - * myBuilder->FindOrCreateAttribute(newObj, "AttributeName")); - * aName->SetValue(_mesh_i->getName().c_str()); - * - * _meshId = newObj->GetID(); - * myBuilder->CommitCommand(); - * - */ - END_OF("Mesh_i::addInStudy(SALOMEDS::Study_ptr myStudy)"); + BEGIN_OF("MED_Mesh_i::addInStudy"); + if (_meshId != "") + { + MESSAGE("Mesh already in Study"); + THROW_SALOME_CORBA_EXCEPTION("Mesh already in Study", + SALOME::BAD_PARAM); + }; + + /* + * SALOMEDS::StudyBuilder_var myBuilder = myStudy->NewBuilder(); + * + * // Create SComponent labelled 'MED' if it doesn't already exit + * SALOMEDS::SComponent_var medfather = myStudy->FindComponent("MED"); + * if ( CORBA::is_nil(medfather) ) + * { + * MESSAGE("Add Component MED"); + * medfather = myBuilder->NewComponent("MED"); + * //myBuilder->AddAttribute (medfather,SALOMEDS::Name,"MED"); + * SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow( + * myBuilder->FindOrCreateAttribute(medfather, "AttributeName")); + * aName->SetValue("MED"); + * + * myBuilder->DefineComponentInstance(medfather,myIor); + * + * } ; + * + * MESSAGE("Add a mesh Object under MED"); + * myBuilder->NewCommand(); + * SALOMEDS::SObject_var newObj = myBuilder->NewObject(medfather); + * + * ORB_INIT &init = *SINGLETON_::Instance() ; + * ASSERT(SINGLETON_::IsAlreadyExisting()) ; + * CORBA::ORB_var &orb = init(0,0); + * CORBA::String_var iorStr = orb->object_to_string(myIor); + * //myBuilder->AddAttribute(newObj,SALOMEDS::IOR,iorStr.in()); + * SALOMEDS::AttributeIOR_var aIOR = SALOMEDS::AttributeIOR::_narrow( + * myBuilder->FindOrCreateAttribute(newObj, "AttributeIOR")); + * aIOR->SetValue(iorStr.c_str()); + * + * //myBuilder->AddAttribute(newObj,SALOMEDS::Name,_mesh_i->getName().c_str()); + * SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow( + * myBuilder->FindOrCreateAttribute(newObj, "AttributeName")); + * aName->SetValue(_mesh_i->getName().c_str()); + * + * _meshId = newObj->GetID(); + * myBuilder->CommitCommand(); + * + */ + END_OF("Mesh_i::addInStudy(SALOMEDS::Study_ptr myStudy)"); } //============================================================================= @@ -902,10 +909,10 @@ void SMESH_MEDMesh_i::addInStudy(SALOMEDS::Study_ptr myStudy, */ //============================================================================= void SMESH_MEDMesh_i::write(CORBA::Long i, const char *driverMeshName) -throw(SALOME::SALOME_Exception) + throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); } //============================================================================= @@ -915,8 +922,8 @@ throw(SALOME::SALOME_Exception) //============================================================================= void SMESH_MEDMesh_i::read(CORBA::Long i) throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); } //============================================================================= @@ -926,8 +933,8 @@ void SMESH_MEDMesh_i::read(CORBA::Long i) throw(SALOME::SALOME_Exception) //============================================================================= void SMESH_MEDMesh_i::rmDriver(CORBA::Long i) throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); } //============================================================================= @@ -936,11 +943,11 @@ void SMESH_MEDMesh_i::rmDriver(CORBA::Long i) throw(SALOME::SALOME_Exception) */ //============================================================================= CORBA::Long SMESH_MEDMesh_i::addDriver(SALOME_MED::medDriverTypes driverType, - const char *fileName, const char *meshName) throw(SALOME::SALOME_Exception) + const char *fileName, const char *meshName) throw(SALOME::SALOME_Exception) { - MESSAGE("Non Implemente"); - THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); - return 0; + MESSAGE("Non Implemente"); + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + return 0; } //============================================================================= @@ -950,174 +957,174 @@ CORBA::Long SMESH_MEDMesh_i::addDriver(SALOME_MED::medDriverTypes driverType, //============================================================================= void SMESH_MEDMesh_i::calculeNbElts() throw(SALOME::SALOME_Exception) { - if (!_compte) - { - _compte = true; - - _mapNbTypes[SALOME_MED::MED_NODE] = 1; - // On compte les aretes MED_SEG2 ou MED_SEG3 - // On range les elements dans les vecteurs correspondants - - _mapIndToSeqElts[SALOME_MED::MED_SEG2] = _indexElts++; - _mapIndToSeqElts[SALOME_MED::MED_SEG3] = _indexElts++; - _mapIndToVectTypes[SALOME_MED::MED_EDGE] = _indexEnts++; - - int trouveSeg2 = 0; - int trouveSeg3 = 0; - SALOME_MED::medGeometryElement medElement; - - SMDS_EdgeIteratorPtr itEdges=_meshDS->edgesIterator(); - while(itEdges->more()) - { - const SMDS_MeshEdge* elem = itEdges->next(); - int nb_of_nodes = elem->NbNodes(); - - switch (nb_of_nodes) - { - case 2: - { - medElement = SALOME_MED::MED_SEG2; - if (trouveSeg2 == 0) - { - trouveSeg2 = 1; - _TypesId[SALOME_MED::MED_EDGE]. - push_back(SALOME_MED::MED_SEG2); - } - break; - } - case 3: - { - medElement = SALOME_MED::MED_SEG3; - if (trouveSeg3 == 0) - { - trouveSeg3 = 1; - _TypesId[SALOME_MED::MED_EDGE]. - push_back(SALOME_MED::MED_SEG3); - } - break; - } - } - int index = _mapIndToSeqElts[medElement]; - SCRUTE(index); - // Traitement de l arete - - int longueur = _seq_elemId[index]->length(); - _seq_elemId[index]->length(longueur + nb_of_nodes); - - SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); - - for(int k=0; itn->more(); k++) - _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; - } - - _mapNbTypes[SALOME_MED::MED_EDGE] = trouveSeg2 + trouveSeg3; - - // On compte les faces MED_TRIA3, MED_HEXA8, MED_TRIA6 - // On range les elements dans les vecteurs correspondants - int trouveTria3 = 0; - int trouveTria6 = 0; - int trouveQuad4 = 0; - - _mapIndToSeqElts[SALOME_MED::MED_TRIA3] = _indexElts++; - _mapIndToSeqElts[SALOME_MED::MED_TRIA6] = _indexElts++; - _mapIndToSeqElts[SALOME_MED::MED_QUAD4] = _indexElts++; - _mapIndToVectTypes[SALOME_MED::MED_FACE] = _indexEnts++; - - SMDS_FaceIteratorPtr itFaces=_meshDS->facesIterator(); - while(itFaces->more()) - { - const SMDS_MeshFace * elem = itFaces->next(); - int nb_of_nodes = elem->NbNodes(); - - switch (nb_of_nodes) - { - case 3: - { - medElement = SALOME_MED::MED_TRIA3; - if (trouveTria3 == 0) - { - trouveTria3 = 1; - _TypesId[SALOME_MED::MED_FACE]. - push_back(SALOME_MED::MED_TRIA3); - } - break; - } - case 4: - { - medElement = SALOME_MED::MED_QUAD4; - if (trouveQuad4 == 0) - { - trouveQuad4 = 1; - _TypesId[SALOME_MED::MED_FACE]. - push_back(SALOME_MED::MED_QUAD4); - } - break; - } - case 6: - { - medElement = SALOME_MED::MED_TRIA6; - if (trouveTria6 == 0) - { - trouveTria6 = 1; - _TypesId[SALOME_MED::MED_FACE]. - push_back(SALOME_MED::MED_TRIA6); - } - break; - } - } - int index = _mapIndToSeqElts[medElement]; - SCRUTE(index); - - // Traitement de la face - // Attention La numérotation des noeuds Med commence a 1 - - int longueur = _seq_elemId[index]->length(); - _seq_elemId[index]->length(longueur + nb_of_nodes); - - SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); - - for(int k=0; itn->more(); k++) - _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; - } //itFaces - - _mapNbTypes[SALOME_MED::MED_FACE] = - trouveTria3 + trouveTria6 + trouveQuad4; - - _mapIndToSeqElts[SALOME_MED::MED_HEXA8] = _indexElts++; - _mapIndToVectTypes[SALOME_MED::MED_CELL] = _indexEnts++; - int index = _mapIndToSeqElts[medElement]; - - int trouveHexa8 = 0; - - SMDS_VolumeIteratorPtr itVolumes=_meshDS->volumesIterator(); - while(itVolumes->more()) - { - const SMDS_MeshVolume * elem = itVolumes->next(); - - int nb_of_nodes = elem->NbNodes(); - medElement = SALOME_MED::MED_HEXA8; - ASSERT(nb_of_nodes == 8); - - if (trouveHexa8 == 0) - { - trouveHexa8 = 1; - _TypesId[SALOME_MED::MED_CELL].push_back(SALOME_MED::MED_HEXA8); - }; - // Traitement de la maille - int longueur = _seq_elemId[index]->length(); - _seq_elemId[index]->length(longueur + nb_of_nodes); - - SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); - for(int k=0; itn->more(); k++) - _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; - } - - _mapNbTypes[SALOME_MED::MED_CELL] = trouveHexa8; - _mapNbTypes[SALOME_MED::MED_ALL_ENTITIES] - = - trouveHexa8 + trouveTria3 + trouveTria6 + trouveQuad4 + trouveSeg2 + - trouveSeg3; - }// fin du _compte + if (!_compte) + { + _compte = true; + + _mapNbTypes[SALOME_MED::MED_NODE] = 1; + // On compte les aretes MED_SEG2 ou MED_SEG3 + // On range les elements dans les vecteurs correspondants + + _mapIndToSeqElts[SALOME_MED::MED_SEG2] = _indexElts++; + _mapIndToSeqElts[SALOME_MED::MED_SEG3] = _indexElts++; + _mapIndToVectTypes[SALOME_MED::MED_EDGE] = _indexEnts++; + + int trouveSeg2 = 0; + int trouveSeg3 = 0; + SALOME_MED::medGeometryElement medElement; + + SMDS_EdgeIteratorPtr itEdges=_meshDS->edgesIterator(); + while(itEdges->more()) + { + const SMDS_MeshEdge* elem = itEdges->next(); + int nb_of_nodes = elem->NbNodes(); + + switch (nb_of_nodes) + { + case 2: + { + medElement = SALOME_MED::MED_SEG2; + if (trouveSeg2 == 0) + { + trouveSeg2 = 1; + _TypesId[SALOME_MED::MED_EDGE]. + push_back(SALOME_MED::MED_SEG2); + } + break; + } + case 3: + { + medElement = SALOME_MED::MED_SEG3; + if (trouveSeg3 == 0) + { + trouveSeg3 = 1; + _TypesId[SALOME_MED::MED_EDGE]. + push_back(SALOME_MED::MED_SEG3); + } + break; + } + } + int index = _mapIndToSeqElts[medElement]; + SCRUTE(index); + // Traitement de l arete + + int longueur = _seq_elemId[index]->length(); + _seq_elemId[index]->length(longueur + nb_of_nodes); + + SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); + + for(int k=0; itn->more(); k++) + _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; + } + + _mapNbTypes[SALOME_MED::MED_EDGE] = trouveSeg2 + trouveSeg3; + + // On compte les faces MED_TRIA3, MED_HEXA8, MED_TRIA6 + // On range les elements dans les vecteurs correspondants + int trouveTria3 = 0; + int trouveTria6 = 0; + int trouveQuad4 = 0; + + _mapIndToSeqElts[SALOME_MED::MED_TRIA3] = _indexElts++; + _mapIndToSeqElts[SALOME_MED::MED_TRIA6] = _indexElts++; + _mapIndToSeqElts[SALOME_MED::MED_QUAD4] = _indexElts++; + _mapIndToVectTypes[SALOME_MED::MED_FACE] = _indexEnts++; + + SMDS_FaceIteratorPtr itFaces=_meshDS->facesIterator(); + while(itFaces->more()) + { + const SMDS_MeshFace * elem = itFaces->next(); + int nb_of_nodes = elem->NbNodes(); + + switch (nb_of_nodes) + { + case 3: + { + medElement = SALOME_MED::MED_TRIA3; + if (trouveTria3 == 0) + { + trouveTria3 = 1; + _TypesId[SALOME_MED::MED_FACE]. + push_back(SALOME_MED::MED_TRIA3); + } + break; + } + case 4: + { + medElement = SALOME_MED::MED_QUAD4; + if (trouveQuad4 == 0) + { + trouveQuad4 = 1; + _TypesId[SALOME_MED::MED_FACE]. + push_back(SALOME_MED::MED_QUAD4); + } + break; + } + case 6: + { + medElement = SALOME_MED::MED_TRIA6; + if (trouveTria6 == 0) + { + trouveTria6 = 1; + _TypesId[SALOME_MED::MED_FACE]. + push_back(SALOME_MED::MED_TRIA6); + } + break; + } + } + int index = _mapIndToSeqElts[medElement]; + SCRUTE(index); + + // Traitement de la face + // Attention La numérotation des noeuds Med commence a 1 + + int longueur = _seq_elemId[index]->length(); + _seq_elemId[index]->length(longueur + nb_of_nodes); + + SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); + + for(int k=0; itn->more(); k++) + _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; + } //itFaces + + _mapNbTypes[SALOME_MED::MED_FACE] = + trouveTria3 + trouveTria6 + trouveQuad4; + + _mapIndToSeqElts[SALOME_MED::MED_HEXA8] = _indexElts++; + _mapIndToVectTypes[SALOME_MED::MED_CELL] = _indexEnts++; + int index = _mapIndToSeqElts[medElement]; + + int trouveHexa8 = 0; + + SMDS_VolumeIteratorPtr itVolumes=_meshDS->volumesIterator(); + while(itVolumes->more()) + { + const SMDS_MeshVolume * elem = itVolumes->next(); + + int nb_of_nodes = elem->NbNodes(); + medElement = SALOME_MED::MED_HEXA8; + ASSERT(nb_of_nodes == 8); + + if (trouveHexa8 == 0) + { + trouveHexa8 = 1; + _TypesId[SALOME_MED::MED_CELL].push_back(SALOME_MED::MED_HEXA8); + }; + // Traitement de la maille + int longueur = _seq_elemId[index]->length(); + _seq_elemId[index]->length(longueur + nb_of_nodes); + + SMDS_NodeIteratorPtr itn=_meshDS->nodesIterator(); + for(int k=0; itn->more(); k++) + _seq_elemId[index][longueur + k] = itn->next()->GetID()+1; + } + + _mapNbTypes[SALOME_MED::MED_CELL] = trouveHexa8; + _mapNbTypes[SALOME_MED::MED_ALL_ENTITIES] + = + trouveHexa8 + trouveTria3 + trouveTria6 + trouveQuad4 + trouveSeg2 + + trouveSeg3; + }// fin du _compte }; //============================================================================= @@ -1128,49 +1135,49 @@ void SMESH_MEDMesh_i::calculeNbElts() throw(SALOME::SALOME_Exception) void SMESH_MEDMesh_i::createFamilies() throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - string famDes = ("Je ne sais pas"); - string famName0 = "Famille_"; - string famName; - char numero[10]; - - if (_creeFamily == false) - { - _creeFamily = true; - //SMESH_subMesh_i *subMeshServant; - - map < int, SMESH_subMesh_i * >::iterator it; - for (it = _mesh_i->_mapSubMesh_i.begin(); - it != _mesh_i->_mapSubMesh_i.end(); it++) - { - SMESH_subMesh_i *submesh_i = (*it).second; - int famIdent = (*it).first; - - ASSERT(famIdent < 999999999); - sprintf(numero, "%d\n", famIdent); - famName = famName0 + numero; - - SMESH_MEDFamily_i *famservant = - new SMESH_MEDFamily_i(famIdent, submesh_i, - famName, famDes, SALOME_MED::MED_NODE); + string famDes = ("Je ne sais pas"); + string famName0 = "Famille_"; + string famName; + char numero[10]; + + if (_creeFamily == false) + { + _creeFamily = true; + //SMESH_subMesh_i *subMeshServant; + + map < int, SMESH_subMesh_i * >::iterator it; + for (it = _mesh_i->_mapSubMesh_i.begin(); + it != _mesh_i->_mapSubMesh_i.end(); it++) + { + SMESH_subMesh_i *submesh_i = (*it).second; + int famIdent = (*it).first; + + ASSERT(famIdent < 999999999); + sprintf(numero, "%d\n", famIdent); + famName = famName0 + numero; + + SMESH_MEDFamily_i *famservant = + new SMESH_MEDFamily_i(famIdent, submesh_i, + famName, famDes, SALOME_MED::MED_NODE); #ifdef WNT SALOME_MED::FAMILY_ptr famille = SALOME_MED::FAMILY::_nil(); POA_SALOME_MED::FAMILY* servantbase = dynamic_cast(famservant); if ( servantbase ) famille = SALOME_MED::FAMILY::_narrow( servantbase->_this() ); #else - SALOME_MED::FAMILY_ptr famille = + SALOME_MED::FAMILY_ptr famille = SALOME_MED::FAMILY::_narrow( famservant->POA_SALOME_MED::FAMILY::_this() ); #endif - _families.push_back(famille); - } - } + _families.push_back(famille); + } + } }; //============================================================================= /*! * Gives informations of the considered mesh. */ //============================================================================= -SALOME_MED::MESH::meshInfos * SMESH_MEDMesh_i::getMeshGlobal() +SALOME_MED::GMESH::meshInfos * SMESH_MEDMesh_i::getMeshGlobal() throw (SALOME::SALOME_Exception) { MESSAGE("!!!! NOT YET IMPLEMENTED !!!!!"); @@ -1179,12 +1186,28 @@ SALOME_MED::MESH::meshInfos * SMESH_MEDMesh_i::getMeshGlobal() return NULL; } + +//================================================================================ +/*! + * \brief Converts this GMESH into MESH + */ +//================================================================================ + +SALOME_MED::MESH_ptr SMESH_MEDMesh_i::convertInMESH() throw (SALOME::SALOME_Exception) +{ + MESSAGE("!!!! NOT YET IMPLEMENTED !!!!!"); + + THROW_SALOME_CORBA_EXCEPTION("Unimplemented Method", SALOME::BAD_PARAM); + + return NULL; +} + //============================================================================= /*! * Gives informations on coordinates of the considered mesh. */ //============================================================================= -SALOME_MED::MESH::coordinateInfos * SMESH_MEDMesh_i::getCoordGlobal() +SALOME_MED::GMESH::coordinateInfos * SMESH_MEDMesh_i::getCoordGlobal() throw (SALOME::SALOME_Exception) { MESSAGE("!!!! NOT YET IMPLEMENTED !!!!!"); @@ -1216,7 +1239,7 @@ SMESH_MEDMesh_i::getConnectGlobal(SALOME_MED::medEntityMesh entity) //============================================================================= SALOME_MED::medGeometryElement SMESH_MEDMesh_i::getElementType(SALOME_MED::medEntityMesh entity, - CORBA::Long number) + CORBA::Long number) throw (SALOME::SALOME_Exception) { MESSAGE("!!!! NOT YET IMPLEMENTED !!!!!"); diff --git a/src/SMESH_I/SMESH_MEDMesh_i.hxx b/src/SMESH_I/SMESH_MEDMesh_i.hxx index 8e56e0e79..6541939af 100644 --- a/src/SMESH_I/SMESH_MEDMesh_i.hxx +++ b/src/SMESH_I/SMESH_MEDMesh_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDMesh_i.hxx // Module : SMESH @@ -62,7 +63,7 @@ protected: int _famIdent; std::map < SALOME_MED::medGeometryElement, int >_mapIndToSeqElts; - SALOME_MED::long_array_var _seq_elemId[MED_NBR_GEOMETRIE_MAILLE]; + SALOME_TYPES::ListOfLong_var _seq_elemId[MED_NBR_GEOMETRIE_MAILLE]; std::map < SALOME_MED::medEntityMesh, int >_mapNbTypes; std::map < SALOME_MED::medEntityMesh, int >_mapIndToVectTypes; @@ -81,13 +82,18 @@ public: void setProtocol(SALOME::TypeOfCommunication typ) {} void release() {} SALOME::SenderDouble_ptr getSenderForCoordinates(SALOME_MED::medModeSwitch) {return SALOME::SenderDouble::_nil();} - SALOME::SenderInt_ptr getSenderForConnectivity(SALOME_MED::medModeSwitch, - SALOME_MED::medConnectivity, - SALOME_MED::medEntityMesh, - SALOME_MED::medGeometryElement) + SALOME::SenderInt_ptr getSenderForConnectivity(SALOME_MED::medConnectivity, + SALOME_MED::medEntityMesh, + SALOME_MED::medGeometryElement) + { + return SALOME::SenderInt::_nil(); + } + SALOME::SenderInt_ptr getSenderForConnectivityIndex(SALOME_MED::medConnectivity, + SALOME_MED::medEntityMesh, + SALOME_MED::medGeometryElement) { return SALOME::SenderInt::_nil(); - } + } SALOME::SenderInt_ptr getSenderForPolygonsConnectivity(SALOME_MED::medConnectivity, SALOME_MED::medEntityMesh) {return SALOME::SenderInt::_nil();} SALOME::SenderInt_ptr getSenderForPolygonsConnectivityIndex(SALOME_MED::medConnectivity, SALOME_MED::medEntityMesh) {return SALOME::SenderInt::_nil();} SALOME::SenderInt_ptr getSenderForPolyhedronConnectivity(SALOME_MED::medConnectivity) {return SALOME::SenderInt::_nil();} @@ -103,7 +109,7 @@ public: CORBA::Boolean existConnectivity(SALOME_MED::medConnectivity connectivityType, - SALOME_MED::medEntityMesh entity) + SALOME_MED::medEntityMesh entity) throw (SALOME::SALOME_Exception); char *getCoordinatesSystem() throw(SALOME::SALOME_Exception); @@ -111,13 +117,13 @@ public: CORBA::Double getCoordinate(CORBA::Long Number, CORBA::Long Axis) throw (SALOME::SALOME_Exception); - SALOME_MED::double_array * getCoordinates(SALOME_MED::medModeSwitch typeSwitch) + SALOME_TYPES::ListOfDouble * getCoordinates(SALOME_MED::medModeSwitch typeSwitch) throw(SALOME::SALOME_Exception); - SALOME_MED::string_array * getCoordinatesNames() + SALOME_TYPES::ListOfString * getCoordinatesNames() throw(SALOME::SALOME_Exception); - SALOME_MED::string_array * getCoordinatesUnits() + SALOME_TYPES::ListOfString * getCoordinatesUnits() throw(SALOME::SALOME_Exception); CORBA::Long getNumberOfNodes() throw(SALOME::SALOME_Exception); @@ -127,44 +133,43 @@ public: SALOME_MED::medGeometryElement_array * getTypes(SALOME_MED::medEntityMesh entity) throw(SALOME:: - SALOME_Exception); + SALOME_Exception); SALOME_MED::medGeometryElement getElementType(SALOME_MED::medEntityMesh entity, - CORBA::Long number) + CORBA::Long number) throw (SALOME::SALOME_Exception); CORBA::Long getNumberOfElements(SALOME_MED::medEntityMesh entity, - SALOME_MED::medGeometryElement geomElement) + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception); - SALOME_MED::long_array * - getConnectivity(SALOME_MED::medModeSwitch typeSwitch, - SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity, - SALOME_MED::medGeometryElement geomElement) + SALOME_TYPES::ListOfLong * + getConnectivity(SALOME_MED::medConnectivity mode, + SALOME_MED::medEntityMesh entity, + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception); - SALOME_MED::long_array * + SALOME_TYPES::ListOfLong * getConnectivityIndex(SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity) + SALOME_MED::medEntityMesh entity) throw(SALOME::SALOME_Exception); - SALOME_MED::long_array* + SALOME_TYPES::ListOfLong* getGlobalNumberingIndex(SALOME_MED::medEntityMesh entity) throw (SALOME::SALOME_Exception); CORBA::Long getElementNumber(SALOME_MED::medConnectivity mode, - SALOME_MED::medEntityMesh entity, - SALOME_MED::medGeometryElement type, - const SALOME_MED::long_array & connectivity) + SALOME_MED::medEntityMesh entity, + SALOME_MED::medGeometryElement type, + const SALOME_TYPES::ListOfLong & connectivity) throw(SALOME::SALOME_Exception); - SALOME_MED::long_array * + SALOME_TYPES::ListOfLong * getReverseConnectivity(SALOME_MED::medConnectivity mode) throw(SALOME::SALOME_Exception); - SALOME_MED::long_array * + SALOME_TYPES::ListOfLong * getReverseConnectivityIndex(SALOME_MED::medConnectivity mode) throw(SALOME::SALOME_Exception); @@ -180,14 +185,14 @@ public: throw(SALOME::SALOME_Exception); SALOME_MED::FAMILY_ptr getFamily(SALOME_MED::medEntityMesh entity, - CORBA::Long i) + CORBA::Long i) throw(SALOME::SALOME_Exception); SALOME_MED::Group_array * getGroups(SALOME_MED::medEntityMesh entity) throw(SALOME::SALOME_Exception); SALOME_MED::GROUP_ptr getGroup(SALOME_MED::medEntityMesh entity, - CORBA::Long i) + CORBA::Long i) throw(SALOME::SALOME_Exception); SALOME_MED::SUPPORT_ptr @@ -221,10 +226,10 @@ public: // Others void addInStudy(SALOMEDS::Study_ptr myStudy, - SALOME_MED::MESH_ptr myIor) + SALOME_MED::GMESH_ptr myIor) throw(SALOME::SALOME_Exception); CORBA::Long addDriver(SALOME_MED::medDriverTypes driverType, - const char *fileName, const char *meshName) + const char *fileName, const char *meshName) throw(SALOME::SALOME_Exception); void rmDriver(CORBA::Long i) throw(SALOME::SALOME_Exception); void read(CORBA::Long i) throw(SALOME::SALOME_Exception); @@ -235,12 +240,14 @@ public: CORBA::Long getCorbaIndex() throw(SALOME::SALOME_Exception); - SALOME_MED::MESH::meshInfos * getMeshGlobal() + SALOME_MED::GMESH::meshInfos * getMeshGlobal() throw (SALOME::SALOME_Exception); - bool areEquals(SALOME_MED::MESH_ptr other) { return false;}; + bool areEquals(SALOME_MED::GMESH_ptr other) { return false;}; + + SALOME_MED::MESH_ptr convertInMESH() throw (SALOME::SALOME_Exception); - SALOME_MED::MESH::coordinateInfos * getCoordGlobal() + SALOME_MED::GMESH::coordinateInfos * getCoordGlobal() throw (SALOME::SALOME_Exception); SALOME_MED::MESH::connectivityInfos * diff --git a/src/SMESH_I/SMESH_MEDSupport_i.cxx b/src/SMESH_I/SMESH_MEDSupport_i.cxx index 56f3b73eb..23dd94144 100644 --- a/src/SMESH_I/SMESH_MEDSupport_i.cxx +++ b/src/SMESH_I/SMESH_MEDSupport_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDSupport_i.cxx // Module : SMESH @@ -28,12 +29,14 @@ #include "Utils_CorbaException.hxx" #include "Utils_ExceptHandlers.hxx" -#include #include "SMESHDS_Mesh.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_Mesh_i.hxx" #include "SMESH_subMesh_i.hxx" +#include "SMESH_Gen_i.hxx" + +#include using namespace std; @@ -45,8 +48,8 @@ using namespace std; //============================================================================= SMESH_MEDSupport_i::SMESH_MEDSupport_i() { - BEGIN_OF("Default Constructor SMESH_MEDSupport_i"); - END_OF("Default Constructor SMESH_MEDSupport_i"); + BEGIN_OF("Default Constructor SMESH_MEDSupport_i"); + END_OF("Default Constructor SMESH_MEDSupport_i"); } //============================================================================= @@ -55,40 +58,34 @@ SMESH_MEDSupport_i::SMESH_MEDSupport_i() */ //============================================================================= SMESH_MEDSupport_i::SMESH_MEDSupport_i(SMESH_subMesh_i * sm, string name, - string description, SALOME_MED::medEntityMesh entity) - :_subMesh_i(sm), _name(name), _description(description), _entity(entity), - _seqNumber(false), _seqLength(0) + string description, SALOME_MED::medEntityMesh entity) + :_subMesh_i(sm), _name(name), _description(description), _entity(entity), + _seqNumber(false), _seqLength(0) { - BEGIN_OF("Constructor SMESH_MEDSupport_i"); - - _meshDS = _subMesh_i->_mesh_i->GetImpl().GetMeshDS(); - - int subMeshId = _subMesh_i->GetId(); - - MESSAGE(" subMeshId " << subMeshId) - - if (_subMesh_i->_mesh_i->_mapSubMesh.find(subMeshId) != - _subMesh_i->_mesh_i->_mapSubMesh.end()) - { - ::SMESH_subMesh * subMesh = _subMesh_i->_mesh_i->_mapSubMesh[subMeshId]; - _subMeshDS = subMesh->GetSubMeshDS(); - } - - if (_entity == SALOME_MED::MED_NODE) - { - _numberOfGeometricType = 1; - _geometricType = new SALOME_MED::medGeometryElement[1]; - _geometricType[0] = SALOME_MED::MED_NONE; - } - else - { - MESSAGE("Pas implemente dans cette version"); - THROW_SALOME_CORBA_EXCEPTION - ("Seules les familles de noeuds sont implementees ", - SALOME::BAD_PARAM); - } - - END_OF("Constructor SMESH_MEDSupport_i"); + BEGIN_OF("Constructor SMESH_MEDSupport_i"); + + int subMeshId = sm->GetId(); + + MESSAGE(" subMeshId " << subMeshId); + + SMESH_Mesh_i* mesh_i = SMESH::DownCast( sm->GetMesh() ); + _subMesh = mesh_i->GetImpl().GetSubMeshContaining( subMeshId ); + + if (_entity == SALOME_MED::MED_NODE) + { + _numberOfGeometricType = 1; + _geometricType = new SALOME_MED::medGeometryElement[1]; + _geometricType[0] = SALOME_MED::MED_NONE; + } + else + { + MESSAGE("Pas implemente dans cette version"); + THROW_SALOME_CORBA_EXCEPTION + ("Seules les familles de noeuds sont implementees ", + SALOME::BAD_PARAM); + } + + END_OF("Constructor SMESH_MEDSupport_i"); } //============================================================================= @@ -97,23 +94,13 @@ SMESH_MEDSupport_i::SMESH_MEDSupport_i(SMESH_subMesh_i * sm, string name, */ //============================================================================= SMESH_MEDSupport_i:: -SMESH_MEDSupport_i(const SMESH_MEDSupport_i & s):_subMesh_i(s._subMesh_i), +SMESH_MEDSupport_i(const SMESH_MEDSupport_i & s):_subMesh(s._subMesh), _name(s._name), _description(s._description), _entity(s._entity), _seqNumber(false), _seqLength(0) { - BEGIN_OF("Constructor SMESH_MEDSupport_i"); - - _meshDS = _subMesh_i->_mesh_i->GetImpl().GetMeshDS(); - - int subMeshId = _subMesh_i->GetId(); - if (_subMesh_i->_mesh_i->_mapSubMesh.find(subMeshId) != - _subMesh_i->_mesh_i->_mapSubMesh.end()) - { - ::SMESH_subMesh * subMesh = _subMesh_i->_mesh_i->_mapSubMesh[subMeshId]; - _subMeshDS = subMesh->GetSubMeshDS(); - } + BEGIN_OF("Constructor SMESH_MEDSupport_i"); - END_OF("Constructor SMESH_MEDSupport_i"); + END_OF("Constructor SMESH_MEDSupport_i"); } //============================================================================= @@ -134,11 +121,11 @@ SMESH_MEDSupport_i::~SMESH_MEDSupport_i() CORBA::Long SMESH_MEDSupport_i::getCorbaIndex()throw(SALOME::SALOME_Exception) { - if (_subMeshDS == NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - MESSAGE("Not implemented for SMESH_i"); - THROW_SALOME_CORBA_EXCEPTION("Not Implemented ", SALOME::BAD_PARAM); + if (_subMesh == NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + MESSAGE("Not implemented for SMESH_i"); + THROW_SALOME_CORBA_EXCEPTION("Not Implemented ", SALOME::BAD_PARAM); } @@ -150,10 +137,10 @@ CORBA::Long SMESH_MEDSupport_i::getCorbaIndex()throw(SALOME::SALOME_Exception) char *SMESH_MEDSupport_i::getName() throw(SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - return CORBA::string_dup(_name.c_str()); + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + return CORBA::string_dup(_name.c_str()); } @@ -165,10 +152,10 @@ char *SMESH_MEDSupport_i::getName() throw(SALOME::SALOME_Exception) char *SMESH_MEDSupport_i::getDescription() throw(SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - return CORBA::string_dup(_description.c_str()); + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + return CORBA::string_dup(_description.c_str()); } //============================================================================= @@ -177,14 +164,14 @@ char *SMESH_MEDSupport_i::getDescription() throw(SALOME::SALOME_Exception) */ //============================================================================= -SALOME_MED::MESH_ptr SMESH_MEDSupport_i::getMesh()throw(SALOME:: - SALOME_Exception) +SALOME_MED::GMESH_ptr SMESH_MEDSupport_i::getMesh()throw(SALOME:: + SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); - return _subMesh_i->_mesh_i->GetMEDMesh(); + return _subMesh_i->GetMesh()->GetMEDMesh(); } //============================================================================= @@ -194,36 +181,36 @@ SALOME_MED::MESH_ptr SMESH_MEDSupport_i::getMesh()throw(SALOME:: //============================================================================= CORBA::Boolean SMESH_MEDSupport_i::isOnAllElements()throw(SALOME:: - SALOME_Exception) + SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - if (_seqNumber == false) - { - if (_entity != SALOME_MED::MED_NONE) - { - _seqLength = _subMeshDS->NbNodes(); - _seqNumber = true; - } - else - { - MESSAGE("Only Node Families are implemented "); - THROW_SALOME_CORBA_EXCEPTION("Not implemented Yet ", - SALOME::BAD_PARAM); - } - } - try - { - _isOnAllElements = (_seqLength == _meshDS->NbNodes()); - } - catch(...) - { - MESSAGE("unable to acces related Mesh"); - THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", - SALOME::INTERNAL_ERROR); - }; - return _isOnAllElements; + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + if (_seqNumber == false) + { + if (_entity != SALOME_MED::MED_NONE) + { + _seqLength = _subMesh_i->GetNumberOfNodes(/*all=*/false); + _seqNumber = true; + } + else + { + MESSAGE("Only Node Families are implemented "); + THROW_SALOME_CORBA_EXCEPTION("Not implemented Yet ", + SALOME::BAD_PARAM); + } + } + try + { + _isOnAllElements = (_seqLength == _subMesh->GetFather()->NbNodes()); + } + catch(...) + { + MESSAGE("unable to acces related Mesh"); + THROW_SALOME_CORBA_EXCEPTION("No associated Mesh", + SALOME::INTERNAL_ERROR); + }; + return _isOnAllElements; } //============================================================================= @@ -233,12 +220,12 @@ CORBA::Boolean SMESH_MEDSupport_i::isOnAllElements()throw(SALOME:: //============================================================================= SALOME_MED::medEntityMesh SMESH_MEDSupport_i::getEntity()throw(SALOME:: - SALOME_Exception) + SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - return _entity; + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + return _entity; } //============================================================================= @@ -248,29 +235,29 @@ SALOME_MED::medEntityMesh SMESH_MEDSupport_i::getEntity()throw(SALOME:: //============================================================================= SALOME_MED::medGeometryElement_array * - SMESH_MEDSupport_i::getTypes()throw(SALOME::SALOME_Exception) + SMESH_MEDSupport_i::getTypes()throw(SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - SALOME_MED::medGeometryElement_array_var myseq = - new SALOME_MED::medGeometryElement_array; - try - { - int mySeqLength = _numberOfGeometricType; - myseq->length(mySeqLength); - for (int i = 0; i < mySeqLength; i++) - { - myseq[i] = _geometricType[i]; - } - } - catch(...) - { - MESSAGE("Exception lors de la recherche des differents types"); - THROW_SALOME_CORBA_EXCEPTION("Unable to acces Support Types", - SALOME::INTERNAL_ERROR); - } - return myseq._retn(); + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + SALOME_MED::medGeometryElement_array_var myseq = + new SALOME_MED::medGeometryElement_array; + try + { + int mySeqLength = _numberOfGeometricType; + myseq->length(mySeqLength); + for (int i = 0; i < mySeqLength; i++) + { + myseq[i] = _geometricType[i]; + } + } + catch(...) + { + MESSAGE("Exception lors de la recherche des differents types"); + THROW_SALOME_CORBA_EXCEPTION("Unable to acces Support Types", + SALOME::INTERNAL_ERROR); + } + return myseq._retn(); } //============================================================================= @@ -280,12 +267,12 @@ SALOME_MED::medGeometryElement_array * */ //============================================================================= CORBA::Long SMESH_MEDSupport_i::getNumberOfElements(SALOME_MED:: - medGeometryElement geomElement) throw(SALOME::SALOME_Exception) + medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); - return _numberOfGeometricType; + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); + return _numberOfGeometricType; } @@ -295,33 +282,36 @@ CORBA::Long SMESH_MEDSupport_i::getNumberOfElements(SALOME_MED:: */ //============================================================================= -SALOME_MED::long_array * SMESH_MEDSupport_i::getNumber( - SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfLong * SMESH_MEDSupport_i::getNumber( + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - if (_subMeshDS==NULL) - THROW_SALOME_CORBA_EXCEPTION("No associated Support", - SALOME::INTERNAL_ERROR); + if (_subMesh==NULL) + THROW_SALOME_CORBA_EXCEPTION("No associated Support", + SALOME::INTERNAL_ERROR); - // A changer s'il ne s agit plus seulement de famille de noeuds - if (geomElement != SALOME_MED::MED_NONE) - THROW_SALOME_CORBA_EXCEPTION("Not implemented", SALOME::BAD_PARAM); + // A changer s'il ne s agit plus seulement de famille de noeuds + if (geomElement != SALOME_MED::MED_NONE) + THROW_SALOME_CORBA_EXCEPTION("Not implemented", SALOME::BAD_PARAM); - SALOME_MED::long_array_var myseq = new SALOME_MED::long_array; + SALOME_TYPES::ListOfLong_var myseq = new SALOME_TYPES::ListOfLong; - int i = 0; - myseq->length(_subMeshDS->NbNodes()); + int i = 0; + myseq->length(_subMesh_i->GetNumberOfNodes(/*all=*/false)); - SMDS_NodeIteratorPtr it = _subMeshDS->GetNodes(); - while(it->more()) - { - myseq[i] = it->next()->GetID(); - i++; - }; + if ( _subMesh->GetSubMeshDS() ) + { + SMDS_NodeIteratorPtr it = _subMesh->GetSubMeshDS()->GetNodes(); + while(it->more()) + { + myseq[i] = it->next()->GetID(); + i++; + }; + } - SCRUTE(myseq->length()); - MESSAGE("End of SMESH_MEDSupport_i::getNumber"); - return myseq._retn(); + SCRUTE(myseq->length()); + MESSAGE("End of SMESH_MEDSupport_i::getNumber"); + return myseq._retn(); } @@ -331,8 +321,8 @@ SALOME_MED::long_array * SMESH_MEDSupport_i::getNumber( */ //============================================================================= -SALOME_MED::long_array * SMESH_MEDSupport_i::getNumberFromFile( - SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfLong * SMESH_MEDSupport_i::getNumberFromFile( + SALOME_MED::medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { return getNumber(geomElement); } @@ -344,12 +334,12 @@ SALOME_MED::long_array * SMESH_MEDSupport_i::getNumberFromFile( */ //============================================================================= -SALOME_MED::long_array * - SMESH_MEDSupport_i::getNumberIndex()throw(SALOME::SALOME_Exception) +SALOME_TYPES::ListOfLong * + SMESH_MEDSupport_i::getNumberIndex()throw(SALOME::SALOME_Exception) { - MESSAGE("Not implemented for SMESH_i"); - THROW_SALOME_CORBA_EXCEPTION("Not Implemented", SALOME::BAD_PARAM); - return NULL; + MESSAGE("Not implemented for SMESH_i"); + THROW_SALOME_CORBA_EXCEPTION("Not Implemented", SALOME::BAD_PARAM); + return NULL; } //============================================================================= /*! @@ -358,10 +348,10 @@ SALOME_MED::long_array * //============================================================================= CORBA::Long SMESH_MEDSupport_i::getNumberOfGaussPoint(SALOME_MED:: - medGeometryElement geomElement) throw(SALOME::SALOME_Exception) + medGeometryElement geomElement) throw(SALOME::SALOME_Exception) { - MESSAGE("Not implemented for SMESH_i"); - return 0; + MESSAGE("Not implemented for SMESH_i"); + return 0; } //============================================================================= /*! @@ -381,7 +371,7 @@ CORBA::Long SMESH_MEDSupport_i::getNumberOfTypes() * included in the support */ //============================================================================= -SALOME_MED::long_array* SMESH_MEDSupport_i::getNumbersOfGaussPoint() +SALOME_TYPES::ListOfLong* SMESH_MEDSupport_i::getNumbersOfGaussPoint() throw (SALOME::SALOME_Exception) { MESSAGE("!!! NOT YET IMPLEMENTED !!!!"); diff --git a/src/SMESH_I/SMESH_MEDSupport_i.hxx b/src/SMESH_I/SMESH_MEDSupport_i.hxx index 15cf3b8f4..67761e2e8 100644 --- a/src/SMESH_I/SMESH_MEDSupport_i.hxx +++ b/src/SMESH_I/SMESH_MEDSupport_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MEDSupport_i.hxx // Module : SMESH @@ -39,81 +40,81 @@ #include "SMESH_MEDSupport_i.hxx" #include "SALOME_GenericObj_i.hh" +class SMESH_subMesh; class SMESH_subMesh_i; class SMESH_I_EXPORT SMESH_MEDSupport_i: - public virtual POA_SALOME_MED::SUPPORT, public virtual SALOME::GenericObj_i + public virtual POA_SALOME_MED::SUPPORT, public virtual SALOME::GenericObj_i { public: // Constructors and associated internal methods - SMESH_MEDSupport_i(SMESH_subMesh_i * sm, - std::string name, std::string description, SALOME_MED::medEntityMesh entity); - SMESH_MEDSupport_i(const SMESH_MEDSupport_i & s); + SMESH_MEDSupport_i(SMESH_subMesh_i * sm, + std::string name, std::string description, SALOME_MED::medEntityMesh entity); + SMESH_MEDSupport_i(const SMESH_MEDSupport_i & s); // IDL Methods - char *getName() throw(SALOME::SALOME_Exception); - char *getDescription() throw(SALOME::SALOME_Exception); - SALOME_MED::MESH_ptr getMesh() throw(SALOME::SALOME_Exception); - CORBA::Boolean isOnAllElements() throw(SALOME::SALOME_Exception); - SALOME_MED::medEntityMesh getEntity() throw(SALOME::SALOME_Exception); + char *getName() throw(SALOME::SALOME_Exception); + char *getDescription() throw(SALOME::SALOME_Exception); + SALOME_MED::GMESH_ptr getMesh() throw(SALOME::SALOME_Exception); + CORBA::Boolean isOnAllElements() throw(SALOME::SALOME_Exception); + SALOME_MED::medEntityMesh getEntity() throw(SALOME::SALOME_Exception); CORBA::Long - getNumberOfElements(SALOME_MED::medGeometryElement geomElement) - throw(SALOME::SALOME_Exception); + getNumberOfElements(SALOME_MED::medGeometryElement geomElement) + throw(SALOME::SALOME_Exception); CORBA::Long getNumberOfTypes() throw (SALOME::SALOME_Exception); - SALOME_MED::long_array * - getNumber(SALOME_MED::medGeometryElement geomElement) - throw(SALOME::SALOME_Exception); + SALOME_TYPES::ListOfLong * + getNumber(SALOME_MED::medGeometryElement geomElement) + throw(SALOME::SALOME_Exception); /*! * Same function as getNumber. */ - SALOME_MED::long_array * - getNumberFromFile(SALOME_MED::medGeometryElement geomElement) - throw(SALOME::SALOME_Exception); + SALOME_TYPES::ListOfLong * + getNumberFromFile(SALOME_MED::medGeometryElement geomElement) + throw(SALOME::SALOME_Exception); - SALOME_MED::long_array * getNumberIndex() - throw(SALOME::SALOME_Exception); + SALOME_TYPES::ListOfLong * getNumberIndex() + throw(SALOME::SALOME_Exception); CORBA::Long - getNumberOfGaussPoint(SALOME_MED::medGeometryElement geomElement) - throw(SALOME::SALOME_Exception); + getNumberOfGaussPoint(SALOME_MED::medGeometryElement geomElement) + throw(SALOME::SALOME_Exception); - SALOME_MED::long_array* getNumbersOfGaussPoint() - throw (SALOME::SALOME_Exception); + SALOME_TYPES::ListOfLong* getNumbersOfGaussPoint() + throw (SALOME::SALOME_Exception); - SALOME_MED::medGeometryElement_array *getTypes() - throw(SALOME::SALOME_Exception); + SALOME_MED::medGeometryElement_array *getTypes() + throw(SALOME::SALOME_Exception); void getBoundaryElements() throw (SALOME::SALOME_Exception); - CORBA::Long getCorbaIndex() throw(SALOME::SALOME_Exception); + CORBA::Long getCorbaIndex() throw(SALOME::SALOME_Exception); SALOME_MED::SUPPORT::supportInfos * getSupportGlobal() - throw (SALOME::SALOME_Exception); + throw (SALOME::SALOME_Exception); - void createSeq() throw(SALOME::SALOME_Exception); + void createSeq() throw(SALOME::SALOME_Exception); public: //public field - const SMESHDS_SubMesh * _subMeshDS; - ::SMESH_subMesh_i * _subMesh_i; + SMESH_subMesh_i * _subMesh_i; + ::SMESH_subMesh * _subMesh; - SMESHDS_Mesh * _meshDS; - std::string _name; + std::string _name; std::string _description; - bool _isOnAllElements; - bool _seqNumber; - int _seqLength; + bool _isOnAllElements; + bool _seqNumber; + int _seqLength; - SALOME_MED::medEntityMesh _entity; - SALOME_MED::medGeometryElement * _geometricType; - int _numberOfGeometricType; + SALOME_MED::medEntityMesh _entity; + SALOME_MED::medGeometryElement * _geometricType; + int _numberOfGeometricType; protected: - SMESH_MEDSupport_i(); - ~SMESH_MEDSupport_i(); + SMESH_MEDSupport_i(); + ~SMESH_MEDSupport_i(); }; #endif /* _MED_MEDSUPPORT_I_HXX_ */ diff --git a/src/SMESH_I/SMESH_Measurements_i.cxx b/src/SMESH_I/SMESH_Measurements_i.cxx new file mode 100644 index 000000000..48572a353 --- /dev/null +++ b/src/SMESH_I/SMESH_Measurements_i.cxx @@ -0,0 +1,259 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_Measurements_i.cxx +// Author : Pavel TELKOV, Open CASCADE S.A.S. (pavel.telkov@opencascade.com) + +#include "SMESH_Measurements_i.hxx" + +#include "SMESH_Gen_i.hxx" +#include "SMESH_PythonDump.hxx" + +#include "SMDS_Mesh.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_MeshElement.hxx" +#include "SMDS_ElemIterator.hxx" + +#include "SMESHDS_Mesh.hxx" + + +using namespace SMESH; + +/** + * this local function to avoid uninitialized fields + */ +static void initMeasure( SMESH::Measure& theMeasure) +{ + + theMeasure.minX = theMeasure.minY = theMeasure.minZ = 0.; + theMeasure.maxX = theMeasure.maxY = theMeasure.maxZ = 0.; + theMeasure.node1 = theMeasure.node2 = -1; + theMeasure.elem1 = theMeasure.elem2 = -1; + theMeasure.value = 0.; +} + +//============================================================================= +/*! + * SMESH_Gen_i::CreateMeasurements + * + * Create measurement instance + */ +//============================================================================= + +SMESH::Measurements_ptr SMESH_Gen_i::CreateMeasurements() +{ + SMESH::Measurements_i* aMeasure = new SMESH::Measurements_i(); + SMESH::Measurements_var anObj = aMeasure->_this(); + return anObj._retn(); +} + + +/* + Class : Measurements + Description : make measure of mesh qunatities +*/ + +//======================================================================= +// name : Measurements_i +// Purpose : Constructor +//======================================================================= +Measurements_i::Measurements_i() +: SALOME::GenericObj_i( SMESH_Gen_i::GetPOA() ) +{ + //Base class Salome_GenericObject do it inmplicitly by overriding PortableServer::POA_ptr _default_POA() method + //PortableServer::ObjectId_var anObjectId = + // SMESH_Gen_i::GetPOA()->activate_object( this ); +} + +//======================================================================= +// name : ~Measurements_i +// Purpose : Destructor +//======================================================================= +Measurements_i::~Measurements_i() +{ + //TPythonDump()<X(); if (theNode2) dd -= theNode2->X(); theMeasure.minX = dd; dd *= dd; dist += dd; + dd = theNode1->Y(); if (theNode2) dd -= theNode2->Y(); theMeasure.minY = dd; dd *= dd; dist += dd; + dd = theNode1->Z(); if (theNode2) dd -= theNode2->Z(); theMeasure.minZ = dd; dd *= dd; dist += dd; + + if (dist < 0) + return false; + + theMeasure.value = sqrt(dist); + theMeasure.node1 = theNode1->GetID(); + theMeasure.node2 = theNode2 ? theNode2->GetID() : 0; + + return true; +} + +static SMESHDS_Mesh* getMesh(SMESH::SMESH_IDSource_ptr theSource) +{ + if (!CORBA::is_nil( theSource )) + { + SMESH_Mesh_i* anImplPtr = DownCast(theSource->GetMesh()); + if (anImplPtr) + return anImplPtr->GetImpl().GetMeshDS(); + } + return 0; +} + +static bool isNodeType (SMESH::array_of_ElementType_var theTypes) +{ + return theTypes->length() > 0 && theTypes[0] == SMESH::NODE; +} + +//======================================================================= +// name : MinDistance +// Purpose : minimal distance between two given entities +//======================================================================= +SMESH::Measure Measurements_i::MinDistance + (SMESH::SMESH_IDSource_ptr theSource1, + SMESH::SMESH_IDSource_ptr theSource2) +{ + SMESH::Measure aMeasure; + initMeasure(aMeasure); + + if (CORBA::is_nil( theSource1 )) + return aMeasure; + + // if second source is null, min distance from theSource1 to the origin is calculated + bool isOrigin = CORBA::is_nil( theSource2 ); + + // calculate minimal distance between two mesh entities + SMESH::array_of_ElementType_var types1 = theSource1->GetTypes(); + SMESH::array_of_ElementType_var types2; + if ( !isOrigin ) types2 = theSource2->GetTypes(); + + // here we assume that type of all IDs defined by first type in array + bool isNode1 = isNodeType(types1); + bool isNode2 = isOrigin || isNodeType(types2); + + SMESH::long_array_var aElementsId1 = theSource1->GetIDs(); + SMESH::long_array_var aElementsId2; + if ( !isOrigin ) aElementsId2 = theSource2->GetIDs(); + + // compute distance between two entities + /* NOTE: currently only node-to-node case is implemented + * all other cases will be implemented later + * below IF should be replaced by complete switch + * on mesh entities types + */ + if (isNode1 && isNode2) + { + // node - node + const SMESHDS_Mesh* aMesh1 = getMesh( theSource1 ); + const SMESHDS_Mesh* aMesh2 = isOrigin ? 0 : getMesh( theSource2 ); + const SMDS_MeshNode* theNode1 = aMesh1 ? aMesh1->FindNode( aElementsId1[0] ) : 0; + const SMDS_MeshNode* theNode2 = aMesh2 ? aMesh2->FindNode( aElementsId2[0] ) : 0; + getNodeNodeDistance( aMeasure, theNode1, theNode2 ); + } + else + { + // NOT_IMPLEMENTED + } + + return aMeasure; +} + +//======================================================================= +// name : enlargeBoundingBox +// Purpose : +//======================================================================= +static void enlargeBoundingBox(const SMDS_MeshNode* theNode, + SMESH::Measure& theMeasure) +{ + if (!theNode) + return; + if ( theMeasure.node1 == -1 ) { + // we use this attribute as a flag that it is the first node added to the bnd box + theMeasure.minX = theMeasure.maxX = theNode->X(); + theMeasure.minY = theMeasure.maxY = theNode->Y(); + theMeasure.minZ = theMeasure.maxZ = theNode->Z(); + theMeasure.node1 = theNode->GetID(); + } + else { + theMeasure.minX = min( theMeasure.minX, theNode->X() ); + theMeasure.maxX = max( theMeasure.maxX, theNode->X() ); + theMeasure.minY = min( theMeasure.minY, theNode->Y() ); + theMeasure.maxY = max( theMeasure.maxY, theNode->Y() ); + theMeasure.minZ = min( theMeasure.minZ, theNode->Z() ); + theMeasure.maxZ = max( theMeasure.maxZ, theNode->Z() ); + } +} + +//======================================================================= +// name : enlargeBoundingBox +// Purpose : +//======================================================================= +static void enlargeBoundingBox(const SMESH::SMESH_IDSource_ptr theObject, + SMESH::Measure& theMeasure) +{ + if ( CORBA::is_nil( theObject ) ) + return; + const SMESHDS_Mesh* aMesh = getMesh( theObject ); + if ( !aMesh ) + return; + SMESH::array_of_ElementType_var types = theObject->GetTypes(); + SMESH::long_array_var aElementsId = theObject->GetIDs(); + // here we assume that type of all IDs defined by first type in array + const bool isNode = isNodeType( types ); + for(int i = 0, n = aElementsId->length(); i < n; i++) + { + if (isNode) + enlargeBoundingBox( aMesh->FindNode( aElementsId[i] ), theMeasure); + else + { + const SMDS_MeshElement* elem = aMesh->FindElement( aElementsId[i] ); + if (!elem) + continue; + SMDS_ElemIteratorPtr aNodeIter = elem->nodesIterator(); + while( aNodeIter->more() ) + enlargeBoundingBox( dynamic_cast( aNodeIter->next() ), theMeasure); + } + } +} + +//======================================================================= +// name : BoundingBox +// Purpose : compute common bounding box of entities +//======================================================================= +SMESH::Measure Measurements_i::BoundingBox (const SMESH::ListOfIDSources& theSources) +{ + SMESH::Measure aMeasure; + initMeasure(aMeasure); + + // calculate bounding box on sources + for ( int i = 0, n = theSources.length(); i < n ; ++i ) + enlargeBoundingBox( theSources[i], aMeasure ); + + return aMeasure; +} diff --git a/src/SMESH_I/SMESH_Measurements_i.hxx b/src/SMESH_I/SMESH_Measurements_i.hxx new file mode 100644 index 000000000..df380ab3e --- /dev/null +++ b/src/SMESH_I/SMESH_Measurements_i.hxx @@ -0,0 +1,63 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_Measurements_i.hxx +// Author : Pavel TELKOV, Open CASCADE S.A.S. (pavel.telkov@opencascade.com) + +#ifndef _SMESH_MEASUREMENTS_I_HXX_ +#define _SMESH_MEASUREMENTS_I_HXX_ + +#include "SMESH.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_Measurements) + +#include "SALOME_GenericObj_i.hh" + +class SMESHDS_Mesh; + +namespace SMESH +{ + + /* + Measurements + */ + class SMESH_I_EXPORT Measurements_i: public virtual POA_SMESH::Measurements, + public virtual SALOME::GenericObj_i + { + public: + Measurements_i(); + ~Measurements_i(); + + /*! + * minimal distance between two given entities + */ + SMESH::Measure MinDistance(SMESH::SMESH_IDSource_ptr theSource1, + SMESH::SMESH_IDSource_ptr theSource2); + + /*! + * common bounding box of entities + */ + SMESH::Measure BoundingBox(const SMESH::ListOfIDSources& theSources); + }; +} + +#endif diff --git a/src/SMESH_I/SMESH_MeshEditor_i.cxx b/src/SMESH_I/SMESH_MeshEditor_i.cxx index 8fb61b896..026eed079 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.cxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.cxx @@ -1,40 +1,48 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MeshEditor_i.cxx // Author : Nicolas REJNERI // Module : SMESH +#ifdef WNT +#define NOMINMAX +#endif + #include "SMESH_MeshEditor_i.hxx" -#include "SMDS_MeshEdge.hxx" +#include "SMDS_LinearEdge.hxx" +#include "SMDS_Mesh0DElement.hxx" #include "SMDS_MeshFace.hxx" #include "SMDS_MeshVolume.hxx" #include "SMDS_PolyhedralVolumeOfNodes.hxx" -#include "SMESH_MeshEditor.hxx" -#include "SMESH_subMeshEventListener.hxx" -#include "SMESH_Gen_i.hxx" +#include "SMDS_SetIterator.hxx" +#include "SMESHDS_Group.hxx" +#include "SMESH_ControlsDef.hxx" #include "SMESH_Filter_i.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group_i.hxx" #include "SMESH_PythonDump.hxx" +#include "SMESH_subMeshEventListener.hxx" +#include "SMESH_subMesh_i.hxx" #include "utilities.h" #include "Utils_ExceptHandlers.hxx" @@ -61,11 +69,13 @@ #endif #include +#include #define cast2Node(elem) static_cast( elem ) using namespace std; using SMESH::TPythonDump; +using SMESH::TVar; namespace { @@ -80,12 +90,12 @@ namespace { SMDSAbs_ElementType myPreviewType; // type to show //!< Constructor TPreviewMesh(SMDSAbs_ElementType previewElements = SMDSAbs_All) { - _isShapeToMesh = _id =_studyId =_idDoc = 0; + _isShapeToMesh = (_id =_studyId =_idDoc = 0); _myMeshDS = new SMESHDS_Mesh( _id, true ); myPreviewType = previewElements; } //!< Destructor - virtual ~TPreviewMesh() { delete _myMeshDS; } + virtual ~TPreviewMesh() { delete _myMeshDS; _myMeshDS = 0; } //!< Copy a set of elements void Copy(const TIDSortedElemSet & theElements, TIDSortedElemSet& theCopyElements, @@ -102,8 +112,12 @@ namespace { if ( type == theAvoidType || ( theSelectType != SMDSAbs_All && type != theSelectType )) continue; - - if ( const SMDS_MeshElement* anElemCopy = Copy( anElem )) + const SMDS_MeshElement* anElemCopy; + if ( type == SMDSAbs_Node) + anElemCopy = Copy( cast2Node(anElem) ); + else + anElemCopy = Copy( anElem ); + if ( anElemCopy ) theCopyElements.insert( theCopyElements.end(), anElemCopy ); } } @@ -125,11 +139,11 @@ namespace { SMDS_MeshElement* anElemCopy = 0; if ( anElem->IsPoly() && anElem->GetType() == SMDSAbs_Volume ) { - const SMDS_PolyhedralVolumeOfNodes* ph = - dynamic_cast (anElem); + const SMDS_VtkVolume* ph = + dynamic_cast (anElem); if ( ph ) anElemCopy = _myMeshDS->AddPolyhedralVolumeWithID - (anElemNodesID, ph->GetQuanities(),anElem->GetID()); + (anElemNodesID, ph->GetQuantities(),anElem->GetID()); } else { anElemCopy = ::SMESH_MeshEditor(this).AddElement( anElemNodesID, @@ -141,29 +155,33 @@ namespace { //!< Copy a node SMDS_MeshNode* Copy( const SMDS_MeshNode* anElemNode ) { - return _myMeshDS->AddNodeWithID(anElemNode->X(), anElemNode->Y(), anElemNode->Z(), + return _myMeshDS->AddNodeWithID(anElemNode->X(), anElemNode->Y(), anElemNode->Z(), anElemNode->GetID()); } };// struct TPreviewMesh - static SMESH_NodeSearcher * myNodeSearcher = 0; + static SMESH_NodeSearcher * theNodeSearcher = 0; + static SMESH_ElementSearcher * theElementSearcher = 0; //============================================================================= /*! - * \brief Deleter of myNodeSearcher at any compute event occured + * \brief Deleter of theNodeSearcher at any compute event occured */ //============================================================================= - struct TNodeSearcherDeleter : public SMESH_subMeshEventListener + struct TSearchersDeleter : public SMESH_subMeshEventListener { SMESH_Mesh* myMesh; + string myMeshPartIOR; //!< Constructor - TNodeSearcherDeleter(): SMESH_subMeshEventListener( false ), // won't be deleted by submesh - myMesh(0) {} - //!< Delete myNodeSearcher + TSearchersDeleter(): SMESH_subMeshEventListener( false, // won't be deleted by submesh + "SMESH_MeshEditor_i::TSearchersDeleter"), + myMesh(0) {} + //!< Delete theNodeSearcher static void Delete() { - if ( myNodeSearcher ) { delete myNodeSearcher; myNodeSearcher = 0; } + if ( theNodeSearcher ) delete theNodeSearcher; theNodeSearcher = 0; + if ( theElementSearcher ) delete theElementSearcher; theElementSearcher = 0; } typedef map < int, SMESH_subMesh * > TDependsOnMap; //!< The meshod called by submesh: do my main job @@ -175,19 +193,23 @@ namespace { Unset( sm->GetFather() ); } } - //!< set self on all submeshes and delete myNodeSearcher if other mesh is set - void Set(SMESH_Mesh* mesh) + //!< set self on all submeshes and delete theNodeSearcher if other mesh is set + void Set(SMESH_Mesh* mesh, const string& meshPartIOR = string()) { - if ( myMesh && myMesh != mesh ) { - Delete(); - Unset( myMesh ); - } - myMesh = mesh; - if ( SMESH_subMesh* myMainSubMesh = mesh->GetSubMeshContaining(1) ) { - const TDependsOnMap & subMeshes = myMainSubMesh->DependsOn(); - TDependsOnMap::const_iterator sm; - for (sm = subMeshes.begin(); sm != subMeshes.end(); sm++) - sm->second->SetEventListener( this, 0, sm->second ); + if ( myMesh != mesh || myMeshPartIOR != meshPartIOR) + { + if ( myMesh ) { + Delete(); + Unset( myMesh ); + } + myMesh = mesh; + myMeshPartIOR = meshPartIOR; + if ( SMESH_subMesh* myMainSubMesh = mesh->GetSubMeshContaining(1) ) { + const TDependsOnMap & subMeshes = myMainSubMesh->DependsOn(); + TDependsOnMap::const_iterator sm; + for (sm = subMeshes.begin(); sm != subMeshes.end(); sm++) + sm->second->SetEventListener( this, 0, sm->second ); + } } } //!< delete self from all submeshes @@ -199,8 +221,10 @@ namespace { for (sm = subMeshes.begin(); sm != subMeshes.end(); sm++) sm->second->DeleteEventListener( this ); } + myMesh = 0; } - }; + + } theSearchersDeleter; TCollection_AsciiString mirrorTypeName( SMESH::SMESH_MeshEditor::MirrorType theMirrorType ) { @@ -217,6 +241,162 @@ namespace { } return typeStr; } + //================================================================================ + /*! + * \brief function for conversion of long_array to TIDSortedElemSet + * \param IDs - array of IDs + * \param aMesh - mesh + * \param aMap - collection to fill + * \param aType - element type + */ + //================================================================================ + + void arrayToSet(const SMESH::long_array & IDs, + const SMESHDS_Mesh* aMesh, + TIDSortedElemSet& aMap, + const SMDSAbs_ElementType aType = SMDSAbs_All ) + { + for (int i=0; iFindNode(ind) : aMesh->FindElement(ind)); + if ( elem && ( aType == SMDSAbs_All || elem->GetType() == aType )) + aMap.insert( elem ); + } + } + //================================================================================ + /*! + * \brief Retrieve elements of given type from SMESH_IDSource + */ + //================================================================================ + + bool idSourceToSet(SMESH::SMESH_IDSource_ptr theIDSource, + const SMESHDS_Mesh* theMeshDS, + TIDSortedElemSet& theElemSet, + const SMDSAbs_ElementType theType, + const bool emptyIfIsMesh=false) + + { + if ( CORBA::is_nil( theIDSource ) ) + return false; + if ( emptyIfIsMesh && SMESH::DownCast( theIDSource )) + return true; + + SMESH::long_array_var anIDs = theIDSource->GetIDs(); + if ( anIDs->length() == 0 ) + return false; + SMESH::array_of_ElementType_var types = theIDSource->GetTypes(); + if ( types->length() == 1 && types[0] == SMESH::NODE ) // group of nodes + { + if ( theType == SMDSAbs_All || theType == SMDSAbs_Node ) + arrayToSet( anIDs, theMeshDS, theElemSet, SMDSAbs_Node ); + else + return false; + } + else + { + arrayToSet( anIDs, theMeshDS, theElemSet, theType); + return bool(anIDs->length()) == bool(theElemSet.size()); + } + return true; + } + //================================================================================ + /*! + * \brief Retrieve nodes from SMESH_IDSource + */ + //================================================================================ + + void idSourceToNodeSet(SMESH::SMESH_IDSource_ptr theObject, + const SMESHDS_Mesh* theMeshDS, + TIDSortedNodeSet& theNodeSet) + + { + if ( CORBA::is_nil( theObject ) ) + return; + SMESH::array_of_ElementType_var types = theObject->GetTypes(); + SMESH::long_array_var aElementsId = theObject->GetIDs(); + if ( types->length() == 1 && types[0] == SMESH::NODE) + { + for(int i = 0; i < aElementsId->length(); i++) + if ( const SMDS_MeshNode * n = theMeshDS->FindNode( aElementsId[i] )) + theNodeSet.insert( theNodeSet.end(), n); + } + else if ( SMESH::DownCast( theObject )) + { + SMDS_NodeIteratorPtr nIt = theMeshDS->nodesIterator(); + while ( nIt->more( )) + if( const SMDS_MeshElement * elem = nIt->next() ) + theNodeSet.insert( elem->begin_nodes(), elem->end_nodes()); + } + else + { + for(int i = 0; i < aElementsId->length(); i++) + if( const SMDS_MeshElement * elem = theMeshDS->FindElement( aElementsId[i] )) + theNodeSet.insert( elem->begin_nodes(), elem->end_nodes()); + } + } + + //================================================================================ + /*! + * \brief Returns elements connected to the given elements + */ + //================================================================================ + + void getElementsAround(const TIDSortedElemSet& theElements, + const SMESHDS_Mesh* theMeshDS, + TIDSortedElemSet& theElementsAround) + { + if ( theElements.empty() ) return; + + SMDSAbs_ElementType elemType = (*theElements.begin())->GetType(); + bool sameElemType = ( elemType == (*theElements.rbegin())->GetType() ); + if ( sameElemType && + theMeshDS->GetMeshInfo().NbElements( elemType ) == theElements.size() ) + return; // all the elements are in theElements + + if ( !sameElemType ) + elemType = SMDSAbs_All; + + TIDSortedElemSet visitedNodes; + TIDSortedElemSet::const_iterator elemIt = theElements.begin(); + for ( ; elemIt != theElements.end(); ++elemIt ) + { + const SMDS_MeshElement* e = *elemIt; + int i = e->NbCornerNodes(); + while ( --i != -1 ) + { + const SMDS_MeshNode* n = e->GetNode( i ); + if ( visitedNodes.insert( n ).second ) + { + SMDS_ElemIteratorPtr invIt = n->GetInverseElementIterator(elemType); + while ( invIt->more() ) + { + const SMDS_MeshElement* elemAround = invIt->next(); + if ( !theElements.count( elemAround )) + theElementsAround.insert( elemAround ); + } + } + } + } + } + + //================================================================================ + /*! + * \brief Return a string used to detect change of mesh part on which theElementSearcher + * is going to be used + */ + //================================================================================ + + string getPartIOR( SMESH::SMESH_IDSource_ptr theMeshPart, SMESH::ElementType type) + { + string partIOR = SMESH_Gen_i::GetORB()->object_to_string( theMeshPart ); + if ( SMESH_Group_i* group_i = SMESH::DownCast( theMeshPart )) + // take into account passible group modification + partIOR += SMESH_Comment( ((SMESHDS_Group*)group_i->GetGroupDS())->SMDSGroup().Tic() ); + partIOR += SMESH_Comment( type ); + return partIOR; + } + } //============================================================================= @@ -225,11 +405,12 @@ namespace { */ //============================================================================= -SMESH_MeshEditor_i::SMESH_MeshEditor_i(SMESH_Mesh_i* theMesh, bool isPreview) +SMESH_MeshEditor_i::SMESH_MeshEditor_i(SMESH_Mesh_i* theMesh, bool isPreview): + myMesh_i( theMesh ), + myMesh( &theMesh->GetImpl() ), + myEditor( myMesh ), + myPreviewMode ( isPreview ) { - myMesh_i = theMesh; - myMesh = & theMesh->GetImpl(); - myPreviewMode = isPreview; } //================================================================================ @@ -248,16 +429,173 @@ SMESH_MeshEditor_i::~SMESH_MeshEditor_i() */ //================================================================================ -void SMESH_MeshEditor_i::initData() +void SMESH_MeshEditor_i::initData(bool deleteSearchers) { if ( myPreviewMode ) { - myPreviewData = new SMESH::MeshPreviewStruct(); + //myPreviewData = new SMESH::MeshPreviewStruct(); } else { - myLastCreatedElems = new SMESH::long_array(); - myLastCreatedNodes = new SMESH::long_array(); - TNodeSearcherDeleter::Delete(); + if ( deleteSearchers ) + TSearchersDeleter::Delete(); + } +} + +//================================================================================ +/*! + * \brief Now does nothing + */ +//================================================================================ + +void SMESH_MeshEditor_i::storeResult(::SMESH_MeshEditor& ) +{ +} + +//================================================================================ +/*! + * Return data of mesh edition preview + */ +//================================================================================ + +SMESH::MeshPreviewStruct* SMESH_MeshEditor_i::GetPreviewData() +{ + if ( myPreviewMode ) { // --- MeshPreviewStruct filling --- + + list aNodesConnectivity; + typedef map TNodesMap; + TNodesMap nodesMap; + + TPreviewMesh * aPreviewMesh = dynamic_cast< TPreviewMesh* >( myEditor.GetMesh() ); + SMDSAbs_ElementType previewType = aPreviewMesh->myPreviewType; + + SMESHDS_Mesh* aMeshDS = myEditor.GetMeshDS(); + int nbEdges = aMeshDS->NbEdges(); + int nbFaces = aMeshDS->NbFaces(); + int nbVolum = aMeshDS->NbVolumes(); + switch ( previewType ) { + case SMDSAbs_Edge : nbFaces = nbVolum = 0; break; + case SMDSAbs_Face : nbEdges = nbVolum = 0; break; + case SMDSAbs_Volume: nbEdges = nbFaces = 0; break; + default:; + } + myPreviewData = new SMESH::MeshPreviewStruct(); + myPreviewData->nodesXYZ.length(aMeshDS->NbNodes()); + myPreviewData->elementTypes.length(nbEdges + nbFaces + nbVolum); + int i = 0, j = 0; + SMDS_ElemIteratorPtr itMeshElems = aMeshDS->elementsIterator(); + + while ( itMeshElems->more() ) { + const SMDS_MeshElement* aMeshElem = itMeshElems->next(); + if ( previewType != SMDSAbs_All && aMeshElem->GetType() != previewType ) + continue; + + SMDS_ElemIteratorPtr itElemNodes = aMeshElem->nodesIterator(); + while ( itElemNodes->more() ) { + const SMDS_MeshNode* aMeshNode = + static_cast( itElemNodes->next() ); + int aNodeID = aMeshNode->GetID(); + TNodesMap::iterator anIter = nodesMap.find(aNodeID); + if ( anIter == nodesMap.end() ) { + // filling the nodes coordinates + myPreviewData->nodesXYZ[j].x = aMeshNode->X(); + myPreviewData->nodesXYZ[j].y = aMeshNode->Y(); + myPreviewData->nodesXYZ[j].z = aMeshNode->Z(); + anIter = nodesMap.insert( make_pair(aNodeID, j) ).first; + j++; + } + aNodesConnectivity.push_back(anIter->second); + } + + // filling the elements types + SMDSAbs_ElementType aType = aMeshElem->GetType(); + bool isPoly = aMeshElem->IsPoly(); + + myPreviewData->elementTypes[i].SMDS_ElementType = (SMESH::ElementType) aType; + myPreviewData->elementTypes[i].isPoly = isPoly; + myPreviewData->elementTypes[i].nbNodesInElement = aMeshElem->NbNodes(); + i++; + + } + myPreviewData->nodesXYZ.length( j ); + + // filling the elements connectivities + list::iterator aConnIter = aNodesConnectivity.begin(); + myPreviewData->elementConnectivities.length(aNodesConnectivity.size()); + for( int i = 0; aConnIter != aNodesConnectivity.end(); aConnIter++, i++ ) + myPreviewData->elementConnectivities[i] = *aConnIter; + } + + return myPreviewData._retn(); +} + +//================================================================================ +/*! + * \brief Returns list of it's IDs of created nodes + * \retval SMESH::long_array* - list of node ID + */ +//================================================================================ + +SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedNodes() +{ + SMESH::long_array_var myLastCreatedNodes = new SMESH::long_array(); + const SMESH_SequenceOfElemPtr& aSeq = myEditor.GetLastCreatedNodes(); + myLastCreatedNodes->length( aSeq.Length() ); + for (int i = 1; i <= aSeq.Length(); i++) + myLastCreatedNodes[i-1] = aSeq.Value(i)->GetID(); + return myLastCreatedNodes._retn(); +} + +//================================================================================ +/*! + * \brief Returns list of it's IDs of created elements + * \retval SMESH::long_array* - list of elements' ID + */ +//================================================================================ + +SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedElems() +{ + SMESH::long_array_var myLastCreatedElems = new SMESH::long_array(); + const SMESH_SequenceOfElemPtr& aSeq = myEditor.GetLastCreatedElems(); + myLastCreatedElems->length( aSeq.Length() ); + for ( int i = 1; i <= aSeq.Length(); i++ ) + myLastCreatedElems[i-1] = aSeq.Value(i)->GetID(); + return myLastCreatedElems._retn(); +} + +//======================================================================= +//function : MakeIDSource +//purpose : Wrap a sequence of ids in a SMESH_IDSource +//======================================================================= + +struct _IDSource : public POA_SMESH::SMESH_IDSource +{ + SMESH::long_array _ids; + SMESH::ElementType _type; + SMESH::SMESH_Mesh_ptr _mesh; + SMESH::long_array* GetIDs() { return new SMESH::long_array( _ids ); } + SMESH::long_array* GetMeshInfo() { return 0; } + SMESH::SMESH_Mesh_ptr GetMesh() { return SMESH::SMESH_Mesh::_duplicate( _mesh ); } + bool IsMeshInfoCorrect() { return true; } + SMESH::array_of_ElementType* GetTypes() + { + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + if ( _ids.length() > 0 ) { + types->length( 1 ); + types[0] = _type; + } + return types._retn(); } +}; + +SMESH::SMESH_IDSource_ptr SMESH_MeshEditor_i::MakeIDSource(const SMESH::long_array& ids, + SMESH::ElementType type) +{ + _IDSource* anIDSource = new _IDSource; + anIDSource->_ids = ids; + anIDSource->_type = type; + anIDSource->_mesh = myMesh_i->_this(); + SMESH::SMESH_IDSource_var anIDSourceVar = anIDSource->_this(); + + return anIDSourceVar._retn(); } //============================================================================= @@ -267,11 +605,10 @@ void SMESH_MeshEditor_i::initData() //============================================================================= CORBA::Boolean - SMESH_MeshEditor_i::RemoveElements(const SMESH::long_array & IDsOfElements) +SMESH_MeshEditor_i::RemoveElements(const SMESH::long_array & IDsOfElements) { initData(); - ::SMESH_MeshEditor anEditor( myMesh ); list< int > IdList; for (int i = 0; i < IDsOfElements.length(); i++) @@ -279,11 +616,13 @@ CORBA::Boolean // Update Python script TPythonDump() << "isDone = " << this << ".RemoveElements( " << IDsOfElements << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'RemoveElements: ', isDone"; -#endif + // Remove Elements - return anEditor.Remove( IdList, false ); + bool ret = myEditor.Remove( IdList, false ); + myMesh->GetMeshDS()->Modified(); + if ( IDsOfElements.length() ) + myMesh->SetIsModified( true ); // issue 0020693 + return ret; } //============================================================================= @@ -296,18 +635,18 @@ CORBA::Boolean SMESH_MeshEditor_i::RemoveNodes(const SMESH::long_array & IDsOfNo { initData(); - ::SMESH_MeshEditor anEditor( myMesh ); list< int > IdList; for (int i = 0; i < IDsOfNodes.length(); i++) IdList.push_back( IDsOfNodes[i] ); // Update Python script TPythonDump() << "isDone = " << this << ".RemoveNodes( " << IDsOfNodes << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'RemoveNodes: ', isDone"; -#endif - return anEditor.Remove( IdList, true ); + bool ret = myEditor.Remove( IdList, true ); + myMesh->GetMeshDS()->Modified(); + if ( IDsOfNodes.length() ) + myMesh->SetIsModified( true ); // issue 0020693 + return ret; } //============================================================================= @@ -316,43 +655,37 @@ CORBA::Boolean SMESH_MeshEditor_i::RemoveNodes(const SMESH::long_array & IDsOfNo */ //============================================================================= -CORBA::Long SMESH_MeshEditor_i::AddEdge(const SMESH::long_array & IDsOfNodes) +CORBA::Long SMESH_MeshEditor_i::RemoveOrphanNodes() { initData(); - int NbNodes = IDsOfNodes.length(); - SMDS_MeshElement* elem = 0; - if (NbNodes == 2) - { - CORBA::Long index1 = IDsOfNodes[0]; - CORBA::Long index2 = IDsOfNodes[1]; - elem = GetMeshDS()->AddEdge(GetMeshDS()->FindNode(index1), GetMeshDS()->FindNode(index2)); - // Update Python script - TPythonDump() << "edge = " << this << ".AddEdge([ " - << index1 << ", " << index2 <<" ])"; - } - if (NbNodes == 3) { - CORBA::Long n1 = IDsOfNodes[0]; - CORBA::Long n2 = IDsOfNodes[1]; - CORBA::Long n12 = IDsOfNodes[2]; - elem = GetMeshDS()->AddEdge(GetMeshDS()->FindNode(n1), - GetMeshDS()->FindNode(n2), - GetMeshDS()->FindNode(n12)); - // Update Python script - TPythonDump() << "edgeID = " << this << ".AddEdge([ " - <GetID(); + // Create filter to find all orphan nodes + SMESH::Controls::Filter::TIdSequence seq; + SMESH::Controls::PredicatePtr predicate( new SMESH::Controls::FreeNodes() ); + SMESH::Controls::Filter::GetElementsId( GetMeshDS(), predicate, seq ); - return 0; + // remove orphan nodes (if there are any) + list< int > IdList; + for ( int i = 0; i < seq.size(); i++ ) + IdList.push_back( seq[i] ); + + int nbNodesBefore = myMesh->NbNodes(); + myEditor.Remove( IdList, true ); + myMesh->GetMeshDS()->Modified(); + if ( IdList.size() ) + myMesh->SetIsModified( true ); + int nbNodesAfter = myMesh->NbNodes(); + + return nbNodesBefore - nbNodesAfter; } //============================================================================= /*! - * + * Add a new node. */ //============================================================================= @@ -365,37 +698,136 @@ CORBA::Long SMESH_MeshEditor_i::AddNode(CORBA::Double x, // Update Python script TPythonDump() << "nodeID = " << this << ".AddNode( " - << x << ", " << y << ", " << z << " )"; + << TVar( x ) << ", " << TVar( y ) << ", " << TVar( z )<< " )"; + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); // issue 0020693 return N->GetID(); } //============================================================================= /*! - * AddFace + * Create 0D element on the given node. */ //============================================================================= -CORBA::Long SMESH_MeshEditor_i::AddFace(const SMESH::long_array & IDsOfNodes) +CORBA::Long SMESH_MeshEditor_i::Add0DElement(CORBA::Long IDOfNode) { initData(); - int NbNodes = IDsOfNodes.length(); - if (NbNodes < 3) - { - return 0; - } + const SMDS_MeshNode* aNode = GetMeshDS()->FindNode(IDOfNode); + SMDS_MeshElement* elem = GetMeshDS()->Add0DElement(aNode); - std::vector nodes (NbNodes); - for (int i = 0; i < NbNodes; i++) - nodes[i] = GetMeshDS()->FindNode(IDsOfNodes[i]); + // Update Python script + TPythonDump() << "elem0d = " << this << ".Add0DElement( " << IDOfNode <<" )"; - SMDS_MeshElement* elem = 0; - if (NbNodes == 3) { - elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2]); - } - else if (NbNodes == 4) { - elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3]); + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); // issue 0020693 + + if (elem) + return elem->GetID(); + + return 0; +} + +//============================================================================= +/*! + * Create a ball element on the given node. + */ +//============================================================================= + +CORBA::Long SMESH_MeshEditor_i::AddBall(CORBA::Long IDOfNode, CORBA::Double diameter) + throw (SALOME::SALOME_Exception) +{ + initData(); + + if ( diameter < std::numeric_limits::min() ) + THROW_SALOME_CORBA_EXCEPTION("Invalid diameter", SALOME::BAD_PARAM); + + const SMDS_MeshNode* aNode = GetMeshDS()->FindNode(IDOfNode); + SMDS_MeshElement* elem = GetMeshDS()->AddBall(aNode, diameter); + + // Update Python script + TPythonDump() << "ballElem = " + << this << ".AddBall( " << IDOfNode << ", " << diameter <<" )"; + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); // issue 0020693 + + if (elem) + return elem->GetID(); + + return 0; +} + +//============================================================================= +/*! + * Create an edge, either linear and quadratic (this is determed + * by number of given nodes, two or three) + */ +//============================================================================= + +CORBA::Long SMESH_MeshEditor_i::AddEdge(const SMESH::long_array & IDsOfNodes) +{ + initData(); + + int NbNodes = IDsOfNodes.length(); + SMDS_MeshElement* elem = 0; + if (NbNodes == 2) + { + CORBA::Long index1 = IDsOfNodes[0]; + CORBA::Long index2 = IDsOfNodes[1]; + elem = GetMeshDS()->AddEdge(GetMeshDS()->FindNode(index1), GetMeshDS()->FindNode(index2)); + + // Update Python script + TPythonDump() << "edge = " << this << ".AddEdge([ " + << index1 << ", " << index2 <<" ])"; + } + if (NbNodes == 3) { + CORBA::Long n1 = IDsOfNodes[0]; + CORBA::Long n2 = IDsOfNodes[1]; + CORBA::Long n12 = IDsOfNodes[2]; + elem = GetMeshDS()->AddEdge(GetMeshDS()->FindNode(n1), + GetMeshDS()->FindNode(n2), + GetMeshDS()->FindNode(n12)); + // Update Python script + TPythonDump() << "edgeID = " << this << ".AddEdge([ " + <GetMeshDS()->Modified(); + if(elem) + return myMesh->SetIsModified( true ), elem->GetID(); + + return 0; +} + +//============================================================================= +/*! + * AddFace + */ +//============================================================================= + +CORBA::Long SMESH_MeshEditor_i::AddFace(const SMESH::long_array & IDsOfNodes) +{ + initData(); + + int NbNodes = IDsOfNodes.length(); + if (NbNodes < 3) + { + return 0; + } + + std::vector nodes (NbNodes); + for (int i = 0; i < NbNodes; i++) + nodes[i] = GetMeshDS()->FindNode(IDsOfNodes[i]); + + SMDS_MeshElement* elem = 0; + if (NbNodes == 3) { + elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2]); + } + else if (NbNodes == 4) { + elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3]); } else if (NbNodes == 6) { elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3], @@ -405,12 +837,20 @@ CORBA::Long SMESH_MeshEditor_i::AddFace(const SMESH::long_array & IDsOfNodes) elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3], nodes[4], nodes[5], nodes[6], nodes[7]); } + else if (NbNodes == 9) { + elem = GetMeshDS()->AddFace(nodes[0], nodes[1], nodes[2], nodes[3], + nodes[4], nodes[5], nodes[6], nodes[7], nodes[8] ); + } + else if (NbNodes > 2) { + elem = GetMeshDS()->AddPolygonalFace(nodes); + } // Update Python script TPythonDump() << "faceID = " << this << ".AddFace( " << IDsOfNodes << " )"; + myMesh->GetMeshDS()->Modified(); if(elem) - return elem->GetID(); + return myMesh->SetIsModified( true ), elem->GetID(); return 0; } @@ -420,8 +860,7 @@ CORBA::Long SMESH_MeshEditor_i::AddFace(const SMESH::long_array & IDsOfNodes) * AddPolygonalFace */ //============================================================================= -CORBA::Long SMESH_MeshEditor_i::AddPolygonalFace - (const SMESH::long_array & IDsOfNodes) +CORBA::Long SMESH_MeshEditor_i::AddPolygonalFace (const SMESH::long_array & IDsOfNodes) { initData(); @@ -431,22 +870,18 @@ CORBA::Long SMESH_MeshEditor_i::AddPolygonalFace nodes[i] = GetMeshDS()->FindNode(IDsOfNodes[i]); const SMDS_MeshElement* elem = GetMeshDS()->AddPolygonalFace(nodes); - + // Update Python script TPythonDump() <<"faceID = "<GetID(); - return 0; + myMesh->GetMeshDS()->Modified(); + return elem ? ( myMesh->SetIsModified( true ), elem->GetID()) : 0; } //============================================================================= /*! - * + * Create volume, either linear and quadratic (this is determed + * by number of given nodes) */ //============================================================================= @@ -469,6 +904,9 @@ CORBA::Long SMESH_MeshEditor_i::AddVolume(const SMESH::long_array & IDsOfNodes) case 10:elem = GetMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5], n[6],n[7],n[8],n[9]); break; + case 12:elem = GetMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5], + n[6],n[7],n[8],n[9],n[10],n[11]); + break; case 13:elem = GetMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5],n[6], n[7],n[8],n[9],n[10],n[11],n[12]); break; @@ -479,16 +917,19 @@ CORBA::Long SMESH_MeshEditor_i::AddVolume(const SMESH::long_array & IDsOfNodes) n[8],n[9],n[10],n[11],n[12],n[13],n[14], n[15],n[16],n[17],n[18],n[19]); break; + case 27:elem = GetMeshDS()->AddVolume(n[0],n[1],n[2],n[3],n[4],n[5],n[6],n[7], + n[8],n[9],n[10],n[11],n[12],n[13],n[14], + n[15],n[16],n[17],n[18],n[19], + n[20],n[21],n[22],n[23],n[24],n[25],n[26]); + break; } // Update Python script TPythonDump() << "volID = " << this << ".AddVolume( " << IDsOfNodes << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'AddVolume: ', volID"; -#endif + myMesh->GetMeshDS()->Modified(); if(elem) - return elem->GetID(); + return myMesh->SetIsModified( true ), elem->GetID(); return 0; } @@ -498,16 +939,19 @@ CORBA::Long SMESH_MeshEditor_i::AddVolume(const SMESH::long_array & IDsOfNodes) * AddPolyhedralVolume */ //============================================================================= -CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolume - (const SMESH::long_array & IDsOfNodes, - const SMESH::long_array & Quantities) +CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolume (const SMESH::long_array & IDsOfNodes, + const SMESH::long_array & Quantities) { initData(); int NbNodes = IDsOfNodes.length(); std::vector n (NbNodes); for (int i = 0; i < NbNodes; i++) - n[i] = GetMeshDS()->FindNode(IDsOfNodes[i]); + { + const SMDS_MeshNode* aNode = GetMeshDS()->FindNode(IDsOfNodes[i]); + if (!aNode) return 0; + n[i] = aNode; + } int NbFaces = Quantities.length(); std::vector q (NbFaces); @@ -519,14 +963,9 @@ CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolume // Update Python script TPythonDump() << "volID = " << this << ".AddPolyhedralVolume( " << IDsOfNodes << ", " << Quantities << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'AddPolyhedralVolume: ', volID"; -#endif + myMesh->GetMeshDS()->Modified(); - if(elem) - return elem->GetID(); - - return 0; + return elem ? ( myMesh->SetIsModified( true ), elem->GetID()) : 0; } //============================================================================= @@ -534,8 +973,7 @@ CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolume * AddPolyhedralVolumeByFaces */ //============================================================================= -CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolumeByFaces - (const SMESH::long_array & IdsOfFaces) +CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolumeByFaces (const SMESH::long_array & IdsOfFaces) { initData(); @@ -558,14 +996,9 @@ CORBA::Long SMESH_MeshEditor_i::AddPolyhedralVolumeByFaces // Update Python script TPythonDump() << "volID = " << this << ".AddPolyhedralVolumeByFaces( " << IdsOfFaces << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'AddPolyhedralVolume: ', volID"; -#endif - - if(elem) - return elem->GetID(); + myMesh->GetMeshDS()->Modified(); - return 0; + return elem ? ( myMesh->SetIsModified( true ), elem->GetID()) : 0; } //============================================================================= @@ -595,6 +1028,8 @@ void SMESH_MeshEditor_i::SetNodeOnVertex(CORBA::Long NodeID, CORBA::Long VertexI THROW_SALOME_CORBA_EXCEPTION("Invalid VertexID", SALOME::BAD_PARAM); mesh->SetNodeOnVertex( node, VertexID ); + + myMesh->SetIsModified( true ); } //============================================================================= @@ -631,6 +1066,8 @@ void SMESH_MeshEditor_i::SetNodeOnEdge(CORBA::Long NodeID, CORBA::Long EdgeID, THROW_SALOME_CORBA_EXCEPTION("Invalid paramOnEdge", SALOME::BAD_PARAM); mesh->SetNodeOnEdge( node, EdgeID, paramOnEdge ); + + myMesh->SetIsModified( true ); } //============================================================================= @@ -671,15 +1108,16 @@ void SMESH_MeshEditor_i::SetNodeOnFace(CORBA::Long NodeID, CORBA::Long FaceID, if ( isOut ) { #ifdef _DEBUG_ MESSAGE ( "FACE " << FaceID << " (" << u << "," << v << ") out of " - << " u( " << surf.FirstUParameter() - << "," << surf.LastUParameter() - << ") v( " << surf.FirstVParameter() - << "," << surf.LastVParameter() << ")" ); -#endif + << " u( " << surf.FirstUParameter() + << "," << surf.LastUParameter() + << ") v( " << surf.FirstVParameter() + << "," << surf.LastVParameter() << ")" ); +#endif THROW_SALOME_CORBA_EXCEPTION("Invalid UV", SALOME::BAD_PARAM); } mesh->SetNodeOnFace( node, FaceID, u, v ); + myMesh->SetIsModified( true ); } //============================================================================= @@ -710,6 +1148,8 @@ void SMESH_MeshEditor_i::SetNodeInVolume(CORBA::Long NodeID, CORBA::Long SolidID THROW_SALOME_CORBA_EXCEPTION("Invalid SolidID", SALOME::BAD_PARAM); mesh->SetNodeInVolume( node, SolidID ); + + // myMesh->SetIsModified( true ); - SetNodeInVolume() can't prevent re-compute, I believe } //============================================================================= @@ -743,33 +1183,8 @@ void SMESH_MeshEditor_i::SetMeshElementOnShape(CORBA::Long ElementID, THROW_SALOME_CORBA_EXCEPTION("Invalid shape type", SALOME::BAD_PARAM); mesh->SetMeshElementOnShape( elem, ShapeID ); -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -CORBA::Boolean SMESH_MeshEditor_i::MoveNode(CORBA::Long NodeID, - CORBA::Double x, - CORBA::Double y, - CORBA::Double z) -{ - initData(); - - const SMDS_MeshNode * node = GetMeshDS()->FindNode( NodeID ); - if ( !node ) - return false; - - GetMeshDS()->MoveNode(node, x, y, z); - - // Update Python script - TPythonDump() << "isDone = " << this << ".MoveNode( " - << NodeID << ", " << x << ", " << y << ", " << z << " )"; - - return true; + myMesh->SetIsModified( true ); } //============================================================================= @@ -792,8 +1207,11 @@ CORBA::Boolean SMESH_MeshEditor_i::InverseDiag(CORBA::Long NodeID1, TPythonDump() << "isDone = " << this << ".InverseDiag( " << NodeID1 << ", " << NodeID2 << " )"; - ::SMESH_MeshEditor aMeshEditor( myMesh ); - return aMeshEditor.InverseDiag ( n1, n2 ); + + int ret = myEditor.InverseDiag ( n1, n2 ); + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + return ret; } //============================================================================= @@ -816,11 +1234,14 @@ CORBA::Boolean SMESH_MeshEditor_i::DeleteDiag(CORBA::Long NodeID1, TPythonDump() << "isDone = " << this << ".DeleteDiag( " << NodeID1 << ", " << NodeID2 << " )"; - ::SMESH_MeshEditor aMeshEditor( myMesh ); - bool stat = aMeshEditor.DeleteDiag ( n1, n2 ); + bool stat = myEditor.DeleteDiag ( n1, n2 ); - storeResult(aMeshEditor); + myMesh->GetMeshDS()->Modified(); + if ( stat ) + myMesh->SetIsModified( true ); // issue 0020693 + + storeResult(myEditor); return stat; } @@ -835,17 +1256,20 @@ CORBA::Boolean SMESH_MeshEditor_i::Reorient(const SMESH::long_array & IDsOfEleme { initData(); - ::SMESH_MeshEditor anEditor( myMesh ); for (int i = 0; i < IDsOfElements.length(); i++) { CORBA::Long index = IDsOfElements[i]; const SMDS_MeshElement * elem = GetMeshDS()->FindElement(index); if ( elem ) - anEditor.Reorient( elem ); + myEditor.Reorient( elem ); } // Update Python script TPythonDump() << "isDone = " << this << ".Reorient( " << IDsOfElements << " )"; + myMesh->GetMeshDS()->Modified(); + if ( IDsOfElements.length() ) + myMesh->SetIsModified( true ); // issue 0020693 + return true; } @@ -860,43 +1284,102 @@ CORBA::Boolean SMESH_MeshEditor_i::ReorientObject(SMESH::SMESH_IDSource_ptr theO { initData(); + TPythonDump aTPythonDump; // suppress dump in Reorient() + SMESH::long_array_var anElementsId = theObject->GetIDs(); CORBA::Boolean isDone = Reorient(anElementsId); - // Clear python line, created by Reorient() - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); - // Update Python script - TPythonDump() << "isDone = " << this << ".ReorientObject( " << theObject << " )"; + aTPythonDump << "isDone = " << this << ".ReorientObject( " << theObject << " )"; return isDone; } -namespace +//======================================================================= +//function : Reorient2D +//purpose : Reorient faces contained in \a the2Dgroup. +// the2Dgroup - the mesh or its part to reorient +// theDirection - desired direction of normal of \a theFace +// theFace - ID of face whose orientation is checked. +// It can be < 1 then \a thePoint is used to find a face. +// thePoint - is used to find a face if \a theFace < 1. +// return number of reoriented elements. +//======================================================================= + +CORBA::Long SMESH_MeshEditor_i::Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup, + const SMESH::DirStruct& theDirection, + CORBA::Long theFace, + const SMESH::PointStruct& thePoint) + throw (SALOME::SALOME_Exception) { - //================================================================================ - /*! - * \brief function for conversion long_array to TIDSortedElemSet - * \param IDs - array of IDs - * \param aMesh - mesh - * \param aMap - collection to fill - * \param aType - element type - */ - //================================================================================ + Unexpect aCatch(SALOME_SalomeException); - void arrayToSet(const SMESH::long_array & IDs, - const SMESHDS_Mesh* aMesh, - TIDSortedElemSet& aMap, - const SMDSAbs_ElementType aType = SMDSAbs_All ) - { - for (int i=0; iFindElement(ind); - if ( elem && ( aType == SMDSAbs_All || elem->GetType() == aType )) - aMap.insert( elem ); + initData(/*deleteSearchers=*/false); + + TIDSortedElemSet elements; + if ( !idSourceToSet( the2Dgroup, GetMeshDS(), elements, SMDSAbs_Face, /*emptyIfIsMesh=*/1)) + THROW_SALOME_CORBA_EXCEPTION("No faces in given group", SALOME::BAD_PARAM); + + + const SMDS_MeshElement* face = 0; + if ( theFace > 0 ) + { + face = GetMeshDS()->FindElement( theFace ); + if ( !face ) + THROW_SALOME_CORBA_EXCEPTION("Inexistent face given", SALOME::BAD_PARAM); + if ( face->GetType() != SMDSAbs_Face ) + THROW_SALOME_CORBA_EXCEPTION("Wrong element type", SALOME::BAD_PARAM); + } + else + { + // create theElementSearcher if needed + theSearchersDeleter.Set( myMesh, getPartIOR( the2Dgroup, SMESH::FACE )); + if ( !theElementSearcher ) + { + if ( elements.empty() ) // search in the whole mesh + { + if ( myMesh->NbFaces() == 0 ) + THROW_SALOME_CORBA_EXCEPTION("No faces in the mesh", SALOME::BAD_PARAM); + + theElementSearcher = myEditor.GetElementSearcher(); + } + else + { + typedef SMDS_SetIterator TIter; + SMDS_ElemIteratorPtr elemsIt( new TIter( elements.begin(), elements.end() )); + + theElementSearcher = myEditor.GetElementSearcher(elemsIt); + } } + // find a face + gp_Pnt p( thePoint.x, thePoint.y, thePoint.z ); + face = theElementSearcher->FindClosestTo( p, SMDSAbs_Face ); + + if ( !face ) + THROW_SALOME_CORBA_EXCEPTION("No face found by point", SALOME::INTERNAL_ERROR ); + if ( !elements.empty() && !elements.count( face )) + THROW_SALOME_CORBA_EXCEPTION("Found face is not in the group", SALOME::BAD_PARAM ); + } + + const SMESH::PointStruct * P = &theDirection.PS; + gp_Vec dirVec( P->x, P->y, P->z ); + if ( dirVec.Magnitude() < std::numeric_limits< double >::min() ) + THROW_SALOME_CORBA_EXCEPTION("Zero size vector", SALOME::BAD_PARAM); + + int nbReori = myEditor.Reorient2D( elements, dirVec, face ); + storeResult(myEditor); + + if ( nbReori ) { + myMesh->SetIsModified( true ); + myMesh->GetMeshDS()->Modified(); } + TPythonDump() << this << ".Reorient2D( " + << the2Dgroup << ", " + << theDirection << ", " + << theFace << ", " + << thePoint << " )"; + + return nbReori; } //============================================================================= @@ -924,16 +1407,15 @@ CORBA::Boolean SMESH_MeshEditor_i::TriToQuad (const SMESH::long_array & IDsOfE // Update Python script TPythonDump() << "isDone = " << this << ".TriToQuad( " - << IDsOfElements << ", " << aNumericalFunctor << ", " << MaxAngle << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'TriToQuad: ', isDone"; -#endif + << IDsOfElements << ", " << aNumericalFunctor << ", " << TVar( MaxAngle ) << " )"; - ::SMESH_MeshEditor anEditor( myMesh ); - bool stat = anEditor.TriToQuad( faces, aCrit, MaxAngle ); + bool stat = myEditor.TriToQuad( faces, aCrit, MaxAngle ); + myMesh->GetMeshDS()->Modified(); + if ( stat ) + myMesh->SetIsModified( true ); // issue 0020693 - storeResult(anEditor); + storeResult(myEditor); return stat; } @@ -950,25 +1432,16 @@ CORBA::Boolean SMESH_MeshEditor_i::TriToQuadObject (SMESH::SMESH_IDSource_ptr { initData(); + TPythonDump aTPythonDump; // suppress dump in TriToQuad() SMESH::long_array_var anElementsId = theObject->GetIDs(); CORBA::Boolean isDone = TriToQuad(anElementsId, Criterion, MaxAngle); - // Clear python line(s), created by TriToQuad() - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#ifdef _DEBUG_ - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#endif - SMESH::NumericalFunctor_i* aNumericalFunctor = SMESH::DownCast( Criterion ); // Update Python script - TPythonDump() << "isDone = " << this << ".TriToQuadObject(" - << theObject << ", " << aNumericalFunctor << ", " << MaxAngle << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'TriToQuadObject: ', isDone"; -#endif + aTPythonDump << "isDone = " << this << ".TriToQuadObject(" + << theObject << ", " << aNumericalFunctor << ", " << TVar( MaxAngle ) << " )"; return isDone; } @@ -999,14 +1472,13 @@ CORBA::Boolean SMESH_MeshEditor_i::QuadToTri (const SMESH::long_array & IDsOfE // Update Python script TPythonDump() << "isDone = " << this << ".QuadToTri( " << IDsOfElements << ", " << aNumericalFunctor << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'QuadToTri: ', isDone"; -#endif - ::SMESH_MeshEditor anEditor( myMesh ); - CORBA::Boolean stat = anEditor.QuadToTri( faces, aCrit ); + CORBA::Boolean stat = myEditor.QuadToTri( faces, aCrit ); + myMesh->GetMeshDS()->Modified(); + if ( stat ) + myMesh->SetIsModified( true ); // issue 0020693 - storeResult(anEditor); + storeResult(myEditor); return stat; } @@ -1022,24 +1494,16 @@ CORBA::Boolean SMESH_MeshEditor_i::QuadToTriObject (SMESH::SMESH_IDSource_ptr { initData(); + TPythonDump aTPythonDump; // suppress dump in QuadToTri() + SMESH::long_array_var anElementsId = theObject->GetIDs(); CORBA::Boolean isDone = QuadToTri(anElementsId, Criterion); - // Clear python line(s), created by QuadToTri() - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#ifdef _DEBUG_ - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#endif - SMESH::NumericalFunctor_i* aNumericalFunctor = SMESH::DownCast( Criterion ); // Update Python script - TPythonDump() << "isDone = " << this << ".QuadToTriObject( " << theObject << ", " << aNumericalFunctor << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'QuadToTriObject: ', isDone"; -#endif + aTPythonDump << "isDone = " << this << ".QuadToTriObject( " << theObject << ", " << aNumericalFunctor << " )"; return isDone; } @@ -1062,14 +1526,14 @@ CORBA::Boolean SMESH_MeshEditor_i::SplitQuad (const SMESH::long_array & IDsOfEle // Update Python script TPythonDump() << "isDone = " << this << ".SplitQuad( " << IDsOfElements << ", " << Diag13 << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'SplitQuad: ', isDone"; -#endif - ::SMESH_MeshEditor anEditor( myMesh ); - CORBA::Boolean stat = anEditor.QuadToTri( faces, Diag13 ); + CORBA::Boolean stat = myEditor.QuadToTri( faces, Diag13 ); + myMesh->GetMeshDS()->Modified(); + if ( stat ) + myMesh->SetIsModified( true ); // issue 0020693 - storeResult(anEditor); + + storeResult(myEditor); return stat; } @@ -1085,22 +1549,14 @@ CORBA::Boolean SMESH_MeshEditor_i::SplitQuadObject (SMESH::SMESH_IDSource_ptr th { initData(); + TPythonDump aTPythonDump; // suppress dump in SplitQuad() + SMESH::long_array_var anElementsId = theObject->GetIDs(); CORBA::Boolean isDone = SplitQuad(anElementsId, Diag13); - // Clear python line(s), created by SplitQuad() - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#ifdef _DEBUG_ - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#endif - // Update Python script - TPythonDump() << "isDone = " << this << ".SplitQuadObject( " - << theObject << ", " << Diag13 << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'SplitQuadObject: ', isDone"; -#endif + aTPythonDump << "isDone = " << this << ".SplitQuadObject( " + << theObject << ", " << Diag13 << " )"; return isDone; } @@ -1114,6 +1570,8 @@ CORBA::Boolean SMESH_MeshEditor_i::SplitQuadObject (SMESH::SMESH_IDSource_ptr th CORBA::Long SMESH_MeshEditor_i::BestSplit (CORBA::Long IDOfQuad, SMESH::NumericalFunctor_ptr Criterion) { + initData(); + const SMDS_MeshElement* quad = GetMeshDS()->FindElement(IDOfQuad); if (quad && quad->GetType() == SMDSAbs_Face && quad->NbNodes() == 4) { @@ -1125,12 +1583,40 @@ CORBA::Long SMESH_MeshEditor_i::BestSplit (CORBA::Long IDOfQuad, else aCrit.reset(new SMESH::Controls::AspectRatio()); - ::SMESH_MeshEditor anEditor (myMesh); - return anEditor.BestSplit(quad, aCrit); + return myEditor.BestSplit(quad, aCrit); } return -1; } +//================================================================================ +/*! + * \brief Split volumic elements into tetrahedrons + */ +//================================================================================ + +void SMESH_MeshEditor_i::SplitVolumesIntoTetra (SMESH::SMESH_IDSource_ptr elems, + CORBA::Short methodFlags) + throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + + initData(); + + SMESH::long_array_var anElementsId = elems->GetIDs(); + TIDSortedElemSet elemSet; + arrayToSet( anElementsId, GetMeshDS(), elemSet, SMDSAbs_Volume ); + + myEditor.SplitVolumesIntoTetra( elemSet, int( methodFlags )); + myMesh->GetMeshDS()->Modified(); + + storeResult(myEditor); + +// if ( myLastCreatedElems.length() ) - it does not influence Compute() +// myMesh->SetIsModified( true ); // issue 0020693 + + TPythonDump() << this << ".SplitVolumesIntoTetra( " + << elems << ", " << methodFlags << " )"; +} //======================================================================= //function : Smooth @@ -1138,14 +1624,14 @@ CORBA::Long SMESH_MeshEditor_i::BestSplit (CORBA::Long IDOfQuad, //======================================================================= CORBA::Boolean - SMESH_MeshEditor_i::Smooth(const SMESH::long_array & IDsOfElements, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method) +SMESH_MeshEditor_i::Smooth(const SMESH::long_array & IDsOfElements, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method) { return smooth( IDsOfElements, IDsOfFixedNodes, MaxNbOfIterations, - MaxAspectRatio, Method, false ); + MaxAspectRatio, Method, false ); } @@ -1155,14 +1641,14 @@ CORBA::Boolean //======================================================================= CORBA::Boolean - SMESH_MeshEditor_i::SmoothParametric(const SMESH::long_array & IDsOfElements, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method) +SMESH_MeshEditor_i::SmoothParametric(const SMESH::long_array & IDsOfElements, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method) { return smooth( IDsOfElements, IDsOfFixedNodes, MaxNbOfIterations, - MaxAspectRatio, Method, true ); + MaxAspectRatio, Method, true ); } @@ -1172,11 +1658,11 @@ CORBA::Boolean //======================================================================= CORBA::Boolean - SMESH_MeshEditor_i::SmoothObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method) +SMESH_MeshEditor_i::SmoothObject(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method) { return smoothObject (theObject, IDsOfFixedNodes, MaxNbOfIterations, MaxAspectRatio, Method, false); @@ -1189,11 +1675,11 @@ CORBA::Boolean //======================================================================= CORBA::Boolean - SMESH_MeshEditor_i::SmoothParametricObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method) +SMESH_MeshEditor_i::SmoothParametricObject(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method) { return smoothObject (theObject, IDsOfFixedNodes, MaxNbOfIterations, MaxAspectRatio, Method, true); @@ -1207,12 +1693,12 @@ CORBA::Boolean //============================================================================= CORBA::Boolean - SMESH_MeshEditor_i::smooth(const SMESH::long_array & IDsOfElements, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method, - bool IsParametric) +SMESH_MeshEditor_i::smooth(const SMESH::long_array & IDsOfElements, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method, + bool IsParametric) { initData(); @@ -1232,23 +1718,22 @@ CORBA::Boolean if ( Method != SMESH::SMESH_MeshEditor::LAPLACIAN_SMOOTH ) method = ::SMESH_MeshEditor::CENTROIDAL; - ::SMESH_MeshEditor anEditor( myMesh ); - anEditor.Smooth(elements, fixedNodes, method, + myEditor.Smooth(elements, fixedNodes, method, MaxNbOfIterations, MaxAspectRatio, IsParametric ); - storeResult(anEditor); + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); // issue 0020693 + + storeResult(myEditor); // Update Python script TPythonDump() << "isDone = " << this << "." << (IsParametric ? "SmoothParametric( " : "Smooth( ") << IDsOfElements << ", " << IDsOfFixedNodes << ", " - << MaxNbOfIterations << ", " << MaxAspectRatio << ", " + << TVar( MaxNbOfIterations ) << ", " << TVar( MaxAspectRatio ) << ", " << "SMESH.SMESH_MeshEditor." << ( Method == SMESH::SMESH_MeshEditor::CENTROIDAL_SMOOTH ? "CENTROIDAL_SMOOTH )" : "LAPLACIAN_SMOOTH )"); -#ifdef _DEBUG_ - TPythonDump() << "print 'Smooth: ', isDone"; -#endif return true; } @@ -1270,28 +1755,20 @@ SMESH_MeshEditor_i::smoothObject(SMESH::SMESH_IDSource_ptr theObjec { initData(); + TPythonDump aTPythonDump; // suppress dump in smooth() + SMESH::long_array_var anElementsId = theObject->GetIDs(); CORBA::Boolean isDone = smooth (anElementsId, IDsOfFixedNodes, MaxNbOfIterations, MaxAspectRatio, Method, IsParametric); - // Clear python line(s), created by Smooth() - SMESH_Gen_i* aSMESHGen = SMESH_Gen_i::GetSMESHGen(); - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#ifdef _DEBUG_ - aSMESHGen->RemoveLastFromPythonScript(aSMESHGen->GetCurrentStudyID()); -#endif - // Update Python script - TPythonDump() << "isDone = " << this << "." - << (IsParametric ? "SmoothParametricObject( " : "SmoothObject( ") - << theObject << ", " << IDsOfFixedNodes << ", " - << MaxNbOfIterations << ", " << MaxAspectRatio << ", " - << "SMESH.SMESH_MeshEditor." - << ( Method == SMESH::SMESH_MeshEditor::CENTROIDAL_SMOOTH ? - "CENTROIDAL_SMOOTH )" : "LAPLACIAN_SMOOTH )"); -#ifdef _DEBUG_ - TPythonDump() << "print 'SmoothObject: ', isDone"; -#endif + aTPythonDump << "isDone = " << this << "." + << (IsParametric ? "SmoothParametricObject( " : "SmoothObject( ") + << theObject << ", " << IDsOfFixedNodes << ", " + << TVar( MaxNbOfIterations ) << ", " << TVar( MaxAspectRatio ) << ", " + << "SMESH.SMESH_MeshEditor." + << ( Method == SMESH::SMESH_MeshEditor::CENTROIDAL_SMOOTH ? + "CENTROIDAL_SMOOTH )" : "LAPLACIAN_SMOOTH )"); return isDone; } @@ -1327,9 +1804,9 @@ void SMESH_MeshEditor_i::RenumberElements() } //======================================================================= - /*! - * \brief Return groups by their IDs - */ +/*! + * \brief Return groups by their IDs + */ //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::getGroups(const std::list* groupIDs) @@ -1342,7 +1819,7 @@ SMESH::ListOfGroups* SMESH_MeshEditor_i::getGroups(const std::list* groupID //======================================================================= //function : rotationSweep -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1352,7 +1829,7 @@ SMESH_MeshEditor_i::rotationSweep(const SMESH::long_array & theIDsOfElements, CORBA::Long theNbOfSteps, CORBA::Double theTolerance, const bool theMakeGroups, - const SMDSAbs_ElementType theElementType) + const SMDSAbs_ElementType theElementType) { initData(); @@ -1379,11 +1856,13 @@ SMESH_MeshEditor_i::rotationSweep(const SMESH::long_array & theIDsOfElements, gp_Ax1 Ax1 (gp_Pnt( theAxis.x, theAxis.y, theAxis.z ), gp_Vec( theAxis.vx, theAxis.vy, theAxis.vz )); - ::SMESH_MeshEditor anEditor( mesh ); ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.RotationSweep (*workElements, Ax1, theAngleInRadians, - theNbOfSteps, theTolerance, theMakeGroups, makeWalls); - storeResult(anEditor); + myEditor.RotationSweep (*workElements, Ax1, theAngleInRadians, + theNbOfSteps, theTolerance, theMakeGroups, makeWalls); + storeResult(myEditor); + myMesh->GetMeshDS()->Modified(); + + // myMesh->SetIsModified( true ); -- it does not influence Compute() return theMakeGroups ? getGroups(groupIds.get()) : 0; } @@ -1401,11 +1880,11 @@ void SMESH_MeshEditor_i::RotationSweep(const SMESH::long_array & theIDsOfElement { if ( !myPreviewMode ) { TPythonDump() << this << ".RotationSweep( " - << theIDsOfElements << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + << theIDsOfElements << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } rotationSweep(theIDsOfElements, theAxis, @@ -1417,7 +1896,7 @@ void SMESH_MeshEditor_i::RotationSweep(const SMESH::long_array & theIDsOfElement //======================================================================= //function : RotationSweepMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1427,21 +1906,22 @@ SMESH_MeshEditor_i::RotationSweepMakeGroups(const SMESH::long_array& theIDsOfEle CORBA::Long theNbOfSteps, CORBA::Double theTolerance) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::ListOfGroups *aGroups = rotationSweep(theIDsOfElements, theAxis, theAngleInRadians, theNbOfSteps, theTolerance, true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump<< this << ".RotationSweepMakeGroups( " - << theIDsOfElements << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".RotationSweepMakeGroups( " + << theIDsOfElements << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } return aGroups; } @@ -1452,10 +1932,10 @@ SMESH_MeshEditor_i::RotationSweepMakeGroups(const SMESH::long_array& theIDsOfEle //======================================================================= void SMESH_MeshEditor_i::RotationSweepObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & theAxis, - CORBA::Double theAngleInRadians, - CORBA::Long theNbOfSteps, - CORBA::Double theTolerance) + const SMESH::AxisStruct & theAxis, + CORBA::Double theAngleInRadians, + CORBA::Long theNbOfSteps, + CORBA::Double theTolerance) { if ( !myPreviewMode ) { TPythonDump() << this << ".RotationSweepObject( " @@ -1480,18 +1960,18 @@ void SMESH_MeshEditor_i::RotationSweepObject(SMESH::SMESH_IDSource_ptr theObject //======================================================================= void SMESH_MeshEditor_i::RotationSweepObject1D(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & theAxis, - CORBA::Double theAngleInRadians, - CORBA::Long theNbOfSteps, - CORBA::Double theTolerance) + const SMESH::AxisStruct & theAxis, + CORBA::Double theAngleInRadians, + CORBA::Long theNbOfSteps, + CORBA::Double theTolerance) { if ( !myPreviewMode ) { TPythonDump() << this << ".RotationSweepObject1D( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + << theObject << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } SMESH::long_array_var anElementsId = theObject->GetIDs(); rotationSweep(anElementsId, @@ -1500,7 +1980,7 @@ void SMESH_MeshEditor_i::RotationSweepObject1D(SMESH::SMESH_IDSource_ptr theObje theNbOfSteps, theTolerance, false, - SMDSAbs_Edge); + SMDSAbs_Edge); } //======================================================================= @@ -1509,18 +1989,18 @@ void SMESH_MeshEditor_i::RotationSweepObject1D(SMESH::SMESH_IDSource_ptr theObje //======================================================================= void SMESH_MeshEditor_i::RotationSweepObject2D(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & theAxis, - CORBA::Double theAngleInRadians, - CORBA::Long theNbOfSteps, - CORBA::Double theTolerance) + const SMESH::AxisStruct & theAxis, + CORBA::Double theAngleInRadians, + CORBA::Long theNbOfSteps, + CORBA::Double theTolerance) { if ( !myPreviewMode ) { TPythonDump() << this << ".RotationSweepObject2D( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + << theObject << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } SMESH::long_array_var anElementsId = theObject->GetIDs(); rotationSweep(anElementsId, @@ -1529,12 +2009,12 @@ void SMESH_MeshEditor_i::RotationSweepObject2D(SMESH::SMESH_IDSource_ptr theObje theNbOfSteps, theTolerance, false, - SMDSAbs_Face); + SMDSAbs_Face); } //======================================================================= //function : RotationSweepObjectMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1544,6 +2024,8 @@ SMESH_MeshEditor_i::RotationSweepObjectMakeGroups(SMESH::SMESH_IDSource_ptr theO CORBA::Long theNbOfSteps, CORBA::Double theTolerance) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); SMESH::ListOfGroups *aGroups = rotationSweep(anElementsId, theAxis, @@ -1551,31 +2033,32 @@ SMESH_MeshEditor_i::RotationSweepObjectMakeGroups(SMESH::SMESH_IDSource_ptr theO theNbOfSteps, theTolerance, true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump<< this << ".RotationSweepObjectMakeGroups( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".RotationSweepObjectMakeGroups( " + << theObject << ", " + << theAxis << ", " + << theAngleInRadians << ", " + << theNbOfSteps << ", " + << theTolerance << " )"; } return aGroups; } //======================================================================= //function : RotationSweepObject1DMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::RotationSweepObject1DMakeGroups(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct& theAxis, - CORBA::Double theAngleInRadians, - CORBA::Long theNbOfSteps, - CORBA::Double theTolerance) + const SMESH::AxisStruct& theAxis, + CORBA::Double theAngleInRadians, + CORBA::Long theNbOfSteps, + CORBA::Double theTolerance) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); SMESH::ListOfGroups *aGroups = rotationSweep(anElementsId, theAxis, @@ -1583,32 +2066,33 @@ SMESH_MeshEditor_i::RotationSweepObject1DMakeGroups(SMESH::SMESH_IDSource_ptr th theNbOfSteps, theTolerance, true, - SMDSAbs_Edge); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump<< this << ".RotationSweepObject1DMakeGroups( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + SMDSAbs_Edge); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".RotationSweepObject1DMakeGroups( " + << theObject << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } return aGroups; } //======================================================================= //function : RotationSweepObject2DMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::RotationSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct& theAxis, - CORBA::Double theAngleInRadians, - CORBA::Long theNbOfSteps, - CORBA::Double theTolerance) + const SMESH::AxisStruct& theAxis, + CORBA::Double theAngleInRadians, + CORBA::Long theNbOfSteps, + CORBA::Double theTolerance) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); SMESH::ListOfGroups *aGroups = rotationSweep(anElementsId, theAxis, @@ -1616,16 +2100,15 @@ SMESH_MeshEditor_i::RotationSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr th theNbOfSteps, theTolerance, true, - SMDSAbs_Face); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump<< this << ".RotationSweepObject2DMakeGroups( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theNbOfSteps << ", " - << theTolerance << " )"; + SMDSAbs_Face); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".RotationSweepObject2DMakeGroups( " + << theObject << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << TVar( theNbOfSteps ) << ", " + << TVar( theTolerance ) << " )"; } return aGroups; } @@ -1633,39 +2116,59 @@ SMESH_MeshEditor_i::RotationSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr th //======================================================================= //function : extrusionSweep -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::extrusionSweep(const SMESH::long_array & theIDsOfElements, const SMESH::DirStruct & theStepVector, CORBA::Long theNbOfSteps, - const bool theMakeGroups, + bool theMakeGroups, const SMDSAbs_ElementType theElementType) { initData(); - try { + try { #ifdef NO_CAS_CATCH OCC_CATCH_SIGNALS; #endif - TIDSortedElemSet elements; + TIDSortedElemSet elements, copyElements; arrayToSet(theIDsOfElements, GetMeshDS(), elements, theElementType); const SMESH::PointStruct * P = &theStepVector.PS; gp_Vec stepVec( P->x, P->y, P->z ); + TIDSortedElemSet* workElements = & elements; + + SMDSAbs_ElementType aType = SMDSAbs_Face; + //::SMESH_MeshEditor::ExtrusionFlags aFlag = ::SMESH_MeshEditor::ExtrusionFlags::EXTRUSION_FLAG_BOUNDARY; + if (theElementType == SMDSAbs_Node) + { + aType = SMDSAbs_Edge; + //aFlag = ::SMESH_MeshEditor::ExtrusionFlags::EXTRUSION_FLAG_SEW; + } + TPreviewMesh tmpMesh( aType ); + SMESH_Mesh* mesh = myMesh; + + if ( myPreviewMode ) { + SMDSAbs_ElementType select = SMDSAbs_All, avoid = SMDSAbs_Volume; + tmpMesh.Copy( elements, copyElements, select, avoid ); + mesh = &tmpMesh; + workElements = & copyElements; + theMakeGroups = false; + } + TElemOfElemListMap aHystory; - ::SMESH_MeshEditor anEditor( myMesh ); - ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.ExtrusionSweep (elements, stepVec, theNbOfSteps, aHystory, theMakeGroups); + ::SMESH_MeshEditor::PGroupIDs groupIds = + myEditor.ExtrusionSweep (*workElements, stepVec, theNbOfSteps, aHystory, theMakeGroups); - storeResult(anEditor); + myMesh->GetMeshDS()->Modified(); + storeResult(myEditor); return theMakeGroups ? getGroups(groupIds.get()) : 0; } catch(Standard_Failure) { - Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); INFOS( "SMESH_MeshEditor_i::ExtrusionSweep fails - "<< aFail->GetMessageString() ); } return 0; @@ -1681,12 +2184,27 @@ void SMESH_MeshEditor_i::ExtrusionSweep(const SMESH::long_array & theIDsOfElemen CORBA::Long theNbOfSteps) { extrusionSweep (theIDsOfElements, theStepVector, theNbOfSteps, false ); - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << this << ".ExtrusionSweep( " - << theIDsOfElements << ", " << theStepVector <<", " << theNbOfSteps << " )"; + << theIDsOfElements << ", " << theStepVector <<", " << TVar(theNbOfSteps) << " )"; } } +//======================================================================= +//function : ExtrusionSweep0D +//purpose : +//======================================================================= + +void SMESH_MeshEditor_i::ExtrusionSweep0D(const SMESH::long_array & theIDsOfElements, + const SMESH::DirStruct & theStepVector, + CORBA::Long theNbOfSteps) +{ + extrusionSweep (theIDsOfElements, theStepVector, theNbOfSteps, false, SMDSAbs_Node ); + if (!myPreviewMode) { + TPythonDump() << this << ".ExtrusionSweep0D( " + << theIDsOfElements << ", " << theStepVector <<", " << TVar(theNbOfSteps)<< " )"; + } +} //======================================================================= //function : ExtrusionSweepObject @@ -1694,17 +2212,34 @@ void SMESH_MeshEditor_i::ExtrusionSweep(const SMESH::long_array & theIDsOfElemen //======================================================================= void SMESH_MeshEditor_i::ExtrusionSweepObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::DirStruct & theStepVector, - CORBA::Long theNbOfSteps) + const SMESH::DirStruct & theStepVector, + CORBA::Long theNbOfSteps) { SMESH::long_array_var anElementsId = theObject->GetIDs(); extrusionSweep (anElementsId, theStepVector, theNbOfSteps, false ); - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << this << ".ExtrusionSweepObject( " << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; } } +//======================================================================= +//function : ExtrusionSweepObject0D +//purpose : +//======================================================================= + +void SMESH_MeshEditor_i::ExtrusionSweepObject0D(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::DirStruct & theStepVector, + CORBA::Long theNbOfSteps) +{ + SMESH::long_array_var anElementsId = theObject->GetIDs(); + extrusionSweep (anElementsId, theStepVector, theNbOfSteps, false, SMDSAbs_Node ); + if ( !myPreviewMode ) { + TPythonDump() << this << ".ExtrusionSweepObject0D( " + << theObject << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; + } +} + //======================================================================= //function : ExtrusionSweepObject1D //purpose : @@ -1718,7 +2253,7 @@ void SMESH_MeshEditor_i::ExtrusionSweepObject1D(SMESH::SMESH_IDSource_ptr theObj extrusionSweep (anElementsId, theStepVector, theNbOfSteps, false, SMDSAbs_Edge ); if ( !myPreviewMode ) { TPythonDump() << this << ".ExtrusionSweepObject1D( " - << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; + << theObject << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; } } @@ -1735,13 +2270,13 @@ void SMESH_MeshEditor_i::ExtrusionSweepObject2D(SMESH::SMESH_IDSource_ptr theObj extrusionSweep (anElementsId, theStepVector, theNbOfSteps, false, SMDSAbs_Face ); if ( !myPreviewMode ) { TPythonDump() << this << ".ExtrusionSweepObject2D( " - << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; + << theObject << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; } } //======================================================================= //function : ExtrusionSweepMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1749,19 +2284,43 @@ SMESH_MeshEditor_i::ExtrusionSweepMakeGroups(const SMESH::long_array& theIDsOfEl const SMESH::DirStruct& theStepVector, CORBA::Long theNbOfSteps) { - SMESH::ListOfGroups* aGroups = extrusionSweep (theIDsOfElements, theStepVector, theNbOfSteps, true ); - - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump << this << ".ExtrusionSweepMakeGroups( " - << theIDsOfElements << ", " << theStepVector <<", " << theNbOfSteps << " )"; + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups* aGroups = extrusionSweep(theIDsOfElements, theStepVector, theNbOfSteps, true); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepMakeGroups( " << theIDsOfElements + << ", " << theStepVector <<", " << TVar( theNbOfSteps ) << " )"; + } + return aGroups; +} + +//======================================================================= +//function : ExtrusionSweepMakeGroups0D +//purpose : +//======================================================================= + +SMESH::ListOfGroups* +SMESH_MeshEditor_i::ExtrusionSweepMakeGroups0D(const SMESH::long_array& theIDsOfElements, + const SMESH::DirStruct& theStepVector, + CORBA::Long theNbOfSteps) +{ + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups* aGroups = extrusionSweep(theIDsOfElements, theStepVector, theNbOfSteps, true,SMDSAbs_Node); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepMakeGroups0D( " << theIDsOfElements + << ", " << theStepVector <<", " << TVar( theNbOfSteps ) << " )"; } return aGroups; } + //======================================================================= //function : ExtrusionSweepObjectMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1769,21 +2328,45 @@ SMESH_MeshEditor_i::ExtrusionSweepObjectMakeGroups(SMESH::SMESH_IDSource_ptr the const SMESH::DirStruct& theStepVector, CORBA::Long theNbOfSteps) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = extrusionSweep (anElementsId, theStepVector, theNbOfSteps, true ); - - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump<< this << ".ExtrusionSweepObjectMakeGroups( " - << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; + SMESH::ListOfGroups * aGroups = extrusionSweep(anElementsId, theStepVector, theNbOfSteps, true); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepObjectMakeGroups( " << theObject + << ", " << theStepVector << ", " << theNbOfSteps << " )"; + } + return aGroups; +} + +//======================================================================= +//function : ExtrusionSweepObject0DMakeGroups +//purpose : +//======================================================================= + +SMESH::ListOfGroups* +SMESH_MeshEditor_i::ExtrusionSweepObject0DMakeGroups(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::DirStruct& theStepVector, + CORBA::Long theNbOfSteps) +{ + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::long_array_var anElementsId = theObject->GetIDs(); + SMESH::ListOfGroups * aGroups = extrusionSweep(anElementsId, theStepVector, + theNbOfSteps, true, SMDSAbs_Node); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepObject0DMakeGroups( " << theObject + << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; } return aGroups; } //======================================================================= //function : ExtrusionSweepObject1DMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1791,20 +2374,22 @@ SMESH_MeshEditor_i::ExtrusionSweepObject1DMakeGroups(SMESH::SMESH_IDSource_ptr t const SMESH::DirStruct& theStepVector, CORBA::Long theNbOfSteps) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = extrusionSweep (anElementsId, theStepVector, theNbOfSteps, true, SMDSAbs_Edge ); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump << this << ".ExtrusionSweepObject1DMakeGroups( " - << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; + SMESH::ListOfGroups * aGroups = extrusionSweep(anElementsId, theStepVector, + theNbOfSteps, true, SMDSAbs_Edge); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepObject1DMakeGroups( " << theObject + << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; } return aGroups; } //======================================================================= //function : ExtrusionSweepObject2DMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1812,13 +2397,15 @@ SMESH_MeshEditor_i::ExtrusionSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr t const SMESH::DirStruct& theStepVector, CORBA::Long theNbOfSteps) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = extrusionSweep (anElementsId, theStepVector, theNbOfSteps, true, SMDSAbs_Face ); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); - aPythonDump << this << ".ExtrusionSweepObject2DMakeGroups( " - << theObject << ", " << theStepVector << ", " << theNbOfSteps << " )"; + SMESH::ListOfGroups * aGroups = extrusionSweep(anElementsId, theStepVector, + theNbOfSteps, true, SMDSAbs_Face); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".ExtrusionSweepObject2DMakeGroups( " << theObject + << ", " << theStepVector << ", " << TVar( theNbOfSteps ) << " )"; } return aGroups; } @@ -1826,7 +2413,7 @@ SMESH_MeshEditor_i::ExtrusionSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr t //======================================================================= //function : advancedExtrusion -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -1845,12 +2432,11 @@ SMESH_MeshEditor_i::advancedExtrusion(const SMESH::long_array & theIDsOfElements const SMESH::PointStruct * P = &theStepVector.PS; gp_Vec stepVec( P->x, P->y, P->z ); - ::SMESH_MeshEditor anEditor( myMesh ); TElemOfElemListMap aHystory; ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.ExtrusionSweep (elements, stepVec, theNbOfSteps, aHystory, + myEditor.ExtrusionSweep (elements, stepVec, theNbOfSteps, aHystory, theMakeGroups, theExtrFlags, theSewTolerance); - storeResult(anEditor); + storeResult(myEditor); return theMakeGroups ? getGroups(groupIds.get()) : 0; } @@ -1861,10 +2447,10 @@ SMESH_MeshEditor_i::advancedExtrusion(const SMESH::long_array & theIDsOfElements //======================================================================= void SMESH_MeshEditor_i::AdvancedExtrusion(const SMESH::long_array & theIDsOfElements, - const SMESH::DirStruct & theStepVector, - CORBA::Long theNbOfSteps, - CORBA::Long theExtrFlags, - CORBA::Double theSewTolerance) + const SMESH::DirStruct & theStepVector, + CORBA::Long theNbOfSteps, + CORBA::Long theExtrFlags, + CORBA::Double theSewTolerance) { if ( !myPreviewMode ) { TPythonDump() << "stepVector = " << theStepVector; @@ -1885,9 +2471,8 @@ void SMESH_MeshEditor_i::AdvancedExtrusion(const SMESH::long_array & theIDsOfEle //======================================================================= //function : AdvancedExtrusionMakeGroups -//purpose : +//purpose : //======================================================================= - SMESH::ListOfGroups* SMESH_MeshEditor_i::AdvancedExtrusionMakeGroups(const SMESH::long_array& theIDsOfElements, const SMESH::DirStruct& theStepVector, @@ -1895,17 +2480,20 @@ SMESH_MeshEditor_i::AdvancedExtrusionMakeGroups(const SMESH::long_array& theIDsO CORBA::Long theExtrFlags, CORBA::Double theSewTolerance) { - SMESH::ListOfGroups * aGroups = advancedExtrusion( theIDsOfElements, - theStepVector, - theNbOfSteps, - theExtrFlags, - theSewTolerance, - true); - - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << "stepVector = " << theStepVector; - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + } + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = advancedExtrusion( theIDsOfElements, + theStepVector, + theNbOfSteps, + theExtrFlags, + theSewTolerance, + true); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".AdvancedExtrusionMakeGroups(" << theIDsOfElements << ", stepVector, " @@ -1928,13 +2516,13 @@ SMESH_MeshEditor_i::AdvancedExtrusionMakeGroups(const SMESH::long_array& theIDsO static SMESH::SMESH_MeshEditor::Extrusion_Error convExtrError( const::SMESH_MeshEditor::Extrusion_Error e ) { switch ( e ) { - RETCASE( EXTR_OK ); - RETCASE( EXTR_NO_ELEMENTS ); - RETCASE( EXTR_PATH_NOT_EDGE ); - RETCASE( EXTR_BAD_PATH_SHAPE ); - RETCASE( EXTR_BAD_STARTING_NODE ); - RETCASE( EXTR_BAD_ANGLES_NUMBER ); - RETCASE( EXTR_CANT_GET_TANGENT ); + RETCASE( EXTR_OK ); + RETCASE( EXTR_NO_ELEMENTS ); + RETCASE( EXTR_PATH_NOT_EDGE ); + RETCASE( EXTR_BAD_PATH_SHAPE ); + RETCASE( EXTR_BAD_STARTING_NODE ); + RETCASE( EXTR_BAD_ANGLES_NUMBER ); + RETCASE( EXTR_CANT_GET_TANGENT ); } return SMESH::SMESH_MeshEditor::EXTR_OK; } @@ -1942,9 +2530,8 @@ static SMESH::SMESH_MeshEditor::Extrusion_Error convExtrError( const::SMESH_Mesh //======================================================================= //function : extrusionAlongPath -//purpose : +//purpose : //======================================================================= - SMESH::ListOfGroups* SMESH_MeshEditor_i::extrusionAlongPath(const SMESH::long_array & theIDsOfElements, SMESH::SMESH_Mesh_ptr thePathMesh, @@ -1956,8 +2543,9 @@ SMESH_MeshEditor_i::extrusionAlongPath(const SMESH::long_array & theIDsOfEleme const SMESH::PointStruct & theRefPoint, const bool theMakeGroups, SMESH::SMESH_MeshEditor::Extrusion_Error & theError, - const SMDSAbs_ElementType theElementType) + const SMDSAbs_ElementType theElementType) { + MESSAGE("extrusionAlongPath"); initData(); if ( thePathMesh->_is_nil() || thePathShape->_is_nil() ) { @@ -1992,12 +2580,12 @@ SMESH_MeshEditor_i::extrusionAlongPath(const SMESH::long_array & theIDsOfEleme int nbOldGroups = myMesh->NbGroup(); - ::SMESH_MeshEditor anEditor( myMesh ); ::SMESH_MeshEditor::Extrusion_Error error = - anEditor.ExtrusionAlongTrack( elements, aSubMesh, nodeStart, - theHasAngles, angles, + myEditor.ExtrusionAlongTrack( elements, aSubMesh, nodeStart, + theHasAngles, angles, false, theHasRefPoint, refPnt, theMakeGroups ); - storeResult(anEditor); + myMesh->GetMeshDS()->Modified(); + storeResult(myEditor); theError = convExtrError( error ); if ( theMakeGroups ) { @@ -2010,21 +2598,131 @@ SMESH_MeshEditor_i::extrusionAlongPath(const SMESH::long_array & theIDsOfEleme return 0; } + //======================================================================= -//function : ExtrusionAlongPath +//function : extrusionAlongPathX //purpose : //======================================================================= +SMESH::ListOfGroups* +SMESH_MeshEditor_i::extrusionAlongPathX(const SMESH::long_array & IDsOfElements, + SMESH::SMESH_IDSource_ptr Path, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean LinearVariation, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + bool MakeGroups, + const SMDSAbs_ElementType ElementType, + SMESH::SMESH_MeshEditor::Extrusion_Error & Error) +{ + SMESH::ListOfGroups* EmptyGr = new SMESH::ListOfGroups; + + initData(); + + list angles; + for (int i = 0; i < Angles.length(); i++) { + angles.push_back( Angles[i] ); + } + gp_Pnt refPnt( RefPoint.x, RefPoint.y, RefPoint.z ); + int nbOldGroups = myMesh->NbGroup(); + + if ( Path->_is_nil() ) { + Error = SMESH::SMESH_MeshEditor::EXTR_BAD_PATH_SHAPE; + return EmptyGr; + } + + TIDSortedElemSet elements, copyElements; + arrayToSet(IDsOfElements, GetMeshDS(), elements, ElementType); + + TIDSortedElemSet* workElements = &elements; + TPreviewMesh tmpMesh( SMDSAbs_Face ); + SMESH_Mesh* mesh = myMesh; + + if ( myPreviewMode ) + { + SMDSAbs_ElementType select = SMDSAbs_All, avoid = SMDSAbs_Volume; + tmpMesh.Copy( elements, copyElements, select, avoid ); + mesh = &tmpMesh; + workElements = & copyElements; + MakeGroups = false; + } + + ::SMESH_MeshEditor::Extrusion_Error error; + + if ( SMESH_Mesh_i* aMeshImp = SMESH::DownCast( Path )) + { + // path as mesh + SMDS_MeshNode* aNodeStart = + (SMDS_MeshNode*)aMeshImp->GetImpl().GetMeshDS()->FindNode(NodeStart); + if ( !aNodeStart ) { + Error = SMESH::SMESH_MeshEditor::EXTR_BAD_STARTING_NODE; + return EmptyGr; + } + error = myEditor.ExtrusionAlongTrack( *workElements, &(aMeshImp->GetImpl()), aNodeStart, + HasAngles, angles, LinearVariation, + HasRefPoint, refPnt, MakeGroups ); + myMesh->GetMeshDS()->Modified(); + } + else if ( SMESH_subMesh_i* aSubMeshImp = SMESH::DownCast( Path )) + { + // path as submesh + SMESH::SMESH_Mesh_ptr aPathMesh = aSubMeshImp->GetFather(); + aMeshImp = SMESH::DownCast( aPathMesh ); + SMDS_MeshNode* aNodeStart = + (SMDS_MeshNode*)aMeshImp->GetImpl().GetMeshDS()->FindNode(NodeStart); + if ( !aNodeStart ) { + Error = SMESH::SMESH_MeshEditor::EXTR_BAD_STARTING_NODE; + return EmptyGr; + } + SMESH_subMesh* aSubMesh = + aMeshImp->GetImpl().GetSubMeshContaining(aSubMeshImp->GetId()); + error = myEditor.ExtrusionAlongTrack( *workElements, aSubMesh, aNodeStart, + HasAngles, angles, LinearVariation, + HasRefPoint, refPnt, MakeGroups ); + myMesh->GetMeshDS()->Modified(); + } + else if ( SMESH::DownCast( Path )) + { + // path as group of 1D elements + // ???????? + } + else + { + // invalid path + Error = SMESH::SMESH_MeshEditor::EXTR_BAD_PATH_SHAPE; + return EmptyGr; + } + + storeResult(myEditor); + Error = convExtrError( error ); + + if ( MakeGroups ) { + list groupIDs = myMesh->GetGroupIds(); + list::iterator newBegin = groupIDs.begin(); + std::advance( newBegin, nbOldGroups ); // skip old groups + groupIDs.erase( groupIDs.begin(), newBegin ); + return getGroups( & groupIDs ); + } + return EmptyGr; +} + +//======================================================================= +//function : ExtrusionAlongPath +//purpose : +//======================================================================= SMESH::SMESH_MeshEditor::Extrusion_Error - SMESH_MeshEditor_i::ExtrusionAlongPath(const SMESH::long_array & theIDsOfElements, - SMESH::SMESH_Mesh_ptr thePathMesh, - GEOM::GEOM_Object_ptr thePathShape, - CORBA::Long theNodeStart, - CORBA::Boolean theHasAngles, - const SMESH::double_array & theAngles, - CORBA::Boolean theHasRefPoint, - const SMESH::PointStruct & theRefPoint) +SMESH_MeshEditor_i::ExtrusionAlongPath(const SMESH::long_array & theIDsOfElements, + SMESH::SMESH_Mesh_ptr thePathMesh, + GEOM::GEOM_Object_ptr thePathShape, + CORBA::Long theNodeStart, + CORBA::Boolean theHasAngles, + const SMESH::double_array & theAngles, + CORBA::Boolean theHasRefPoint, + const SMESH::PointStruct & theRefPoint) { + MESSAGE("ExtrusionAlongPath"); if ( !myPreviewMode ) { TPythonDump() << "error = " << this << ".ExtrusionAlongPath( " << theIDsOfElements << ", " @@ -2057,7 +2755,6 @@ SMESH::SMESH_MeshEditor::Extrusion_Error //function : ExtrusionAlongPathObject //purpose : //======================================================================= - SMESH::SMESH_MeshEditor::Extrusion_Error SMESH_MeshEditor_i::ExtrusionAlongPathObject(SMESH::SMESH_IDSource_ptr theObject, SMESH::SMESH_Mesh_ptr thePathMesh, @@ -2101,16 +2798,15 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObject(SMESH::SMESH_IDSource_ptr theObje //function : ExtrusionAlongPathObject1D //purpose : //======================================================================= - SMESH::SMESH_MeshEditor::Extrusion_Error SMESH_MeshEditor_i::ExtrusionAlongPathObject1D(SMESH::SMESH_IDSource_ptr theObject, - SMESH::SMESH_Mesh_ptr thePathMesh, - GEOM::GEOM_Object_ptr thePathShape, - CORBA::Long theNodeStart, - CORBA::Boolean theHasAngles, - const SMESH::double_array & theAngles, - CORBA::Boolean theHasRefPoint, - const SMESH::PointStruct & theRefPoint) + SMESH::SMESH_Mesh_ptr thePathMesh, + GEOM::GEOM_Object_ptr thePathShape, + CORBA::Long theNodeStart, + CORBA::Boolean theHasAngles, + const SMESH::double_array & theAngles, + CORBA::Boolean theHasRefPoint, + const SMESH::PointStruct & theRefPoint) { if ( !myPreviewMode ) { TPythonDump() << "error = " << this << ".ExtrusionAlongPathObject1D( " @@ -2138,7 +2834,7 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObject1D(SMESH::SMESH_IDSource_ptr theOb theRefPoint, false, anError, - SMDSAbs_Edge); + SMDSAbs_Edge); return anError; } @@ -2146,16 +2842,15 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObject1D(SMESH::SMESH_IDSource_ptr theOb //function : ExtrusionAlongPathObject2D //purpose : //======================================================================= - SMESH::SMESH_MeshEditor::Extrusion_Error SMESH_MeshEditor_i::ExtrusionAlongPathObject2D(SMESH::SMESH_IDSource_ptr theObject, - SMESH::SMESH_Mesh_ptr thePathMesh, - GEOM::GEOM_Object_ptr thePathShape, - CORBA::Long theNodeStart, - CORBA::Boolean theHasAngles, - const SMESH::double_array & theAngles, - CORBA::Boolean theHasRefPoint, - const SMESH::PointStruct & theRefPoint) + SMESH::SMESH_Mesh_ptr thePathMesh, + GEOM::GEOM_Object_ptr thePathShape, + CORBA::Long theNodeStart, + CORBA::Boolean theHasAngles, + const SMESH::double_array & theAngles, + CORBA::Boolean theHasRefPoint, + const SMESH::PointStruct & theRefPoint) { if ( !myPreviewMode ) { TPythonDump() << "error = " << this << ".ExtrusionAlongPathObject2D( " @@ -2183,16 +2878,15 @@ SMESH_MeshEditor_i::ExtrusionAlongPathObject2D(SMESH::SMESH_IDSource_ptr theOb theRefPoint, false, anError, - SMDSAbs_Face); + SMDSAbs_Face); return anError; } //======================================================================= //function : ExtrusionAlongPathMakeGroups -//purpose : +//purpose : //======================================================================= - SMESH::ListOfGroups* SMESH_MeshEditor_i::ExtrusionAlongPathMakeGroups(const SMESH::long_array& theIDsOfElements, SMESH::SMESH_Mesh_ptr thePathMesh, @@ -2204,27 +2898,25 @@ SMESH_MeshEditor_i::ExtrusionAlongPathMakeGroups(const SMESH::long_array& theI const SMESH::PointStruct& theRefPoint, SMESH::SMESH_MeshEditor::Extrusion_Error& Error) { - SMESH::ListOfGroups * aGroups = extrusionAlongPath( theIDsOfElements, - thePathMesh, - thePathShape, - theNodeStart, - theHasAngles, - theAngles, - theHasRefPoint, - theRefPoint, - true, - Error); - if ( !myPreviewMode ) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = extrusionAlongPath( theIDsOfElements, + thePathMesh, + thePathShape, + theNodeStart, + theHasAngles, + theAngles, + theHasRefPoint, + theRefPoint, + true, + Error); + if (!myPreviewMode) { bool isDumpGroups = aGroups && aGroups->length() > 0; - TPythonDump aPythonDump; - if(isDumpGroups) { - aPythonDump << "("<GetIDs(); SMESH::ListOfGroups * aGroups = extrusionAlongPath( anElementsId, thePathMesh, @@ -2268,15 +2961,11 @@ ExtrusionAlongPathObjectMakeGroups(SMESH::SMESH_IDSource_ptr theObject, theRefPoint, true, Error); - - if ( !myPreviewMode ) { + + if (!myPreviewMode) { bool isDumpGroups = aGroups && aGroups->length() > 0; - TPythonDump aPythonDump; - if(isDumpGroups) { - aPythonDump << "("<GetIDs(); SMESH::ListOfGroups * aGroups = extrusionAlongPath( anElementsId, thePathMesh, @@ -2323,18 +3013,14 @@ ExtrusionAlongPathObject1DMakeGroups(SMESH::SMESH_IDSource_ptr theObject, theRefPoint, true, Error, - SMDSAbs_Edge); - - if ( !myPreviewMode ) { + SMDSAbs_Edge); + + if (!myPreviewMode) { bool isDumpGroups = aGroups && aGroups->length() > 0; - TPythonDump aPythonDump; - if(isDumpGroups) { - aPythonDump << "("<GetIDs(); SMESH::ListOfGroups * aGroups = extrusionAlongPath( anElementsId, thePathMesh, @@ -2379,18 +3066,14 @@ ExtrusionAlongPathObject2DMakeGroups(SMESH::SMESH_IDSource_ptr theObject, theRefPoint, true, Error, - SMDSAbs_Face); - - if ( !myPreviewMode ) { + SMDSAbs_Face); + + if (!myPreviewMode) { bool isDumpGroups = aGroups && aGroups->length() > 0; - TPythonDump aPythonDump; - if(isDumpGroups) { - aPythonDump << "("<GetIDs(); + SMESH::ListOfGroups * aGroups = extrusionAlongPathX(anElementsId, + Path, + NodeStart, + HasAngles, + Angles, + LinearVariation, + HasRefPoint, + RefPoint, + MakeGroups, + (SMDSAbs_ElementType)ElemType, + Error); + + if (!myPreviewMode) { + bool isDumpGroups = aGroups && aGroups->length() > 0; + if (isDumpGroups) + aPythonDump << "(" << *aGroups << ", error)"; + else + aPythonDump << "error"; + + aPythonDump << " = " << this << ".ExtrusionAlongPathObjX( " + << Object << ", " + << Path << ", " + << NodeStart << ", " + << HasAngles << ", " + << TVar( Angles ) << ", " + << LinearVariation << ", " + << HasRefPoint << ", " + << "SMESH.PointStruct( " + << TVar( HasRefPoint ? RefPoint.x : 0 ) << ", " + << TVar( HasRefPoint ? RefPoint.y : 0 ) << ", " + << TVar( HasRefPoint ? RefPoint.z : 0 ) << " ), " + << MakeGroups << ", " + << ElemType << " )"; + } + return aGroups; +} + + +//======================================================================= +//function : ExtrusionAlongPathX +//purpose : +//======================================================================= +SMESH::ListOfGroups* SMESH_MeshEditor_i:: +ExtrusionAlongPathX(const SMESH::long_array& IDsOfElements, + SMESH::SMESH_IDSource_ptr Path, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean LinearVariation, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + CORBA::Boolean MakeGroups, + SMESH::ElementType ElemType, + SMESH::SMESH_MeshEditor::Extrusion_Error& Error) +{ + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = extrusionAlongPathX(IDsOfElements, + Path, + NodeStart, + HasAngles, + Angles, + LinearVariation, + HasRefPoint, + RefPoint, + MakeGroups, + (SMDSAbs_ElementType)ElemType, + Error); + + if (!myPreviewMode) { + bool isDumpGroups = aGroups && aGroups->length() > 0; + if (isDumpGroups) + aPythonDump << "(" << *aGroups << ", error)"; + else + aPythonDump <<"error"; + + aPythonDump << " = " << this << ".ExtrusionAlongPathX( " + << IDsOfElements << ", " + << Path << ", " + << NodeStart << ", " + << HasAngles << ", " + << TVar( Angles ) << ", " + << LinearVariation << ", " + << HasRefPoint << ", " + << "SMESH.PointStruct( " + << TVar( HasRefPoint ? RefPoint.x : 0 ) << ", " + << TVar( HasRefPoint ? RefPoint.y : 0 ) << ", " + << TVar( HasRefPoint ? RefPoint.z : 0 ) << " ), " + << MakeGroups << ", " + << ElemType << " )"; } return aGroups; } + //================================================================================ /*! * \brief Compute rotation angles for ExtrusionAlongPath as linear variation * of given angles along path steps - * \param PathMesh mesh containing a 1D sub-mesh on the edge, along - * which proceeds the extrusion - * \param PathShape is shape(edge); as the mesh can be complex, the edge - * is used to define the sub-mesh for the path + * \param PathMesh mesh containing a 1D sub-mesh on the edge, along + * which proceeds the extrusion + * \param PathShape is shape(edge); as the mesh can be complex, the edge + * is used to define the sub-mesh for the path */ //================================================================================ @@ -2455,7 +3254,7 @@ SMESH_MeshEditor_i::LinearAnglesVariation(SMESH::SMESH_Mesh_ptr thePathMes int iP = int( angPrevFloor ); double angPrevCeil = ceil(angPrev); angle = ( angPrevCeil - angPrev ) * theAngles[ iP ]; - + int iC = int( angCurFloor ); if ( iC < nbAngles ) angle += ( angCur - angCurFloor ) * theAngles[ iC ]; @@ -2482,25 +3281,25 @@ SMESH_MeshEditor_i::LinearAnglesVariation(SMESH::SMESH_Mesh_ptr thePathMes //======================================================================= //function : mirror -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* -SMESH_MeshEditor_i::mirror(const SMESH::long_array & theIDsOfElements, +SMESH_MeshEditor_i::mirror(TIDSortedElemSet & theElements, const SMESH::AxisStruct & theAxis, SMESH::SMESH_MeshEditor::MirrorType theMirrorType, CORBA::Boolean theCopy, - const bool theMakeGroups, + bool theMakeGroups, ::SMESH_Mesh* theTargetMesh) { initData(); - TIDSortedElemSet elements; - arrayToSet(theIDsOfElements, GetMeshDS(), elements); - gp_Pnt P ( theAxis.x, theAxis.y, theAxis.z ); gp_Vec V ( theAxis.vx, theAxis.vy, theAxis.vz ); + if ( theTargetMesh ) + theCopy = false; + gp_Trsf aTrsf; switch ( theMirrorType ) { case SMESH::SMESH_MeshEditor::POINT: @@ -2513,20 +3312,50 @@ SMESH_MeshEditor_i::mirror(const SMESH::long_array & theIDsOfElements, aTrsf.SetMirror( gp_Ax2( P, V )); } - ::SMESH_MeshEditor anEditor( myMesh ); - ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.Transform (elements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + TIDSortedElemSet copyElements; + TPreviewMesh tmpMesh; + TIDSortedElemSet* workElements = & theElements; + SMESH_Mesh* mesh = myMesh; - if(theCopy) { - storeResult(anEditor); + if ( myPreviewMode ) + { + tmpMesh.Copy( theElements, copyElements); + if ( !theCopy && !theTargetMesh ) + { + TIDSortedElemSet elemsAround, elemsAroundCopy; + getElementsAround( theElements, GetMeshDS(), elemsAround ); + tmpMesh.Copy( elemsAround, elemsAroundCopy); + } + mesh = &tmpMesh; + workElements = & copyElements; + theMakeGroups = false; } - return theMakeGroups ? getGroups(groupIds.get()) : 0; -} -//======================================================================= -//function : Mirror -//purpose : -//======================================================================= + ::SMESH_MeshEditor::PGroupIDs groupIds = + myEditor.Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + + if(theCopy || myPreviewMode) + storeResult(myEditor); // store preview data or new elements + + if ( !myPreviewMode ) + { + if ( theTargetMesh ) + { + theTargetMesh->GetMeshDS()->Modified(); + } + else + { + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + } + } + return theMakeGroups ? getGroups(groupIds.get()) : 0; +} + +//======================================================================= +//function : Mirror +//purpose : +//======================================================================= void SMESH_MeshEditor_i::Mirror(const SMESH::long_array & theIDsOfElements, const SMESH::AxisStruct & theAxis, @@ -2535,12 +3364,17 @@ void SMESH_MeshEditor_i::Mirror(const SMESH::long_array & theIDsOfElem { if ( !myPreviewMode ) { TPythonDump() << this << ".Mirror( " - << theIDsOfElements << ", " - << theAxis << ", " + << theIDsOfElements << ", " + << theAxis << ", " << mirrorTypeName(theMirrorType) << ", " - << theCopy << " )"; + << theCopy << " )"; + } + if ( theIDsOfElements.length() > 0 ) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + mirror(elements, theAxis, theMirrorType, theCopy, false); } - mirror(theIDsOfElements, theAxis, theMirrorType, theCopy, false); } @@ -2550,24 +3384,28 @@ void SMESH_MeshEditor_i::Mirror(const SMESH::long_array & theIDsOfElem //======================================================================= void SMESH_MeshEditor_i::MirrorObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & theAxis, - SMESH::SMESH_MeshEditor::MirrorType theMirrorType, - CORBA::Boolean theCopy) + const SMESH::AxisStruct & theAxis, + SMESH::SMESH_MeshEditor::MirrorType theMirrorType, + CORBA::Boolean theCopy) { if ( !myPreviewMode ) { TPythonDump() << this << ".MirrorObject( " - << theObject << ", " - << theAxis << ", " + << theObject << ", " + << theAxis << ", " << mirrorTypeName(theMirrorType) << ", " - << theCopy << " )"; + << theCopy << " )"; } - SMESH::long_array_var anElementsId = theObject->GetIDs(); - mirror(anElementsId, theAxis, theMirrorType, theCopy, false); + TIDSortedElemSet elements; + + bool emptyIfIsMesh = myPreviewMode ? false : true; + + if (idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, emptyIfIsMesh)) + mirror(elements, theAxis, theMirrorType, theCopy, false); } //======================================================================= //function : MirrorMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -2575,13 +3413,20 @@ SMESH_MeshEditor_i::MirrorMakeGroups(const SMESH::long_array& theIDsO const SMESH::AxisStruct& theMirror, SMESH::SMESH_MeshEditor::MirrorType theMirrorType) { - SMESH::ListOfGroups * aGroups = mirror(theIDsOfElements, theMirror, theMirrorType, true, true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = 0; + if ( theIDsOfElements.length() > 0 ) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + aGroups = mirror(elements, theMirror, theMirrorType, true, true); + } + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".MirrorMakeGroups( " - << theIDsOfElements << ", " - << theMirror << ", " + << theIDsOfElements << ", " + << theMirror << ", " << mirrorTypeName(theMirrorType) << " )"; } return aGroups; @@ -2589,7 +3434,7 @@ SMESH_MeshEditor_i::MirrorMakeGroups(const SMESH::long_array& theIDsO //======================================================================= //function : MirrorObjectMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -2597,14 +3442,19 @@ SMESH_MeshEditor_i::MirrorObjectMakeGroups(SMESH::SMESH_IDSource_ptr t const SMESH::AxisStruct& theMirror, SMESH::SMESH_MeshEditor::MirrorType theMirrorType) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = mirror(anElementsId, theMirror, theMirrorType, true, true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = 0; + TIDSortedElemSet elements; + if ( idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + aGroups = mirror(elements, theMirror, theMirrorType, true, true); + + if (!myPreviewMode) + { DumpGroupsList(aPythonDump,aGroups); aPythonDump << this << ".MirrorObjectMakeGroups( " - << theObject << ", " - << theMirror << ", " + << theObject << ", " + << theMirror << ", " << mirrorTypeName(theMirrorType) << " )"; } return aGroups; @@ -2612,7 +3462,7 @@ SMESH_MeshEditor_i::MirrorObjectMakeGroups(SMESH::SMESH_IDSource_ptr t //======================================================================= //function : MirrorMakeMesh -//purpose : +//purpose : //======================================================================= SMESH::SMESH_Mesh_ptr @@ -2626,37 +3476,40 @@ SMESH_MeshEditor_i::MirrorMakeMesh(const SMESH::long_array& theIDsOfE SMESH::SMESH_Mesh_var mesh; { // open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() - + TPythonDump pydump; // to prevent dump at mesh creation mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - if (mesh_i) { - mirror(theIDsOfElements, theMirror, theMirrorType, + if (mesh_i && theIDsOfElements.length() > 0 ) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + mirror(elements, theMirror, theMirrorType, false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } - - if ( !myPreviewMode ) { + + if (!myPreviewMode) { pydump << mesh << " = " << this << ".MirrorMakeMesh( " - << theIDsOfElements << ", " - << theMirror << ", " + << theIDsOfElements << ", " + << theMirror << ", " << mirrorTypeName(theMirrorType) << ", " - << theCopyGroups << ", '" - << theMeshName << "' )"; + << theCopyGroups << ", '" + << theMeshName << "' )"; } } //dump "GetGroups" - if(!myPreviewMode && mesh_i) + if (!myPreviewMode && mesh_i) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= //function : MirrorObjectMakeMesh -//purpose : +//purpose : //======================================================================= SMESH::SMESH_Mesh_ptr @@ -2670,62 +3523,94 @@ SMESH_MeshEditor_i::MirrorObjectMakeMesh(SMESH::SMESH_IDSource_ptr the SMESH::SMESH_Mesh_var mesh; { // open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() - + TPythonDump pydump; // to prevent dump at mesh creation mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - if ( mesh_i ) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - mirror(anElementsId, theMirror, theMirrorType, + TIDSortedElemSet elements; + if ( mesh_i && + idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + { + mirror(elements, theMirror, theMirrorType, false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } - - if ( !myPreviewMode ) { + if (!myPreviewMode) { pydump << mesh << " = " << this << ".MirrorObjectMakeMesh( " - << theObject << ", " - << theMirror << ", " + << theObject << ", " + << theMirror << ", " << mirrorTypeName(theMirrorType) << ", " - << theCopyGroups << ", '" - << theMeshName << "' )"; + << theCopyGroups << ", '" + << theMeshName << "' )"; } - } + } //dump "GetGroups" - if(!myPreviewMode && mesh_i) + if (!myPreviewMode && mesh_i) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= //function : translate -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* -SMESH_MeshEditor_i::translate(const SMESH::long_array & theIDsOfElements, +SMESH_MeshEditor_i::translate(TIDSortedElemSet & theElements, const SMESH::DirStruct & theVector, CORBA::Boolean theCopy, - const bool theMakeGroups, + bool theMakeGroups, ::SMESH_Mesh* theTargetMesh) { initData(); - TIDSortedElemSet elements; - arrayToSet(theIDsOfElements, GetMeshDS(), elements); + if ( theTargetMesh ) + theCopy = false; gp_Trsf aTrsf; const SMESH::PointStruct * P = &theVector.PS; aTrsf.SetTranslation( gp_Vec( P->x, P->y, P->z )); - ::SMESH_MeshEditor anEditor( myMesh ); + TIDSortedElemSet copyElements; + TIDSortedElemSet* workElements = &theElements; + TPreviewMesh tmpMesh; + SMESH_Mesh* mesh = myMesh; + + if ( myPreviewMode ) + { + tmpMesh.Copy( theElements, copyElements); + if ( !theCopy && !theTargetMesh ) + { + TIDSortedElemSet elemsAround, elemsAroundCopy; + getElementsAround( theElements, GetMeshDS(), elemsAround ); + tmpMesh.Copy( elemsAround, elemsAroundCopy); + } + mesh = &tmpMesh; + workElements = & copyElements; + theMakeGroups = false; + } + ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.Transform (elements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + myEditor.Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh); - if(theCopy) - storeResult(anEditor); + if(theCopy || myPreviewMode) + storeResult(myEditor); + + if ( !myPreviewMode ) + { + if ( theTargetMesh ) + { + theTargetMesh->GetMeshDS()->Modified(); + } + else + { + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + } + } return theMakeGroups ? getGroups(groupIds.get()) : 0; } @@ -2739,16 +3624,17 @@ void SMESH_MeshEditor_i::Translate(const SMESH::long_array & theIDsOfElements, const SMESH::DirStruct & theVector, CORBA::Boolean theCopy) { - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << this << ".Translate( " << theIDsOfElements << ", " - << theVector << ", " - << theCopy << " )"; + << theVector << ", " + << theCopy << " )"; + } + if (theIDsOfElements.length()) { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + translate(elements, theVector, theCopy, false); } - translate(theIDsOfElements, - theVector, - theCopy, - false); } //======================================================================= @@ -2757,58 +3643,67 @@ void SMESH_MeshEditor_i::Translate(const SMESH::long_array & theIDsOfElements, //======================================================================= void SMESH_MeshEditor_i::TranslateObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::DirStruct & theVector, - CORBA::Boolean theCopy) + const SMESH::DirStruct & theVector, + CORBA::Boolean theCopy) { - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << this << ".TranslateObject( " << theObject << ", " << theVector << ", " - << theCopy << " )"; + << theCopy << " )"; } - SMESH::long_array_var anElementsId = theObject->GetIDs(); - translate(anElementsId, - theVector, - theCopy, - false); + TIDSortedElemSet elements; + + bool emptyIfIsMesh = myPreviewMode ? false : true; + + if (idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, emptyIfIsMesh)) + translate(elements, theVector, theCopy, false); } //======================================================================= //function : TranslateMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::TranslateMakeGroups(const SMESH::long_array& theIDsOfElements, const SMESH::DirStruct& theVector) { - SMESH::ListOfGroups * aGroups = translate(theIDsOfElements,theVector,true,true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = 0; + if (theIDsOfElements.length()) { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + aGroups = translate(elements,theVector,true,true); + } + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".TranslateMakeGroups( " << theIDsOfElements << ", " - << theVector << " )"; + << theVector << " )"; } return aGroups; } //======================================================================= //function : TranslateObjectMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* SMESH_MeshEditor_i::TranslateObjectMakeGroups(SMESH::SMESH_IDSource_ptr theObject, const SMESH::DirStruct& theVector) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = translate(anElementsId, theVector, true, true); - - if ( !myPreviewMode ) { + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + SMESH::ListOfGroups * aGroups = 0; + TIDSortedElemSet elements; + if (idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + aGroups = translate(elements, theVector, true, true); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".TranslateObjectMakeGroups( " << theObject << ", " << theVector << " )"; @@ -2818,7 +3713,7 @@ SMESH_MeshEditor_i::TranslateObjectMakeGroups(SMESH::SMESH_IDSource_ptr theObjec //======================================================================= //function : TranslateMakeMesh -//purpose : +//purpose : //======================================================================= SMESH::SMESH_Mesh_ptr @@ -2829,40 +3724,42 @@ SMESH_MeshEditor_i::TranslateMakeMesh(const SMESH::long_array& theIDsOfElements, { SMESH_Mesh_i* mesh_i; SMESH::SMESH_Mesh_var mesh; - + { // open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() TPythonDump pydump; // to prevent dump at mesh creation - + mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - - if ( mesh_i ) { - translate(theIDsOfElements, theVector, - false, theCopyGroups, & mesh_i->GetImpl()); + + if ( mesh_i && theIDsOfElements.length() ) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + translate(elements, theVector, false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } - + if ( !myPreviewMode ) { pydump << mesh << " = " << this << ".TranslateMakeMesh( " << theIDsOfElements << ", " - << theVector << ", " - << theCopyGroups << ", '" - << theMeshName << "' )"; + << theVector << ", " + << theCopyGroups << ", '" + << theMeshName << "' )"; } } - + //dump "GetGroups" - if(!myPreviewMode && mesh_i) + if (!myPreviewMode && mesh_i) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= //function : TranslateObjectMakeMesh -//purpose : +//purpose : //======================================================================= SMESH::SMESH_Mesh_ptr @@ -2875,50 +3772,51 @@ SMESH_MeshEditor_i::TranslateObjectMakeMesh(SMESH::SMESH_IDSource_ptr theObject, SMESH::SMESH_Mesh_var mesh; { // open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() - + TPythonDump pydump; // to prevent dump at mesh creation mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - - if ( mesh_i ) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - translate(anElementsId, theVector, - false, theCopyGroups, & mesh_i->GetImpl()); + + TIDSortedElemSet elements; + if ( mesh_i && + idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + { + translate(elements, theVector,false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } if ( !myPreviewMode ) { pydump << mesh << " = " << this << ".TranslateObjectMakeMesh( " - << theObject << ", " - << theVector << ", " + << theObject << ", " + << theVector << ", " << theCopyGroups << ", '" - << theMeshName << "' )"; + << theMeshName << "' )"; } } - - //dump "GetGroups" - if(!myPreviewMode && mesh_i) + + // dump "GetGroups" + if (!myPreviewMode && mesh_i) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= //function : rotate -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* -SMESH_MeshEditor_i::rotate(const SMESH::long_array & theIDsOfElements, +SMESH_MeshEditor_i::rotate(TIDSortedElemSet & theElements, const SMESH::AxisStruct & theAxis, CORBA::Double theAngle, CORBA::Boolean theCopy, - const bool theMakeGroups, + bool theMakeGroups, ::SMESH_Mesh* theTargetMesh) { initData(); - TIDSortedElemSet elements; - arrayToSet(theIDsOfElements, GetMeshDS(), elements); + if ( theTargetMesh ) + theCopy = false; gp_Pnt P ( theAxis.x, theAxis.y, theAxis.z ); gp_Vec V ( theAxis.vx, theAxis.vy, theAxis.vz ); @@ -2926,13 +3824,43 @@ SMESH_MeshEditor_i::rotate(const SMESH::long_array & theIDsOfElements, gp_Trsf aTrsf; aTrsf.SetRotation( gp_Ax1( P, V ), theAngle); - ::SMESH_MeshEditor anEditor( myMesh ); + TIDSortedElemSet copyElements; + TIDSortedElemSet* workElements = &theElements; + TPreviewMesh tmpMesh; + SMESH_Mesh* mesh = myMesh; + + if ( myPreviewMode ) { + tmpMesh.Copy( theElements, copyElements ); + if ( !theCopy && !theTargetMesh ) + { + TIDSortedElemSet elemsAround, elemsAroundCopy; + getElementsAround( theElements, GetMeshDS(), elemsAround ); + tmpMesh.Copy( elemsAround, elemsAroundCopy); + } + mesh = &tmpMesh; + workElements = ©Elements; + theMakeGroups = false; + } + ::SMESH_MeshEditor::PGroupIDs groupIds = - anEditor.Transform (elements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + myEditor.Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + + if(theCopy || myPreviewMode) + storeResult(myEditor); - if(theCopy) { - storeResult(anEditor); + if ( !myPreviewMode ) + { + if ( theTargetMesh ) + { + theTargetMesh->GetMeshDS()->Modified(); + } + else + { + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + } } + return theMakeGroups ? getGroups(groupIds.get()) : 0; } @@ -2946,18 +3874,19 @@ void SMESH_MeshEditor_i::Rotate(const SMESH::long_array & theIDsOfElements, CORBA::Double theAngle, CORBA::Boolean theCopy) { - if ( !myPreviewMode ) { + if (!myPreviewMode) { TPythonDump() << this << ".Rotate( " << theIDsOfElements << ", " - << theAxis << ", " - << theAngle << ", " - << theCopy << " )"; + << theAxis << ", " + << TVar( theAngle ) << ", " + << theCopy << " )"; + } + if (theIDsOfElements.length() > 0) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + rotate(elements,theAxis,theAngle,theCopy,false); } - rotate(theIDsOfElements, - theAxis, - theAngle, - theCopy, - false); } //======================================================================= @@ -2966,28 +3895,26 @@ void SMESH_MeshEditor_i::Rotate(const SMESH::long_array & theIDsOfElements, //======================================================================= void SMESH_MeshEditor_i::RotateObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & theAxis, - CORBA::Double theAngle, - CORBA::Boolean theCopy) + const SMESH::AxisStruct & theAxis, + CORBA::Double theAngle, + CORBA::Boolean theCopy) { if ( !myPreviewMode ) { TPythonDump() << this << ".RotateObject( " - << theObject << ", " - << theAxis << ", " - << theAngle << ", " - << theCopy << " )"; + << theObject << ", " + << theAxis << ", " + << TVar( theAngle ) << ", " + << theCopy << " )"; } - SMESH::long_array_var anElementsId = theObject->GetIDs(); - rotate(anElementsId, - theAxis, - theAngle, - theCopy, - false); + TIDSortedElemSet elements; + bool emptyIfIsMesh = myPreviewMode ? false : true; + if (idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, emptyIfIsMesh)) + rotate(elements,theAxis,theAngle,theCopy,false); } //======================================================================= //function : RotateMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -2995,21 +3922,28 @@ SMESH_MeshEditor_i::RotateMakeGroups(const SMESH::long_array& theIDsOfElements, const SMESH::AxisStruct& theAxis, CORBA::Double theAngle) { - SMESH::ListOfGroups * aGroups = rotate(theIDsOfElements,theAxis,theAngle,true,true); - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = 0; + if (theIDsOfElements.length() > 0) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + aGroups = rotate(elements,theAxis,theAngle,true,true); + } + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".RotateMakeGroups( " << theIDsOfElements << ", " - << theAxis << ", " - << theAngle << " )"; + << theAxis << ", " + << TVar( theAngle ) << " )"; } return aGroups; } //======================================================================= //function : RotateObjectMakeGroups -//purpose : +//purpose : //======================================================================= SMESH::ListOfGroups* @@ -3017,26 +3951,29 @@ SMESH_MeshEditor_i::RotateObjectMakeGroups(SMESH::SMESH_IDSource_ptr theObject, const SMESH::AxisStruct& theAxis, CORBA::Double theAngle) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - SMESH::ListOfGroups * aGroups = rotate(anElementsId,theAxis,theAngle,true,true); - - if ( !myPreviewMode ) { - TPythonDump aPythonDump; - DumpGroupsList(aPythonDump,aGroups); + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = 0; + TIDSortedElemSet elements; + if (idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + aGroups = rotate(elements, theAxis, theAngle, true, true); + + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); aPythonDump << this << ".RotateObjectMakeGroups( " - << theObject << ", " - << theAxis << ", " - << theAngle << " )"; + << theObject << ", " + << theAxis << ", " + << TVar( theAngle ) << " )"; } return aGroups; } //======================================================================= //function : RotateMakeMesh -//purpose : +//purpose : //======================================================================= -SMESH::SMESH_Mesh_ptr +SMESH::SMESH_Mesh_ptr SMESH_MeshEditor_i::RotateMakeMesh(const SMESH::long_array& theIDsOfElements, const SMESH::AxisStruct& theAxis, CORBA::Double theAngleInRadians, @@ -3048,40 +3985,43 @@ SMESH_MeshEditor_i::RotateMakeMesh(const SMESH::long_array& theIDsOfElements, { // open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() - + TPythonDump pydump; // to prevent dump at mesh creation mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - - if ( mesh_i ) { - rotate(theIDsOfElements, theAxis, theAngleInRadians, + + if ( mesh_i && theIDsOfElements.length() > 0 ) + { + TIDSortedElemSet elements; + arrayToSet(theIDsOfElements, GetMeshDS(), elements); + rotate(elements, theAxis, theAngleInRadians, false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } if ( !myPreviewMode ) { pydump << mesh << " = " << this << ".RotateMakeMesh( " - << theIDsOfElements << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theCopyGroups << ", '" - << theMeshName << "' )"; + << theIDsOfElements << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << theCopyGroups << ", '" + << theMeshName << "' )"; } } - - //dump "GetGroups" - if(!myPreviewMode && mesh_i) + + // dump "GetGroups" + if (!myPreviewMode && mesh_i && theIDsOfElements.length() > 0 ) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= //function : RotateObjectMakeMesh -//purpose : +//purpose : //======================================================================= -SMESH::SMESH_Mesh_ptr +SMESH::SMESH_Mesh_ptr SMESH_MeshEditor_i::RotateObjectMakeMesh(SMESH::SMESH_IDSource_ptr theObject, const SMESH::AxisStruct& theAxis, CORBA::Double theAngleInRadians, @@ -3090,108 +4030,220 @@ SMESH_MeshEditor_i::RotateObjectMakeMesh(SMESH::SMESH_IDSource_ptr theObject, { SMESH::SMESH_Mesh_var mesh; SMESH_Mesh_i* mesh_i; - + {// open new scope to dump "MakeMesh" command // and then "GetGroups" using SMESH_Mesh::GetGroups() - + TPythonDump pydump; // to prevent dump at mesh creation mesh = makeMesh( theMeshName ); mesh_i = SMESH::DownCast( mesh ); - - if (mesh_i ) { - SMESH::long_array_var anElementsId = theObject->GetIDs(); - rotate(anElementsId, theAxis, theAngleInRadians, + + TIDSortedElemSet elements; + if (mesh_i && + idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, /*emptyIfIsMesh=*/1)) + { + rotate(elements, theAxis, theAngleInRadians, false, theCopyGroups, & mesh_i->GetImpl()); mesh_i->CreateGroupServants(); } if ( !myPreviewMode ) { pydump << mesh << " = " << this << ".RotateObjectMakeMesh( " - << theObject << ", " - << theAxis << ", " - << theAngleInRadians << ", " - << theCopyGroups << ", '" - << theMeshName << "' )"; + << theObject << ", " + << theAxis << ", " + << TVar( theAngleInRadians ) << ", " + << theCopyGroups << ", '" + << theMeshName << "' )"; } } - - //dump "GetGroups" - if(!myPreviewMode && mesh_i) + + // dump "GetGroups" + if (!myPreviewMode && mesh_i) mesh_i->GetGroups(); - + return mesh._retn(); } //======================================================================= -//function : FindCoincidentNodes +//function : scale //purpose : //======================================================================= -void SMESH_MeshEditor_i::FindCoincidentNodes (CORBA::Double Tolerance, - SMESH::array_of_long_array_out GroupsOfNodes) +SMESH::ListOfGroups* +SMESH_MeshEditor_i::scale(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact, + CORBA::Boolean theCopy, + bool theMakeGroups, + ::SMESH_Mesh* theTargetMesh) { initData(); + if ( theScaleFact.length() < 1 ) + THROW_SALOME_CORBA_EXCEPTION("Scale factor not given", SALOME::BAD_PARAM); + if ( theScaleFact.length() == 2 ) + THROW_SALOME_CORBA_EXCEPTION("Invalid nb of scale factors : 2", SALOME::BAD_PARAM); - ::SMESH_MeshEditor::TListOfListOfNodes aListOfListOfNodes; - ::SMESH_MeshEditor anEditor( myMesh ); - set nodes; // no input nodes - anEditor.FindCoincidentNodes( nodes, Tolerance, aListOfListOfNodes ); + if ( theTargetMesh ) + theCopy = false; - GroupsOfNodes = new SMESH::array_of_long_array; - GroupsOfNodes->length( aListOfListOfNodes.size() ); - ::SMESH_MeshEditor::TListOfListOfNodes::iterator llIt = aListOfListOfNodes.begin(); - for ( CORBA::Long i = 0; llIt != aListOfListOfNodes.end(); llIt++, i++ ) { - list< const SMDS_MeshNode* >& aListOfNodes = *llIt; - list< const SMDS_MeshNode* >::iterator lIt = aListOfNodes.begin();; - SMESH::long_array& aGroup = (*GroupsOfNodes)[ i ]; - aGroup.length( aListOfNodes.size() ); - for ( int j = 0; lIt != aListOfNodes.end(); lIt++, j++ ) - aGroup[ j ] = (*lIt)->GetID(); + TIDSortedElemSet elements; + bool emptyIfIsMesh = myPreviewMode ? false : true; + if ( !idSourceToSet(theObject, GetMeshDS(), elements, SMDSAbs_All, emptyIfIsMesh)) + return 0; + + double S[3] = { + theScaleFact[0], + (theScaleFact.length() == 1) ? theScaleFact[0] : theScaleFact[1], + (theScaleFact.length() == 1) ? theScaleFact[0] : theScaleFact[2], + }; + double tol = std::numeric_limits::max(); + gp_Trsf aTrsf; + aTrsf.SetValues( S[0], 0, 0, thePoint.x * (1-S[0]), + 0, S[1], 0, thePoint.y * (1-S[1]), + 0, 0, S[2], thePoint.z * (1-S[2]), tol, tol); + + TIDSortedElemSet copyElements; + TPreviewMesh tmpMesh; + TIDSortedElemSet* workElements = &elements; + SMESH_Mesh* mesh = myMesh; + + if ( myPreviewMode ) + { + tmpMesh.Copy( elements, copyElements); + if ( !theCopy && !theTargetMesh ) + { + TIDSortedElemSet elemsAround, elemsAroundCopy; + getElementsAround( elements, GetMeshDS(), elemsAround ); + tmpMesh.Copy( elemsAround, elemsAroundCopy); + } + mesh = &tmpMesh; + workElements = & copyElements; + theMakeGroups = false; } - TPythonDump() << "coincident_nodes = " << this << ".FindCoincidentNodes( " - << Tolerance << " )"; + + ::SMESH_MeshEditor::PGroupIDs groupIds = + myEditor.Transform (*workElements, aTrsf, theCopy, theMakeGroups, theTargetMesh); + + if(theCopy || myPreviewMode ) + storeResult(myEditor); + + if ( !myPreviewMode ) + { + if ( theTargetMesh ) + { + theTargetMesh->GetMeshDS()->Modified(); + } + else + { + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + } + } + + return theMakeGroups ? getGroups(groupIds.get()) : 0; } //======================================================================= -//function : FindCoincidentNodesOnPart +//function : Scale //purpose : //======================================================================= -void SMESH_MeshEditor_i::FindCoincidentNodesOnPart(SMESH::SMESH_IDSource_ptr theObject, - CORBA::Double Tolerance, - SMESH::array_of_long_array_out GroupsOfNodes) + +void SMESH_MeshEditor_i::Scale(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact, + CORBA::Boolean theCopy) { - initData(); - SMESH::long_array_var aElementsId = theObject->GetIDs(); + if ( !myPreviewMode ) { + TPythonDump() << this << ".Scale( " + << theObject << ", " + << thePoint << ", " + << TVar( theScaleFact ) << ", " + << theCopy << " )"; + } + scale(theObject, thePoint, theScaleFact, theCopy, false); +} - SMESHDS_Mesh* aMesh = GetMeshDS(); - set nodes; - if ( !CORBA::is_nil(SMESH::SMESH_GroupBase::_narrow(theObject)) && - SMESH::SMESH_GroupBase::_narrow(theObject)->GetType() == SMESH::NODE) { - for(int i = 0; i < aElementsId->length(); i++) { - CORBA::Long ind = aElementsId[i]; - const SMDS_MeshNode * elem = aMesh->FindNode(ind); - if(elem) - nodes.insert(elem); - } +//======================================================================= +//function : ScaleMakeGroups +//purpose : +//======================================================================= + +SMESH::ListOfGroups* +SMESH_MeshEditor_i::ScaleMakeGroups(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact) +{ + TPythonDump aPythonDump; // it is here to prevent dump of GetGroups() + + SMESH::ListOfGroups * aGroups = scale(theObject, thePoint, theScaleFact, true, true); + if (!myPreviewMode) { + DumpGroupsList(aPythonDump, aGroups); + aPythonDump << this << ".Scale(" + << theObject << "," + << thePoint << "," + << TVar( theScaleFact ) << ",True,True)"; } - else { - for(int i = 0; i < aElementsId->length(); i++) { - CORBA::Long ind = aElementsId[i]; - const SMDS_MeshElement * elem = aMesh->FindElement(ind); - if(elem) { - SMDS_ElemIteratorPtr nIt = elem->nodesIterator(); - while ( nIt->more() ) - nodes.insert( nodes.end(),static_cast(nIt->next())); - } + return aGroups; +} + + +//======================================================================= +//function : ScaleMakeMesh +//purpose : +//======================================================================= + +SMESH::SMESH_Mesh_ptr +SMESH_MeshEditor_i::ScaleMakeMesh(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact, + CORBA::Boolean theCopyGroups, + const char* theMeshName) +{ + SMESH_Mesh_i* mesh_i; + SMESH::SMESH_Mesh_var mesh; + { // open new scope to dump "MakeMesh" command + // and then "GetGroups" using SMESH_Mesh::GetGroups() + + TPythonDump pydump; // to prevent dump at mesh creation + mesh = makeMesh( theMeshName ); + mesh_i = SMESH::DownCast( mesh ); + + if ( mesh_i ) + { + scale(theObject, thePoint, theScaleFact,false, theCopyGroups, & mesh_i->GetImpl()); + mesh_i->CreateGroupServants(); } + if ( !myPreviewMode ) + pydump << mesh << " = " << this << ".ScaleMakeMesh( " + << theObject << ", " + << thePoint << ", " + << TVar( theScaleFact ) << ", " + << theCopyGroups << ", '" + << theMeshName << "' )"; } - - + + // dump "GetGroups" + if (!myPreviewMode && mesh_i) + mesh_i->GetGroups(); + + return mesh._retn(); +} + + +//======================================================================= +//function : FindCoincidentNodes +//purpose : +//======================================================================= + +void SMESH_MeshEditor_i::FindCoincidentNodes (CORBA::Double Tolerance, + SMESH::array_of_long_array_out GroupsOfNodes) +{ + initData(); + ::SMESH_MeshEditor::TListOfListOfNodes aListOfListOfNodes; - ::SMESH_MeshEditor anEditor( myMesh ); - if(!nodes.empty()) - anEditor.FindCoincidentNodes( nodes, Tolerance, aListOfListOfNodes ); - + TIDSortedNodeSet nodes; // no input nodes + myEditor.FindCoincidentNodes( nodes, Tolerance, aListOfListOfNodes ); + GroupsOfNodes = new SMESH::array_of_long_array; GroupsOfNodes->length( aListOfListOfNodes.size() ); ::SMESH_MeshEditor::TListOfListOfNodes::iterator llIt = aListOfListOfNodes.begin(); @@ -3203,11 +4255,92 @@ void SMESH_MeshEditor_i::FindCoincidentNodesOnPart(SMESH::SMESH_IDSource_ptr for ( int j = 0; lIt != aListOfNodes.end(); lIt++, j++ ) aGroup[ j ] = (*lIt)->GetID(); } + TPythonDump() << "coincident_nodes = " << this << ".FindCoincidentNodes( " + << Tolerance << " )"; +} + +//======================================================================= +//function : FindCoincidentNodesOnPart +//purpose : +//======================================================================= +void SMESH_MeshEditor_i::FindCoincidentNodesOnPart(SMESH::SMESH_IDSource_ptr theObject, + CORBA::Double Tolerance, + SMESH::array_of_long_array_out GroupsOfNodes) +{ + initData(); + + TIDSortedNodeSet nodes; + idSourceToNodeSet( theObject, GetMeshDS(), nodes ); + + ::SMESH_MeshEditor::TListOfListOfNodes aListOfListOfNodes; + if(!nodes.empty()) + myEditor.FindCoincidentNodes( nodes, Tolerance, aListOfListOfNodes ); + + GroupsOfNodes = new SMESH::array_of_long_array; + GroupsOfNodes->length( aListOfListOfNodes.size() ); + ::SMESH_MeshEditor::TListOfListOfNodes::iterator llIt = aListOfListOfNodes.begin(); + for ( CORBA::Long i = 0; llIt != aListOfListOfNodes.end(); llIt++, i++ ) + { + list< const SMDS_MeshNode* >& aListOfNodes = *llIt; + list< const SMDS_MeshNode* >::iterator lIt = aListOfNodes.begin();; + SMESH::long_array& aGroup = (*GroupsOfNodes)[ i ]; + aGroup.length( aListOfNodes.size() ); + for ( int j = 0; lIt != aListOfNodes.end(); lIt++, j++ ) + aGroup[ j ] = (*lIt)->GetID(); + } TPythonDump() << "coincident_nodes_on_part = " << this << ".FindCoincidentNodesOnPart( " <length( aListOfListOfNodes.size() ); + ::SMESH_MeshEditor::TListOfListOfNodes::iterator llIt = aListOfListOfNodes.begin(); + for ( CORBA::Long i = 0; llIt != aListOfListOfNodes.end(); llIt++, i++ ) + { + list< const SMDS_MeshNode* >& aListOfNodes = *llIt; + list< const SMDS_MeshNode* >::iterator lIt = aListOfNodes.begin();; + SMESH::long_array& aGroup = (*theGroupsOfNodes)[ i ]; + aGroup.length( aListOfNodes.size() ); + for ( int j = 0; lIt != aListOfNodes.end(); lIt++, j++ ) + aGroup[ j ] = (*lIt)->GetID(); + } + TPythonDump() << "coincident_nodes_on_part = " << this << ".FindCoincidentNodesOnPartBut( " + << theObject<<", " + << theTolerance << ", " + << theExceptSubMeshOrGroups << " )"; +} + //======================================================================= //function : MergeNodes //purpose : @@ -3240,10 +4373,11 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN if ( i > 0 ) aTPythonDump << ", "; aTPythonDump << aNodeGroup; } - ::SMESH_MeshEditor anEditor( myMesh ); - anEditor.MergeNodes( aListOfListOfNodes ); + myEditor.MergeNodes( aListOfListOfNodes ); aTPythonDump << "])"; + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); } //======================================================================= @@ -3251,11 +4385,13 @@ void SMESH_MeshEditor_i::MergeNodes (const SMESH::array_of_long_array& GroupsOfN //purpose : //======================================================================= void SMESH_MeshEditor_i::FindEqualElements(SMESH::SMESH_IDSource_ptr theObject, - SMESH::array_of_long_array_out GroupsOfElementsID) + SMESH::array_of_long_array_out GroupsOfElementsID) { initData(); - if ( !(!CORBA::is_nil(SMESH::SMESH_GroupBase::_narrow(theObject)) && - SMESH::SMESH_GroupBase::_narrow(theObject)->GetType() == SMESH::NODE) ) { + + SMESH::SMESH_GroupBase_var group = SMESH::SMESH_GroupBase::_narrow(theObject); + if ( !(!group->_is_nil() && group->GetType() == SMESH::NODE) ) + { typedef list TListOfIDs; set elems; SMESH::long_array_var aElementsId = theObject->GetIDs(); @@ -3265,13 +4401,12 @@ void SMESH_MeshEditor_i::FindEqualElements(SMESH::SMESH_IDSource_ptr theObj CORBA::Long anID = aElementsId[i]; const SMDS_MeshElement * elem = aMesh->FindElement(anID); if (elem) { - elems.insert(elem); + elems.insert(elem); } } ::SMESH_MeshEditor::TListOfListOfElementsID aListOfListOfElementsID; - ::SMESH_MeshEditor anEditor( myMesh ); - anEditor.FindEqualElements( elems, aListOfListOfElementsID ); + myEditor.FindEqualElements( elems, aListOfListOfElementsID ); GroupsOfElementsID = new SMESH::array_of_long_array; GroupsOfElementsID->length( aListOfListOfElementsID.size() ); @@ -3283,12 +4418,12 @@ void SMESH_MeshEditor_i::FindEqualElements(SMESH::SMESH_IDSource_ptr theObj aGroup.length( listOfIDs.size() ); TListOfIDs::iterator idIt = listOfIDs.begin(); for (int k = 0; idIt != listOfIDs.end(); ++idIt, ++k ) { - aGroup[ k ] = *idIt; + aGroup[ k ] = *idIt; } } - TPythonDump() << "equal_elements = " << this << ".FindEqualElements( " - <GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); aTPythonDump << "] )"; } @@ -3335,12 +4471,91 @@ void SMESH_MeshEditor_i::MergeEqualElements() { initData(); - ::SMESH_MeshEditor anEditor( myMesh ); - anEditor.MergeEqualElements(); + myEditor.MergeEqualElements(); + + myMesh->GetMeshDS()->Modified(); TPythonDump() << this << ".MergeEqualElements()"; } +//============================================================================= +/*! + * Move the node to a given point + */ +//============================================================================= + +CORBA::Boolean SMESH_MeshEditor_i::MoveNode(CORBA::Long NodeID, + CORBA::Double x, + CORBA::Double y, + CORBA::Double z) +{ + initData(/*deleteSearchers=*/false); + + const SMDS_MeshNode * node = GetMeshDS()->FindNode( NodeID ); + if ( !node ) + return false; + + if ( theNodeSearcher ) + theSearchersDeleter.Set( myMesh ); // remove theNodeSearcher if mesh is other + + if ( myPreviewMode ) // make preview data + { + // in a preview mesh, make edges linked to a node + TPreviewMesh tmpMesh; + TIDSortedElemSet linkedNodes; + ::SMESH_MeshEditor::GetLinkedNodes( node, linkedNodes ); + TIDSortedElemSet::iterator nIt = linkedNodes.begin(); + SMDS_MeshNode *nodeCpy1 = tmpMesh.Copy(node); + for ( ; nIt != linkedNodes.end(); ++nIt ) + { + SMDS_MeshNode *nodeCpy2 = tmpMesh.Copy ( cast2Node( *nIt )); + tmpMesh.GetMeshDS()->AddEdge(nodeCpy1, nodeCpy2); + } + // move copied node + if ( nodeCpy1 ) + tmpMesh.GetMeshDS()->MoveNode(nodeCpy1, x, y, z); + // fill preview data + storeResult( myEditor ); + } + else if ( theNodeSearcher ) // move node and update theNodeSearcher data accordingly + theNodeSearcher->MoveNode(node, gp_Pnt( x,y,z )); + else + GetMeshDS()->MoveNode(node, x, y, z); + + if ( !myPreviewMode ) + { + // Update Python script + TPythonDump() << "isDone = " << this << ".MoveNode( " + << NodeID << ", " << TVar(x) << ", " << TVar(y) << ", " << TVar(z) << " )"; + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); + } + + return true; +} + +//================================================================================ +/*! + * \brief Return ID of node closest to a given point + */ +//================================================================================ + +CORBA::Long SMESH_MeshEditor_i::FindNodeClosestTo(CORBA::Double x, + CORBA::Double y, + CORBA::Double z) +{ + theSearchersDeleter.Set( myMesh ); // remove theNodeSearcher if mesh is other + + if ( !theNodeSearcher ) { + theNodeSearcher = myEditor.GetNodeSearcher(); + } + gp_Pnt p( x,y,z ); + if ( const SMDS_MeshNode* node = theNodeSearcher->FindClosestTo( p )) + return node->GetID(); + + return 0; +} + //================================================================================ /*! * \brief If the given ID is a valid node ID (nodeID > 0), just move this node, else @@ -3353,24 +4568,23 @@ CORBA::Long SMESH_MeshEditor_i::MoveClosestNodeToPoint(CORBA::Double x, CORBA::Double z, CORBA::Long theNodeID) { - // We keep myNodeSearcher until any mesh modification: - // 1) initData() deletes myNodeSearcher at any edition, - // 2) TNodeSearcherDeleter - at any mesh compute event and mesh change + // We keep theNodeSearcher until any mesh modification: + // 1) initData() deletes theNodeSearcher at any edition, + // 2) TSearchersDeleter - at any mesh compute event and mesh change - initData(); + initData(/*deleteSearchers=*/false); + + theSearchersDeleter.Set( myMesh ); // remove theNodeSearcher if mesh is other int nodeID = theNodeID; const SMDS_MeshNode* node = GetMeshDS()->FindNode( nodeID ); - if ( !node ) + if ( !node ) // preview moving node { - static TNodeSearcherDeleter deleter; - deleter.Set( myMesh ); - if ( !myNodeSearcher ) { - ::SMESH_MeshEditor anEditor( myMesh ); - myNodeSearcher = anEditor.GetNodeSearcher(); + if ( !theNodeSearcher ) { + theNodeSearcher = myEditor.GetNodeSearcher(); } gp_Pnt p( x,y,z ); - node = myNodeSearcher->FindClosestTo( p ); + node = theNodeSearcher->FindClosestTo( p ); } if ( node ) { nodeID = node->GetID(); @@ -3383,7 +4597,7 @@ CORBA::Long SMESH_MeshEditor_i::MoveClosestNodeToPoint(CORBA::Double x, TIDSortedElemSet::iterator nIt = linkedNodes.begin(); for ( ; nIt != linkedNodes.end(); ++nIt ) { - SMDS_MeshEdge edge( node, cast2Node( *nIt )); + SMDS_LinearEdge edge( node, cast2Node( *nIt )); tmpMesh.Copy( &edge ); } // move copied node @@ -3391,8 +4605,11 @@ CORBA::Long SMESH_MeshEditor_i::MoveClosestNodeToPoint(CORBA::Double x, if ( node ) tmpMesh.GetMeshDS()->MoveNode(node, x, y, z); // fill preview data - ::SMESH_MeshEditor anEditor( & tmpMesh ); - storeResult( anEditor ); + storeResult( myEditor ); + } + else if ( theNodeSearcher ) // move node and update theNodeSearcher data accordingly + { + theNodeSearcher->MoveNode(node, gp_Pnt( x,y,z )); } else { @@ -3400,15 +4617,137 @@ CORBA::Long SMESH_MeshEditor_i::MoveClosestNodeToPoint(CORBA::Double x, } } - if ( !myPreviewMode ) { + if ( !myPreviewMode ) + { TPythonDump() << "nodeID = " << this << ".MoveClosestNodeToPoint( "<< x << ", " << y << ", " << z << ", " << nodeID << " )"; + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); } return nodeID; } +//======================================================================= +/*! + * Return elements of given type where the given point is IN or ON. + * + * 'ALL' type means elements of any type excluding nodes + */ +//======================================================================= + +SMESH::long_array* SMESH_MeshEditor_i::FindElementsByPoint(CORBA::Double x, + CORBA::Double y, + CORBA::Double z, + SMESH::ElementType type) +{ + SMESH::long_array_var res = new SMESH::long_array; + vector< const SMDS_MeshElement* > foundElems; + + theSearchersDeleter.Set( myMesh ); + if ( !theElementSearcher ) { + theElementSearcher = myEditor.GetElementSearcher(); + } + theElementSearcher->FindElementsByPoint( gp_Pnt( x,y,z ), + SMDSAbs_ElementType( type ), + foundElems); + res->length( foundElems.size() ); + for ( int i = 0; i < foundElems.size(); ++i ) + res[i] = foundElems[i]->GetID(); + + if ( !myPreviewMode ) // call from tui + TPythonDump() << "res = " << this << ".FindElementsByPoint( " + << x << ", " + << y << ", " + << z << ", " + << type << " )"; + + return res._retn(); +} + +//======================================================================= +//function : FindAmongElementsByPoint +//purpose : Searching among the given elements, return elements of given type +// where the given point is IN or ON. +// 'ALL' type means elements of any type excluding nodes +//======================================================================= + +SMESH::long_array* +SMESH_MeshEditor_i::FindAmongElementsByPoint(SMESH::SMESH_IDSource_ptr elementIDs, + CORBA::Double x, + CORBA::Double y, + CORBA::Double z, + SMESH::ElementType type) +{ + SMESH::long_array_var res = new SMESH::long_array; + + SMESH::array_of_ElementType_var types = elementIDs->GetTypes(); + if ( types->length() == 1 && // a part contains only nodes or 0D elements + ( types[0] == SMESH::NODE || types[0] == SMESH::ELEM0D || types[0] == SMESH::BALL) && + type != types[0] ) // but search of elements of dim > 0 + return res._retn(); + + if ( SMESH::DownCast( elementIDs )) // elementIDs is the whole mesh + return FindElementsByPoint( x,y,z, type ); + + TIDSortedElemSet elements; // elems should live until FindElementsByPoint() finishes + + theSearchersDeleter.Set( myMesh, getPartIOR( elementIDs, type )); + if ( !theElementSearcher ) + { + // create a searcher from elementIDs + SMESH::SMESH_Mesh_var mesh = elementIDs->GetMesh(); + SMESHDS_Mesh* meshDS = SMESH::DownCast( mesh )->GetImpl().GetMeshDS(); + + if ( !idSourceToSet( elementIDs, meshDS, elements, + SMDSAbs_ElementType(type), /*emptyIfIsMesh=*/true)) + return res._retn(); + + typedef SMDS_SetIterator TIter; + SMDS_ElemIteratorPtr elemsIt( new TIter( elements.begin(), elements.end() )); + + theElementSearcher = myEditor.GetElementSearcher(elemsIt); + } + + vector< const SMDS_MeshElement* > foundElems; + + theElementSearcher->FindElementsByPoint( gp_Pnt( x,y,z ), + SMDSAbs_ElementType( type ), + foundElems); + res->length( foundElems.size() ); + for ( int i = 0; i < foundElems.size(); ++i ) + res[i] = foundElems[i]->GetID(); + + if ( !myPreviewMode ) // call from tui + TPythonDump() << "res = " << this << ".FindAmongElementsByPoint( " + << elementIDs << ", " + << x << ", " + << y << ", " + << z << ", " + << type << " )"; + + return res._retn(); + +} +//======================================================================= +//function : GetPointState +//purpose : Return point state in a closed 2D mesh in terms of TopAbs_State enumeration. +// TopAbs_UNKNOWN state means that either mesh is wrong or the analysis fails. +//======================================================================= + +CORBA::Short SMESH_MeshEditor_i::GetPointState(CORBA::Double x, + CORBA::Double y, + CORBA::Double z) +{ + theSearchersDeleter.Set( myMesh ); + if ( !theElementSearcher ) { + theElementSearcher = myEditor.GetElementSearcher(); + } + return CORBA::Short( theElementSearcher->GetPointState( gp_Pnt( x,y,z ))); +} + //======================================================================= //function : convError //purpose : @@ -3419,16 +4758,16 @@ CORBA::Long SMESH_MeshEditor_i::MoveClosestNodeToPoint(CORBA::Double x, static SMESH::SMESH_MeshEditor::Sew_Error convError( const::SMESH_MeshEditor::Sew_Error e ) { switch ( e ) { - RETCASE( SEW_OK ); - RETCASE( SEW_BORDER1_NOT_FOUND ); - RETCASE( SEW_BORDER2_NOT_FOUND ); - RETCASE( SEW_BOTH_BORDERS_NOT_FOUND ); - RETCASE( SEW_BAD_SIDE_NODES ); - RETCASE( SEW_VOLUMES_TO_SPLIT ); - RETCASE( SEW_DIFF_NB_OF_ELEMENTS ); - RETCASE( SEW_TOPO_DIFF_SETS_OF_ELEMENTS ); - RETCASE( SEW_BAD_SIDE1_NODES ); - RETCASE( SEW_BAD_SIDE2_NODES ); + RETCASE( SEW_OK ); + RETCASE( SEW_BORDER1_NOT_FOUND ); + RETCASE( SEW_BORDER2_NOT_FOUND ); + RETCASE( SEW_BOTH_BORDERS_NOT_FOUND ); + RETCASE( SEW_BAD_SIDE_NODES ); + RETCASE( SEW_VOLUMES_TO_SPLIT ); + RETCASE( SEW_DIFF_NB_OF_ELEMENTS ); + RETCASE( SEW_TOPO_DIFF_SETS_OF_ELEMENTS ); + RETCASE( SEW_BAD_SIDE1_NODES ); + RETCASE( SEW_BAD_SIDE2_NODES ); } return SMESH::SMESH_MeshEditor::SEW_OK; } @@ -3439,14 +4778,14 @@ static SMESH::SMESH_MeshEditor::Sew_Error convError( const::SMESH_MeshEditor::Se //======================================================================= SMESH::SMESH_MeshEditor::Sew_Error - SMESH_MeshEditor_i::SewFreeBorders(CORBA::Long FirstNodeID1, - CORBA::Long SecondNodeID1, - CORBA::Long LastNodeID1, - CORBA::Long FirstNodeID2, - CORBA::Long SecondNodeID2, - CORBA::Long LastNodeID2, - CORBA::Boolean CreatePolygons, - CORBA::Boolean CreatePolyedrs) +SMESH_MeshEditor_i::SewFreeBorders(CORBA::Long FirstNodeID1, + CORBA::Long SecondNodeID1, + CORBA::Long LastNodeID1, + CORBA::Long FirstNodeID2, + CORBA::Long SecondNodeID2, + CORBA::Long LastNodeID2, + CORBA::Boolean CreatePolygons, + CORBA::Boolean CreatePolyedrs) { initData(); @@ -3478,9 +4817,8 @@ SMESH::SMESH_MeshEditor::Sew_Error << CreatePolygons<< ", " << CreatePolyedrs<< " )"; - ::SMESH_MeshEditor anEditor( myMesh ); SMESH::SMESH_MeshEditor::Sew_Error error = - convError( anEditor.SewFreeBorder (aBorderFirstNode, + convError( myEditor.SewFreeBorder (aBorderFirstNode, aBorderSecondNode, aBorderLastNode, aSide2FirstNode, @@ -3490,7 +4828,10 @@ SMESH::SMESH_MeshEditor::Sew_Error CreatePolygons, CreatePolyedrs) ); - storeResult(anEditor); + storeResult(myEditor); + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); return error; } @@ -3534,9 +4875,8 @@ SMESH_MeshEditor_i::SewConformFreeBorders(CORBA::Long FirstNodeID1, << FirstNodeID2 << ", " << SecondNodeID2 << " )"; - ::SMESH_MeshEditor anEditor( myMesh ); SMESH::SMESH_MeshEditor::Sew_Error error = - convError( anEditor.SewFreeBorder (aBorderFirstNode, + convError( myEditor.SewFreeBorder (aBorderFirstNode, aBorderSecondNode, aBorderLastNode, aSide2FirstNode, @@ -3545,7 +4885,10 @@ SMESH_MeshEditor_i::SewConformFreeBorders(CORBA::Long FirstNodeID1, true, false, false) ); - storeResult(anEditor); + storeResult(myEditor); + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); return error; } @@ -3593,9 +4936,8 @@ SMESH_MeshEditor_i::SewBorderToSide(CORBA::Long FirstNodeIDOnFreeBorder, << CreatePolygons << ", " << CreatePolyedrs << ") "; - ::SMESH_MeshEditor anEditor( myMesh ); SMESH::SMESH_MeshEditor::Sew_Error error = - convError( anEditor.SewFreeBorder (aBorderFirstNode, + convError( myEditor.SewFreeBorder (aBorderFirstNode, aBorderSecondNode, aBorderLastNode, aSide2FirstNode, @@ -3605,7 +4947,10 @@ SMESH_MeshEditor_i::SewBorderToSide(CORBA::Long FirstNodeIDOnFreeBorder, CreatePolygons, CreatePolyedrs) ); - storeResult(anEditor); + storeResult(myEditor); + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); return error; } @@ -3652,15 +4997,17 @@ SMESH_MeshEditor_i::SewSideElements(const SMESH::long_array& IDsOfSide1Elements, << NodeID2OfSide1ToMerge << ", " << NodeID2OfSide2ToMerge << ")"; - ::SMESH_MeshEditor anEditor( myMesh ); SMESH::SMESH_MeshEditor::Sew_Error error = - convError( anEditor.SewSideElements (aSide1Elems, aSide2Elems, + convError( myEditor.SewSideElements (aSide1Elems, aSide2Elems, aFirstNode1ToMerge, aFirstNode2ToMerge, aSecondNode1ToMerge, aSecondNode2ToMerge)); - storeResult(anEditor); + storeResult(myEditor); + + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); return error; } @@ -3668,9 +5015,9 @@ SMESH_MeshEditor_i::SewSideElements(const SMESH::long_array& IDsOfSide1Elements, //================================================================================ /*! * \brief Set new nodes for given element - * \param ide - element id - * \param newIDs - new node ids - * \retval CORBA::Boolean - true if result is OK + * \param ide - element id + * \param newIDs - new node ids + * \retval CORBA::Boolean - true if result is OK */ //================================================================================ @@ -3695,176 +5042,114 @@ CORBA::Boolean SMESH_MeshEditor_i::ChangeElemNodes(CORBA::Long ide, } TPythonDump() << "isDone = " << this << ".ChangeElemNodes( " << ide << ", " << newIDs << " )"; -#ifdef _DEBUG_ - TPythonDump() << "print 'ChangeElemNodes: ', isDone"; -#endif - return GetMeshDS()->ChangeElementNodes( elem, & aNodes[0], nbn1+1 ); + MESSAGE("ChangeElementNodes"); + bool res = GetMeshDS()->ChangeElementNodes( elem, & aNodes[0], nbn1+1 ); + + myMesh->GetMeshDS()->Modified(); + if ( res ) + myMesh->SetIsModified( true ); + + return res; +} + +//======================================================================= +//function : ConvertToQuadratic +//purpose : +//======================================================================= + +void SMESH_MeshEditor_i::ConvertToQuadratic(CORBA::Boolean theForce3d) +{ + myEditor.ConvertToQuadratic(theForce3d); + TPythonDump() << this << ".ConvertToQuadratic( " << theForce3d << " )"; + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); +} + +//======================================================================= +//function : ConvertFromQuadratic +//purpose : +//======================================================================= + +CORBA::Boolean SMESH_MeshEditor_i::ConvertFromQuadratic() +{ + CORBA::Boolean isDone = myEditor.ConvertFromQuadratic(); + TPythonDump() << this << ".ConvertFromQuadratic()"; + myMesh->GetMeshDS()->Modified(); + if ( isDone ) + myMesh->SetIsModified( true ); + return isDone; } - //================================================================================ /*! - * \brief Update myLastCreated* or myPreviewData - * \param anEditor - it contains last modification results + * \brief Makes a part of the mesh quadratic */ //================================================================================ -void SMESH_MeshEditor_i::storeResult(::SMESH_MeshEditor& anEditor) +void SMESH_MeshEditor_i::ConvertToQuadraticObject(CORBA::Boolean theForce3d, + SMESH::SMESH_IDSource_ptr theObject) + throw (SALOME::SALOME_Exception) { - if ( myPreviewMode ) { // --- MeshPreviewStruct filling --- + Unexpect aCatch(SALOME_SalomeException); + TPythonDump pyDump; + TIDSortedElemSet elems; + if ( idSourceToSet( theObject, GetMeshDS(), elems, SMDSAbs_All, /*emptyIfIsMesh=*/true )) + { + if ( elems.empty() ) + { + ConvertToQuadratic( theForce3d ); + } + else if ( (*elems.begin())->GetType() == SMDSAbs_Node ) + { + THROW_SALOME_CORBA_EXCEPTION("Group of nodes is not allowed", SALOME::BAD_PARAM); + } + else + { + myEditor.ConvertToQuadratic(theForce3d, elems); + } + } + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); - list aNodesConnectivity; - typedef map TNodesMap; - TNodesMap nodesMap; + pyDump << this << ".ConvertToQuadraticObject( "<( anEditor.GetMesh() ); - SMDSAbs_ElementType previewType = aPreviewMesh->myPreviewType; - - SMESHDS_Mesh* aMeshDS = anEditor.GetMeshDS(); - int nbEdges = aMeshDS->NbEdges(); - int nbFaces = aMeshDS->NbFaces(); - int nbVolum = aMeshDS->NbVolumes(); - switch ( previewType ) { - case SMDSAbs_Edge : nbFaces = nbVolum = 0; break; - case SMDSAbs_Face : nbEdges = nbVolum = 0; break; - case SMDSAbs_Volume: nbEdges = nbFaces = 0; break; - default:; - } - myPreviewData->nodesXYZ.length(aMeshDS->NbNodes()); - myPreviewData->elementTypes.length(nbEdges + nbFaces + nbVolum); - int i = 0, j = 0; - SMDS_ElemIteratorPtr itMeshElems = aMeshDS->elementsIterator(); - - while ( itMeshElems->more() ) { - const SMDS_MeshElement* aMeshElem = itMeshElems->next(); - if ( previewType != SMDSAbs_All && aMeshElem->GetType() != previewType ) - continue; - - SMDS_ElemIteratorPtr itElemNodes = aMeshElem->nodesIterator(); - while ( itElemNodes->more() ) { - const SMDS_MeshNode* aMeshNode = - static_cast( itElemNodes->next() ); - int aNodeID = aMeshNode->GetID(); - TNodesMap::iterator anIter = nodesMap.find(aNodeID); - if ( anIter == nodesMap.end() ) { - // filling the nodes coordinates - myPreviewData->nodesXYZ[j].x = aMeshNode->X(); - myPreviewData->nodesXYZ[j].y = aMeshNode->Y(); - myPreviewData->nodesXYZ[j].z = aMeshNode->Z(); - anIter = nodesMap.insert( make_pair(aNodeID, j) ).first; - j++; - } - aNodesConnectivity.push_back(anIter->second); - } - - // filling the elements types - SMDSAbs_ElementType aType; - bool isPoly; - /*if (aMeshElem->GetType() == SMDSAbs_Volume) { - aType = SMDSAbs_Node; - isPoly = false; - } - else*/ { - aType = aMeshElem->GetType(); - isPoly = aMeshElem->IsPoly(); - } - - myPreviewData->elementTypes[i].SMDS_ElementType = (SMESH::ElementType) aType; - myPreviewData->elementTypes[i].isPoly = isPoly; - myPreviewData->elementTypes[i].nbNodesInElement = aMeshElem->NbNodes(); - i++; +//================================================================================ +/*! + * \brief Makes a part of the mesh linear + */ +//================================================================================ +void SMESH_MeshEditor_i::ConvertFromQuadraticObject(SMESH::SMESH_IDSource_ptr theObject) + throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + TPythonDump pyDump; + TIDSortedElemSet elems; + if ( idSourceToSet( theObject, GetMeshDS(), elems, SMDSAbs_All, /*emptyIfIsMesh=*/true )) + { + if ( elems.empty() ) + { + ConvertFromQuadratic(); + } + else if ( (*elems.begin())->GetType() == SMDSAbs_Node ) + { + THROW_SALOME_CORBA_EXCEPTION("Group of nodes is not allowed", SALOME::BAD_PARAM); + } + else + { + myEditor.ConvertFromQuadratic(elems); } - myPreviewData->nodesXYZ.length( j ); - - // filling the elements connectivities - list::iterator aConnIter = aNodesConnectivity.begin(); - myPreviewData->elementConnectivities.length(aNodesConnectivity.size()); - for( int i = 0; aConnIter != aNodesConnectivity.end(); aConnIter++, i++ ) - myPreviewData->elementConnectivities[i] = *aConnIter; - - return; } + myMesh->GetMeshDS()->Modified(); + myMesh->SetIsModified( true ); - { - // add new nodes into myLastCreatedNodes - const SMESH_SequenceOfElemPtr& aSeq = anEditor.GetLastCreatedNodes(); - myLastCreatedNodes->length(aSeq.Length()); - for(int i=0; iGetID(); - } - { - // add new elements into myLastCreatedElems - const SMESH_SequenceOfElemPtr& aSeq = anEditor.GetLastCreatedElems(); - myLastCreatedElems->length(aSeq.Length()); - for(int i=0; iGetID(); - } -} - -//================================================================================ -/*! - * Return data of mesh edition preview - */ -//================================================================================ - -SMESH::MeshPreviewStruct* SMESH_MeshEditor_i::GetPreviewData() -{ - return myPreviewData._retn(); -} - -//================================================================================ -/*! - * \brief Returns list of it's IDs of created nodes - * \retval SMESH::long_array* - list of node ID - */ -//================================================================================ - -SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedNodes() -{ - return myLastCreatedNodes._retn(); -} - -//================================================================================ -/*! - * \brief Returns list of it's IDs of created elements - * \retval SMESH::long_array* - list of elements' ID - */ -//================================================================================ - -SMESH::long_array* SMESH_MeshEditor_i::GetLastCreatedElems() -{ - return myLastCreatedElems._retn(); -} - -//======================================================================= -//function : ConvertToQuadratic -//purpose : -//======================================================================= - -void SMESH_MeshEditor_i::ConvertToQuadratic(CORBA::Boolean theForce3d) -{ - ::SMESH_MeshEditor anEditor( myMesh ); - anEditor.ConvertToQuadratic(theForce3d); - TPythonDump() << this << ".ConvertToQuadratic( " << theForce3d << " )"; -} - -//======================================================================= -//function : ConvertFromQuadratic -//purpose : -//======================================================================= - -CORBA::Boolean SMESH_MeshEditor_i::ConvertFromQuadratic() -{ - ::SMESH_MeshEditor anEditor( myMesh ); - CORBA::Boolean isDone = anEditor.ConvertFromQuadratic(); - TPythonDump() << this << ".ConvertFromQuadratic()"; - return isDone; + pyDump << this << ".ConvertFromQuadraticObject( "<GetCurrentStudy(); SALOMEDS::SObject_var meshSO = gen->ObjectToSObject( study, mesh ); gen->SetName( meshSO, theMeshName, "Mesh" ); - - SALOMEDS::StudyBuilder_var builder = study->NewBuilder(); - SALOMEDS::GenericAttribute_var anAttr - = builder->FindOrCreateAttribute( meshSO, "AttributePixMap" ); - SALOMEDS::AttributePixMap::_narrow( anAttr )->SetPixMap( "ICON_SMESH_TREE_MESH_IMPORTED" ); + gen->SetPixMap( meshSO, "ICON_SMESH_TREE_MESH_IMPORTED"); return mesh._retn(); } @@ -3887,7 +5168,7 @@ SMESH::SMESH_Mesh_ptr SMESH_MeshEditor_i::makeMesh(const char* theMeshName) //function : DumpGroupsList //purpose : //======================================================================= -void SMESH_MeshEditor_i::DumpGroupsList(TPythonDump & theDumpPython, +void SMESH_MeshEditor_i::DumpGroupsList(TPythonDump & theDumpPython, const SMESH::ListOfGroups * theGroupList) { bool isDumpGroupList = theGroupList && theGroupList->length() > 0; @@ -3896,24 +5177,63 @@ void SMESH_MeshEditor_i::DumpGroupsList(TPythonDump & theDumpPytho } } +//================================================================================ +/*! + \brief Generates the unique group name. + \param thePrefix name prefix + \return unique name +*/ +//================================================================================ +string SMESH_MeshEditor_i::generateGroupName(const string& thePrefix) +{ + SMESH::ListOfGroups_var groups = myMesh_i->GetGroups(); + set groupNames; + + // Get existing group names + for (int i = 0, nbGroups = groups->length(); i < nbGroups; i++ ) { + SMESH::SMESH_GroupBase_var aGroup = groups[i]; + if (CORBA::is_nil(aGroup)) + continue; + + groupNames.insert(aGroup->GetName()); + } + + // Find new name + string name = thePrefix; + int index = 0; + + while (!groupNames.insert(name).second) { + if (index == 0) { + name += "_1"; + } + else { + TCollection_AsciiString nbStr(index+1); + name.resize( name.rfind('_')+1 ); + name += nbStr.ToCString(); + } + ++index; + } + + return name; +} + //================================================================================ /*! \brief Creates a hole in a mesh by doubling the nodes of some particular elements \param theNodes - identifiers of nodes to be doubled - \param theModifiedElems - identifiers of elements to be updated by the new (doubled) - nodes. If list of element identifiers is empty then nodes are doubled but + \param theModifiedElems - identifiers of elements to be updated by the new (doubled) + nodes. If list of element identifiers is empty then nodes are doubled but they not assigned to elements \return TRUE if operation has been completed successfully, FALSE otherwise \sa DoubleNode(), DoubleNodeGroup(), DoubleNodeGroups() */ //================================================================================ -CORBA::Boolean SMESH_MeshEditor_i::DoubleNodes( const SMESH::long_array& theNodes, +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodes( const SMESH::long_array& theNodes, const SMESH::long_array& theModifiedElems ) { initData(); - ::SMESH_MeshEditor aMeshEditor( myMesh ); list< int > aListOfNodes; int i, n; for ( i = 0, n = theNodes.length(); i < n; i++ ) @@ -3923,9 +5243,15 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodes( const SMESH::long_array& theNode for ( i = 0, n = theModifiedElems.length(); i < n; i++ ) aListOfElems.push_back( theModifiedElems[ i ] ); - bool aResult = aMeshEditor.DoubleNodes( aListOfNodes, aListOfElems ); + bool aResult = myEditor.DoubleNodes( aListOfNodes, aListOfElems ); - storeResult( aMeshEditor) ; + myMesh->GetMeshDS()->Modified(); + storeResult( myEditor) ; + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << this << ".DoubleNodes( " << theNodes << ", "<< theModifiedElems << " )"; return aResult; } @@ -3941,13 +5267,20 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodes( const SMESH::long_array& theNode */ //================================================================================ -CORBA::Boolean SMESH_MeshEditor_i::DoubleNode( CORBA::Long theNodeId, +CORBA::Boolean SMESH_MeshEditor_i::DoubleNode( CORBA::Long theNodeId, const SMESH::long_array& theModifiedElems ) { SMESH::long_array_var aNodes = new SMESH::long_array; aNodes->length( 1 ); aNodes[ 0 ] = theNodeId; - return DoubleNodes( aNodes, theModifiedElems ); + + TPythonDump pyDump; // suppress dump by the next line + + CORBA::Boolean done = DoubleNodes( aNodes, theModifiedElems ); + + pyDump << this << ".DoubleNode( " << theNodeId << ", " << theModifiedElems << " )"; + + return done; } //================================================================================ @@ -3961,9 +5294,8 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNode( CORBA::Long theNodeI */ //================================================================================ -CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroup( - SMESH::SMESH_GroupBase_ptr theNodes, - SMESH::SMESH_GroupBase_ptr theModifiedElems ) +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroup(SMESH::SMESH_GroupBase_ptr theNodes, + SMESH::SMESH_GroupBase_ptr theModifiedElems ) { if ( CORBA::is_nil( theNodes ) && theNodes->GetType() != SMESH::NODE ) return false; @@ -3972,13 +5304,68 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroup( SMESH::long_array_var aModifiedElems; if ( !CORBA::is_nil( theModifiedElems ) ) aModifiedElems = theModifiedElems->GetListOfID(); - else + else { aModifiedElems = new SMESH::long_array; aModifiedElems->length( 0 ); } - return DoubleNodes( aNodes, aModifiedElems ); + TPythonDump pyDump; // suppress dump by the next line + + bool done = DoubleNodes( aNodes, aModifiedElems ); + + pyDump << this << ".DoubleNodeGroup( " << theNodes << ", " << theModifiedElems << " )"; + + return done; +} + +/*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeGroup(), but returns a new group with newly created nodes. + * \param theNodes - group of nodes to be doubled. + * \param theModifiedElems - group of elements to be updated. + * \return a new group with newly created nodes + * \sa DoubleNodeGroup() + */ +SMESH::SMESH_Group_ptr +SMESH_MeshEditor_i::DoubleNodeGroupNew( SMESH::SMESH_GroupBase_ptr theNodes, + SMESH::SMESH_GroupBase_ptr theModifiedElems ) +{ + SMESH::SMESH_Group_var aNewGroup; + + if ( CORBA::is_nil( theNodes ) && theNodes->GetType() != SMESH::NODE ) + return aNewGroup._retn(); + + // Duplicate nodes + SMESH::long_array_var aNodes = theNodes->GetListOfID(); + SMESH::long_array_var aModifiedElems; + if ( !CORBA::is_nil( theModifiedElems ) ) + aModifiedElems = theModifiedElems->GetListOfID(); + else { + aModifiedElems = new SMESH::long_array; + aModifiedElems->length( 0 ); + } + + TPythonDump pyDump; // suppress dump by the next line + + bool aResult = DoubleNodes( aNodes, aModifiedElems ); + if ( aResult ) + { + // Create group with newly created nodes + SMESH::long_array_var anIds = GetLastCreatedNodes(); + if (anIds->length() > 0) { + string anUnindexedName (theNodes->GetName()); + string aNewName = generateGroupName(anUnindexedName + "_double"); + aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); + aNewGroup->Add(anIds); + pyDump << aNewGroup << " = "; + } + } + + pyDump << this << ".DoubleNodeGroupNew( " << theNodes << ", " + << theModifiedElems << " )"; + + return aNewGroup._retn(); } //================================================================================ @@ -3992,13 +5379,11 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroup( */ //================================================================================ -CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroups( - const SMESH::ListOfGroups& theNodes, - const SMESH::ListOfGroups& theModifiedElems ) +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroups(const SMESH::ListOfGroups& theNodes, + const SMESH::ListOfGroups& theModifiedElems ) { initData(); - ::SMESH_MeshEditor aMeshEditor( myMesh ); std::list< int > aNodes; int i, n, j, m; @@ -4025,9 +5410,899 @@ CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeGroups( } } - bool aResult = aMeshEditor.DoubleNodes( aNodes, anElems ); + bool aResult = myEditor.DoubleNodes( aNodes, anElems ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + + TPythonDump() << this << ".DoubleNodeGroups( " << theNodes << ", " << theModifiedElems << " )"; + + return aResult; +} + +//================================================================================ +/*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeGroups(), but returns a new group with newly created nodes. + * \param theNodes - group of nodes to be doubled. + * \param theModifiedElems - group of elements to be updated. + * \return a new group with newly created nodes + * \sa DoubleNodeGroups() + */ +//================================================================================ + +SMESH::SMESH_Group_ptr +SMESH_MeshEditor_i::DoubleNodeGroupsNew( const SMESH::ListOfGroups& theNodes, + const SMESH::ListOfGroups& theModifiedElems ) +{ + SMESH::SMESH_Group_var aNewGroup; + + TPythonDump pyDump; // suppress dump by the next line + + bool aResult = DoubleNodeGroups( theNodes, theModifiedElems ); + + if ( aResult ) + { + // Create group with newly created nodes + SMESH::long_array_var anIds = GetLastCreatedNodes(); + if (anIds->length() > 0) { + string anUnindexedName (theNodes[0]->GetName()); + string aNewName = generateGroupName(anUnindexedName + "_double"); + aNewGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); + aNewGroup->Add(anIds); + pyDump << aNewGroup << " = "; + } + } + + pyDump << this << ".DoubleNodeGroupsNew( " << theNodes << ", " + << theModifiedElems << " )"; + + return aNewGroup._retn(); +} + + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - the list of elements (edges or faces) to be replicated + The nodes for duplication could be found from these elements + \param theNodesNot - list of nodes to NOT replicate + \param theAffectedElems - the list of elements (cells and edges) to which the + replicated nodes should be associated to. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodeGroup(), DoubleNodeGroups() +*/ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeElem( const SMESH::long_array& theElems, + const SMESH::long_array& theNodesNot, + const SMESH::long_array& theAffectedElems ) + +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + arrayToSet(theElems, aMeshDS, anElems, SMDSAbs_All); + arrayToSet(theNodesNot, aMeshDS, aNodes, SMDSAbs_Node); + arrayToSet(theAffectedElems, aMeshDS, anAffected, SMDSAbs_All); + + bool aResult = myEditor.DoubleNodes( anElems, aNodes, anAffected ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << this << ".DoubleNodeElem( " << theElems << ", " + << theNodesNot << ", " << theAffectedElems << " )"; + return aResult; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - the list of elements (edges or faces) to be replicated + The nodes for duplication could be found from these elements + \param theNodesNot - list of nodes to NOT replicate + \param theShape - shape to detect affected elements (element which geometric center + located on or inside shape). + The replicated nodes should be associated to affected elements. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodeGroupInRegion(), DoubleNodeGroupsInRegion() +*/ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeElemInRegion ( const SMESH::long_array& theElems, + const SMESH::long_array& theNodesNot, + GEOM::GEOM_Object_ptr theShape ) + +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes; + arrayToSet(theElems, aMeshDS, anElems, SMDSAbs_All); + arrayToSet(theNodesNot, aMeshDS, aNodes, SMDSAbs_Node); + + TopoDS_Shape aShape = SMESH_Gen_i::GetSMESHGen()->GeomObjectToShape( theShape ); + bool aResult = myEditor.DoubleNodesInRegion( anElems, aNodes, aShape ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodeElemInRegion( " << theElems << ", " + << theNodesNot << ", " << theShape << " )"; + return aResult; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - group of of elements (edges or faces) to be replicated + \param theNodesNot - group of nodes not to replicated + \param theAffectedElems - group of elements to which the replicated nodes + should be associated to. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodes(), DoubleNodeGroups() +*/ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeElemGroup(SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems) +{ + if ( CORBA::is_nil( theElems ) && theElems->GetType() == SMESH::NODE ) + return false; + + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + idSourceToSet( theElems, aMeshDS, anElems, SMDSAbs_All ); + idSourceToSet( theNodesNot, aMeshDS, aNodes, SMDSAbs_Node ); + idSourceToSet( theAffectedElems, aMeshDS, anAffected, SMDSAbs_All ); + + bool aResult = myEditor.DoubleNodes( anElems, aNodes, anAffected ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodeElemGroup( " << theElems << ", " + << theNodesNot << ", " << theAffectedElems << " )"; + return aResult; +} + +/*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * Works as DoubleNodeElemGroup(), but returns a new group with newly created elements. + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroup() + */ +SMESH::SMESH_Group_ptr +SMESH_MeshEditor_i::DoubleNodeElemGroupNew(SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems) +{ + TPythonDump pyDump; + SMESH::ListOfGroups_var twoGroups = DoubleNodeElemGroup2New( theElems, + theNodesNot, + theAffectedElems, + true, false ); + SMESH::SMESH_GroupBase_var baseGroup = twoGroups[0].in(); + SMESH::SMESH_Group_var elemGroup = SMESH::SMESH_Group::_narrow( baseGroup ); + + pyDump << elemGroup << " = " << this << ".DoubleNodeElemGroupNew( " + << theElems << ", " + << theNodesNot << ", " + << theAffectedElems << " )"; + + return elemGroup._retn(); +} + +SMESH::ListOfGroups* +SMESH_MeshEditor_i::DoubleNodeElemGroup2New(SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems, + CORBA::Boolean theElemGroupNeeded, + CORBA::Boolean theNodeGroupNeeded) +{ + SMESH::SMESH_Group_var aNewElemGroup, aNewNodeGroup; + SMESH::ListOfGroups_var aTwoGroups = new SMESH::ListOfGroups(); + aTwoGroups->length( 2 ); + + if ( CORBA::is_nil( theElems ) && theElems->GetType() == SMESH::NODE ) + return aTwoGroups._retn(); + + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + idSourceToSet( theElems, aMeshDS, anElems, SMDSAbs_All ); + idSourceToSet( theNodesNot, aMeshDS, aNodes, SMDSAbs_Node ); + idSourceToSet( theAffectedElems, aMeshDS, anAffected, SMDSAbs_All ); + + + bool aResult = myEditor.DoubleNodes( anElems, aNodes, anAffected ); + + storeResult( myEditor) ; + myMesh->GetMeshDS()->Modified(); + + TPythonDump pyDump; + + if ( aResult ) + { + myMesh->SetIsModified( true ); + + // Create group with newly created elements + CORBA::String_var elemGroupName = theElems->GetName(); + string aNewName = generateGroupName( string(elemGroupName.in()) + "_double"); + if ( !myEditor.GetLastCreatedElems().IsEmpty() && theElemGroupNeeded ) + { + SMESH::long_array_var anIds = GetLastCreatedElems(); + SMESH::ElementType aGroupType = myMesh_i->GetElementType(anIds[0], true); + aNewElemGroup = myMesh_i->CreateGroup(aGroupType, aNewName.c_str()); + aNewElemGroup->Add(anIds); + } + if ( !myEditor.GetLastCreatedNodes().IsEmpty() && theNodeGroupNeeded ) + { + SMESH::long_array_var anIds = GetLastCreatedNodes(); + aNewNodeGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); + aNewNodeGroup->Add(anIds); + } + } + + // Update Python script + + pyDump << "[ "; + if ( aNewElemGroup->_is_nil() ) pyDump << "nothing, "; + else pyDump << aNewElemGroup << ", "; + if ( aNewNodeGroup->_is_nil() ) pyDump << "nothing ] = "; + else pyDump << aNewNodeGroup << " ] = "; + + pyDump << this << ".DoubleNodeElemGroup2New( " << theElems << ", " + << theNodesNot << ", " + << theAffectedElems << ", " + << theElemGroupNeeded << ", " + << theNodeGroupNeeded <<" )"; + + aTwoGroups[0] = aNewElemGroup._retn(); + aTwoGroups[1] = aNewNodeGroup._retn(); + return aTwoGroups._retn(); +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + \param theElems - group of of elements (edges or faces) to be replicated + \param theNodesNot - group of nodes not to replicated + \param theShape - shape to detect affected elements (element which geometric center + located on or inside shape). + The replicated nodes should be associated to affected elements. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodesInRegion(), DoubleNodeGroupsInRegion() +*/ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeElemGroupInRegion(SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + GEOM::GEOM_Object_ptr theShape ) + +{ + if ( CORBA::is_nil( theElems ) && theElems->GetType() == SMESH::NODE ) + return false; + + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + idSourceToSet( theElems, aMeshDS, anElems, SMDSAbs_All ); + idSourceToSet( theNodesNot, aMeshDS, aNodes, SMDSAbs_Node ); + + TopoDS_Shape aShape = SMESH_Gen_i::GetSMESHGen()->GeomObjectToShape( theShape ); + bool aResult = myEditor.DoubleNodesInRegion( anElems, aNodes, aShape ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodeElemGroupInRegion( " << theElems << ", " + << theNodesNot << ", " << theShape << " )"; + return aResult; +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + This method provided for convenience works as DoubleNodes() described above. + \param theElems - list of groups of elements (edges or faces) to be replicated + \param theNodesNot - list of groups of nodes not to replicated + \param theAffectedElems - group of elements to which the replicated nodes + should be associated to. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodeGroup(), DoubleNodes(), DoubleNodeElemGroupsNew() +*/ +//================================================================================ + +static void listOfGroupToSet(const SMESH::ListOfGroups& theGrpList, + SMESHDS_Mesh* theMeshDS, + TIDSortedElemSet& theElemSet, + const bool theIsNodeGrp) +{ + for ( int i = 0, n = theGrpList.length(); i < n; i++ ) + { + SMESH::SMESH_GroupBase_var aGrp = theGrpList[ i ]; + if ( !CORBA::is_nil( aGrp ) && (theIsNodeGrp ? aGrp->GetType() == SMESH::NODE + : aGrp->GetType() != SMESH::NODE ) ) + { + SMESH::long_array_var anIDs = aGrp->GetIDs(); + arrayToSet( anIDs, theMeshDS, theElemSet, theIsNodeGrp ? SMDSAbs_Node : SMDSAbs_All ); + } + } +} + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodeElemGroups(const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems) +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + listOfGroupToSet(theElems, aMeshDS, anElems, false ); + listOfGroupToSet(theNodesNot, aMeshDS, aNodes, true ); + listOfGroupToSet(theAffectedElems, aMeshDS, anAffected, false ); + + bool aResult = myEditor.DoubleNodes( anElems, aNodes, anAffected ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodeElemGroups( " << &theElems << ", " + << &theNodesNot << ", " << &theAffectedElems << " )"; + return aResult; +} + +//================================================================================ +/*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * Works as DoubleNodeElemGroups(), but returns a new group with newly created elements. + \param theElems - list of groups of elements (edges or faces) to be replicated + \param theNodesNot - list of groups of nodes not to replicated + \param theAffectedElems - group of elements to which the replicated nodes + should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroups() + */ +//================================================================================ + +SMESH::SMESH_Group_ptr +SMESH_MeshEditor_i::DoubleNodeElemGroupsNew(const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems) +{ + TPythonDump pyDump; + SMESH::ListOfGroups_var twoGroups = DoubleNodeElemGroups2New( theElems, + theNodesNot, + theAffectedElems, + true, false ); + SMESH::SMESH_GroupBase_var baseGroup = twoGroups[0].in(); + SMESH::SMESH_Group_var elemGroup = SMESH::SMESH_Group::_narrow( baseGroup ); + + pyDump << elemGroup << " = " << this << ".DoubleNodeElemGroupsNew( " + << theElems << ", " + << theNodesNot << ", " + << theAffectedElems << " )"; + + return elemGroup._retn(); +} + +SMESH::ListOfGroups* +SMESH_MeshEditor_i::DoubleNodeElemGroups2New(const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems, + CORBA::Boolean theElemGroupNeeded, + CORBA::Boolean theNodeGroupNeeded) +{ + SMESH::SMESH_Group_var aNewElemGroup, aNewNodeGroup; + SMESH::ListOfGroups_var aTwoGroups = new SMESH::ListOfGroups(); + aTwoGroups->length( 2 ); + + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes, anAffected; + listOfGroupToSet(theElems, aMeshDS, anElems, false ); + listOfGroupToSet(theNodesNot, aMeshDS, aNodes, true ); + listOfGroupToSet(theAffectedElems, aMeshDS, anAffected, false ); + + bool aResult = myEditor.DoubleNodes( anElems, aNodes, anAffected ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + TPythonDump pyDump; + if ( aResult ) + { + myMesh->SetIsModified( true ); + + // Create group with newly created elements + CORBA::String_var elemGroupName = theElems[0]->GetName(); + string aNewName = generateGroupName( string(elemGroupName.in()) + "_double"); + if ( !myEditor.GetLastCreatedElems().IsEmpty() && theElemGroupNeeded ) + { + SMESH::long_array_var anIds = GetLastCreatedElems(); + SMESH::ElementType aGroupType = myMesh_i->GetElementType(anIds[0], true); + aNewElemGroup = myMesh_i->CreateGroup(aGroupType, aNewName.c_str()); + aNewElemGroup->Add(anIds); + } + if ( !myEditor.GetLastCreatedNodes().IsEmpty() && theNodeGroupNeeded ) + { + SMESH::long_array_var anIds = GetLastCreatedNodes(); + aNewNodeGroup = myMesh_i->CreateGroup(SMESH::NODE, aNewName.c_str()); + aNewNodeGroup->Add(anIds); + } + } + + // Update Python script + + pyDump << "[ "; + if ( aNewElemGroup->_is_nil() ) pyDump << "nothing, "; + else pyDump << aNewElemGroup << ", "; + if ( aNewNodeGroup->_is_nil() ) pyDump << "nothing ] = "; + else pyDump << aNewNodeGroup << " ] = "; + + pyDump << this << ".DoubleNodeElemGroups2New( " << &theElems << ", " + << &theNodesNot << ", " + << &theAffectedElems << ", " + << theElemGroupNeeded << ", " + << theNodeGroupNeeded << " )"; + + aTwoGroups[0] = aNewElemGroup._retn(); + aTwoGroups[1] = aNewNodeGroup._retn(); + return aTwoGroups._retn(); +} + +//================================================================================ +/*! + \brief Creates a hole in a mesh by doubling the nodes of some particular elements + This method provided for convenience works as DoubleNodes() described above. + \param theElems - list of groups of elements (edges or faces) to be replicated + \param theNodesNot - list of groups of nodes not to replicated + \param theShape - shape to detect affected elements (element which geometric center + located on or inside shape). + The replicated nodes should be associated to affected elements. + \return TRUE if operation has been completed successfully, FALSE otherwise + \sa DoubleNodeGroupInRegion(), DoubleNodesInRegion() +*/ +//================================================================================ + +CORBA::Boolean +SMESH_MeshEditor_i::DoubleNodeElemGroupsInRegion(const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + GEOM::GEOM_Object_ptr theShape ) +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + TIDSortedElemSet anElems, aNodes; + listOfGroupToSet(theElems, aMeshDS, anElems,false ); + listOfGroupToSet(theNodesNot, aMeshDS, aNodes, true ); + + TopoDS_Shape aShape = SMESH_Gen_i::GetSMESHGen()->GeomObjectToShape( theShape ); + bool aResult = myEditor.DoubleNodesInRegion( anElems, aNodes, aShape ); + + storeResult( myEditor) ; + + myMesh->GetMeshDS()->Modified(); + if ( aResult ) + myMesh->SetIsModified( true ); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodeElemGroupsInRegion( " << &theElems << ", " + << &theNodesNot << ", " << theShape << " )"; + return aResult; +} + +//================================================================================ +/*! + \brief Generated skin mesh (containing 2D cells) from 3D mesh + The created 2D mesh elements based on nodes of free faces of boundary volumes + \return TRUE if operation has been completed successfully, FALSE otherwise +*/ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::Make2DMeshFrom3D() +{ + initData(); + + bool aResult = myEditor.Make2DMeshFrom3D(); + storeResult( myEditor) ; + myMesh->GetMeshDS()->Modified(); + TPythonDump() << "isDone = " << this << ".Make2DMeshFrom3D()"; + return aResult; +} + +//================================================================================ +/*! + * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand. + * The list of groups must describe a partition of the mesh volumes. + * The nodes of the internal faces at the boundaries of the groups are doubled. + * In option, the internal faces are replaced by flat elements. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * The flat elements are stored in groups of volumes. + * @param theDomains - list of groups of volumes + * @param createJointElems - if TRUE, create the elements + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::DoubleNodesOnGroupBoundaries( const SMESH::ListOfGroups& theDomains, + CORBA::Boolean createJointElems ) + throw (SALOME::SALOME_Exception) +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + + vector domains; + domains.clear(); + + for ( int i = 0, n = theDomains.length(); i < n; i++ ) + { + SMESH::SMESH_GroupBase_var aGrp = theDomains[ i ]; + if ( !CORBA::is_nil( aGrp ) /*&& ( aGrp->GetType() != SMESH::NODE )*/ ) + { +// if ( aGrp->GetType() != SMESH::VOLUME ) +// THROW_SALOME_CORBA_EXCEPTION("Not a volume group", SALOME::BAD_PARAM); + TIDSortedElemSet domain; + domain.clear(); + domains.push_back(domain); + SMESH::long_array_var anIDs = aGrp->GetIDs(); + arrayToSet( anIDs, aMeshDS, domains[ i ], SMDSAbs_All ); + } + } + + bool aResult = myEditor.DoubleNodesOnGroupBoundaries( domains, createJointElems ); + // TODO publish the groups of flat elements in study + + storeResult( myEditor) ; + myMesh->GetMeshDS()->Modified(); + + // Update Python script + TPythonDump() << "isDone = " << this << ".DoubleNodesOnGroupBoundaries( " << &theDomains + << ", " << createJointElems << " )"; + return aResult; +} + +//================================================================================ +/*! + * \brief Double nodes on some external faces and create flat elements. + * Flat elements are mainly used by some types of mechanic calculations. + * + * Each group of the list must be constituted of faces. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * @param theGroupsOfFaces - list of groups of faces + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ +//================================================================================ + +CORBA::Boolean SMESH_MeshEditor_i::CreateFlatElementsOnFacesGroups( const SMESH::ListOfGroups& theGroupsOfFaces ) +{ + initData(); + + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + + vector faceGroups; + faceGroups.clear(); + + for ( int i = 0, n = theGroupsOfFaces.length(); i < n; i++ ) + { + SMESH::SMESH_GroupBase_var aGrp = theGroupsOfFaces[ i ]; + if ( !CORBA::is_nil( aGrp ) && ( aGrp->GetType() != SMESH::NODE ) ) + { + TIDSortedElemSet faceGroup; + faceGroup.clear(); + faceGroups.push_back(faceGroup); + SMESH::long_array_var anIDs = aGrp->GetIDs(); + arrayToSet( anIDs, aMeshDS, faceGroups[ i ], SMDSAbs_All ); + } + } + + bool aResult = myEditor.CreateFlatElementsOnFacesGroups( faceGroups ); + // TODO publish the groups of flat elements in study - storeResult( aMeshEditor) ; + storeResult( myEditor) ; + myMesh->GetMeshDS()->Modified(); + // Update Python script + TPythonDump() << "isDone = " << this << ".CreateFlatElementsOnFacesGroups( " << &theGroupsOfFaces << " )"; return aResult; } + +// issue 20749 =================================================================== +/*! + * \brief Creates missing boundary elements + * \param elements - elements whose boundary is to be checked + * \param dimension - defines type of boundary elements to create + * \param groupName - a name of group to store created boundary elements in, + * "" means not to create the group + * \param meshName - a name of new mesh to store created boundary elements in, + * "" means not to create the new mesh + * \param toCopyElements - if true, the checked elements will be copied into the new mesh + * \param toCopyExistingBondary - if true, not only new but also pre-existing + * boundary elements will be copied into the new mesh + * \param group - returns the create group, if any + * \retval SMESH::SMESH_Mesh - the mesh where elements were added to + */ +// ================================================================================ + +SMESH::SMESH_Mesh_ptr +SMESH_MeshEditor_i::MakeBoundaryMesh(SMESH::SMESH_IDSource_ptr idSource, + SMESH::Bnd_Dimension dim, + const char* groupName, + const char* meshName, + CORBA::Boolean toCopyElements, + CORBA::Boolean toCopyExistingBondary, + SMESH::SMESH_Group_out group) +{ + initData(); + + if ( dim > SMESH::BND_1DFROM2D ) + THROW_SALOME_CORBA_EXCEPTION("Invalid boundary dimension", SALOME::BAD_PARAM); + + SMESHDS_Mesh* aMeshDS = GetMeshDS(); + + SMESH::SMESH_Mesh_var mesh_var; + SMESH::SMESH_Group_var group_var; + + TPythonDump pyDump; + + TIDSortedElemSet elements; + SMDSAbs_ElementType elemType = (dim == SMESH::BND_1DFROM2D) ? SMDSAbs_Face : SMDSAbs_Volume; + if ( idSourceToSet( idSource, aMeshDS, elements, elemType,/*emptyIfIsMesh=*/true )) + { + // mesh to fill in + mesh_var = + strlen(meshName) ? makeMesh(meshName) : SMESH::SMESH_Mesh::_duplicate(myMesh_i->_this()); + SMESH_Mesh_i* mesh_i = SMESH::DownCast( mesh_var ); + // other mesh + SMESH_Mesh* smesh_mesh = (mesh_i==myMesh_i) ? (SMESH_Mesh*)0 : &mesh_i->GetImpl(); + + // group of new boundary elements + SMESH_Group* smesh_group = 0; + if ( strlen(groupName) ) + { + group_var = mesh_i->CreateGroup( SMESH::ElementType(int(elemType)-1),groupName); + if ( SMESH_GroupBase_i* group_i = SMESH::DownCast( group_var )) + smesh_group = group_i->GetSmeshGroup(); + } + + // do it + myEditor.MakeBoundaryMesh( elements, + ::SMESH_MeshEditor::Bnd_Dimension(dim), + smesh_group, + smesh_mesh, + toCopyElements, + toCopyExistingBondary); + storeResult( myEditor ); + + if ( smesh_mesh ) + smesh_mesh->GetMeshDS()->Modified(); + } + + const char* dimName[] = { "BND_2DFROM3D", "BND_1DFROM3D", "BND_1DFROM2D" }; + + // result of MakeBoundaryMesh() is a tuple (mesh, group) + if ( mesh_var->_is_nil() ) + pyDump << myMesh_i->_this() << ", "; + else + pyDump << mesh_var << ", "; + if ( group_var->_is_nil() ) + pyDump << "_NoneGroup = "; // assignment to None is forbiden + else + pyDump << group_var << " = "; + pyDump << this << ".MakeBoundaryMesh( " + << idSource << ", " + << "SMESH." << dimName[int(dim)] << ", " + << "'" << groupName << "', " + << "'" << meshName<< "', " + << toCopyElements << ", " + << toCopyExistingBondary << ")"; + + group = group_var._retn(); + return mesh_var._retn(); +} + +//================================================================================ +/*! + * \brief Creates missing boundary elements + * \param dimension - defines type of boundary elements to create + * \param groupName - a name of group to store all boundary elements in, + * "" means not to create the group + * \param meshName - a name of a new mesh, which is a copy of the initial + * mesh + created boundary elements; "" means not to create the new mesh + * \param toCopyAll - if true, the whole initial mesh will be copied into + * the new mesh else only boundary elements will be copied into the new mesh + * \param groups - optional groups of elements to make boundary around + * \param mesh - returns the mesh where elements were added to + * \param group - returns the created group, if any + * \retval long - number of added boundary elements + */ +//================================================================================ + +CORBA::Long SMESH_MeshEditor_i::MakeBoundaryElements(SMESH::Bnd_Dimension dim, + const char* groupName, + const char* meshName, + CORBA::Boolean toCopyAll, + const SMESH::ListOfIDSources& groups, + SMESH::SMESH_Mesh_out mesh, + SMESH::SMESH_Group_out group) + throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + + initData(); + + if ( dim > SMESH::BND_1DFROM2D ) + THROW_SALOME_CORBA_EXCEPTION("Invalid boundary dimension", SALOME::BAD_PARAM); + + // separate groups belonging to this and other mesh + SMESH::ListOfIDSources_var groupsOfThisMesh = new SMESH::ListOfIDSources; + SMESH::ListOfIDSources_var groupsOfOtherMesh = new SMESH::ListOfIDSources; + groupsOfThisMesh->length( groups.length() ); + groupsOfOtherMesh->length( groups.length() ); + int nbGroups = 0, nbGroupsOfOtherMesh = 0; + for ( int i = 0; i < groups.length(); ++i ) + { + SMESH::SMESH_Mesh_var m = groups[i]->GetMesh(); + if ( myMesh_i != SMESH::DownCast( m )) + groupsOfOtherMesh[ nbGroupsOfOtherMesh++ ] = groups[i]; + else + groupsOfThisMesh[ nbGroups++ ] = groups[i]; + if ( SMESH::DownCast( groups[i] )) + THROW_SALOME_CORBA_EXCEPTION("expect a group but recieve a mesh", SALOME::BAD_PARAM); + } + groupsOfThisMesh->length( nbGroups ); + groupsOfOtherMesh->length( nbGroupsOfOtherMesh ); + + int nbAdded = 0; + TPythonDump pyDump; + + if ( nbGroupsOfOtherMesh > 0 ) + { + // process groups belonging to another mesh + SMESH::SMESH_Mesh_var otherMesh = groupsOfOtherMesh[0]->GetMesh(); + SMESH::SMESH_MeshEditor_var editor = otherMesh->GetMeshEditor(); + nbAdded += editor->MakeBoundaryElements( dim, groupName, meshName, toCopyAll, + groupsOfOtherMesh, mesh, group ); + } + + SMESH::SMESH_Mesh_var mesh_var; + SMESH::SMESH_Group_var group_var; + + // get mesh to fill + mesh_var = SMESH::SMESH_Mesh::_duplicate( myMesh_i->_this() ); + const bool toCopyMesh = ( strlen( meshName ) > 0 ); + if ( toCopyMesh ) + { + if ( toCopyAll ) + mesh_var = SMESH_Gen_i::GetSMESHGen()->CopyMesh(mesh_var, + meshName, + /*toCopyGroups=*/false, + /*toKeepIDs=*/true); + else + mesh_var = makeMesh(meshName); + } + SMESH_Mesh_i* mesh_i = SMESH::DownCast( mesh_var ); + SMESH_Mesh* tgtMesh = &mesh_i->GetImpl(); + + // source mesh + SMESH_Mesh* srcMesh = ( toCopyMesh && !toCopyAll ) ? myMesh : tgtMesh; + SMESHDS_Mesh* srcMeshDS = srcMesh->GetMeshDS(); + + // group of boundary elements + SMESH_Group* smesh_group = 0; + SMDSAbs_ElementType elemType = (dim == SMESH::BND_2DFROM3D) ? SMDSAbs_Volume : SMDSAbs_Face; + if ( strlen(groupName) ) + { + SMESH::ElementType groupType = SMESH::ElementType( int(elemType)-1 ); + group_var = mesh_i->CreateGroup( groupType, groupName ); + if ( SMESH_GroupBase_i* group_i = SMESH::DownCast( group_var )) + smesh_group = group_i->GetSmeshGroup(); + } + + TIDSortedElemSet elements; + + if ( groups.length() > 0 ) + { + for ( int i = 0; i < nbGroups; ++i ) + { + elements.clear(); + if ( idSourceToSet( groupsOfThisMesh[i], srcMeshDS, elements, elemType,/*emptyIfIsMesh=*/0 )) + { + SMESH::Bnd_Dimension bdim = + ( elemType == SMDSAbs_Volume ) ? SMESH::BND_2DFROM3D : SMESH::BND_1DFROM2D; + nbAdded += myEditor.MakeBoundaryMesh( elements, + ::SMESH_MeshEditor::Bnd_Dimension(bdim), + smesh_group, + tgtMesh, + /*toCopyElements=*/false, + /*toCopyExistingBondary=*/srcMesh != tgtMesh, + /*toAddExistingBondary=*/true, + /*aroundElements=*/true); + storeResult( myEditor ); + } + } + } + else + { + nbAdded += myEditor.MakeBoundaryMesh( elements, + ::SMESH_MeshEditor::Bnd_Dimension(dim), + smesh_group, + tgtMesh, + /*toCopyElements=*/false, + /*toCopyExistingBondary=*/srcMesh != tgtMesh, + /*toAddExistingBondary=*/true); + storeResult( myEditor ); + } + tgtMesh->GetMeshDS()->Modified(); + + const char* dimName[] = { "BND_2DFROM3D", "BND_1DFROM3D", "BND_1DFROM2D" }; + + // result of MakeBoundaryElements() is a tuple (nb, mesh, group) + pyDump << "nbAdded, "; + if ( mesh_var->_is_nil() ) + pyDump << myMesh_i->_this() << ", "; + else + pyDump << mesh_var << ", "; + if ( group_var->_is_nil() ) + pyDump << "_NoneGroup = "; // assignment to None is forbiden + else + pyDump << group_var << " = "; + pyDump << this << ".MakeBoundaryElements( " + << "SMESH." << dimName[int(dim)] << ", " + << "'" << groupName << "', " + << "'" << meshName<< "', " + << toCopyAll << ", " + << groups << ")"; + + mesh = mesh_var._retn(); + group = group_var._retn(); + return nbAdded; +} diff --git a/src/SMESH_I/SMESH_MeshEditor_i.hxx b/src/SMESH_I/SMESH_MeshEditor_i.hxx index b127f2f89..15233798b 100644 --- a/src/SMESH_I/SMESH_MeshEditor_i.hxx +++ b/src/SMESH_I/SMESH_MeshEditor_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_MeshEditor_i.hxx // Author : Nicolas REJNERI @@ -34,6 +35,7 @@ #include "SMESH_Mesh.hxx" #include "SMESH_PythonDump.hxx" +#include "SMESH_MeshEditor.hxx" #include class SMESH_MeshEditor; @@ -41,20 +43,30 @@ class SMESH_Mesh_i; class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor { - public: +public: SMESH_MeshEditor_i(SMESH_Mesh_i * theMesh, bool isPreview); virtual ~ SMESH_MeshEditor_i(); // --- CORBA + + /*! + * \brief Wrap a sequence of ids in a SMESH_IDSource + */ + SMESH::SMESH_IDSource_ptr MakeIDSource(const SMESH::long_array& IDsOfElements, + SMESH::ElementType type); CORBA::Boolean RemoveElements(const SMESH::long_array & IDsOfElements); CORBA::Boolean RemoveNodes(const SMESH::long_array & IDsOfNodes); + CORBA::Long RemoveOrphanNodes(); /*! * Methods for creation new elements. * Returns ID of created element or 0 if element not created */ CORBA::Long AddNode(CORBA::Double x, CORBA::Double y, CORBA::Double z); + CORBA::Long Add0DElement(CORBA::Long IDOfNode); + CORBA::Long AddBall(CORBA::Long IDOfNodem, CORBA::Double diameter) + throw (SALOME::SALOME_Exception); CORBA::Long AddEdge(const SMESH::long_array & IDsOfNodes); CORBA::Long AddFace(const SMESH::long_array & IDsOfNodes); CORBA::Long AddPolygonalFace(const SMESH::long_array & IDsOfNodes); @@ -112,6 +124,19 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Boolean DeleteDiag(CORBA::Long NodeID1, CORBA::Long NodeID2); CORBA::Boolean Reorient(const SMESH::long_array & IDsOfElements); CORBA::Boolean ReorientObject(SMESH::SMESH_IDSource_ptr theObject); + /*! + * \brief Reorient faces contained in \a the2Dgroup. + * \param the2Dgroup - the mesh or its part to reorient + * \param theDirection - desired direction of normal of \a theFace + * \param theFace - ID of face whose orientation is checked. + * It can be < 1 then \a thePoint is used to find a face. + * \param thePoint - is used to find a face if \a theFace < 1. + * \return number of reoriented elements. + */ + CORBA::Long Reorient2D(SMESH::SMESH_IDSource_ptr the2Dgroup, + const SMESH::DirStruct& theDirection, + CORBA::Long theFace, + const SMESH::PointStruct& thePoint) throw (SALOME::SALOME_Exception); // Split/Join faces CORBA::Boolean TriToQuad (const SMESH::long_array & IDsOfElements, @@ -130,6 +155,8 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Boolean Diag13); CORBA::Long BestSplit (CORBA::Long IDOfQuad, SMESH::NumericalFunctor_ptr Criterion); + void SplitVolumesIntoTetra (SMESH::SMESH_IDSource_ptr elems, + CORBA::Short methodFlags) throw (SALOME::SALOME_Exception); CORBA::Boolean Smooth(const SMESH::long_array & IDsOfElements, const SMESH::long_array & IDsOfFixedNodes, @@ -137,10 +164,10 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Double MaxAspectRatio, SMESH::SMESH_MeshEditor::Smooth_Method Method); CORBA::Boolean SmoothObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method); + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method); CORBA::Boolean SmoothParametric(const SMESH::long_array & IDsOfElements, const SMESH::long_array & IDsOfFixedNodes, CORBA::Long MaxNbOfIterations, @@ -158,15 +185,20 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor SMESH::SMESH_MeshEditor::Smooth_Method Method, bool IsParametric); CORBA::Boolean smoothObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::long_array & IDsOfFixedNodes, - CORBA::Long MaxNbOfIterations, - CORBA::Double MaxAspectRatio, - SMESH::SMESH_MeshEditor::Smooth_Method Method, + const SMESH::long_array & IDsOfFixedNodes, + CORBA::Long MaxNbOfIterations, + CORBA::Double MaxAspectRatio, + SMESH::SMESH_MeshEditor::Smooth_Method Method, bool IsParametric); void ConvertToQuadratic(CORBA::Boolean Force3d); CORBA::Boolean ConvertFromQuadratic(); + void ConvertToQuadraticObject(CORBA::Boolean theForce3d, + SMESH::SMESH_IDSource_ptr theObject) + throw (SALOME::SALOME_Exception); + void ConvertFromQuadraticObject(SMESH::SMESH_IDSource_ptr theObject) + throw (SALOME::SALOME_Exception); void RenumberNodes(); void RenumberElements(); @@ -177,27 +209,35 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Long NbOfSteps, CORBA::Double Tolerance); void RotationSweepObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & Axis, - CORBA::Double AngleInRadians, - CORBA::Long NbOfSteps, - CORBA::Double Tolerance); + const SMESH::AxisStruct & Axis, + CORBA::Double AngleInRadians, + CORBA::Long NbOfSteps, + CORBA::Double Tolerance); void RotationSweepObject1D(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & Axis, - CORBA::Double AngleInRadians, - CORBA::Long NbOfSteps, - CORBA::Double Tolerance); + const SMESH::AxisStruct & Axis, + CORBA::Double AngleInRadians, + CORBA::Long NbOfSteps, + CORBA::Double Tolerance); void RotationSweepObject2D(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & Axis, - CORBA::Double AngleInRadians, - CORBA::Long NbOfSteps, - CORBA::Double Tolerance); + const SMESH::AxisStruct & Axis, + CORBA::Double AngleInRadians, + CORBA::Long NbOfSteps, + CORBA::Double Tolerance); void ExtrusionSweep(const SMESH::long_array & IDsOfElements, const SMESH::DirStruct & StepVector, CORBA::Long NbOfSteps); + void ExtrusionSweep0D(const SMESH::long_array & IDsOfElements, + const SMESH::DirStruct & StepVector, + CORBA::Long NbOfSteps); + void ExtrusionSweepObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::DirStruct & StepVector, - CORBA::Long NbOfSteps); + const SMESH::DirStruct & StepVector, + CORBA::Long NbOfSteps); + + void ExtrusionSweepObject0D(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::DirStruct & StepVector, + CORBA::Long NbOfSteps); void ExtrusionSweepObject1D(SMESH::SMESH_IDSource_ptr theObject, const SMESH::DirStruct & StepVector, CORBA::Long NbOfSteps); @@ -205,23 +245,32 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor const SMESH::DirStruct & StepVector, CORBA::Long NbOfSteps); void AdvancedExtrusion(const SMESH::long_array & theIDsOfElements, - const SMESH::DirStruct & theStepVector, - CORBA::Long theNbOfSteps, - CORBA::Long theExtrFlags, - CORBA::Double theSewTolerance); + const SMESH::DirStruct & theStepVector, + CORBA::Long theNbOfSteps, + CORBA::Long theExtrFlags, + CORBA::Double theSewTolerance); SMESH::SMESH_MeshEditor::Extrusion_Error - ExtrusionAlongPath(const SMESH::long_array & IDsOfElements, - SMESH::SMESH_Mesh_ptr PathMesh, - GEOM::GEOM_Object_ptr PathShape, - CORBA::Long NodeStart, - CORBA::Boolean HasAngles, - const SMESH::double_array & Angles, - CORBA::Boolean HasRefPoint, - const SMESH::PointStruct & RefPoint); + ExtrusionAlongPath(const SMESH::long_array & IDsOfElements, + SMESH::SMESH_Mesh_ptr PathMesh, + GEOM::GEOM_Object_ptr PathShape, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array & Angles, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct & RefPoint); SMESH::SMESH_MeshEditor::Extrusion_Error - ExtrusionAlongPathObject(SMESH::SMESH_IDSource_ptr theObject, + ExtrusionAlongPathObject(SMESH::SMESH_IDSource_ptr theObject, + SMESH::SMESH_Mesh_ptr PathMesh, + GEOM::GEOM_Object_ptr PathShape, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array & Angles, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct & RefPoint); + SMESH::SMESH_MeshEditor::Extrusion_Error + ExtrusionAlongPathObject1D(SMESH::SMESH_IDSource_ptr theObject, SMESH::SMESH_Mesh_ptr PathMesh, GEOM::GEOM_Object_ptr PathShape, CORBA::Long NodeStart, @@ -230,23 +279,14 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Boolean HasRefPoint, const SMESH::PointStruct & RefPoint); SMESH::SMESH_MeshEditor::Extrusion_Error - ExtrusionAlongPathObject1D(SMESH::SMESH_IDSource_ptr theObject, - SMESH::SMESH_Mesh_ptr PathMesh, - GEOM::GEOM_Object_ptr PathShape, - CORBA::Long NodeStart, - CORBA::Boolean HasAngles, - const SMESH::double_array & Angles, - CORBA::Boolean HasRefPoint, - const SMESH::PointStruct & RefPoint); - SMESH::SMESH_MeshEditor::Extrusion_Error - ExtrusionAlongPathObject2D(SMESH::SMESH_IDSource_ptr theObject, - SMESH::SMESH_Mesh_ptr PathMesh, - GEOM::GEOM_Object_ptr PathShape, - CORBA::Long NodeStart, - CORBA::Boolean HasAngles, - const SMESH::double_array & Angles, - CORBA::Boolean HasRefPoint, - const SMESH::PointStruct & RefPoint); + ExtrusionAlongPathObject2D(SMESH::SMESH_IDSource_ptr theObject, + SMESH::SMESH_Mesh_ptr PathMesh, + GEOM::GEOM_Object_ptr PathShape, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array & Angles, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct & RefPoint); SMESH::double_array* LinearAnglesVariation(SMESH::SMESH_Mesh_ptr PathMesh, GEOM::GEOM_Object_ptr PathShape, @@ -257,23 +297,23 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor SMESH::SMESH_MeshEditor::MirrorType MirrorType, CORBA::Boolean Copy); void MirrorObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & Axis, - SMESH::SMESH_MeshEditor::MirrorType MirrorType, - CORBA::Boolean Copy); + const SMESH::AxisStruct & Axis, + SMESH::SMESH_MeshEditor::MirrorType MirrorType, + CORBA::Boolean Copy); void Translate(const SMESH::long_array & IDsOfElements, const SMESH::DirStruct & Vector, CORBA::Boolean Copy); void TranslateObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::DirStruct & Vector, - CORBA::Boolean Copy); + const SMESH::DirStruct & Vector, + CORBA::Boolean Copy); void Rotate(const SMESH::long_array & IDsOfElements, const SMESH::AxisStruct & Axis, CORBA::Double Angle, CORBA::Boolean Copy); void RotateObject(SMESH::SMESH_IDSource_ptr theObject, - const SMESH::AxisStruct & Axis, - CORBA::Double Angle, - CORBA::Boolean Copy); + const SMESH::AxisStruct & Axis, + CORBA::Double Angle, + CORBA::Boolean Copy); SMESH::ListOfGroups* RotationSweepMakeGroups(const SMESH::long_array& IDsOfElements, const SMESH::AxisStruct& Axix, @@ -286,18 +326,22 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Long NbOfSteps, CORBA::Double Tolerance); SMESH::ListOfGroups* RotationSweepObject1DMakeGroups(SMESH::SMESH_IDSource_ptr Object, - const SMESH::AxisStruct& Axix, - CORBA::Double AngleInRadians, - CORBA::Long NbOfSteps, - CORBA::Double Tolerance); + const SMESH::AxisStruct& Axix, + CORBA::Double AngleInRadians, + CORBA::Long NbOfSteps, + CORBA::Double Tolerance); SMESH::ListOfGroups* RotationSweepObject2DMakeGroups(SMESH::SMESH_IDSource_ptr Object, - const SMESH::AxisStruct& Axix, - CORBA::Double AngleInRadians, - CORBA::Long NbOfSteps, - CORBA::Double Tolerance); + const SMESH::AxisStruct& Axix, + CORBA::Double AngleInRadians, + CORBA::Long NbOfSteps, + CORBA::Double Tolerance); SMESH::ListOfGroups* ExtrusionSweepMakeGroups(const SMESH::long_array& IDsOfElements, const SMESH::DirStruct& StepVector, CORBA::Long NbOfSteps); + SMESH::ListOfGroups* ExtrusionSweepMakeGroups0D(const SMESH::long_array& IDsOfElements, + const SMESH::DirStruct& StepVector, + CORBA::Long NbOfSteps); + SMESH::ListOfGroups* AdvancedExtrusionMakeGroups(const SMESH::long_array& IDsOfElements, const SMESH::DirStruct& StepVector, CORBA::Long NbOfSteps, @@ -306,6 +350,9 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor SMESH::ListOfGroups* ExtrusionSweepObjectMakeGroups(SMESH::SMESH_IDSource_ptr Object, const SMESH::DirStruct& StepVector, CORBA::Long NbOfSteps); + SMESH::ListOfGroups* ExtrusionSweepObject0DMakeGroups(SMESH::SMESH_IDSource_ptr Object, + const SMESH::DirStruct& StepVector, + CORBA::Long NbOfSteps); SMESH::ListOfGroups* ExtrusionSweepObject1DMakeGroups(SMESH::SMESH_IDSource_ptr Object, const SMESH::DirStruct& StepVector, CORBA::Long NbOfSteps); @@ -331,23 +378,48 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor const SMESH::PointStruct& RefPoint, SMESH::SMESH_MeshEditor::Extrusion_Error& Error); SMESH::ListOfGroups* ExtrusionAlongPathObject1DMakeGroups(SMESH::SMESH_IDSource_ptr Object, - SMESH::SMESH_Mesh_ptr PathMesh, - GEOM::GEOM_Object_ptr PathShape, - CORBA::Long NodeStart, - CORBA::Boolean HasAngles, - const SMESH::double_array& Angles, - CORBA::Boolean HasRefPoint, - const SMESH::PointStruct& RefPoint, - SMESH::SMESH_MeshEditor::Extrusion_Error& Error); + SMESH::SMESH_Mesh_ptr PathMesh, + GEOM::GEOM_Object_ptr PathShape, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + SMESH::SMESH_MeshEditor::Extrusion_Error& Error); SMESH::ListOfGroups* ExtrusionAlongPathObject2DMakeGroups(SMESH::SMESH_IDSource_ptr Object, - SMESH::SMESH_Mesh_ptr PathMesh, - GEOM::GEOM_Object_ptr PathShape, - CORBA::Long NodeStart, - CORBA::Boolean HasAngles, - const SMESH::double_array& Angles, - CORBA::Boolean HasRefPoint, - const SMESH::PointStruct& RefPoint, - SMESH::SMESH_MeshEditor::Extrusion_Error& Error); + SMESH::SMESH_Mesh_ptr PathMesh, + GEOM::GEOM_Object_ptr PathShape, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + SMESH::SMESH_MeshEditor::Extrusion_Error& Error); + + // skl 04.06.2009 + SMESH::ListOfGroups* ExtrusionAlongPathObjX(SMESH::SMESH_IDSource_ptr Object, + SMESH::SMESH_IDSource_ptr Path, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean LinearVariation, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + CORBA::Boolean MakeGroups, + SMESH::ElementType ElemType, + SMESH::SMESH_MeshEditor::Extrusion_Error& Error); + SMESH::ListOfGroups* ExtrusionAlongPathX(const SMESH::long_array& IDsOfElements, + SMESH::SMESH_IDSource_ptr Path, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean LinearVariation, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + CORBA::Boolean MakeGroups, + SMESH::ElementType ElemType, + SMESH::SMESH_MeshEditor::Extrusion_Error& Error); + SMESH::ListOfGroups* MirrorMakeGroups(const SMESH::long_array& IDsOfElements, const SMESH::AxisStruct& Mirror, SMESH::SMESH_MeshEditor::MirrorType MirrorType); @@ -394,11 +466,30 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Boolean CopyGroups, const char* MeshName); + void Scale(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact, + CORBA::Boolean theCopy); + + SMESH::ListOfGroups* ScaleMakeGroups(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact); + + SMESH::SMESH_Mesh_ptr ScaleMakeMesh(SMESH::SMESH_IDSource_ptr Object, + const SMESH::PointStruct& Point, + const SMESH::double_array& theScaleFact, + CORBA::Boolean CopyGroups, + const char* MeshName); + void FindCoincidentNodes (CORBA::Double Tolerance, SMESH::array_of_long_array_out GroupsOfNodes); void FindCoincidentNodesOnPart(SMESH::SMESH_IDSource_ptr Object, CORBA::Double Tolerance, SMESH::array_of_long_array_out GroupsOfNodes); + void FindCoincidentNodesOnPartBut(SMESH::SMESH_IDSource_ptr Object, + CORBA::Double Tolerance, + SMESH::array_of_long_array_out GroupsOfNodes, + const SMESH::ListOfIDSources& ExceptSubMeshOrGroups); void MergeNodes (const SMESH::array_of_long_array& GroupsOfNodes); void FindEqualElements(SMESH::SMESH_IDSource_ptr Object, SMESH::array_of_long_array_out GroupsOfElementsID); @@ -408,8 +499,36 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Double y, CORBA::Double z, CORBA::Long nodeID); + /*! + * \brief Return ID of node closest to a given point + */ + CORBA::Long FindNodeClosestTo(CORBA::Double x, + CORBA::Double y, + CORBA::Double z); + /*! + * Return elements of given type where the given point is IN or ON. + * 'ALL' type means elements of any type excluding nodes + */ + SMESH::long_array* FindElementsByPoint(CORBA::Double x, + CORBA::Double y, + CORBA::Double z, + SMESH::ElementType type); + /*! + * Searching among the given elements, return elements of given type + * where the given point is IN or ON. + * 'ALL' type means elements of any type excluding nodes + */ + SMESH::long_array* FindAmongElementsByPoint(SMESH::SMESH_IDSource_ptr elements, + CORBA::Double x, + CORBA::Double y, + CORBA::Double z, + SMESH::ElementType type); - + /*! + * Return point state in a closed 2D mesh in terms of TopAbs_State enumeration. + * TopAbs_UNKNOWN state means that either mesh is wrong or the analysis fails. + */ + CORBA::Short GetPointState(CORBA::Double x, CORBA::Double y, CORBA::Double z); SMESH::SMESH_MeshEditor::Sew_Error SewFreeBorders(CORBA::Long FirstNodeID1, @@ -448,7 +567,7 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor * element - returns false */ CORBA::Boolean ChangeElemNodes(CORBA::Long ide, const SMESH::long_array& newIDs); - + /*! * Return data of mesh edition preview */ @@ -470,10 +589,10 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor /*! * \brief Return edited mesh ID - * \retval int - mesh ID + * \retval int - mesh ID */ int GetMeshId() const { return myMesh->GetId(); } - + CORBA::Boolean DoubleNodes( const SMESH::long_array& theNodes, const SMESH::long_array& theModifiedElems ); @@ -483,8 +602,194 @@ class SMESH_MeshEditor_i: public POA_SMESH::SMESH_MeshEditor CORBA::Boolean DoubleNodeGroup( SMESH::SMESH_GroupBase_ptr theNodes, SMESH::SMESH_GroupBase_ptr theModifiedElems ); + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements. + * Works as DoubleNodeGroup(), but returns a new group with newly created nodes. + * \param theNodes - group of nodes to be doubled. + * \param theModifiedElems - group of elements to be updated. + * \return a new group with newly created nodes + * \sa DoubleNodeGroup() + */ + SMESH::SMESH_Group_ptr DoubleNodeGroupNew( SMESH::SMESH_GroupBase_ptr theNodes, + SMESH::SMESH_GroupBase_ptr theModifiedElems ); + CORBA::Boolean DoubleNodeGroups( const SMESH::ListOfGroups& theNodes, - const SMESH::ListOfGroups& theModifiedElems); + const SMESH::ListOfGroups& theModifiedElems ); + + SMESH::SMESH_Group_ptr DoubleNodeGroupsNew( const SMESH::ListOfGroups& theNodes, + const SMESH::ListOfGroups& theModifiedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - the list of elements (edges or faces) to be replicated + * The nodes for duplication could be found from these elements + * \param theNodesNot - list of nodes to NOT replicate + * \param theAffectedElems - the list of elements (cells and edges) to which the + * replicated nodes should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroup(), DoubleNodeGroups() + */ + CORBA::Boolean DoubleNodeElem( const SMESH::long_array& theElems, + const SMESH::long_array& theNodesNot, + const SMESH::long_array& theAffectedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - the list of elements (edges or faces) to be replicated + * The nodes for duplication could be found from these elements + * \param theNodesNot - list of nodes to NOT replicate + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroupInRegion(), DoubleNodeGroupsInRegion() + */ + CORBA::Boolean DoubleNodeElemInRegion( const SMESH::long_array& theElems, + const SMESH::long_array& theNodesNot, + GEOM::GEOM_Object_ptr theShape ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodes(), DoubleNodeGroups(), DoubleNodeElemGroupNew() + */ + CORBA::Boolean DoubleNodeElemGroup( SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * Works as DoubleNodeElemGroup(), but returns a new group with newly created elements. + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroup() + */ + SMESH::SMESH_Group_ptr DoubleNodeElemGroupNew( SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems ); + + SMESH::ListOfGroups* DoubleNodeElemGroup2New(SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + SMESH::SMESH_GroupBase_ptr theAffectedElems, + CORBA::Boolean theElemGroupNeeded, + CORBA::Boolean theNodeGroupNeeded); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * \param theElems - group of of elements (edges or faces) to be replicated + * \param theNodesNot - group of nodes not to replicated + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodesInRegion(), DoubleNodeGroupsInRegion() + */ + CORBA::Boolean DoubleNodeElemGroupInRegion( SMESH::SMESH_GroupBase_ptr theElems, + SMESH::SMESH_GroupBase_ptr theNodesNot, + GEOM::GEOM_Object_ptr theShape ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroup(), DoubleNodes(), DoubleNodeElemGroupsNew() + */ + CORBA::Boolean DoubleNodeElemGroups( const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems ); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * Works as DoubleNodeElemGroups(), but returns a new group with newly created elements. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theAffectedElems - group of elements to which the replicated nodes + * should be associated to. + * \return a new group with newly created elements + * \sa DoubleNodeElemGroups() + */ + SMESH::SMESH_Group_ptr DoubleNodeElemGroupsNew( const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems ); + + SMESH::ListOfGroups* DoubleNodeElemGroups2New(const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + const SMESH::ListOfGroups& theAffectedElems, + CORBA::Boolean theElemGroupNeeded, + CORBA::Boolean theNodeGroupNeeded); + + /*! + * \brief Creates a hole in a mesh by doubling the nodes of some particular elements + * This method provided for convenience works as DoubleNodes() described above. + * \param theElems - list of groups of elements (edges or faces) to be replicated + * \param theNodesNot - list of groups of nodes not to replicated + * \param theShape - shape to detect affected elements (element which geometric center + * located on or inside shape). + * The replicated nodes should be associated to affected elements. + * \return TRUE if operation has been completed successfully, FALSE otherwise + * \sa DoubleNodeGroupInRegion(), DoubleNodesInRegion() + */ + CORBA::Boolean DoubleNodeElemGroupsInRegion( const SMESH::ListOfGroups& theElems, + const SMESH::ListOfGroups& theNodesNot, + GEOM::GEOM_Object_ptr theShape ); + /*! + * \brief Double nodes on shared faces between groups of volumes and create flat elements on demand. + * The list of groups must describe a partition of the mesh volumes. + * The nodes of the internal faces at the boundaries of the groups are doubled. + * In option, the internal faces are replaced by flat elements. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * @param theDomains - list of groups of volumes + * @param createJointElems - if TRUE, create the elements + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ + CORBA::Boolean DoubleNodesOnGroupBoundaries( const SMESH::ListOfGroups& theDomains, + CORBA::Boolean createJointElems ) + throw (SALOME::SALOME_Exception); + /*! + * \brief Double nodes on some external faces and create flat elements. + * Flat elements are mainly used by some types of mechanic calculations. + * + * Each group of the list must be constituted of faces. + * Triangles are transformed in prisms, and quadrangles in hexahedrons. + * @param theGroupsOfFaces - list of groups of faces + * @return TRUE if operation has been completed successfully, FALSE otherwise + */ + CORBA::Boolean CreateFlatElementsOnFacesGroups( const SMESH::ListOfGroups& theGroupsOfFaces ); + + /*! + * \brief Generated skin mesh (containing 2D cells) from 3D mesh + * The created 2D mesh elements based on nodes of free faces of boundary volumes + * \return TRUE if operation has been completed successfully, FALSE otherwise + */ + CORBA::Boolean Make2DMeshFrom3D(); + + SMESH::SMESH_Mesh_ptr MakeBoundaryMesh(SMESH::SMESH_IDSource_ptr elements, + SMESH::Bnd_Dimension dimension, + const char* groupName, + const char* meshName, + CORBA::Boolean toCopyElements, + CORBA::Boolean toCopyMissingBondary, + SMESH::SMESH_Group_out group); + + CORBA::Long MakeBoundaryElements(SMESH::Bnd_Dimension dimension, + const char* groupName, + const char* meshName, + CORBA::Boolean toCopyAll, + const SMESH::ListOfIDSources& groups, + SMESH::SMESH_Mesh_out mesh, + SMESH::SMESH_Group_out group) + throw (SALOME::SALOME_Exception); private: //!< private methods @@ -492,13 +797,13 @@ private: //!< private methods /*! * \brief Update myLastCreated* or myPreviewData - * \param anEditor - it contains edition results + * \param anEditor - it contains edition results */ void storeResult(::SMESH_MeshEditor& anEditor); /*! * \brief Clear myLastCreated* or myPreviewData */ - void initData(); + void initData(bool deleteSearchers=true); /*! * \brief Return groups by their IDs @@ -511,11 +816,11 @@ private: //!< private methods CORBA::Long NbOfSteps, CORBA::Double Tolerance, const bool MakeGroups, - const SMDSAbs_ElementType ElementType=SMDSAbs_All); + const SMDSAbs_ElementType ElementType=SMDSAbs_All); SMESH::ListOfGroups* extrusionSweep(const SMESH::long_array & IDsOfElements, const SMESH::DirStruct & StepVector, CORBA::Long NbOfSteps, - const bool MakeGroups, + bool MakeGroups, const SMDSAbs_ElementType ElementType=SMDSAbs_All); SMESH::ListOfGroups* advancedExtrusion(const SMESH::long_array & theIDsOfElements, const SMESH::DirStruct & theStepVector, @@ -533,37 +838,55 @@ private: //!< private methods const SMESH::PointStruct & RefPoint, const bool MakeGroups, SMESH::SMESH_MeshEditor::Extrusion_Error & Error, - const SMDSAbs_ElementType ElementType=SMDSAbs_All); - SMESH::ListOfGroups* mirror(const SMESH::long_array & IDsOfElements, + const SMDSAbs_ElementType ElementType=SMDSAbs_All); + SMESH::ListOfGroups* extrusionAlongPathX(const SMESH::long_array & IDsOfElements, + SMESH::SMESH_IDSource_ptr Path, + CORBA::Long NodeStart, + CORBA::Boolean HasAngles, + const SMESH::double_array& Angles, + CORBA::Boolean LinearVariation, + CORBA::Boolean HasRefPoint, + const SMESH::PointStruct& RefPoint, + bool MakeGroups, + const SMDSAbs_ElementType ElementType, + SMESH::SMESH_MeshEditor::Extrusion_Error & theError); + SMESH::ListOfGroups* mirror(TIDSortedElemSet & IDsOfElements, const SMESH::AxisStruct & Axis, SMESH::SMESH_MeshEditor::MirrorType MirrorType, CORBA::Boolean Copy, - const bool MakeGroups, + bool MakeGroups, ::SMESH_Mesh* TargetMesh=0); - SMESH::ListOfGroups* translate(const SMESH::long_array & IDsOfElements, + SMESH::ListOfGroups* translate(TIDSortedElemSet & IDsOfElements, const SMESH::DirStruct & Vector, CORBA::Boolean Copy, - const bool MakeGroups, + bool MakeGroups, ::SMESH_Mesh* TargetMesh=0); - SMESH::ListOfGroups* rotate(const SMESH::long_array & IDsOfElements, + SMESH::ListOfGroups* rotate(TIDSortedElemSet & IDsOfElements, const SMESH::AxisStruct & Axis, CORBA::Double Angle, CORBA::Boolean Copy, - const bool MakeGroups, + bool MakeGroups, ::SMESH_Mesh* TargetMesh=0); + SMESH::ListOfGroups* scale(SMESH::SMESH_IDSource_ptr theObject, + const SMESH::PointStruct& thePoint, + const SMESH::double_array& theScaleFact, + CORBA::Boolean theCopy, + bool theMakeGroups, + ::SMESH_Mesh* theTargetMesh=0); + SMESH::SMESH_Mesh_ptr makeMesh(const char* theMeshName); - + void DumpGroupsList(SMESH::TPythonDump & theDumpPython, const SMESH::ListOfGroups * theGroupList); -private: //!< fields + string generateGroupName(const string& thePrefix); - SMESH_Mesh_i* myMesh_i; - SMESH_Mesh * myMesh; +private: //!< fields - SMESH::long_array_var myLastCreatedElems; - SMESH::long_array_var myLastCreatedNodes; + SMESH_Mesh_i* myMesh_i; + SMESH_Mesh * myMesh; + ::SMESH_MeshEditor myEditor; SMESH::MeshPreviewStruct_var myPreviewData; bool myPreviewMode; diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index 8371ccf2f..2e4d1772c 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -1,57 +1,60 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Mesh_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// -#include "SMESH_Mesh_i.hxx" -#include "SMESH_Filter_i.hxx" -#include "SMESH_Gen_i.hxx" -#include "SMESH_Group_i.hxx" -#include "SMESH_MEDMesh_i.hxx" -#include "SMESH_MeshEditor_i.hxx" -#include "SMESH_PythonDump.hxx" -#include "SMESH_subMesh_i.hxx" +#include "SMESH_Mesh_i.hxx" #include "DriverMED_R_SMESHDS_Mesh.h" #include "DriverMED_W_SMESHDS_Mesh.h" +#include "SMDS_EdgePosition.hxx" +#include "SMDS_ElemIterator.hxx" +#include "SMDS_FacePosition.hxx" +#include "SMDS_IteratorOnIterators.hxx" +#include "SMDS_SetIterator.hxx" #include "SMDS_VolumeTool.hxx" #include "SMESHDS_Command.hxx" #include "SMESHDS_CommandType.hxx" #include "SMESHDS_GroupOnGeom.hxx" +#include "SMESH_Filter_i.hxx" +#include "SMESH_Gen_i.hxx" #include "SMESH_Group.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_MEDMesh_i.hxx" #include "SMESH_MeshEditor.hxx" +#include "SMESH_MeshEditor_i.hxx" #include "SMESH_MesherHelper.hxx" -#include "SMDS_EdgePosition.hxx" -#include "SMDS_FacePosition.hxx" +#include "SMESH_PreMeshInfo.hxx" +#include "SMESH_PythonDump.hxx" +#include "SMESH_subMesh_i.hxx" -#include "OpUtil.hxx" -#include "SALOME_NamingService.hxx" -#include "Utils_CorbaException.hxx" -#include "Utils_ExceptHandlers.hxx" -#include "Utils_SINGLETON.hxx" -#include "utilities.h" +#include +#include +#include +#include +#include +#include +#include // OCCT Includes #include @@ -63,10 +66,14 @@ #include #include #include +#include #include #include +#include +#include // STL Includes +#include #include #include #include @@ -81,9 +88,11 @@ static int MYDEBUG = 0; using namespace std; using SMESH::TPythonDump; -int SMESH_Mesh_i::myIdGenerator = 0; - +int SMESH_Mesh_i::_idGenerator = 0; +//To disable automatic genericobj management, the following line should be commented. +//Otherwise, it should be uncommented. Refer to KERNEL_SRC/src/SALOMEDSImpl/SALOMEDSImpl_AttributeIOR.cxx +#define WITHGENERICOBJ //============================================================================= /*! @@ -92,15 +101,16 @@ int SMESH_Mesh_i::myIdGenerator = 0; //============================================================================= SMESH_Mesh_i::SMESH_Mesh_i( PortableServer::POA_ptr thePOA, - SMESH_Gen_i* gen_i, - CORBA::Long studyId ) + SMESH_Gen_i* gen_i, + CORBA::Long studyId ) : SALOME::GenericObj_i( thePOA ) { MESSAGE("SMESH_Mesh_i"); _impl = NULL; _gen_i = gen_i; - _id = myIdGenerator++; + _id = _idGenerator++; _studyId = studyId; + _preMeshInfo = NULL; } //============================================================================= @@ -111,19 +121,52 @@ SMESH_Mesh_i::SMESH_Mesh_i( PortableServer::POA_ptr thePOA, SMESH_Mesh_i::~SMESH_Mesh_i() { - INFOS("~SMESH_Mesh_i"); - map::iterator it; - for ( it = _mapGroups.begin(); it != _mapGroups.end(); it++ ) { - SMESH_GroupBase_i* aGroup = dynamic_cast( SMESH_Gen_i::GetServant( it->second ).in() ); - if ( aGroup ) { - // this method is colled from destructor of group (PAL6331) + MESSAGE("~SMESH_Mesh_i"); + +#ifdef WITHGENERICOBJ + // destroy groups + map::iterator itGr; + for (itGr = _mapGroups.begin(); itGr != _mapGroups.end(); itGr++) { + if ( CORBA::is_nil( itGr->second )) + continue; + SMESH_GroupBase_i* aGroup = dynamic_cast(SMESH_Gen_i::GetServant(itGr->second).in()); + if (aGroup) { + // this method is called from destructor of group (PAL6331) //_impl->RemoveGroup( aGroup->GetLocalID() ); - - aGroup->Destroy(); + aGroup->myMeshServant = 0; + aGroup->UnRegister(); } } _mapGroups.clear(); - delete _impl; + + // destroy submeshes + map::iterator itSM; + for ( itSM = _mapSubMeshIor.begin(); itSM != _mapSubMeshIor.end(); itSM++ ) { + if ( CORBA::is_nil( itSM->second )) + continue; + SMESH_subMesh_i* aSubMesh = dynamic_cast(SMESH_Gen_i::GetServant(itSM->second).in()); + if (aSubMesh) { + aSubMesh->UnRegister(); + } + } + _mapSubMeshIor.clear(); + + // destroy hypotheses + map::iterator itH; + for ( itH = _mapHypo.begin(); itH != _mapHypo.end(); itH++ ) { + if ( CORBA::is_nil( itH->second )) + continue; + SMESH_Hypothesis_i* aHypo = dynamic_cast(SMESH_Gen_i::GetServant(itH->second).in()); + if (aHypo) { + aHypo->UnRegister(); + } + } + _mapHypo.clear(); +#endif + + delete _impl; _impl = NULL; + + if ( _preMeshInfo ) delete _preMeshInfo; _preMeshInfo = NULL; } //============================================================================= @@ -146,6 +189,8 @@ void SMESH_Mesh_i::SetShape( GEOM::GEOM_Object_ptr theShapeObject ) catch(SALOME_Exception & S_ex) { THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); } + // to track changes of GEOM groups + addGeomGroupData( theShapeObject, _this() ); } //================================================================================ @@ -189,6 +234,31 @@ GEOM::GEOM_Object_ptr SMESH_Mesh_i::GetShapeToMesh() return aShapeObj._retn(); } +//================================================================================ +/*! + * \brief Return false if the mesh is not yet fully loaded from the study file + */ +//================================================================================ + +CORBA::Boolean SMESH_Mesh_i::IsLoaded() throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + return !_preMeshInfo; +} + +//================================================================================ +/*! + * \brief Load full mesh data from the study file + */ +//================================================================================ + +void SMESH_Mesh_i::Load() throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); +} + //================================================================================ /*! * \brief Remove all nodes and elements @@ -198,8 +268,12 @@ GEOM::GEOM_Object_ptr SMESH_Mesh_i::GetShapeToMesh() void SMESH_Mesh_i::Clear() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->ForgetAllData(); + try { _impl->Clear(); + CheckGeomGroupModif(); // issue 20145 } catch(SALOME_Exception & S_ex) { THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); @@ -217,6 +291,9 @@ void SMESH_Mesh_i::ClearSubMesh(CORBA::Long ShapeID) throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + try { _impl->ClearSubMesh( ShapeID ); } @@ -280,9 +357,9 @@ SMESH_Mesh_i::ImportMEDFile( const char* theFileName, const char* theMeshName ) int major, minor, release; if( !MED::getMEDVersion( theFileName, major, minor, release ) ) major = minor = release = -1; - myFileInfo = new SALOME_MED::MedFileInfo(); - myFileInfo->fileName = theFileName; - myFileInfo->fileSize = 0; + _medFileInfo = new SALOME_MED::MedFileInfo(); + _medFileInfo->fileName = theFileName; + _medFileInfo->fileSize = 0; #ifdef WIN32 struct _stati64 d; if ( ::_stati64( theFileName, &d ) != -1 ) @@ -290,10 +367,38 @@ SMESH_Mesh_i::ImportMEDFile( const char* theFileName, const char* theMeshName ) struct stat64 d; if ( ::stat64( theFileName, &d ) != -1 ) #endif - myFileInfo->fileSize = d.st_size; - myFileInfo->major = major; - myFileInfo->minor = minor; - myFileInfo->release = release; + _medFileInfo->fileSize = d.st_size; + _medFileInfo->major = major; + _medFileInfo->minor = minor; + _medFileInfo->release = release; + + return ConvertDriverMEDReadStatus(status); +} + +//================================================================================ +/*! + * \brief Imports mesh data from the CGNS file + */ +//================================================================================ + +SMESH::DriverMED_ReadStatus SMESH_Mesh_i::ImportCGNSFile( const char* theFileName, + const int theMeshIndex, + std::string& theMeshName ) + throw ( SALOME::SALOME_Exception ) +{ + Unexpect aCatch(SALOME_SalomeException); + int status; + try { + status = _impl->CGNSToMesh( theFileName, theMeshIndex, theMeshName ); + } + catch( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } + catch ( ... ) { + THROW_SALOME_CORBA_EXCEPTION("ImportCGNSFile(): unknown exception", SALOME::BAD_PARAM); + } + + CreateGroupServants(); return ConvertDriverMEDReadStatus(status); } @@ -306,7 +411,7 @@ SMESH_Mesh_i::ImportMEDFile( const char* theFileName, const char* theMeshName ) char* SMESH_Mesh_i::GetVersionString(SMESH::MED_VERSION version, CORBA::Short nbDigits) { - std::string ver = DriverMED_W_SMESHDS_Mesh::GetVersionString(MED::EVersion(version), + string ver = DriverMED_W_SMESHDS_Mesh::GetVersionString(MED::EVersion(version), nbDigits); return CORBA::string_dup( ver.c_str() ); } @@ -346,23 +451,6 @@ int SMESH_Mesh_i::ImportSTLFile( const char* theFileName ) return 1; } -//============================================================================= -/*! - * importMEDFile - * - * Imports mesh data from MED file - */ -//============================================================================= - -// int SMESH_Mesh_i::importMEDFile( const char* theFileName, const char* theMeshName ) -// { -// // Read mesh with name = and all its groups into SMESH_Mesh -// int status = _impl->MEDToMesh( theFileName, theMeshName ); -// CreateGroupServants(); - -// return status; -// } - //============================================================================= /*! * @@ -404,11 +492,14 @@ SMESH::Hypothesis_Status SMESH_Mesh_i::ConvertHypothesisStatus */ //============================================================================= -SMESH::Hypothesis_Status SMESH_Mesh_i::AddHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, +SMESH::Hypothesis_Status SMESH_Mesh_i::AddHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, SMESH::SMESH_Hypothesis_ptr anHyp) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); + SMESH_Hypothesis::Hypothesis_Status status = addHypothesis( aSubShapeObject, anHyp ); if ( !SMESH_Hypothesis::IsStatusFatal(status) ) @@ -442,7 +533,7 @@ SMESH_Hypothesis::Hypothesis_Status if(MYDEBUG) MESSAGE("addHypothesis"); if (CORBA::is_nil(aSubShapeObject) && HasShapeToMesh()) - THROW_SALOME_CORBA_EXCEPTION("bad subShape reference", + THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM); SMESH::SMESH_Hypothesis_var myHyp = SMESH::SMESH_Hypothesis::_narrow(anHyp); @@ -464,6 +555,9 @@ SMESH_Hypothesis::Hypothesis_Status status = _impl->AddHypothesis(myLocSubShape, hypId); if ( !SMESH_Hypothesis::IsStatusFatal(status) ) { _mapHypo[hypId] = SMESH::SMESH_Hypothesis::_duplicate( myHyp ); +#ifdef WITHGENERICOBJ + _mapHypo[hypId]->Register(); +#endif // assure there is a corresponding submesh if ( !_impl->IsMainShape( myLocSubShape )) { int shapeId = _impl->GetMeshDS()->ShapeToIndex( myLocSubShape ); @@ -490,6 +584,9 @@ SMESH::Hypothesis_Status SMESH_Mesh_i::RemoveHypothesis(GEOM::GEOM_Object_ptr aS throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); + SMESH_Hypothesis::Hypothesis_Status status = removeHypothesis( aSubShapeObject, anHyp ); if ( !SMESH_Hypothesis::IsStatusFatal(status) ) @@ -516,41 +613,40 @@ SMESH::Hypothesis_Status SMESH_Mesh_i::RemoveHypothesis(GEOM::GEOM_Object_ptr aS */ //============================================================================= -SMESH_Hypothesis::Hypothesis_Status SMESH_Mesh_i::removeHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, - SMESH::SMESH_Hypothesis_ptr anHyp) +SMESH_Hypothesis::Hypothesis_Status +SMESH_Mesh_i::removeHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, + SMESH::SMESH_Hypothesis_ptr anHyp) { - if(MYDEBUG) MESSAGE("removeHypothesis()"); - // **** proposer liste de subShape (selection multiple) + if(MYDEBUG) MESSAGE("removeHypothesis()"); + // **** proposer liste de sub-shape (selection multiple) + + if (CORBA::is_nil(aSubShapeObject) && HasShapeToMesh()) + THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM); - if (CORBA::is_nil(aSubShapeObject) && HasShapeToMesh()) - THROW_SALOME_CORBA_EXCEPTION("bad subShape reference", - SALOME::BAD_PARAM); + SMESH::SMESH_Hypothesis_var myHyp = SMESH::SMESH_Hypothesis::_narrow(anHyp); + if (CORBA::is_nil(myHyp)) + THROW_SALOME_CORBA_EXCEPTION("bad hypothesis reference", SALOME::BAD_PARAM); - SMESH::SMESH_Hypothesis_var myHyp = SMESH::SMESH_Hypothesis::_narrow(anHyp); - if (CORBA::is_nil(myHyp)) - THROW_SALOME_CORBA_EXCEPTION("bad hypothesis reference", - SALOME::BAD_PARAM); + SMESH_Hypothesis::Hypothesis_Status status = SMESH_Hypothesis::HYP_OK; + try + { + TopoDS_Shape myLocSubShape; + //use PseudoShape in case if mesh has no shape + if(HasShapeToMesh()) + myLocSubShape = _gen_i->GeomObjectToShape( aSubShapeObject); + else + myLocSubShape = _impl->GetShapeToMesh(); - SMESH_Hypothesis::Hypothesis_Status status = SMESH_Hypothesis::HYP_OK; - try - { - TopoDS_Shape myLocSubShape; - //use PseudoShape in case if mesh has no shape - if(HasShapeToMesh()) - myLocSubShape = _gen_i->GeomObjectToShape( aSubShapeObject); - else - myLocSubShape = _impl->GetShapeToMesh(); - - int hypId = myHyp->GetId(); - status = _impl->RemoveHypothesis(myLocSubShape, hypId); - if ( !SMESH_Hypothesis::IsStatusFatal(status) ) - _mapHypo.erase( hypId ); - } - catch(SALOME_Exception & S_ex) - { - THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); - } - return status; + int hypId = myHyp->GetId(); + status = _impl->RemoveHypothesis(myLocSubShape, hypId); +// if ( !SMESH_Hypothesis::IsStatusFatal(status) ) EAP: hyp can be used on many sub-shapes +// _mapHypo.erase( hypId ); + } + catch(SALOME_Exception & S_ex) + { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } + return status; } //============================================================================= @@ -560,19 +656,20 @@ SMESH_Hypothesis::Hypothesis_Status SMESH_Mesh_i::removeHypothesis(GEOM::GEOM_Ob //============================================================================= SMESH::ListOfHypothesis * - SMESH_Mesh_i::GetHypothesisList(GEOM::GEOM_Object_ptr aSubShapeObject) + SMESH_Mesh_i::GetHypothesisList(GEOM::GEOM_Object_ptr aSubShapeObject) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); if (MYDEBUG) MESSAGE("GetHypothesisList"); - if (CORBA::is_nil(aSubShapeObject)) - THROW_SALOME_CORBA_EXCEPTION("bad subShape reference", - SALOME::BAD_PARAM); + if (_impl->HasShapeToMesh() && CORBA::is_nil(aSubShapeObject)) + THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", SALOME::BAD_PARAM); SMESH::ListOfHypothesis_var aList = new SMESH::ListOfHypothesis(); try { TopoDS_Shape myLocSubShape = _gen_i->GeomObjectToShape(aSubShapeObject); + if ( myLocSubShape.IsNull() && !_impl->HasShapeToMesh() ) + myLocSubShape = _impl->GetShapeToMesh(); const list& aLocalList = _impl->GetHypothesisList( myLocSubShape ); int i = 0, n = aLocalList.size(); aList->length( n ); @@ -580,7 +677,7 @@ throw(SALOME::SALOME_Exception) for ( list::const_iterator anIt = aLocalList.begin(); i < n && anIt != aLocalList.end(); anIt++ ) { SMESHDS_Hypothesis* aHyp = (SMESHDS_Hypothesis*)(*anIt); if ( _mapHypo.find( aHyp->GetID() ) != _mapHypo.end() ) - aList[i++] = SMESH::SMESH_Hypothesis::_narrow( _mapHypo[aHyp->GetID()] ); + aList[i++] = SMESH::SMESH_Hypothesis::_narrow( _mapHypo[aHyp->GetID()] ); } aList->length( i ); @@ -592,20 +689,56 @@ throw(SALOME::SALOME_Exception) return aList._retn(); } +SMESH::submesh_array* SMESH_Mesh_i::GetSubMeshes() throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if (MYDEBUG) MESSAGE("GetSubMeshes"); + + SMESH::submesh_array_var aList = new SMESH::submesh_array(); + + // Python Dump + TPythonDump aPythonDump; + if ( !_mapSubMeshIor.empty() ) + aPythonDump << "[ "; + + try { + aList->length( _mapSubMeshIor.size() ); + int i = 0; + map::iterator it = _mapSubMeshIor.begin(); + for ( ; it != _mapSubMeshIor.end(); it++ ) { + if ( CORBA::is_nil( it->second )) continue; + aList[i++] = SMESH::SMESH_subMesh::_duplicate( it->second ); + // Python Dump + if (i > 1) aPythonDump << ", "; + aPythonDump << it->second; + } + aList->length( i ); + } + catch(SALOME_Exception & S_ex) { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } + + // Update Python script + if ( !_mapSubMeshIor.empty() ) + aPythonDump << " ] = " << _this() << ".GetSubMeshes()"; + + return aList._retn(); +} + //============================================================================= /*! * */ //============================================================================= SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::GetSubMesh(GEOM::GEOM_Object_ptr aSubShapeObject, - const char* theName ) + const char* theName ) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); MESSAGE("SMESH_Mesh_i::GetSubMesh"); if (CORBA::is_nil(aSubShapeObject)) - THROW_SALOME_CORBA_EXCEPTION("bad subShape reference", - SALOME::BAD_PARAM); + THROW_SALOME_CORBA_EXCEPTION("bad Sub-shape reference", + SALOME::BAD_PARAM); SMESH::SMESH_subMesh_var subMesh; SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow(_this()); @@ -615,12 +748,18 @@ SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::GetSubMesh(GEOM::GEOM_Object_ptr aSubShap //Get or Create the SMESH_subMesh object implementation int subMeshId = _impl->GetMeshDS()->ShapeToIndex( myLocSubShape ); + + if ( !subMeshId && ! _impl->GetMeshDS()->IsGroupOfSubShapes( myLocSubShape )) + { + TopoDS_Iterator it( myLocSubShape ); + if ( it.More() ) + THROW_SALOME_CORBA_EXCEPTION("not sub-shape of the main shape", SALOME::BAD_PARAM); + } subMesh = getSubMesh( subMeshId ); // create a new subMesh object servant if there is none for the shape if ( subMesh->_is_nil() ) subMesh = createSubMesh( aSubShapeObject ); - if ( _gen_i->CanPublishInStudy( subMesh )) { SALOMEDS::SObject_var aSO = _gen_i->PublishSubMesh(_gen_i->GetCurrentStudy(), aMesh, @@ -660,7 +799,10 @@ void SMESH_Mesh_i::RemoveSubMesh( SMESH::SMESH_subMesh_ptr theSubMesh ) long aTag = SMESH_Gen_i::GetRefOnShapeTag(); SALOMEDS::SObject_var anObj, aRef; if ( anSO->FindSubObject( aTag, anObj ) && anObj->ReferencedObject( aRef ) ) - aSubShapeObject = GEOM::GEOM_Object::_narrow( aRef->GetObject() ); + aSubShapeObject = GEOM::GEOM_Object::_narrow( aRef->GetObject() ); + +// if ( aSubShapeObject->_is_nil() ) // not published shape (IPAL13617) +// aSubShapeObject = theSubMesh->GetSubShape(); aStudy->NewBuilder()->RemoveObjectWithChildren( anSO ); @@ -669,26 +811,9 @@ void SMESH_Mesh_i::RemoveSubMesh( SMESH::SMESH_subMesh_ptr theSubMesh ) } } - removeSubMesh( theSubMesh, aSubShapeObject.in() ); -} - -//============================================================================= -/*! - * ElementTypeString - */ -//============================================================================= -#define CASE2STRING(enum) case SMESH::enum: return "SMESH."#enum; -inline TCollection_AsciiString ElementTypeString (SMESH::ElementType theElemType) -{ - switch (theElemType) { - CASE2STRING( ALL ); - CASE2STRING( NODE ); - CASE2STRING( EDGE ); - CASE2STRING( FACE ); - CASE2STRING( VOLUME ); - default:; - } - return ""; + if ( removeSubMesh( theSubMesh, aSubShapeObject.in() )) + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); } //============================================================================= @@ -698,10 +823,13 @@ inline TCollection_AsciiString ElementTypeString (SMESH::ElementType theElemType //============================================================================= SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateGroup( SMESH::ElementType theElemType, - const char* theName ) + const char* theName ) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::SMESH_Group_var aNewGroup = SMESH::SMESH_Group::_narrow( createGroup( theElemType, theName )); @@ -712,7 +840,7 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateGroup( SMESH::ElementType theElemType if ( !aSO->_is_nil()) { // Update Python script TPythonDump() << aSO << " = " << _this() << ".CreateGroup( " - << ElementTypeString(theElemType) << ", '" << theName << "' )"; + << theElemType << ", '" << theName << "' )"; } } return aNewGroup._retn(); @@ -724,18 +852,24 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateGroup( SMESH::ElementType theElemType * */ //============================================================================= -SMESH::SMESH_GroupOnGeom_ptr SMESH_Mesh_i::CreateGroupFromGEOM (SMESH::ElementType theElemType, - const char* theName, - GEOM::GEOM_Object_ptr theGeomObj) +SMESH::SMESH_GroupOnGeom_ptr +SMESH_Mesh_i::CreateGroupFromGEOM (SMESH::ElementType theElemType, + const char* theName, + GEOM::GEOM_Object_ptr theGeomObj) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::SMESH_GroupOnGeom_var aNewGroup; TopoDS_Shape aShape = _gen_i->GeomObjectToShape( theGeomObj ); - if ( !aShape.IsNull() ) { + if ( !aShape.IsNull() ) + { aNewGroup = SMESH::SMESH_GroupOnGeom::_narrow ( createGroup( theElemType, theName, aShape )); + if ( _gen_i->CanPublishInStudy( aNewGroup ) ) { SALOMEDS::SObject_var aSO = _gen_i->PublishGroup(_gen_i->GetCurrentStudy(), _this(), @@ -743,8 +877,7 @@ SMESH::SMESH_GroupOnGeom_ptr SMESH_Mesh_i::CreateGroupFromGEOM (SMESH::ElementTy if ( !aSO->_is_nil()) { // Update Python script TPythonDump() << aSO << " = " << _this() << ".CreateGroupFromGEOM(" - << ElementTypeString(theElemType) << ", '" << theName << "', " - << theGeomObj << " )"; + << theElemType << ", '" << theName << "', " << theGeomObj << " )"; } } } @@ -752,6 +885,55 @@ SMESH::SMESH_GroupOnGeom_ptr SMESH_Mesh_i::CreateGroupFromGEOM (SMESH::ElementTy return aNewGroup._retn(); } +//================================================================================ +/*! + * \brief Creates a group whose contents is defined by filter + * \param theElemType - group type + * \param theName - group name + * \param theFilter - the filter + * \retval SMESH::SMESH_GroupOnFilter_ptr - group defined by filter + */ +//================================================================================ + +SMESH::SMESH_GroupOnFilter_ptr +SMESH_Mesh_i::CreateGroupFromFilter(SMESH::ElementType theElemType, + const char* theName, + SMESH::Filter_ptr theFilter ) + throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + if ( CORBA::is_nil( theFilter )) + THROW_SALOME_CORBA_EXCEPTION("NULL filter", SALOME::BAD_PARAM); + + SMESH_PredicatePtr predicate = SMESH_GroupOnFilter_i::GetPredicate( theFilter ); + if ( !predicate ) + THROW_SALOME_CORBA_EXCEPTION("Invalid filter", SALOME::BAD_PARAM); + + SMESH::SMESH_GroupOnFilter_var aNewGroup = SMESH::SMESH_GroupOnFilter::_narrow + ( createGroup( theElemType, theName, TopoDS_Shape(), predicate )); + + TPythonDump pd; + if ( !aNewGroup->_is_nil() ) + aNewGroup->SetFilter( theFilter ); + + if ( _gen_i->CanPublishInStudy( aNewGroup ) ) + { + SALOMEDS::SObject_var aSO = + _gen_i->PublishGroup(_gen_i->GetCurrentStudy(), _this(), aNewGroup, + GEOM::GEOM_Object::_nil(), theName); + if ( !aSO->_is_nil()) { + // Update Python script + pd << aSO << " = " << _this() << ".CreateGroupFromFilter(" + << theElemType << ", '" << theName << "', " << theFilter << " )"; + } + } + + return aNewGroup._retn(); +} + //============================================================================= /*! * @@ -787,13 +969,17 @@ void SMESH_Mesh_i::RemoveGroup( SMESH::SMESH_GroupBase_ptr theGroup ) } //============================================================================= -/*! RemoveGroupWithContents +/*! * Remove group with its contents */ //============================================================================= + void SMESH_Mesh_i::RemoveGroupWithContents( SMESH::SMESH_GroupBase_ptr theGroup ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( theGroup->_is_nil() ) return; @@ -805,8 +991,7 @@ void SMESH_Mesh_i::RemoveGroupWithContents( SMESH::SMESH_GroupBase_ptr theGroup SMESH::long_array_var anIds = aGroup->GetListOfID(); SMESH::SMESH_MeshEditor_var aMeshEditor = SMESH_Mesh_i::GetMeshEditor(); - // Update Python script - TPythonDump() << _this() << ".RemoveGroupWithContents( " << theGroup << " )"; + TPythonDump pyDump; // Supress dump from RemoveNodes/Elements() and RemoveGroup() // Remove contents if ( aGroup->GetType() == SMESH::NODE ) @@ -817,12 +1002,10 @@ void SMESH_Mesh_i::RemoveGroupWithContents( SMESH::SMESH_GroupBase_ptr theGroup // Remove group RemoveGroup( theGroup ); - // Clear python lines, created by RemoveNodes/Elements() and RemoveGroup() - _gen_i->RemoveLastFromPythonScript(_gen_i->GetCurrentStudy()->StudyId()); - _gen_i->RemoveLastFromPythonScript(_gen_i->GetCurrentStudy()->StudyId()); + // Update Python script + pyDump << _this() << ".RemoveGroupWithContents( " << theGroup << " )"; } - //================================================================================ /*! * \brief Get the list of groups existing in the mesh @@ -865,6 +1048,7 @@ SMESH::ListOfGroups * SMESH_Mesh_i::GetGroups() throw(SALOME::SALOME_Exception) return aList._retn(); } + //============================================================================= /*! * Get number of groups existing in the mesh @@ -888,6 +1072,9 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::UnionGroups( SMESH::SMESH_GroupBase_ptr the const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + try { if ( theGroup1->_is_nil() || theGroup2->_is_nil() || @@ -951,12 +1138,15 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::UnionListOfGroups(const SMESH::ListOfGroups const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( !theName ) return SMESH::SMESH_Group::_nil(); try { - NCollection_Map< int > anIds; + vector< int > anIds; SMESH::ElementType aType = SMESH::ALL; for ( int g = 0, n = theGroups.length(); g < n; g++ ) { @@ -979,7 +1169,7 @@ throw (SALOME::SALOME_Exception) for ( int i = 0, n = aCurrIds->length(); i < n; i++ ) { int aCurrId = aCurrIds[ i ]; - anIds.Add( aCurrId ); + anIds.push_back( aCurrId ); } } @@ -990,12 +1180,12 @@ throw (SALOME::SALOME_Exception) // Create array of identifiers SMESH::long_array_var aResIds = new SMESH::long_array; - aResIds->length( anIds.Extent() ); + aResIds->length( anIds.size() ); - NCollection_Map< int >::Iterator anIter( anIds ); - for ( int i = 0; anIter.More(); anIter.Next(), i++ ) + //NCollection_Map< int >::Iterator anIter( anIds ); + for ( int i = 0; iAdd( aResIds ); @@ -1028,6 +1218,9 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::IntersectGroups( SMESH::SMESH_GroupBase_ptr const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( theGroup1->_is_nil() || theGroup2->_is_nil() || theGroup1->GetType() != theGroup2->GetType() ) return SMESH::SMESH_Group::_nil(); @@ -1080,10 +1273,14 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::IntersectGroups( SMESH::SMESH_GroupBase_ptr \return pointer on the group */ //============================================================================= -SMESH::SMESH_Group_ptr SMESH_Mesh_i::IntersectListOfGroups( - const SMESH::ListOfGroups& theGroups, const char* theName ) -throw (SALOME::SALOME_Exception) +SMESH::SMESH_Group_ptr +SMESH_Mesh_i::IntersectListOfGroups(const SMESH::ListOfGroups& theGroups, + const char* theName ) + throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( !theName ) return SMESH::SMESH_Group::_nil(); @@ -1121,14 +1318,14 @@ throw (SALOME::SALOME_Exception) // create map of ids int nbGrp = theGroups.length(); - NCollection_Map< int > anIds; + vector< int > anIds; NCollection_DataMap< int, int >::Iterator anIter( anIdToCount ); for ( ; anIter.More(); anIter.Next() ) { int aCurrId = anIter.Key(); int aCurrNb = anIter.Value(); if ( aCurrNb == nbGrp ) - anIds.Add( aCurrId ); + anIds.push_back( aCurrId ); } // Create group @@ -1138,12 +1335,12 @@ throw (SALOME::SALOME_Exception) // Create array of identifiers SMESH::long_array_var aResIds = new SMESH::long_array; - aResIds->length( anIds.Extent() ); + aResIds->length( anIds.size() ); - NCollection_Map< int >::Iterator aListIter( anIds ); - for ( int i = 0; aListIter.More(); aListIter.Next(), i++ ) + //NCollection_Map< int >::Iterator aListIter( anIds ); + for ( int i = 0; iAdd( aResIds ); @@ -1173,9 +1370,12 @@ throw (SALOME::SALOME_Exception) //============================================================================= SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutGroups( SMESH::SMESH_GroupBase_ptr theGroup1, SMESH::SMESH_GroupBase_ptr theGroup2, - const char* theName ) + const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( theGroup1->_is_nil() || theGroup2->_is_nil() || theGroup1->GetType() != theGroup2->GetType() ) return SMESH::SMESH_Group::_nil(); @@ -1229,18 +1429,21 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutGroups( SMESH::SMESH_GroupBase_ptr theGr \return pointer on the group */ //============================================================================= -SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutListOfGroups( - const SMESH::ListOfGroups& theMainGroups, - const SMESH::ListOfGroups& theToolGroups, - const char* theName ) +SMESH::SMESH_Group_ptr +SMESH_Mesh_i::CutListOfGroups(const SMESH::ListOfGroups& theMainGroups, + const SMESH::ListOfGroups& theToolGroups, + const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + if ( !theName ) return SMESH::SMESH_Group::_nil(); try { - NCollection_Map< int > aToolIds; + set< int > aToolIds; SMESH::ElementType aType = SMESH::ALL; int g, n; // iterate through tool groups @@ -1265,11 +1468,11 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutListOfGroups( for ( int i = 0, n = aCurrIds->length(); i < n; i++ ) { int aCurrId = aCurrIds[ i ]; - aToolIds.Add( aCurrId ); + aToolIds.insert( aCurrId ); } } - NCollection_Map< int > anIds; // result + vector< int > anIds; // result // Iterate through main group for ( g = 0, n = theMainGroups.length(); g < n; g++ ) @@ -1293,8 +1496,8 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutListOfGroups( for ( int i = 0, n = aCurrIds->length(); i < n; i++ ) { int aCurrId = aCurrIds[ i ]; - if ( !aToolIds.Contains( aCurrId ) ) - anIds.Add( aCurrId ); + if ( !aToolIds.count( aCurrId ) ) + anIds.push_back( aCurrId ); } } @@ -1305,12 +1508,11 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutListOfGroups( // Create array of identifiers SMESH::long_array_var aResIds = new SMESH::long_array; - aResIds->length( anIds.Extent() ); + aResIds->length( anIds.size() ); - NCollection_Map< int >::Iterator anIter( anIds ); - for ( int i = 0; anIter.More(); anIter.Next(), i++ ) + for (int i=0; iAdd( aResIds ); @@ -1345,12 +1547,15 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CutListOfGroups( \return pointer on new group */ //============================================================================= -SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( - const SMESH::ListOfGroups& theGroups, - SMESH::ElementType theElemType, - const char* theName ) +SMESH::SMESH_Group_ptr +SMESH_Mesh_i::CreateDimGroup(const SMESH::ListOfGroups& theGroups, + SMESH::ElementType theElemType, + const char* theName ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS(); if ( !theName || !aMeshDS ) @@ -1362,7 +1567,7 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( { // Create map of nodes from all groups - NCollection_Map< int > aNodeMap; + set< int > aNodeMap; for ( int g = 0, n = theGroups.length(); g < n; g++ ) { @@ -1381,7 +1586,7 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( int aCurrId = aCurrIds[ i ]; const SMDS_MeshNode* aNode = aMeshDS->FindNode( aCurrId ); if ( aNode ) - aNodeMap.Add( aNode->GetID() ); + aNodeMap.insert( aNode->GetID() ); } } else @@ -1399,7 +1604,7 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( const SMDS_MeshNode* aNode = dynamic_cast( aNodeIter->next() ); if ( aNode ) - aNodeMap.Add( aNode->GetID() ); + aNodeMap.insert( aNode->GetID() ); } } } @@ -1407,22 +1612,25 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( // Get result identifiers - NCollection_Map< int > aResultIds; + vector< int > aResultIds; if ( theElemType == SMESH::NODE ) { - NCollection_Map< int >::Iterator aNodeIter( aNodeMap ); - for ( ; aNodeIter.More(); aNodeIter.Next() ) - aResultIds.Add( aNodeIter.Value() ); + //NCollection_Map< int >::Iterator aNodeIter( aNodeMap ); + set::iterator iter = aNodeMap.begin(); + for ( ; iter != aNodeMap.end(); iter++ ) + aResultIds.push_back( *iter); } else { // Create list of elements of given dimension constructed on the nodes - NCollection_Map< int > anElemList; - NCollection_Map< int >::Iterator aNodeIter( aNodeMap ); - for ( ; aNodeIter.More(); aNodeIter.Next() ) + vector< int > anElemList; + //NCollection_Map< int >::Iterator aNodeIter( aNodeMap ); + //for ( ; aNodeIter.More(); aNodeIter.Next() ) + set::iterator iter = aNodeMap.begin(); + for ( ; iter != aNodeMap.end(); iter++ ) { const SMDS_MeshElement* aNode = - dynamic_cast( aMeshDS->FindNode( aNodeIter.Value() ) ); + dynamic_cast( aMeshDS->FindNode( *iter ) ); if ( !aNode ) continue; @@ -1432,15 +1640,14 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( const SMDS_MeshElement* anElem = dynamic_cast( anElemIter->next() ); if ( anElem && anElem->GetType() == anElemType ) - anElemList.Add( anElem->GetID() ); + anElemList.push_back( anElem->GetID() ); } } // check whether all nodes of elements are present in nodes map - NCollection_Map< int >::Iterator anIter( anElemList ); - for ( ; anIter.More(); anIter.Next() ) + for (int i=0; i< anElemList.size(); i++) { - const SMDS_MeshElement* anElem = aMeshDS->FindElement( anIter.Value() ); + const SMDS_MeshElement* anElem = aMeshDS->FindElement( anElemList[i] ); if ( !anElem ) continue; @@ -1450,14 +1657,14 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( { const SMDS_MeshNode* aNode = dynamic_cast( aNodeIter->next() ); - if ( !aNode || !aNodeMap.Contains( aNode->GetID() ) ) + if ( !aNode || !aNodeMap.count( aNode->GetID() ) ) { isOk = false; break; } } if ( isOk ) - aResultIds.Add( anElem->GetID() ); + aResultIds.push_back( anElem->GetID() ); } } @@ -1469,11 +1676,10 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( // Create array of identifiers SMESH::long_array_var aResIds = new SMESH::long_array; - aResIds->length( aResultIds.Extent() ); + aResIds->length( aResultIds.size() ); - NCollection_Map< int >::Iterator aResIter( aResultIds ); - for ( int i = 0; aResIter.More(); aResIter.Next(), i++ ) - aResIds[ i ] = aResIter.Value(); + for (int i=0; i< aResultIds.size(); i++) + aResIds[ i ] = aResultIds[i]; aResGrp->Add( aResIds ); // Remove strings corresponding to group creation @@ -1496,61 +1702,128 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::CreateDimGroup( //================================================================================ /*! - * \brief Return group items of a group present in a study + * \brief Remember GEOM group data */ //================================================================================ -static GEOM::GEOM_Object_ptr getGroupItemsFromStudy(CORBA::Object_ptr theMesh, - SMESH_Gen_i* theGen, - list & theItems) +void SMESH_Mesh_i::addGeomGroupData(GEOM::GEOM_Object_ptr theGeomObj, + CORBA::Object_ptr theSmeshObj) { - GEOM::GEOM_Object_var groupObj; - SALOMEDS::Study_var study = theGen->GetCurrentStudy(); - GEOM::GEOM_Gen_var geomGen = theGen->GetGeomEngine(); - if ( study->_is_nil() || geomGen->_is_nil() ) - return groupObj._retn(); - + if ( CORBA::is_nil( theGeomObj ) || theGeomObj->GetType() != GEOM_GROUP ) + return; + // group SO + SALOMEDS::Study_var study = _gen_i->GetCurrentStudy(); + SALOMEDS::SObject_var groupSO = _gen_i->ObjectToSObject( study, theGeomObj ); + if ( groupSO->_is_nil() ) + return; + // group indices + GEOM::GEOM_Gen_var geomGen = _gen_i->GetGeomEngine(); GEOM::GEOM_IGroupOperations_var groupOp = - geomGen->GetIGroupOperations( theGen->GetCurrentStudyID() ); - GEOM::GEOM_IShapesOperations_var shapeOp = - geomGen->GetIShapesOperations( theGen->GetCurrentStudyID() ); - - SALOMEDS::SObject_var meshOS = theGen->ObjectToSObject(study, theMesh); - if ( meshOS->_is_nil() || groupOp->_is_nil() || shapeOp->_is_nil() ) - return groupObj._retn(); - SALOMEDS::SObject_var fatherSO = meshOS->GetFather(); - if ( fatherSO->_is_nil() || fatherSO->Tag() != theGen->GetSubMeshOnCompoundTag() ) - return groupObj._retn(); // keep only submeshes on groups - - SALOMEDS::ChildIterator_var anIter = study->NewChildIterator(meshOS); - if ( anIter->_is_nil() ) return groupObj._retn(); - for ( ; anIter->More(); anIter->Next()) - { - SALOMEDS::SObject_var aSObject = anIter->Value(); - SALOMEDS::SObject_var aRefSO; - if ( !aSObject->_is_nil() && aSObject->ReferencedObject(aRefSO) ) - { - groupObj = GEOM::GEOM_Object::_narrow(aRefSO->GetObject()); - if ( groupObj->_is_nil() ) break; - GEOM::ListOfLong_var ids = groupOp->GetObjects( groupObj ); - GEOM::GEOM_Object_var mainShape = groupObj->GetMainShape(); - for ( int i = 0; i < ids->length(); ++i ) { - GEOM::GEOM_Object_var subShape = shapeOp->GetSubShape( mainShape, ids[i] ); - TopoDS_Shape S = theGen->GeomObjectToShape( subShape ); - if ( !S.IsNull() ) - theItems.push_back( S ); - } - break; + geomGen->GetIGroupOperations( _gen_i->GetCurrentStudyID() ); + GEOM::ListOfLong_var ids = groupOp->GetObjects( theGeomObj ); + + // store data + _geomGroupData.push_back( TGeomGroupData() ); + TGeomGroupData & groupData = _geomGroupData.back(); + // entry + CORBA::String_var entry = groupSO->GetID(); + groupData._groupEntry = entry.in(); + // indices + for ( int i = 0; i < ids->length(); ++i ) + groupData._indices.insert( ids[i] ); + // SMESH object + groupData._smeshObject = theSmeshObj; +} + +//================================================================================ +/*! + * Remove GEOM group data relating to removed smesh object + */ +//================================================================================ + +void SMESH_Mesh_i::removeGeomGroupData(CORBA::Object_ptr theSmeshObj) +{ + list::iterator + data = _geomGroupData.begin(), dataEnd = _geomGroupData.end(); + for ( ; data != dataEnd; ++data ) { + if ( theSmeshObj->_is_equivalent( data->_smeshObject )) { + _geomGroupData.erase( data ); + return; } } - return groupObj._retn(); } +//================================================================================ +/*! + * \brief Return new group contents if it has been changed and update group data + */ +//================================================================================ + +TopoDS_Shape SMESH_Mesh_i::newGroupShape( TGeomGroupData & groupData) +{ + TopoDS_Shape newShape; + + // get geom group + SALOMEDS::Study_var study = _gen_i->GetCurrentStudy(); + if ( study->_is_nil() ) return newShape; // means "not changed" + SALOMEDS::SObject_var groupSO = study->FindObjectID( groupData._groupEntry.c_str() ); + if ( !groupSO->_is_nil() ) + { + CORBA::Object_var groupObj = _gen_i->SObjectToObject( groupSO ); + if ( CORBA::is_nil( groupObj )) return newShape; + GEOM::GEOM_Object_var geomGroup = GEOM::GEOM_Object::_narrow( groupObj ); + + // get indices of group items + set curIndices; + GEOM::GEOM_Gen_var geomGen = _gen_i->GetGeomEngine(); + GEOM::GEOM_IGroupOperations_var groupOp = + geomGen->GetIGroupOperations( _gen_i->GetCurrentStudyID() ); + GEOM::ListOfLong_var ids = groupOp->GetObjects( geomGroup ); + for ( int i = 0; i < ids->length(); ++i ) + curIndices.insert( ids[i] ); + + if ( groupData._indices == curIndices ) + return newShape; // group not changed + + // update data + groupData._indices = curIndices; + + GEOM_Client* geomClient = _gen_i->GetShapeReader(); + if ( !geomClient ) return newShape; + TCollection_AsciiString groupIOR = geomGen->GetStringFromIOR( geomGroup ); + geomClient->RemoveShapeFromBuffer( groupIOR ); + newShape = _gen_i->GeomObjectToShape( geomGroup ); + } + + if ( newShape.IsNull() ) { + // geom group becomes empty - return empty compound + TopoDS_Compound compound; + BRep_Builder().MakeCompound(compound); + newShape = compound; + } + return newShape; +} + +namespace +{ + //============================================================================= + /*! + * \brief Storage of shape and index used in CheckGeomGroupModif() + */ + //============================================================================= + struct TIndexedShape + { + int _index; + TopoDS_Shape _shape; + TIndexedShape( int i, const TopoDS_Shape& s ):_index(i), _shape(s) {} + }; +} //============================================================================= /*! - * \brief Update hypotheses assigned to geom groups if the latter change + * \brief Update objects depending on changed geom groups * - * NPAL16168: "geometrical group edition from a submesh don't modifiy mesh computation" + * NPAL16168: geometrical group edition from a submesh don't modifiy mesh computation + * issue 0020210: Update of a smesh group after modification of the associated geom group */ //============================================================================= @@ -1561,83 +1834,240 @@ void SMESH_Mesh_i::CheckGeomGroupModif() SALOMEDS::Study_var study = _gen_i->GetCurrentStudy(); if ( study->_is_nil() ) return; - // check if items of groups changed - map::iterator i_sm = _mapSubMesh.begin(); - for ( ; i_sm != _mapSubMesh.end(); ++i_sm ) - { - const TopoDS_Shape & oldGroupShape = i_sm->second->GetSubShape(); - SMESHDS_SubMesh * oldDS = i_sm->second->GetSubMeshDS(); - if ( !oldDS /*|| !oldDS->IsComplexSubmesh()*/ ) - continue; - int oldID = i_sm->first; - map::iterator i_smIor = _mapSubMeshIor.find( oldID ); - if ( i_smIor == _mapSubMeshIor.end() ) - continue; - list< TopoDS_Shape> newItems; - GEOM::GEOM_Object_var groupObj = getGroupItemsFromStudy ( i_smIor->second, _gen_i, newItems ); - if ( groupObj->_is_nil() ) - continue; + CORBA::Long nbEntities = NbNodes() + NbElements(); - int nbOldItems = oldDS->IsComplexSubmesh() ? oldDS->NbSubMeshes() : 1; - int nbNewItems = newItems.size(); - bool groupChanged = ( nbOldItems != nbNewItems); - if ( !groupChanged ) { - if ( !oldDS->IsComplexSubmesh() ) { // old group has one item - groupChanged = ( oldGroupShape != newItems.front() ); - } - else { - list::iterator item = newItems.begin(); - for ( ; item != newItems.end() && !groupChanged; ++item ) - { - SMESHDS_SubMesh * itemDS = _impl->GetMeshDS()->MeshElements( *item ); - groupChanged = ( !itemDS || !oldDS->ContainsSubMesh( itemDS )); - } - } + // Check if group contents changed + + typedef map< string, TopoDS_Shape > TEntry2Geom; + TEntry2Geom newGroupContents; + + list::iterator + data = _geomGroupData.begin(), dataEnd = _geomGroupData.end(); + for ( ; data != dataEnd; ++data ) + { + pair< TEntry2Geom::iterator, bool > it_new = + newGroupContents.insert( make_pair( data->_groupEntry, TopoDS_Shape() )); + bool processedGroup = !it_new.second; + TopoDS_Shape& newShape = it_new.first->second; + if ( !processedGroup ) + newShape = newGroupShape( *data ); + if ( newShape.IsNull() ) + continue; // no changes + + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); + + if ( processedGroup ) { // update group indices + list::iterator data2 = data; + for ( --data2; data2->_groupEntry != data->_groupEntry; --data2) {} + data->_indices = data2->_indices; } - // update hypotheses and submeshes if necessary - if ( groupChanged ) + + // Update SMESH objects according to new GEOM group contents + + SMESH::SMESH_subMesh_var submesh = SMESH::SMESH_subMesh::_narrow( data->_smeshObject ); + if ( !submesh->_is_nil() ) // -------------- Sub mesh --------------------- { - // get a new group shape - GEOM_Client* geomClient = _gen_i->GetShapeReader(); - if ( !geomClient ) continue; - TCollection_AsciiString groupIOR = _gen_i->GetGeomEngine()->GetStringFromIOR( groupObj ); - geomClient->RemoveShapeFromBuffer( groupIOR ); - TopoDS_Shape newGroupShape = _gen_i->GeomObjectToShape( groupObj ); + int oldID = submesh->GetId(); + if ( _mapSubMeshIor.find( oldID ) == _mapSubMeshIor.end() ) + continue; + TopoDS_Shape oldShape = _mapSubMesh[oldID]->GetSubShape(); + // update hypotheses - list hyps = _impl->GetHypothesisList(oldGroupShape); + list hyps = _impl->GetHypothesisList(oldShape); list ::iterator hypIt; for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt ) { - _impl->RemoveHypothesis( oldGroupShape, (*hypIt)->GetID()); - _impl->AddHypothesis ( newGroupShape, (*hypIt)->GetID()); + _impl->RemoveHypothesis( oldShape, (*hypIt)->GetID()); + _impl->AddHypothesis ( newShape, (*hypIt)->GetID()); } // care of submeshes - SMESH_subMesh* newSubmesh = _impl->GetSubMesh( newGroupShape ); + SMESH_subMesh* newSubmesh = _impl->GetSubMesh( newShape ); int newID = newSubmesh->GetId(); if ( newID != oldID ) { _mapSubMesh [ newID ] = newSubmesh; _mapSubMesh_i [ newID ] = _mapSubMesh_i [ oldID ]; _mapSubMeshIor[ newID ] = _mapSubMeshIor[ oldID ]; - _mapSubMesh.erase (oldID); - _mapSubMesh_i.erase (oldID); + _mapSubMesh. erase(oldID); + _mapSubMesh_i. erase(oldID); _mapSubMeshIor.erase(oldID); _mapSubMesh_i [ newID ]->changeLocalId( newID ); } + continue; } - } -} -//============================================================================= -/*! - * \brief Create standalone group instead if group on geometry - * - */ -//============================================================================= + SMESH::SMESH_GroupOnGeom_var smeshGroup = + SMESH::SMESH_GroupOnGeom::_narrow( data->_smeshObject ); + if ( !smeshGroup->_is_nil() ) // ------------ GROUP ----------------------- + { + SMESH_GroupOnGeom_i* group_i = SMESH::DownCast( smeshGroup ); + if ( group_i ) { + ::SMESH_Group* group = _impl->GetGroup( group_i->GetLocalID() ); + SMESHDS_GroupOnGeom* ds = static_cast( group->GetGroupDS() ); + ds->SetShape( newShape ); + } + continue; + } -SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupOnGeom_ptr theGroup ) -{ - SMESH::SMESH_Group_var aGroup; - if ( theGroup->_is_nil() ) + SMESH::SMESH_Mesh_var mesh = SMESH::SMESH_Mesh::_narrow( data->_smeshObject ); + if ( !mesh->_is_nil() ) // -------------- MESH ---------------------------- + { + // Remove groups and submeshes basing on removed sub-shapes + + TopTools_MapOfShape newShapeMap; + TopoDS_Iterator shapeIt( newShape ); + for ( ; shapeIt.More(); shapeIt.Next() ) + newShapeMap.Add( shapeIt.Value() ); + + SMESHDS_Mesh* meshDS = _impl->GetMeshDS(); + for ( shapeIt.Initialize( meshDS->ShapeToMesh() ); shapeIt.More(); shapeIt.Next() ) + { + if ( newShapeMap.Contains( shapeIt.Value() )) + continue; + TopTools_IndexedMapOfShape oldShapeMap; + TopExp::MapShapes( shapeIt.Value(), oldShapeMap ); + for ( int i = 1; i <= oldShapeMap.Extent(); ++i ) + { + const TopoDS_Shape& oldShape = oldShapeMap(i); + int oldInd = meshDS->ShapeToIndex( oldShape ); + // -- submeshes -- + map::iterator i_smIor = _mapSubMeshIor.find( oldInd ); + if ( i_smIor != _mapSubMeshIor.end() ) { + RemoveSubMesh( i_smIor->second ); // one submesh per shape index + } + // --- groups --- + map::iterator i_grp = _mapGroups.begin(); + for ( ; i_grp != _mapGroups.end(); ++i_grp ) + { + // check if a group bases on oldInd shape + SMESHDS_GroupOnGeom* grpOnGeom = 0; + if ( ::SMESH_Group* g = _impl->GetGroup( i_grp->first )) + grpOnGeom = dynamic_cast( g->GetGroupDS() ); + if ( grpOnGeom && oldShape.IsSame( grpOnGeom->GetShape() )) + { // remove + RemoveGroup( i_grp->second ); // several groups can base on same shape + i_grp = _mapGroups.begin(); // _mapGroups changed - restart iteration + } + } + } + } + // Reassign hypotheses and update groups after setting the new shape to mesh + + // collect anassigned hypotheses + typedef list< pair< TIndexedShape, list > > TShapeHypList; + list ::const_iterator hypIt; + TShapeHypList assignedHyps; + for ( int i = 1; i <= meshDS->MaxShapeIndex(); ++i ) + { + const TopoDS_Shape& oldShape = meshDS->IndexToShape(i); + list hyps = meshDS->GetHypothesis( oldShape );// copy + if ( !hyps.empty() ) { + assignedHyps.push_back( make_pair( TIndexedShape(i,oldShape), hyps )); + for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt ) + _impl->RemoveHypothesis( oldShape, (*hypIt)->GetID()); + } + } + // collect shapes supporting groups + typedef list < pair< TIndexedShape, SMDSAbs_ElementType > > TShapeTypeList; + TShapeTypeList groupData; + const set& groups = meshDS->GetGroups(); + set::const_iterator grIt = groups.begin(); + for ( ; grIt != groups.end(); ++grIt ) + { + if ( SMESHDS_GroupOnGeom* gog = dynamic_cast( *grIt )) + groupData.push_back + ( make_pair( TIndexedShape( gog->GetID(),gog->GetShape()), gog->GetType())); + } + // set new shape to mesh -> DS of submeshes and geom groups is deleted + _impl->ShapeToMesh( newShape ); + + // reassign hypotheses + TShapeHypList::iterator indS_hyps = assignedHyps.begin(); + for ( ; indS_hyps != assignedHyps.end(); ++indS_hyps ) + { + TIndexedShape& geom = indS_hyps->first; + list& hyps = indS_hyps->second; + int oldID = geom._index; + int newID = meshDS->ShapeToIndex( geom._shape ); + if ( !newID ) + continue; + if ( oldID == 1 ) { // main shape + newID = 1; + geom._shape = newShape; + } + for ( hypIt = hyps.begin(); hypIt != hyps.end(); ++hypIt ) + _impl->AddHypothesis( geom._shape, (*hypIt)->GetID()); + // care of submeshes + SMESH_subMesh* newSubmesh = _impl->GetSubMesh( geom._shape ); + if ( newID != oldID ) { + _mapSubMesh [ newID ] = newSubmesh; + _mapSubMesh_i [ newID ] = _mapSubMesh_i [ oldID ]; + _mapSubMeshIor[ newID ] = _mapSubMeshIor[ oldID ]; + _mapSubMesh. erase(oldID); + _mapSubMesh_i. erase(oldID); + _mapSubMeshIor.erase(oldID); + _mapSubMesh_i [ newID ]->changeLocalId( newID ); + } + } + // recreate groups + TShapeTypeList::iterator geomType = groupData.begin(); + for ( ; geomType != groupData.end(); ++geomType ) + { + const TIndexedShape& geom = geomType->first; + int oldID = geom._index; + if ( _mapGroups.find( oldID ) == _mapGroups.end() ) + continue; + // get group name + SALOMEDS::SObject_var groupSO = _gen_i->ObjectToSObject( study,_mapGroups[oldID] ); + CORBA::String_var name = groupSO->GetName(); + // update + SMESH_GroupBase_i* group_i = SMESH::DownCast(_mapGroups[oldID] ); + int newID; + if ( group_i && _impl->AddGroup( geomType->second, name.in(), newID, geom._shape )) + group_i->changeLocalId( newID ); + } + + break; // everything has been updated + + } // update mesh + } // loop on group data + + // Update icons + + CORBA::Long newNbEntities = NbNodes() + NbElements(); + list< SALOMEDS::SObject_var > soToUpdateIcons; + if ( newNbEntities != nbEntities ) + { + // Add all SObjects with icons to soToUpdateIcons + soToUpdateIcons.push_back( _gen_i->ObjectToSObject( study, _this() )); // mesh + + for (map::iterator i_sm = _mapSubMeshIor.begin(); + i_sm != _mapSubMeshIor.end(); ++i_sm ) // submeshes + soToUpdateIcons.push_back( _gen_i->ObjectToSObject( study, i_sm->second )); + + for ( map::iterator i_gr = _mapGroups.begin(); + i_gr != _mapGroups.end(); ++i_gr ) // groups + soToUpdateIcons.push_back( _gen_i->ObjectToSObject( study, i_gr->second )); + } + + list< SALOMEDS::SObject_var >::iterator so = soToUpdateIcons.begin(); + for ( ; so != soToUpdateIcons.end(); ++so ) + _gen_i->SetPixMap( *so, "ICON_SMESH_TREE_MESH_WARN" ); +} + +//============================================================================= +/*! + * \brief Create standalone group from a group on geometry or filter + */ +//============================================================================= + +SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupBase_ptr theGroup ) +{ + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + SMESH::SMESH_Group_var aGroup; + if ( theGroup->_is_nil() ) return aGroup._retn(); Unexpect aCatch(SALOME_SalomeException); @@ -1647,13 +2077,14 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupOnGe if ( !aGroupToRem ) return aGroup._retn(); + const bool isOnFilter = ( SMESH::DownCast< SMESH_GroupOnFilter_i* > ( theGroup )); + int anId = aGroupToRem->GetLocalID(); if ( !_impl->ConvertToStandalone( anId ) ) return aGroup._retn(); + removeGeomGroupData( theGroup ); - SMESH_GroupBase_i* aGroupImpl; - aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId ); - + SMESH_GroupBase_i* aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId ); // remove old instance of group from own map _mapGroups.erase( anId ); @@ -1666,15 +2097,29 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupOnGe aGroupSO = _gen_i->ObjectToSObject( aStudy, theGroup ); if ( !aGroupSO->_is_nil() ) { - // remove reference to geometry - SALOMEDS::ChildIterator_var chItr = aStudy->NewChildIterator(aGroupSO); - for ( ; chItr->More(); chItr->Next() ) - // Remove group's child SObject - builder->RemoveObject( chItr->Value() ); + // remove reference to geometry + SALOMEDS::ChildIterator_var chItr = aStudy->NewChildIterator(aGroupSO); + for ( ; chItr->More(); chItr->Next() ) + // Remove group's child SObject + builder->RemoveObject( chItr->Value() ); // Update Python script TPythonDump() << aGroupSO << " = " << _this() << ".ConvertToStandalone( " << aGroupSO << " )"; + + // change icon of Group on Filter + if ( isOnFilter ) + { + SMESH::array_of_ElementType_var elemTypes = aGroupImpl->GetTypes(); + const int isEmpty = ( elemTypes->length() == 0 ); + if ( !isEmpty ) + { + SALOMEDS::GenericAttribute_var anAttr = + builder->FindOrCreateAttribute( aGroupSO, "AttributePixMap" ); + SALOMEDS::AttributePixMap_var pm = SALOMEDS::AttributePixMap::_narrow( anAttr ); + pm->SetPixMap( "ICON_SMESH_TREE_GROUP" ); + } + } } } @@ -1688,8 +2133,8 @@ SMESH::SMESH_Group_ptr SMESH_Mesh_i::ConvertToStandalone( SMESH::SMESH_GroupOnGe _mapGroups[anId] = SMESH::SMESH_GroupBase::_duplicate( aGroup ); // register CORBA object for persistence - //int nextId = _gen_i->RegisterObject( aGroup ); - //if(MYDEBUG) MESSAGE( "Add group to map with id = "<< nextId); + /*int nextId =*/ _gen_i->RegisterObject( aGroup ); + builder->SetIOR( aGroupSO, _gen_i->GetORB()->object_to_string( aGroup ) ); return aGroup._retn(); @@ -1720,6 +2165,9 @@ SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::createSubMesh( GEOM::GEOM_Object_ptr theS int nextId = _gen_i->RegisterObject( subMesh ); if(MYDEBUG) MESSAGE( "Add submesh to map with id = "<< nextId); + // to track changes of GEOM groups + addGeomGroupData( theSubShapeObject, subMesh ); + return subMesh._retn(); } @@ -1744,29 +2192,50 @@ SMESH::SMESH_subMesh_ptr SMESH_Mesh_i::getSubMesh(int shapeID) */ //============================================================================= -void SMESH_Mesh_i::removeSubMesh (SMESH::SMESH_subMesh_ptr theSubMesh, +bool SMESH_Mesh_i::removeSubMesh (SMESH::SMESH_subMesh_ptr theSubMesh, GEOM::GEOM_Object_ptr theSubShapeObject ) { - MESSAGE("SMESH_Mesh_i::removeSubMesh()"); - if ( theSubMesh->_is_nil() || theSubShapeObject->_is_nil() ) - return; + bool isHypChanged = false; + if ( theSubMesh->_is_nil() /*|| theSubShapeObject->_is_nil()*/ ) + return isHypChanged; - try { - SMESH::ListOfHypothesis_var aHypList = GetHypothesisList( theSubShapeObject ); - for ( int i = 0, n = aHypList->length(); i < n; i++ ) { - removeHypothesis( theSubShapeObject, aHypList[i] ); + if ( theSubShapeObject->_is_nil() ) // not published shape (IPAL13617) + { + CORBA::Long shapeId = theSubMesh->GetId(); + if ( _mapSubMesh.find( shapeId ) != _mapSubMesh.end()) + { + TopoDS_Shape S = _mapSubMesh[ shapeId ]->GetSubShape(); + if ( !S.IsNull() ) + { + list hyps = _impl->GetHypothesisList( S ); + isHypChanged = !hyps.empty(); + list::const_iterator hyp = hyps.begin(); + for ( ; hyp != hyps.end(); ++hyp ) + _impl->RemoveHypothesis(S, (*hyp)->GetID()); + } } } - catch( const SALOME::SALOME_Exception& ) { - INFOS("SMESH_Mesh_i::removeSubMesh(): exception caught!"); + else + { + try { + SMESH::ListOfHypothesis_var aHypList = GetHypothesisList( theSubShapeObject ); + isHypChanged = ( aHypList->length() > 0 ); + for ( int i = 0, n = aHypList->length(); i < n; i++ ) { + removeHypothesis( theSubShapeObject, aHypList[i] ); + } + } + catch( const SALOME::SALOME_Exception& ) { + INFOS("SMESH_Mesh_i::removeSubMesh(): exception caught!"); + } + removeGeomGroupData( theSubShapeObject ); } - int subMeshId = theSubMesh->GetId(); _mapSubMesh.erase(subMeshId); _mapSubMesh_i.erase(subMeshId); _mapSubMeshIor.erase(subMeshId); - if(MYDEBUG) MESSAGE("SMESH_Mesh_i::removeSubMesh() completed"); + + return isHypChanged; } //============================================================================= @@ -1775,16 +2244,32 @@ void SMESH_Mesh_i::removeSubMesh (SMESH::SMESH_subMesh_ptr theSubMesh, */ //============================================================================= -SMESH::SMESH_GroupBase_ptr SMESH_Mesh_i::createGroup (SMESH::ElementType theElemType, - const char* theName, - const TopoDS_Shape& theShape ) +SMESH::SMESH_GroupBase_ptr SMESH_Mesh_i::createGroup (SMESH::ElementType theElemType, + const char* theName, + const TopoDS_Shape& theShape, + const SMESH_PredicatePtr& thePredicate ) { + std::string newName; + if ( !theName || strlen( theName ) == 0 ) + { + std::set< std::string > presentNames; + std::map::const_iterator i_gr = _mapGroups.begin(); + for ( ; i_gr != _mapGroups.end(); ++i_gr ) + presentNames.insert( i_gr->second->GetName() ); + do { + newName = "noname_Group_" + SMESH_Comment( presentNames.size() + 1 ); + } while ( !presentNames.insert( newName ).second ); + theName = newName.c_str(); + } int anId; SMESH::SMESH_GroupBase_var aGroup; - if ( _impl->AddGroup( (SMDSAbs_ElementType)theElemType, theName, anId, theShape )) { + if ( _impl->AddGroup( (SMDSAbs_ElementType)theElemType, theName, anId, theShape, thePredicate )) + { SMESH_GroupBase_i* aGroupImpl; if ( !theShape.IsNull() ) aGroupImpl = new SMESH_GroupOnGeom_i( SMESH_Gen_i::GetPOA(), this, anId ); + else if ( thePredicate ) + aGroupImpl = new SMESH_GroupOnFilter_i( SMESH_Gen_i::GetPOA(), this, anId ); else aGroupImpl = new SMESH_Group_i( SMESH_Gen_i::GetPOA(), this, anId ); @@ -1799,6 +2284,12 @@ SMESH::SMESH_GroupBase_ptr SMESH_Mesh_i::createGroup (SMESH::ElementType theElem // register CORBA object for persistence int nextId = _gen_i->RegisterObject( aGroup ); if(MYDEBUG) MESSAGE( "Add group to map with id = "<< nextId); + + // to track changes of GEOM groups + if ( !theShape.IsNull() ) { + GEOM::GEOM_Object_var geom = _gen_i->ShapeToGeomObject( theShape ); + addGeomGroupData( geom, aGroup ); + } } return aGroup._retn(); } @@ -1815,12 +2306,17 @@ void SMESH_Mesh_i::removeGroup( const int theId ) { if(MYDEBUG) MESSAGE("SMESH_Mesh_i::removeGroup()" ); if ( _mapGroups.find( theId ) != _mapGroups.end() ) { + SMESH::SMESH_GroupBase_ptr group = _mapGroups[theId]; _mapGroups.erase( theId ); - _impl->RemoveGroup( theId ); + removeGeomGroupData( group ); + if (! _impl->RemoveGroup( theId )) + { + // it seems to be a call up from _impl caused by hyp modification (issue 0020918) + RemoveGroup( group ); + } } } - //============================================================================= /*! * @@ -1830,7 +2326,8 @@ void SMESH_Mesh_i::removeGroup( const int theId ) SMESH::log_array * SMESH_Mesh_i::GetLog(CORBA::Boolean clearAfterGet) throw(SALOME::SALOME_Exception) { - if(MYDEBUG) MESSAGE("SMESH_Mesh_i::GetLog"); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); SMESH::log_array_var aLog; try{ @@ -1860,14 +2357,14 @@ throw(SALOME::SALOME_Exception) aLog[indexLog].coords.length(rnum); aLog[indexLog].indexes.length(inum); for(int i = 0; i < rnum; i++){ - aLog[indexLog].coords[i] = *ir; - //MESSAGE(" "<ClearLog(); } //============================================================================= @@ -1917,6 +2414,34 @@ CORBA::Long SMESH_Mesh_i::GetStudyId()throw(SALOME::SALOME_Exception) return _studyId; } +//============================================================================= +namespace +{ + //!< implementation of struct used to call methods of SMESH_Mesh_i from SMESH_Mesh + // issue 0020918: groups removal is caused by hyp modification + // issue 0021208: to forget not loaded mesh data at hyp modification + struct TCallUp_i : public SMESH_Mesh::TCallUp + { + SMESH_Mesh_i* _mesh; + TCallUp_i(SMESH_Mesh_i* mesh):_mesh(mesh) {} + virtual void RemoveGroup (const int theGroupID) { _mesh->removeGroup( theGroupID ); } + virtual void HypothesisModified () { _mesh->onHypothesisModified(); } + virtual void Load () { _mesh->Load(); } + }; +} + +//================================================================================ +/*! + * \brief callback from _impl to forget not loaded mesh data (issue 0021208) + */ +//================================================================================ + +void SMESH_Mesh_i::onHypothesisModified() +{ + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); +} + //============================================================================= /*! * @@ -1927,6 +2452,8 @@ void SMESH_Mesh_i::SetImpl(::SMESH_Mesh * impl) { if(MYDEBUG) MESSAGE("SMESH_Mesh_i::SetImpl"); _impl = impl; + if ( _impl ) + _impl->SetCallUp( new TCallUp_i(this)); } //============================================================================= @@ -1949,6 +2476,9 @@ void SMESH_Mesh_i::SetImpl(::SMESH_Mesh * impl) SMESH::SMESH_MeshEditor_ptr SMESH_Mesh_i::GetMeshEditor() { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + // Create MeshEditor SMESH_MeshEditor_i *aMeshEditor = new SMESH_MeshEditor_i( this, false ); SMESH::SMESH_MeshEditor_var aMesh = aMeshEditor->_this(); @@ -1967,37 +2497,102 @@ SMESH::SMESH_MeshEditor_ptr SMESH_Mesh_i::GetMeshEditor() SMESH::SMESH_MeshEditor_ptr SMESH_Mesh_i::GetMeshEditPreviewer() { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH_MeshEditor_i *aMeshEditor = new SMESH_MeshEditor_i( this, true ); SMESH::SMESH_MeshEditor_var aMesh = aMeshEditor->_this(); return aMesh._retn(); } +//================================================================================ +/*! + * \brief Return true if the mesh has been edited since a last total re-compute + * and those modifications may prevent successful partial re-compute + */ +//================================================================================ + +CORBA::Boolean SMESH_Mesh_i::HasModificationsToDiscard() throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + return _impl->HasModificationsToDiscard(); +} + +//================================================================================ +/*! + * \brief Returns a random unique color + */ +//================================================================================ + +static SALOMEDS::Color getUniqueColor( const std::list& theReservedColors ) +{ + const int MAX_ATTEMPTS = 100; + int cnt = 0; + double tolerance = 0.5; + SALOMEDS::Color col; + + bool ok = false; + while ( !ok ) { + // generate random color + double red = (double)rand() / RAND_MAX; + double green = (double)rand() / RAND_MAX; + double blue = (double)rand() / RAND_MAX; + // check existence in the list of the existing colors + bool matched = false; + std::list::const_iterator it; + for ( it = theReservedColors.begin(); it != theReservedColors.end() && !matched; ++it ) { + SALOMEDS::Color color = *it; + double tol = fabs( color.R - red ) + fabs( color.G - green ) + fabs( color.B - blue ); + matched = tol < tolerance; + } + if ( (cnt+1) % 20 == 0 ) tolerance = tolerance/2; + ok = ( ++cnt == MAX_ATTEMPTS ) || !matched; + col.R = red; + col.G = green; + col.B = blue; + } + return col; +} + //============================================================================= /*! - * + * Sets auto-color mode. If it is on, groups get unique random colors */ //============================================================================= + void SMESH_Mesh_i::SetAutoColor(CORBA::Boolean theAutoColor) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); _impl->SetAutoColor(theAutoColor); + + TPythonDump pyDump; // not to dump group->SetColor() from below code + pyDump<<_this()<<".SetAutoColor( "< aReservedColors; + map::iterator it = _mapGroups.begin(); + for ( ; it != _mapGroups.end(); it++ ) { + if ( CORBA::is_nil( it->second )) continue; + SALOMEDS::Color aColor = getUniqueColor( aReservedColors ); + it->second->SetColor( aColor ); + aReservedColors.push_back( aColor ); + } } //============================================================================= /*! - * + * Returns true if auto-color mode is on */ //============================================================================= + CORBA::Boolean SMESH_Mesh_i::GetAutoColor() throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); return _impl->GetAutoColor(); } - //============================================================================= /*! - * Export in different formats + * Checks if there are groups with equal names */ //============================================================================= @@ -2006,7 +2601,13 @@ CORBA::Boolean SMESH_Mesh_i::HasDuplicatedGroupNamesMED() return _impl->HasDuplicatedGroupNamesMED(); } -void SMESH_Mesh_i::PrepareForWriting (const char* file) +//================================================================================ +/*! + * \brief Care of a file before exporting mesh into it + */ +//================================================================================ + +void SMESH_Mesh_i::PrepareForWriting (const char* file, bool overwrite) { TCollection_AsciiString aFullName ((char*)file); OSD_Path aPath (aFullName); @@ -2015,8 +2616,10 @@ void SMESH_Mesh_i::PrepareForWriting (const char* file) // existing filesystem node if (aFile.KindOfFile() == OSD_FILE) { if (aFile.IsWriteable()) { - aFile.Reset(); - aFile.Remove(); + if (overwrite) { + aFile.Reset(); + aFile.Remove(); + } if (aFile.Failed()) { TCollection_AsciiString msg ("File "); msg += aFullName + " cannot be replaced."; @@ -2047,98 +2650,188 @@ void SMESH_Mesh_i::PrepareForWriting (const char* file) } } -void SMESH_Mesh_i::ExportToMED (const char* file, - CORBA::Boolean auto_groups, - SMESH::MED_VERSION theVersion) - throw(SALOME::SALOME_Exception) -{ - Unexpect aCatch(SALOME_SalomeException); +//================================================================================ +/*! + * \brief Prepares a file for export and pass names of mesh groups from study to mesh DS + * \param file - file name + * \param overwrite - to erase the file or not + * \retval string - mesh name + */ +//================================================================================ +string SMESH_Mesh_i::prepareMeshNameAndGroups(const char* file, + CORBA::Boolean overwrite) +{ // Perform Export - PrepareForWriting(file); - const char* aMeshName = "Mesh"; + PrepareForWriting(file, overwrite); + string aMeshName = "Mesh"; SALOMEDS::Study_ptr aStudy = _gen_i->GetCurrentStudy(); if ( !aStudy->_is_nil() ) { SALOMEDS::SObject_var aMeshSO = _gen_i->ObjectToSObject( aStudy, _this() ); if ( !aMeshSO->_is_nil() ) { - aMeshName = aMeshSO->GetName(); + CORBA::String_var name = aMeshSO->GetName(); + aMeshName = name; // asv : 27.10.04 : fix of 6903: check for StudyLocked before adding attributes if ( !aStudy->GetProperties()->IsLocked() ) - { - SALOMEDS::GenericAttribute_var anAttr; - SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); - SALOMEDS::AttributeExternalFileDef_var aFileName; - anAttr=aStudyBuilder->FindOrCreateAttribute(aMeshSO, "AttributeExternalFileDef"); - aFileName = SALOMEDS::AttributeExternalFileDef::_narrow(anAttr); - ASSERT(!aFileName->_is_nil()); + { + SALOMEDS::GenericAttribute_var anAttr; + SALOMEDS::StudyBuilder_var aStudyBuilder = aStudy->NewBuilder(); + SALOMEDS::AttributeExternalFileDef_var aFileName; + anAttr=aStudyBuilder->FindOrCreateAttribute(aMeshSO, "AttributeExternalFileDef"); + aFileName = SALOMEDS::AttributeExternalFileDef::_narrow(anAttr); + ASSERT(!aFileName->_is_nil()); aFileName->SetValue(file); SALOMEDS::AttributeFileType_var aFileType; anAttr=aStudyBuilder->FindOrCreateAttribute(aMeshSO, "AttributeFileType"); aFileType = SALOMEDS::AttributeFileType::_narrow(anAttr); ASSERT(!aFileType->_is_nil()); aFileType->SetValue("FICHIERMED"); - } + } } } // Update Python script // set name of mesh before export - TPythonDump() << _gen_i << ".SetName(" << _this() << ", '" << aMeshName << "')"; - + TPythonDump() << _gen_i << ".SetName(" << _this() << ", '" << aMeshName.c_str() << "')"; + // check names of groups checkGroupNames(); - TPythonDump() << _this() << ".ExportToMED( '" - << file << "', " << auto_groups << ", " << theVersion << " )"; + return aMeshName; +} + +//================================================================================ +/*! + * \brief Export to med file + */ +//================================================================================ + +void SMESH_Mesh_i::ExportToMEDX (const char* file, + CORBA::Boolean auto_groups, + SMESH::MED_VERSION theVersion, + CORBA::Boolean overwrite) + throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + string aMeshName = prepareMeshNameAndGroups(file, overwrite); + TPythonDump() << _this() << ".ExportToMEDX( r'" + << file << "', " << auto_groups << ", " << theVersion << ", " << overwrite << " )"; + + _impl->ExportMED( file, aMeshName.c_str(), auto_groups, theVersion ); +} + +//================================================================================ +/*! + * \brief Export a mesh to a med file + */ +//================================================================================ - _impl->ExportMED( file, aMeshName, auto_groups, theVersion ); +void SMESH_Mesh_i::ExportToMED (const char* file, + CORBA::Boolean auto_groups, + SMESH::MED_VERSION theVersion) + throw(SALOME::SALOME_Exception) +{ + ExportToMEDX(file,auto_groups,theVersion,true); } +//================================================================================ +/*! + * \brief Export a mesh to a med file + */ +//================================================================================ + void SMESH_Mesh_i::ExportMED (const char* file, - CORBA::Boolean auto_groups) + CORBA::Boolean auto_groups) + throw(SALOME::SALOME_Exception) +{ + ExportToMEDX(file,auto_groups,SMESH::MED_V2_2,true); +} + +//================================================================================ +/*! + * \brief Export a mesh to a SAUV file + */ +//================================================================================ + +void SMESH_Mesh_i::ExportSAUV (const char* file, + CORBA::Boolean auto_groups) throw(SALOME::SALOME_Exception) { - ExportToMED(file,auto_groups,SMESH::MED_V2_1); + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + string aMeshName = prepareMeshNameAndGroups(file, true); + TPythonDump() << _this() << ".ExportSAUV( r'" << file << "', " << auto_groups << " )"; + _impl->ExportSAUV(file, aMeshName.c_str(), auto_groups); } + +//================================================================================ +/*! + * \brief Export a mesh to a DAT file + */ +//================================================================================ + void SMESH_Mesh_i::ExportDAT (const char *file) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); // Update Python script // check names of groups checkGroupNames(); - TPythonDump() << _this() << ".ExportDAT( '" << file << "' )"; + TPythonDump() << _this() << ".ExportDAT( r'" << file << "' )"; // Perform Export PrepareForWriting(file); _impl->ExportDAT(file); } +//================================================================================ +/*! + * \brief Export a mesh to an UNV file + */ +//================================================================================ + void SMESH_Mesh_i::ExportUNV (const char *file) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); // Update Python script // check names of groups checkGroupNames(); - TPythonDump() << _this() << ".ExportUNV( '" << file << "' )"; + TPythonDump() << _this() << ".ExportUNV( r'" << file << "' )"; // Perform Export PrepareForWriting(file); _impl->ExportUNV(file); } +//================================================================================ +/*! + * \brief Export a mesh to an STL file + */ +//================================================================================ + void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); // Update Python script // check names of groups checkGroupNames(); - TPythonDump() << _this() << ".ExportSTL( '" << file << "', " << isascii << " )"; + TPythonDump() << _this() << ".ExportSTL( r'" << file << "', " << isascii << " )"; // Perform Export PrepareForWriting(file); @@ -2147,84 +2840,282 @@ void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii) //============================================================================= /*! - * + * \brief Class providing SMESHDS_Mesh API to SMESH_IDSource. + * It is used to export a part of mesh as a whole mesh. */ -//============================================================================= - -SALOME_MED::MESH_ptr SMESH_Mesh_i::GetMEDMesh()throw(SALOME::SALOME_Exception) +class SMESH_MeshPartDS : public SMESHDS_Mesh { - Unexpect aCatch(SALOME_SalomeException); - SMESH_MEDMesh_i *aMedMesh = new SMESH_MEDMesh_i(this); - SALOME_MED::MESH_var aMesh = aMedMesh->_this(); - return aMesh._retn(); -} +public: + SMESH_MeshPartDS(SMESH::SMESH_IDSource_ptr meshPart); + + virtual SMDS_NodeIteratorPtr nodesIterator (bool idInceasingOrder=false) const; + virtual SMDS_EdgeIteratorPtr edgesIterator (bool idInceasingOrder=false) const; + virtual SMDS_FaceIteratorPtr facesIterator (bool idInceasingOrder=false) const; + virtual SMDS_VolumeIteratorPtr volumesIterator (bool idInceasingOrder=false) const; + + virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type=SMDSAbs_All) const; + virtual SMDS_ElemIteratorPtr elementGeomIterator(SMDSAbs_GeometryType type) const; + virtual SMDS_ElemIteratorPtr elementEntityIterator(SMDSAbs_EntityType type) const; + +private: + TIDSortedElemSet _elements[ SMDSAbs_NbElementTypes ]; + SMESHDS_Mesh* _meshDS; + /*! + * \brief Class used to access to protected data of SMDS_MeshInfo + */ + struct TMeshInfo : public SMDS_MeshInfo + { + void Add(const SMDS_MeshElement* e) { SMDS_MeshInfo::addWithPoly( e ); } + }; +}; -//============================================================================= +//================================================================================ /*! - * + * \brief Export a part of mesh to a med file */ -//============================================================================= -CORBA::Long SMESH_Mesh_i::NbNodes()throw(SALOME::SALOME_Exception) +//================================================================================ + +void SMESH_Mesh_i::ExportPartToMED(::SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + CORBA::Boolean auto_groups, + ::SMESH::MED_VERSION version, + ::CORBA::Boolean overwrite) + throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - return _impl->NbNodes(); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + PrepareForWriting(file, overwrite); + + string aMeshName = "Mesh"; + SALOMEDS::Study_ptr aStudy = _gen_i->GetCurrentStudy(); + if ( !aStudy->_is_nil() ) { + SALOMEDS::SObject_var SO = _gen_i->ObjectToSObject( aStudy, meshPart ); + if ( !SO->_is_nil() ) { + CORBA::String_var name = SO->GetName(); + aMeshName = name; + } + } + SMESH_MeshPartDS partDS( meshPart ); + _impl->ExportMED( file, aMeshName.c_str(), auto_groups, version, &partDS ); + + TPythonDump() << _this() << ".ExportPartToMED( " << meshPart << ", r'" << file << "', " + << auto_groups << ", " << version << ", " << overwrite << " )"; } -//============================================================================= +//================================================================================ /*! - * + * \brief Export a part of mesh to a DAT file */ -//============================================================================= -CORBA::Long SMESH_Mesh_i::NbElements()throw (SALOME::SALOME_Exception) +//================================================================================ + +void SMESH_Mesh_i::ExportPartToDAT(::SMESH::SMESH_IDSource_ptr meshPart, + const char* file) + throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - return NbEdges() + NbFaces() + NbVolumes(); -} + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); -//============================================================================= + PrepareForWriting(file); + + SMESH_MeshPartDS partDS( meshPart ); + _impl->ExportDAT(file,&partDS); + + TPythonDump() << _this() << ".ExportPartToDAT( " << meshPart << ", r'" << file << "' )"; +} +//================================================================================ /*! - * + * \brief Export a part of mesh to an UNV file */ -//============================================================================= -CORBA::Long SMESH_Mesh_i::NbEdges()throw(SALOME::SALOME_Exception) +//================================================================================ + +void SMESH_Mesh_i::ExportPartToUNV(::SMESH::SMESH_IDSource_ptr meshPart, + const char* file) + throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - return _impl->NbEdges(); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + PrepareForWriting(file); + + SMESH_MeshPartDS partDS( meshPart ); + _impl->ExportUNV(file, &partDS); + + TPythonDump() << _this() << ".ExportPartToUNV( " << meshPart<< ", r'" << file << "' )"; } +//================================================================================ +/*! + * \brief Export a part of mesh to an STL file + */ +//================================================================================ -CORBA::Long SMESH_Mesh_i::NbEdgesOfOrder(SMESH::ElementOrder order) - throw(SALOME::SALOME_Exception) +void SMESH_Mesh_i::ExportPartToSTL(::SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + ::CORBA::Boolean isascii) + throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - return _impl->NbEdges( (SMDSAbs_ElementOrder) order); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + PrepareForWriting(file); + + SMESH_MeshPartDS partDS( meshPart ); + _impl->ExportSTL(file, isascii, &partDS); + + TPythonDump() << _this() << ".ExportPartToSTL( " + << meshPart<< ", r'" << file << "', " << isascii << ")"; } -//============================================================================= +//================================================================================ /*! - * + * \brief Export a part of mesh to an STL file + */ +//================================================================================ + +void SMESH_Mesh_i::ExportCGNS(::SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + CORBA::Boolean overwrite) + throw (SALOME::SALOME_Exception) +{ +#ifdef WITH_CGNS + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + PrepareForWriting(file,overwrite); + + SMESH_MeshPartDS partDS( meshPart ); + _impl->ExportCGNS(file, &partDS); + + TPythonDump() << _this() << ".ExportCGNS( " + << meshPart<< ", r'" << file << "', " << overwrite << ")"; +#else + THROW_SALOME_CORBA_EXCEPTION("CGNS library is unavailable", SALOME::INTERNAL_ERROR); +#endif +} + +//============================================================================= +/*! + * Return implementation of SALOME_MED::MESH interfaces */ //============================================================================= + +SALOME_MED::MESH_ptr SMESH_Mesh_i::GetMEDMesh()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + SMESH_MEDMesh_i *aMedMesh = new SMESH_MEDMesh_i(this); + SALOME_MED::MESH_var aMesh = aMedMesh->_this(); + return aMesh._retn(); +} + +//============================================================================= + +CORBA::Long SMESH_Mesh_i::NbNodes()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbNodes(); + + return _impl->NbNodes(); +} + +CORBA::Long SMESH_Mesh_i::NbElements()throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbElements(); + + return Nb0DElements() + NbEdges() + NbFaces() + NbVolumes() + NbBalls(); +} + +CORBA::Long SMESH_Mesh_i::Nb0DElements()throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->Nb0DElements(); + + return _impl->Nb0DElements(); +} + +CORBA::Long SMESH_Mesh_i::NbBalls() throw (SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbBalls(); + + return _impl->NbBalls(); +} + +CORBA::Long SMESH_Mesh_i::NbEdges()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbEdges(); + + return _impl->NbEdges(); +} + +CORBA::Long SMESH_Mesh_i::NbEdgesOfOrder(SMESH::ElementOrder order) + throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbEdges( (SMDSAbs_ElementOrder) order ); + + return _impl->NbEdges( (SMDSAbs_ElementOrder) order); +} + +//============================================================================= + CORBA::Long SMESH_Mesh_i::NbFaces()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbFaces(); + return _impl->NbFaces(); } CORBA::Long SMESH_Mesh_i::NbTriangles()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbTriangles(); + return _impl->NbTriangles(); } CORBA::Long SMESH_Mesh_i::NbQuadrangles()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbQuadrangles(); + return _impl->NbQuadrangles(); } +CORBA::Long SMESH_Mesh_i::NbBiQuadQuadrangles()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbBiQuadQuadrangles(); + + return _impl->NbBiQuadQuadrangles(); +} + CORBA::Long SMESH_Mesh_i::NbPolygons()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPolygons(); + return _impl->NbPolygons(); } @@ -2232,6 +3123,9 @@ CORBA::Long SMESH_Mesh_i::NbFacesOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbFaces( (SMDSAbs_ElementOrder) order ); + return _impl->NbFaces( (SMDSAbs_ElementOrder) order); } @@ -2239,6 +3133,9 @@ CORBA::Long SMESH_Mesh_i::NbTrianglesOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbTriangles( (SMDSAbs_ElementOrder) order ); + return _impl->NbTriangles( (SMDSAbs_ElementOrder) order); } @@ -2246,47 +3143,83 @@ CORBA::Long SMESH_Mesh_i::NbQuadranglesOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbQuadrangles( (SMDSAbs_ElementOrder) order ); + return _impl->NbQuadrangles( (SMDSAbs_ElementOrder) order); } //============================================================================= -/*! - * - */ -//============================================================================= + CORBA::Long SMESH_Mesh_i::NbVolumes()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbVolumes(); + return _impl->NbVolumes(); } CORBA::Long SMESH_Mesh_i::NbTetras()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbTetras(); + return _impl->NbTetras(); } CORBA::Long SMESH_Mesh_i::NbHexas()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbHexas(); + return _impl->NbHexas(); } +CORBA::Long SMESH_Mesh_i::NbTriQuadraticHexas()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbTriQuadHexas(); + + return _impl->NbTriQuadraticHexas(); +} + CORBA::Long SMESH_Mesh_i::NbPyramids()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPyramids(); + return _impl->NbPyramids(); } CORBA::Long SMESH_Mesh_i::NbPrisms()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPrisms(); + return _impl->NbPrisms(); } +CORBA::Long SMESH_Mesh_i::NbHexagonalPrisms()throw(SALOME::SALOME_Exception) +{ + Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbHexPrisms(); + + return _impl->NbHexagonalPrisms(); +} + CORBA::Long SMESH_Mesh_i::NbPolyhedrons()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPolyhedrons(); + return _impl->NbPolyhedrons(); } @@ -2294,6 +3227,9 @@ CORBA::Long SMESH_Mesh_i::NbVolumesOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbVolumes( (SMDSAbs_ElementOrder) order ); + return _impl->NbVolumes( (SMDSAbs_ElementOrder) order); } @@ -2301,6 +3237,9 @@ CORBA::Long SMESH_Mesh_i::NbTetrasOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbTetras( (SMDSAbs_ElementOrder) order); + return _impl->NbTetras( (SMDSAbs_ElementOrder) order); } @@ -2308,6 +3247,9 @@ CORBA::Long SMESH_Mesh_i::NbHexasOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbHexas( (SMDSAbs_ElementOrder) order); + return _impl->NbHexas( (SMDSAbs_ElementOrder) order); } @@ -2315,6 +3257,9 @@ CORBA::Long SMESH_Mesh_i::NbPyramidsOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPyramids( (SMDSAbs_ElementOrder) order); + return _impl->NbPyramids( (SMDSAbs_ElementOrder) order); } @@ -2322,14 +3267,18 @@ CORBA::Long SMESH_Mesh_i::NbPrismsOfOrder(SMESH::ElementOrder order) throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + return _preMeshInfo->NbPrisms( (SMDSAbs_ElementOrder) order); + return _impl->NbPrisms( (SMDSAbs_ElementOrder) order); } //============================================================================= /*! - * + * Returns nb of published sub-meshes */ //============================================================================= + CORBA::Long SMESH_Mesh_i::NbSubMesh()throw(SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); @@ -2338,41 +3287,31 @@ CORBA::Long SMESH_Mesh_i::NbSubMesh()throw(SALOME::SALOME_Exception) //============================================================================= /*! - * + * Dumps mesh into a string */ //============================================================================= + char* SMESH_Mesh_i::Dump() { - std::ostringstream os; + ostringstream os; _impl->Dump( os ); return CORBA::string_dup( os.str().c_str() ); } //============================================================================= /*! - * + * Method of SMESH_IDSource interface */ //============================================================================= + SMESH::long_array* SMESH_Mesh_i::GetIDs() { -// SMESH::long_array_var aResult = new SMESH::long_array(); -// SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); -// int aMinId = aSMESHDS_Mesh->MinElementID(); -// int aMaxId = aSMESHDS_Mesh->MaxElementID(); - -// aResult->length(aMaxId - aMinId + 1); - -// for (int i = 0, id = aMinId; id <= aMaxId; id++ ) -// aResult[i++] = id; - -// return aResult._retn(); - // PAL12398 return GetElementsId(); } //============================================================================= /*! - * + * Returns ids of all elements */ //============================================================================= @@ -2380,7 +3319,9 @@ SMESH::long_array* SMESH_Mesh_i::GetElementsId() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_Mesh_i::GetElementsId"); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); @@ -2399,7 +3340,7 @@ SMESH::long_array* SMESH_Mesh_i::GetElementsId() //============================================================================= /*! - * + * Returns ids of all elements of given type */ //============================================================================= @@ -2407,7 +3348,9 @@ SMESH::long_array* SMESH_Mesh_i::GetElementsByType( SMESH::ElementType theElemTy throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetElementsByType"); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); @@ -2419,7 +3362,7 @@ SMESH::long_array* SMESH_Mesh_i::GetElementsByType( SMESH::ElementType theElemTy // No sense in returning ids of elements along with ids of nodes: // when theElemType == SMESH::ALL, return node ids only if // there are no elements - if ( theElemType == SMESH::NODE || theElemType == SMESH::ALL && nbElements == 0 ) + if ( theElemType == SMESH::NODE || (theElemType == SMESH::ALL && nbElements == 0) ) return GetNodesId(); aResult->length( nbElements ); @@ -2440,7 +3383,7 @@ SMESH::long_array* SMESH_Mesh_i::GetElementsByType( SMESH::ElementType theElemTy //============================================================================= /*! - * + * Returns ids of all nodes */ //============================================================================= @@ -2448,7 +3391,9 @@ SMESH::long_array* SMESH_Mesh_i::GetNodesId() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetNodesId"); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); @@ -2457,7 +3402,7 @@ SMESH::long_array* SMESH_Mesh_i::GetNodesId() long nbNodes = NbNodes(); aResult->length( nbNodes ); - SMDS_NodeIteratorPtr anIt = aSMESHDS_Mesh->nodesIterator(); + SMDS_NodeIteratorPtr anIt = aSMESHDS_Mesh->nodesIterator(/*idInceasingOrder=*/true); for ( int i = 0, n = nbNodes; i < n && anIt->more(); i++ ) aResult[i] = anIt->next()->GetID(); @@ -2473,9 +3418,30 @@ SMESH::long_array* SMESH_Mesh_i::GetNodesId() SMESH::ElementType SMESH_Mesh_i::GetElementType( const CORBA::Long id, const bool iselem ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + return ( SMESH::ElementType )_impl->GetElementType( id, iselem ); } +//============================================================================= +/*! + * + */ +//============================================================================= + +SMESH::EntityType SMESH_Mesh_i::GetElementGeomType( const CORBA::Long id ) + throw (SALOME::SALOME_Exception) +{ + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + const SMDS_MeshElement* e = _impl->GetMeshDS()->FindElement(id); + if ( !e ) + THROW_SALOME_CORBA_EXCEPTION( "invalid element id", SALOME::BAD_PARAM ); + + return ( SMESH::EntityType ) e->GetEntityType(); +} //============================================================================= /*! @@ -2485,6 +3451,9 @@ SMESH::ElementType SMESH_Mesh_i::GetElementType( const CORBA::Long id, const boo SMESH::long_array* SMESH_Mesh_i::GetSubMeshElementsId(const CORBA::Long ShapeID) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID); @@ -2512,9 +3481,13 @@ SMESH::long_array* SMESH_Mesh_i::GetSubMeshElementsId(const CORBA::Long ShapeID) * returns only nodes on shapes. */ //============================================================================= -SMESH::long_array* SMESH_Mesh_i::GetSubMeshNodesId(const CORBA::Long ShapeID, CORBA::Boolean all) - throw (SALOME::SALOME_Exception) +SMESH::long_array* SMESH_Mesh_i::GetSubMeshNodesId(const CORBA::Long ShapeID, + CORBA::Boolean all) + throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID); @@ -2552,15 +3525,18 @@ SMESH::long_array* SMESH_Mesh_i::GetSubMeshNodesId(const CORBA::Long ShapeID, CO return aResult._retn(); } - //============================================================================= /*! * Returns type of elements for given submesh */ //============================================================================= + SMESH::ElementType SMESH_Mesh_i::GetSubMeshElementType(const CORBA::Long ShapeID) - throw (SALOME::SALOME_Exception) + throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH_subMesh* SM = _impl->GetSubMeshContaining(ShapeID); if(!SM) return SMESH::ALL; @@ -2578,12 +3554,15 @@ SMESH::ElementType SMESH_Mesh_i::GetSubMeshElementType(const CORBA::Long ShapeID //============================================================================= /*! - * + * Returns pointer to _impl as an integer value. Is called from constructor of SMESH_Client */ //============================================================================= CORBA::LongLong SMESH_Mesh_i::GetMeshPtr() { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + CORBA::LongLong pointeur = CORBA::LongLong(_impl); if ( MYDEBUG ) MESSAGE("CORBA::LongLong SMESH_Mesh_i::GetMeshPtr() "<FullLoadFromFile(); + SMESH::double_array_var aResult = new SMESH::double_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) @@ -2628,6 +3610,9 @@ SMESH::double_array* SMESH_Mesh_i::GetNodeXYZ(const CORBA::Long id) SMESH::long_array* SMESH_Mesh_i::GetNodeInverseElements(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) @@ -2663,6 +3648,9 @@ SMESH::long_array* SMESH_Mesh_i::GetNodeInverseElements(const CORBA::Long id) SMESH::NodePosition* SMESH_Mesh_i::GetNodePosition(CORBA::Long NodeID) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::NodePosition* aNodePosition = new SMESH::NodePosition(); aNodePosition->shapeID = 0; aNodePosition->shapeType = GEOM::SHAPE; @@ -2674,21 +3662,21 @@ SMESH::NodePosition* SMESH_Mesh_i::GetNodePosition(CORBA::Long NodeID) { if ( SMDS_PositionPtr pos = aNode->GetPosition() ) { - aNodePosition->shapeID = pos->GetShapeId(); + aNodePosition->shapeID = aNode->getshapeId(); switch ( pos->GetTypeOfPosition() ) { case SMDS_TOP_EDGE: aNodePosition->shapeType = GEOM::EDGE; aNodePosition->params.length(1); aNodePosition->params[0] = - static_cast( pos.get() )->GetUParameter(); + static_cast( pos )->GetUParameter(); break; case SMDS_TOP_FACE: aNodePosition->shapeType = GEOM::FACE; aNodePosition->params.length(2); aNodePosition->params[0] = - static_cast( pos.get() )->GetUParameter(); + static_cast( pos )->GetUParameter(); aNodePosition->params[1] = - static_cast( pos.get() )->GetVParameter(); + static_cast( pos )->GetVParameter(); break; case SMDS_TOP_VERTEX: aNodePosition->shapeType = GEOM::VERTEX; @@ -2715,6 +3703,9 @@ SMESH::NodePosition* SMESH_Mesh_i::GetNodePosition(CORBA::Long NodeID) CORBA::Long SMESH_Mesh_i::GetShapeID(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; @@ -2722,11 +3713,7 @@ CORBA::Long SMESH_Mesh_i::GetShapeID(const CORBA::Long id) // try to find node const SMDS_MeshNode* aNode = aSMESHDS_Mesh->FindNode(id); if(aNode) { - SMDS_PositionPtr pos = aNode->GetPosition(); - if(!pos) - return -1; - else - return pos->GetShapeId(); + return aNode->getshapeId(); } return -1; @@ -2743,6 +3730,9 @@ CORBA::Long SMESH_Mesh_i::GetShapeID(const CORBA::Long id) CORBA::Long SMESH_Mesh_i::GetShapeIDForElem(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; @@ -2771,6 +3761,9 @@ CORBA::Long SMESH_Mesh_i::GetShapeIDForElem(const CORBA::Long id) CORBA::Long SMESH_Mesh_i::GetElemNbNodes(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; // try to find element @@ -2790,6 +3783,9 @@ CORBA::Long SMESH_Mesh_i::GetElemNbNodes(const CORBA::Long id) CORBA::Long SMESH_Mesh_i::GetElemNode(const CORBA::Long id, const CORBA::Long index) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(id); @@ -2806,6 +3802,9 @@ CORBA::Long SMESH_Mesh_i::GetElemNode(const CORBA::Long id, const CORBA::Long in SMESH::long_array* SMESH_Mesh_i::GetElemNodes(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::long_array_var aResult = new SMESH::long_array(); if ( SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS() ) { @@ -2828,6 +3827,9 @@ SMESH::long_array* SMESH_Mesh_i::GetElemNodes(const CORBA::Long id) CORBA::Boolean SMESH_Mesh_i::IsMediumNode(const CORBA::Long ide, const CORBA::Long idn) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return false; // try to find node @@ -2851,6 +3853,9 @@ CORBA::Boolean SMESH_Mesh_i::IsMediumNode(const CORBA::Long ide, const CORBA::Lo CORBA::Boolean SMESH_Mesh_i::IsMediumNodeOfAnyElem(const CORBA::Long idn, SMESH::ElementType theElemType) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return false; @@ -2878,6 +3883,9 @@ CORBA::Boolean SMESH_Mesh_i::IsMediumNodeOfAnyElem(const CORBA::Long idn, CORBA::Long SMESH_Mesh_i::ElemNbEdges(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(id); @@ -2894,6 +3902,9 @@ CORBA::Long SMESH_Mesh_i::ElemNbEdges(const CORBA::Long id) CORBA::Long SMESH_Mesh_i::ElemNbFaces(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return -1; const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(id); @@ -2901,6 +3912,63 @@ CORBA::Long SMESH_Mesh_i::ElemNbFaces(const CORBA::Long id) return elem->NbFaces(); } +//======================================================================= +//function : GetElemFaceNodes +//purpose : Returns nodes of given face (counted from zero) for given element. +//======================================================================= + +SMESH::long_array* SMESH_Mesh_i::GetElemFaceNodes(CORBA::Long elemId, + CORBA::Short faceIndex) +{ + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + SMESH::long_array_var aResult = new SMESH::long_array(); + if ( SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS() ) + { + if ( const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(elemId) ) + { + SMDS_VolumeTool vtool( elem ); + if ( faceIndex < vtool.NbFaces() ) + { + aResult->length( vtool.NbFaceNodes( faceIndex )); + const SMDS_MeshNode** nn = vtool.GetFaceNodes( faceIndex ); + for ( int i = 0; i < aResult->length(); ++i ) + aResult[ i ] = nn[ i ]->GetID(); + } + } + } + return aResult._retn(); +} + +//======================================================================= +//function : FindElementByNodes +//purpose : Returns an element based on all given nodes. +//======================================================================= + +CORBA::Long SMESH_Mesh_i::FindElementByNodes(const SMESH::long_array& nodes) +{ + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + CORBA::Long elemID(0); + if ( SMESHDS_Mesh* mesh = _impl->GetMeshDS() ) + { + vector< const SMDS_MeshNode * > nn( nodes.length() ); + for ( int i = 0; i < nodes.length(); ++i ) + if ( !( nn[i] = mesh->FindNode( nodes[i] ))) + return elemID; + + const SMDS_MeshElement* elem = mesh->FindElement( nn ); + if ( !elem && ( _impl->NbEdges ( ORDER_QUADRATIC ) || + _impl->NbFaces ( ORDER_QUADRATIC ) || + _impl->NbVolumes( ORDER_QUADRATIC ))) + elem = mesh->FindElement( nn, SMDSAbs_All, /*noMedium=*/true ); + + if ( elem ) elemID = CORBA::Long( elem->GetID() ); + } + return elemID; +} //============================================================================= /*! @@ -2910,6 +3978,9 @@ CORBA::Long SMESH_Mesh_i::ElemNbFaces(const CORBA::Long id) CORBA::Boolean SMESH_Mesh_i::IsPoly(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return false; const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(id); @@ -2926,6 +3997,9 @@ CORBA::Boolean SMESH_Mesh_i::IsPoly(const CORBA::Long id) CORBA::Boolean SMESH_Mesh_i::IsQuadratic(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) return false; const SMDS_MeshElement* elem = aSMESHDS_Mesh->FindElement(id); @@ -2933,6 +4007,23 @@ CORBA::Boolean SMESH_Mesh_i::IsQuadratic(const CORBA::Long id) return elem->IsQuadratic(); } +//============================================================================= +/*! + * Returns diameter of ball discrete element or zero in case of an invalid \a id + */ +//============================================================================= + +CORBA::Double SMESH_Mesh_i::GetBallDiameter(CORBA::Long id) +{ + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + + if ( const SMDS_BallElement* ball = + dynamic_cast( _impl->GetMeshDS()->FindElement( id ))) + return ball->GetDiameter(); + + return 0; +} //============================================================================= /*! @@ -2942,6 +4033,9 @@ CORBA::Boolean SMESH_Mesh_i::IsQuadratic(const CORBA::Long id) SMESH::double_array* SMESH_Mesh_i::BaryCenter(const CORBA::Long id) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + SMESH::double_array_var aResult = new SMESH::double_array(); SMESHDS_Mesh* aSMESHDS_Mesh = _impl->GetMeshDS(); if ( aSMESHDS_Mesh == NULL ) @@ -2993,6 +4087,7 @@ void SMESH_Mesh_i::CreateGroupServants() { SALOMEDS::Study_ptr aStudy = _gen_i->GetCurrentStudy(); + set addedIDs; ::SMESH_Mesh::GroupIteratorPtr groupIt = _impl->GetGroups(); while ( groupIt->more() ) { @@ -3002,6 +4097,7 @@ void SMESH_Mesh_i::CreateGroupServants() map::iterator it = _mapGroups.find(anId); if ( it != _mapGroups.end() && !CORBA::is_nil( it->second )) continue; + addedIDs.insert( anId ); SMESH_GroupBase_i* aGroupImpl; TopoDS_Shape shape; @@ -3027,12 +4123,23 @@ void SMESH_Mesh_i::CreateGroupServants() int nextId = _gen_i->RegisterObject( groupVar ); if(MYDEBUG) MESSAGE( "Add group to map with id = "<< nextId); - // publishing of the groups in the study + // publishing the groups in the study if ( !aStudy->_is_nil() ) { GEOM::GEOM_Object_var shapeVar = _gen_i->ShapeToGeomObject( shape ); _gen_i->PublishGroup( aStudy, _this(), groupVar, shapeVar, groupVar->GetName()); } } + if ( !addedIDs.empty() ) + { + // python dump + set::iterator id = addedIDs.begin(); + for ( ; id != addedIDs.end(); ++id ) + { + map::iterator it = _mapGroups.find(*id); + int i = std::distance( _mapGroups.begin(), it ); + TPythonDump() << it->second << " = " << _this() << ".GetGroups()[ "<< i << " ]"; + } + } } //============================================================================= @@ -3066,7 +4173,7 @@ SMESH::ListOfGroups* SMESH_Mesh_i::GetGroups(const list& groupIDs) const SALOME_MED::MedFileInfo* SMESH_Mesh_i::GetMEDFileInfo() { - SALOME_MED::MedFileInfo_var res( myFileInfo ); + SALOME_MED::MedFileInfo_var res( _medFileInfo ); if ( !res.operator->() ) { res = new SALOME_MED::MedFileInfo; res->fileName = ""; @@ -3077,7 +4184,7 @@ SALOME_MED::MedFileInfo* SMESH_Mesh_i::GetMEDFileInfo() //============================================================================= /*! - * \brief Check and correct names of mesh groups + * \brief Pass names of mesh groups from study to mesh DS */ //============================================================================= @@ -3120,8 +4227,9 @@ void SMESH_Mesh_i::checkGroupNames() //============================================================================= void SMESH_Mesh_i::SetParameters(const char* theParameters) { - SMESH_Gen_i::GetSMESHGen()->UpdateParameters(SMESH::SMESH_Mesh::_narrow(_this()), - CORBA::string_dup(theParameters)); + // SMESH_Gen_i::GetSMESHGen()->UpdateParameters(SMESH::SMESH_Mesh::_narrow(_this()), + // CORBA::string_dup(theParameters)); + SMESH_Gen_i::GetSMESHGen()->UpdateParameters(theParameters); } //============================================================================= @@ -3159,3 +4267,653 @@ SMESH::string_array* SMESH_Mesh_i::GetLastParameters() } return aResult._retn(); } + +//======================================================================= +//function : GetTypes +//purpose : Returns types of elements it contains +//======================================================================= + +SMESH::array_of_ElementType* SMESH_Mesh_i::GetTypes() +{ + if ( _preMeshInfo ) + return _preMeshInfo->GetTypes(); + + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + + types->length( 5 ); + int nbTypes = 0; + if (_impl->NbEdges()) types[nbTypes++] = SMESH::EDGE; + if (_impl->NbFaces()) types[nbTypes++] = SMESH::FACE; + if (_impl->NbVolumes()) types[nbTypes++] = SMESH::VOLUME; + if (_impl->Nb0DElements()) types[nbTypes++] = SMESH::ELEM0D; + if (_impl->NbBalls()) types[nbTypes++] = SMESH::BALL; + types->length( nbTypes ); + + return types._retn(); +} + +//======================================================================= +//function : GetMesh +//purpose : Returns self +//======================================================================= + +SMESH::SMESH_Mesh_ptr SMESH_Mesh_i::GetMesh() +{ + return SMESH::SMESH_Mesh::_duplicate( _this() ); +} + +//======================================================================= +//function : IsMeshInfoCorrect +//purpose : * Returns false if GetMeshInfo() returns incorrect information that may +// * happen if mesh data is not yet fully loaded from the file of study. +//======================================================================= + +bool SMESH_Mesh_i::IsMeshInfoCorrect() +{ + return _preMeshInfo ? _preMeshInfo->IsMeshInfoCorrect() : true; +} + +//============================================================================= +/*! + * \brief Returns statistic of mesh elements + */ +//============================================================================= + +SMESH::long_array* SMESH_Mesh_i::GetMeshInfo() +{ + if ( _preMeshInfo ) + return _preMeshInfo->GetMeshInfo(); + + SMESH::long_array_var aRes = new SMESH::long_array(); + aRes->length(SMESH::Entity_Last); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = 0; + SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS(); + if (!aMeshDS) + return aRes._retn(); + const SMDS_MeshInfo& aMeshInfo = aMeshDS->GetMeshInfo(); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = aMeshInfo.NbEntities((SMDSAbs_EntityType)i); + return aRes._retn(); +} + +//============================================================================= +/*! + * \brief Collect statistic of mesh elements given by iterator + */ +//============================================================================= + +void SMESH_Mesh_i::CollectMeshInfo(const SMDS_ElemIteratorPtr theItr, + SMESH::long_array& theInfo) +{ + if (!theItr) return; + while (theItr->more()) + theInfo[ theItr->next()->GetEntityType() ]++; +} + +//============================================================================= +/*! + * \brief mapping of mesh dimension into shape type + */ +//============================================================================= + +static TopAbs_ShapeEnum shapeTypeByDim(const int theDim) +{ + TopAbs_ShapeEnum aType = TopAbs_SOLID; + switch ( theDim ) { + case 0: aType = TopAbs_VERTEX; break; + case 1: aType = TopAbs_EDGE; break; + case 2: aType = TopAbs_FACE; break; + case 3: + default:aType = TopAbs_SOLID; break; + } + return aType; +} + +//============================================================================= +/*! + * \brief Internal structure used to find concurent submeshes + * + * It represents a pair < submesh, concurent dimension >, where + * 'concurrent dimension' is dimension of shape where the submesh can concurent + * with another submesh. In other words, it is dimension of a hypothesis assigned + * to submesh. + */ +//============================================================================= + +class SMESH_DimHyp +{ + public: + //! fileds + int _dim; //!< a dimension the algo can build (concurrent dimension) + int _ownDim; //!< dimension of shape of _subMesh (>=_dim) + TopTools_MapOfShape _shapeMap; + SMESH_subMesh* _subMesh; + list _hypothesises; //!< algo is first, then its parameters + + //! Constructors + SMESH_DimHyp(const SMESH_subMesh* theSubMesh, + const int theDim, + const TopoDS_Shape& theShape) + { + _subMesh = (SMESH_subMesh*)theSubMesh; + SetShape( theDim, theShape ); + } + + //! set shape + void SetShape(const int theDim, + const TopoDS_Shape& theShape) + { + _dim = theDim; + _ownDim = (int)SMESH_Gen::GetShapeDim(theShape); + if (_dim >= _ownDim) + _shapeMap.Add( theShape ); + else { + TopExp_Explorer anExp( theShape, shapeTypeByDim(theDim) ); + for( ; anExp.More(); anExp.Next() ) + _shapeMap.Add( anExp.Current() ); + } + } + + //! Check sharing of sub-shapes + static bool isShareSubShapes(const TopTools_MapOfShape& theToCheck, + const TopTools_MapOfShape& theToFind, + const TopAbs_ShapeEnum theType) + { + bool isShared = false; + TopTools_MapIteratorOfMapOfShape anItr( theToCheck ); + for (; !isShared && anItr.More(); anItr.Next() ) + { + const TopoDS_Shape aSubSh = anItr.Key(); + // check for case when concurrent dimensions are same + isShared = theToFind.Contains( aSubSh ); + // check for sub-shape with concurrent dimension + TopExp_Explorer anExp( aSubSh, theType ); + for ( ; !isShared && anExp.More(); anExp.Next() ) + isShared = theToFind.Contains( anExp.Current() ); + } + return isShared; + } + + //! check algorithms + static bool checkAlgo(const SMESHDS_Hypothesis* theA1, + const SMESHDS_Hypothesis* theA2) + { + if ( theA1->GetType() == SMESHDS_Hypothesis::PARAM_ALGO || + theA2->GetType() == SMESHDS_Hypothesis::PARAM_ALGO ) + return false; // one of the hypothesis is not algorithm + // check algorithm names (should be equal) + return strcmp( theA1->GetName(), theA2->GetName() ) == 0; + } + + + //! Check if sub-shape hypotheses are concurrent + bool IsConcurrent(const SMESH_DimHyp* theOther) const + { + if ( _subMesh == theOther->_subMesh ) + return false; // same sub-shape - should not be + + // if ( == && + // any of the two submeshes is not on COMPOUND shape ) + // -> no concurrency + bool meIsCompound = (_subMesh->GetSubMeshDS() && _subMesh->GetSubMeshDS()->IsComplexSubmesh()); + bool otherIsCompound = (theOther->_subMesh->GetSubMeshDS() && theOther->_subMesh->GetSubMeshDS()->IsComplexSubmesh()); + if ( (_ownDim == _dim || theOther->_ownDim == _dim ) && (!meIsCompound || !otherIsCompound)) + return false; + +// bool checkSubShape = ( _dim >= theOther->_dim ) +// ? isShareSubShapes( _shapeMap, theOther->_shapeMap, shapeTypeByDim(theOther->_dim) ) +// : isShareSubShapes( theOther->_shapeMap, _shapeMap, shapeTypeByDim(_dim) ) ; + bool checkSubShape = isShareSubShapes( _shapeMap, theOther->_shapeMap, shapeTypeByDim(_dim)); + if ( !checkSubShape ) + return false; + + // check algorithms to be same + if (!checkAlgo( _hypothesises.front(), theOther->_hypothesises.front() )) + return true; // different algorithms + + // check hypothesises for concurrence (skip first as algorithm) + int nbSame = 0; + // pointers should be same, becase it is referenes from mesh hypothesis partition + list ::const_iterator hypIt = _hypothesises.begin(); + list ::const_iterator otheEndIt = theOther->_hypothesises.end(); + for ( hypIt++ /*skip first as algo*/; hypIt != _hypothesises.end(); hypIt++ ) + if ( find( theOther->_hypothesises.begin(), otheEndIt, *hypIt ) != otheEndIt ) + nbSame++; + // the submeshes are concurrent if their algorithms has different parameters + return nbSame != theOther->_hypothesises.size() - 1; + } + +}; // end of SMESH_DimHyp + +typedef list TDimHypList; + +static void addDimHypInstance(const int theDim, + const TopoDS_Shape& theShape, + const SMESH_Algo* theAlgo, + const SMESH_subMesh* theSubMesh, + const list & theHypList, + TDimHypList* theDimHypListArr ) +{ + TDimHypList& listOfdimHyp = theDimHypListArr[theDim]; + if ( listOfdimHyp.empty() || listOfdimHyp.back()->_subMesh != theSubMesh ) { + SMESH_DimHyp* dimHyp = new SMESH_DimHyp( theSubMesh, theDim, theShape ); + listOfdimHyp.push_back( dimHyp ); + } + + SMESH_DimHyp* dimHyp = listOfdimHyp.back(); + dimHyp->_hypothesises.push_front(theAlgo); + list ::const_iterator hypIt = theHypList.begin(); + for( ; hypIt != theHypList.end(); hypIt++ ) + dimHyp->_hypothesises.push_back( *hypIt ); +} + +static void findConcurrents(const SMESH_DimHyp* theDimHyp, + const TDimHypList& theListOfDimHyp, + TListOfInt& theListOfConcurr ) +{ + TDimHypList::const_reverse_iterator rIt = theListOfDimHyp.rbegin(); + for ( ; rIt != theListOfDimHyp.rend(); rIt++ ) { + const SMESH_DimHyp* curDimHyp = *rIt; + if ( curDimHyp == theDimHyp ) + break; // meet own dimHyp pointer in same dimension + else if ( theDimHyp->IsConcurrent( curDimHyp ) ) + if ( find( theListOfConcurr.begin(), + theListOfConcurr.end(), + curDimHyp->_subMesh->GetId() ) == theListOfConcurr.end() ) + theListOfConcurr.push_back( curDimHyp->_subMesh->GetId() ); + } +} + +static void unionLists(TListOfInt& theListOfId, + TListOfListOfInt& theListOfListOfId, + const int theIndx ) +{ + TListOfListOfInt::iterator it = theListOfListOfId.begin(); + for ( int i = 0; it != theListOfListOfId.end(); it++, i++ ) { + if ( i < theIndx ) + continue; //skip already treated lists + // check if other list has any same submesh object + TListOfInt& otherListOfId = *it; + if ( find_first_of( theListOfId.begin(), theListOfId.end(), + otherListOfId.begin(), otherListOfId.end() ) == theListOfId.end() ) + continue; + + // union two lists (from source into target) + TListOfInt::iterator it2 = otherListOfId.begin(); + for ( ; it2 != otherListOfId.end(); it2++ ) { + if ( find( theListOfId.begin(), theListOfId.end(), (*it2) ) == theListOfId.end() ) + theListOfId.push_back(*it2); + } + // clear source list + otherListOfId.clear(); + } +} + +//! free memory allocated for dimension-hypothesis objects +static void removeDimHyps( TDimHypList* theArrOfList ) +{ + for (int i = 0; i < 4; i++ ) { + TDimHypList& listOfdimHyp = theArrOfList[i]; + TDimHypList::const_iterator it = listOfdimHyp.begin(); + for ( ; it != listOfdimHyp.end(); it++ ) + delete (*it); + } +} + +//============================================================================= +/*! + * \brief Return submesh objects list in meshing order + */ +//============================================================================= + +SMESH::submesh_array_array* SMESH_Mesh_i::GetMeshOrder() +{ + SMESH::submesh_array_array_var aResult = new SMESH::submesh_array_array(); + + SMESHDS_Mesh* aMeshDS = _impl->GetMeshDS(); + if ( !aMeshDS ) + return aResult._retn(); + + ::SMESH_Mesh& mesh = GetImpl(); + TListOfListOfInt anOrder = mesh.GetMeshOrder(); // is there already defined order? + if ( !anOrder.size() ) { + + // collect submeshes detecting concurrent algorithms and hypothesises + TDimHypList dimHypListArr[4]; // dimHyp list for each shape dimension + + map::iterator i_sm = _mapSubMesh.begin(); + for ( ; i_sm != _mapSubMesh.end(); i_sm++ ) { + ::SMESH_subMesh* sm = (*i_sm).second; + // shape of submesh + const TopoDS_Shape& aSubMeshShape = sm->GetSubShape(); + + // list of assigned hypothesises + const list & hypList = mesh.GetHypothesisList(aSubMeshShape); + // Find out dimensions where the submesh can be concurrent. + // We define the dimensions by algo of each of hypotheses in hypList + list ::const_iterator hypIt = hypList.begin(); + for( ; hypIt != hypList.end(); hypIt++ ) { + SMESH_Algo* anAlgo = 0; + const SMESH_Hypothesis* hyp = dynamic_cast(*hypIt); + if ( hyp->GetType() != SMESHDS_Hypothesis::PARAM_ALGO ) + // hyp it-self is algo + anAlgo = (SMESH_Algo*)dynamic_cast(hyp); + else { + // try to find algorithm with help of sub-shapes + TopExp_Explorer anExp( aSubMeshShape, shapeTypeByDim(hyp->GetDim()) ); + for ( ; !anAlgo && anExp.More(); anExp.Next() ) + anAlgo = mesh.GetGen()->GetAlgo( mesh, anExp.Current() ); + } + if (!anAlgo) + continue; // no assigned algorithm to current submesh + + int dim = anAlgo->GetDim(); // top concurrent dimension (see comment to SMESH_DimHyp) + // the submesh can concurrent at (or lower dims if !anAlgo->NeedDiscreteBoundary()) + + // create instance of dimension-hypothesis for found concurrent dimension(s) and algorithm + for ( int j = anAlgo->NeedDiscreteBoundary() ? dim : 1, jn = dim; j <= jn; j++ ) + addDimHypInstance( j, aSubMeshShape, anAlgo, sm, hypList, dimHypListArr ); + } + } // end iterations on submesh + + // iterate on created dimension-hypotheses and check for concurrents + for ( int i = 0; i < 4; i++ ) { + const list& listOfDimHyp = dimHypListArr[i]; + // check for concurrents in own and other dimensions (step-by-step) + TDimHypList::const_iterator dhIt = listOfDimHyp.begin(); + for ( ; dhIt != listOfDimHyp.end(); dhIt++ ) { + const SMESH_DimHyp* dimHyp = *dhIt; + TListOfInt listOfConcurr; + // looking for concurrents and collect into own list + for ( int j = i; j < 4; j++ ) + findConcurrents( dimHyp, dimHypListArr[j], listOfConcurr ); + // check if any concurrents found + if ( listOfConcurr.size() > 0 ) { + // add own submesh to list of concurrent + listOfConcurr.push_front( dimHyp->_subMesh->GetId() ); + anOrder.push_back( listOfConcurr ); + } + } + } + + removeDimHyps(dimHypListArr); + + // now, minimise the number of concurrent groups + // Here we assume that lists of submeshes can have same submesh + // in case of multi-dimension algorithms, as result + // list with common submesh has to be united into one list + int listIndx = 0; + TListOfListOfInt::iterator listIt = anOrder.begin(); + for(; listIt != anOrder.end(); listIt++, listIndx++ ) + unionLists( *listIt, anOrder, listIndx + 1 ); + } + // convert submesh ids into interface instances + // and dump command into python + convertMeshOrder( anOrder, aResult, false ); + + return aResult._retn(); +} + +//============================================================================= +/*! + * \brief find common submeshes with given submesh + * \param theSubMeshList list of already collected submesh to check + * \param theSubMesh given submesh to intersect with other + * \param theCommonSubMeshes collected common submeshes + */ +//============================================================================= + +static void findCommonSubMesh (list& theSubMeshList, + const SMESH_subMesh* theSubMesh, + set& theCommon ) +{ + if ( !theSubMesh ) + return; + list::const_iterator it = theSubMeshList.begin(); + for ( ; it != theSubMeshList.end(); it++ ) + theSubMesh->FindIntersection( *it, theCommon ); + theSubMeshList.push_back( theSubMesh ); + //theCommon.insert( theSubMesh ); +} + +//============================================================================= +/*! + * \brief Set submesh object order + * \param theSubMeshArray submesh array order + */ +//============================================================================= + +::CORBA::Boolean SMESH_Mesh_i::SetMeshOrder(const SMESH::submesh_array_array& theSubMeshArray) +{ + if ( _preMeshInfo ) + _preMeshInfo->ForgetOrLoad(); + + bool res = false; + ::SMESH_Mesh& mesh = GetImpl(); + + TPythonDump aPythonDump; // prevent dump of called methods + aPythonDump << "isDone = " << _this() << ".SetMeshOrder( [ "; + + TListOfListOfInt subMeshOrder; + for ( int i = 0, n = theSubMeshArray.length(); i < n; i++ ) + { + const SMESH::submesh_array& aSMArray = theSubMeshArray[i]; + TListOfInt subMeshIds; + aPythonDump << "[ "; + // Collect subMeshes which should be clear + // do it list-by-list, because modification of submesh order + // take effect between concurrent submeshes only + set subMeshToClear; + list subMeshList; + for ( int j = 0, jn = aSMArray.length(); j < jn; j++ ) + { + const SMESH::SMESH_subMesh_var subMesh = SMESH::SMESH_subMesh::_duplicate(aSMArray[j]); + if ( j > 0 ) + aPythonDump << ", "; + aPythonDump << subMesh; + subMeshIds.push_back( subMesh->GetId() ); + // detect common parts of submeshes + if ( _mapSubMesh.find(subMesh->GetId()) != _mapSubMesh.end() ) + findCommonSubMesh( subMeshList, _mapSubMesh[ subMesh->GetId() ], subMeshToClear ); + } + aPythonDump << " ]"; + subMeshOrder.push_back( subMeshIds ); + + // clear collected submeshes + set::iterator clrIt = subMeshToClear.begin(); + for ( ; clrIt != subMeshToClear.end(); clrIt++ ) + if ( SMESH_subMesh* sm = (SMESH_subMesh*)*clrIt ) + sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); + } + aPythonDump << " ])"; + + mesh.SetMeshOrder( subMeshOrder ); + res = true; + + return res; +} + +//============================================================================= +/*! + * \brief Convert submesh ids into submesh interfaces + */ +//============================================================================= + +void SMESH_Mesh_i::convertMeshOrder (const TListOfListOfInt& theIdsOrder, + SMESH::submesh_array_array& theResOrder, + const bool theIsDump) +{ + int nbSet = theIdsOrder.size(); + TPythonDump aPythonDump; // prevent dump of called methods + if ( theIsDump ) + aPythonDump << "[ "; + theResOrder.length(nbSet); + TListOfListOfInt::const_iterator it = theIdsOrder.begin(); + int listIndx = 0; + for( ; it != theIdsOrder.end(); it++ ) { + // translate submesh identificators into submesh objects + // takeing into account real number of concurrent lists + const TListOfInt& aSubOrder = (*it); + if (!aSubOrder.size()) + continue; + if ( theIsDump ) + aPythonDump << "[ "; + // convert shape indeces into interfaces + SMESH::submesh_array_var aResSubSet = new SMESH::submesh_array(); + aResSubSet->length(aSubOrder.size()); + TListOfInt::const_iterator subIt = aSubOrder.begin(); + for( int j = 0; subIt != aSubOrder.end(); subIt++ ) { + if ( _mapSubMeshIor.find(*subIt) == _mapSubMeshIor.end() ) + continue; + SMESH::SMESH_subMesh_var subMesh = + SMESH::SMESH_subMesh::_duplicate( _mapSubMeshIor[*subIt] ); + if ( theIsDump ) { + if ( j > 0 ) + aPythonDump << ", "; + aPythonDump << subMesh; + } + aResSubSet[ j++ ] = subMesh; + } + if ( theIsDump ) + aPythonDump << " ]"; + theResOrder[ listIndx++ ] = aResSubSet; + } + // correct number of lists + theResOrder.length( listIndx ); + + if ( theIsDump ) { + // finilise python dump + aPythonDump << " ]"; + aPythonDump << " = " << _this() << ".GetMeshOrder()"; + } +} + +//================================================================================ +// +// Implementation of SMESH_MeshPartDS +// +SMESH_MeshPartDS::SMESH_MeshPartDS(SMESH::SMESH_IDSource_ptr meshPart): + SMESHDS_Mesh( /*meshID=*/-1, /*isEmbeddedMode=*/true) +{ + SMESH::SMESH_Mesh_var mesh = meshPart->GetMesh(); + SMESH_Mesh_i* mesh_i = SMESH::DownCast( mesh ); + + _meshDS = mesh_i->GetImpl().GetMeshDS(); + + SetPersistentId( _meshDS->GetPersistentId() ); + + if ( mesh_i == SMESH::DownCast( meshPart )) + { + // is the whole mesh + myInfo = _meshDS->GetMeshInfo(); // copy mesh info; + // copy groups + set& myGroupSet = const_cast&>( GetGroups() ); + myGroupSet = _meshDS->GetGroups(); + } + else + { + TMeshInfo tmpInfo; + SMESH::long_array_var anIDs = meshPart->GetIDs(); + SMESH::array_of_ElementType_var types = meshPart->GetTypes(); + if ( types->length() == 1 && types[0] == SMESH::NODE ) // group of nodes + { + for (int i=0; i < anIDs->length(); i++) + if ( const SMDS_MeshNode * n = _meshDS->FindNode(anIDs[i])) + if ( _elements[ SMDSAbs_Node ].insert( n ).second ) + tmpInfo.Add( n ); + } + else + { + for (int i=0; i < anIDs->length(); i++) + if ( const SMDS_MeshElement * e = _meshDS->FindElement(anIDs[i])) + if ( _elements[ e->GetType() ].insert( e ).second ) + { + tmpInfo.Add( e ); + SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + while ( nIt->more() ) + { + const SMDS_MeshNode * n = (const SMDS_MeshNode*) nIt->next(); + if ( _elements[ SMDSAbs_Node ].insert( n ).second ) + tmpInfo.Add( n ); + } + } + } + myInfo = tmpInfo; + + _meshDS = 0; // to enforce iteration on _elements and _nodes + } +} +// ------------------------------------------------------------------------------------- +SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementGeomIterator(SMDSAbs_GeometryType geomType) const +{ + if ( _meshDS ) return _meshDS->elementGeomIterator( geomType ); + + typedef SMDS_SetIterator + , + SMDS_MeshElement::GeomFilter + > TIter; + + SMDSAbs_ElementType type = SMDS_MeshCell::toSmdsType( geomType ); + + return SMDS_ElemIteratorPtr( new TIter( _elements[type].begin(), + _elements[type].end(), + SMDS_MeshElement::GeomFilter( geomType ))); +} +// ------------------------------------------------------------------------------------- +SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementEntityIterator(SMDSAbs_EntityType entity) const +{ + if ( _meshDS ) return _meshDS->elementEntityIterator( entity ); + + typedef SMDS_SetIterator + , + SMDS_MeshElement::EntityFilter + > TIter; + + SMDSAbs_ElementType type = SMDS_MeshCell::toSmdsType( entity ); + + return SMDS_ElemIteratorPtr( new TIter( _elements[type].begin(), + _elements[type].end(), + SMDS_MeshElement::EntityFilter( entity ))); +} +// ------------------------------------------------------------------------------------- +SMDS_ElemIteratorPtr SMESH_MeshPartDS::elementsIterator(SMDSAbs_ElementType type) const +{ + typedef SMDS_SetIterator TIter; + if ( type == SMDSAbs_All && !_meshDS ) + { + typedef vector< SMDS_ElemIteratorPtr > TIterVec; + TIterVec iterVec; + for ( int i = 0; i < SMDSAbs_NbElementTypes; ++i ) + if ( !_elements[i].empty() && i != SMDSAbs_Node ) + iterVec.push_back + ( SMDS_ElemIteratorPtr( new TIter( _elements[i].begin(), _elements[i].end() ))); + + typedef SMDS_IteratorOnIterators TIterOnIters; + return SMDS_ElemIteratorPtr( new TIterOnIters( iterVec )); + } + return _meshDS ? _meshDS->elementsIterator(type) : SMDS_ElemIteratorPtr + ( new TIter( _elements[type].begin(), _elements[type].end() )); +} +// ------------------------------------------------------------------------------------- +#define _GET_ITER_DEFINE( iterType, methName, elem, elemType) \ + iterType SMESH_MeshPartDS::methName( bool idInceasingOrder) const \ + { \ + typedef SMDS_SetIterator TIter; \ + return _meshDS ? _meshDS->methName(idInceasingOrder) : iterType \ + ( new TIter( _elements[elemType].begin(), _elements[elemType].end() )); \ + } +// ------------------------------------------------------------------------------------- +_GET_ITER_DEFINE( SMDS_NodeIteratorPtr, nodesIterator, SMDS_MeshNode, SMDSAbs_Node ) +_GET_ITER_DEFINE( SMDS_EdgeIteratorPtr, edgesIterator, SMDS_MeshEdge, SMDSAbs_Edge ) +_GET_ITER_DEFINE( SMDS_FaceIteratorPtr, facesIterator, SMDS_MeshFace, SMDSAbs_Face ) +_GET_ITER_DEFINE( SMDS_VolumeIteratorPtr, volumesIterator, SMDS_MeshVolume, SMDSAbs_Volume) +#undef _GET_ITER_DEFINE +// +// END Implementation of SMESH_MeshPartDS +// +//================================================================================ diff --git a/src/SMESH_I/SMESH_Mesh_i.hxx b/src/SMESH_I/SMESH_Mesh_i.hxx index 32fe2153d..3c13916a4 100644 --- a/src/SMESH_I/SMESH_Mesh_i.hxx +++ b/src/SMESH_I/SMESH_Mesh_i.hxx @@ -1,29 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Mesh_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// + #ifndef _SMESH_MESH_I_HXX_ #define _SMESH_MESH_I_HXX_ @@ -46,6 +45,7 @@ class SMESH_Gen_i; class SMESH_GroupBase_i; class SMESH_subMesh_i; +class SMESH_PreMeshInfo; #include @@ -58,7 +58,7 @@ class SMESH_I_EXPORT SMESH_Mesh_i: public: SMESH_Mesh_i( PortableServer::POA_ptr thePOA, SMESH_Gen_i* myGen_i, - CORBA::Long studyId ); + CORBA::Long studyId ); virtual ~SMESH_Mesh_i(); @@ -72,6 +72,12 @@ public: GEOM::GEOM_Object_ptr GetShapeToMesh() throw (SALOME::SALOME_Exception); + CORBA::Boolean IsLoaded() + throw (SALOME::SALOME_Exception); + + void Load() + throw (SALOME::SALOME_Exception); + void Clear() throw (SALOME::SALOME_Exception); @@ -89,6 +95,9 @@ public: SMESH::ListOfHypothesis* GetHypothesisList(GEOM::GEOM_Object_ptr aSubShapeObject) throw (SALOME::SALOME_Exception); + SMESH::submesh_array* GetSubMeshes() + throw (SALOME::SALOME_Exception); + SMESH::SMESH_subMesh_ptr GetSubMesh(GEOM::GEOM_Object_ptr aSubShapeObject, const char* theName) throw (SALOME::SALOME_Exception); @@ -97,59 +106,64 @@ public: SMESH::SMESH_Group_ptr CreateGroup( SMESH::ElementType theElemType, const char* theName ) throw (SALOME::SALOME_Exception); - + SMESH::SMESH_GroupOnGeom_ptr CreateGroupFromGEOM(SMESH::ElementType theElemType, const char* theName, GEOM::GEOM_Object_ptr theGeomObj ) throw (SALOME::SALOME_Exception); + SMESH::SMESH_GroupOnFilter_ptr CreateGroupFromFilter(SMESH::ElementType theElemType, + const char* theName, + SMESH::Filter_ptr theFilter ) + throw (SALOME::SALOME_Exception); + void RemoveGroup( SMESH::SMESH_GroupBase_ptr theGroup ) throw (SALOME::SALOME_Exception); - + void RemoveGroupWithContents( SMESH::SMESH_GroupBase_ptr theGroup ) throw (SALOME::SALOME_Exception); - + SMESH::ListOfGroups* GetGroups() throw (SALOME::SALOME_Exception); CORBA::Long NbGroups() throw (SALOME::SALOME_Exception); - SMESH::SMESH_Group_ptr UnionGroups( SMESH::SMESH_GroupBase_ptr theGroup1, - SMESH::SMESH_GroupBase_ptr theGroup2, + SMESH::SMESH_Group_ptr UnionGroups( SMESH::SMESH_GroupBase_ptr theGroup1, + SMESH::SMESH_GroupBase_ptr theGroup2, const char* theName ) throw (SALOME::SALOME_Exception); - SMESH::SMESH_Group_ptr UnionListOfGroups( const SMESH::ListOfGroups& theGroups, + SMESH::SMESH_Group_ptr UnionListOfGroups( const SMESH::ListOfGroups& theGroups, const char* theName) throw (SALOME::SALOME_Exception); - - SMESH::SMESH_Group_ptr IntersectGroups( SMESH::SMESH_GroupBase_ptr theGroup1, - SMESH::SMESH_GroupBase_ptr theGroup2, + + SMESH::SMESH_Group_ptr IntersectGroups( SMESH::SMESH_GroupBase_ptr theGroup1, + SMESH::SMESH_GroupBase_ptr theGroup2, const char* theName ) throw (SALOME::SALOME_Exception); - SMESH::SMESH_Group_ptr IntersectListOfGroups( const SMESH::ListOfGroups& theGroups, + SMESH::SMESH_Group_ptr IntersectListOfGroups( const SMESH::ListOfGroups& theGroups, const char* theName ) throw (SALOME::SALOME_Exception); - - SMESH::SMESH_Group_ptr CutGroups( SMESH::SMESH_GroupBase_ptr theGroup1, - SMESH::SMESH_GroupBase_ptr theGroup2, + + SMESH::SMESH_Group_ptr CutGroups( SMESH::SMESH_GroupBase_ptr theGroup1, + SMESH::SMESH_GroupBase_ptr theGroup2, const char* theName ) throw (SALOME::SALOME_Exception); - SMESH::SMESH_Group_ptr CutListOfGroups( const SMESH::ListOfGroups& theMainGroups, - const SMESH::ListOfGroups& theToolGroups, + SMESH::SMESH_Group_ptr CutListOfGroups( const SMESH::ListOfGroups& theMainGroups, + const SMESH::ListOfGroups& theToolGroups, const char* theName ) throw (SALOME::SALOME_Exception); - SMESH::SMESH_Group_ptr CreateDimGroup( const SMESH::ListOfGroups& theGroups, - SMESH::ElementType theElemType, + SMESH::SMESH_Group_ptr CreateDimGroup( const SMESH::ListOfGroups& theGroups, + SMESH::ElementType theElemType, const char* theName ) throw (SALOME::SALOME_Exception); - - SMESH::SMESH_Group_ptr ConvertToStandalone( SMESH::SMESH_GroupOnGeom_ptr theGeomGroup ); + + SMESH::SMESH_Group_ptr ConvertToStandalone( SMESH::SMESH_GroupBase_ptr theGroupOn ); // SMESH::string_array* GetLog(CORBA::Boolean clearAfterGet) // throw (SALOME::SALOME_Exception); @@ -161,6 +175,8 @@ public: SMESH::SMESH_MeshEditor_ptr GetMeshEditPreviewer(); + CORBA::Boolean HasModificationsToDiscard() throw (SALOME::SALOME_Exception); + void ClearLog() throw (SALOME::SALOME_Exception); @@ -188,6 +204,11 @@ public: SMESH::DriverMED_ReadStatus ImportMEDFile( const char* theFileName, const char* theMeshName ) throw (SALOME::SALOME_Exception); + SMESH::DriverMED_ReadStatus ImportCGNSFile( const char* theFileName, + const int theMeshIndex, + std::string& theMeshName) + throw (SALOME::SALOME_Exception); + /*! * Auto color */ @@ -206,17 +227,37 @@ public: */ char* GetVersionString(SMESH::MED_VERSION version, CORBA::Short nbDigits); - void ExportToMED( const char* file, CORBA::Boolean auto_groups, SMESH::MED_VERSION theVersion ) - throw (SALOME::SALOME_Exception); - void ExportMED( const char* file, CORBA::Boolean auto_groups ) - throw (SALOME::SALOME_Exception); - - void ExportDAT( const char* file ) - throw (SALOME::SALOME_Exception); - void ExportUNV( const char* file ) - throw (SALOME::SALOME_Exception); - void ExportSTL( const char* file, bool isascii ) - throw (SALOME::SALOME_Exception); + void ExportToMEDX( const char* file, + CORBA::Boolean auto_groups, + SMESH::MED_VERSION version, + CORBA::Boolean overwrite ) throw (SALOME::SALOME_Exception); + void ExportToMED ( const char* file, + CORBA::Boolean auto_groups, + SMESH::MED_VERSION version ) throw (SALOME::SALOME_Exception); + void ExportMED ( const char* file, + CORBA::Boolean auto_groups ) throw (SALOME::SALOME_Exception); + + void ExportSAUV( const char* file, CORBA::Boolean auto_groups ) throw (SALOME::SALOME_Exception); + + void ExportDAT( const char* file ) throw (SALOME::SALOME_Exception); + void ExportUNV( const char* file ) throw (SALOME::SALOME_Exception); + void ExportSTL( const char* file, bool isascii ) throw (SALOME::SALOME_Exception); + void ExportCGNS(SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + CORBA::Boolean overwrite) throw (SALOME::SALOME_Exception); + + void ExportPartToMED(SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + CORBA::Boolean auto_groups, + SMESH::MED_VERSION version, + CORBA::Boolean overwrite) throw (SALOME::SALOME_Exception); + void ExportPartToDAT(SMESH::SMESH_IDSource_ptr meshPart, + const char* file) throw (SALOME::SALOME_Exception); + void ExportPartToUNV(SMESH::SMESH_IDSource_ptr meshPart, + const char* file) throw (SALOME::SALOME_Exception); + void ExportPartToSTL(SMESH::SMESH_IDSource_ptr meshPart, + const char* file, + CORBA::Boolean isascii) throw (SALOME::SALOME_Exception); SALOME_MED::MESH_ptr GetMEDMesh() throw (SALOME::SALOME_Exception); @@ -227,6 +268,12 @@ public: CORBA::Long NbElements() throw (SALOME::SALOME_Exception); + CORBA::Long Nb0DElements() + throw (SALOME::SALOME_Exception); + + CORBA::Long NbBalls() + throw (SALOME::SALOME_Exception); + CORBA::Long NbEdges() throw (SALOME::SALOME_Exception); @@ -251,6 +298,9 @@ public: CORBA::Long NbQuadranglesOfOrder(SMESH::ElementOrder order) throw (SALOME::SALOME_Exception); + CORBA::Long NbBiQuadQuadrangles() + throw (SALOME::SALOME_Exception); + CORBA::Long NbPolygons() throw (SALOME::SALOME_Exception); @@ -272,6 +322,9 @@ public: CORBA::Long NbHexasOfOrder(SMESH::ElementOrder order) throw (SALOME::SALOME_Exception); + CORBA::Long NbTriQuadraticHexas() + throw (SALOME::SALOME_Exception); + CORBA::Long NbPyramids() throw (SALOME::SALOME_Exception); @@ -284,6 +337,9 @@ public: CORBA::Long NbPrismsOfOrder(SMESH::ElementOrder order) throw (SALOME::SALOME_Exception); + CORBA::Long NbHexagonalPrisms() + throw (SALOME::SALOME_Exception); + CORBA::Long NbPolyhedrons() throw (SALOME::SALOME_Exception); @@ -295,13 +351,16 @@ public: SMESH::long_array* GetElementsByType( SMESH::ElementType theElemType ) throw (SALOME::SALOME_Exception); - + SMESH::long_array* GetNodesId() throw (SALOME::SALOME_Exception); - + SMESH::ElementType GetElementType( CORBA::Long id, bool iselem ) throw (SALOME::SALOME_Exception); - + + SMESH::EntityType GetElementGeomType( CORBA::Long id ) + throw (SALOME::SALOME_Exception); + /*! * Returns ID of elements for given submesh */ @@ -315,15 +374,15 @@ public: */ SMESH::long_array* GetSubMeshNodesId(CORBA::Long ShapeID, CORBA::Boolean all) throw (SALOME::SALOME_Exception); - + /*! * Returns type of elements for given submesh */ SMESH::ElementType GetSubMeshElementType(CORBA::Long ShapeID) throw (SALOME::SALOME_Exception); - + char* Dump(); - + // Internal methods not available through CORBA // They are called by corresponding interface methods SMESH_Hypothesis::Hypothesis_Status addHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, @@ -331,22 +390,23 @@ public: SMESH_Hypothesis::Hypothesis_Status removeHypothesis(GEOM::GEOM_Object_ptr aSubShapeObject, SMESH::SMESH_Hypothesis_ptr anHyp); - + static SMESH::Hypothesis_Status ConvertHypothesisStatus (SMESH_Hypothesis::Hypothesis_Status theStatus); - static void PrepareForWriting (const char* file); + static void PrepareForWriting (const char* file, bool overwrite = true); //int importMEDFile( const char* theFileName, const char* theMeshName ); SMESH::SMESH_subMesh_ptr createSubMesh( GEOM::GEOM_Object_ptr theSubShapeObject ); - void removeSubMesh(SMESH::SMESH_subMesh_ptr theSubMesh, + bool removeSubMesh(SMESH::SMESH_subMesh_ptr theSubMesh, GEOM::GEOM_Object_ptr theSubShapeObject ); - SMESH::SMESH_GroupBase_ptr createGroup(SMESH::ElementType theElemType, - const char* theName, - const TopoDS_Shape& theShape = TopoDS_Shape()); + SMESH::SMESH_GroupBase_ptr createGroup(SMESH::ElementType theElemType, + const char* theName, + const TopoDS_Shape& theShape = TopoDS_Shape(), + const SMESH_PredicatePtr& thePred = SMESH_PredicatePtr()); void removeGroup( const int theId ); @@ -356,15 +416,18 @@ public: const std::map& getGroups() { return _mapGroups; } // return an existing group object. + void onHypothesisModified(); + // callback from _impl to forget not loaded mesh data (issue 0021208) + + void checkMeshLoaded(); + /*! * \brief Update hypotheses assigned to geom groups if the latter change - * + * * NPAL16168: "geometrical group edition from a submesh don't modifiy mesh computation" */ void CheckGeomGroupModif(); - virtual SMESH::long_array* GetIDs(); - CORBA::LongLong GetMeshPtr(); /*! @@ -382,7 +445,7 @@ public: * If there is not node for given ID - returns empty list */ SMESH::double_array* GetNodeXYZ(CORBA::Long id); - + /*! * For given node returns list of IDs of inverse elements * If there is not node for given ID - returns empty list @@ -401,7 +464,7 @@ public: CORBA::Long GetShapeID(CORBA::Long id); /*! - * For given element returns ID of result shape after + * For given element returns ID of result shape after * ::FindShape() from SMESH_MeshEditor * If there is not element for given ID - returns -1 */ @@ -424,40 +487,54 @@ public: * If there is not node for given index - returns -2 */ CORBA::Long GetElemNode(CORBA::Long id, CORBA::Long index); - + /*! * Returns true if given node is medium node * in given quadratic element */ CORBA::Boolean IsMediumNode(CORBA::Long ide, CORBA::Long idn); - + /*! * Returns true if given node is medium node * in one of quadratic elements */ CORBA::Boolean IsMediumNodeOfAnyElem(CORBA::Long idn, SMESH::ElementType theElemType); - + /*! * Returns number of edges for given element */ CORBA::Long ElemNbEdges(CORBA::Long id); - + /*! * Returns number of faces for given element */ CORBA::Long ElemNbFaces(CORBA::Long id); - + /*! + * Returns nodes of given face (counted from zero) for given element. + */ + SMESH::long_array* GetElemFaceNodes(CORBA::Long elemId, CORBA::Short faceIndex); + + /*! + * Returns an element based on all given nodes. + */ + CORBA::Long FindElementByNodes(const SMESH::long_array& nodes); + /*! * Returns true if given element is polygon */ CORBA::Boolean IsPoly(CORBA::Long id); - + /*! * Returns true if given element is quadratic */ CORBA::Boolean IsQuadratic(CORBA::Long id); - + + /*! + * Returns diameter of ball discrete element or zero in case of an invalid \a id + */ + CORBA::Double GetBallDiameter(CORBA::Long id); + /*! * Returns bary center for given element */ @@ -472,7 +549,7 @@ public: * Sets list of notebook variables used for Mesh operations separated by ":" symbol */ void SetParameters (const char* theParameters); - + /*! * Returns list of notebook variables used for Mesh operations separated by ":" symbol */ @@ -482,28 +559,108 @@ public: * Returns list of notebook variables used for last Mesh operation */ SMESH::string_array* GetLastParameters(); - + + /*! + * Collect statistic of mesh elements given by iterator + */ + static void CollectMeshInfo(const SMDS_ElemIteratorPtr theItr, + SMESH::long_array& theInfo); + + /*! + * \brief Return submesh objects list in meshing order + */ + virtual SMESH::submesh_array_array* GetMeshOrder(); + /*! + * \brief Set submesh object order + */ + virtual ::CORBA::Boolean SetMeshOrder(const SMESH::submesh_array_array& theSubMeshArray); + + + // ========================= + // SMESH_IDSource interface + // ========================= + + virtual SMESH::long_array* GetIDs(); + /*! + * Returns statistic of mesh elements + * Result array of number enityties + * Inherited from SMESH_IDSource + */ + virtual SMESH::long_array* GetMeshInfo(); + /*! + * Returns types of elements it contains + */ + virtual SMESH::array_of_ElementType* GetTypes(); + /*! + * Returns self + */ + virtual SMESH::SMESH_Mesh_ptr GetMesh(); + /*! + * Returns false if GetMeshInfo() returns incorrect information that may + * happen if mesh data is not yet fully loaded from the file of study. + */ + bool IsMeshInfoCorrect(); + std::map _mapSubMesh_i; //NRI std::map _mapSubMesh; //NRI private: + std::string prepareMeshNameAndGroups( const char* file, CORBA::Boolean overwrite ); + /*! * Check and correct names of mesh groups */ void checkGroupNames(); + /*! + * Convert submesh ids into submesh interfaces + */ + void convertMeshOrder(const TListOfListOfInt& theIdsOrder, + SMESH::submesh_array_array& theSubMeshOrder, + const bool theIsDump); + private: - static int myIdGenerator; - ::SMESH_Mesh* _impl; // :: force no namespace here - SMESH_Gen_i* _gen_i; - int _id; // id given by creator (unique within the creator instance) - int _studyId; + static int _idGenerator; + ::SMESH_Mesh* _impl; // :: force no namespace here + SMESH_Gen_i* _gen_i; + int _id; // id given by creator (unique within the creator instance) + int _studyId; std::map _mapSubMeshIor; std::map _mapGroups; std::map _mapHypo; - SALOME_MED::MedFileInfo_var myFileInfo; + SALOME_MED::MedFileInfo_var _medFileInfo; + SMESH_PreMeshInfo* _preMeshInfo; // mesh info before full loading from study file + + SMESH_PreMeshInfo* & changePreMeshInfo() { return _preMeshInfo; } + friend class SMESH_PreMeshInfo; + +private: + + // Data used to track changes of GEOM groups + struct TGeomGroupData { + // keep study entry but not ior because GEOM_Object actually changes if + // number of items in a group varies (1) <-> (>1) + std::string _groupEntry; + std::set _indices; // indices of group items within group's main shape + CORBA::Object_ptr _smeshObject; // SMESH object depending on GEOM group + }; + std::list _geomGroupData; + + /*! + * Remember GEOM group data + */ + void addGeomGroupData(GEOM::GEOM_Object_ptr theGeomObj, + CORBA::Object_ptr theSmeshObj); + /*! + * Remove GEOM group data relating to removed smesh object + */ + void removeGeomGroupData(CORBA::Object_ptr theSmeshObj); + /*! + * \brief Return new group contents if it has been changed and update group data + */ + TopoDS_Shape newGroupShape( TGeomGroupData & groupData); + }; #endif - diff --git a/src/SMESH_I/SMESH_NoteBook.cxx b/src/SMESH_I/SMESH_NoteBook.cxx index bd7268049..7ecbe0823 100644 --- a/src/SMESH_I/SMESH_NoteBook.cxx +++ b/src/SMESH_I/SMESH_NoteBook.cxx @@ -1,12 +1,11 @@ -// Copyright (C) 2008 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// Copyright (C) 2007-2012 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 +// 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. @@ -17,13 +16,15 @@ // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_NoteBook.cxx // Author : Roman NIKOLAEV - +// #include "SMESH_2smeshpy.hxx" #include "SMESH_NoteBook.hxx" #include "SMESH_Gen_i.hxx" #include "SMESH_PythonDump.hxx" +#include "SMESH_Hypothesis_i.hxx" #include #include @@ -41,14 +42,28 @@ static int MYDEBUG = 0; using namespace std; -void SetVariable(Handle(_pyCommand) theCommand,const ObjectStates* theStates, int position, int theArgNb); +namespace +{ + /*! + * Set variable of the SMESH_ObjectStates from position to the _pyCommand + * method as nbArg argument + */ + void SetVariable(Handle(_pyCommand) theCommand, + const SMESH_ObjectStates* theStates, + int position, int theArgNb) + { + if(theStates->GetCurrectState().size() > position) + if(!theStates->GetCurrectState().at(position).IsEmpty()) + theCommand->SetArg(theArgNb,theStates->GetCurrectState().at(position)); + } +} //================================================================================ /*! * \brief Constructor */ //================================================================================ -ObjectStates::ObjectStates(TCollection_AsciiString theType) +SMESH_ObjectStates::SMESH_ObjectStates(TCollection_AsciiString theType) { _type = theType; _dumpstate = 0; @@ -59,7 +74,7 @@ ObjectStates::ObjectStates(TCollection_AsciiString theType) * \brief Destructor */ //================================================================================ -ObjectStates::~ObjectStates() +SMESH_ObjectStates::~SMESH_ObjectStates() { } @@ -69,7 +84,7 @@ ObjectStates::~ObjectStates() * \param theState - Object state (vector of notebook variable) */ //================================================================================ -void ObjectStates::AddState(const TState &theState) +void SMESH_ObjectStates::AddState(const TState &theState) { _states.push_back(theState); } @@ -80,7 +95,7 @@ void ObjectStates::AddState(const TState &theState) * \\retval state - Object state (vector of notebook variable) */ //================================================================================ -TState ObjectStates::GetCurrectState() const +TState SMESH_ObjectStates::GetCurrectState() const { if(_states.size() > _dumpstate) return _states[_dumpstate]; @@ -94,7 +109,7 @@ TState ObjectStates::GetCurrectState() const * */ //================================================================================ -TAllStates ObjectStates::GetAllStates() const +TAllStates SMESH_ObjectStates::GetAllStates() const { return _states; } @@ -104,7 +119,7 @@ TAllStates ObjectStates::GetAllStates() const * */ //================================================================================ -void ObjectStates::IncrementState() +void SMESH_ObjectStates::IncrementState() { _dumpstate++; } @@ -114,7 +129,7 @@ void ObjectStates::IncrementState() * */ //================================================================================ -TCollection_AsciiString ObjectStates::GetObjectType() const{ +TCollection_AsciiString SMESH_ObjectStates::GetObjectType() const{ return _type; } @@ -125,7 +140,7 @@ TCollection_AsciiString ObjectStates::GetObjectType() const{ */ //================================================================================ LayerDistributionStates::LayerDistributionStates(): - ObjectStates("LayerDistribution") + SMESH_ObjectStates("LayerDistribution") { } //================================================================================ @@ -229,7 +244,7 @@ void SMESH_NoteBook::ReplaceVariables() cout<<"Object : "<< aObject<GetArg(1)); + aMethod.IsEqual("ApplyToHexahedrons")) + it = _objectMap.find(aCmd->GetArg(1)); } - + if(it != _objectMap.end()) { if(MYDEBUG) - cout << "Found object : " << (*it).first << endl; - ObjectStates *aStates = (*it).second; + cout << "Found object : " << (*it).first << endl; + SMESH_ObjectStates *aStates = (*it).second; // Case for LocalLength hypothesis - if(aStates->GetObjectType().IsEqual("LocalLength") && aStates->GetCurrectState().size() >= 2) { + if(aStates->GetObjectType().IsEqual("LocalLength") && aStates->GetCurrectState().size() >= 2) + { if(aMethod.IsEqual("SetLength")) { if(!aStates->GetCurrectState().at(0).IsEmpty() ) aCmd->SetArg(1,aStates->GetCurrectState().at(0)); @@ -326,57 +342,6 @@ void SMESH_NoteBook::ReplaceVariables() aStates->IncrementState(); } } - - // Case for NETGEN_Parameters_2D or NETGEN_Parameters_2D hypothesis - else if(aStates->GetObjectType().IsEqual("NETGEN_Parameters_2D") || - aStates->GetObjectType().IsEqual("NETGEN_Parameters")){ - if(aMethod == "SetMaxSize" && aStates->GetCurrectState().size() >= 1) { - if(!aStates->GetCurrectState().at(0).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(0)); - aStates->IncrementState(); - } - else if(aMethod == "SetGrowthRate" && aStates->GetCurrectState().size() >= 2) { - if(!aStates->GetCurrectState().at(1).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(1)); - aStates->IncrementState(); - } - else if(aMethod == "SetNbSegPerEdge" && aStates->GetCurrectState().size() >= 3) { - if(!aStates->GetCurrectState().at(2).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(2)); - aStates->IncrementState(); - } - else if(aMethod == "SetNbSegPerRadius" && aStates->GetCurrectState().size() >= 4) { - if(!aStates->GetCurrectState().at(3).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(3)); - aStates->IncrementState(); - } - } - - // Case for NETGEN_SimpleParameters_3D or NETGEN_SimpleParameters_2D hypothesis - else if(aStates->GetObjectType().IsEqual("NETGEN_SimpleParameters_3D") || - aStates->GetObjectType().IsEqual("NETGEN_SimpleParameters_2D")){ - - if((aMethod == "SetNumberOfSegments" || aMethod == "SetLocalLength") && - aStates->GetCurrectState().size() >= 1) { - if(!aStates->GetCurrectState().at(0).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(0)); - aStates->IncrementState(); - } - else if(aMethod == "SetMaxElementArea" && aStates->GetCurrectState().size() >= 2) { - if(!aStates->GetCurrectState().at(1).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(1)); - aStates->IncrementState(); - } - else if(aMethod == "SetMaxElementVolume" && aStates->GetCurrectState().size() >= 3) { - if(!aStates->GetCurrectState().at(2).IsEmpty() ) - aCmd->SetArg(1,aStates->GetCurrectState().at(2)); - aStates->IncrementState(); - } - else if(aMethod == "LengthFromEdges" || aMethod == "LengthFromFaces"){ - aStates->IncrementState(); - } - } - // Case for NumberOfLayers hypothesis else if(aStates->GetObjectType().IsEqual("NumberOfLayers")){ if(aMethod == "SetNumberOfLayers" && aStates->GetCurrectState().size() >= 1) { @@ -402,9 +367,9 @@ void SMESH_NoteBook::ReplaceVariables() } else if(aStates->GetObjectType().IsEqual("Mesh")) { - TState aCurrentState = aStates->GetCurrectState(); + TState aCurrentState = aStates->GetCurrectState(); int aCurrentStateSize = aCurrentState.size(); - if(aMethod.IsEqual("Translate") || + if(aMethod.IsEqual("Translate") || aMethod.IsEqual("TranslateMakeGroups") || aMethod.IsEqual("TranslateMakeMesh") || aMethod.IsEqual("TranslateObject") || @@ -419,211 +384,284 @@ void SMESH_NoteBook::ReplaceVariables() } } if(anArgIndex > 0) { - if(aCurrentStateSize == 3) { // translation by dx, dy, dz - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) { - isVariableFound = true; - aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); - } - } - } - else if(aCurrentStateSize == 6) { // translation by x1, x2, y1, y2, z1, z2 - // TODO: limitation until operations on the variables will be introduced - /* - isVariableFound = true; - for(int j = 0; j < 3; j++) { - TCollection_AsciiString anArg = aCmd->GetArg(anArgIndex+j); - TCollection_AsciiString aValue1 = aCurrentState.at(2*j), aValue2 = aCurrentState.at(2*j+1); - bool aV1 = !aValue1.IsEmpty(); - bool aV2 = !aValue2.IsEmpty(); - double aValue, aCurrentValue = anArg.IsRealValue() ? anArg.RealValue() : 0; - if(aV1 && !aV2) { - if(!GetReal(aValue1, aValue)) - aValue = 0; - aValue2 = TCollection_AsciiString( aValue + aCurrentValue ); - } - else if(!aV1 && aV2) { - if(!GetReal(aValue2, aValue)) - aValue = 0; - aValue1 = TCollection_AsciiString( aValue - aCurrentValue ); - } - else if(!aV1 && !aV2) { - aValue1 = TCollection_AsciiString( 0 ); - aValue2 = TCollection_AsciiString( aCurrentValue ); - } - aCmd->SetArg(anArgIndex+j, aValue1 + ", " + aValue2 ); - } - */ - } + if(aCurrentStateSize == 3) { // translation by dx, dy, dz + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) { + isVariableFound = true; + aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); + } + } + } + else if(aCurrentStateSize == 6) { // translation by x1, x2, y1, y2, z1, z2 + // TODO: limitation until operations on the variables will be introduced + /* + isVariableFound = true; + for(int j = 0; j < 3; j++) { + TCollection_AsciiString anArg = aCmd->GetArg(anArgIndex+j); + TCollection_AsciiString aValue1 = aCurrentState.at(2*j), aValue2 = aCurrentState.at(2*j+1); + bool aV1 = !aValue1.IsEmpty(); + bool aV2 = !aValue2.IsEmpty(); + double aValue, aCurrentValue = anArg.IsRealValue() ? anArg.RealValue() : 0; + if(aV1 && !aV2) { + if(!GetReal(aValue1, aValue)) + aValue = 0; + aValue2 = TCollection_AsciiString( aValue + aCurrentValue ); + } + else if(!aV1 && aV2) { + if(!GetReal(aValue2, aValue)) + aValue = 0; + aValue1 = TCollection_AsciiString( aValue - aCurrentValue ); + } + else if(!aV1 && !aV2) { + aValue1 = TCollection_AsciiString( 0 ); + aValue2 = TCollection_AsciiString( aCurrentValue ); + } + aCmd->SetArg(anArgIndex+j, aValue1 + ", " + aValue2 ); + } + */ + } } if(isVariableFound) { TCollection_AsciiString aDim; - if(aCurrentStateSize == 6) - aDim = "6"; + if(aCurrentStateSize == 6) + aDim = "6"; aCmd->SetArg(anArgIndex - 1, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".PointStructStr"+aDim); aCmd->SetArg(anArgIndex - 2, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".DirStructStr"); } aStates->IncrementState(); } - else if(aMethod.IsEqual("Rotate") || - aMethod.IsEqual("RotateMakeGroups") || - aMethod.IsEqual("RotateMakeMesh") || + else if(aMethod.IsEqual("Rotate") || + aMethod.IsEqual("RotateMakeGroups") || + aMethod.IsEqual("RotateMakeMesh") || aMethod.IsEqual("RotateObject") || aMethod.IsEqual("RotateObjectMakeGroups") || aMethod.IsEqual("RotateObjectMakeMesh") || - aMethod.IsEqual("RotationSweep") || - aMethod.IsEqual("RotationSweepObject") || - aMethod.IsEqual("RotationSweepObject1D") || - aMethod.IsEqual("RotationSweepObject2D") || - aMethod.IsEqual("RotationSweepMakeGroups") || - aMethod.IsEqual("RotationSweepObjectMakeGroups") || - aMethod.IsEqual("RotationSweepObject1DMakeGroups") || - aMethod.IsEqual("RotationSweepObject2DMakeGroups") || - aMethod.IsEqual("Mirror") || - aMethod.IsEqual("MirrorMakeMesh") || + aMethod.IsEqual("RotationSweep") || + aMethod.IsEqual("RotationSweepObject") || + aMethod.IsEqual("RotationSweepObject1D") || + aMethod.IsEqual("RotationSweepObject2D") || + aMethod.IsEqual("RotationSweepMakeGroups") || + aMethod.IsEqual("RotationSweepObjectMakeGroups") || + aMethod.IsEqual("RotationSweepObject1DMakeGroups") || + aMethod.IsEqual("RotationSweepObject2DMakeGroups") || + aMethod.IsEqual("Mirror") || + aMethod.IsEqual("MirrorMakeMesh") || aMethod.IsEqual("MirrorMakeGroups") || aMethod.IsEqual("MirrorObject") || aMethod.IsEqual("MirrorObjectMakeMesh") || aMethod.IsEqual("MirrorObjectMakeGroups")) { - bool isSubstitute = false; - int anArgIndex = 0; - for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { - if(aCmd->GetArg(i).IsEqual("SMESH.AxisStruct")) { - anArgIndex = i+1; - break; - } - } - if(anArgIndex > 0) { - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) { - if(j < 6) // 0-5 - axis struct, 6 - angle (rotation & sweep), 7-8 - nbSteps and tolerance (sweep) - isSubstitute = true; - aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); - } - } - } - if(isSubstitute) - aCmd->SetArg(anArgIndex - 1, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".AxisStructStr"); - aStates->IncrementState(); - } - else if(aMethod.IsEqual("AddNode") || - aMethod.IsEqual("MoveClosestNodeToPoint")) { - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) - aCmd->SetArg(j+1, aCurrentState.at(j)); - } - aStates->IncrementState(); - } - else if(aMethod.IsEqual("MoveNode")) { - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) - aCmd->SetArg(j+2, aCurrentState.at(j)); - } - aStates->IncrementState(); - } - else if(aMethod.IsEqual("ExtrusionSweep") || - aMethod.IsEqual("ExtrusionSweepObject") || - aMethod.IsEqual("ExtrusionSweepObject1D") || - aMethod.IsEqual("ExtrusionSweepObject2D") || - aMethod.IsEqual("ExtrusionSweepMakeGroups") || - aMethod.IsEqual("ExtrusionSweepObjectMakeGroups") || - aMethod.IsEqual("ExtrusionSweepObject1DMakeGroups") || - aMethod.IsEqual("ExtrusionSweepObject2DMakeGroups")) { - bool isSubstitute = false; - int anArgIndex = 0; - for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { - if(aCmd->GetArg(i).IsEqual("SMESH.PointStruct")) { - anArgIndex = i+1; - break; - } - } - if(anArgIndex > 0) { - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) { - if(j < 3) // 0-2 - dir struct, 3 - number of steps - isSubstitute = true; - aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); - } - } - } - if(isSubstitute) { + bool isSubstitute = false; + int anArgIndex = 0; + for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { + if(aCmd->GetArg(i).IsEqual("SMESH.AxisStruct")) { + anArgIndex = i+1; + break; + } + } + if(anArgIndex > 0) { + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) { + if(j < 6) // 0-5 - axis struct, 6 - angle (rotation & sweep), 7-8 - nbSteps and tolerance (sweep) + isSubstitute = true; + aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); + } + } + } + if(isSubstitute) + aCmd->SetArg(anArgIndex - 1, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".AxisStructStr"); + aStates->IncrementState(); + } + else if(aMethod.IsEqual("AddNode") || + aMethod.IsEqual("MoveClosestNodeToPoint")) { + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) + aCmd->SetArg(j+1, aCurrentState.at(j)); + } + aStates->IncrementState(); + } + else if(aMethod.IsEqual("MoveNode")) { + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) + aCmd->SetArg(j+2, aCurrentState.at(j)); + } + aStates->IncrementState(); + } + else if(aMethod.IsEqual("ExtrusionSweep") || + aMethod.IsEqual("ExtrusionSweepObject") || + aMethod.IsEqual("ExtrusionSweepObject1D") || + aMethod.IsEqual("ExtrusionSweepObject2D") || + aMethod.IsEqual("ExtrusionSweepMakeGroups") || + aMethod.IsEqual("ExtrusionSweepObjectMakeGroups") || + aMethod.IsEqual("ExtrusionSweepObject1DMakeGroups") || + aMethod.IsEqual("ExtrusionSweepObject2DMakeGroups")) { + bool isSubstitute = false; + int anArgIndex = 0; + for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { + if(aCmd->GetArg(i).IsEqual("SMESH.PointStruct")) { + anArgIndex = i+1; + break; + } + } + if(anArgIndex > 0) { + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) { + if(j < 3) // 0-2 - dir struct, 3 - number of steps + isSubstitute = true; + aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); + } + } + } + if(isSubstitute) { aCmd->SetArg(anArgIndex - 1, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".PointStructStr"); aCmd->SetArg(anArgIndex - 2, TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".DirStructStr"); - } - aStates->IncrementState(); - } - else if(aMethod.IsEqual("ExtrusionAlongPath") || - aMethod.IsEqual("ExtrusionAlongPathObject") || - aMethod.IsEqual("ExtrusionAlongPathObject1D") || - aMethod.IsEqual("ExtrusionAlongPathObject2D") || - aMethod.IsEqual("ExtrusionAlongPathMakeGroups") || - aMethod.IsEqual("ExtrusionAlongPathObjectMakeGroups") || - aMethod.IsEqual("ExtrusionAlongPathObject1DMakeGroups") || - aMethod.IsEqual("ExtrusionAlongPathObject2DMakeGroups") || - /* workaround for a bug in the command parsing algorithm */ - aCmd->GetString().Search("ExtrusionAlongPathMakeGroups") != -1 || - aCmd->GetString().Search("ExtrusionAlongPathObjectMakeGroups") != -1 || - aCmd->GetString().Search("ExtrusionAlongPathObject1DMakeGroups") != -1 || - aCmd->GetString().Search("ExtrusionAlongPathObject2DMakeGroups") != -1 ) { - int aNbAngles = aCurrentStateSize-3; // State looks like "Angle1:...:AngleN:X:Y:Z" - bool isSubstitute = false; - int anArgIndex = 0; - for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { - if(aCmd->GetArg(i).IsEqual("SMESH.PointStruct")) { - anArgIndex = i-1-aNbAngles; - break; - } - } - if(anArgIndex > 0) { - int j = 0; - for(; j < aNbAngles; j++) { - if(!aCurrentState.at(j).IsEmpty()) { - aCmd->SetArg(anArgIndex+j-1, aCurrentState.at(j)); - } - } - for(; j < aNbAngles+3; j++) { - if(!aCurrentState.at(j).IsEmpty()) { - isSubstitute = true; - aCmd->SetArg(anArgIndex+j+2, aCurrentState.at(j)); - } - } - } - if(isSubstitute) - aCmd->SetArg(anArgIndex + aNbAngles + 1, - TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".PointStructStr"); - aStates->IncrementState(); - } - else if(aMethod.IsEqual("TriToQuad") || - aMethod.IsEqual("Concatenate") || - aMethod.IsEqual("ConcatenateWithGroups")) { - if(aCurrentStateSize && !aCurrentState.at(0).IsEmpty()) - aCmd->SetArg(aCmd->GetNbArgs(), aCurrentState.at(0)); - aStates->IncrementState(); - } - else if(aMethod.IsEqual("Smooth") || - aMethod.IsEqual("SmoothObject") || - aMethod.IsEqual("SmoothParametric") || - aMethod.IsEqual("SmoothParametricObject")) { - int anArgIndex = aCmd->GetNbArgs() - 2; - for(int j = 0; j < aCurrentStateSize; j++) { - if(!aCurrentState.at(j).IsEmpty()) - aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); - } - aStates->IncrementState(); - } - else if(aMethod.IsEqual("ApplyToMeshFaces") || - aMethod.IsEqual("ApplyToHexahedrons")) { - int anArgIndex = aCmd->GetNbArgs()-1; - for(int j = 0; j < aCurrentStateSize; j++) - if(!aCurrentState.at(j).IsEmpty()) - aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); - aStates->IncrementState(); - } + } + aStates->IncrementState(); + } + else if(aMethod.IsEqual("ExtrusionAlongPath") || + aMethod.IsEqual("ExtrusionAlongPathObject") || + aMethod.IsEqual("ExtrusionAlongPathObject1D") || + aMethod.IsEqual("ExtrusionAlongPathObject2D") || + aMethod.IsEqual("ExtrusionAlongPathMakeGroups") || + aMethod.IsEqual("ExtrusionAlongPathObjectMakeGroups") || + aMethod.IsEqual("ExtrusionAlongPathObject1DMakeGroups") || + aMethod.IsEqual("ExtrusionAlongPathObject2DMakeGroups") || + /* workaround for a bug in the command parsing algorithm */ + aCmd->GetString().Search("ExtrusionAlongPathMakeGroups") != -1 || + aCmd->GetString().Search("ExtrusionAlongPathObjectMakeGroups") != -1 || + aCmd->GetString().Search("ExtrusionAlongPathObject1DMakeGroups") != -1 || + aCmd->GetString().Search("ExtrusionAlongPathObject2DMakeGroups") != -1 ) { + int aNbAngles = aCurrentStateSize-3; // State looks like "Angle1:...:AngleN:X:Y:Z" + bool isSubstitute = false; + int anArgIndex = 0; + for(int i = 1, n = aCmd->GetNbArgs(); i <= n; i++) { + if(aCmd->GetArg(i).IsEqual("SMESH.PointStruct")) { + anArgIndex = i-1-aNbAngles; + break; + } + } + if(anArgIndex > 0) { + int j = 0; + for(; j < aNbAngles; j++) { + if(!aCurrentState.at(j).IsEmpty()) { + aCmd->SetArg(anArgIndex+j-1, aCurrentState.at(j)); + } + } + for(; j < aNbAngles+3; j++) { + if(!aCurrentState.at(j).IsEmpty()) { + isSubstitute = true; + aCmd->SetArg(anArgIndex+j+2, aCurrentState.at(j)); + } + } + } + if(isSubstitute) + aCmd->SetArg(anArgIndex + aNbAngles + 1, + TCollection_AsciiString(SMESH_2smeshpy::SmeshpyName())+".PointStructStr"); + aStates->IncrementState(); + } + else if(aMethod.IsEqual("TriToQuad") || + aMethod.IsEqual("Concatenate") || + aMethod.IsEqual("ConcatenateWithGroups")) { + if(aCurrentStateSize && !aCurrentState.at(0).IsEmpty()) + aCmd->SetArg(aCmd->GetNbArgs(), aCurrentState.at(0)); + aStates->IncrementState(); + } + else if(aMethod.IsEqual("Smooth") || + aMethod.IsEqual("SmoothObject") || + aMethod.IsEqual("SmoothParametric") || + aMethod.IsEqual("SmoothParametricObject")) { + int anArgIndex = aCmd->GetNbArgs() - 2; + for(int j = 0; j < aCurrentStateSize; j++) { + if(!aCurrentState.at(j).IsEmpty()) + aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); + } + aStates->IncrementState(); + } + else if(aMethod.IsEqual("ApplyToMeshFaces") || + aMethod.IsEqual("ApplyToHexahedrons")) { + int anArgIndex = aCmd->GetNbArgs()-1; + for(int j = 0; j < aCurrentStateSize; j++) + if(!aCurrentState.at(j).IsEmpty()) + aCmd->SetArg(anArgIndex+j, aCurrentState.at(j)); + aStates->IncrementState(); + } + } // if ( aStates->GetObjectType().IsEqual("Mesh")) + + // Case for NETGEN_Parameters_2D or NETGEN_Parameters_2D hypothesis + // else if(aStates->GetObjectType().IsEqual("NETGEN_Parameters_2D") || + // aStates->GetObjectType().IsEqual("NETGEN_Parameters")){ + // if(aMethod == "SetMaxSize" && aStates->GetCurrectState().size() >= 1) { + // if(!aStates->GetCurrectState().at(0).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(0)); + // aStates->IncrementState(); + // } + // else if(aMethod == "SetGrowthRate" && aStates->GetCurrectState().size() >= 2) { + // if(!aStates->GetCurrectState().at(1).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(1)); + // aStates->IncrementState(); + // } + // else if(aMethod == "SetNbSegPerEdge" && aStates->GetCurrectState().size() >= 3) { + // if(!aStates->GetCurrectState().at(2).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(2)); + // aStates->IncrementState(); + // } + // else if(aMethod == "SetNbSegPerRadius" && aStates->GetCurrectState().size() >= 4) { + // if(!aStates->GetCurrectState().at(3).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(3)); + // aStates->IncrementState(); + // } + // } + + // // Case for NETGEN_SimpleParameters_3D or NETGEN_SimpleParameters_2D hypothesis + // else if(aStates->GetObjectType().IsEqual("NETGEN_SimpleParameters_3D") || + // aStates->GetObjectType().IsEqual("NETGEN_SimpleParameters_2D")) { + + // if((aMethod == "SetNumberOfSegments" || aMethod == "SetLocalLength") && + // aStates->GetCurrectState().size() >= 1) { + // if(!aStates->GetCurrectState().at(0).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(0)); + // aStates->IncrementState(); + // } + // else if(aMethod == "SetMaxElementArea" && aStates->GetCurrectState().size() >= 2) { + // if(!aStates->GetCurrectState().at(1).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(1)); + // aStates->IncrementState(); + // } + // else if(aMethod == "SetMaxElementVolume" && aStates->GetCurrectState().size() >= 3) { + // if(!aStates->GetCurrectState().at(2).IsEmpty() ) + // aCmd->SetArg(1,aStates->GetCurrectState().at(2)); + // aStates->IncrementState(); + // } + // else if(aMethod == "LengthFromEdges" || aMethod == "LengthFromFaces"){ + // aStates->IncrementState(); + // } + // } + + else + { + // treat Netgen hypotheses; + // this (and above) code can work wrong since nb of states can differ from nb of + // dumped calls due to the fix of + // issue 0021364:: Dump of netgen parameters has duplicate lines + SMESH_Gen_i *aGen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_ptr aStudy = aGen->GetCurrentStudy(); + SALOMEDS::SObject_var sobj = aStudy->FindObjectID( (*it).first.ToCString() ); + CORBA::Object_var obj = aGen->SObjectToObject( sobj ); + if ( SMESH_Hypothesis_i* h = SMESH::DownCast< SMESH_Hypothesis_i*>( obj )) + { + TState aCurrentState = aStates->GetCurrectState(); + int argIndex = h->getParamIndex( aMethod, aCurrentState.size() ); + if ( 0 <= argIndex && argIndex < aCurrentState.size() && + !aCurrentState[argIndex].IsEmpty() ) + aCmd->SetArg( 1, aCurrentState[argIndex] ); + + if ( argIndex >= 0 ) + aStates->IncrementState(); + } } } else { if(MYDEBUG) - cout << "Object not found" << endl; + cout << "Object not found" << endl; } if(MYDEBUG) { cout<<"Command after: "<< aCmd->GetString()<_is_nil()) { - anObjType = TCollection_AsciiString(aHyp->GetName()); + CORBA::String_var hypName = aHyp->GetName(); + anObjType = hypName.in(); } - else if(SMESH::SMESH_Mesh_var aMesh = SMESH::SMESH_Mesh::_narrow(anObject)) { - anObjType = TCollection_AsciiString("Mesh"); + else if (!aMesh->_is_nil() ) { + anObjType = "Mesh"; } if(MYDEBUG) cout<<"The object Type : "<length(); i++) { TState aVars; SALOMEDS::ListOfStrings aListOfVars = aSections[i]; for(int j = 0;jIsVariable(aVar.ToCString())) { - aVar.InsertBefore(1,"\""); - aVar.InsertAfter(aVar.Length(),"\""); + aVar.InsertBefore(1, SMESH::TVar::Quote() ); + aVar.InsertAfter(aVar.Length(), SMESH::TVar::Quote() ); } aVars.push_back(aVar); if(MYDEBUG) { @@ -697,7 +736,15 @@ void SMESH_NoteBook::InitObjectMap() } aState->AddState(aVars); } - _objectMap.insert(pair(TCollection_AsciiString(aSObject->GetID()),aState)); + if ( aState->GetAllStates().empty() ) + { + delete aState; + } + else + { + CORBA::String_var objID = aSObject->GetID(); + _objectMap.insert( make_pair(TCollection_AsciiString( objID.in() ), aState )); + } } } } @@ -716,7 +763,7 @@ void SMESH_NoteBook::AddCommand(const TCollection_AsciiString& theString) if ( aCommand->GetMethod() == "GetMeshEditor" ) { // MeshEditor creation myMeshEditors.insert( make_pair( aCommand->GetResultValue(), - aCommand->GetObject() ) ); + aCommand->GetObject() ) ); } } @@ -848,14 +895,3 @@ bool SMESH_NoteBook::GetReal(const TCollection_AsciiString& theVarName, double& return ok; } - -/*! - * Set variable of the ObjectStates from position to the _pyCommand - * method as nbArg argument - */ -void SetVariable(Handle(_pyCommand) theCommand, const ObjectStates* theStates, int position, int theArgNb) -{ - if(theStates->GetCurrectState().size() > position) - if(!theStates->GetCurrectState().at(position).IsEmpty()) - theCommand->SetArg(theArgNb,theStates->GetCurrectState().at(position)); -} diff --git a/src/SMESH_I/SMESH_NoteBook.hxx b/src/SMESH_I/SMESH_NoteBook.hxx index dfeb3b50a..661f42f49 100644 --- a/src/SMESH_I/SMESH_NoteBook.hxx +++ b/src/SMESH_I/SMESH_NoteBook.hxx @@ -1,12 +1,11 @@ -// Copyright (C) 2008 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, -// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// Copyright (C) 2007-2012 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 +// 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. @@ -17,13 +16,17 @@ // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_NoteBook.hxx // Author : Roman NIKOLAEV () - - +// #ifndef SMESH_NoteBook_HeaderFile #define SMESH_NoteBook_HeaderFile +// All this stuff is obsolete since issue 0021308: +// "Remove hard-coded dependency of the external mesh plugins from the SMESH module" +// is implemented (Mar 2012). It is kept for backward compatibility only. + #include #include @@ -34,14 +37,14 @@ class _pyCommand; typedef std::vector TState; typedef std::vector TAllStates; -typedef TCollection_AsciiString _pyID; +typedef TCollection_AsciiString _pyID; -class ObjectStates{ +class SMESH_ObjectStates{ public: - ObjectStates(TCollection_AsciiString theType); - virtual ~ObjectStates(); + SMESH_ObjectStates(TCollection_AsciiString theType); + virtual ~SMESH_ObjectStates(); void AddState(const TState &theState); @@ -58,7 +61,7 @@ private: int _dumpstate; }; -class LayerDistributionStates : public ObjectStates +class LayerDistributionStates : public SMESH_ObjectStates { public: typedef std::map TDistributionMap; @@ -81,7 +84,7 @@ private: class SMESH_NoteBook { public: - typedef std::map TVariablesMap; + typedef std::map TVariablesMap; typedef std::map TMeshEditorMap; SMESH_NoteBook(); ~SMESH_NoteBook(); diff --git a/src/SMESH_I/SMESH_Pattern_i.cxx b/src/SMESH_I/SMESH_Pattern_i.cxx index b96a8d954..b1a3d0c35 100644 --- a/src/SMESH_I/SMESH_Pattern_i.cxx +++ b/src/SMESH_I/SMESH_Pattern_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Pattern_i.cxx // Created : Fri Aug 20 16:15:49 2004 // Author : Edward AGAPOV (eap) -// $Header: // #include "SMESH_Pattern_i.hxx" @@ -46,6 +46,7 @@ #include using SMESH::TPythonDump; +using SMESH::TVar; //======================================================================= //function : dumpErrorCode @@ -149,7 +150,7 @@ CORBA::Boolean SMESH_Pattern_i::LoadFromFace(SMESH::SMESH_Mesh_ptr theMesh, return false; // Update Python script - TPythonDump() << "isDone = pattern.LoadFromFace( " << theMesh << ", " + TPythonDump() << "isDone = pattern.LoadFromFace( " << theMesh << ".GetMesh(), " << theFace << ", " << theProject << " )"; addErrorCode( "LoadFromFace" ); @@ -180,7 +181,7 @@ CORBA::Boolean SMESH_Pattern_i::LoadFrom3DBlock(SMESH::SMESH_Mesh_ptr theMesh, return false; // Update Python script - TPythonDump() << "isDone = pattern.LoadFrom3DBlock( " << theMesh << ", " << theBlock << " )"; + TPythonDump() << "isDone = pattern.LoadFrom3DBlock( " << theMesh << ".GetMesh(), " << theBlock << " )"; addErrorCode( "LoadFrom3DBlock" ); return myPattern.Load( aMesh, TopoDS::Shell( exp.Current() )); @@ -214,7 +215,6 @@ SMESH::point_array* SMESH_Pattern_i::ApplyToFace(GEOM::GEOM_Object_ptr theFace, (*xyzIt)->Coord( p.x, p.y, p.z ); } } - // Update Python script TPythonDump() << "pattern.ApplyToFace( " << theFace << ", " << theVertexOnKeyPoint1 << ", " << theReverse << " )"; @@ -316,9 +316,9 @@ SMESH::point_array* } // Update Python script - TPythonDump() << "pattern.ApplyToMeshFaces( " << theMesh << ", " + TPythonDump() << "pattern.ApplyToMeshFaces( " << theMesh << ".GetMesh(), " << theFacesIDs << ", " - << theNodeIndexOnKeyPoint1 << ", " << theReverse << " )"; + << TVar( theNodeIndexOnKeyPoint1 ) << ", " << theReverse << " )"; return points._retn(); } @@ -361,9 +361,9 @@ SMESH::point_array* } // Update Python script - TPythonDump() << "pattern.ApplyToHexahedrons( " << theMesh << ", " + TPythonDump() << "pattern.ApplyToHexahedrons( " << theMesh << ".GetMesh(), " << theVolumesIDs << ", " - << theNode000Index << ", " << theNode001Index << " )"; + << TVar(theNode000Index) << ", " << TVar(theNode001Index) << " )"; return points._retn(); } @@ -382,11 +382,20 @@ CORBA::Boolean SMESH_Pattern_i::MakeMesh (SMESH::SMESH_Mesh_ptr theMesh, return false; // Update Python script - TPythonDump() << "isDone = pattern.MakeMesh( " << theMesh << ", " + TPythonDump() << "isDone = pattern.MakeMesh( " << theMesh << ".GetMesh(), " << CreatePolygons << ", " << CreatePolyedrs << " )"; addErrorCode( "MakeMesh" ); - return myPattern.MakeMesh( aMesh, CreatePolygons, CreatePolyedrs ); + int nb = aMesh->NbNodes() + aMesh->NbEdges() + aMesh->NbFaces() + aMesh->NbVolumes(); + + bool res = myPattern.MakeMesh( aMesh, CreatePolygons, CreatePolyedrs ); + + if ( nb > 0 && nb != aMesh->NbNodes() + aMesh->NbEdges() + aMesh->NbFaces() + aMesh->NbVolumes()) + { + aMesh->SetIsModified(true); + aMesh->GetMeshDS()->Modified(); + } + return res; } //======================================================================= diff --git a/src/SMESH_I/SMESH_Pattern_i.hxx b/src/SMESH_I/SMESH_Pattern_i.hxx index 8526ef9ad..b363ac10d 100644 --- a/src/SMESH_I/SMESH_Pattern_i.hxx +++ b/src/SMESH_I/SMESH_Pattern_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_Pattern_i.hxx // Created : Fri Aug 20 16:03:15 2004 diff --git a/src/SMESH_I/SMESH_PreMeshInfo.cxx b/src/SMESH_I/SMESH_PreMeshInfo.cxx new file mode 100644 index 000000000..e56adfadf --- /dev/null +++ b/src/SMESH_I/SMESH_PreMeshInfo.cxx @@ -0,0 +1,1278 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_PreMeshInfo.cxx +// Created : Fri Feb 10 17:36:39 2012 +// Author : Edward AGAPOV (eap) +// + +#include "SMESH_PreMeshInfo.hxx" + +#include "DriverMED_R_SMESHDS_Mesh.h" +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" +#include "SMDS_SpacePosition.hxx" +#include "SMDS_VertexPosition.hxx" +#include "SMESHDS_Group.hxx" +#include "SMESHDS_GroupOnFilter.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_Mesh_i.hxx" +#include "SMESH_subMesh_i.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include CORBA_SERVER_HEADER(SALOME_Session) + +#define MYDEBUGOUT(msg) //std::cout << msg << std::endl; + +//================================================================================ +#define PreMeshInfo_TRY \ + try { OCC_CATCH_SIGNALS +//================================================================================ +#define PreMeshInfo_CATCH } \ + catch (Standard_Failure& ex) { \ + onExceptionCaught(SMESH_Comment("OCC Exception caught: \t")< theStudyIDToMeshCounter; + + //================================================================================ + /*! + * \brief Counts not fully loaded meshes + */ + //================================================================================ + + void meshInfoLoaded( SMESH_Mesh_i* mesh ) + { + map< int, int >::iterator id2counter = + theStudyIDToMeshCounter.insert( make_pair( (int) mesh->GetStudyId(), 0 )).first; + id2counter->second++; + } + //================================================================================ + /*! + * \brief Removes temporary files if none of meshes needs them + */ + //================================================================================ + + void filesNoMoreNeeded(SMESH_Mesh_i* mesh, + std::string medFile, + std::string hdfFile) + { + if ( --theStudyIDToMeshCounter[ (int) mesh->GetStudyId() ] == 0 ) + { + string tmpDir = SALOMEDS_Tool::GetDirFromPath( hdfFile ); + + SALOMEDS::ListOfFileNames_var aFiles = new SALOMEDS::ListOfFileNames; + aFiles->length(2); + medFile = SALOMEDS_Tool::GetNameFromPath( medFile ) + ".med"; + hdfFile = SALOMEDS_Tool::GetNameFromPath( hdfFile ) + ".hdf"; + aFiles[0] = medFile.c_str(); + aFiles[1] = hdfFile.c_str(); + + SALOMEDS_Tool::RemoveTemporaryFiles( tmpDir.c_str(), aFiles.in(), true ); + } + } + + //================================================================================ + /*! + * \brief Method useful only to set a breakpoint to debug in case of exception + */ + //================================================================================ + + void onExceptionCaught(const string& msg) + { + INFOS( msg ); + MYDEBUGOUT( msg ); + } + + //============================================================================= + /*! + * \brief Class sending signals on start and finish of loading + */ + //============================================================================= + + class SignalToGUI + { + string _messagePrefix; + SALOME::Session_var _session; + public: + SignalToGUI( SMESH_Mesh_i* mesh ) + { + SMESH_Gen_i* gen = SMESH_Gen_i::GetSMESHGen(); + SALOMEDS::Study_var study = gen->GetCurrentStudy(); + if ( !study->_is_nil() && study->StudyId() == mesh->GetStudyId() ) + { + SALOMEDS::SObject_var meshSO = gen->ObjectToSObject(study, mesh->_this() ); + CORBA::Object_var obj = gen->GetNS()->Resolve( "/Kernel/Session" ); + _session = SALOME::Session::_narrow( obj ); + if ( !meshSO->_is_nil() && !_session->_is_nil() ) + { + CORBA::String_var meshEntry = meshSO->GetID(); + _messagePrefix = "SMESH/mesh_loading/"; + _messagePrefix += meshEntry.in(); + + string msgToGUI = _messagePrefix + "/"; + msgToGUI += SMESH_Comment( mesh->NbNodes() ); + msgToGUI += "/"; + msgToGUI += SMESH_Comment( mesh->NbElements() ); + + _session->emitMessageOneWay( msgToGUI.c_str()); + } + } + } + void sendStop() + { + if ( !_messagePrefix.empty() ) + { + string msgToGUI = _messagePrefix + "/stop"; + _session->emitMessageOneWay( msgToGUI.c_str()); + _messagePrefix.clear(); + } + } + ~SignalToGUI() { sendStop(); } + }; + + //============================================================================= + /*! + * \brief Creates SMDS_Position according to shape type + */ + //============================================================================= + + class PositionCreator { + public: + SMDS_PositionPtr MakePosition(const TopAbs_ShapeEnum type) { + return (this->*myFuncTable[ type ])(); + } + PositionCreator() { + myFuncTable.resize( (size_t) TopAbs_SHAPE, & PositionCreator::defaultPosition ); + myFuncTable[ TopAbs_SOLID ] = & PositionCreator::volumePosition; + myFuncTable[ TopAbs_FACE ] = & PositionCreator::facePosition; + myFuncTable[ TopAbs_EDGE ] = & PositionCreator::edgePosition; + myFuncTable[ TopAbs_VERTEX ] = & PositionCreator::vertexPosition; + } + private: + SMDS_PositionPtr edgePosition() const { return SMDS_PositionPtr( new SMDS_EdgePosition ); } + SMDS_PositionPtr facePosition() const { return SMDS_PositionPtr( new SMDS_FacePosition ); } + SMDS_PositionPtr volumePosition() const { return SMDS_PositionPtr( new SMDS_SpacePosition ); } + SMDS_PositionPtr vertexPosition() const { return SMDS_PositionPtr( new SMDS_VertexPosition); } + SMDS_PositionPtr defaultPosition() const { return SMDS_SpacePosition::originSpacePosition(); } + typedef SMDS_PositionPtr (PositionCreator:: * FmakePos)() const; + vector myFuncTable; + }; + + //================================================================================ + /*! + * \brief Returns ids of simple shapes composing a complex one + */ + //================================================================================ + + vector getSimpleSubMeshIds( SMESHDS_Mesh* meshDS, int shapeId ) + { + vector ids; + + list shapeQueue( 1, meshDS->IndexToShape( shapeId )); + list::iterator shape = shapeQueue.begin(); + for ( ; shape != shapeQueue.end(); ++shape ) + { + if ( shape->IsNull() ) continue; + if ( shape->ShapeType() == TopAbs_COMPOUND || + shape->ShapeType() == TopAbs_COMPSOLID ) + { + for ( TopoDS_Iterator it( *shape ); it.More(); it.Next() ) + shapeQueue.push_back( it.Value() ); + } + else + { + ids.push_back( meshDS->ShapeToIndex( *shape )); + } + } + return ids; + } + + //================================================================================ + /*! + * \brief Return EEntiteMaillage by EGeometrieElement + */ + //================================================================================ + + MED::EEntiteMaillage entityByGeom(const MED::EGeometrieElement geom ) + { + return geom == MED::eBALL ? MED::eSTRUCT_ELEMENT : MED::eMAILLE; + } + + //================================================================================ + /*! + * \brief Return a map< EGeometrieElement, SMDSAbs_EntityType > + */ + //================================================================================ + + typedef map< MED::EGeometrieElement, SMDSAbs_EntityType > Tmed2smeshElemTypeMap; + const Tmed2smeshElemTypeMap& med2smeshElemTypeMap() + { + static map< MED::EGeometrieElement, SMDSAbs_EntityType> med2smeshTypes; + if ( med2smeshTypes.empty() ) + { + med2smeshTypes[ MED::ePOINT1 ] = SMDSEntity_0D ; + med2smeshTypes[ MED::eSEG2 ] = SMDSEntity_Edge ; + med2smeshTypes[ MED::eSEG3 ] = SMDSEntity_Quad_Edge ; + med2smeshTypes[ MED::eTRIA3 ] = SMDSEntity_Triangle ; + med2smeshTypes[ MED::eTRIA6 ] = SMDSEntity_Quad_Triangle ; + med2smeshTypes[ MED::eQUAD4 ] = SMDSEntity_Quadrangle ; + med2smeshTypes[ MED::eQUAD8 ] = SMDSEntity_Quad_Quadrangle ; + med2smeshTypes[ MED::eQUAD9 ] = SMDSEntity_BiQuad_Quadrangle ; + med2smeshTypes[ MED::eTETRA4 ] = SMDSEntity_Tetra ; + med2smeshTypes[ MED::ePYRA5 ] = SMDSEntity_Pyramid ; + med2smeshTypes[ MED::ePENTA6 ] = SMDSEntity_Penta ; + med2smeshTypes[ MED::eHEXA8 ] = SMDSEntity_Hexa ; + med2smeshTypes[ MED::eOCTA12 ] = SMDSEntity_Hexagonal_Prism ; + med2smeshTypes[ MED::eTETRA10 ] = SMDSEntity_Quad_Tetra ; + med2smeshTypes[ MED::ePYRA13 ] = SMDSEntity_Quad_Pyramid ; + med2smeshTypes[ MED::ePENTA15 ] = SMDSEntity_Quad_Penta ; + med2smeshTypes[ MED::eHEXA20 ] = SMDSEntity_Quad_Hexa ; + med2smeshTypes[ MED::eHEXA27 ] = SMDSEntity_TriQuad_Hexa ; + med2smeshTypes[ MED::ePOLYGONE ] = SMDSEntity_Polygon ; + med2smeshTypes[ MED::ePOLYEDRE ] = SMDSEntity_Polyhedra ; + med2smeshTypes[ MED::eNONE ] = SMDSEntity_Node ; + med2smeshTypes[ MED::eBALL ] = SMDSEntity_Ball ; + } + return med2smeshTypes; + } + + //================================================================================ + /*! + * \brief Return a vector intended to retrieve + * MED::EGeometrieElement by SMDSAbs_EntityType + */ + //================================================================================ + + const vector& mesh2medElemType() + { + static vector mesh2medElemTypes; + if ( mesh2medElemTypes.empty() ) + { + mesh2medElemTypes.resize( SMDSEntity_Last + 1 ); + Tmed2smeshElemTypeMap::const_iterator me2sme = med2smeshElemTypeMap().begin(); + Tmed2smeshElemTypeMap::const_iterator me2smeEnd = med2smeshElemTypeMap().end(); + for ( ; me2sme != me2smeEnd; ++me2sme ) + mesh2medElemTypes[ me2sme->second ] = me2sme->first; + } + return mesh2medElemTypes; + } + + //================================================================================ + /*! + * \brief Writes meshInfo into a HDF file + */ + //================================================================================ + + void meshInfo2hdf( SMESH::long_array_var meshInfo, + const std::string& name, + HDFgroup* hdfGroup) + { + // we use med identification of element (MED::EGeometrieElement>) types + // but not enum SMDSAbs_EntityType because values of SMDSAbs_EntityType may + // change at insertion of new items in the middle. + const vector& medTypes = mesh2medElemType(); + + vector data; + + for ( size_t i = 0; i < meshInfo->length(); ++i ) + if ( meshInfo[i] > 0 ) + { + data.push_back( medTypes[ i ] ); + data.push_back( meshInfo[ i ] ); + } + + if ( !data.empty() ) + { + hdf_size datasetSize[] = { data.size() }; + HDFarray* anArray = new HDFarray(0, HDF_INT32, 1, datasetSize); + anArray->CreateOnDisk(); + datasetSize[0] = 1; + HDFdataset* dataset = new HDFdataset( name.c_str(), hdfGroup, HDF_ARRAY, datasetSize, 1 ); + dataset->SetArrayId(anArray->GetId()); + dataset->CreateOnDisk(); + dataset->WriteOnDisk( & data[0] ); + dataset->CloseOnDisk(); + anArray->CloseOnDisk(); + } + } +} + +//================================================================================ +/*! + * \brief Reads meshInfo from a HDF file + */ +//================================================================================ + +void SMESH_PreMeshInfo::hdf2meshInfo( const std::string& name, + HDFgroup* hdfGroup) +{ + if ( hdfGroup->ExistInternalObject( name.c_str()) ) + { + HDFdataset* dataset = new HDFdataset( name.c_str(), hdfGroup ); + dataset->OpenOnDisk(); + + // // hdf_size datasetSize[ 1 ]; + // // HDFarray *array = new HDFarray(dataset); + // // array->GetDim( datasetSize ); + // int size = dataset->GetSize(); + + vector info( SMDSEntity_Last * 2, 0 ); + dataset->ReadFromDisk( &info[0] ); + dataset->CloseOnDisk(); + + const Tmed2smeshElemTypeMap& med2smesh = med2smeshElemTypeMap(); + Tmed2smeshElemTypeMap::const_iterator me2sme, me2smeEnd = med2smesh.end(); + for ( size_t i = 0; i < info.size(); ) + { + int medType = info[i++]; + int nbElems = info[i++]; + if ( !nbElems ) break; + me2sme = med2smesh.find( (MED::EGeometrieElement) medType ); + if ( me2sme != me2smeEnd ) + setNb( me2sme->second, nbElems ); + } + } + _isInfoOk = true; + + if ( NbNodes() == GroupOnFilter_OutOfDate ) // case of !SMESHDS_GroupOnFilter::IsUpToDate() + { + _isInfoOk = false; + setNb( SMDSEntity_Node, 0 ); + } +} + +//================================================================================ +/*! + * \brief Constructor callable by SMESH_PreMeshInfo only + */ +//================================================================================ + +SMESH_PreMeshInfo::SMESH_PreMeshInfo(SMESH_Mesh_i* mesh, + const int meshID, + const std::string& medFile, + const std::string& hdfFile) + : _medFileName( medFile ), + _hdfFileName( hdfFile ), + _toRemoveFiles( false ), + _meshID( meshID ), + _mesh( mesh ), + _isInfoOk( false ), + _elemCounter( 0 ) +{ +} + +//================================================================================ +/*! + * \brief Release temporary files + */ +//================================================================================ + +SMESH_PreMeshInfo::~SMESH_PreMeshInfo() +{ + if ( _toRemoveFiles ) // it can be true only for SMESH_PreMeshInfo of the mesh + filesNoMoreNeeded( _mesh, _medFileName, _hdfFileName ); + + _toRemoveFiles = false; +} + +//================================================================================ +/*! + * \brief fills SMESH_PreMeshInfo field of all objects of mesh + */ +//================================================================================ + +void SMESH_PreMeshInfo::LoadFromFile( SMESH_Mesh_i* mesh, + const int meshID, + const std::string& medFile, + const std::string& hdfFile, + const bool toRemoveFiles) +{ + PreMeshInfo_TRY; + + SMESH_PreMeshInfo* meshPreInfo = new SMESH_PreMeshInfo( mesh,meshID,medFile,hdfFile ); + mesh->changePreMeshInfo() = meshPreInfo; + + meshPreInfo->_toRemoveFiles = toRemoveFiles; + if ( toRemoveFiles ) + meshInfoLoaded( mesh ); + + if ( meshPreInfo->readPreInfoFromHDF() ) + // all SMESH_PreMeshInfo's are stored in HDF file (written after + // implementing SMESH_PreMeshInfo) + return; + + // try to read SMESH_PreMeshInfo from med file (as study is older than SMESH_PreMeshInfo) + if ( meshPreInfo->readMeshInfo() ) + { + meshPreInfo->readGroupInfo(); + meshPreInfo->readSubMeshInfo(); + } + else + { + meshPreInfo->FullLoadFromFile(); + } + PreMeshInfo_CATCH; +} + +//================================================================================ +/*! + * \brief Tries to read all SMESH_PreMeshInfo from a HDF file + * \retval bool - true if succeeded + * + * This method is symmetrical to SaveToFile() + */ +//================================================================================ + +bool SMESH_PreMeshInfo::readPreInfoFromHDF() +{ + HDFfile* aFile = new HDFfile( (char*) _hdfFileName.c_str() ); + aFile->OpenOnDisk( HDF_RDONLY ); + + SMESH_Comment hdfGroupName("SMESH_PreMeshInfo"); hdfGroupName << _meshID; + const bool infoAvailable = aFile->ExistInternalObject( hdfGroupName ); + if ( infoAvailable ) + { + HDFgroup* infoHdfGroup = new HDFgroup( hdfGroupName, aFile ); + infoHdfGroup->OpenOnDisk(); + + _mesh->changePreMeshInfo()->hdf2meshInfo( "Mesh", infoHdfGroup ); + + // read SMESH_PreMeshInfo of groups + map::const_iterator i2group = _mesh->_mapGroups.begin(); + for ( ; i2group != _mesh->_mapGroups.end(); ++i2group ) + { + if ( SMESH_GroupBase_i* group_i = + SMESH::DownCast( i2group->second )) + { + group_i->changePreMeshInfo() = newInstance(); + if ( SMESHDS_GroupBase* group = group_i->GetGroupDS() ) + { + const string name = group->GetStoreName(); + group_i->changePreMeshInfo()->hdf2meshInfo( name, infoHdfGroup ); + } + } + } + + // read SMESH_PreMeshInfo of sub-meshes + map::iterator id2sm = _mesh->_mapSubMeshIor.begin(); + for ( ; id2sm != _mesh->_mapSubMeshIor.end(); ++id2sm ) + { + if ( SMESH_subMesh_i* sm = SMESH::DownCast( id2sm->second )) + { + sm->changePreMeshInfo() = newInstance(); + sm->changePreMeshInfo()->hdf2meshInfo( SMESH_Comment( sm->GetId()), infoHdfGroup ); + } + } + } + + aFile->CloseOnDisk(); + delete aFile; + + return infoAvailable; +} + +//================================================================================ +/*! + * \brief Reads mesh info of mesh from the med file + */ +//================================================================================ + +bool SMESH_PreMeshInfo::readMeshInfo() +{ + _isInfoOk = true; + + MED::PWrapper aMed = MED::CrWrapper(_medFileName,true); + // if ( aMed->GetVersion() != MED::eV2_2 ) + // return false; + + MED::PMeshInfo medMeshInfo = aMed->CrMeshInfo(3,3,SMESH_Comment( _meshID )); + + // read nb nodes + int nbNodes = std::max( 0, aMed->GetNbNodes( medMeshInfo )); + if ( nbNodes > 0 ) + { + setNb( SMDSEntity_Node, nbNodes); + + // read nb of elements + Tmed2smeshElemTypeMap::const_iterator me2sme = med2smeshElemTypeMap().begin(); + Tmed2smeshElemTypeMap::const_iterator me2smeEnd = med2smeshElemTypeMap().end(); + for ( ; me2sme != me2smeEnd; ++me2sme ) + { + int nbElems = aMed->GetNbCells( medMeshInfo, entityByGeom(me2sme->first), me2sme->first ); + if ( nbElems > 0 ) + setNb( me2sme->second, nbElems ); + } + } + return true; +} + +//================================================================================ +/*! + * \brief Reads info of groups from the med file + */ +//================================================================================ + +void SMESH_PreMeshInfo::readGroupInfo() +{ + if ( _mesh->_mapGroups.empty() ) return; + + // make SMESH_PreMeshInfo of groups + map< string, SMESH_PreMeshInfo* > name2GroupInfo; + map::const_iterator i2group = _mesh->_mapGroups.begin(); + for ( ; i2group != _mesh->_mapGroups.end(); ++i2group ) + { + if ( SMESH_GroupBase_i* group_i = + SMESH::DownCast( i2group->second )) + { + SMESH_PreMeshInfo* info = newInstance(); + group_i->changePreMeshInfo() = info; + if ( SMESHDS_Group* group = dynamic_cast< SMESHDS_Group* >( group_i->GetGroupDS() )) + { + string name = group->GetStoreName(); + name2GroupInfo.insert( make_pair( name, info )); + info->_isInfoOk = true; + } + } + } + + map< int, vector< SMESH_PreMeshInfo* > > famId2grInfo; + + MED::PWrapper aMed = MED::CrWrapper(_medFileName,false); + MED::PMeshInfo medMeshInfo = aMed->CrMeshInfo(3,3,SMESH_Comment( _meshID )); + + // read families to fill in famId2grInfo + int nbFams = aMed->GetNbFamilies( medMeshInfo ); + if ( nbFams <= 1 ) return; // zero family is always present + for ( int iF = 0; iF <= nbFams; ++iF ) + { + int nbGroups = aMed->GetNbFamGroup( iF, medMeshInfo ); + if ( nbGroups < 1 ) continue; + MED::PFamilyInfo medFamInfo = aMed->CrFamilyInfo( medMeshInfo, nbGroups, nbGroups ); + aMed->GetFamilyInfo( iF, medFamInfo ); // read groups of a family + vector< SMESH_PreMeshInfo* >& grInfoVec = famId2grInfo[ medFamInfo->GetId() ]; + for ( int iG = 0; iG < nbGroups; ++iG ) + { + const string grName = medFamInfo->GetGroupName( iG ); + map< string, SMESH_PreMeshInfo* >::iterator n2i = name2GroupInfo.find( grName ); + if ( n2i != name2GroupInfo.end() ) + grInfoVec.push_back( n2i->second ); + } + } + + // read family numbers of elements + Tmed2smeshElemTypeMap::const_iterator me2sme = med2smeshElemTypeMap().begin(); + Tmed2smeshElemTypeMap::const_iterator me2smeEnd = med2smeshElemTypeMap().end(); + MED::PElemInfo medElemInfo = aMed->CrElemInfo( medMeshInfo, 0 ); + MED::TIntVector& famNums = medElemInfo->myFamNum; + for ( ; me2sme != me2smeEnd; ++me2sme ) // loop on elem types + { + famNums.resize( NbEntities( me2sme->second )); + if ( famNums.empty() ) continue; + aMed->GetFamilies( medElemInfo, famNums.size(), entityByGeom(me2sme->first), me2sme->first ); + // distribute elements of a type among groups + map< int, vector< SMESH_PreMeshInfo* > >::iterator f2infos = famId2grInfo.begin(); + for ( size_t i = 0; i < famNums.size(); ++i ) + { + if ( famNums[i] != f2infos->first ) + { + f2infos = famId2grInfo.find( famNums[i] ); + if ( f2infos == famId2grInfo.end() ) + f2infos = famId2grInfo.insert + ( make_pair( famNums[i], vector< SMESH_PreMeshInfo*>())).first; + } + vector< SMESH_PreMeshInfo* >& infoVec = f2infos->second ; + for ( size_t j = 0; j < infoVec.size(); ++j ) + infoVec[j]->_elemCounter++; + } + // pass _elemCounter to a real elem type + map< string, SMESH_PreMeshInfo* >::iterator n2i = name2GroupInfo.begin(); + for ( ; n2i != name2GroupInfo.end(); ++n2i ) + { + SMESH_PreMeshInfo* info = n2i->second; + info->setNb( me2sme->second, info->_elemCounter ); + info->_elemCounter = 0; + } + } +} + +//================================================================================ +/*! + * \brief Reads info of sub-meshes from hdf file of old study + */ +//================================================================================ + +void SMESH_PreMeshInfo::readSubMeshInfo() +{ + if ( _mesh->_mapSubMeshIor.empty() ) return; + + // create SMESH_PreMeshInfo of sub-meshes + map::iterator id2sm = _mesh->_mapSubMeshIor.begin(); + for ( ; id2sm != _mesh->_mapSubMeshIor.end(); ++id2sm ) + { + if ( SMESH_subMesh_i* sm = SMESH::DownCast( id2sm->second )) + { + sm->changePreMeshInfo() = newInstance(); + sm->changePreMeshInfo()->_isInfoOk = true; + } + } + + // try to read + HDFfile* aFile = new HDFfile( (char*) _hdfFileName.c_str() ); + aFile->OpenOnDisk( HDF_RDONLY ); + + char meshGrpName[ 30 ]; + sprintf( meshGrpName, "Mesh %d", _meshID ); + if ( aFile->ExistInternalObject( meshGrpName ) ) + { + HDFgroup* aTopGroup = new HDFgroup( meshGrpName, aFile ); + aTopGroup->OpenOnDisk(); + if ( aTopGroup->ExistInternalObject( "Submeshes" )) + { + HDFgroup* aGroup = new HDFgroup( "Submeshes", aTopGroup ); + aGroup->OpenOnDisk(); + + SMESHDS_Mesh* meshDS = _mesh->GetImpl().GetMeshDS(); + int maxSmId = Max( meshDS->MaxSubMeshIndex(), meshDS->MaxShapeIndex() ); + + for ( int isNode = 0; isNode < 2; ++isNode ) + { + string aDSName( isNode ? "Node Submeshes" : "Element Submeshes"); + if ( aGroup->ExistInternalObject( (char*) aDSName.c_str() )) + { + // read sub-mesh id of all nodes or elems + HDFdataset* aDataset = new HDFdataset( (char*) aDSName.c_str(), aGroup ); + aDataset->OpenOnDisk(); + int nbElems = aDataset->GetSize(); + int* smIDs = new int [ nbElems ]; + aDataset->ReadFromDisk( smIDs ); + aDataset->CloseOnDisk(); + // count nb elems in each sub-mesh + vector nbBySubmeshId( maxSmId + 1, 0 ); + for ( int i = 0; i < nbElems; ++i ) + { + const int smID = smIDs[ i ]; + if ( smID < (int) nbBySubmeshId.size() ) + nbBySubmeshId[ smID ]++; + } + delete [] smIDs; + + // store nb elems in SMESH_PreMeshInfo of sub-meshes + map::iterator id2sm = _mesh->_mapSubMeshIor.begin(); + for ( ; id2sm != _mesh->_mapSubMeshIor.end(); ++id2sm ) + { + if ( SMESH_subMesh_i* sm = SMESH::DownCast( id2sm->second )) + { + SMESH_PreMeshInfo* & info = sm->changePreMeshInfo(); + + vector smIds = getSimpleSubMeshIds( meshDS, id2sm->first ); + for ( size_t i = 0; i < smIds.size(); ++i ) + info->_elemCounter += nbBySubmeshId[ smIds[i] ]; + + SMDSAbs_EntityType elemType; + if ( isNode ) + { + elemType = SMDSEntity_Node; + } + else + { + bool koElemType = false; + const TopoDS_Shape& shape = meshDS->IndexToShape( smIds[0] ); + elemType = getElemType( shape.ShapeType(), info->_elemCounter, koElemType ); + info->_isInfoOk = !koElemType; + } + info->setNb( elemType, info->_elemCounter ); + } + } + } // if ( aGroup->ExistInternalObject( aDSName )) + } // for ( int isNode = 0; isNode < 2; ++isNode ) + + aGroup->CloseOnDisk(); + } // if ( aTopGroup->ExistInternalObject( "Submeshes" )) + + aTopGroup->CloseOnDisk(); + } // if ( aFile->ExistInternalObject( meshGrpName ) ) + + aFile->CloseOnDisk(); + delete aFile; +} + +//================================================================================ +/*! + * \brief Return type of element for sub-mesh on a shape of given type + */ +//================================================================================ + +SMDSAbs_EntityType SMESH_PreMeshInfo::getElemType( const TopAbs_ShapeEnum shapeType, + const int nbElemsInSubMesh, + bool& isKoType) const +{ + isKoType = false; + int type, typeEnd; + SMESH_PreMeshInfo* meshInfo = _mesh->changePreMeshInfo(); + + switch ( shapeType ) + { + case TopAbs_SOLID: + type = SMDSEntity_Tetra; + typeEnd = SMDSEntity_Last; + isKoType = ( meshInfo->NbVolumes() != nbElemsInSubMesh ); + break; + case TopAbs_FACE: + case TopAbs_SHELL: + type = SMDSEntity_Triangle; + typeEnd = SMDSEntity_Tetra; + isKoType = ( meshInfo->NbFaces() != nbElemsInSubMesh ); + break; + case TopAbs_WIRE: + case TopAbs_EDGE: return SMDSEntity_Edge; + case TopAbs_VERTEX: return SMDSEntity_0D; + default: return SMDSEntity_Last; + } + + if ( !isKoType ) + { + for ( int t = type; t < typeEnd; ++t ) + if ( nbElemsInSubMesh == meshInfo->NbEntities( SMDSAbs_EntityType( t ))) + return SMDSAbs_EntityType( t ); + } + isKoType = true; + return SMDSAbs_EntityType( type ); +} + +//================================================================================ +/*! + * \brief Saves SMESH_PreMeshInfo to the study file + */ +//================================================================================ + +void SMESH_PreMeshInfo::SaveToFile( SMESH_Mesh_i* mesh, + const int meshID, + HDFfile* hdfFile) +{ + // create a HDF group for SMESH_PreMeshInfo of this mesh + SMESH_Comment hdfGroupName("SMESH_PreMeshInfo"); hdfGroupName << meshID; + HDFgroup* infoHdfGroup = new HDFgroup( hdfGroupName, hdfFile ); + infoHdfGroup->CreateOnDisk(); + + PreMeshInfo_TRY; + + // info of mesh + meshInfo2hdf( mesh->GetMeshInfo(), "Mesh", infoHdfGroup ); + + // info of groups + SMESH_PreMeshInfo incompleteInfo( 0,0,"",""); + incompleteInfo.setNb( SMDSEntity_Node, GroupOnFilter_OutOfDate ); + SMESHDS_Mesh* meshDS = mesh->GetImpl().GetMeshDS(); + + map::const_iterator i2group = mesh->_mapGroups.begin(); + for ( ; i2group != mesh->_mapGroups.end(); ++i2group ) + { + if ( SMESH_GroupBase_i* group_i = SMESH::DownCast( i2group->second )) + { + SMESHDS_GroupBase * group = group_i->GetGroupDS(); + if ( SMESHDS_GroupOnFilter* gof = dynamic_cast(group)) + { + // prevent too long storage time due to applying filter to many elements + if ( !gof->IsUpToDate() && meshDS->GetMeshInfo().NbElements( gof->GetType() ) > 1e5 ) + { + meshInfo2hdf( incompleteInfo.GetMeshInfo(), + group->GetStoreName(), + infoHdfGroup); + continue; + } + } + meshInfo2hdf( group_i->GetMeshInfo(), group->GetStoreName(), infoHdfGroup); + } + } + + // info of sub-meshes + map::iterator id2sm = mesh->_mapSubMeshIor.begin(); + for ( ; id2sm != mesh->_mapSubMeshIor.end(); ++id2sm ) + { + if ( SMESH_subMesh_i* sm = SMESH::DownCast( id2sm->second )) + { + meshInfo2hdf( sm->GetMeshInfo(), + SMESH_Comment( sm->GetId() ), + infoHdfGroup); + } + } + + PreMeshInfo_CATCH; + + infoHdfGroup->CloseOnDisk(); +} + +//================================================================================ +/*! + * \brief Reads all data and remove all SMESH_PreMeshInfo fields from objects + */ +//================================================================================ + +void SMESH_PreMeshInfo::FullLoadFromFile() const +{ + SignalToGUI signalOnLoading( _mesh ); + + SMESH_PreMeshInfo* meshInfo = _mesh->changePreMeshInfo(); + _mesh->changePreMeshInfo() = NULL; // to allow GUI accessing to real info + + ::SMESH_Mesh& mesh = _mesh->GetImpl(); + SMESHDS_Mesh* meshDS = mesh.GetMeshDS(); + + PreMeshInfo_TRY; + + MYDEBUGOUT( "BEG FullLoadFromFile() " << _meshID ); + + // load mesh + DriverMED_R_SMESHDS_Mesh myReader; + myReader.SetFile( _medFileName.c_str() ); + myReader.SetMesh( meshDS ); + myReader.SetMeshId( _meshID ); + myReader.Perform(); + + // load groups + const set& groups = meshDS->GetGroups(); + set::const_iterator groupIt = groups.begin(); + for ( ; groupIt != groups.end(); ++groupIt ) + if ( SMESHDS_Group* aGrp = dynamic_cast( *groupIt )) + myReader.GetGroup( aGrp ); + + // load sub-meshes + readSubMeshes( &myReader ); + + PreMeshInfo_CATCH; + + _mesh->changePreMeshInfo() = meshInfo; + + ForgetAllData(); + + signalOnLoading.sendStop(); + + meshDS->Modified(); + + // load dependent meshes referring/referred via hypotheses + mesh.GetSubMesh( mesh.GetShapeToMesh() )-> + ComputeStateEngine (SMESH_subMesh::SUBMESH_LOADED); + + MYDEBUGOUT( "END FullLoadFromFile()" ); +} + +//================================================================================ +/*! + * \brief Reads full data of sub-meshes + */ +//================================================================================ + +void SMESH_PreMeshInfo::readSubMeshes(DriverMED_R_SMESHDS_Mesh* reader) const +{ + HDFfile* aFile = new HDFfile( (char*) _hdfFileName.c_str() ); + aFile->OpenOnDisk( HDF_RDONLY ); + + char meshGrpName[ 30 ]; + sprintf( meshGrpName, "Mesh %d", _meshID ); + if ( aFile->ExistInternalObject( meshGrpName ) ) + { + HDFgroup* aTopGroup = new HDFgroup( meshGrpName, aFile ); + aTopGroup->OpenOnDisk(); + + SMESHDS_Mesh* meshDS = _mesh->GetImpl().GetMeshDS(); + + bool submeshesInFamilies = ( ! aTopGroup->ExistInternalObject( "Submeshes" )); + if ( submeshesInFamilies ) // from MED + { + // old way working before fix of PAL 12992 + reader->CreateAllSubMeshes(); + } + else + { + // open a group + HDFgroup* aGroup = new HDFgroup( "Submeshes", aTopGroup ); + aGroup->OpenOnDisk(); + + int maxID = Max( meshDS->MaxSubMeshIndex(), meshDS->MaxShapeIndex() ); + vector< SMESHDS_SubMesh * > subMeshes( maxID + 1, (SMESHDS_SubMesh*) 0 ); + vector< TopAbs_ShapeEnum > smType ( maxID + 1, TopAbs_SHAPE ); + + PositionCreator aPositionCreator; + + SMDS_NodeIteratorPtr nIt = meshDS->nodesIterator(); + SMDS_ElemIteratorPtr eIt = meshDS->elementsIterator(); + for ( int isNode = 0; isNode < 2; ++isNode ) + { + string aDSName( isNode ? "Node Submeshes" : "Element Submeshes"); + if ( aGroup->ExistInternalObject( (char*) aDSName.c_str() )) + { + HDFdataset* aDataset = new HDFdataset( (char*) aDSName.c_str(), aGroup ); + aDataset->OpenOnDisk(); + // read submesh IDs for all elements sorted by ID + int nbElems = aDataset->GetSize(); + int* smIDs = new int [ nbElems ]; + aDataset->ReadFromDisk( smIDs ); + aDataset->CloseOnDisk(); + + // get elements sorted by ID + TIDSortedElemSet elemSet; + if ( isNode ) + while ( nIt->more() ) elemSet.insert( elemSet.end(), nIt->next() ); + else + while ( eIt->more() ) elemSet.insert( elemSet.end(), eIt->next() ); + //ASSERT( elemSet.size() == nbElems ); -- issue 20182 + // -- Most probably a bad study was saved when there were + // not fixed bugs in SMDS_MeshInfo + if ( elemSet.size() < nbElems ) { +#ifdef _DEBUG_ + cout << "SMESH_Gen_i::Load(), warning: Node position data is invalid" << endl; +#endif + nbElems = elemSet.size(); + } + // add elements to submeshes + TIDSortedElemSet::iterator iE = elemSet.begin(); + for ( int i = 0; i < nbElems; ++i, ++iE ) + { + int smID = smIDs[ i ]; + if ( smID == 0 ) continue; + const SMDS_MeshElement* elem = *iE; + if ( smID > maxID ) { + // corresponding subshape no longer exists: maybe geom group has been edited + if ( _mesh->GetImpl().HasShapeToMesh() ) + meshDS->RemoveElement( elem ); + continue; + } + // get or create submesh + SMESHDS_SubMesh* & sm = subMeshes[ smID ]; + if ( ! sm ) { + sm = meshDS->NewSubMesh( smID ); + smType[ smID ] = meshDS->IndexToShape( smID ).ShapeType(); + } + // add + if ( isNode ) { + SMDS_PositionPtr pos = aPositionCreator.MakePosition( smType[ smID ]); + SMDS_MeshNode* node = const_cast( static_cast( elem )); + node->SetPosition( pos ); + sm->AddNode( node ); + } else { + sm->AddElement( elem ); + } + } + delete [] smIDs; + } + } + } // end reading submeshes + + // Read node positions on sub-shapes (SMDS_Position) + + if ( aTopGroup->ExistInternalObject( "Node Positions" )) + { + // There are 5 datasets to read: + // "Nodes on Edges" - ID of node on edge + // "Edge positions" - U parameter on node on edge + // "Nodes on Faces" - ID of node on face + // "Face U positions" - U parameter of node on face + // "Face V positions" - V parameter of node on face + const char* aEid_DSName = "Nodes on Edges"; + const char* aEu_DSName = "Edge positions"; + const char* aFu_DSName = "Face U positions"; + //char* aFid_DSName = "Nodes on Faces"; + //char* aFv_DSName = "Face V positions"; + + // data to retrieve + int nbEids = 0, nbFids = 0; + int *aEids = 0, *aFids = 0; + double *aEpos = 0, *aFupos = 0, *aFvpos = 0; + + // open a group + HDFgroup* aGroup = new HDFgroup( "Node Positions", aTopGroup ); + aGroup->OpenOnDisk(); + + // loop on 5 data sets + int aNbObjects = aGroup->nInternalObjects(); + for ( int i = 0; i < aNbObjects; i++ ) + { + // identify dataset + char aDSName[ HDF_NAME_MAX_LEN+1 ]; + aGroup->InternalObjectIndentify( i, aDSName ); + // read data + HDFdataset* aDataset = new HDFdataset( aDSName, aGroup ); + aDataset->OpenOnDisk(); + if ( aDataset->GetType() == HDF_FLOAT64 ) // Positions + { + double* pos = new double [ aDataset->GetSize() ]; + aDataset->ReadFromDisk( pos ); + // which one? + if ( strncmp( aDSName, aEu_DSName, strlen( aEu_DSName )) == 0 ) + aEpos = pos; + else if ( strncmp( aDSName, aFu_DSName, strlen( aFu_DSName )) == 0 ) + aFupos = pos; + else + aFvpos = pos; + } + else // NODE IDS + { + int aSize = aDataset->GetSize(); + + // for reading files, created from 18.07.2005 till 10.10.2005 + if (aDataset->GetType() == HDF_STRING) + aSize /= sizeof(int); + + int* ids = new int [aSize]; + aDataset->ReadFromDisk( ids ); + // on face or nodes? + if ( strncmp( aDSName, aEid_DSName, strlen( aEid_DSName )) == 0 ) { + aEids = ids; + nbEids = aSize; + } + else { + aFids = ids; + nbFids = aSize; + } + } + aDataset->CloseOnDisk(); + } // loop on 5 datasets + + // Set node positions on edges or faces + for ( int onFace = 0; onFace < 2; onFace++ ) + { + int nbNodes = ( onFace ? nbFids : nbEids ); + if ( nbNodes == 0 ) continue; + int* aNodeIDs = ( onFace ? aFids : aEids ); + double* aUPos = ( onFace ? aFupos : aEpos ); + double* aVPos = ( onFace ? aFvpos : 0 ); + // loop on node IDs + for ( int iNode = 0; iNode < nbNodes; iNode++ ) + { + const SMDS_MeshNode* node = meshDS->FindNode( aNodeIDs[ iNode ]); + if ( !node ) continue; // maybe removed while Loading() if geometry changed + SMDS_PositionPtr aPos = node->GetPosition(); + ASSERT( aPos ); + if ( onFace ) { + // ASSERT( aPos->GetTypeOfPosition() == SMDS_TOP_FACE );-- issue 20182 + // -- Most probably a bad study was saved when there were + // not fixed bugs in SMDS_MeshInfo + if ( aPos->GetTypeOfPosition() == SMDS_TOP_FACE ) { + SMDS_FacePosition* fPos = const_cast + ( static_cast( aPos )); + fPos->SetUParameter( aUPos[ iNode ]); + fPos->SetVParameter( aVPos[ iNode ]); + } + } + else { + // ASSERT( aPos->GetTypeOfPosition() == SMDS_TOP_EDGE );-- issue 20182 + if ( aPos->GetTypeOfPosition() == SMDS_TOP_EDGE ) { + SMDS_EdgePosition* fPos = const_cast + ( static_cast( aPos )); + fPos->SetUParameter( aUPos[ iNode ]); + } + } + } + } + if ( aEids ) delete [] aEids; + if ( aFids ) delete [] aFids; + if ( aEpos ) delete [] aEpos; + if ( aFupos ) delete [] aFupos; + if ( aFvpos ) delete [] aFvpos; + + aGroup->CloseOnDisk(); + + } // if ( aTopGroup->ExistInternalObject( "Node Positions" ) ) + + aTopGroup->CloseOnDisk(); + } // if ( aFile->ExistInternalObject( meshGrpName ) ) + + aFile->CloseOnDisk(); + delete aFile; +} + +//================================================================================ +/*! + * \brief Remove all SMESH_PreMeshInfo fields from objects w/o data loading + */ +//================================================================================ + +void SMESH_PreMeshInfo::ForgetAllData() const +{ + PreMeshInfo_TRY; + + if ( _mesh->changePreMeshInfo() != this ) + return _mesh->changePreMeshInfo()->ForgetAllData(); + + // remove SMESH_PreMeshInfo from groups + map::const_iterator i2group = _mesh->_mapGroups.begin(); + for ( ; i2group != _mesh->_mapGroups.end(); ++i2group ) + { + if ( SMESH_GroupBase_i* group_i = + SMESH::DownCast( i2group->second )) + { + SMESH_PreMeshInfo* & info = group_i->changePreMeshInfo(); + delete info; + info = NULL; + } + } + // remove SMESH_PreMeshInfo from sub-meshes + map::iterator id2sm = _mesh->_mapSubMeshIor.begin(); + for ( ; id2sm != _mesh->_mapSubMeshIor.end(); ++id2sm ) + { + if ( SMESH_subMesh_i* sm = SMESH::DownCast( id2sm->second )) + { + SMESH_PreMeshInfo* & info = sm->changePreMeshInfo(); + delete info; + info = NULL; + } + } + // remove SMESH_PreMeshInfo from the mesh + _mesh->changePreMeshInfo() = NULL; + delete this; + + PreMeshInfo_CATCH; + + + // Finalize loading + + // PreMeshInfo_TRY; + + // ::SMESH_Mesh& mesh = _mesh->GetImpl(); + + // // update hyps needing full mesh data restored (issue 20918) + // // map::iterator id2hyp= _mesh->_mapHypo.begin(); + // // for ( ; id2hyp != _mesh->_mapHypo.end(); ++id2hyp ) + // // if ( SMESH_Hypothesis_i* hyp = SMESH::DownCast( id2hyp->second )) + // // hyp->UpdateAsMeshesRestored(); + + + // PreMeshInfo_CATCH; +} + +//================================================================================ +/*! + * \brief Calls either FullLoadFromFile() or ForgetAllData() depending on preferences + */ +//================================================================================ + +void SMESH_PreMeshInfo::ForgetOrLoad() const +{ + if ( SMESH_Gen_i::GetSMESHGen()->ToForgetMeshDataOnHypModif() && + _mesh->HasShapeToMesh()) + ForgetAllData(); + else + FullLoadFromFile(); +} + +//================================================================================ +/*! + * \brief Method of SMESH_IDSource interface + */ +//================================================================================ + +SMESH::array_of_ElementType* SMESH_PreMeshInfo::GetTypes() const +{ + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + + types->length( 4 ); + int nbTypes = 0; + if (NbEdges()) types[nbTypes++] = SMESH::EDGE; + if (NbFaces()) types[nbTypes++] = SMESH::FACE; + if (NbVolumes()) types[nbTypes++] = SMESH::VOLUME; + if (Nb0DElements()) types[nbTypes++] = SMESH::ELEM0D; + if (NbBalls()) types[nbTypes++] = SMESH::BALL; + types->length( nbTypes ); + + return types._retn(); +} + +//================================================================================ +/*! + * \brief Method of SMESH_IDSource interface returning nb elements by element type + */ +//================================================================================ + +SMESH::long_array* SMESH_PreMeshInfo::GetMeshInfo() const +{ + SMESH::long_array_var aRes = new SMESH::long_array(); + aRes->length(SMESH::Entity_Last); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = 0; + + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = NbEntities((SMDSAbs_EntityType)i); + return aRes._retn(); +} + +//================================================================================ +/*! + * Returns false if GetMeshInfo() returns incorrect information that may + * happen if mesh data is not yet fully loaded from the file of study. + */ +//================================================================================ + +bool SMESH_PreMeshInfo::IsMeshInfoCorrect() const +{ + return _isInfoOk; +} + +//================================================================================ +/*! + * \brief TEMPORARY method to remove study files on closing study; + * RIGHT WAY: study files are remove automatically when meshes are destroyed + */ +//================================================================================ + +void SMESH_PreMeshInfo::RemoveStudyFiles_TMP_METHOD(SALOMEDS::SComponent_ptr smeshComp) +{ + SALOMEDS::Study_var study = smeshComp->GetStudy(); + if ( theStudyIDToMeshCounter[ (int) study->StudyId() ] > 0 ) + { + SALOMEDS::ChildIterator_var itBig = study->NewChildIterator( smeshComp ); + for ( ; itBig->More(); itBig->Next() ) { + SALOMEDS::SObject_var gotBranch = itBig->Value(); + CORBA::Object_var anObject = SMESH_Gen_i::SObjectToObject( gotBranch ); + if ( SMESH_Mesh_i* mesh = SMESH::DownCast( anObject )) + { + if ( mesh->changePreMeshInfo() ) + { + mesh->changePreMeshInfo()->ForgetAllData(); + } + } + } + } +} diff --git a/src/SMESH_I/SMESH_PreMeshInfo.hxx b/src/SMESH_I/SMESH_PreMeshInfo.hxx new file mode 100644 index 000000000..38b15a242 --- /dev/null +++ b/src/SMESH_I/SMESH_PreMeshInfo.hxx @@ -0,0 +1,125 @@ +// Copyright (C) 2007-2012 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 +// +// File : SMESH_PreMeshInfo.hxx +// Created : Fri Feb 10 13:25:02 2012 +// Author : Edward AGAPOV (eap) + + +#ifndef __SMESH_PreMeshInfo_HXX__ +#define __SMESH_PreMeshInfo_HXX__ + +#include "SMDS_MeshInfo.hxx" +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SALOMEDS) + +#include + +class DriverMED_R_SMESHDS_Mesh; +class HDFfile; +class HDFgroup; +class SMESH_Mesh_i; + +/*! + * \brief Class loading and holding information of a mesh object (mesh, group, submesh) + * not yet loaded from HDF file of study. + * + * Usage scenario: + * - SMESH_PreMeshInfo::LoadFromFile() // reads info of all objects + * - if ( myPreInfo ) myPreInfo->NbElements() // access to info by objects + * - myPreInfo->FullLoadFromFile() // reads all mesh data and resets myPreInfo=NULL + */ + +class SMESH_PreMeshInfo : public SMDS_MeshInfo +{ +public: + // fills SMESH_PreMeshInfo field of all objects of mesh + static void LoadFromFile( SMESH_Mesh_i* mesh, + const int meshID, + const std::string& medFile, + const std::string& hdfFile, + const bool toRemoveFiles); + + // saves SMESH_PreMeshInfo to the study file + static void SaveToFile( SMESH_Mesh_i* mesh, + const int meshID, + HDFfile* hdfFile); + + // reads all data and remove all SMESH_PreMeshInfo fields from objects + void FullLoadFromFile() const; + + // remove all SMESH_PreMeshInfo fields from objects w/o data loading + void ForgetAllData() const; + + // calls either FullLoadFromFile() or ForgetAllData() depending on preferences; + // is called on hypothesis modification + void ForgetOrLoad() const; + + // meshods of SMESH_IDSource interface + SMESH::array_of_ElementType* GetTypes() const; + SMESH::long_array* GetMeshInfo() const; + bool IsMeshInfoCorrect() const; + + ~SMESH_PreMeshInfo(); + + // TEMPORARY method to remove study files on closing study; + // RIGHT WAY: study files are remove automatically when meshes are destroyed + static void RemoveStudyFiles_TMP_METHOD(SALOMEDS::SComponent_ptr smeshComp); + +private: + + // creation by LoadFromFile() only + SMESH_PreMeshInfo(SMESH_Mesh_i* mesh, + const int meshID, + const std::string& medFile, + const std::string& hdfFile); + + SMESH_PreMeshInfo* newInstance() + { return new SMESH_PreMeshInfo( _mesh,_meshID,_medFileName,_hdfFileName ); } + + // reading from the new study, for which SaveToFile() was called + bool readPreInfoFromHDF(); + void hdf2meshInfo( const std::string& dataSetName, HDFgroup* infoHdfGroup ); + + // reading from the old study, for which SaveToFile() was not called + bool readMeshInfo(); + void readGroupInfo(); + void readSubMeshInfo(); + SMDSAbs_EntityType getElemType( const TopAbs_ShapeEnum shapeType, + const int nbElemsInSubMesh, + bool& isKoType) const; + + void readSubMeshes(DriverMED_R_SMESHDS_Mesh* reader) const; + + // general data + std::string _medFileName, _hdfFileName; + bool _toRemoveFiles; + int _meshID; + SMESH_Mesh_i* _mesh; + bool _isInfoOk; + + int _elemCounter; /* used as a counter while mesh info reading and + as a signal that mesh info is incorrect after reading + */ +}; + +#endif diff --git a/src/SMESH_I/SMESH_PythonDump.hxx b/src/SMESH_I/SMESH_PythonDump.hxx index 1ea9f59ff..29c544313 100644 --- a/src/SMESH_I/SMESH_PythonDump.hxx +++ b/src/SMESH_I/SMESH_PythonDump.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + #ifndef _SMESH_PYTHONDUMP_HXX_ #define _SMESH_PYTHONDUMP_HXX_ @@ -29,6 +30,7 @@ #include CORBA_SERVER_HEADER(SALOMEDS) #include +#include class SMESH_Gen_i; class SMESH_MeshEditor_i; @@ -37,7 +39,7 @@ class Resource_DataMapOfAsciiStringAsciiString; // =========================================================================================== /*! - * \brief Tool converting SMESH engine calls into commands defined in smesh.py + * \brief Tool converting SMESH engine calls into commands defined in smeshDC.py * * Implementation is in SMESH_2smeshpy.cxx */ @@ -51,19 +53,23 @@ public: * \param theScript - Input script * \param theEntry2AccessorMethod - The returning method names to access to * objects wrapped with python class + * \param theHistoricalDump - true means to keep all commands, false means + * to exclude commands relating to objects removed from study * \retval TCollection_AsciiString - Convertion result */ static TCollection_AsciiString - ConvertScript(const TCollection_AsciiString& theScript, + ConvertScript(const TCollection_AsciiString& theScript, Resource_DataMapOfAsciiStringAsciiString& theEntry2AccessorMethod, - Resource_DataMapOfAsciiStringAsciiString& theObjectNames); + Resource_DataMapOfAsciiStringAsciiString& theObjectNames, + SALOMEDS::Study_ptr& theStudy, + const bool theHistoricalDump); /*! * \brief Return the name of the python file wrapping IDL API - * \retval TCollection_AsciiString - The file name + * \retval const char* - the file name */ static const char* SmeshpyName() { return "smesh"; } - static const char* GenName() { return "smesh.smesh"; } + static const char* GenName() { return "smesh"; } }; namespace SMESH @@ -72,73 +78,113 @@ namespace SMESH class FilterManager_i; class Filter_i; class Functor_i; + class Measurements_i; -// =========================================================================================== -/*! - * \brief Utility helping in storing SMESH engine calls as python commands - */ -// =========================================================================================== + // =========================================================================================== + /*! + * \brief Object used to make TPythonDump know that its held value can be a varible + * + * TPythonDump substitute TVar with names of notebook variables if any. + */ + // =========================================================================================== + + struct SMESH_I_EXPORT TVar + { + std::vector< std::string > myVals; + TVar(CORBA::Double value); + TVar(CORBA::Long value); + TVar(CORBA::Short value); + TVar(const SMESH::double_array& value); + // string used to temporary quote variable names in order + // not to confuse variables with string arguments + static char Quote() { return '$'; } + }; + + // =========================================================================================== + /*! + * \brief Utility helping in storing SMESH engine calls as python commands + */ + // =========================================================================================== class SMESH_I_EXPORT TPythonDump { std::ostringstream myStream; - static size_t myCounter; + static size_t myCounter; + int myVarsCounter; // counts stored TVar's public: TPythonDump(); virtual ~TPythonDump(); - - TPythonDump& + + TPythonDump& + operator<<(const TVar& theVariableValue); + + TPythonDump& operator<<(long int theArg); - TPythonDump& + TPythonDump& operator<<(int theArg); - TPythonDump& + TPythonDump& operator<<(double theArg); - TPythonDump& + TPythonDump& operator<<(float theArg); - TPythonDump& + TPythonDump& operator<<(const void* theArg); - TPythonDump& + TPythonDump& operator<<(const char* theArg); - TPythonDump& + TPythonDump& operator<<(const SMESH::ElementType& theArg); - TPythonDump& + TPythonDump& + operator<<(const SMESH::GeometryType& theArg); + + TPythonDump& operator<<(const SMESH::long_array& theArg); - TPythonDump& + TPythonDump& operator<<(const SMESH::double_array& theArg); - TPythonDump& + TPythonDump& + operator<<(const SMESH::string_array& theArg); + + TPythonDump& + operator<<(SMESH::SMESH_Hypothesis_ptr theArg); + + TPythonDump& + operator<<(SMESH::SMESH_IDSource_ptr theArg); + + TPythonDump& operator<<(SALOMEDS::SObject_ptr theArg); - TPythonDump& + TPythonDump& operator<<(CORBA::Object_ptr theArg); - TPythonDump& + TPythonDump& operator<<(SMESH::FilterLibrary_i* theArg); - TPythonDump& + TPythonDump& operator<<(SMESH::FilterManager_i* theArg); - TPythonDump& + TPythonDump& operator<<(SMESH::Filter_i* theArg); - TPythonDump& + TPythonDump& operator<<(SMESH::Functor_i* theArg); - TPythonDump& + TPythonDump& + operator<<(SMESH::Measurements_i* theArg); + + TPythonDump& operator<<(SMESH_Gen_i* theArg); - TPythonDump& + TPythonDump& operator<<(SMESH_MeshEditor_i* theArg); - TPythonDump& + TPythonDump& operator<<(SMESH::MED_VERSION theArg); TPythonDump& @@ -147,18 +193,27 @@ namespace SMESH TPythonDump& operator<<(const SMESH::DirStruct & theDir); + TPythonDump& + operator<<(const SMESH::PointStruct & P); + TPythonDump& operator<<(const TCollection_AsciiString & theArg); + TPythonDump& + operator<<(const SMESH::ListOfGroups& theList); + TPythonDump& operator<<(const SMESH::ListOfGroups * theList); + TPythonDump& + operator<<(const SMESH::ListOfIDSources& theList); + static const char* SMESHGenName() { return "smeshgen"; } static const char* MeshEditorName() { return "mesh_editor"; } /*! * \brief Return marker of long string literal beginning - * \param type - a name of functionality producing the string literal + * \param type - a name of functionality producing the string literal * \retval TCollection_AsciiString - the marker string to be written into * a raw python script */ @@ -176,7 +231,7 @@ namespace SMESH * \param theLongString - the retrieved literal * \param theStringType - a name of functionality produced the literal * \retval bool - true if a string literal found - * + * * The literal is removed from theText; theFrom points position right after * the removed literal */ diff --git a/src/SMESH_I/SMESH_subMesh_i.cxx b/src/SMESH_I/SMESH_subMesh_i.cxx index f90cb300a..fd7fc3d80 100644 --- a/src/SMESH_I/SMESH_subMesh_i.cxx +++ b/src/SMESH_I/SMESH_subMesh_i.cxx @@ -1,33 +1,34 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_subMesh_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "SMESH_subMesh_i.hxx" #include "SMESH_Gen_i.hxx" #include "SMESH_Mesh_i.hxx" +#include "SMESH_PreMeshInfo.hxx" #include "Utils_CorbaException.hxx" #include "utilities.h" @@ -49,7 +50,7 @@ SMESH_subMesh_i::SMESH_subMesh_i() : SALOME::GenericObj_i( PortableServer::POA::_nil() ) { MESSAGE("SMESH_subMesh_i::SMESH_subMesh_i default, not for use"); - ASSERT(0); + ASSERT(0); } //============================================================================= @@ -59,16 +60,15 @@ SMESH_subMesh_i::SMESH_subMesh_i() //============================================================================= SMESH_subMesh_i::SMESH_subMesh_i( PortableServer::POA_ptr thePOA, - SMESH_Gen_i* gen_i, - SMESH_Mesh_i* mesh_i, - int localId ) + SMESH_Gen_i* gen_i, + SMESH_Mesh_i* mesh_i, + int localId ) : SALOME::GenericObj_i( thePOA ) { - MESSAGE("SMESH_subMesh_i::SMESH_subMesh_i"); _gen_i = gen_i; _mesh_i = mesh_i; _localId = localId; - // **** + _preMeshInfo = NULL; } //============================================================================= /*! @@ -79,7 +79,8 @@ SMESH_subMesh_i::SMESH_subMesh_i( PortableServer::POA_ptr thePOA, SMESH_subMesh_i::~SMESH_subMesh_i() { MESSAGE("SMESH_subMesh_i::~SMESH_subMesh_i"); - // **** + if ( _preMeshInfo ) delete _preMeshInfo; + _preMeshInfo = NULL; } //======================================================================= @@ -161,7 +162,10 @@ CORBA::Long SMESH_subMesh_i::GetNumberOfElements() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetNumberOfElements"); + + if ( _preMeshInfo ) + return _preMeshInfo->NbElements(); + if ( _mesh_i->_mapSubMesh.find( _localId ) == _mesh_i->_mapSubMesh.end() ) return 0; @@ -191,10 +195,15 @@ CORBA::Long SMESH_subMesh_i::GetNumberOfNodes(CORBA::Boolean all) throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetNumberOfNodes"); + if ( _mesh_i->_mapSubMesh.find( _localId ) == _mesh_i->_mapSubMesh.end() ) return 0; + if ( _preMeshInfo ) + { + if ( all ) return _preMeshInfo->NbNodes(); + else _preMeshInfo->FullLoadFromFile(); + } ::SMESH_subMesh* aSubMesh = _mesh_i->_mapSubMesh[_localId]; SMESHDS_SubMesh* aSubMeshDS = aSubMesh->GetSubMeshDS(); @@ -257,12 +266,15 @@ SMESH::long_array* SMESH_subMesh_i::GetElementsId() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetElementsId"); + SMESH::long_array_var aResult = new SMESH::long_array(); if ( _mesh_i->_mapSubMesh.find( _localId ) == _mesh_i->_mapSubMesh.end() ) return aResult._retn(); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + ::SMESH_subMesh* aSubMesh = _mesh_i->_mapSubMesh[_localId]; SMESHDS_SubMesh* aSubMeshDS = aSubMesh->GetSubMeshDS(); @@ -304,12 +316,15 @@ SMESH::long_array* SMESH_subMesh_i::GetElementsByType( SMESH::ElementType theEle throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetElementsByType"); + SMESH::long_array_var aResult = new SMESH::long_array(); if ( _mesh_i->_mapSubMesh.find( _localId ) == _mesh_i->_mapSubMesh.end() ) return aResult._retn(); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); + ::SMESH_subMesh* aSubMesh = _mesh_i->_mapSubMesh[_localId]; SMESHDS_SubMesh* aSubMeshDS = aSubMesh->GetSubMeshDS(); @@ -412,7 +427,7 @@ SMESH::long_array* SMESH_subMesh_i::GetNodesId() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetNodesId"); + SMESH::long_array_var aResult = GetElementsByType( SMESH::NODE ); return aResult._retn(); } @@ -427,7 +442,6 @@ SMESH::SMESH_Mesh_ptr SMESH_subMesh_i::GetFather() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); - MESSAGE("SMESH_subMesh_i::GetFather"); return _mesh_i->_this(); } @@ -439,7 +453,6 @@ SMESH::SMESH_Mesh_ptr SMESH_subMesh_i::GetFather() CORBA::Long SMESH_subMesh_i::GetId() { - MESSAGE("SMESH_subMesh_i::GetId"); return _localId; } @@ -458,11 +471,11 @@ GEOM::GEOM_Object_ptr SMESH_subMesh_i::GetSubShape() TopoDS_Shape S = _mesh_i->_mapSubMesh[ _localId ]->GetSubShape(); if ( !S.IsNull() ) { aShapeObj = _gen_i->ShapeToGeomObject( S ); - //mzn: N7PAL16232, N7PAL16233 - //In some cases it's possible that GEOM_Client contains the shape same to S, but - //with another orientation. - if (aShapeObj->_is_nil()) - aShapeObj = _gen_i->ShapeToGeomObject( S.Reversed() ); + //mzn: N7PAL16232, N7PAL16233 + //In some cases it's possible that GEOM_Client contains the shape same to S, but + //with another orientation. + if (aShapeObj->_is_nil()) + aShapeObj = _gen_i->ShapeToGeomObject( S.Reversed() ); } } } @@ -481,6 +494,8 @@ SALOME_MED::FAMILY_ptr SMESH_subMesh_i::GetFamily() throw (SALOME::SALOME_Exception) { Unexpect aCatch(SALOME_SalomeException); + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); SALOME_MED::MESH_var MEDMesh = GetFather()->GetMEDMesh(); SALOME_MED::Family_array_var families = @@ -501,8 +516,7 @@ SALOME_MED::FAMILY_ptr SMESH_subMesh_i::GetFamily() //============================================================================= SMESH::long_array* SMESH_subMesh_i::GetIDs() { - SMESH::long_array_var aResult = GetElementsId(); - return aResult._retn(); + return GetElementsId(); } //============================================================================= @@ -513,5 +527,100 @@ SMESH::long_array* SMESH_subMesh_i::GetIDs() SMESH::ElementType SMESH_subMesh_i::GetElementType( const CORBA::Long id, const bool iselem ) throw (SALOME::SALOME_Exception) { + if ( _preMeshInfo ) + _preMeshInfo->FullLoadFromFile(); return GetFather()->GetElementType( id, iselem ); } + + +//============================================================================= +/*! + * Returns statistic of mesh elements + * Result array of number enityties + * Inherited from SMESH_IDSource + */ +//============================================================================= +SMESH::long_array* SMESH_subMesh_i::GetMeshInfo() +{ + if ( _preMeshInfo ) + return _preMeshInfo->GetMeshInfo(); + + SMESH::long_array_var aRes = new SMESH::long_array(); + aRes->length(SMESH::Entity_Last); + for (int i = SMESH::Entity_Node; i < SMESH::Entity_Last; i++) + aRes[i] = 0; + + // get number of nodes + aRes[ SMESH::Entity_Node ] = GetNumberOfNodes(true); + + ::SMESH_subMesh* aSubMesh = _mesh_i->_mapSubMesh[_localId]; + + // get statistic from child sub-meshes + TListOfSubMeshes smList; + if ( getSubMeshes( aSubMesh, smList ) ) + for ( TListOfSubMeshes::iterator sm = smList.begin(); sm != smList.end(); ++sm ) + SMESH_Mesh_i::CollectMeshInfo( (*sm)->GetElements(), aRes ); + + return aRes._retn(); +} + + +//======================================================================= +//function : GetTypes +//purpose : Returns types of elements it contains +//======================================================================= + +SMESH::array_of_ElementType* SMESH_subMesh_i::GetTypes() +{ + if ( _preMeshInfo ) + return _preMeshInfo->GetTypes(); + + SMESH::array_of_ElementType_var types = new SMESH::array_of_ElementType; + + ::SMESH_subMesh* aSubMesh = _mesh_i->_mapSubMesh[_localId]; + if ( SMESHDS_SubMesh* smDS = aSubMesh->GetSubMeshDS() ) + { + SMDS_ElemIteratorPtr eIt = smDS->GetElements(); + if ( eIt->more() ) + { + types->length( 1 ); + types[0] = SMESH::ElementType( eIt->next()->GetType()); + } + else if ( smDS->GetNodes()->more() ) + { + TopoDS_Shape shape = aSubMesh->GetSubShape(); + while ( !shape.IsNull() && shape.ShapeType() == TopAbs_COMPOUND ) + { + TopoDS_Iterator it( shape ); + shape = it.More() ? it.Value() : TopoDS_Shape(); + } + if ( !shape.IsNull() && shape.ShapeType() == TopAbs_VERTEX ) + { + types->length( 1 ); + types[0] = SMESH::NODE; + } + } + } + return types._retn(); +} + +//======================================================================= +//function : GetMesh +//purpose : interface SMESH_IDSource +//======================================================================= + +SMESH::SMESH_Mesh_ptr SMESH_subMesh_i::GetMesh() +{ + return GetFather(); +} + +//======================================================================= +//function : IsMeshInfoCorrect +//purpose : * Returns false if GetMeshInfo() returns incorrect information that may +// * happen if mesh data is not yet fully loaded from the file of study. +//======================================================================= + +bool SMESH_subMesh_i::IsMeshInfoCorrect() +{ + return _preMeshInfo ? _preMeshInfo->IsMeshInfoCorrect() : true; +} diff --git a/src/SMESH_I/SMESH_subMesh_i.hxx b/src/SMESH_I/SMESH_subMesh_i.hxx index fcfc5673b..d9cf053a6 100644 --- a/src/SMESH_I/SMESH_subMesh_i.hxx +++ b/src/SMESH_I/SMESH_subMesh_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : SMESH_subMesh_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_SUBMESH_I_HXX_ #define _SMESH_SUBMESH_I_HXX_ @@ -40,7 +40,7 @@ #include "SMESH_Mesh_i.hxx" class SMESH_Gen_i; -class SMESH_Mesh_i; +class SMESH_PreMeshInfo; class SMESH_I_EXPORT SMESH_subMesh_i: public virtual POA_SMESH::SMESH_subMesh, @@ -50,8 +50,8 @@ public: SMESH_subMesh_i(); SMESH_subMesh_i( PortableServer::POA_ptr thePOA, SMESH_Gen_i* gen_i, - SMESH_Mesh_i* mesh_i, - int localId ); + SMESH_Mesh_i* mesh_i, + int localId ); ~SMESH_subMesh_i(); CORBA::Long GetNumberOfElements() @@ -66,9 +66,6 @@ public: SMESH::long_array* GetElementsByType( SMESH::ElementType theElemType ) throw (SALOME::SALOME_Exception); - //for omniORB conflict compilation - /*SMESH::ElementType GetElementType( const CORBA::Long id, const bool iselem ) - throw (SALOME::SALOME_Exception);*/ SMESH::ElementType GetElementType( CORBA::Long id, bool iselem ) throw (SALOME::SALOME_Exception); @@ -81,21 +78,53 @@ public: GEOM::GEOM_Object_ptr GetSubShape() throw (SALOME::SALOME_Exception); - CORBA::Long GetId(); + CORBA::Long GetId(); SALOME_MED::FAMILY_ptr GetFamily() throw (SALOME::SALOME_Exception); + + // ========================= + // interface SMESH_IDSource + // ========================= + /*! + * Returns a sequence of all element IDs + */ virtual SMESH::long_array* GetIDs(); + /*! + * Returns statistic of mesh elements + * Result array of number enityties + * Inherited from SMESH_IDSource + */ + virtual SMESH::long_array* GetMeshInfo(); + /*! + * Returns types of elements it contains + */ + virtual SMESH::array_of_ElementType* GetTypes(); + /*! + * Returns the mesh + */ + virtual SMESH::SMESH_Mesh_ptr GetMesh(); + /*! + * Returns false if GetMeshInfo() returns incorrect information that may + * happen if mesh data is not yet fully loaded from the file of study. + */ + virtual bool IsMeshInfoCorrect(); - SMESH_Mesh_i* _mesh_i; //NRI protected: - void changeLocalId(int localId) { _localId = localId; } - SMESH_Gen_i* _gen_i; - int _localId; + SMESH_Gen_i* _gen_i; + int _localId; + SMESH_Mesh_i* _mesh_i; //NRI + + void changeLocalId(int localId) { _localId = localId; } friend void SMESH_Mesh_i::CheckGeomGroupModif(); + + SMESH_PreMeshInfo* _preMeshInfo; // mesh info before full loading from study file + + SMESH_PreMeshInfo* & changePreMeshInfo() { return _preMeshInfo; } + friend class SMESH_PreMeshInfo; }; #endif diff --git a/src/SMESH_I/smeshpy.py b/src/SMESH_I/smeshpy.py index 869ea8108..fa01ce28f 100644 --- a/src/SMESH_I/smeshpy.py +++ b/src/SMESH_I/smeshpy.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses # File : smeshpy.py # Module : SMESH diff --git a/src/SMESH_PY/Makefile.am b/src/SMESH_PY/Makefile.am new file mode 100644 index 000000000..3219c8567 --- /dev/null +++ b/src/SMESH_PY/Makefile.am @@ -0,0 +1,25 @@ +# Copyright (C) 2007-2012 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 = $(smeshpypkgdir) +salomepypkg_PYTHON = \ + __init__.py \ + smeshstudytools.py diff --git a/src/SMESH_PY/__init__.py b/src/SMESH_PY/__init__.py new file mode 100644 index 000000000..f333c9216 --- /dev/null +++ b/src/SMESH_PY/__init__.py @@ -0,0 +1,19 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2010-2011 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 +# diff --git a/src/SMESH_PY/smeshstudytools.py b/src/SMESH_PY/smeshstudytools.py new file mode 100644 index 000000000..7281fe519 --- /dev/null +++ b/src/SMESH_PY/smeshstudytools.py @@ -0,0 +1,214 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2007-2012 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 +# +""" +This module provides a new class :class:`SMeshStudyTools` to facilitate the +use of mesh objects in Salome study. +""" + +import salome +SMESH = None # SMESH module is loaded only when needed + +from salome.kernel.studyedit import getStudyEditor +try: + from salome.gui import helper +except ImportError: + pass + +class SMeshStudyTools: + """ + This class provides several methods to manipulate mesh 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 SMESH + if SMESH is None: + SMESH = __import__("SMESH") + if studyEditor is None: + studyEditor = getStudyEditor() + self.editor = studyEditor + self.smeshGui = None + + def updateStudy(self, studyId=None): + """ + This function updates the tools so that it works on the + specified study. + """ + self.editor = getStudyEditor(studyId) + + def getMeshFromGroup(self, meshGroupItem): + """ + Get the mesh item owning the mesh group `meshGroupItem`. + + :type meshGroupItem: SObject + :param meshGroupItem: Mesh group belonging to the searched mesh. + + :return: The SObject corresponding to the mesh, or None if it was not + found. + """ + meshItem = None + obj = self.editor.getOrLoadObject(meshGroupItem) + group = obj._narrow(SMESH.SMESH_GroupBase) + if group is not None: # The type of the object is ok + meshObj = group.GetMesh() + meshItem = salome.ObjectToSObject(meshObj) + return meshItem + + + def getMeshObjectSelected(self): + ''' + Returns the MESH object currently selected in the active study. + ''' + sobject, entry = helper.getSObjectSelected() + meshObject = self.getMeshObjectFromEntry(entry) + return meshObject + + def getMeshObjectFromEntry(self, entry): + ''' + Returns the MESH object associated to the specified entry, + (the entry is the identifier of an item in the objects browser). + ''' + if entry is None: + return None + import smesh + smesh.SetCurrentStudy(self.editor.study) + meshObject=smesh.IDToObject(entry) + return meshObject + + def getMeshObjectFromSObject(self, sobject): + ''' + Returns the SMESH object associated to the specified SObject, + (the SObject is an item in the objects browser). + ''' + if sobject is None: + return None + + obj = self.editor.getOrLoadObject(sobject) + meshObject = obj._narrow(SMESH.SMESH_Mesh) + return meshObject + + def displayMeshObjectFromEntry(self,entry): + ''' + Display the SMESH object associated to the specified entry + (the entry is the identifier of an item in the objects browser). + ''' + if self.smeshGui is None: + self.smeshGui = salome.ImportComponentGUI("SMESH") + + if not helper.SalomeGUI.hasDesktop(): + print "displayMeshObject: no desktop available" + return + self.smeshGui.CreateAndDisplayActor(entry) + +# +# ================================================================== +# Use cases and demo functions +# ================================================================== +# + +# CAUTION: Before running this test functions, you first have to +# create (or import) an smesh object and select this object in the +# objects browser. You can run the box mesh creation procedure below +# instead. + +# How to test? +# 1. Run a SALOME application including GEOM and SMESH, and create a new study +# 2. In the console, enter: +# >>> from salome.smesh import smeshstudytools +# >>> smeshstudytools.TEST_createBoxMesh() +# 3. Select the object named "boxmesh" in the browser +# 4. In the console, enter: +# >>> smeshstudytools.TEST_selectAndExport_01() +# >>> smeshstudytools.TEST_selectAndExport_02() +# >>> smeshstudytools.TEST_display() + + +def TEST_createBoxMesh(): + theStudy = helper.getActiveStudy() + + import geompy + geompy.init_geom(theStudy) + box = geompy.MakeBoxDXDYDZ(200, 200, 200) + + import smesh, SMESH, SALOMEDS + smesh.SetCurrentStudy(theStudy) + import StdMeshers + boxmesh = smesh.Mesh(box) + Regular_1D = boxmesh.Segment() + Nb_Segments_1 = Regular_1D.NumberOfSegments(15) + Nb_Segments_1.SetDistrType( 0 ) + Quadrangle_2D = boxmesh.Quadrangle() + Hexa_3D = smesh.CreateHypothesis('Hexa_3D') + status = boxmesh.AddHypothesis(Hexa_3D) + isDone = boxmesh.Compute() + + smesh.SetName(boxmesh.GetMesh(), 'boxmesh') + if salome.sg.hasDesktop(): + salome.sg.updateObjBrowser(1) + +# +# Definitions: +# - the SObject is an item in the study (Study Object). +# - the entry is the identifier of an item. +# - the object (geom object or smesh object) is a CORBA servant +# embedded in the SALOME component container and with a reference in +# the SALOME study, so that it can be retrieved. +# + +def TEST_selectAndExport_01(): + tool = SMeshStudyTools() + myMesh = tool.getMeshObjectSelected() + myMesh.ExportUNV("/tmp/myMesh.unv") + +def TEST_selectAndExport_02(): + # In this case, we want to retrieve the name of the mesh in the + # object browser. Note that in SALOME, a mesh object has no + # name. Only the SObject in the object browser has a name + # attribute. + tool = SMeshStudyTools() + + mySObject, myEntry = helper.getSObjectSelected() + myName = mySObject.GetName() + + myMesh = tool.getMeshObjectFromEntry(myEntry) + exportFileName = "/tmp/"+myName+".unv" + myMesh.ExportUNV(exportFileName) + +def TEST_display(): + mySObject, myEntry = helper.getSObjectSelected() + + tool = SMeshStudyTools() + tool.displayMeshObjectFromEntry(myEntry) + +if __name__ == "__main__": + TEST_selectAndExport_01() + TEST_selectAndExport_02() + TEST_display() diff --git a/src/SMESH_SWIG/Makefile.am b/src/SMESH_SWIG/Makefile.am index 7b6ef9fe4..ab94f663c 100644 --- a/src/SMESH_SWIG/Makefile.am +++ b/src/SMESH_SWIG/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : Makefile.in # Author : Nicolas REJNERI, Paul RASCLE # Modified by : Alexander BORODIN (OCN) - autotools usage @@ -31,6 +29,7 @@ include $(top_srcdir)/adm_local/unix/make_common_starter.am dist_salomescript_DATA= \ smesh.py \ smeshDC.py \ + StdMeshersDC.py \ batchmode_smesh.py \ batchmode_mefisto.py \ ex00_all.py \ diff --git a/src/SMESH_SWIG/PAL_MESH_041_mesh.py b/src/SMESH_SWIG/PAL_MESH_041_mesh.py index c1138f9be..bbfdd1abf 100755 --- a/src/SMESH_SWIG/PAL_MESH_041_mesh.py +++ b/src/SMESH_SWIG/PAL_MESH_041_mesh.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + import salome import geompy import smesh @@ -53,7 +55,7 @@ Id_face1 = geompy.addToStudy(face1,"Face1") #-----------------------------SMESH------------------------------------------- - +smesh.SetCurrentStudy(salome.myStudy) # -- Init mesh -- plane_mesh = salome.IDToObject( Id_face1) diff --git a/src/SMESH_SWIG/PAL_MESH_043_2D.py b/src/SMESH_SWIG/PAL_MESH_043_2D.py index 678f6db30..8db9bf12f 100755 --- a/src/SMESH_SWIG/PAL_MESH_043_2D.py +++ b/src/SMESH_SWIG/PAL_MESH_043_2D.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_testExtrusion2D.py # Module : SMESH # Description : Create meshes to test extrusion of mesh elements along path @@ -51,6 +53,7 @@ id_ellipse2 = geompy.addToStudy(ellipse2, "Ellips 2") #---------------------------------SMESH +smesh.SetCurrentStudy(salome.myStudy) # create the path mesh mesh1 = smesh.Mesh(ellipse1, "Path Mesh") diff --git a/src/SMESH_SWIG/PAL_MESH_043_3D.py b/src/SMESH_SWIG/PAL_MESH_043_3D.py index e40854bbe..9607f9bb7 100755 --- a/src/SMESH_SWIG/PAL_MESH_043_3D.py +++ b/src/SMESH_SWIG/PAL_MESH_043_3D.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_testExtrusion3D.py # Module : SMESH # Description : Create meshes to test extrusion of mesh elements along path @@ -49,6 +51,7 @@ idcircle = geompy.addToStudy(circle, "Circle") idface = geompy.addToStudy(face, "Circular face") +smesh.SetCurrentStudy(salome.myStudy) # init a Mesh with the circular face mesh1 = smesh.Mesh(face, "Mesh on circular face") diff --git a/src/SMESH_SWIG/SMESH_AdvancedEditor.py b/src/SMESH_SWIG/SMESH_AdvancedEditor.py index 86e8ce1f0..958dba283 100644 --- a/src/SMESH_SWIG/SMESH_AdvancedEditor.py +++ b/src/SMESH_SWIG/SMESH_AdvancedEditor.py @@ -1,27 +1,32 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: utf-8 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + +import salome import smesh import math +salome.salome_init() + def GetNewNodes(mesh,Elems,OldNodes): """ Auxilary function, which return list of nodes from @@ -50,7 +55,8 @@ def GetNewNodes(mesh,Elems,OldNodes): pass return newnodes - +smesh.SetCurrentStudy(salome.myStudy) + # create empty mesh mesh = smesh.Mesh() diff --git a/src/SMESH_SWIG/SMESH_BelongToGeom.py b/src/SMESH_SWIG/SMESH_BelongToGeom.py index b66986067..d1ef60872 100644 --- a/src/SMESH_SWIG/SMESH_BelongToGeom.py +++ b/src/SMESH_SWIG/SMESH_BelongToGeom.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + from SMESH_test1 import * ## Old style @@ -38,6 +40,7 @@ def CheckBelongToGeomFilterOld(theMeshGen, theMesh, theShape, theSubShape, theEl aBelongToGeom.SetElementType(theElemType) aFilter.SetPredicate(aBelongToGeom) + aFilterMgr.UnRegister() return aFilter.GetElementsId(theMesh) ## Current style diff --git a/src/SMESH_SWIG/SMESH_BuildCompound.py b/src/SMESH_SWIG/SMESH_BuildCompound.py index 87c7e47ca..0c1a9126d 100644 --- a/src/SMESH_SWIG/SMESH_BuildCompound.py +++ b/src/SMESH_SWIG/SMESH_BuildCompound.py @@ -1,24 +1,23 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_BuildCompound.py # Author : Alexander KOVALEV # Module : SMESH @@ -28,6 +27,7 @@ # ! as some sequences of symbols from this example are used during # ! documentation generation to identify certain places of this file # +import salome import geompy import smesh @@ -60,6 +60,8 @@ geompy.addToStudy(Box_sup, "Box_sup") geompy.addToStudyInFather(Box_sup, Fsup2, "Fsup") geompy.addToStudyInFather(Box_sup, Finf2, "Finf") +smesh.SetCurrentStudy(salome.myStudy) + ## create a bottom mesh Mesh_inf = smesh.Mesh(Box_inf, "Mesh_inf") algo1D_1=Mesh_inf.Segment() @@ -93,6 +95,6 @@ Compound1 = smesh.smesh.Concatenate([Mesh_inf.GetMesh(), Mesh_sup.GetMesh()], 0, smesh.SetName(Compound1, 'Compound_with_RenamedGrps_and_MergeElems') # create a compound of two meshes with uniting groups with the same names and # creating groups of all elements -Compound2 = smesh.smesh.ConcatenateWithGroups([Mesh_inf.GetMesh(), Mesh_sup.GetMesh()], 1, 0, 1e-05) +Compound2 = smesh.smesh.Concatenate([Mesh_inf.GetMesh(), Mesh_sup.GetMesh()], 1, 0, 1e-05, True) smesh.SetName(Compound2, 'Compound_with_UniteGrps_and_GrpsOfAllElems') #end diff --git a/src/SMESH_SWIG/SMESH_GroupFromGeom.py b/src/SMESH_SWIG/SMESH_GroupFromGeom.py index 1bc88014b..8c746d15e 100644 --- a/src/SMESH_SWIG/SMESH_GroupFromGeom.py +++ b/src/SMESH_SWIG/SMESH_GroupFromGeom.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_GroupFromGeom.py # Module : SMESH # diff --git a/src/SMESH_SWIG/SMESH_GroupFromGeom2.py b/src/SMESH_SWIG/SMESH_GroupFromGeom2.py index 0b15ecbac..02ec044fe 100755 --- a/src/SMESH_SWIG/SMESH_GroupFromGeom2.py +++ b/src/SMESH_SWIG/SMESH_GroupFromGeom2.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #============================================================================== # Info. # Bug (from script, bug) : SMESH_GroupFromGeom.py, PAL6945 diff --git a/src/SMESH_SWIG/SMESH_GroupLyingOnGeom.py b/src/SMESH_SWIG/SMESH_GroupLyingOnGeom.py index 67d498fad..03978e0d5 100644 --- a/src/SMESH_SWIG/SMESH_GroupLyingOnGeom.py +++ b/src/SMESH_SWIG/SMESH_GroupLyingOnGeom.py @@ -1,26 +1,31 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + +import salome import smesh +smesh.SetCurrentStudy(salome.myStudy) + def BuildGroupLyingOn(theMesh, theElemType, theName, theShape): aFilterMgr = smesh.smesh.CreateFilterManager() aFilter = aFilterMgr.CreateFilter() @@ -31,6 +36,7 @@ def BuildGroupLyingOn(theMesh, theElemType, theName, theShape): aFilter.SetPredicate(aLyingOnGeom) anIds = aFilter.GetElementsId(theMesh) + aFilterMgr.UnRegister() aGroup = theMesh.CreateGroup(theElemType, theName) aGroup.Add(anIds) diff --git a/src/SMESH_SWIG/SMESH_Nut.py b/src/SMESH_SWIG/SMESH_Nut.py index 6d2ac9e86..e292cbc2a 100755 --- a/src/SMESH_SWIG/SMESH_Nut.py +++ b/src/SMESH_SWIG/SMESH_Nut.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + ##################################################################### #Created :17/02/2005 #Auhtor :MASLOV Eugeny, KOVALTCHUK Alexey @@ -72,7 +74,7 @@ for i in range(0, len(CommonExplodedListEdges)): #Fillet applying print "Fillet creation..." -Fillet_1 = geompy.MakeFillet(Common_1, 10, geompy.ShapeType["EDGE"], [6]) +Fillet_1 = geompy.MakeFillet(Common_1, 10, geompy.ShapeType["EDGE"], [5]) geompy.addToStudy(Fillet_1, "Fillet_1") #Chamfer applying @@ -85,7 +87,7 @@ geompy.addToStudy(Chamfer_2, "Chamfer_2") #Import of the shape from "slots.brep" print "Import multi-rotation from the DATA_DIR/Shapes/Brep/slots.brep" thePath = os.getenv("DATA_DIR") -theFileName = thePath + "/Shapes/Brep/slots.brep" +theFileName = os.path.join( thePath,"Shapes","Brep","slots.brep") theShapeForCut = geompy.ImportBREP(theFileName) geompy.addToStudy(theShapeForCut, "slot.brep_1") @@ -96,6 +98,8 @@ Cut_1_ID = geompy.addToStudy(Cut_1, "Cut_1") #Mesh creation +smesh.SetCurrentStudy(salome.myStudy) + # -- Init -- shape_mesh = salome.IDToObject( Cut_1_ID ) diff --git a/src/SMESH_SWIG/SMESH_Partition1_tetra.py b/src/SMESH_SWIG/SMESH_Partition1_tetra.py index 11a68dfa4..ddd5bb16e 100644 --- a/src/SMESH_SWIG/SMESH_Partition1_tetra.py +++ b/src/SMESH_SWIG/SMESH_Partition1_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of the geometry generated by the Python script GEOM_Partition1.py # Hypothesis and algorithms for the mesh generation are global # -- Rayon de la bariere @@ -86,7 +88,7 @@ print "number of Edges in alveole : ", len(subEdgeList) subshapes = geompy.SubShapeAll(alveole, geompy.ShapeType["SHAPE"]) -## there are 9 subshapes +## there are 9 sub-shapes comp1 = geompy.MakeCompound( [ subshapes[0], subshapes[1] ] ) comp2 = geompy.MakeCompound( [ subshapes[2], subshapes[3] ] ) @@ -118,6 +120,7 @@ status = geompy.CheckShape(alveole) print " check status ", status # ---- launch SMESH +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the alveole shape_mesh = salome.IDToObject( idalveole ) diff --git a/src/SMESH_SWIG/SMESH_Sphere.py b/src/SMESH_SWIG/SMESH_Sphere.py index b39917819..64a390752 100644 --- a/src/SMESH_SWIG/SMESH_Sphere.py +++ b/src/SMESH_SWIG/SMESH_Sphere.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # GEOM GEOM_SWIG : binding of C++ omplementaion with Python # File : GEOM_Sphere.py # Author : Damien COQUERET, Open CASCADE @@ -77,8 +79,8 @@ Cube = MakeHexa2Faces(Face1, Face3) Common1 = MakeBoolean(Sphere, Block, 1) Common2 = MakeRotation(Common1, VZ, Angle90) -MultiBlock1 = MakeMultiTransformation1D(Common1, 21, -1, 3) -MultiBlock2 = MakeMultiTransformation1D(Common2, 31, -1, 3) +MultiBlock1 = MakeMultiTransformation1D(Common1, 20, -1, 3) +MultiBlock2 = MakeMultiTransformation1D(Common2, 30, -1, 3) #Reconstruct sphere from several blocks ShapesList.append(Cube) @@ -102,6 +104,7 @@ Id_Result = addToStudy(Result, "Result") #----------------------------------------------------------------------- #Meshing +smesh.SetCurrentStudy(salome.myStudy) my_hexa = smesh.Mesh(Result, "Sphere_Mesh") algo = my_hexa.Segment() algo.NumberOfSegments(NbSeg) diff --git a/src/SMESH_SWIG/SMESH_blocks.py b/src/SMESH_SWIG/SMESH_blocks.py index e61ce50f2..2de2a408f 100644 --- a/src/SMESH_SWIG/SMESH_blocks.py +++ b/src/SMESH_SWIG/SMESH_blocks.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESH_SWIG : binding of C++ implementaion with Python # File : SMESH_blocks.py # Author : Julia DOROVSKIKH @@ -35,6 +37,7 @@ import GEOM_Spanner isBlocksTest = 0 # False isMeshTest = 1 # True +smesh.SetCurrentStudy(salome.myStudy) GEOM_Spanner.MakeSpanner(geompy, math, isBlocksTest, isMeshTest, smesh) diff --git a/src/SMESH_SWIG/SMESH_box.py b/src/SMESH_SWIG/SMESH_box.py index b57138a1c..8680208cb 100755 --- a/src/SMESH_SWIG/SMESH_box.py +++ b/src/SMESH_SWIG/SMESH_box.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #============================================================================== # Info. # Bug (from script, bug) : box.py, PAL5223 @@ -46,6 +48,7 @@ box = geompy.MakeBox(0.,0.,0.,1.,1.,1.) boxId = geompy.addToStudy(box,"box") # ---- SMESH +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh diff --git a/src/SMESH_SWIG/SMESH_box2_tetra.py b/src/SMESH_SWIG/SMESH_box2_tetra.py index c77f1f001..0aaa8cbab 100644 --- a/src/SMESH_SWIG/SMESH_box2_tetra.py +++ b/src/SMESH_SWIG/SMESH_box2_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of the geometry union of 2 boxes having a face in common # Hypothesis and algorithms for the mesh generation are global # @@ -72,6 +74,7 @@ print "number of Edges in shell : ", len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the shell diff --git a/src/SMESH_SWIG/SMESH_box3_tetra.py b/src/SMESH_SWIG/SMESH_box3_tetra.py index e13fd8659..503ff4f87 100644 --- a/src/SMESH_SWIG/SMESH_box3_tetra.py +++ b/src/SMESH_SWIG/SMESH_box3_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of the geometry union of 3 boxes aligned where the middle # one has a race in common with the two others. # Hypothesis and algorithms for the mesh generation are global @@ -83,7 +85,7 @@ print "number of Edges in shell : ", len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- - +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the shell diff --git a/src/SMESH_SWIG/SMESH_box_tetra.py b/src/SMESH_SWIG/SMESH_box_tetra.py index 39dd1db2d..4aff58411 100644 --- a/src/SMESH_SWIG/SMESH_box_tetra.py +++ b/src/SMESH_SWIG/SMESH_box_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of a simple box. Hypothesis and algorithms for # the mesh generation are global # @@ -44,6 +46,7 @@ print "number of Edges in box : ", len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the boxe diff --git a/src/SMESH_SWIG/SMESH_controls.py b/src/SMESH_SWIG/SMESH_controls.py index 18cdb4a4c..d7f533cab 100644 --- a/src/SMESH_SWIG/SMESH_controls.py +++ b/src/SMESH_SWIG/SMESH_controls.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_control.py # Author : Sergey LITONIN # Module : SMESH @@ -126,4 +128,16 @@ print "Criterion: Borders at multi-connections = 2 Nb = ", len( anIds ) #print anIds[ i ] +# Criterion : Element Diameter 2D > 10 + +# create group +aGroup = mesh.MakeGroup("Element Diameter 2D > 10", smesh.FACE, smesh.FT_MaxElementLength2D, smesh.FT_MoreThan, 10 ) + +# print result +anIds = aGroup.GetIDs() +print "Criterion: Element Diameter 2D > 10 Nb = ", len( anIds ) +#for i in range( len( anIds ) ): + #print anIds[ i ] + + salome.sg.updateObjBrowser(1) diff --git a/src/SMESH_SWIG/SMESH_demo_hexa2_upd.py b/src/SMESH_SWIG/SMESH_demo_hexa2_upd.py index 245fccec4..555ad5b08 100755 --- a/src/SMESH_SWIG/SMESH_demo_hexa2_upd.py +++ b/src/SMESH_SWIG/SMESH_demo_hexa2_upd.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #============================================================================== # Info. # Bug (from script, bug) : SMESH_demo_hexa2_upd.py, PAL6781 @@ -123,6 +125,7 @@ for i in range(8): idEdgeZ.append(geompy.addToStudyInFather(vol,edgeZ[i],"EdgeZ"+str(i+1))) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the volume diff --git a/src/SMESH_SWIG/SMESH_fixation.py b/src/SMESH_SWIG/SMESH_fixation.py index 597b1d0ea..e5a68fdec 100644 --- a/src/SMESH_SWIG/SMESH_fixation.py +++ b/src/SMESH_SWIG/SMESH_fixation.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_fix_volute.py # Author : Paul RASCLE, EDF # Module : SMESH diff --git a/src/SMESH_SWIG/SMESH_fixation_hexa.py b/src/SMESH_SWIG/SMESH_fixation_hexa.py index 5195cc9ae..3110f003a 100644 --- a/src/SMESH_SWIG/SMESH_fixation_hexa.py +++ b/src/SMESH_SWIG/SMESH_fixation_hexa.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Hexahedrization of the geometry generated by the Python script # SMESH_fixation.py # Hypothesis and algorithms for the mesh generation are global @@ -44,6 +46,7 @@ status = geompy.CheckShape(compshell) print " check status ", status ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the compshell shape_mesh = salome.IDToObject( idcomp ) diff --git a/src/SMESH_SWIG/SMESH_fixation_netgen.py b/src/SMESH_SWIG/SMESH_fixation_netgen.py index ae0039735..f3374b7bf 100644 --- a/src/SMESH_SWIG/SMESH_fixation_netgen.py +++ b/src/SMESH_SWIG/SMESH_fixation_netgen.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of the geometry generated by the Python script # SMESH_fixation.py # The new Netgen algorithm is used that discretizes baoundaries itself @@ -44,6 +46,7 @@ status = geompy.CheckShape(compshell) print " check status ", status ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) print "-------------------------- create Mesh, algorithm, hypothesis" diff --git a/src/SMESH_SWIG/SMESH_fixation_tetra.py b/src/SMESH_SWIG/SMESH_fixation_tetra.py index bb190b30f..b94c354a3 100644 --- a/src/SMESH_SWIG/SMESH_fixation_tetra.py +++ b/src/SMESH_SWIG/SMESH_fixation_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Tetrahedrization of the geometry generated by the Python script # SMESH_fixation.py # Hypothesis and algorithms for the mesh generation are global @@ -44,6 +46,7 @@ status = geompy.CheckShape(compshell) print " check status ", status ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the compshell diff --git a/src/SMESH_SWIG/SMESH_flight_skin.py b/src/SMESH_SWIG/SMESH_flight_skin.py index 6aa8d3716..2d5206194 100644 --- a/src/SMESH_SWIG/SMESH_flight_skin.py +++ b/src/SMESH_SWIG/SMESH_flight_skin.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Triangulation of the skin of the geometry from a Brep representing a plane # Hypothesis and algorithms for the mesh generation are global # @@ -54,6 +56,7 @@ print "number of Edges in flight : ", len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the shell shape_mesh = salome.IDToObject( idShape ) diff --git a/src/SMESH_SWIG/SMESH_freebord.py b/src/SMESH_SWIG/SMESH_freebord.py index b1c1e8b9b..56b3fe13f 100644 --- a/src/SMESH_SWIG/SMESH_freebord.py +++ b/src/SMESH_SWIG/SMESH_freebord.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + import salome import geompy import smesh @@ -40,6 +42,7 @@ idbox = geompy.addToStudy( aBox, "box" ) aBox = salome.IDToObject( idbox ) # Create mesh +smesh.SetCurrentStudy(salome.myStudy) mesh = smesh.Mesh(aBox, "Mesh_freebord") diff --git a/src/SMESH_SWIG/SMESH_hexaedre.py b/src/SMESH_SWIG/SMESH_hexaedre.py index d32e9b0bd..4159ded64 100755 --- a/src/SMESH_SWIG/SMESH_hexaedre.py +++ b/src/SMESH_SWIG/SMESH_hexaedre.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + #============================================================================== # Info. # Bug (from script, bug) : hexaedre_modified.py, PAL6194, PAL7153 @@ -94,6 +96,7 @@ salome.sg.updateObjBrowser(1) # ----------------------------------------------------------------------------- print "-------------------------- mesh" +smesh.SetCurrentStudy(salome.myStudy) # ---- init a Mesh with the geom shape shape_mesh = blob diff --git a/src/SMESH_SWIG/SMESH_mechanic.py b/src/SMESH_SWIG/SMESH_mechanic.py index f1b02854d..5411c8dfd 100644 --- a/src/SMESH_SWIG/SMESH_mechanic.py +++ b/src/SMESH_SWIG/SMESH_mechanic.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_withHole.py # Author : Lucien PIGNOLONI # Module : SMESH @@ -94,31 +96,32 @@ Id_mechanic = geompy.addToStudy( mechanic, "mechanic" ) # ---- explode on faces SubFaceL = geompy.SubShapeAllSorted(mechanic, geompy.ShapeType["FACE"]) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face1 = SubFaceL[0] name = geompy.SubShapeName( sub_face1, mechanic ) Id_SubFace1 = geompy.addToStudyInFather( mechanic, sub_face1, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face2 = SubFaceL[4] name = geompy.SubShapeName( sub_face2, mechanic ) Id_SubFace2 = geompy.addToStudyInFather( mechanic, sub_face2, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face3 = SubFaceL[5] name = geompy.SubShapeName( sub_face3, mechanic ) Id_SubFace3 = geompy.addToStudyInFather( mechanic, sub_face3, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face4 = SubFaceL[10] name = geompy.SubShapeName( sub_face4, mechanic ) Id_SubFace4 = geompy.addToStudyInFather( mechanic, sub_face4, name ) # ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # -- Init -- shape_mesh = salome.IDToObject( Id_mechanic ) diff --git a/src/SMESH_SWIG/SMESH_mechanic_editor.py b/src/SMESH_SWIG/SMESH_mechanic_editor.py index 80780ca63..f754354a9 100644 --- a/src/SMESH_SWIG/SMESH_mechanic_editor.py +++ b/src/SMESH_SWIG/SMESH_mechanic_editor.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: utf-8 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_withHole.py # Author : Lucien PIGNOLONI # Module : SMESH @@ -29,6 +31,7 @@ import salome import geompy import smesh +salome.salome_init() # ---------------------------- GEOM -------------------------------------- # ---- define contigous arcs and segment to define a closed wire @@ -92,31 +95,32 @@ Id_mechanic = geompy.addToStudy( mechanic, "mechanic" ) # ---- explode on faces SubFaceL = geompy.SubShapeAllSorted(mechanic, geompy.ShapeType["FACE"]) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face1 = SubFaceL[0] name = geompy.SubShapeName( sub_face1, mechanic ) Id_SubFace1 = geompy.addToStudyInFather( mechanic, sub_face1, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face2 = SubFaceL[4] name = geompy.SubShapeName( sub_face2, mechanic ) Id_SubFace2 = geompy.addToStudyInFather( mechanic, sub_face2, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face3 = SubFaceL[5] name = geompy.SubShapeName( sub_face3, mechanic ) Id_SubFace3 = geompy.addToStudyInFather( mechanic, sub_face3, name ) -# ---- add a face sub shape in study to be meshed different +# ---- add a face sub-shape in study to be meshed different sub_face4 = SubFaceL[10] name = geompy.SubShapeName( sub_face4, mechanic ) Id_SubFace4 = geompy.addToStudyInFather( mechanic, sub_face4, name ) # ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) # -- Init -- shape_mesh = salome.IDToObject( Id_mechanic ) @@ -189,9 +193,9 @@ print "Number of tetrahedrons: ", mesh.NbTetras() mesh.SplitQuadObject(submesh2, 1) #2 cutting of triangles of the group -FacesTriToQuad = [2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2421, 2422] +FacesTriToQuad = [ 2391, 2824, 2825, 2826, 2827, 2828, 2832, 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2841, 2844, 2845, 2847, 2854, 2861, 2863, 2922, 2923, 2924, 2925, 2926, 2927, 2928, 2929, 2930, 2931, 2932, 2933, 2934, 2935, 2936, 2937, 2938, 2940, 2941, 2946, 2951, 2970, 2971, 2972, 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 2981, 2982, 2983, 2984, 2985 ] GroupTriToQuad = mesh.MakeGroupByIds("Group of faces (quad)", smesh.FACE, FacesTriToQuad) -mesh.TriToQuadObject(GroupTriToQuad, None , 1.57) +mesh.TriToQuadObject(GroupTriToQuad, smesh.FT_AspectRatio , 1.57) #3 extrusion of the group point = smesh.PointStruct(0, 0, 5) diff --git a/src/SMESH_SWIG/SMESH_mechanic_netgen.py b/src/SMESH_SWIG/SMESH_mechanic_netgen.py index e296eaed1..f5088378b 100644 --- a/src/SMESH_SWIG/SMESH_mechanic_netgen.py +++ b/src/SMESH_SWIG/SMESH_mechanic_netgen.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # Quadrangulation of the geometry generated by the Python script # SMESH_mechanic.py # The new Netgen algorithm is used that discretizes baoundaries itself @@ -103,6 +105,7 @@ print "number of Faces in mechanic : ",len(subFaceList) print "number of Edges in mechanic : ",len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) print "-------------------------- create Mesh, algorithm, hypothesis" diff --git a/src/SMESH_SWIG/SMESH_mechanic_tetra.py b/src/SMESH_SWIG/SMESH_mechanic_tetra.py index ff2f421f7..135d5d152 100644 --- a/src/SMESH_SWIG/SMESH_mechanic_tetra.py +++ b/src/SMESH_SWIG/SMESH_mechanic_tetra.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_withHole.py # Author : Lucien PIGNOLONI # Module : SMESH @@ -103,6 +105,7 @@ print "number of Faces in mechanic : ",len(subFaceList) print "number of Edges in mechanic : ",len(subEdgeList) ### ---------------------------- SMESH -------------------------------------- +smesh.SetCurrentStudy(salome.myStudy) shape_mesh = salome.IDToObject( Id_mechanic ) diff --git a/src/SMESH_SWIG/SMESH_reg.py b/src/SMESH_SWIG/SMESH_reg.py index 8a0ec7cfc..afd2a0a7c 100644 --- a/src/SMESH_SWIG/SMESH_reg.py +++ b/src/SMESH_SWIG/SMESH_reg.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_reg.py # Module : SMESH # @@ -57,7 +59,7 @@ salome.sg.updateObjBrowser(1); # ---- launch SMESH smeshgui = salome.ImportComponentGUI("SMESH") smeshgui.Init(salome.myStudyId) - +smesh.SetCurrentStudy(salome.myStudy) # ---- Creating meshes diff --git a/src/SMESH_SWIG/SMESH_shared_modules.py b/src/SMESH_SWIG/SMESH_shared_modules.py index 01286ea65..e0021db89 100644 --- a/src/SMESH_SWIG/SMESH_shared_modules.py +++ b/src/SMESH_SWIG/SMESH_shared_modules.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + """ """ diff --git a/src/SMESH_SWIG/SMESH_test.py b/src/SMESH_SWIG/SMESH_test.py index bc98e1c5c..7b2bbbcac 100644 --- a/src/SMESH_SWIG/SMESH_test.py +++ b/src/SMESH_SWIG/SMESH_test.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses # File : SMESH_test.py # Module : SMESH @@ -55,6 +57,7 @@ ide = geompy.addToStudyInFather(face, edge, name) # ---- SMESH +smesh.SetCurrentStudy(salome.myStudy) box = salome.IDToObject(idb) mesh = smesh.Mesh(box, "Meshbox") @@ -123,11 +126,17 @@ for a in log: elif comType == 2: for i in range(a.number): ind = a.indexes[ii] + print ind ii = ii+1 + print ii i1 = a.indexes[ii] ii = ii+1 i2 = a.indexes[ii] + print i2 ii = ii+1 + print "ii", ii i3 = a.indexes[ii] + print i3 + #ii = ii+1 ii = ii+1 print "AddTriangle %i - %i %i %i" % (ind, i1, i2, i3) diff --git a/src/SMESH_SWIG/SMESH_test0.py b/src/SMESH_SWIG/SMESH_test0.py index 421acc1ed..10fd4ab4b 100644 --- a/src/SMESH_SWIG/SMESH_test0.py +++ b/src/SMESH_SWIG/SMESH_test0.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test0.py # Module : SMESH # diff --git a/src/SMESH_SWIG/SMESH_test1.py b/src/SMESH_SWIG/SMESH_test1.py index 25ce49704..a739dc9b9 100644 --- a/src/SMESH_SWIG/SMESH_test1.py +++ b/src/SMESH_SWIG/SMESH_test1.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test1.py # Module : SMESH # @@ -26,7 +28,6 @@ import salome import geompy import smesh - # ---- define a box box = geompy.MakeBox(0., 0., 0., 100., 200., 300.) @@ -59,6 +60,8 @@ idedge = geompy.addToStudyInFather(face, edge, name) # ---- SMESH +smesh.SetCurrentStudy(salome.myStudy) + # ---- Init a Mesh with the box mesh = smesh.Mesh(box, "Meshbox") diff --git a/src/SMESH_SWIG/SMESH_test1_AndDisplay.py b/src/SMESH_SWIG/SMESH_test1_AndDisplay.py index e529d2c13..9c1578404 100644 --- a/src/SMESH_SWIG/SMESH_test1_AndDisplay.py +++ b/src/SMESH_SWIG/SMESH_test1_AndDisplay.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test1.py # Module : SMESH # @@ -59,6 +61,8 @@ idedge = geompy.addToStudyInFather(face, edge, name) # ---- SMESH +smesh.SetCurrentStudy(salome.myStudy) + # ---- Init a Mesh with the box mesh = smesh.Mesh(box, "Meshbox") diff --git a/src/SMESH_SWIG/SMESH_test2.py b/src/SMESH_SWIG/SMESH_test2.py index fe38b2da4..89e4e8f11 100644 --- a/src/SMESH_SWIG/SMESH_test2.py +++ b/src/SMESH_SWIG/SMESH_test2.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test2.py # Module : SMESH # diff --git a/src/SMESH_SWIG/SMESH_test3.py b/src/SMESH_SWIG/SMESH_test3.py index 59c853a46..04455050b 100644 --- a/src/SMESH_SWIG/SMESH_test3.py +++ b/src/SMESH_SWIG/SMESH_test3.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test3.py # Module : SMESH #import salome diff --git a/src/SMESH_SWIG/SMESH_test4.py b/src/SMESH_SWIG/SMESH_test4.py index dd86094d5..230b1f3fb 100755 --- a/src/SMESH_SWIG/SMESH_test4.py +++ b/src/SMESH_SWIG/SMESH_test4.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + import salome import geompy import smesh @@ -39,6 +41,7 @@ face = salome.IDToObject(idface) # ---- SMESH +smesh.SetCurrentStudy(salome.myStudy) mesh = smesh.Mesh(box, "Meshbox") # Set 1D algorithm/hypotheses to mesh diff --git a/src/SMESH_SWIG/SMESH_test5.py b/src/SMESH_SWIG/SMESH_test5.py index 03a380ca6..749c9d9d9 100644 --- a/src/SMESH_SWIG/SMESH_test5.py +++ b/src/SMESH_SWIG/SMESH_test5.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : SMESH_test5.py # Module : SMESH # @@ -29,6 +31,8 @@ import CORBA import os import os.path +smesh.SetCurrentStudy(salome.myStudy) + def SetSObjName(theSObj,theName) : ok, anAttr = theSObj.FindAttribute("AttributeName") if ok: diff --git a/src/SMESH_SWIG/StdMeshersDC.py b/src/SMESH_SWIG/StdMeshersDC.py new file mode 100644 index 000000000..af20cb4e2 --- /dev/null +++ b/src/SMESH_SWIG/StdMeshersDC.py @@ -0,0 +1,1126 @@ +# Copyright (C) 2007-2012 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 +# + +from smesh import Mesh_Algorithm, AssureGeomPublished, IsEqual, ParseParameters +from smesh import GetName, TreatHypoStatus +from smeshDC import Mesh + +import StdMeshers + +# Types of algorithms +REGULAR = "Regular_1D" +PYTHON = "Python_1D" +COMPOSITE = "CompositeSegment_1D" +MEFISTO = "MEFISTO_2D" +Hexa = "Hexa_3D" +QUADRANGLE = "Quadrangle_2D" +RADIAL_QUAD = "RadialQuadrangle_1D2D" + + +# import items of enum QuadType +for e in StdMeshers.QuadType._items: exec('%s = StdMeshers.%s'%(e,e)) + + +# Public class: Mesh_Segment +# -------------------------- + +## Class to define a REGULAR 1D algorithm for discretization. It is created by +# calling Mesh.Segment(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Segment(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Segment" + ## Name of algorithm type + algoType = REGULAR + isDefault = True + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + ## Defines "LocalLength" hypothesis to cut an edge in several segments with the same length + # @param l for the length of segments that cut an edge + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @param p precision, used for calculation of the number of segments. + # The precision should be a positive, meaningful value within the range [0,1]. + # In general, the number of segments is calculated with the formula: + # nb = ceil((edge_length / l) - p) + # Function ceil rounds its argument to the higher integer. + # So, p=0 means rounding of (edge_length / l) to the higher integer, + # p=0.5 means rounding of (edge_length / l) to the nearest integer, + # p=1 means rounding of (edge_length / l) to the lower integer. + # Default value is 1e-07. + # @return an instance of StdMeshers_LocalLength hypothesis + # @ingroup l3_hypos_1dhyps + def LocalLength(self, l, UseExisting=0, p=1e-07): + comFun=lambda hyp, args: IsEqual(hyp.GetLength(), args[0]) and IsEqual(hyp.GetPrecision(), args[1]) + hyp = self.Hypothesis("LocalLength", [l,p], UseExisting=UseExisting, CompareMethod=comFun) + hyp.SetLength(l) + hyp.SetPrecision(p) + return hyp + + ## Defines "MaxSize" hypothesis to cut an edge into segments not longer than given value + # @param length is optional maximal allowed length of segment, if it is omitted + # the preestimated length is used that depends on geometry size + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @return an instance of StdMeshers_MaxLength hypothesis + # @ingroup l3_hypos_1dhyps + def MaxSize(self, length=0.0, UseExisting=0): + hyp = self.Hypothesis("MaxLength", [length], UseExisting=UseExisting) + if length > 0.0: + # set given length + hyp.SetLength(length) + if not UseExisting: + # set preestimated length + gen = self.mesh.smeshpyD + initHyp = gen.GetHypothesisParameterValues("MaxLength", "libStdMeshersEngine.so", + self.mesh.GetMesh(), self.mesh.GetShape(), + False) # <- byMesh + preHyp = initHyp._narrow(StdMeshers.StdMeshers_MaxLength) + if preHyp: + hyp.SetPreestimatedLength( preHyp.GetPreestimatedLength() ) + pass + pass + hyp.SetUsePreestimatedLength( length == 0.0 ) + return hyp + + ## Defines "NumberOfSegments" hypothesis to cut an edge in a fixed number of segments + # @param n for the number of segments that cut an edge + # @param s for the scale factor (optional) + # @param reversedEdges is a list of edges to mesh using reversed orientation. + # A list item can also be a tuple (edge, 1st_vertex_of_edge) + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - create a new one + # @return an instance of StdMeshers_NumberOfSegments hypothesis + # @ingroup l3_hypos_1dhyps + def NumberOfSegments(self, n, s=[], reversedEdges=[], UseExisting=0): + if not isinstance(reversedEdges,list): #old version script, before adding reversedEdges + reversedEdges, UseExisting = [], reversedEdges + entry = self.MainShapeEntry() + reversedEdgeInd = self.ReversedEdgeIndices(reversedEdges) + if s == []: + hyp = self.Hypothesis("NumberOfSegments", [n, reversedEdgeInd, entry], + UseExisting=UseExisting, + CompareMethod=self._compareNumberOfSegments) + else: + hyp = self.Hypothesis("NumberOfSegments", [n,s, reversedEdgeInd, entry], + UseExisting=UseExisting, + CompareMethod=self._compareNumberOfSegments) + hyp.SetDistrType( 1 ) + hyp.SetScaleFactor(s) + hyp.SetNumberOfSegments(n) + hyp.SetReversedEdges( reversedEdgeInd ) + hyp.SetObjectEntry( entry ) + return hyp + + ## Private method + ## Checks if the given "NumberOfSegments" hypothesis has the same parameters as the given arguments + def _compareNumberOfSegments(self, hyp, args): + if hyp.GetNumberOfSegments() == args[0]: + if len(args) == 3: + if hyp.GetReversedEdges() == args[1]: + if not args[1] or hyp.GetObjectEntry() == args[2]: + return True + else: + if hyp.GetReversedEdges() == args[2]: + if not args[2] or hyp.GetObjectEntry() == args[3]: + if hyp.GetDistrType() == 1: + if IsEqual(hyp.GetScaleFactor(), args[1]): + return True + return False + + ## Defines "Arithmetic1D" hypothesis to cut an edge in several segments with increasing arithmetic length + # @param start defines the length of the first segment + # @param end defines the length of the last segment + # @param reversedEdges is a list of edges to mesh using reversed orientation. + # A list item can also be a tuple (edge, 1st_vertex_of_edge) + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @return an instance of StdMeshers_Arithmetic1D hypothesis + # @ingroup l3_hypos_1dhyps + def Arithmetic1D(self, start, end, reversedEdges=[], UseExisting=0): + if not isinstance(reversedEdges,list): #old version script, before adding reversedEdges + reversedEdges, UseExisting = [], reversedEdges + reversedEdgeInd = self.ReversedEdgeIndices(reversedEdges) + entry = self.MainShapeEntry() + compFun = lambda hyp, args: ( IsEqual(hyp.GetLength(1), args[0]) and \ + IsEqual(hyp.GetLength(0), args[1]) and \ + hyp.GetReversedEdges() == args[2] and \ + (not args[2] or hyp.GetObjectEntry() == args[3])) + hyp = self.Hypothesis("Arithmetic1D", [start, end, reversedEdgeInd, entry], + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetStartLength(start) + hyp.SetEndLength(end) + hyp.SetReversedEdges( reversedEdgeInd ) + hyp.SetObjectEntry( entry ) + return hyp + + ## Defines "FixedPoints1D" hypothesis to cut an edge using parameter + # on curve from 0 to 1 (additionally it is neecessary to check + # orientation of edges and create list of reversed edges if it is + # needed) and sets numbers of segments between given points (default + # values are equals 1 + # @param points defines the list of parameters on curve + # @param nbSegs defines the list of numbers of segments + # @param reversedEdges is a list of edges to mesh using reversed orientation. + # A list item can also be a tuple (edge, 1st_vertex_of_edge) + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @return an instance of StdMeshers_Arithmetic1D hypothesis + # @ingroup l3_hypos_1dhyps + def FixedPoints1D(self, points, nbSegs=[1], reversedEdges=[], UseExisting=0): + if not isinstance(reversedEdges,list): #old version script, before adding reversedEdges + reversedEdges, UseExisting = [], reversedEdges + reversedEdgeInd = self.ReversedEdgeIndices(reversedEdges) + entry = self.MainShapeEntry() + compFun = lambda hyp, args: ( hyp.GetPoints() == args[0] and \ + hyp.GetNbSegments() == args[1] and \ + hyp.GetReversedEdges() == args[2] and \ + (not args[2] or hyp.GetObjectEntry() == args[3])) + hyp = self.Hypothesis("FixedPoints1D", [points, nbSegs, reversedEdgeInd, entry], + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetPoints(points) + hyp.SetNbSegments(nbSegs) + hyp.SetReversedEdges(reversedEdgeInd) + hyp.SetObjectEntry(entry) + return hyp + + ## Defines "StartEndLength" hypothesis to cut an edge in several segments with increasing geometric length + # @param start defines the length of the first segment + # @param end defines the length of the last segment + # @param reversedEdges is a list of edges to mesh using reversed orientation. + # A list item can also be a tuple (edge, 1st_vertex_of_edge) + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @return an instance of StdMeshers_StartEndLength hypothesis + # @ingroup l3_hypos_1dhyps + def StartEndLength(self, start, end, reversedEdges=[], UseExisting=0): + if not isinstance(reversedEdges,list): #old version script, before adding reversedEdges + reversedEdges, UseExisting = [], reversedEdges + reversedEdgeInd = self.ReversedEdgeIndices(reversedEdges) + entry = self.MainShapeEntry() + compFun = lambda hyp, args: ( IsEqual(hyp.GetLength(1), args[0]) and \ + IsEqual(hyp.GetLength(0), args[1]) and \ + hyp.GetReversedEdges() == args[2] and \ + (not args[2] or hyp.GetObjectEntry() == args[3])) + hyp = self.Hypothesis("StartEndLength", [start, end, reversedEdgeInd, entry], + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetStartLength(start) + hyp.SetEndLength(end) + hyp.SetReversedEdges( reversedEdgeInd ) + hyp.SetObjectEntry( entry ) + return hyp + + ## Defines "Deflection1D" hypothesis + # @param d for the deflection + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - create a new one + # @ingroup l3_hypos_1dhyps + def Deflection1D(self, d, UseExisting=0): + compFun = lambda hyp, args: IsEqual(hyp.GetDeflection(), args[0]) + hyp = self.Hypothesis("Deflection1D", [d], UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetDeflection(d) + return hyp + + ## Defines "Propagation" hypothesis that propagates all other hypotheses on all other edges that are at + # the opposite side in case of quadrangular faces + # @ingroup l3_hypos_additi + def Propagation(self): + return self.Hypothesis("Propagation", UseExisting=1, CompareMethod=self.CompareEqualHyp) + + ## Defines "AutomaticLength" hypothesis + # @param fineness for the fineness [0-1] + # @param UseExisting if ==true - searches for an existing hypothesis created with the + # same parameters, else (default) - create a new one + # @ingroup l3_hypos_1dhyps + def AutomaticLength(self, fineness=0, UseExisting=0): + compFun = lambda hyp, args: IsEqual(hyp.GetFineness(), args[0]) + hyp = self.Hypothesis("AutomaticLength",[fineness],UseExisting=UseExisting, + CompareMethod=compFun) + hyp.SetFineness( fineness ) + return hyp + + ## Defines "SegmentLengthAroundVertex" hypothesis + # @param length for the segment length + # @param vertex for the length localization: the vertex index [0,1] | vertex object. + # Any other integer value means that the hypothesis will be set on the + # whole 1D shape, where Mesh_Segment algorithm is assigned. + # @param UseExisting if ==true - searches for an existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_algos_segmarv + def LengthNearVertex(self, length, vertex=0, UseExisting=0): + import types + store_geom = self.geom + if type(vertex) is types.IntType: + if vertex == 0 or vertex == 1: + import geompyDC + vertex = self.mesh.geompyD.ExtractShapes(self.geom, geompyDC.ShapeType["VERTEX"],True)[vertex] + self.geom = vertex + pass + pass + else: + self.geom = vertex + pass + ### 0D algorithm + if self.geom is None: + raise RuntimeError, "Attemp to create SegmentAroundVertex_0D algoritm on None shape" + AssureGeomPublished( self.mesh, self.geom ) + name = GetName(self.geom) + + algo = self.FindAlgorithm("SegmentAroundVertex_0D", self.mesh.smeshpyD) + if algo is None: + algo = self.mesh.smeshpyD.CreateHypothesis("SegmentAroundVertex_0D", "libStdMeshersEngine.so") + pass + status = self.mesh.mesh.AddHypothesis(self.geom, algo) + TreatHypoStatus(status, "SegmentAroundVertex_0D", name, True) + ### + comFun = lambda hyp, args: IsEqual(hyp.GetLength(), args[0]) + hyp = self.Hypothesis("SegmentLengthAroundVertex", [length], UseExisting=UseExisting, + CompareMethod=comFun) + self.geom = store_geom + hyp.SetLength( length ) + return hyp + + ## Defines "QuadraticMesh" hypothesis, forcing construction of quadratic edges. + # If the 2D mesher sees that all boundary edges are quadratic, + # it generates quadratic faces, else it generates linear faces using + # medium nodes as if they are vertices. + # The 3D mesher generates quadratic volumes only if all boundary faces + # are quadratic, else it fails. + # + # @ingroup l3_hypos_additi + def QuadraticMesh(self): + hyp = self.Hypothesis("QuadraticMesh", UseExisting=1, CompareMethod=self.CompareEqualHyp) + return hyp + +# Public class: Mesh_CompositeSegment +# -------------------------- + +## A regular 1D algorithm for discretization of a set of adjacent edges as one. +# It is created by calling Mesh.Segment(COMPOSITE,geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_CompositeSegment(StdMeshersDC_Segment): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Segment" + ## Name of algorithm type + algoType = COMPOSITE + isDefault = False + + ## Private constructor. + def __init__(self, mesh, geom=0): + self.Create(mesh, geom, self.algoType) + + +# Public class: Mesh_Segment_Python +# --------------------------------- + +## Defines a segment 1D algorithm for discretization with python function +# It is created by calling Mesh.Segment(PYTHON,geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Segment_Python(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Segment" + ## Name of algorithm type + algoType = PYTHON + + ## Private constructor. + def __init__(self, mesh, geom=0): + import Python1dPlugin + self.Create(mesh, geom, self.algoType, "libPython1dEngine.so") + + ## Defines "PythonSplit1D" hypothesis + # @param n for the number of segments that cut an edge + # @param func for the python function that calculates the length of all segments + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_1dhyps + def PythonSplit1D(self, n, func, UseExisting=0): + compFun = lambda hyp, args: False + hyp = self.Hypothesis("PythonSplit1D", [n], "libPython1dEngine.so", + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetNumberOfSegments(n) + hyp.SetPythonLog10RatioFunction(func) + return hyp + +# Public class: Mesh_Triangle_MEFISTO +# ----------------------------------- + +## Triangle MEFISTO 2D algorithm +# It is created by calling Mesh.Triangle(MEFISTO,geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Triangle_MEFISTO(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Triangle" + ## Name of algorithm type + algoType = MEFISTO + isDefault = True + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + ## Defines "MaxElementArea" hypothesis basing on the definition of the maximum area of each triangle + # @param area for the maximum area of each triangle + # @param UseExisting if ==true - searches for an existing hypothesis created with the + # same parameters, else (default) - creates a new one + # + # @ingroup l3_hypos_2dhyps + def MaxElementArea(self, area, UseExisting=0): + comparator = lambda hyp, args: IsEqual(hyp.GetMaxElementArea(), args[0]) + hyp = self.Hypothesis("MaxElementArea", [area], UseExisting=UseExisting, + CompareMethod=comparator) + hyp.SetMaxElementArea(area) + return hyp + + ## Defines "LengthFromEdges" hypothesis to build triangles + # based on the length of the edges taken from the wire + # + # @ingroup l3_hypos_2dhyps + def LengthFromEdges(self): + hyp = self.Hypothesis("LengthFromEdges", UseExisting=1, CompareMethod=self.CompareEqualHyp) + return hyp + +# Public class: Mesh_Quadrangle +# ----------------------------- + +## Defines a quadrangle 2D algorithm +# It is created by calling Mesh.Quadrangle(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Quadrangle(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Quadrangle" + ## Name of algorithm type + algoType = QUADRANGLE + isDefault = True + + params=0 + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + return + + ## Defines "QuadrangleParameters" hypothesis + # @param quadType defines the algorithm of transition between differently descretized + # sides of a geometrical face: + # - QUAD_STANDARD - both triangles and quadrangles are possible in the transition + # area along the finer meshed sides. + # - QUAD_TRIANGLE_PREF - only triangles are built in the transition area along the + # finer meshed sides. + # - QUAD_QUADRANGLE_PREF - only quadrangles are built in the transition area along + # the finer meshed sides, iff the total quantity of segments on + # all four sides of the face is even (divisible by 2). + # - QUAD_QUADRANGLE_PREF_REVERSED - same as QUAD_QUADRANGLE_PREF but the transition + # area is located along the coarser meshed sides. + # - QUAD_REDUCED - only quadrangles are built and the transition between the sides + # is made gradually, layer by layer. This type has a limitation on + # the number of segments: one pair of opposite sides must have the + # same number of segments, the other pair must have an even difference + # between the numbers of segments on the sides. + # @param triangleVertex: vertex of a trilateral geometrical face, around which triangles + # will be created while other elements will be quadrangles. + # Vertex can be either a GEOM_Object or a vertex ID within the + # shape to mesh + # @param UseExisting: if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_quad + def QuadrangleParameters(self, quadType=StdMeshers.QUAD_STANDARD, triangleVertex=0, UseExisting=0): + import GEOM + vertexID = triangleVertex + if isinstance( triangleVertex, GEOM._objref_GEOM_Object ): + vertexID = self.mesh.geompyD.GetSubShapeID( self.mesh.geom, triangleVertex ) + if not self.params: + compFun = lambda hyp,args: \ + hyp.GetQuadType() == args[0] and \ + ( hyp.GetTriaVertex()==args[1] or ( hyp.GetTriaVertex()<1 and args[1]<1)) + self.params = self.Hypothesis("QuadrangleParams", [quadType,vertexID], + UseExisting = UseExisting, CompareMethod=compFun) + pass + if self.params.GetQuadType() != quadType: + self.params.SetQuadType(quadType) + if vertexID > 0: + self.params.SetTriaVertex( vertexID ) + return self.params + + ## Defines "QuadrangleParams" hypothesis with a type of quadrangulation that only + # quadrangles are built in the transition area along the finer meshed sides, + # iff the total quantity of segments on all four sides of the face is even. + # @param reversed if True, transition area is located along the coarser meshed sides. + # @param UseExisting: if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_quad + def QuadranglePreference(self, reversed=False, UseExisting=0): + if reversed: + return self.QuadrangleParameters(QUAD_QUADRANGLE_PREF_REVERSED,UseExisting=UseExisting) + return self.QuadrangleParameters(QUAD_QUADRANGLE_PREF,UseExisting=UseExisting) + + ## Defines "QuadrangleParams" hypothesis with a type of quadrangulation that only + # triangles are built in the transition area along the finer meshed sides. + # @param UseExisting: if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_quad + def TrianglePreference(self, UseExisting=0): + return self.QuadrangleParameters(QUAD_TRIANGLE_PREF,UseExisting=UseExisting) + + ## Defines "QuadrangleParams" hypothesis with a type of quadrangulation that only + # quadrangles are built and the transition between the sides is made gradually, + # layer by layer. This type has a limitation on the number of segments: one pair + # of opposite sides must have the same number of segments, the other pair must + # have an even difference between the numbers of segments on the sides. + # @param UseExisting: if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_quad + def Reduced(self, UseExisting=0): + return self.QuadrangleParameters(QUAD_REDUCED,UseExisting=UseExisting) + + ## Defines "QuadrangleParams" hypothesis with QUAD_STANDARD type of quadrangulation + # @param vertex: vertex of a trilateral geometrical face, around which triangles + # will be created while other elements will be quadrangles. + # Vertex can be either a GEOM_Object or a vertex ID within the + # shape to mesh + # @param UseExisting: if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # @ingroup l3_hypos_quad + def TriangleVertex(self, vertex, UseExisting=0): + return self.QuadrangleParameters(QUAD_STANDARD,vertex,UseExisting) + + +# Public class: Mesh_Hexahedron +# ------------------------------ + +## Defines a hexahedron 3D algorithm +# It is created by calling Mesh.Hexahedron(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Hexahedron(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Hexahedron" + ## Name of algorithm type + algoType = Hexa + isDefault = True + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, Hexa) + pass + +# Public class: Mesh_Projection1D +# ------------------------------- + +## Defines a projection 1D algorithm +# It is created by calling Mesh.Projection1D(geom=0) +# @ingroup l3_algos_proj +# +class StdMeshersDC_Projection1D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Projection1D" + ## Name of algorithm type + algoType = "Projection_1D" + isDefault = True + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + ## Defines "Source Edge" hypothesis, specifying a meshed edge, from where + # a mesh pattern is taken, and, optionally, the association of vertices + # between the source edge and a target edge (to which a hypothesis is assigned) + # @param edge from which nodes distribution is taken + # @param mesh from which nodes distribution is taken (optional) + # @param srcV a vertex of \a edge to associate with \a tgtV (optional) + # @param tgtV a vertex of \a the edge to which the algorithm is assigned, + # to associate with \a srcV (optional) + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def SourceEdge(self, edge, mesh=None, srcV=None, tgtV=None, UseExisting=0): + AssureGeomPublished( self.mesh, edge ) + AssureGeomPublished( self.mesh, srcV ) + AssureGeomPublished( self.mesh, tgtV ) + hyp = self.Hypothesis("ProjectionSource1D", [edge,mesh,srcV,tgtV], + UseExisting=0) + # it does not seem to be useful to reuse the existing "SourceEdge" hypothesis + #UseExisting=UseExisting, CompareMethod=self.CompareSourceEdge) + hyp.SetSourceEdge( edge ) + if not mesh is None and isinstance(mesh, Mesh): + mesh = mesh.GetMesh() + hyp.SetSourceMesh( mesh ) + hyp.SetVertexAssociation( srcV, tgtV ) + return hyp + + +# Public class: Mesh_Projection2D +# ------------------------------ + +## Defines a projection 2D algorithm +# It is created by calling Mesh.Projection2D(geom=0) +# @ingroup l3_algos_proj +# +class StdMeshersDC_Projection2D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Projection2D" + ## Name of algorithm type + algoType = "Projection_2D" + isDefault = True + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + ## Defines "Source Face" hypothesis, specifying a meshed face, from where + # a mesh pattern is taken, and, optionally, the association of vertices + # between the source face and the target face (to which a hypothesis is assigned) + # @param face from which the mesh pattern is taken + # @param mesh from which the mesh pattern is taken (optional) + # @param srcV1 a vertex of \a face to associate with \a tgtV1 (optional) + # @param tgtV1 a vertex of \a the face to which the algorithm is assigned, + # to associate with \a srcV1 (optional) + # @param srcV2 a vertex of \a face to associate with \a tgtV1 (optional) + # @param tgtV2 a vertex of \a the face to which the algorithm is assigned, + # to associate with \a srcV2 (optional) + # @param UseExisting if ==true - forces the search for the existing hypothesis created with + # the same parameters, else (default) - forces the creation a new one + # + # Note: all association vertices must belong to one edge of a face + def SourceFace(self, face, mesh=None, srcV1=None, tgtV1=None, + srcV2=None, tgtV2=None, UseExisting=0): + from smeshDC import Mesh + if isinstance(mesh, Mesh): + mesh = mesh.GetMesh() + for geom in [ face, srcV1, tgtV1, srcV2, tgtV2 ]: + AssureGeomPublished( self.mesh, geom ) + hyp = self.Hypothesis("ProjectionSource2D", [face,mesh,srcV1,tgtV1,srcV2,tgtV2], + UseExisting=0) + # it does not seem to be useful to reuse the existing "SourceFace" hypothesis + #UseExisting=UseExisting, CompareMethod=self.CompareSourceFace) + hyp.SetSourceFace( face ) + hyp.SetSourceMesh( mesh ) + hyp.SetVertexAssociation( srcV1, srcV2, tgtV1, tgtV2 ) + return hyp + +# Public class: Mesh_Projection1D2D +# --------------------------------- + +## Defines a projection 1D-2D algorithm +# It is created by calling Mesh.Projection1D2D(geom=0) +# +# @ingroup l3_algos_proj + +class StdMeshersDC_Projection1D2D(StdMeshersDC_Projection2D): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Projection1D2D" + ## Name of algorithm type + algoType = "Projection_1D2D" + + ## Private constructor. + def __init__(self, mesh, geom=0): + StdMeshersDC_Projection2D.__init__(self, mesh, geom) + +# Public class: Mesh_Projection3D +# ------------------------------ + +## Defines a projection 3D algorithm +# It is created by calling Mesh.Projection3D(COMPOSITE) +# +# @ingroup l3_algos_proj +# +class StdMeshersDC_Projection3D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Projection3D" + ## Name of algorithm type + algoType = "Projection_3D" + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + ## Defines the "Source Shape 3D" hypothesis, specifying a meshed solid, from where + # the mesh pattern is taken, and, optionally, the association of vertices + # between the source and the target solid (to which a hipothesis is assigned) + # @param solid from where the mesh pattern is taken + # @param mesh from where the mesh pattern is taken (optional) + # @param srcV1 a vertex of \a solid to associate with \a tgtV1 (optional) + # @param tgtV1 a vertex of \a the solid where the algorithm is assigned, + # to associate with \a srcV1 (optional) + # @param srcV2 a vertex of \a solid to associate with \a tgtV1 (optional) + # @param tgtV2 a vertex of \a the solid to which the algorithm is assigned, + # to associate with \a srcV2 (optional) + # @param UseExisting - if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + # + # Note: association vertices must belong to one edge of a solid + def SourceShape3D(self, solid, mesh=0, srcV1=0, tgtV1=0, + srcV2=0, tgtV2=0, UseExisting=0): + for geom in [ solid, srcV1, tgtV1, srcV2, tgtV2 ]: + AssureGeomPublished( self.mesh, geom ) + hyp = self.Hypothesis("ProjectionSource3D", + [solid,mesh,srcV1,tgtV1,srcV2,tgtV2], + UseExisting=0) + # seems to be not really useful to reuse existing "SourceShape3D" hypothesis + #UseExisting=UseExisting, CompareMethod=self.CompareSourceShape3D) + hyp.SetSource3DShape( solid ) + if isinstance(mesh, Mesh): + mesh = mesh.GetMesh() + if mesh: + hyp.SetSourceMesh( mesh ) + if srcV1 and srcV2 and tgtV1 and tgtV2: + hyp.SetVertexAssociation( srcV1, srcV2, tgtV1, tgtV2 ) + #elif srcV1 or srcV2 or tgtV1 or tgtV2: + return hyp + +# Public class: Mesh_Prism +# ------------------------ + +## Defines a Prism 3D algorithm, which is either "Extrusion 3D" or "Radial Prism" +# depending on geometry +# It is created by calling Mesh.Prism(geom=0) +# +# @ingroup l3_algos_3dextr +# +class StdMeshersDC_Prism3D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Prism" + ## Name of algorithm type + algoType = "Prism_3D" + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + + shape = geom + if not shape: + shape = mesh.geom + from geompy import SubShapeAll, ShapeType + nbSolids = len( SubShapeAll( shape, ShapeType["SOLID"] )) + nbShells = len( SubShapeAll( shape, ShapeType["SHELL"] )) + if nbSolids == 0 or nbSolids == nbShells: + self.Create(mesh, geom, "Prism_3D") + else: + self.algoType = "RadialPrism_3D" + self.Create(mesh, geom, "RadialPrism_3D") + self.distribHyp = self.Hypothesis("LayerDistribution", UseExisting=0) + self.nbLayers = None + + ## Return 3D hypothesis holding the 1D one + def Get3DHypothesis(self): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + return self.distribHyp + + ## Private method creating a 1D hypothesis and storing it in the LayerDistribution + # hypothesis. Returns the created hypothesis + def OwnHypothesis(self, hypType, args=[], so="libStdMeshersEngine.so"): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + if not self.nbLayers is None: + self.mesh.GetMesh().RemoveHypothesis( self.geom, self.nbLayers ) + self.mesh.GetMesh().AddHypothesis( self.geom, self.distribHyp ) + study = self.mesh.smeshpyD.GetCurrentStudy() # prevents publishing own 1D hypothesis + self.mesh.smeshpyD.SetCurrentStudy( None ) + hyp = self.mesh.smeshpyD.CreateHypothesis(hypType, so) + self.mesh.smeshpyD.SetCurrentStudy( study ) # enables publishing + self.distribHyp.SetLayerDistribution( hyp ) + return hyp + + ## Defines "NumberOfLayers" hypothesis, specifying the number of layers of + # prisms to build between the inner and outer shells + # @param n number of layers + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def NumberOfLayers(self, n, UseExisting=0): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + self.mesh.RemoveHypothesis( self.distribHyp, self.geom ) + compFun = lambda hyp, args: IsEqual(hyp.GetNumberOfLayers(), args[0]) + self.nbLayers = self.Hypothesis("NumberOfLayers", [n], UseExisting=UseExisting, + CompareMethod=compFun) + self.nbLayers.SetNumberOfLayers( n ) + return self.nbLayers + + ## Defines "LocalLength" hypothesis, specifying the segment length + # to build between the inner and the outer shells + # @param l the length of segments + # @param p the precision of rounding + def LocalLength(self, l, p=1e-07): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + hyp = self.OwnHypothesis("LocalLength", [l,p]) + hyp.SetLength(l) + hyp.SetPrecision(p) + return hyp + + ## Defines "NumberOfSegments" hypothesis, specifying the number of layers of + # prisms to build between the inner and the outer shells. + # @param n the number of layers + # @param s the scale factor (optional) + def NumberOfSegments(self, n, s=[]): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + if s == []: + hyp = self.OwnHypothesis("NumberOfSegments", [n]) + else: + hyp = self.OwnHypothesis("NumberOfSegments", [n,s]) + hyp.SetDistrType( 1 ) + hyp.SetScaleFactor(s) + hyp.SetNumberOfSegments(n) + return hyp + + ## Defines "Arithmetic1D" hypothesis, specifying the distribution of segments + # to build between the inner and the outer shells with a length that changes in arithmetic progression + # @param start the length of the first segment + # @param end the length of the last segment + def Arithmetic1D(self, start, end ): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + hyp = self.OwnHypothesis("Arithmetic1D", [start, end]) + hyp.SetLength(start, 1) + hyp.SetLength(end , 0) + return hyp + + ## Defines "StartEndLength" hypothesis, specifying distribution of segments + # to build between the inner and the outer shells as geometric length increasing + # @param start for the length of the first segment + # @param end for the length of the last segment + def StartEndLength(self, start, end): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + hyp = self.OwnHypothesis("StartEndLength", [start, end]) + hyp.SetLength(start, 1) + hyp.SetLength(end , 0) + return hyp + + ## Defines "AutomaticLength" hypothesis, specifying the number of segments + # to build between the inner and outer shells + # @param fineness defines the quality of the mesh within the range [0-1] + def AutomaticLength(self, fineness=0): + if self.algoType != "RadialPrism_3D": + print "Prism_3D algorith doesn't support any hyposesis" + return None + hyp = self.OwnHypothesis("AutomaticLength") + hyp.SetFineness( fineness ) + return hyp + + +# Public class: Mesh_RadialQuadrangle1D2D +# ------------------------------- + +## Defines a Radial Quadrangle 1D2D algorithm +# It is created by calling Mesh.Quadrangle(RADIAL_QUAD,geom=0) +# +# @ingroup l2_algos_radialq +class StdMeshersDC_RadialQuadrangle1D2D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "Quadrangle" + ## Name of algorithm type + algoType = RADIAL_QUAD + + ## Private constructor. + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + + self.distribHyp = None #self.Hypothesis("LayerDistribution2D", UseExisting=0) + self.nbLayers = None + + ## Return 2D hypothesis holding the 1D one + def Get2DHypothesis(self): + if not self.distribHyp: + self.distribHyp = self.Hypothesis("LayerDistribution2D", UseExisting=0) + return self.distribHyp + + ## Private method creating a 1D hypothesis and storing it in the LayerDistribution + # hypothesis. Returns the created hypothesis + def OwnHypothesis(self, hypType, args=[], so="libStdMeshersEngine.so"): + if self.nbLayers: + self.mesh.GetMesh().RemoveHypothesis( self.geom, self.nbLayers ) + if self.distribHyp is None: + self.distribHyp = self.Hypothesis("LayerDistribution2D", UseExisting=0) + else: + self.mesh.GetMesh().AddHypothesis( self.geom, self.distribHyp ) + study = self.mesh.smeshpyD.GetCurrentStudy() # prevents publishing own 1D hypothesis + self.mesh.smeshpyD.SetCurrentStudy( None ) + hyp = self.mesh.smeshpyD.CreateHypothesis(hypType, so) + self.mesh.smeshpyD.SetCurrentStudy( study ) # enables publishing + self.distribHyp.SetLayerDistribution( hyp ) + return hyp + + ## Defines "NumberOfLayers" hypothesis, specifying the number of layers + # @param n number of layers + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def NumberOfLayers(self, n, UseExisting=0): + if self.distribHyp: + self.mesh.GetMesh().RemoveHypothesis( self.geom, self.distribHyp ) + compFun = lambda hyp, args: IsEqual(hyp.GetNumberOfLayers(), args[0]) + self.nbLayers = self.Hypothesis("NumberOfLayers2D", [n], UseExisting=UseExisting, + CompareMethod=compFun) + self.nbLayers.SetNumberOfLayers( n ) + return self.nbLayers + + ## Defines "LocalLength" hypothesis, specifying the segment length + # @param l the length of segments + # @param p the precision of rounding + def LocalLength(self, l, p=1e-07): + hyp = self.OwnHypothesis("LocalLength", [l,p]) + hyp.SetLength(l) + hyp.SetPrecision(p) + return hyp + + ## Defines "NumberOfSegments" hypothesis, specifying the number of layers + # @param n the number of layers + # @param s the scale factor (optional) + def NumberOfSegments(self, n, s=[]): + if s == []: + hyp = self.OwnHypothesis("NumberOfSegments", [n]) + else: + hyp = self.OwnHypothesis("NumberOfSegments", [n,s]) + hyp.SetDistrType( 1 ) + hyp.SetScaleFactor(s) + hyp.SetNumberOfSegments(n) + return hyp + + ## Defines "Arithmetic1D" hypothesis, specifying the distribution of segments + # with a length that changes in arithmetic progression + # @param start the length of the first segment + # @param end the length of the last segment + def Arithmetic1D(self, start, end ): + hyp = self.OwnHypothesis("Arithmetic1D", [start, end]) + hyp.SetLength(start, 1) + hyp.SetLength(end , 0) + return hyp + + ## Defines "StartEndLength" hypothesis, specifying distribution of segments + # as geometric length increasing + # @param start for the length of the first segment + # @param end for the length of the last segment + def StartEndLength(self, start, end): + hyp = self.OwnHypothesis("StartEndLength", [start, end]) + hyp.SetLength(start, 1) + hyp.SetLength(end , 0) + return hyp + + ## Defines "AutomaticLength" hypothesis, specifying the number of segments + # @param fineness defines the quality of the mesh within the range [0-1] + def AutomaticLength(self, fineness=0): + hyp = self.OwnHypothesis("AutomaticLength") + hyp.SetFineness( fineness ) + return hyp + + +# Public class: Mesh_UseExistingElements +# -------------------------------------- +## Defines a Radial Quadrangle 1D2D algorithm +# It is created by calling Mesh.UseExisting1DElements(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_UseExistingElements_1D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "UseExisting1DElements" + ## Name of algorithm type + algoType = "Import_1D" + isDefault = True + + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + return + + ## Defines "Source edges" hypothesis, specifying groups of edges to import + # @param groups list of groups of edges + # @param toCopyMesh if True, the whole mesh \a groups belong to is imported + # @param toCopyGroups if True, all groups of the mesh \a groups belong to are imported + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def SourceEdges(self, groups, toCopyMesh=False, toCopyGroups=False, UseExisting=False): + for group in groups: + AssureGeomPublished( self.mesh, group ) + compFun = lambda hyp, args: ( hyp.GetSourceEdges() == args[0] and \ + hyp.GetCopySourceMesh() == args[1], args[2] ) + hyp = self.Hypothesis("ImportSource1D", [groups, toCopyMesh, toCopyGroups], + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetSourceEdges(groups) + hyp.SetCopySourceMesh(toCopyMesh, toCopyGroups) + return hyp + +# Public class: Mesh_UseExistingElements +# -------------------------------------- +## Defines a Radial Quadrangle 1D2D algorithm +# It is created by calling Mesh.UseExisting2DElements(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_UseExistingElements_1D2D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "UseExisting2DElements" + ## Name of algorithm type + algoType = "Import_1D2D" + isDefault = True + + def __init__(self, mesh, geom=0): + Mesh_Algorithm.__init__(self) + self.Create(mesh, geom, self.algoType) + return + + ## Defines "Source faces" hypothesis, specifying groups of faces to import + # @param groups list of groups of faces + # @param toCopyMesh if True, the whole mesh \a groups belong to is imported + # @param toCopyGroups if True, all groups of the mesh \a groups belong to are imported + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def SourceFaces(self, groups, toCopyMesh=False, toCopyGroups=False, UseExisting=False): + for group in groups: + AssureGeomPublished( self.mesh, group ) + compFun = lambda hyp, args: ( hyp.GetSourceFaces() == args[0] and \ + hyp.GetCopySourceMesh() == args[1], args[2] ) + hyp = self.Hypothesis("ImportSource2D", [groups, toCopyMesh, toCopyGroups], + UseExisting=UseExisting, CompareMethod=compFun) + hyp.SetSourceFaces(groups) + hyp.SetCopySourceMesh(toCopyMesh, toCopyGroups) + return hyp + + +# Public class: Mesh_Cartesian_3D +# -------------------------------------- +## Defines a Body Fitting 3D algorithm +# It is created by calling Mesh.BodyFitted(geom=0) +# +# @ingroup l3_algos_basic +class StdMeshersDC_Cartesian_3D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "BodyFitted" + ## Name of algorithm type + algoType = "Cartesian_3D" + isDefault = True + + def __init__(self, mesh, geom=0): + self.Create(mesh, geom, self.algoType) + self.hyp = None + return + + ## Defines "Body Fitting parameters" hypothesis + # @param xGridDef is definition of the grid along the X asix. + # It can be in either of two following forms: + # - Explicit coordinates of nodes, e.g. [-1.5, 0.0, 3.1] or range( -100,200,10) + # - Functions f(t) defining grid spacing at each point on grid axis. If there are + # several functions, they must be accompanied by relative coordinates of + # points dividing the whole shape into ranges where the functions apply; points + # coodrinates should vary within (0.0, 1.0) range. Parameter \a t of the spacing + # function f(t) varies from 0.0 to 1.0 witin a shape range. + # Examples: + # - "10.5" - defines a grid with a constant spacing + # - [["1", "1+10*t", "11"] [0.1, 0.6]] - defines different spacing in 3 ranges. + # @param yGridDef defines the grid along the Y asix the same way as \a xGridDef does + # @param zGridDef defines the grid along the Z asix the same way as \a xGridDef does + # @param sizeThreshold (> 1.0) defines a minimal size of a polyhedron so that + # a polyhedron of size less than hexSize/sizeThreshold is not created + # @param UseExisting if ==true - searches for the existing hypothesis created with + # the same parameters, else (default) - creates a new one + def SetGrid(self, xGridDef, yGridDef, zGridDef, sizeThreshold=4.0, UseExisting=False): + if not self.hyp: + compFun = lambda hyp, args: False + self.hyp = self.Hypothesis("CartesianParameters3D", + [xGridDef, yGridDef, zGridDef, sizeThreshold], + UseExisting=UseExisting, CompareMethod=compFun) + if not self.mesh.IsUsedHypothesis( self.hyp, self.geom ): + self.mesh.AddHypothesis( self.hyp, self.geom ) + + for axis, gridDef in enumerate( [xGridDef, yGridDef, zGridDef]): + if not gridDef: raise ValueError, "Empty grid definition" + if isinstance( gridDef, str ): + self.hyp.SetGridSpacing( [gridDef], [], axis ) + elif isinstance( gridDef[0], str ): + self.hyp.SetGridSpacing( gridDef, [], axis ) + elif isinstance( gridDef[0], int ) or \ + isinstance( gridDef[0], float ): + self.hyp.SetGrid(gridDef, axis ) + else: + self.hyp.SetGridSpacing( gridDef[0], gridDef[1], axis ) + self.hyp.SetSizeThreshold( sizeThreshold ) + return self.hyp + +# Public class: Mesh_UseExisting_1D +# --------------------------------- +## Defines a stub 1D algorithm, which enables "manual" creation of nodes and +# segments usable by 2D algoritms +# It is created by calling Mesh.UseExistingSegments(geom=0) +# +# @ingroup l3_algos_basic + +class StdMeshersDC_UseExisting_1D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "UseExistingSegments" + ## Name of algorithm type + algoType = "UseExisting_1D" + + def __init__(self, mesh, geom=0): + self.Create(mesh, geom, self.algoType) + + +# Public class: Mesh_UseExisting +# ------------------------------- +## Defines a stub 2D algorithm, which enables "manual" creation of nodes and +# faces usable by 3D algoritms +# It is created by calling Mesh.UseExistingFaces(geom=0) +# +# @ingroup l3_algos_basic + +class StdMeshersDC_UseExisting_2D(Mesh_Algorithm): + + ## Name of method of class Mesh creating an instance of this class + meshMethod = "UseExistingFaces" + ## Name of algorithm type + algoType = "UseExisting_2D" + + def __init__(self, mesh, geom=0): + self.Create(mesh, geom, self.algoType) diff --git a/src/SMESH_SWIG/batchmode_mefisto.py b/src/SMESH_SWIG/batchmode_mefisto.py index 775023370..fb888795a 100644 --- a/src/SMESH_SWIG/batchmode_mefisto.py +++ b/src/SMESH_SWIG/batchmode_mefisto.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + import os import re diff --git a/src/SMESH_SWIG/batchmode_smesh.py b/src/SMESH_SWIG/batchmode_smesh.py index d6688362d..6daa0e946 100644 --- a/src/SMESH_SWIG/batchmode_smesh.py +++ b/src/SMESH_SWIG/batchmode_smesh.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : batchmode_smesh.py # Author : Oksana TCHEBANOVA # Module : SMESH @@ -32,6 +34,7 @@ import SMESH modulecatalog = naming_service.Resolve("/Kernel/ModulCatalog") smesh = lcc.FindOrLoadComponent("FactoryServer", "SMESH") +smesh.SetCurrentStudy(myStudy) myStudyBuilder = myStudy.NewBuilder() if myStudyBuilder is None: diff --git a/src/SMESH_SWIG/ex00_all.py b/src/SMESH_SWIG/ex00_all.py index e4159ebdb..381b9ae3c 100644 --- a/src/SMESH_SWIG/ex00_all.py +++ b/src/SMESH_SWIG/ex00_all.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ================================== # Load all examples # ----------------- diff --git a/src/SMESH_SWIG/ex01_cube2build.py b/src/SMESH_SWIG/ex01_cube2build.py index fbc8c5f23..10ba3f33a 100644 --- a/src/SMESH_SWIG/ex01_cube2build.py +++ b/src/SMESH_SWIG/ex01_cube2build.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -286,6 +288,8 @@ piece_id = addToStudy(piece, "ex01_cube2build") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create hexahedrical mesh on piece # --------------------------------- diff --git a/src/SMESH_SWIG/ex02_cube2primitive.py b/src/SMESH_SWIG/ex02_cube2primitive.py index 732b579b7..9350199eb 100644 --- a/src/SMESH_SWIG/ex02_cube2primitive.py +++ b/src/SMESH_SWIG/ex02_cube2primitive.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -97,6 +99,8 @@ piece_id = addToStudy(piece, "ex02_cube2primitive") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create hexahedrical mesh on piece # --------------------------------- diff --git a/src/SMESH_SWIG/ex03_cube2partition.py b/src/SMESH_SWIG/ex03_cube2partition.py index e3447c37d..1849895fe 100644 --- a/src/SMESH_SWIG/ex03_cube2partition.py +++ b/src/SMESH_SWIG/ex03_cube2partition.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -84,6 +86,8 @@ piece_id = addToStudy(piece, "ex03_cube2partition") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create hexahedrical mesh on piece # --------------------------------- diff --git a/src/SMESH_SWIG/ex04_cube5tetraHexa.py b/src/SMESH_SWIG/ex04_cube5tetraHexa.py index 18d4900ff..773aca145 100644 --- a/src/SMESH_SWIG/ex04_cube5tetraHexa.py +++ b/src/SMESH_SWIG/ex04_cube5tetraHexa.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -65,6 +67,8 @@ piece_id = addToStudy(piece, "ex04_cube5tetraHexa") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex05_hole1build.py b/src/SMESH_SWIG/ex05_hole1build.py index 050509e29..231c3f1d4 100644 --- a/src/SMESH_SWIG/ex05_hole1build.py +++ b/src/SMESH_SWIG/ex05_hole1build.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -123,6 +125,8 @@ piece_id = addToStudy(piece, "ex05_hole1build") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex06_hole1boolean.py b/src/SMESH_SWIG/ex06_hole1boolean.py index 3f2c6893d..0335c3357 100644 --- a/src/SMESH_SWIG/ex06_hole1boolean.py +++ b/src/SMESH_SWIG/ex06_hole1boolean.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -107,7 +109,7 @@ c_l.append(baseHexa4) c_cpd = MakeCompound(c_l) c_glu = MakeGlueFaces(c_cpd, 1.e-5) -piece = RemoveExtraEdges(c_glu) +piece = RemoveExtraEdges(c_glu, doUnionFaces=True) # Add in study # ------------ @@ -117,6 +119,8 @@ piece_id = addToStudy(piece, "ex06_hole1boolean") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex07_hole1partition.py b/src/SMESH_SWIG/ex07_hole1partition.py index c46864c2d..84a4326d6 100644 --- a/src/SMESH_SWIG/ex07_hole1partition.py +++ b/src/SMESH_SWIG/ex07_hole1partition.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -70,7 +72,7 @@ p_tools.append(MakePlane(p_centre, MakeVectorDXDYDZ(-g_largeur, 0, g_longueur), p_part = MakePartition([c_piece], p_tools, [], [], ShapeType["SOLID"]) -p_blocs = RemoveExtraEdges(p_part) +p_blocs = RemoveExtraEdges(p_part, doUnionFaces=True) piece = MakeGlueFaces(p_blocs, 1.e-5) # Add in study @@ -81,6 +83,8 @@ piece_id = addToStudy(piece, "ex07_hole1partition") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex08_hole2build.py b/src/SMESH_SWIG/ex08_hole2build.py index 6ccb77d41..0bc58f133 100644 --- a/src/SMESH_SWIG/ex08_hole2build.py +++ b/src/SMESH_SWIG/ex08_hole2build.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -114,6 +116,8 @@ piece_id = addToStudy(piece, "ex08_hole2build") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex09_grid4build.py b/src/SMESH_SWIG/ex09_grid4build.py index d0e0fb72a..cbbec434c 100644 --- a/src/SMESH_SWIG/ex09_grid4build.py +++ b/src/SMESH_SWIG/ex09_grid4build.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -117,6 +119,8 @@ piece_id = addToStudy(piece, "ex09_grid4build") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex10_grid4geometry.py b/src/SMESH_SWIG/ex10_grid4geometry.py index 8f63e031d..1679e4b5f 100644 --- a/src/SMESH_SWIG/ex10_grid4geometry.py +++ b/src/SMESH_SWIG/ex10_grid4geometry.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -79,6 +81,8 @@ piece_id = addToStudy(piece, "ex10_grid4geometry") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex11_grid3partition.py b/src/SMESH_SWIG/ex11_grid3partition.py index 3369074d2..252aa5563 100644 --- a/src/SMESH_SWIG/ex11_grid3partition.py +++ b/src/SMESH_SWIG/ex11_grid3partition.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -83,7 +85,7 @@ p_tools.append(MakePlane(e_centre, MakeVectorDXDYDZ(-1, 0, 1), g_trim)) p_part = MakePartition([e_blo1, e_blo2, e_blo3], p_tools, [], [], ShapeType["SOLID"]) -p_element = RemoveExtraEdges(p_part) +p_element = RemoveExtraEdges(p_part, doUnionFaces=True) # Grid and glue # ------------- @@ -100,6 +102,8 @@ piece_id = addToStudy(piece, "ex11_grid3partition") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex12_grid17partition.py b/src/SMESH_SWIG/ex12_grid17partition.py index 2f41e3bee..bf6593b99 100644 --- a/src/SMESH_SWIG/ex12_grid17partition.py +++ b/src/SMESH_SWIG/ex12_grid17partition.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -115,6 +117,8 @@ piece_id = addToStudy(piece, "ex12_grid17partition") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex13_hole1partial.py b/src/SMESH_SWIG/ex13_hole1partial.py index 0b3ff6baf..a54fd6266 100644 --- a/src/SMESH_SWIG/ex13_hole1partial.py +++ b/src/SMESH_SWIG/ex13_hole1partial.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ================================== # from geompy import * @@ -204,7 +206,7 @@ blocks.append(full_parts) piece_cpd = MakeCompound(blocks) -piece_ok = RemoveExtraEdges(piece_cpd) +piece_ok = RemoveExtraEdges(piece_cpd, doUnionFaces=True) piece = MakeGlueFaces(piece_ok, 1.e-3) @@ -213,6 +215,8 @@ piece_id = addToStudy(piece, "ex13_hole1partial") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a mesh # ------------- diff --git a/src/SMESH_SWIG/ex14_cyl1holed.py b/src/SMESH_SWIG/ex14_cyl1holed.py index 42d460b93..0f058c098 100644 --- a/src/SMESH_SWIG/ex14_cyl1holed.py +++ b/src/SMESH_SWIG/ex14_cyl1holed.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -98,6 +100,8 @@ piece_id = addToStudy(piece, "ex14_cyl1holed") # Maillage # ======== +smesh.SetCurrentStudy(salome.myStudy) + # Creer un maillage hexahedrique # ------------------------------ diff --git a/src/SMESH_SWIG/ex15_cyl2geometry.py b/src/SMESH_SWIG/ex15_cyl2geometry.py index ffd3395c6..5322e9ef5 100644 --- a/src/SMESH_SWIG/ex15_cyl2geometry.py +++ b/src/SMESH_SWIG/ex15_cyl2geometry.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -167,7 +169,7 @@ cpd = cpd + r_element # Compound # -------- -piece = RemoveExtraEdges(MakeCompound(cpd)) +piece = RemoveExtraEdges(MakeCompound(cpd), True) # Ajouter la piece dans l'etude # ----------------------------- @@ -177,6 +179,8 @@ piece_id = addToStudy(piece, "ex15_cyl2geometry") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex16_cyl2complementary.py b/src/SMESH_SWIG/ex16_cyl2complementary.py index 774cc5f7d..f644c22a7 100644 --- a/src/SMESH_SWIG/ex16_cyl2complementary.py +++ b/src/SMESH_SWIG/ex16_cyl2complementary.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -98,6 +100,7 @@ d_element[10] = MakeCut(d_element[10], c_cyl) # -------- piece = RemoveExtraEdges(MakeCompound(d_element)) +piece = MakeGlueFaces(piece, 1e-07) # Add piece in study # ------------------ @@ -107,6 +110,8 @@ piece_id = addToStudy(piece, "ex16_cyl2complementary") # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex17_dome1.py b/src/SMESH_SWIG/ex17_dome1.py index a25de9627..2626431f0 100644 --- a/src/SMESH_SWIG/ex17_dome1.py +++ b/src/SMESH_SWIG/ex17_dome1.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -80,6 +82,8 @@ piece_id = addToStudy(piece, "ex17_dome1") # Maillage # ======== +smesh.SetCurrentStudy(salome.myStudy) + # Maillage hexahedrique # --------------------- diff --git a/src/SMESH_SWIG/ex18_dome2.py b/src/SMESH_SWIG/ex18_dome2.py index f56015619..dc31fe8f8 100644 --- a/src/SMESH_SWIG/ex18_dome2.py +++ b/src/SMESH_SWIG/ex18_dome2.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # from geompy import * @@ -117,6 +119,8 @@ piece_id = addToStudy(piece, "ex18_dome2") # Maillage # ======== +smesh.SetCurrentStudy(salome.myStudy) + # Maillage hexahedrique # --------------------- diff --git a/src/SMESH_SWIG/ex19_sphereINcube.py b/src/SMESH_SWIG/ex19_sphereINcube.py index 510adf800..879596348 100644 --- a/src/SMESH_SWIG/ex19_sphereINcube.py +++ b/src/SMESH_SWIG/ex19_sphereINcube.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ================================== # from geompy import * @@ -74,17 +76,13 @@ f4 = MakePlane(sphere_centre, MakeVectorDXDYDZ( 1, 0, -1), plan_trim) #sphere_decoupee = MakePartition(solids, sphere_outils, [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(sphere_troue,geompy.ShapeType["SOLID"]) -sphere_decoupee = MakePartition(solids, [f1], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(sphere_decoupee,geompy.ShapeType["SOLID"]) -sphere_decoupee = MakePartition(solids, [f2], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(sphere_decoupee,geompy.ShapeType["SOLID"]) -sphere_decoupee = MakePartition(solids, [f3], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(sphere_decoupee,geompy.ShapeType["SOLID"]) -sphere_decoupee = MakePartition(solids, [f4], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(sphere_decoupee,geompy.ShapeType["SOLID"]) - -sphere_partie = geompy.MakeCompound(solids) + +sphere_decoupee = MakePartition([sphere_troue], [f1], [], [], ShapeType["SOLID"]) +sphere_decoupee = MakePartition([sphere_decoupee], [f2], [], [], ShapeType["SOLID"]) +sphere_decoupee = MakePartition([sphere_decoupee], [f3], [], [], ShapeType["SOLID"]) +sphere_decoupee = MakePartition([sphere_decoupee], [f4], [], [], ShapeType["SOLID"]) + +sphere_partie = geompy.MakeCompound([sphere_decoupee]) sphere_partie = GetBlockNearPoint(sphere_decoupee, MakeVertex(-sphere_rayon, 0, 0)) sphere_bloc = RemoveExtraEdges(sphere_partie) @@ -111,17 +109,12 @@ cube_plein = MakeBox(-cube_cote, -cube_cote, -cube_cote, +cube_cote, +cube_co cube_trou = MakeCut(cube_plein, sphere_pleine) #cube_decoupe = MakePartition([cube_trou], sphere_outils, [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(cube_trou,geompy.ShapeType["SOLID"]) -cube_decoupe = MakePartition(solids, [f1], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(cube_decoupe,geompy.ShapeType["SOLID"]) -cube_decoupe = MakePartition(solids, [f2], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(cube_decoupe,geompy.ShapeType["SOLID"]) -cube_decoupe = MakePartition(solids, [f3], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(cube_decoupe,geompy.ShapeType["SOLID"]) -cube_decoupe = MakePartition(solids, [f4], [], [], ShapeType["SOLID"]) -solids = geompy.SubShapeAll(cube_decoupe,geompy.ShapeType["SOLID"]) +cube_decoupe = MakePartition([cube_trou], [f1], [], [], ShapeType["SOLID"]) +cube_decoupe = MakePartition([cube_decoupe], [f2], [], [], ShapeType["SOLID"]) +cube_decoupe = MakePartition([cube_decoupe], [f3], [], [], ShapeType["SOLID"]) +cube_decoupe = MakePartition([cube_decoupe], [f4], [], [], ShapeType["SOLID"]) -cube_decoupe = geompy.MakeCompound(solids) +cube_decoupe = geompy.MakeCompound([cube_decoupe]) cube_partie = GetBlockNearPoint(cube_decoupe, MakeVertex(-cube_cote, 0, 0)) @@ -167,6 +160,8 @@ UnionList(groupe, groupe_sphere) # Meshing # ======= +smesh.SetCurrentStudy(salome.myStudy) + # Create a hexahedral mesh # ------------------------ diff --git a/src/SMESH_SWIG/ex21_lamp.py b/src/SMESH_SWIG/ex21_lamp.py index f6c3c3789..befd0e1d8 100644 --- a/src/SMESH_SWIG/ex21_lamp.py +++ b/src/SMESH_SWIG/ex21_lamp.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ================================== # from geompy import * @@ -92,6 +94,8 @@ UnionIDs(group, faces) # Create a mesh # ============= +smesh.SetCurrentStudy(salome.myStudy) + # Define a mesh on a geometry # --------------------------- diff --git a/src/SMESH_SWIG/ex24_cylinder.py b/src/SMESH_SWIG/ex24_cylinder.py index 6d9f3077d..91100376b 100644 --- a/src/SMESH_SWIG/ex24_cylinder.py +++ b/src/SMESH_SWIG/ex24_cylinder.py @@ -1,31 +1,30 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ================================== # import math import geompy import smesh - +import salome geo = geompy # Parameters @@ -97,6 +96,8 @@ geo.DifferenceList(group_1, [group_1_box]) # Mesh the blocks with hexahedral # ------------------------------- +smesh.SetCurrentStudy(salome.myStudy) + def discretize(x, y, z, n, s=blocks): p = geo.MakeVertex(x, y, z) e = geo.GetEdgeNearPoint(s, p) diff --git a/src/SMESH_SWIG/ex29_refine.py b/src/SMESH_SWIG/ex29_refine.py index 1e218a8fa..8363ea81a 100644 --- a/src/SMESH_SWIG/ex29_refine.py +++ b/src/SMESH_SWIG/ex29_refine.py @@ -1,24 +1,23 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # ======================================= # Procedure that take a triangulation and split all triangles in 4 others triangles # diff --git a/src/SMESH_SWIG/ex30_groupsOp.py b/src/SMESH_SWIG/ex30_groupsOp.py index af892e7ff..5bab30823 100755 --- a/src/SMESH_SWIG/ex30_groupsOp.py +++ b/src/SMESH_SWIG/ex30_groupsOp.py @@ -1,73 +1,92 @@ - -import sys -import salome -import geompy -import math -import SALOMEDS -import SMESH -import smesh - -salome.salome_init() -aStudyId = salome.myStudy._get_StudyId() - -geompy.init_geom(salome.myStudy) -global Face_1 -Face_1 = geompy.MakeFaceHW(100, 100, 1) -geompy.addToStudy( Face_1, "Face_1" ) - -#smesh.smesh.SetCurrentStudy(aStudyId) -import StdMeshers -pattern = smesh.GetPattern() -Mesh_1 = smesh.Mesh(Face_1) -Regular_1D = Mesh_1.Segment() -Nb_Segments_1 = Regular_1D.NumberOfSegments(10) -Nb_Segments_1.SetDistrType( 0 ) -Quadrangle_2D = Mesh_1.Quadrangle() -isDone = Mesh_1.Compute() - -# groups creation - -aListOfElems = [ 52, 53, 54, 55, 56, 57, - 62, 63, 64, 65, 66, 67, - 72, 73, 74, 75, 76, 77, - 82, 83, 84, 85, 86, 87 ] - -aRedGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Red" ) -aRedGroup.Add( aListOfElems ); -aRedGroup.SetColor( SALOMEDS.Color( 1, 0, 0 ) ) - -aListOfElems = [ 55, 56, 57, 58, 59, - 65, 66, 67, 68, 69, - 75, 76, 77, 78, 79, - 85, 86, 87, 88, 89, - 95, 96, 97, 98, 99, - 105, 106, 107, 108, 109, - 115, 116, 117, 118, 119, - 125, 126, 127, 128, 129 ] - -aGreenGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Green" ) -aGreenGroup.Add( aListOfElems ); -aGreenGroup.SetColor( SALOMEDS.Color( 0, 1, 0 ) ) - -aListOfElems = [ 63, 64, 65, 66, 67, 68, - 73, 74, 75, 76, 77, 78, - 83, 84, 85, 86, 87, 88, - 93, 94, 95, 96, 97, 98, - 103, 104, 105, 106, 107, 108, - 113, 114, 115, 116, 117, 118 ] - -aBlueGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Blue" ) -aBlueGroup.Add( aListOfElems ); -aBlueGroup.SetColor( SALOMEDS.Color( 0, 0, 1 ) ) - -# UnionListOfGroups() -aUnGrp = Mesh_1.UnionListOfGroups([aRedGroup, aGreenGroup, aBlueGroup], "UnionGrp" ) - -# IntersectListOfGroups() -aIntGrp=Mesh_1.IntersectListOfGroups([aRedGroup, aGreenGroup, aBlueGroup], "IntGrp" ) - -# CutListOfGroups() -aCutGrp=Mesh_1.CutListOfGroups([aRedGroup],[aGreenGroup,aBlueGroup],"CutGrp") - -salome.sg.updateObjBrowser( 1 ) - +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# + +import sys +import salome +import geompy +import math +import SALOMEDS +import SMESH +import smesh + +salome.salome_init() +aStudyId = salome.myStudy._get_StudyId() + +geompy.init_geom(salome.myStudy) +global Face_1 +Face_1 = geompy.MakeFaceHW(100, 100, 1) +geompy.addToStudy( Face_1, "Face_1" ) + +smesh.SetCurrentStudy(salome.myStudy) +import StdMeshers +pattern = smesh.GetPattern() +Mesh_1 = smesh.Mesh(Face_1) +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(10) +Nb_Segments_1.SetDistrType( 0 ) +Quadrangle_2D = Mesh_1.Quadrangle() +isDone = Mesh_1.Compute() + +# groups creation + +aListOfElems = [ 52, 53, 54, 55, 56, 57, + 62, 63, 64, 65, 66, 67, + 72, 73, 74, 75, 76, 77, + 82, 83, 84, 85, 86, 87 ] + +aRedGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Red" ) +aRedGroup.Add( aListOfElems ); +aRedGroup.SetColor( SALOMEDS.Color( 1, 0, 0 ) ) + +aListOfElems = [ 55, 56, 57, 58, 59, + 65, 66, 67, 68, 69, + 75, 76, 77, 78, 79, + 85, 86, 87, 88, 89, + 95, 96, 97, 98, 99, + 105, 106, 107, 108, 109, + 115, 116, 117, 118, 119, + 125, 126, 127, 128, 129 ] + +aGreenGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Green" ) +aGreenGroup.Add( aListOfElems ); +aGreenGroup.SetColor( SALOMEDS.Color( 0, 1, 0 ) ) + +aListOfElems = [ 63, 64, 65, 66, 67, 68, + 73, 74, 75, 76, 77, 78, + 83, 84, 85, 86, 87, 88, + 93, 94, 95, 96, 97, 98, + 103, 104, 105, 106, 107, 108, + 113, 114, 115, 116, 117, 118 ] + +aBlueGroup = Mesh_1.GetMesh().CreateGroup( smesh.FACE, "Blue" ) +aBlueGroup.Add( aListOfElems ); +aBlueGroup.SetColor( SALOMEDS.Color( 0, 0, 1 ) ) + +# UnionListOfGroups() +aUnGrp = Mesh_1.UnionListOfGroups([aRedGroup, aGreenGroup, aBlueGroup], "UnionGrp" ) + +# IntersectListOfGroups() +aIntGrp=Mesh_1.IntersectListOfGroups([aRedGroup, aGreenGroup, aBlueGroup], "IntGrp" ) + +# CutListOfGroups() +aCutGrp=Mesh_1.CutListOfGroups([aRedGroup],[aGreenGroup,aBlueGroup],"CutGrp") + +salome.sg.updateObjBrowser( 1 ) + diff --git a/src/SMESH_SWIG/ex30_tepal.py b/src/SMESH_SWIG/ex30_tepal.py index c73dc872d..ece87736d 100644 --- a/src/SMESH_SWIG/ex30_tepal.py +++ b/src/SMESH_SWIG/ex30_tepal.py @@ -1,6 +1,25 @@ -# CEA/LGLS 2008, Christian Van Wambeke (CEA/LGLS), Francis KLOSS (OCC) -# ==================================================================== +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# +# ==================================================================== +# import os import geompy @@ -27,6 +46,8 @@ geompy.addToStudy(cylinder, "Cylinder") # Define a mesh on a geometry # --------------------------- +smesh.SetCurrentStudy(salome.myStudy) + m = smesh.Mesh(cylinder) # 2D mesh with BLSURF diff --git a/src/SMESH_SWIG/ex31_dimGroup.py b/src/SMESH_SWIG/ex31_dimGroup.py index 13cd9cf26..ae8daeb0c 100755 --- a/src/SMESH_SWIG/ex31_dimGroup.py +++ b/src/SMESH_SWIG/ex31_dimGroup.py @@ -1,47 +1,67 @@ -import sys -import salome -import geompy -import math -import SALOMEDS -import SMESH -import smesh - -salome.salome_init() -aStudyId = salome.myStudy._get_StudyId() - -geompy.init_geom(salome.myStudy) - -geompy.init_geom(salome.myStudy) -global Box_1 -Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) -geompy.addToStudy( Box_1, "Box_1" ) - -#smesh.smesh.SetCurrentStudy(theStudy) -import StdMeshers -Mesh_1 = smesh.Mesh(Box_1) -Regular_1D = Mesh_1.Segment() -Nb_Segments_1 = Regular_1D.NumberOfSegments(10) -Nb_Segments_1.SetDistrType( 0 ) -Quadrangle_2D = Mesh_1.Quadrangle() -Hexa_3D = Mesh_1.Hexahedron() -isDone = Mesh_1.Compute() - -### CreateDimGroup() - -aListOf3d_1=range(721,821) - -aGrp3D_1=Mesh_1.GetMesh().CreateGroup( smesh.VOLUME, "Src 3D 1" ) -aGrp3D_1.Add( aListOf3d_1 ) - -aListOf3d_2=range(821, 921) -aGrp3D_2=Mesh_1.GetMesh().CreateGroup( smesh.VOLUME, "Src 3D 2" ) -aGrp3D_2.Add( aListOf3d_2 ) - -aGrp2D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.FACE, "Faces" ) - -aGrp1D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.EDGE, "Edges" ) - -aGrp0D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.NODE, "Nodes" ) - -salome.sg.updateObjBrowser( 1 ) - +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# + +import sys +import salome +import geompy +import math +import SALOMEDS +import SMESH +import smesh + +salome.salome_init() +aStudyId = salome.myStudy._get_StudyId() + +geompy.init_geom(salome.myStudy) + +geompy.init_geom(salome.myStudy) +global Box_1 +Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200) +geompy.addToStudy( Box_1, "Box_1" ) + +smesh.SetCurrentStudy(salome.myStudy) +import StdMeshers +Mesh_1 = smesh.Mesh(Box_1) +Regular_1D = Mesh_1.Segment() +Nb_Segments_1 = Regular_1D.NumberOfSegments(10) +Nb_Segments_1.SetDistrType( 0 ) +Quadrangle_2D = Mesh_1.Quadrangle() +Hexa_3D = Mesh_1.Hexahedron() +isDone = Mesh_1.Compute() + +### CreateDimGroup() + +aListOf3d_1=range(721,821) + +aGrp3D_1=Mesh_1.GetMesh().CreateGroup( smesh.VOLUME, "Src 3D 1" ) +aGrp3D_1.Add( aListOf3d_1 ) + +aListOf3d_2=range(821, 921) +aGrp3D_2=Mesh_1.GetMesh().CreateGroup( smesh.VOLUME, "Src 3D 2" ) +aGrp3D_2.Add( aListOf3d_2 ) + +aGrp2D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.FACE, "Faces" ) + +aGrp1D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.EDGE, "Edges" ) + +aGrp0D = Mesh_1.CreateDimGroup( [aGrp3D_1, aGrp3D_2], smesh.NODE, "Nodes" ) + +salome.sg.updateObjBrowser( 1 ) + diff --git a/src/SMESH_SWIG/smesh.py b/src/SMESH_SWIG/smesh.py index dc22bc389..c13a57e8e 100644 --- a/src/SMESH_SWIG/smesh.py +++ b/src/SMESH_SWIG/smesh.py @@ -1,24 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2007-2012 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 +# 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 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. +# 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 +# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # File : smesh.py # Author : Francis KLOSS, OCC # Module : SMESH @@ -34,12 +36,34 @@ import geompy import smeshDC from smeshDC import * +# get instance of class smeshDC smesh = salome.lcc.FindOrLoadComponent("FactoryServer", "SMESH") smesh.init_smesh(salome.myStudy,geompy.geom) -# Export the methods of smeshD +# load plugins +from smeshDC import Mesh, algoCreator +for pluginName in os.environ["SMESH_MeshersList"].split(":"): + + pluginName += "DC" + try: + exec("from %s import *" % pluginName ) + except Exception, e: + print "Exception while loading %s: %s" % ( pluginName, e ) + continue + exec("import %s" % pluginName ) + plugin = eval(pluginName) + + # add methods creating algorithms to Mesh + for k in dir(plugin): + if k[0] == '_':continue + algo = getattr(plugin,k) + if type( algo ).__name__ == 'classobj' and hasattr( algo, "meshMethod"): + if not hasattr( Mesh, algo.meshMethod ): + setattr( Mesh, algo.meshMethod, algoCreator()) + getattr( Mesh, algo.meshMethod ).add( algo ) + +# Export the methods of smeshDC for k in dir(smesh): if k[0] == '_':continue globals()[k]=getattr(smesh,k) del k - diff --git a/src/SMESH_SWIG/smeshDC.py b/src/SMESH_SWIG/smeshDC.py index 7b81262a0..9780503a1 100644 --- a/src/SMESH_SWIG/smeshDC.py +++ b/src/SMESH_SWIG/smeshDC.py @@ -1,28 +1,25 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # # File : smesh.py # Author : Francis KLOSS, OCC # Module : SMESH -# + """ \namespace smesh \brief Module smesh @@ -47,10 +44,7 @@ ## @defgroup l3_hypos_1dhyps 1D Meshing Hypotheses ## @defgroup l3_hypos_2dhyps 2D Meshing Hypotheses ## @defgroup l3_hypos_maxvol Max Element Volume hypothesis -## @defgroup l3_hypos_netgen Netgen 2D and 3D hypotheses -## @defgroup l3_hypos_ghs3dh GHS3D Parameters hypothesis -## @defgroup l3_hypos_blsurf BLSURF Parameters hypothesis -## @defgroup l3_hypos_hexotic Hexotic Parameters hypothesis +## @defgroup l3_hypos_quad Quadrangle Parameters hypothesis ## @defgroup l3_hypos_additi Additional Hypotheses ## @} @@ -88,6 +82,7 @@ ## @defgroup l2_modif_tofromqu Convert to/from Quadratic Mesh ## @} +## @defgroup l1_measurements Measurements import salome import geompyDC @@ -95,41 +90,12 @@ import geompyDC import SMESH # This is necessary for back compatibility from SMESH import * -import StdMeshers - import SALOME - -# import NETGENPlugin module if possible -noNETGENPlugin = 0 -try: - import NETGENPlugin -except ImportError: - noNETGENPlugin = 1 - pass +import SALOMEDS ## @addtogroup l1_auxiliary ## @{ -# Types of algorithms -REGULAR = 1 -PYTHON = 2 -COMPOSITE = 3 -SOLE = 0 -SIMPLE = 1 - -MEFISTO = 3 -NETGEN = 4 -GHS3D = 5 -FULL_NETGEN = 6 -NETGEN_2D = 7 -NETGEN_1D2D = NETGEN -NETGEN_1D2D3D = FULL_NETGEN -NETGEN_FULL = FULL_NETGEN -Hexa = 8 -Hexotic = 9 -BLSURF = 10 -GHS3DPRL = 11 - # MirrorType enumeration POINT = SMESH_MeshEditor.POINT AXIS = SMESH_MeshEditor.AXIS @@ -139,213 +105,74 @@ PLANE = SMESH_MeshEditor.PLANE LAPLACIAN_SMOOTH = SMESH_MeshEditor.LAPLACIAN_SMOOTH CENTROIDAL_SMOOTH = SMESH_MeshEditor.CENTROIDAL_SMOOTH -# Fineness enumeration (for NETGEN) -VeryCoarse = 0 -Coarse = 1 -Moderate = 2 -Fine = 3 -VeryFine = 4 -Custom = 5 - -# Optimization level of GHS3D -None_Optimization, Light_Optimization, Medium_Optimization, Strong_Optimization = 0,1,2,3 - -# Topology treatment way of BLSURF -FromCAD, PreProcess, PreProcessPlus = 0,1,2 +PrecisionConfusion = 1e-07 -# Element size flag of BLSURF -DefaultSize, DefaultGeom, Custom = 0,0,1 +# TopAbs_State enumeration +[TopAbs_IN, TopAbs_OUT, TopAbs_ON, TopAbs_UNKNOWN] = range(4) -PrecisionConfusion = 1e-07 +# Methods of splitting a hexahedron into tetrahedra +Hex_5Tet, Hex_6Tet, Hex_24Tet = 1, 2, 3 ## Converts an angle from degrees to radians def DegreesToRadians(AngleInDegrees): from math import pi return AngleInDegrees * pi / 180.0 +import salome_notebook +notebook = salome_notebook.notebook # Salome notebook variable separator var_separator = ":" -# Parametrized substitute for PointStruct -class PointStructStr: - - x = 0 - y = 0 - z = 0 - xStr = "" - yStr = "" - zStr = "" - - def __init__(self, xStr, yStr, zStr): - self.xStr = xStr - self.yStr = yStr - self.zStr = zStr - if isinstance(xStr, str) and notebook.isVariable(xStr): - self.x = notebook.get(xStr) - else: - self.x = xStr - if isinstance(yStr, str) and notebook.isVariable(yStr): - self.y = notebook.get(yStr) - else: - self.y = yStr - if isinstance(zStr, str) and notebook.isVariable(zStr): - self.z = notebook.get(zStr) - else: - self.z = zStr - -# Parametrized substitute for PointStruct (with 6 parameters) -class PointStructStr6: - - x1 = 0 - y1 = 0 - z1 = 0 - x2 = 0 - y2 = 0 - z2 = 0 - xStr1 = "" - yStr1 = "" - zStr1 = "" - xStr2 = "" - yStr2 = "" - zStr2 = "" - - def __init__(self, x1Str, x2Str, y1Str, y2Str, z1Str, z2Str): - self.x1Str = x1Str - self.x2Str = x2Str - self.y1Str = y1Str - self.y2Str = y2Str - self.z1Str = z1Str - self.z2Str = z2Str - if isinstance(x1Str, str) and notebook.isVariable(x1Str): - self.x1 = notebook.get(x1Str) - else: - self.x1 = x1Str - if isinstance(x2Str, str) and notebook.isVariable(x2Str): - self.x2 = notebook.get(x2Str) - else: - self.x2 = x2Str - if isinstance(y1Str, str) and notebook.isVariable(y1Str): - self.y1 = notebook.get(y1Str) - else: - self.y1 = y1Str - if isinstance(y2Str, str) and notebook.isVariable(y2Str): - self.y2 = notebook.get(y2Str) - else: - self.y2 = y2Str - if isinstance(z1Str, str) and notebook.isVariable(z1Str): - self.z1 = notebook.get(z1Str) - else: - self.z1 = z1Str - if isinstance(z2Str, str) and notebook.isVariable(z2Str): - self.z2 = notebook.get(z2Str) - else: - self.z2 = z2Str - -# Parametrized substitute for AxisStruct -class AxisStructStr: - - x = 0 - y = 0 - z = 0 - dx = 0 - dy = 0 - dz = 0 - xStr = "" - yStr = "" - zStr = "" - dxStr = "" - dyStr = "" - dzStr = "" - - def __init__(self, xStr, yStr, zStr, dxStr, dyStr, dzStr): - self.xStr = xStr - self.yStr = yStr - self.zStr = zStr - self.dxStr = dxStr - self.dyStr = dyStr - self.dzStr = dzStr - if isinstance(xStr, str) and notebook.isVariable(xStr): - self.x = notebook.get(xStr) - else: - self.x = xStr - if isinstance(yStr, str) and notebook.isVariable(yStr): - self.y = notebook.get(yStr) - else: - self.y = yStr - if isinstance(zStr, str) and notebook.isVariable(zStr): - self.z = notebook.get(zStr) - else: - self.z = zStr - if isinstance(dxStr, str) and notebook.isVariable(dxStr): - self.dx = notebook.get(dxStr) - else: - self.dx = dxStr - if isinstance(dyStr, str) and notebook.isVariable(dyStr): - self.dy = notebook.get(dyStr) - else: - self.dy = dyStr - if isinstance(dzStr, str) and notebook.isVariable(dzStr): - self.dz = notebook.get(dzStr) - else: - self.dz = dzStr - -# Parametrized substitute for DirStruct -class DirStructStr: - - def __init__(self, pointStruct): - self.pointStruct = pointStruct - -# Returns list of variable values from salome notebook -def ParsePointStruct(Point): - Parameters = 2*var_separator - if isinstance(Point, PointStructStr): - Parameters = str(Point.xStr) + var_separator + str(Point.yStr) + var_separator + str(Point.zStr) - Point = PointStruct(Point.x, Point.y, Point.z) - return Point, Parameters - -# Returns list of variable values from salome notebook -def ParseDirStruct(Dir): - Parameters = 2*var_separator - if isinstance(Dir, DirStructStr): - pntStr = Dir.pointStruct - if isinstance(pntStr, PointStructStr6): - Parameters = str(pntStr.x1Str) + var_separator + str(pntStr.x2Str) + var_separator - Parameters += str(pntStr.y1Str) + var_separator + str(pntStr.y2Str) + var_separator - Parameters += str(pntStr.z1Str) + var_separator + str(pntStr.z2Str) - Point = PointStruct(pntStr.x2 - pntStr.x1, pntStr.y2 - pntStr.y1, pntStr.z2 - pntStr.z1) - else: - Parameters = str(pntStr.xStr) + var_separator + str(pntStr.yStr) + var_separator + str(pntStr.zStr) - Point = PointStruct(pntStr.x, pntStr.y, pntStr.z) - Dir = DirStruct(Point) - return Dir, Parameters - -# Returns list of variable values from salome notebook -def ParseAxisStruct(Axis): - Parameters = 5*var_separator - if isinstance(Axis, AxisStructStr): - Parameters = str(Axis.xStr) + var_separator + str(Axis.yStr) + var_separator + str(Axis.zStr) + var_separator - Parameters += str(Axis.dxStr) + var_separator + str(Axis.dyStr) + var_separator + str(Axis.dzStr) - Axis = AxisStruct(Axis.x, Axis.y, Axis.z, Axis.dx, Axis.dy, Axis.dz) - return Axis, Parameters - -## Return list of variable values from salome notebook -def ParseAngles(list): +## Return list of variable values from salome notebook. +# The last argument, if is callable, is used to modify values got from notebook +def ParseParameters(*args): Result = [] Parameters = "" - for parameter in list: - if isinstance(parameter,str) and notebook.isVariable(parameter): - Result.append(DegreesToRadians(notebook.get(parameter))) - pass - else: - Result.append(parameter) + hasVariables = False + varModifFun=None + if args and callable( args[-1] ): + args, varModifFun = args[:-1], args[-1] + for parameter in args: + + Parameters += str(parameter) + var_separator + + if isinstance(parameter,str): + # check if there is an inexistent variable name + if not notebook.isVariable(parameter): + raise ValueError, "Variable with name '" + parameter + "' doesn't exist!!!" + parameter = notebook.get(parameter) + hasVariables = True + if varModifFun: + parameter = varModifFun(parameter) + pass pass - - Parameters = Parameters + str(parameter) - Parameters = Parameters + var_separator + Result.append(parameter) + pass - Parameters = Parameters[:len(Parameters)-1] - return Result, Parameters - + Parameters = Parameters[:-1] + Result.append( Parameters ) + Result.append( hasVariables ) + return Result + +# Parse parameters converting variables to radians +def ParseAngles(*args): + return ParseParameters( *( args + (DegreesToRadians, ))) + +# Substitute PointStruct.__init__() to create SMESH.PointStruct using notebook variables. +# Parameters are stored in PointStruct.parameters attribute +def __initPointStruct(point,*args): + point.x, point.y, point.z, point.parameters,hasVars = ParseParameters(*args) + pass +SMESH.PointStruct.__init__ = __initPointStruct + +# Substitute AxisStruct.__init__() to create SMESH.AxisStruct using notebook variables. +# Parameters are stored in AxisStruct.parameters attribute +def __initAxisStruct(ax,*args): + ax.x, ax.y, ax.z, ax.vx, ax.vy, ax.vz, ax.parameters,hasVars = ParseParameters(*args) + pass +SMESH.AxisStruct.__init__ = __initAxisStruct + + def IsEqual(val1, val2, tol=PrecisionConfusion): if abs(val1 - val2) < tol: return True @@ -355,13 +182,33 @@ NO_NAME = "NoName" ## Gets object name def GetName(obj): - ior = salome.orb.object_to_string(obj) - sobj = salome.myStudy.FindObjectIOR(ior) - if sobj is None: - return NO_NAME - else: - attr = sobj.FindAttribute("AttributeName")[1] - return attr.Value() + if obj: + # object not null + if isinstance(obj, SALOMEDS._objref_SObject): + # study object + return obj.GetName() + ior = salome.orb.object_to_string(obj) + if ior: + # CORBA object + studies = salome.myStudyManager.GetOpenStudies() + for sname in studies: + s = salome.myStudyManager.GetStudyByName(sname) + if not s: continue + sobj = s.FindObjectIOR(ior) + if not sobj: continue + return sobj.GetName() + if hasattr(obj, "GetName"): + # unknown CORBA object, having GetName() method + return obj.GetName() + else: + # unknown CORBA object, no GetName() method + return NO_NAME + pass + if hasattr(obj, "GetName"): + # unknown non-CORBA object, having GetName() method + return obj.GetName() + pass + raise RuntimeError, "Null or invalid object" ## Prints error message if a hypothesis was not assigned. def TreatHypoStatus(status, hypName, geomName, isAlgo): @@ -377,13 +224,14 @@ def TreatHypoStatus(status, hypName, geomName, isAlgo): elif status == HYP_NOTCONFORM : reason = "a non-conform mesh would be built" elif status == HYP_ALREADY_EXIST : + if isAlgo: return # it does not influence anything reason = hypType + " of the same dimension is already assigned to this shape" elif status == HYP_BAD_DIM : reason = hypType + " mismatches the shape" elif status == HYP_CONCURENT : reason = "there are concurrent hypotheses on sub-shapes" elif status == HYP_BAD_SUBSHAPE : - reason = "the shape is neither the main one, nor its subshape, nor a valid group" + reason = "the shape is neither the main one, nor its sub-shape, nor a valid group" elif status == HYP_BAD_GEOMETRY: reason = "geometry mismatches the expectation of the algorithm" elif status == HYP_HIDDEN_ALGO: @@ -396,18 +244,76 @@ def TreatHypoStatus(status, hypName, geomName, isAlgo): return hypName = '"' + hypName + '"' geomName= '"' + geomName+ '"' - if status < HYP_UNKNOWN_FATAL: + if status < HYP_UNKNOWN_FATAL and not geomName =='""': print hypName, "was assigned to", geomName,"but", reason - else: + elif not geomName == '""': print hypName, "was not assigned to",geomName,":", reason + else: + print hypName, "was not assigned:", reason pass +## Private method. Add geom (sub-shape of the main shape) into the study if not yet there +def AssureGeomPublished(mesh, geom, name=''): + if not isinstance( geom, geompyDC.GEOM._objref_GEOM_Object ): + return + if not geom.IsSame( mesh.geom ) and \ + not geom.GetStudyEntry() and \ + mesh.smeshpyD.GetCurrentStudy(): + ## set the study + studyID = mesh.smeshpyD.GetCurrentStudy()._get_StudyId() + if studyID != mesh.geompyD.myStudyId: + mesh.geompyD.init_geom( mesh.smeshpyD.GetCurrentStudy()) + ## get a name + if not name and geom.GetShapeType() != geompyDC.GEOM.COMPOUND: + # for all groups SubShapeName() returns "Compound_-1" + name = mesh.geompyD.SubShapeName(geom, mesh.geom) + if not name: + name = "%s_%s"%(geom.GetShapeType(), id(geom)%10000) + ## publish + mesh.geompyD.addToStudyInFather( mesh.geom, geom, name ) + return + +## Return the first vertex of a geomertical edge by ignoring orienation +def FirstVertexOnCurve(edge): + from geompy import SubShapeAll, ShapeType, KindOfShape, PointCoordinates + vv = SubShapeAll( edge, ShapeType["VERTEX"]) + if not vv: + raise TypeError, "Given object has no vertices" + if len( vv ) == 1: return vv[0] + info = KindOfShape(edge) + xyz = info[1:4] # coords of the first vertex + xyz1 = PointCoordinates( vv[0] ) + xyz2 = PointCoordinates( vv[1] ) + dist1, dist2 = 0,0 + for i in range(3): + dist1 += abs( xyz[i] - xyz1[i] ) + dist2 += abs( xyz[i] - xyz2[i] ) + if dist1 < dist2: + return vv[0] + else: + return vv[1] + # end of l1_auxiliary ## @} # All methods of this class are accessible directly from the smesh.py package. class smeshDC(SMESH._objref_SMESH_Gen): + ## Dump component to the Python script + # This method overrides IDL function to allow default values for the parameters. + def DumpPython(self, theStudy, theIsPublished=True, theIsMultiFile=True): + return SMESH._objref_SMESH_Gen.DumpPython(self, theStudy, theIsPublished, theIsMultiFile) + + ## Set mode of DumpPython(), \a historical or \a snapshot. + # In the \a historical mode, the Python Dump script includes all commands + # performed by SMESH engine. In the \a snapshot mode, commands + # relating to objects removed from the Study are excluded from the script + # as well as commands not influencing the current state of meshes + def SetDumpPythonHistorical(self, isHistorical): + if isHistorical: val = "true" + else: val = "false" + SMESH._objref_SMESH_Gen.SetOption(self, "historical_python_dump", val) + ## Sets the current study and Geometry component # @ingroup l1_auxiliary def init_smesh(self,theStudy,geompyD): @@ -420,7 +326,9 @@ class smeshDC(SMESH._objref_SMESH_Gen): # @return an instance of Mesh class. # @ingroup l2_construct def Mesh(self, obj=0, name=0): - return Mesh(self,self.geompyD,obj,name) + if isinstance(obj,str): + obj,name = name,obj + return Mesh(self,self.geompyD,obj,name) ## Returns a long value from enumeration # Should be used for SMESH.FunctorType enumeration @@ -428,6 +336,20 @@ class smeshDC(SMESH._objref_SMESH_Gen): def EnumToLong(self,theItem): return theItem._v + ## Returns a string representation of the color. + # To be used with filters. + # @param c color value (SALOMEDS.Color) + # @ingroup l1_controls + def ColorToString(self,c): + val = "" + if isinstance(c, SALOMEDS.Color): + val = "%s;%s;%s" % (c.R, c.G, c.B) + elif isinstance(c, str): + val = c + else: + raise ValueError, "Color value should be of string or SALOMEDS.Color type" + return val + ## Gets PointStruct from vertex # @param theVertex a GEOM object(vertex) # @return SMESH.PointStruct @@ -493,7 +415,6 @@ class smeshDC(SMESH._objref_SMESH_Gen): # @param name a new object name # @ingroup l1_auxiliary def SetName(self, obj, name): - print "obj_name = ", name if isinstance( obj, Mesh ): obj = obj.GetMesh() elif isinstance( obj, Mesh_Algorithm ): @@ -524,6 +445,11 @@ class smeshDC(SMESH._objref_SMESH_Gen): self.geompyD=geompyD self.SetGeomEngine(geompyD) SMESH._objref_SMESH_Gen.SetCurrentStudy(self,theStudy) + global notebook + if theStudy: + notebook = salome_notebook.NoteBook( theStudy ) + else: + notebook = salome_notebook.NoteBook( salome_notebook.PseudoStudyForNoteBook() ) ## Gets the current study # @ingroup l1_auxiliary @@ -550,6 +476,17 @@ class smeshDC(SMESH._objref_SMESH_Gen): aMeshes.append(aMesh) return aMeshes, aStatus + ## Creates a Mesh object(s) importing data from the given SAUV file + # @return a list of Mesh class instances + # @ingroup l2_impexp + def CreateMeshesFromSAUV( self,theFileName ): + aSmeshMeshes, aStatus = SMESH._objref_SMESH_Gen.CreateMeshesFromSAUV(self,theFileName) + aMeshes = [] + for iMesh in range(len(aSmeshMeshes)) : + aMesh = Mesh(self, self.geompyD, aSmeshMeshes[iMesh]) + aMeshes.append(aMesh) + return aMeshes, aStatus + ## Creates a Mesh object importing data from the given STL file # @return an instance of Mesh class # @ingroup l2_impexp @@ -558,6 +495,17 @@ class smeshDC(SMESH._objref_SMESH_Gen): aMesh = Mesh(self, self.geompyD, aSmeshMesh) return aMesh + ## Creates Mesh objects importing data from the given CGNS file + # @return an instance of Mesh class + # @ingroup l2_impexp + def CreateMeshesFromCGNS( self, theFileName ): + aSmeshMeshes, aStatus = SMESH._objref_SMESH_Gen.CreateMeshesFromCGNS(self,theFileName) + aMeshes = [] + for iMesh in range(len(aSmeshMeshes)) : + aMesh = Mesh(self, self.geompyD, aSmeshMeshes[iMesh]) + aMeshes.append(aMesh) + return aMeshes, aStatus + ## Concatenate the given meshes into one mesh. # @return an instance of Mesh class # @param meshes the meshes to combine into one mesh @@ -567,6 +515,12 @@ class smeshDC(SMESH._objref_SMESH_Gen): # @param allGroups forces creation of groups of all elements def Concatenate( self, meshes, uniteIdenticalGroups, mergeNodesAndElements = False, mergeTolerance = 1e-5, allGroups = False): + if not meshes: return None + for i,m in enumerate(meshes): + if isinstance(m, Mesh): + meshes[i] = m.GetMesh() + mergeTolerance,Parameters,hasVars = ParseParameters(mergeTolerance) + meshes[0].SetParameters(Parameters) if allGroups: aSmeshMesh = SMESH._objref_SMESH_Gen.ConcatenateWithGroups( self,meshes,uniteIdenticalGroups,mergeNodesAndElements,mergeTolerance) @@ -576,6 +530,20 @@ class smeshDC(SMESH._objref_SMESH_Gen): aMesh = Mesh(self, self.geompyD, aSmeshMesh) return aMesh + ## Create a mesh by copying a part of another mesh. + # @param meshPart a part of mesh to copy, either a Mesh, a sub-mesh or a group; + # to copy nodes or elements not contained in any mesh object, + # pass result of Mesh.GetIDSource( list_of_ids, type ) as meshPart + # @param meshName a name of the new mesh + # @param toCopyGroups to create in the new mesh groups the copied elements belongs to + # @param toKeepIDs to preserve IDs of the copied elements or not + # @return an instance of Mesh class + def CopyMesh( self, meshPart, meshName, toCopyGroups=False, toKeepIDs=False): + if (isinstance( meshPart, Mesh )): + meshPart = meshPart.GetMesh() + mesh = SMESH._objref_SMESH_Gen.CopyMesh( self,meshPart,meshName,toCopyGroups,toKeepIDs ) + return Mesh(self, self.geompyD, mesh) + ## From SMESH_Gen interface # @return the list of integer values # @ingroup l1_auxiliary @@ -597,26 +565,6 @@ class smeshDC(SMESH._objref_SMESH_Gen): def SetBoundaryBoxSegmentation(self, nbSegments): SMESH._objref_SMESH_Gen.SetBoundaryBoxSegmentation(self,nbSegments) - ## Concatenate the given meshes into one mesh. - # @return an instance of Mesh class - # @param meshes the meshes to combine into one mesh - # @param uniteIdenticalGroups if true, groups with same names are united, else they are renamed - # @param mergeNodesAndElements if true, equal nodes and elements aremerged - # @param mergeTolerance tolerance for merging nodes - # @param allGroups forces creation of groups of all elements - def Concatenate( self, meshes, uniteIdenticalGroups, - mergeNodesAndElements = False, mergeTolerance = 1e-5, allGroups = False): - mergeTolerance,Parameters = geompyDC.ParseParameters(mergeTolerance) - if allGroups: - aSmeshMesh = SMESH._objref_SMESH_Gen.ConcatenateWithGroups( - self,meshes,uniteIdenticalGroups,mergeNodesAndElements,mergeTolerance) - else: - aSmeshMesh = SMESH._objref_SMESH_Gen.Concatenate( - self,meshes,uniteIdenticalGroups,mergeNodesAndElements,mergeTolerance) - aSmeshMesh.SetParameters(Parameters) - aMesh = Mesh(self, self.geompyD, aSmeshMesh) - return aMesh - # Filtering. Auxiliary functions: # ------------------------------ @@ -638,26 +586,35 @@ class smeshDC(SMESH._objref_SMESH_Gen): UnaryOp, BinaryOp, Tolerance, TypeOfElement, Precision) ## Creates a criterion by the given parameters + # \n Criterion structures allow to define complex filters by combining them with logical operations (AND / OR) (see example below) # @param elementType the type of elements(NODE, EDGE, FACE, VOLUME) # @param CritType the type of criterion (FT_Taper, FT_Area, FT_RangeOfIds, FT_LyingOnGeom etc.) # @param Compare belongs to {FT_LessThan, FT_MoreThan, FT_EqualTo} - # @param Treshold the threshold value (range of ids as string, shape, numeric) + # @param Threshold the threshold value (range of ids as string, shape, numeric) # @param UnaryOp FT_LogicalNOT or FT_Undefined # @param BinaryOp a binary logical operation FT_LogicalAND, FT_LogicalOR or # FT_Undefined (must be for the last criterion of all criteria) + # @param Tolerance the tolerance used by FT_BelongToGeom, FT_BelongToSurface, + # FT_LyingOnGeom, FT_CoplanarFaces criteria # @return SMESH.Filter.Criterion + # + # Example of Criteria usage # @ingroup l1_controls def GetCriterion(self,elementType, CritType, Compare = FT_EqualTo, - Treshold="", + Threshold="", UnaryOp=FT_Undefined, - BinaryOp=FT_Undefined): + BinaryOp=FT_Undefined, + Tolerance=1e-07): + if not CritType in SMESH.FunctorType._items: + raise TypeError, "CritType should be of SMESH.FunctorType" aCriterion = self.GetEmptyCriterion() aCriterion.TypeOfElement = elementType aCriterion.Type = self.EnumToLong(CritType) + aCriterion.Tolerance = Tolerance - aTreshold = Treshold + aThreshold = Threshold if Compare in [FT_LessThan, FT_MoreThan, FT_EqualTo]: aCriterion.Compare = self.EnumToLong(Compare) @@ -667,47 +624,89 @@ class smeshDC(SMESH._objref_SMESH_Gen): aCriterion.Compare = self.EnumToLong(FT_LessThan) elif Compare == ">": aCriterion.Compare = self.EnumToLong(FT_MoreThan) - else: + elif Compare != FT_Undefined: aCriterion.Compare = self.EnumToLong(FT_EqualTo) - aTreshold = Compare + aThreshold = Compare if CritType in [FT_BelongToGeom, FT_BelongToPlane, FT_BelongToGenSurface, FT_BelongToCylinder, FT_LyingOnGeom]: - # Checks the treshold - if isinstance(aTreshold, geompyDC.GEOM._objref_GEOM_Object): - aCriterion.ThresholdStr = GetName(aTreshold) - aCriterion.ThresholdID = salome.ObjectToID(aTreshold) + # Checks that Threshold is GEOM object + if isinstance(aThreshold, geompyDC.GEOM._objref_GEOM_Object): + aCriterion.ThresholdStr = GetName(aThreshold) + aCriterion.ThresholdID = aThreshold.GetStudyEntry() + if not aCriterion.ThresholdID: + raise RuntimeError, "Threshold shape must be published" else: - print "Error: The treshold should be a shape." + print "Error: The Threshold should be a shape." return None + if isinstance(UnaryOp,float): + aCriterion.Tolerance = UnaryOp + UnaryOp = FT_Undefined + pass elif CritType == FT_RangeOfIds: - # Checks the treshold - if isinstance(aTreshold, str): - aCriterion.ThresholdStr = aTreshold + # Checks that Threshold is string + if isinstance(aThreshold, str): + aCriterion.ThresholdStr = aThreshold else: - print "Error: The treshold should be a string." + print "Error: The Threshold should be a string." + return None + elif CritType == FT_CoplanarFaces: + # Checks the Threshold + if isinstance(aThreshold, int): + aCriterion.ThresholdID = str(aThreshold) + elif isinstance(aThreshold, str): + ID = int(aThreshold) + if ID < 1: + raise ValueError, "Invalid ID of mesh face: '%s'"%aThreshold + aCriterion.ThresholdID = aThreshold + else: + raise ValueError,\ + "The Threshold should be an ID of mesh face and not '%s'"%aThreshold + elif CritType == FT_ElemGeomType: + # Checks the Threshold + try: + aCriterion.Threshold = self.EnumToLong(aThreshold) + assert( aThreshold in SMESH.GeometryType._items ) + except: + if isinstance(aThreshold, int): + aCriterion.Threshold = aThreshold + else: + print "Error: The Threshold should be an integer or SMESH.GeometryType." + return None + pass + pass + elif CritType == FT_GroupColor: + # Checks the Threshold + try: + aCriterion.ThresholdStr = self.ColorToString(aThreshold) + except: + print "Error: The threshold value should be of SALOMEDS.Color type" return None - elif CritType in [FT_FreeBorders, FT_FreeEdges, FT_BadOrientedVolume, FT_FreeNodes, - FT_FreeFaces, FT_ElemGeomType, FT_GroupColor]: - # At this point the treshold is unnecessary - if aTreshold == FT_LogicalNOT: + pass + elif CritType in [FT_FreeBorders, FT_FreeEdges, FT_FreeNodes, FT_FreeFaces, + FT_LinearOrQuadratic, FT_BadOrientedVolume, + FT_BareBorderFace, FT_BareBorderVolume, + FT_OverConstrainedFace, FT_OverConstrainedVolume, + FT_EqualNodes,FT_EqualEdges,FT_EqualFaces,FT_EqualVolumes ]: + # At this point the Threshold is unnecessary + if aThreshold == FT_LogicalNOT: aCriterion.UnaryOp = self.EnumToLong(FT_LogicalNOT) - elif aTreshold in [FT_LogicalAND, FT_LogicalOR]: - aCriterion.BinaryOp = aTreshold + elif aThreshold in [FT_LogicalAND, FT_LogicalOR]: + aCriterion.BinaryOp = aThreshold else: - # Check treshold + # Check Threshold try: - aTreshold = float(aTreshold) - aCriterion.Threshold = aTreshold + aThreshold = float(aThreshold) + aCriterion.Threshold = aThreshold except: - print "Error: The treshold should be a number." + print "Error: The Threshold should be a number." return None - if Treshold == FT_LogicalNOT or UnaryOp == FT_LogicalNOT: + if Threshold == FT_LogicalNOT or UnaryOp == FT_LogicalNOT: aCriterion.UnaryOp = self.EnumToLong(FT_LogicalNOT) - if Treshold in [FT_LogicalAND, FT_LogicalOR]: - aCriterion.BinaryOp = self.EnumToLong(Treshold) + if Threshold in [FT_LogicalAND, FT_LogicalOR]: + aCriterion.BinaryOp = self.EnumToLong(Threshold) if UnaryOp in [FT_LogicalAND, FT_LogicalOR]: aCriterion.BinaryOp = self.EnumToLong(UnaryOp) @@ -721,21 +720,40 @@ class smeshDC(SMESH._objref_SMESH_Gen): # @param elementType the type of elements in the group # @param CritType the type of criterion ( FT_Taper, FT_Area, FT_RangeOfIds, FT_LyingOnGeom etc. ) # @param Compare belongs to {FT_LessThan, FT_MoreThan, FT_EqualTo} - # @param Treshold the threshold value (range of id ids as string, shape, numeric) + # @param Threshold the threshold value (range of id ids as string, shape, numeric) # @param UnaryOp FT_LogicalNOT or FT_Undefined + # @param Tolerance the tolerance used by FT_BelongToGeom, FT_BelongToSurface, + # FT_LyingOnGeom, FT_CoplanarFaces and FT_EqualNodes criteria # @return SMESH_Filter + # + # Example of Filters usage # @ingroup l1_controls def GetFilter(self,elementType, CritType=FT_Undefined, Compare=FT_EqualTo, - Treshold="", - UnaryOp=FT_Undefined): - aCriterion = self.GetCriterion(elementType, CritType, Compare, Treshold, UnaryOp, FT_Undefined) + Threshold="", + UnaryOp=FT_Undefined, + Tolerance=1e-07): + aCriterion = self.GetCriterion(elementType, CritType, Compare, Threshold, UnaryOp, FT_Undefined,Tolerance) aFilterMgr = self.CreateFilterManager() aFilter = aFilterMgr.CreateFilter() aCriteria = [] aCriteria.append(aCriterion) aFilter.SetCriteria(aCriteria) + aFilterMgr.UnRegister() + return aFilter + + ## Creates a filter from criteria + # @param criteria a list of criteria + # @return SMESH_Filter + # + # Example of Filters usage + # @ingroup l1_controls + def GetFilterFromCriteria(self,criteria): + aFilterMgr = self.CreateFilterManager() + aFilter = aFilterMgr.CreateFilter() + aFilter.SetCriteria(criteria) + aFilterMgr.UnRegister() return aFilter ## Creates a numerical functor by its type @@ -760,6 +778,10 @@ class smeshDC(SMESH._objref_SMESH_Gen): return aFilterMgr.CreateArea() elif theCriterion == FT_Volume3D: return aFilterMgr.CreateVolume3D() + elif theCriterion == FT_MaxElementLength2D: + return aFilterMgr.CreateMaxElementLength2D() + elif theCriterion == FT_MaxElementLength3D: + return aFilterMgr.CreateMaxElementLength3D() elif theCriterion == FT_MultiConnection: return aFilterMgr.CreateMultiConnection() elif theCriterion == FT_MultiConnection2D: @@ -769,14 +791,146 @@ class smeshDC(SMESH._objref_SMESH_Gen): elif theCriterion == FT_Length2D: return aFilterMgr.CreateLength2D() else: - print "Error: given parameter is not numerucal functor type." + print "Error: given parameter is not numerical functor type." ## Creates hypothesis - # @param - # @param + # @param theHType mesh hypothesis type (string) + # @param theLibName mesh plug-in library name # @return created hypothesis instance def CreateHypothesis(self, theHType, theLibName="libStdMeshersEngine.so"): - return SMESH._objref_SMESH_Gen.CreateHypothesis(self, theHType, theLibName ) + hyp = SMESH._objref_SMESH_Gen.CreateHypothesis(self, theHType, theLibName ) + + if isinstance( hyp, SMESH._objref_SMESH_Algo ): + return hyp + + # wrap hypothesis methods + #print "HYPOTHESIS", theHType + for meth_name in dir( hyp.__class__ ): + if not meth_name.startswith("Get") and \ + not meth_name in dir ( SMESH._objref_SMESH_Hypothesis ): + method = getattr ( hyp.__class__, meth_name ) + if callable(method): + setattr( hyp, meth_name, hypMethodWrapper( hyp, method )) + + return hyp + + ## Gets the mesh statistic + # @return dictionary "element type" - "count of elements" + # @ingroup l1_meshinfo + def GetMeshInfo(self, obj): + if isinstance( obj, Mesh ): + obj = obj.GetMesh() + d = {} + if hasattr(obj, "GetMeshInfo"): + values = obj.GetMeshInfo() + for i in range(SMESH.Entity_Last._v): + if i < len(values): d[SMESH.EntityType._item(i)]=values[i] + pass + return d + + ## Get minimum distance between two objects + # + # If @a src2 is None, and @a id2 = 0, distance from @a src1 / @a id1 to the origin is computed. + # If @a src2 is None, and @a id2 != 0, it is assumed that both @a id1 and @a id2 belong to @a src1. + # + # @param src1 first source object + # @param src2 second source object + # @param id1 node/element id from the first source + # @param id2 node/element id from the second (or first) source + # @param isElem1 @c True if @a id1 is element id, @c False if it is node id + # @param isElem2 @c True if @a id2 is element id, @c False if it is node id + # @return minimum distance value + # @sa GetMinDistance() + # @ingroup l1_measurements + def MinDistance(self, src1, src2=None, id1=0, id2=0, isElem1=False, isElem2=False): + result = self.GetMinDistance(src1, src2, id1, id2, isElem1, isElem2) + if result is None: + result = 0.0 + else: + result = result.value + return result + + ## Get measure structure specifying minimum distance data between two objects + # + # If @a src2 is None, and @a id2 = 0, distance from @a src1 / @a id1 to the origin is computed. + # If @a src2 is None, and @a id2 != 0, it is assumed that both @a id1 and @a id2 belong to @a src1. + # + # @param src1 first source object + # @param src2 second source object + # @param id1 node/element id from the first source + # @param id2 node/element id from the second (or first) source + # @param isElem1 @c True if @a id1 is element id, @c False if it is node id + # @param isElem2 @c True if @a id2 is element id, @c False if it is node id + # @return Measure structure or None if input data is invalid + # @sa MinDistance() + # @ingroup l1_measurements + def GetMinDistance(self, src1, src2=None, id1=0, id2=0, isElem1=False, isElem2=False): + if isinstance(src1, Mesh): src1 = src1.mesh + if isinstance(src2, Mesh): src2 = src2.mesh + if src2 is None and id2 != 0: src2 = src1 + if not hasattr(src1, "_narrow"): return None + src1 = src1._narrow(SMESH.SMESH_IDSource) + if not src1: return None + if id1 != 0: + m = src1.GetMesh() + e = m.GetMeshEditor() + if isElem1: + src1 = e.MakeIDSource([id1], SMESH.FACE) + else: + src1 = e.MakeIDSource([id1], SMESH.NODE) + pass + if hasattr(src2, "_narrow"): + src2 = src2._narrow(SMESH.SMESH_IDSource) + if src2 and id2 != 0: + m = src2.GetMesh() + e = m.GetMeshEditor() + if isElem2: + src2 = e.MakeIDSource([id2], SMESH.FACE) + else: + src2 = e.MakeIDSource([id2], SMESH.NODE) + pass + pass + aMeasurements = self.CreateMeasurements() + result = aMeasurements.MinDistance(src1, src2) + aMeasurements.UnRegister() + return result + + ## Get bounding box of the specified object(s) + # @param objects single source object or list of source objects + # @return tuple of six values (minX, minY, minZ, maxX, maxY, maxZ) + # @sa GetBoundingBox() + # @ingroup l1_measurements + def BoundingBox(self, objects): + result = self.GetBoundingBox(objects) + if result is None: + result = (0.0,)*6 + else: + result = (result.minX, result.minY, result.minZ, result.maxX, result.maxY, result.maxZ) + return result + + ## Get measure structure specifying bounding box data of the specified object(s) + # @param objects single source object or list of source objects + # @return Measure structure + # @sa BoundingBox() + # @ingroup l1_measurements + def GetBoundingBox(self, objects): + if isinstance(objects, tuple): + objects = list(objects) + if not isinstance(objects, list): + objects = [objects] + srclist = [] + for o in objects: + if isinstance(o, Mesh): + srclist.append(o.mesh) + elif hasattr(o, "_narrow"): + src = o._narrow(SMESH.SMESH_IDSource) + if src: srclist.append(src) + pass + pass + aMeasurements = self.CreateMeasurements() + result = aMeasurements.BoundingBox(srclist) + aMeasurements.UnRegister() + return result import omniORB #Registering the new proxy for SMESH_Gen @@ -812,16 +966,27 @@ class Mesh: if obj is None: obj = 0 if obj != 0: + objHasName = True if isinstance(obj, geompyDC.GEOM._objref_GEOM_Object): self.geom = obj + # publish geom of mesh (issue 0021122) + if not self.geom.GetStudyEntry() and smeshpyD.GetCurrentStudy(): + objHasName = False + studyID = smeshpyD.GetCurrentStudy()._get_StudyId() + if studyID != geompyD.myStudyId: + geompyD.init_geom( smeshpyD.GetCurrentStudy()) + pass + geo_name = "%s_%s_for_meshing"%(self.geom.GetShapeType(), id(self.geom)%100) + geompyD.addToStudy( self.geom, geo_name ) self.mesh = self.smeshpyD.CreateMesh(self.geom) + elif isinstance(obj, SMESH._objref_SMESH_Mesh): self.SetMesh(obj) else: self.mesh = self.smeshpyD.CreateEmptyMesh() if name != 0: self.smeshpyD.SetName(self.mesh, name) - elif obj != 0: + elif obj != 0 and objHasName: self.smeshpyD.SetName(self.mesh, GetName(obj)) if not self.geom: @@ -829,6 +994,12 @@ class Mesh: self.editor = self.mesh.GetMeshEditor() + # set self to algoCreator's + for attrName in dir(self): + attr = getattr( self, attrName ) + if isinstance( attr, algoCreator ): + setattr( self, attrName, attr.copy( self )) + ## Initializes the Mesh object from an instance of SMESH_Mesh interface # @param theMesh a SMESH_Mesh object # @ingroup l2_construct @@ -857,12 +1028,13 @@ class Mesh: ## Gets the subMesh object associated to a \a theSubObject geometrical object. # The subMesh object gives access to the IDs of nodes and elements. - # @param theSubObject a geometrical object (shape) - # @param theName a name for the submesh + # @param geom a geometrical object (shape) + # @param name a name for the submesh # @return an object of type SMESH_SubMesh, representing a part of mesh, which lies on the given shape # @ingroup l2_submeshes - def GetSubMesh(self, theSubObject, theName): - submesh = self.mesh.GetSubMesh(theSubObject, theName) + def GetSubMesh(self, geom, name): + AssureGeomPublished( self, geom, name ) + submesh = self.mesh.GetSubMesh( geom, name ) return submesh ## Returns the shape associated to the mesh @@ -877,8 +1049,12 @@ class Mesh: def SetShape(self, geom): self.mesh = self.smeshpyD.CreateMesh(geom) + ## Loads mesh from the study after opening the study + def Load(self): + self.mesh.Load() + ## Returns true if the hypotheses are defined well - # @param theSubObject a subshape of a mesh shape + # @param theSubObject a sub-shape of a mesh shape # @return True or False # @ingroup l2_construct def IsReadyToCompute(self, theSubObject): @@ -886,7 +1062,7 @@ class Mesh: ## Returns errors of hypotheses definition. # The list of errors is empty if everything is OK. - # @param theSubObject a subshape of a mesh shape + # @param theSubObject a sub-shape of a mesh shape # @return a list of errors # @ingroup l2_construct def GetAlgoState(self, theSubObject): @@ -917,163 +1093,27 @@ class Mesh: return 0; pass - ## Creates a segment discretization 1D algorithm. - # If the optional \a algo parameter is not set, this algorithm is REGULAR. - # \n If the optional \a geom parameter is not set, this algorithm is global. - # Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param algo the type of the required algorithm. Possible values are: - # - smesh.REGULAR, - # - smesh.PYTHON for discretization via a python function, - # - smesh.COMPOSITE for meshing a set of edges on one face side as a whole. - # @param geom If defined is the subshape to be meshed - # @return an instance of Mesh_Segment or Mesh_Segment_Python, or Mesh_CompositeSegment class - # @ingroup l3_algos_basic - def Segment(self, algo=REGULAR, geom=0): - ## if Segment(geom) is called by mistake - if isinstance( algo, geompyDC.GEOM._objref_GEOM_Object): - algo, geom = geom, algo - if not algo: algo = REGULAR - pass - if algo == REGULAR: - return Mesh_Segment(self, geom) - elif algo == PYTHON: - return Mesh_Segment_Python(self, geom) - elif algo == COMPOSITE: - return Mesh_CompositeSegment(self, geom) - else: - return Mesh_Segment(self, geom) - - ## Enables creation of nodes and segments usable by 2D algoritms. - # The added nodes and segments must be bound to edges and vertices by - # SetNodeOnVertex(), SetNodeOnEdge() and SetMeshElementOnShape() - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom the subshape to be manually meshed - # @return StdMeshers_UseExisting_1D algorithm that generates nothing - # @ingroup l3_algos_basic - def UseExistingSegments(self, geom=0): - algo = Mesh_UseExisting(1,self,geom) - return algo.GetAlgorithm() - - ## Enables creation of nodes and faces usable by 3D algoritms. - # The added nodes and faces must be bound to geom faces by SetNodeOnFace() - # and SetMeshElementOnShape() - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom the subshape to be manually meshed - # @return StdMeshers_UseExisting_2D algorithm that generates nothing - # @ingroup l3_algos_basic - def UseExistingFaces(self, geom=0): - algo = Mesh_UseExisting(2,self,geom) - return algo.GetAlgorithm() - - ## Creates a triangle 2D algorithm for faces. - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param algo values are: smesh.MEFISTO || smesh.NETGEN_1D2D || smesh.NETGEN_2D || smesh.BLSURF - # @param geom If defined, the subshape to be meshed (GEOM_Object) - # @return an instance of Mesh_Triangle algorithm - # @ingroup l3_algos_basic - def Triangle(self, algo=MEFISTO, geom=0): - ## if Triangle(geom) is called by mistake - if (isinstance(algo, geompyDC.GEOM._objref_GEOM_Object)): - geom = algo - algo = MEFISTO - - return Mesh_Triangle(self, algo, geom) - - ## Creates a quadrangle 2D algorithm for faces. - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom If defined, the subshape to be meshed (GEOM_Object) - # @return an instance of Mesh_Quadrangle algorithm - # @ingroup l3_algos_basic - def Quadrangle(self, geom=0): - return Mesh_Quadrangle(self, geom) - - ## Creates a tetrahedron 3D algorithm for solids. - # The parameter \a algo permits to choose the algorithm: NETGEN or GHS3D - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param algo values are: smesh.NETGEN, smesh.GHS3D, smesh.GHS3DPRL, smesh.FULL_NETGEN - # @param geom If defined, the subshape to be meshed (GEOM_Object) - # @return an instance of Mesh_Tetrahedron algorithm - # @ingroup l3_algos_basic - def Tetrahedron(self, algo=NETGEN, geom=0): - ## if Tetrahedron(geom) is called by mistake - if ( isinstance( algo, geompyDC.GEOM._objref_GEOM_Object)): - algo, geom = geom, algo - if not algo: algo = NETGEN - pass - return Mesh_Tetrahedron(self, algo, geom) - - ## Creates a hexahedron 3D algorithm for solids. - # If the optional \a geom parameter is not set, this algorithm is global. - # \n Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param algo possible values are: smesh.Hexa, smesh.Hexotic - # @param geom If defined, the subshape to be meshed (GEOM_Object) - # @return an instance of Mesh_Hexahedron algorithm - # @ingroup l3_algos_basic - def Hexahedron(self, algo=Hexa, geom=0): - ## if Hexahedron(geom, algo) or Hexahedron(geom) is called by mistake - if ( isinstance(algo, geompyDC.GEOM._objref_GEOM_Object) ): - if geom in [Hexa, Hexotic]: algo, geom = geom, algo - elif geom == 0: algo, geom = Hexa, algo - return Mesh_Hexahedron(self, algo, geom) - - ## Deprecated, used only for compatibility! - # @return an instance of Mesh_Netgen algorithm - # @ingroup l3_algos_basic - def Netgen(self, is3D, geom=0): - return Mesh_Netgen(self, is3D, geom) - - ## Creates a projection 1D algorithm for edges. - # If the optional \a geom parameter is not set, this algorithm is global. - # Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom If defined, the subshape to be meshed - # @return an instance of Mesh_Projection1D algorithm - # @ingroup l3_algos_proj - def Projection1D(self, geom=0): - return Mesh_Projection1D(self, geom) - - ## Creates a projection 2D algorithm for faces. - # If the optional \a geom parameter is not set, this algorithm is global. - # Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom If defined, the subshape to be meshed - # @return an instance of Mesh_Projection2D algorithm - # @ingroup l3_algos_proj - def Projection2D(self, geom=0): - return Mesh_Projection2D(self, geom) - - ## Creates a projection 3D algorithm for solids. - # If the optional \a geom parameter is not set, this algorithm is global. - # Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom If defined, the subshape to be meshed - # @return an instance of Mesh_Projection3D algorithm - # @ingroup l3_algos_proj - def Projection3D(self, geom=0): - return Mesh_Projection3D(self, geom) - - ## Creates a 3D extrusion (Prism 3D) or RadialPrism 3D algorithm for solids. - # If the optional \a geom parameter is not set, this algorithm is global. - # Otherwise, this algorithm defines a submesh based on \a geom subshape. - # @param geom If defined, the subshape to be meshed - # @return an instance of Mesh_Prism3D or Mesh_RadialPrism3D algorithm - # @ingroup l3_algos_radialp l3_algos_3dextr - def Prism(self, geom=0): - shape = geom - if shape==0: - shape = self.geom - nbSolids = len( self.geompyD.SubShapeAll( shape, geompyDC.ShapeType["SOLID"] )) - nbShells = len( self.geompyD.SubShapeAll( shape, geompyDC.ShapeType["SHELL"] )) - if nbSolids == 0 or nbSolids == nbShells: - return Mesh_Prism3D(self, geom) - return Mesh_RadialPrism3D(self, geom) + ## Evaluates size of prospective mesh on a shape + # @return a list where i-th element is a number of elements of i-th SMESH.EntityType + # To know predicted number of e.g. edges, inquire it this way + # Evaluate()[ EnumToLong( Entity_Edge )] + def Evaluate(self, geom=0): + if geom == 0 or not isinstance(geom, geompyDC.GEOM._objref_GEOM_Object): + if self.geom == 0: + geom = self.mesh.GetShapeToMesh() + else: + geom = self.geom + return self.smeshpyD.Evaluate(self.mesh, geom) + ## Computes the mesh and returns the status of the computation + # @param geom geomtrical shape on which mesh data should be computed + # @param discardModifs if True and the mesh has been edited since + # a last total re-compute and that may prevent successful partial re-compute, + # then the mesh is cleaned before Compute() # @return True or False # @ingroup l2_construct - def Compute(self, geom=0): + def Compute(self, geom=0, discardModifs=False): if geom == 0 or not isinstance(geom, geompyDC.GEOM._objref_GEOM_Object): if self.geom == 0: geom = self.mesh.GetShapeToMesh() @@ -1081,6 +1121,8 @@ class Mesh: geom = self.geom ok = False try: + if discardModifs and self.mesh.HasModificationsToDiscard(): # issue 0020693 + self.mesh.Clear() ok = self.smeshpyD.Compute(self.mesh, geom) except SALOME.SALOME_Exception, ex: print "Mesh computation failed, exception caught:" @@ -1090,8 +1132,64 @@ class Mesh: print "Mesh computation failed, exception caught:" traceback.print_exc() if True:#not ok: - errors = self.smeshpyD.GetAlgoState( self.mesh, geom ) allReasons = "" + + # Treat compute errors + computeErrors = self.smeshpyD.GetComputeErrors( self.mesh, geom ) + for err in computeErrors: + shapeText = "" + if self.mesh.HasShapeToMesh(): + try: + mainIOR = salome.orb.object_to_string(geom) + for sname in salome.myStudyManager.GetOpenStudies(): + s = salome.myStudyManager.GetStudyByName(sname) + if not s: continue + mainSO = s.FindObjectIOR(mainIOR) + if not mainSO: continue + if err.subShapeID == 1: + shapeText = ' on "%s"' % mainSO.GetName() + subIt = s.NewChildIterator(mainSO) + while subIt.More(): + subSO = subIt.Value() + subIt.Next() + obj = subSO.GetObject() + if not obj: continue + go = obj._narrow( geompyDC.GEOM._objref_GEOM_Object ) + if not go: continue + ids = go.GetSubShapeIndices() + if len(ids) == 1 and ids[0] == err.subShapeID: + shapeText = ' on "%s"' % subSO.GetName() + break + if not shapeText: + shape = self.geompyD.GetSubShape( geom, [err.subShapeID]) + if shape: + shapeText = " on %s #%s" % (shape.GetShapeType(), err.subShapeID) + else: + shapeText = " on subshape #%s" % (err.subShapeID) + except: + shapeText = " on subshape #%s" % (err.subShapeID) + errText = "" + stdErrors = ["OK", #COMPERR_OK + "Invalid input mesh", #COMPERR_BAD_INPUT_MESH + "std::exception", #COMPERR_STD_EXCEPTION + "OCC exception", #COMPERR_OCC_EXCEPTION + "SALOME exception", #COMPERR_SLM_EXCEPTION + "Unknown exception", #COMPERR_EXCEPTION + "Memory allocation problem", #COMPERR_MEMORY_PB + "Algorithm failed", #COMPERR_ALGO_FAILED + "Unexpected geometry"]#COMPERR_BAD_SHAPE + if err.code > 0: + if err.code < len(stdErrors): errText = stdErrors[err.code] + else: + errText = "code %s" % -err.code + if errText: errText += ". " + errText += err.comment + if allReasons != "":allReasons += "\n" + allReasons += '- "%s" failed%s. Error: %s' %(err.algoName, shapeText, errText) + pass + + # Treat hyp errors + errors = self.smeshpyD.GetAlgoState( self.mesh, geom ) for err in errors: if err.isGlobalAlgo: glob = "global" @@ -1117,34 +1215,45 @@ class Mesh: reason = "For unknown reason."+\ " Revise Mesh.Compute() implementation in smeshDC.py!" pass - if allReasons != "": - allReasons += "\n" - pass - allReasons += reason + if allReasons != "":allReasons += "\n" + allReasons += "- " + reason pass - if allReasons != "": - print '"' + GetName(self.mesh) + '"',"has not been computed:" + if not ok or allReasons != "": + msg = '"' + GetName(self.mesh) + '"' + if ok: msg += " has been computed with warnings" + else: msg += " has not been computed" + if allReasons != "": msg += ":" + else: msg += "." + print msg print allReasons - ok = False - elif not ok: - print '"' + GetName(self.mesh) + '"',"has not been computed." - pass pass - if salome.sg.hasDesktop(): + if salome.sg.hasDesktop() and self.mesh.GetStudyId() >= 0: smeshgui = salome.ImportComponentGUI("SMESH") - smeshgui.Init(salome.myStudyId) + smeshgui.Init(self.mesh.GetStudyId()) smeshgui.SetMeshIcon( salome.ObjectToID( self.mesh ), ok, (self.NbNodes()==0) ) salome.sg.updateObjBrowser(1) pass return ok + ## Return submesh objects list in meshing order + # @return list of list of submesh objects + # @ingroup l2_construct + def GetMeshOrder(self): + return self.mesh.GetMeshOrder() + + ## Return submesh objects list in meshing order + # @return list of list of submesh objects + # @ingroup l2_construct + def SetMeshOrder(self, submeshes): + return self.mesh.SetMeshOrder(submeshes) + ## Removes all nodes and elements # @ingroup l2_construct def Clear(self): self.mesh.Clear() if salome.sg.hasDesktop(): smeshgui = salome.ImportComponentGUI("SMESH") - smeshgui.Init(salome.myStudyId) + smeshgui.Init(self.mesh.GetStudyId()) smeshgui.SetMeshIcon( salome.ObjectToID( self.mesh ), False, True ) salome.sg.updateObjBrowser(1) @@ -1154,12 +1263,12 @@ class Mesh: self.mesh.ClearSubMesh(geomId) if salome.sg.hasDesktop(): smeshgui = salome.ImportComponentGUI("SMESH") - smeshgui.Init(salome.myStudyId) + smeshgui.Init(self.mesh.GetStudyId()) smeshgui.SetMeshIcon( salome.ObjectToID( self.mesh ), False, True ) salome.sg.updateObjBrowser(1) ## Computes a tetrahedral mesh using AutomaticLength + MEFISTO + NETGEN - # @param fineness [0,-1] defines mesh fineness + # @param fineness [0.0,1.0] defines mesh fineness # @return True or False # @ingroup l3_algos_basic def AutomaticTetrahedralization(self, fineness=0): @@ -1171,12 +1280,13 @@ class Mesh: self.Triangle().LengthFromEdges() pass if dim > 2 : + from NETGENPluginDC import NETGEN self.Tetrahedron(NETGEN) pass return self.Compute() ## Computes an hexahedral mesh using AutomaticLength + Quadrangle + Hexahedron - # @param fineness [0,-1] defines mesh fineness + # @param fineness [0.0, 1.0] defines mesh fineness # @return True or False # @ingroup l3_algos_basic def AutomaticHexahedralization(self, fineness=0): @@ -1206,14 +1316,36 @@ class Mesh: if not geom: geom = self.mesh.GetShapeToMesh() pass + AssureGeomPublished( self, geom, "shape for %s" % hyp.GetName()) status = self.mesh.AddHypothesis(geom, hyp) isAlgo = hyp._narrow( SMESH_Algo ) - TreatHypoStatus( status, GetName( hyp ), GetName( geom ), isAlgo ) + hyp_name = GetName( hyp ) + geom_name = "" + if geom: + geom_name = GetName( geom ) + TreatHypoStatus( status, hyp_name, geom_name, isAlgo ) return status + ## Return True if an algorithm of hypothesis is assigned to a given shape + # @param hyp a hypothesis to check + # @param geom a subhape of mesh geometry + # @return True of False + # @ingroup l2_hypotheses + def IsUsedHypothesis(self, hyp, geom): + if not hyp or not geom: + return False + if isinstance( hyp, Mesh_Algorithm ): + hyp = hyp.GetAlgorithm() + pass + hyps = self.GetHypothesisList(geom) + for h in hyps: + if h.GetId() == hyp.GetId(): + return True + return False + ## Unassigns a hypothesis # @param hyp a hypothesis to unassign - # @param geom a subshape of mesh geometry + # @param geom a sub-shape of mesh geometry # @return SMESH.Hypothesis_Status # @ingroup l2_hypotheses def RemoveHypothesis(self, hyp, geom=0): @@ -1227,7 +1359,7 @@ class Mesh: return status ## Gets the list of hypotheses added on a geometry - # @param geom a subshape of mesh geometry + # @param geom a sub-shape of mesh geometry # @return the sequence of SMESH_Hypothesis # @ingroup l2_hypotheses def GetHypothesisList(self, geom): @@ -1242,54 +1374,95 @@ class Mesh: pass pass - ## Creates a mesh group based on the geometric object \a grp - # and gives a \a name, \n if this parameter is not defined - # the name is the same as the geometric group name \n - # Note: Works like GroupOnGeom(). - # @param grp a geometric group, a vertex, an edge, a face or a solid - # @param name the name of the mesh group - # @return SMESH_GroupOnGeom - # @ingroup l2_grps_create - def Group(self, grp, name=""): - return self.GroupOnGeom(grp, name) - - ## Deprecated, used only for compatibility! Please, use ExportMED() method instead. - # Exports the mesh in a file in MED format and chooses the \a version of MED format - # @param f the file name - # @param version values are SMESH.MED_V2_1, SMESH.MED_V2_2 + ## Exports the mesh in a file in MED format and chooses the \a version of MED format + ## allowing to overwrite the file if it exists or add the exported data to its contents + # @param f is the file name + # @param auto_groups boolean parameter for creating/not creating + # the groups Group_On_All_Nodes, Group_On_All_Faces, ... ; + # the typical use is auto_groups=false. + # @param version MED format version(MED_V2_1 or MED_V2_2) + # @param overwrite boolean parameter for overwriting/not overwriting the file + # @param meshPart a part of mesh (group, sub-mesh) to export instead of the mesh # @ingroup l2_impexp - def ExportToMED(self, f, version, opt=0): - self.mesh.ExportToMED(f, opt, version) + def ExportMED(self, f, auto_groups=0, version=MED_V2_2, overwrite=1, meshPart=None): + if meshPart: + if isinstance( meshPart, list ): + meshPart = self.GetIDSource( meshPart, SMESH.ALL ) + self.mesh.ExportPartToMED( meshPart, f, auto_groups, version, overwrite ) + else: + self.mesh.ExportToMEDX(f, auto_groups, version, overwrite) - ## Exports the mesh in a file in MED format + ## Exports the mesh in a file in SAUV format # @param f is the file name # @param auto_groups boolean parameter for creating/not creating # the groups Group_On_All_Nodes, Group_On_All_Faces, ... ; # the typical use is auto_groups=false. - # @param version MED format version(MED_V2_1 or MED_V2_2) # @ingroup l2_impexp - def ExportMED(self, f, auto_groups=0, version=MED_V2_2): - self.mesh.ExportToMED(f, auto_groups, version) + def ExportSAUV(self, f, auto_groups=0): + self.mesh.ExportSAUV(f, auto_groups) ## Exports the mesh in a file in DAT format # @param f the file name + # @param meshPart a part of mesh (group, sub-mesh) to export instead of the mesh # @ingroup l2_impexp - def ExportDAT(self, f): - self.mesh.ExportDAT(f) + def ExportDAT(self, f, meshPart=None): + if meshPart: + if isinstance( meshPart, list ): + meshPart = self.GetIDSource( meshPart, SMESH.ALL ) + self.mesh.ExportPartToDAT( meshPart, f ) + else: + self.mesh.ExportDAT(f) ## Exports the mesh in a file in UNV format # @param f the file name + # @param meshPart a part of mesh (group, sub-mesh) to export instead of the mesh # @ingroup l2_impexp - def ExportUNV(self, f): - self.mesh.ExportUNV(f) + def ExportUNV(self, f, meshPart=None): + if meshPart: + if isinstance( meshPart, list ): + meshPart = self.GetIDSource( meshPart, SMESH.ALL ) + self.mesh.ExportPartToUNV( meshPart, f ) + else: + self.mesh.ExportUNV(f) ## Export the mesh in a file in STL format # @param f the file name # @param ascii defines the file encoding + # @param meshPart a part of mesh (group, sub-mesh) to export instead of the mesh # @ingroup l2_impexp - def ExportSTL(self, f, ascii=1): - self.mesh.ExportSTL(f, ascii) + def ExportSTL(self, f, ascii=1, meshPart=None): + if meshPart: + if isinstance( meshPart, list ): + meshPart = self.GetIDSource( meshPart, SMESH.ALL ) + self.mesh.ExportPartToSTL( meshPart, f, ascii ) + else: + self.mesh.ExportSTL(f, ascii) + ## Exports the mesh in a file in CGNS format + # @param f is the file name + # @param overwrite boolean parameter for overwriting/not overwriting the file + # @param meshPart a part of mesh (group, sub-mesh) to export instead of the mesh + # @ingroup l2_impexp + def ExportCGNS(self, f, overwrite=1, meshPart=None): + if isinstance( meshPart, list ): + meshPart = self.GetIDSource( meshPart, SMESH.ALL ) + if isinstance( meshPart, Mesh ): + meshPart = meshPart.mesh + elif not meshPart: + meshPart = self.mesh + self.mesh.ExportCGNS(meshPart, f, overwrite) + + ## Deprecated, used only for compatibility! Please, use ExportToMEDX() method instead. + # Exports the mesh in a file in MED format and chooses the \a version of MED format + ## allowing to overwrite the file if it exists or add the exported data to its contents + # @param f the file name + # @param version values are SMESH.MED_V2_1, SMESH.MED_V2_2 + # @param opt boolean parameter for creating/not creating + # the groups Group_On_All_Nodes, Group_On_All_Faces, ... + # @param overwrite boolean parameter for overwriting/not overwriting the file + # @ingroup l2_impexp + def ExportToMED(self, f, version, opt=0, overwrite=1): + self.mesh.ExportToMEDX(f, opt, version, overwrite) # Operations with groups: # ---------------------- @@ -1302,6 +1475,17 @@ class Mesh: def CreateEmptyGroup(self, elementType, name): return self.mesh.CreateGroup(elementType, name) + ## Creates a mesh group based on the geometric object \a grp + # and gives a \a name, \n if this parameter is not defined + # the name is the same as the geometric group name \n + # Note: Works like GroupOnGeom(). + # @param grp a geometric group, a vertex, an edge, a face or a solid + # @param name the name of the mesh group + # @return SMESH_GroupOnGeom + # @ingroup l2_grps_create + def Group(self, grp, name=""): + return self.GroupOnGeom(grp, name) + ## Creates a mesh group based on the geometrical object \a grp # and gives a \a name, \n if this parameter is not defined # the name is the same as the geometrical group name @@ -1312,40 +1496,44 @@ class Mesh: # @return SMESH_GroupOnGeom # @ingroup l2_grps_create def GroupOnGeom(self, grp, name="", typ=None): + AssureGeomPublished( self, grp, name ) if name == "": name = grp.GetName() - - if typ == None: - tgeo = str(grp.GetShapeType()) - if tgeo == "VERTEX": - typ = NODE - elif tgeo == "EDGE": - typ = EDGE - elif tgeo == "FACE": - typ = FACE - elif tgeo == "SOLID": - typ = VOLUME - elif tgeo == "SHELL": - typ = VOLUME - elif tgeo == "COMPOUND": - if len( self.geompyD.GetObjectIDs( grp )) == 0: - print "Mesh.Group: empty geometric group", GetName( grp ) - return 0 - tgeo = self.geompyD.GetType(grp) - if tgeo == geompyDC.ShapeType["VERTEX"]: - typ = NODE - elif tgeo == geompyDC.ShapeType["EDGE"]: - typ = EDGE - elif tgeo == geompyDC.ShapeType["FACE"]: - typ = FACE - elif tgeo == geompyDC.ShapeType["SOLID"]: - typ = VOLUME - - if typ == None: - print "Mesh.Group: bad first argument: expected a group, a vertex, an edge, a face or a solid" - return 0 + if not typ: + typ = self._groupTypeFromShape( grp ) + return self.mesh.CreateGroupFromGEOM(typ, name, grp) + + ## Pivate method to get a type of group on geometry + def _groupTypeFromShape( self, shape ): + tgeo = str(shape.GetShapeType()) + if tgeo == "VERTEX": + typ = NODE + elif tgeo == "EDGE": + typ = EDGE + elif tgeo == "FACE" or tgeo == "SHELL": + typ = FACE + elif tgeo == "SOLID" or tgeo == "COMPSOLID": + typ = VOLUME + elif tgeo == "COMPOUND": + sub = self.geompyD.SubShapeAll( shape, geompyDC.ShapeType["SHAPE"]) + if not sub: + raise ValueError,"_groupTypeFromShape(): empty geometric group or compound '%s'" % GetName(shape) + return self._groupTypeFromShape( sub[0] ) else: - return self.mesh.CreateGroupFromGEOM(typ, name, grp) + raise ValueError, \ + "_groupTypeFromShape(): invalid geometry '%s'" % GetName(shape) + return typ + + ## Creates a mesh group with given \a name based on the \a filter which + ## is a special type of group dynamically updating it's contents during + ## mesh modification + # @param typ the type of elements in the group + # @param name the name of the mesh group + # @param filter the filter defining group contents + # @return SMESH_GroupOnFilter + # @ingroup l2_grps_create + def GroupOnFilter(self, typ, name, filter): + return self.mesh.CreateGroupFromFilter(typ, name, filter) ## Creates a mesh group by the given ids of elements # @param groupName the name of the mesh group @@ -1363,8 +1551,10 @@ class Mesh: # @param elementType the type of elements in the group # @param CritType the type of criterion( FT_Taper, FT_Area, FT_RangeOfIds, FT_LyingOnGeom etc. ) # @param Compare belongs to {FT_LessThan, FT_MoreThan, FT_EqualTo} - # @param Treshold the threshold value (range of id ids as string, shape, numeric) + # @param Threshold the threshold value (range of id ids as string, shape, numeric) # @param UnaryOp FT_LogicalNOT or FT_Undefined + # @param Tolerance the tolerance used by FT_BelongToGeom, FT_BelongToSurface, + # FT_LyingOnGeom, FT_CoplanarFaces criteria # @return SMESH_Group # @ingroup l2_grps_create def MakeGroup(self, @@ -1372,9 +1562,10 @@ class Mesh: elementType, CritType=FT_Undefined, Compare=FT_EqualTo, - Treshold="", - UnaryOp=FT_Undefined): - aCriterion = self.smeshpyD.GetCriterion(elementType, CritType, Compare, Treshold, UnaryOp, FT_Undefined) + Threshold="", + UnaryOp=FT_Undefined, + Tolerance=1e-07): + aCriterion = self.smeshpyD.GetCriterion(elementType, CritType, Compare, Threshold, UnaryOp, FT_Undefined,Tolerance) group = self.MakeGroupByCriterion(groupName, aCriterion) return group @@ -1390,6 +1581,7 @@ class Mesh: aCriteria.append(Criterion) aFilter.SetCriteria(aCriteria) group = self.MakeGroupByFilter(groupName, aFilter) + aFilterMgr.UnRegister() return group ## Creates a mesh group by the given criteria (list of criteria) @@ -1402,6 +1594,7 @@ class Mesh: aFilter = aFilterMgr.CreateFilter() aFilter.SetCriteria(theCriteria) group = self.MakeGroupByFilter(groupName, aFilter) + aFilterMgr.UnRegister() return group ## Creates a mesh group by the given filter @@ -1410,29 +1603,11 @@ class Mesh: # @return SMESH_Group # @ingroup l2_grps_create def MakeGroupByFilter(self, groupName, theFilter): - anIds = theFilter.GetElementsId(self.mesh) - anElemType = theFilter.GetElementType() - group = self.MakeGroupByIds(groupName, anElemType, anIds) + group = self.CreateEmptyGroup(theFilter.GetElementType(), groupName) + theFilter.SetMesh( self.mesh ) + group.AddFrom( theFilter ) return group - ## Passes mesh elements through the given filter and return IDs of fitting elements - # @param theFilter SMESH_Filter - # @return a list of ids - # @ingroup l1_controls - def GetIdsFromFilter(self, theFilter): - return theFilter.GetElementsId(self.mesh) - - ## Verifies whether a 2D mesh element has free edges (edges connected to one face only)\n - # Returns a list of special structures (borders). - # @return a list of SMESH.FreeEdges.Border structure: edge id and ids of two its nodes. - # @ingroup l1_controls - def GetFreeBorders(self): - aFilterMgr = self.smeshpyD.CreateFilterManager() - aPredicate = aFilterMgr.CreateFreeEdges() - aPredicate.SetMesh(self.mesh) - aBorders = aPredicate.GetBorders() - return aBorders - ## Removes a group # @ingroup l2_grps_delete def RemoveGroup(self, group): @@ -1472,15 +1647,15 @@ class Mesh: # @ingroup l2_grps_operon def UnionGroups(self, group1, group2, name): return self.mesh.UnionGroups(group1, group2, name) - + ## Produces a union list of groups - # New group is created. All mesh elements that are present in + # New group is created. All mesh elements that are present in # initial groups are added to the new one # @return an instance of SMESH_Group # @ingroup l2_grps_operon def UnionListOfGroups(self, groups, name): return self.mesh.UnionListOfGroups(groups, name) - + ## Prodices an intersection of two groups # A new group is created. All mesh elements that are common # for the two initial groups are added to the new one. @@ -1488,9 +1663,9 @@ class Mesh: # @ingroup l2_grps_operon def IntersectGroups(self, group1, group2, name): return self.mesh.IntersectGroups(group1, group2, name) - + ## Produces an intersection of groups - # New group is created. All mesh elements that are present in all + # New group is created. All mesh elements that are present in all # initial groups simultaneously are added to the new one # @return an instance of SMESH_Group # @ingroup l2_grps_operon @@ -1504,19 +1679,19 @@ class Mesh: # @ingroup l2_grps_operon def CutGroups(self, main_group, tool_group, name): return self.mesh.CutGroups(main_group, tool_group, name) - + ## Produces a cut of groups - # A new group is created. All mesh elements that are present in main groups + # A new group is created. All mesh elements that are present in main groups # but do not present in tool groups are added to the new one # @return an instance of SMESH_Group # @ingroup l2_grps_operon def CutListOfGroups(self, main_groups, tool_groups, name): return self.mesh.CutListOfGroups(main_groups, tool_groups, name) - - ## Produces a group of elements with specified element type using list of existing groups - # A new group is created. System - # 1) extract all nodes on which groups elements are built - # 2) combine all elements of specified dimension laying on these nodes + + ## Produces a group of elements of specified type using list of existing groups + # A new group is created. System + # 1) extracts all nodes on which groups elements are built + # 2) combines all elements of specified dimension laying on these nodes # @return an instance of SMESH_Group # @ingroup l2_grps_operon def CreateDimGroup(self, groups, elem_type, name): @@ -1586,6 +1761,13 @@ class Mesh: def GetMeshEditor(self): return self.mesh.GetMeshEditor() + ## Wrap a list of IDs of elements or nodes into SMESH_IDSource which + # can be passed as argument to a method accepting mesh, group or sub-mesh + # @return an instance of SMESH_IDSource + # @ingroup l1_auxiliary + def GetIDSource(self, ids, elemType): + return self.GetMeshEditor().MakeIDSource(ids, elemType) + ## Gets MED Mesh # @return an instance of SALOME_MED::MESH # @ingroup l1_auxiliary @@ -1596,6 +1778,13 @@ class Mesh: # Get informations about mesh contents: # ------------------------------------ + ## Gets the mesh stattistic + # @return dictionary type element - count of elements + # @ingroup l1_meshinfo + def GetMeshInfo(self, obj = None): + if not obj: obj = self.mesh + return self.smeshpyD.GetMeshInfo(obj) + ## Returns the number of nodes in the mesh # @return an integer value # @ingroup l1_meshinfo @@ -1608,6 +1797,18 @@ class Mesh: def NbElements(self): return self.mesh.NbElements() + ## Returns the number of 0d elements in the mesh + # @return an integer value + # @ingroup l1_meshinfo + def Nb0DElements(self): + return self.mesh.Nb0DElements() + + ## Returns the number of ball discrete elements in the mesh + # @return an integer value + # @ingroup l1_meshinfo + def NbBalls(self): + return self.mesh.NbBalls() + ## Returns the number of edges in the mesh # @return an integer value # @ingroup l1_meshinfo @@ -1664,6 +1865,12 @@ class Mesh: def NbQuadranglesOfOrder(self, elementOrder): return self.mesh.NbQuadranglesOfOrder(elementOrder) + ## Returns the number of biquadratic quadrangles in the mesh + # @return an integer value + # @ingroup l1_meshinfo + def NbBiQuadQuadrangles(self): + return self.mesh.NbBiQuadQuadrangles() + ## Returns the number of polygons in the mesh # @return an integer value # @ingroup l1_meshinfo @@ -1712,6 +1919,12 @@ class Mesh: def NbHexasOfOrder(self, elementOrder): return self.mesh.NbHexasOfOrder(elementOrder) + ## Returns the number of triquadratic hexahedrons in the mesh + # @return an integer value + # @ingroup l1_meshinfo + def NbTriQuadraticHexas(self): + return self.mesh.NbTriQuadraticHexas() + ## Returns the number of pyramids in the mesh # @return an integer value # @ingroup l1_meshinfo @@ -1740,6 +1953,12 @@ class Mesh: def NbPrismsOfOrder(self, elementOrder): return self.mesh.NbPrismsOfOrder(elementOrder) + ## Returns the number of hexagonal prisms in the mesh + # @return an integer value + # @ingroup l1_meshinfo + def NbHexagonalPrisms(self): + return self.mesh.NbHexagonalPrisms() + ## Returns the number of polyhedrons in the mesh # @return an integer value # @ingroup l1_meshinfo @@ -1759,7 +1978,7 @@ class Mesh: return self.mesh.GetElementsId() ## Returns the list of IDs of mesh elements with the given type - # @param elementType the required type of elements + # @param elementType the required type of elements (SMESH.NODE, SMESH.EDGE, SMESH.FACE or SMESH.VOLUME) # @return list of integer values # @ingroup l1_meshinfo def GetElementsByType(self, elementType): @@ -1780,9 +1999,15 @@ class Mesh: def GetElementType(self, id, iselem): return self.mesh.GetElementType(id, iselem) + ## Returns the geometric type of mesh element + # @return the value from SMESH::EntityType enumeration + # @ingroup l1_meshinfo + def GetElementGeomType(self, id): + return self.mesh.GetElementGeomType(id) + ## Returns the list of submesh elements IDs - # @param Shape a geom object(subshape) IOR - # Shape must be the subshape of a ShapeToMesh() + # @param Shape a geom object(sub-shape) IOR + # Shape must be the sub-shape of a ShapeToMesh() # @return the list of integer values # @ingroup l1_meshinfo def GetSubMeshElementsId(self, Shape): @@ -1793,22 +2018,22 @@ class Mesh: return self.mesh.GetSubMeshElementsId(ShapeID) ## Returns the list of submesh nodes IDs - # @param Shape a geom object(subshape) IOR - # Shape must be the subshape of a ShapeToMesh() + # @param Shape a geom object(sub-shape) IOR + # Shape must be the sub-shape of a ShapeToMesh() # @param all If true, gives all nodes of submesh elements, otherwise gives only submesh nodes # @return the list of integer values # @ingroup l1_meshinfo def GetSubMeshNodesId(self, Shape, all): if ( isinstance( Shape, geompyDC.GEOM._objref_GEOM_Object)): - ShapeID = Shape.GetSubShapeIndices()[0] + ShapeID = self.geompyD.GetSubShapeID( self.geom, Shape ) else: ShapeID = Shape return self.mesh.GetSubMeshNodesId(ShapeID, all) - ## Returns the list of IDs of submesh elements with the given type - # @param Shape a geom object(subshape) IOR - # Shape must be a subshape of a ShapeToMesh() - # @return the list of integer values + ## Returns type of elements on given shape + # @param Shape a geom object(sub-shape) IOR + # Shape must be a sub-shape of a ShapeToMesh() + # @return element type # @ingroup l1_meshinfo def GetSubMeshElementType(self, Shape): if ( isinstance( Shape, geompyDC.GEOM._objref_GEOM_Object)): @@ -1903,6 +2128,16 @@ class Mesh: def ElemNbFaces(self, id): return self.mesh.ElemNbFaces(id) + ## Returns nodes of given face (counted from zero) for given volumic element. + # @ingroup l1_meshinfo + def GetElemFaceNodes(self,elemId, faceIndex): + return self.mesh.GetElemFaceNodes(elemId, faceIndex) + + ## Returns an element based on all given nodes. + # @ingroup l1_meshinfo + def FindElementByNodes(self,nodes): + return self.mesh.FindElementByNodes(nodes) + ## Returns true if the given element is a polygon # @ingroup l1_meshinfo def IsPoly(self, id): @@ -1913,6 +2148,11 @@ class Mesh: def IsQuadratic(self, id): return self.mesh.IsQuadratic(id) + ## Returns diameter of a ball discrete element or zero in case of an invalid \a id + # @ingroup l1_meshinfo + def GetBallDiameter(self, id): + return self.mesh.GetBallDiameter(id) + ## Returns XYZ coordinates of the barycenter of the given element # \n If there is no element for the given ID - returns an empty list # @return a list of three double values @@ -1920,6 +2160,115 @@ class Mesh: def BaryCenter(self, id): return self.mesh.BaryCenter(id) + ## Passes mesh elements through the given filter and return IDs of fitting elements + # @param theFilter SMESH_Filter + # @return a list of ids + # @ingroup l1_controls + def GetIdsFromFilter(self, theFilter): + theFilter.SetMesh( self.mesh ) + return theFilter.GetIDs() + + ## Verifies whether a 2D mesh element has free edges (edges connected to one face only)\n + # Returns a list of special structures (borders). + # @return a list of SMESH.FreeEdges.Border structure: edge id and ids of two its nodes. + # @ingroup l1_controls + def GetFreeBorders(self): + aFilterMgr = self.smeshpyD.CreateFilterManager() + aPredicate = aFilterMgr.CreateFreeEdges() + aPredicate.SetMesh(self.mesh) + aBorders = aPredicate.GetBorders() + aFilterMgr.UnRegister() + return aBorders + + + # Get mesh measurements information: + # ------------------------------------ + + ## Get minimum distance between two nodes, elements or distance to the origin + # @param id1 first node/element id + # @param id2 second node/element id (if 0, distance from @a id1 to the origin is computed) + # @param isElem1 @c True if @a id1 is element id, @c False if it is node id + # @param isElem2 @c True if @a id2 is element id, @c False if it is node id + # @return minimum distance value + # @sa GetMinDistance() + def MinDistance(self, id1, id2=0, isElem1=False, isElem2=False): + aMeasure = self.GetMinDistance(id1, id2, isElem1, isElem2) + return aMeasure.value + + ## Get measure structure specifying minimum distance data between two objects + # @param id1 first node/element id + # @param id2 second node/element id (if 0, distance from @a id1 to the origin is computed) + # @param isElem1 @c True if @a id1 is element id, @c False if it is node id + # @param isElem2 @c True if @a id2 is element id, @c False if it is node id + # @return Measure structure + # @sa MinDistance() + def GetMinDistance(self, id1, id2=0, isElem1=False, isElem2=False): + if isElem1: + id1 = self.editor.MakeIDSource([id1], SMESH.FACE) + else: + id1 = self.editor.MakeIDSource([id1], SMESH.NODE) + if id2 != 0: + if isElem2: + id2 = self.editor.MakeIDSource([id2], SMESH.FACE) + else: + id2 = self.editor.MakeIDSource([id2], SMESH.NODE) + pass + else: + id2 = None + + aMeasurements = self.smeshpyD.CreateMeasurements() + aMeasure = aMeasurements.MinDistance(id1, id2) + aMeasurements.UnRegister() + return aMeasure + + ## Get bounding box of the specified object(s) + # @param objects single source object or list of source objects or list of nodes/elements IDs + # @param isElem if @a objects is a list of IDs, @c True value in this parameters specifies that @a objects are elements, + # @c False specifies that @a objects are nodes + # @return tuple of six values (minX, minY, minZ, maxX, maxY, maxZ) + # @sa GetBoundingBox() + def BoundingBox(self, objects=None, isElem=False): + result = self.GetBoundingBox(objects, isElem) + if result is None: + result = (0.0,)*6 + else: + result = (result.minX, result.minY, result.minZ, result.maxX, result.maxY, result.maxZ) + return result + + ## Get measure structure specifying bounding box data of the specified object(s) + # @param IDs single source object or list of source objects or list of nodes/elements IDs + # @param isElem if @a objects is a list of IDs, @c True value in this parameters specifies that @a objects are elements, + # @c False specifies that @a objects are nodes + # @return Measure structure + # @sa BoundingBox() + def GetBoundingBox(self, IDs=None, isElem=False): + if IDs is None: + IDs = [self.mesh] + elif isinstance(IDs, tuple): + IDs = list(IDs) + if not isinstance(IDs, list): + IDs = [IDs] + if len(IDs) > 0 and isinstance(IDs[0], int): + IDs = [IDs] + srclist = [] + for o in IDs: + if isinstance(o, Mesh): + srclist.append(o.mesh) + elif hasattr(o, "_narrow"): + src = o._narrow(SMESH.SMESH_IDSource) + if src: srclist.append(src) + pass + elif isinstance(o, list): + if isElem: + srclist.append(self.editor.MakeIDSource(o, SMESH.FACE)) + else: + srclist.append(self.editor.MakeIDSource(o, SMESH.NODE)) + pass + pass + aMeasurements = self.smeshpyD.CreateMeasurements() + aMeasure = aMeasurements.BoundingBox(srclist) + aMeasurements.UnRegister() + return aMeasure # Mesh edition (SMESH_MeshEditor functionality): # --------------------------------------------- @@ -1938,20 +2287,41 @@ class Mesh: def RemoveNodes(self, IDsOfNodes): return self.editor.RemoveNodes(IDsOfNodes) + ## Removes all orphan (free) nodes from mesh + # @return number of the removed nodes + # @ingroup l2_modif_del + def RemoveOrphanNodes(self): + return self.editor.RemoveOrphanNodes() + ## Add a node to the mesh by coordinates # @return Id of the new node # @ingroup l2_modif_add def AddNode(self, x, y, z): - x,y,z,Parameters = geompyDC.ParseParameters(x,y,z) - self.mesh.SetParameters(Parameters) + x,y,z,Parameters,hasVars = ParseParameters(x,y,z) + if hasVars: self.mesh.SetParameters(Parameters) return self.editor.AddNode( x, y, z) + ## Creates a 0D element on a node with given number. + # @param IDOfNode the ID of node for creation of the element. + # @return the Id of the new 0D element + # @ingroup l2_modif_add + def Add0DElement(self, IDOfNode): + return self.editor.Add0DElement(IDOfNode) + + ## Creates a ball element on a node with given ID. + # @param IDOfNode the ID of node for creation of the element. + # @param diameter the bal diameter. + # @return the Id of the new ball element + # @ingroup l2_modif_add + def AddBall(self, IDOfNode, diameter): + return self.editor.AddBall( IDOfNode, diameter ) + ## Creates a linear or quadratic edge (this is determined # by the number of given nodes). # @param IDsOfNodes the list of node IDs for creation of the element. # The order of nodes in this list should correspond to the description # of MED. \n This description is located by the following link: - # http://www.salome-platform.org/salome2/web_med_internet/logiciels/medV2.2.2_doc_html/html/modele_de_donnees.html#3. + # http://www.code-aster.org/outils/med/html/modele_de_donnees.html#3. # @return the Id of the new edge # @ingroup l2_modif_add def AddEdge(self, IDsOfNodes): @@ -1962,7 +2332,7 @@ class Mesh: # @param IDsOfNodes the list of node IDs for creation of the element. # The order of nodes in this list should correspond to the description # of MED. \n This description is located by the following link: - # http://www.salome-platform.org/salome2/web_med_internet/logiciels/medV2.2.2_doc_html/html/modele_de_donnees.html#3. + # http://www.code-aster.org/outils/med/html/modele_de_donnees.html#3. # @return the Id of the new face # @ingroup l2_modif_add def AddFace(self, IDsOfNodes): @@ -1980,7 +2350,7 @@ class Mesh: # @param IDsOfNodes the list of node IDs for creation of the element. # The order of nodes in this list should correspond to the description # of MED. \n This description is located by the following link: - # http://www.salome-platform.org/salome2/web_med_internet/logiciels/medV2.2.2_doc_html/html/modele_de_donnees.html#3. + # http://www.code-aster.org/outils/med/html/modele_de_donnees.html#3. # @return the Id of the new volumic element # @ingroup l2_modif_add def AddVolume(self, IDsOfNodes): @@ -2099,19 +2469,21 @@ class Mesh: # @return True if succeed else False # @ingroup l2_modif_movenode def MoveNode(self, NodeID, x, y, z): - x,y,z,Parameters = geompyDC.ParseParameters(x,y,z) - self.mesh.SetParameters(Parameters) + x,y,z,Parameters,hasVars = ParseParameters(x,y,z) + if hasVars: self.mesh.SetParameters(Parameters) return self.editor.MoveNode(NodeID, x, y, z) ## Finds the node closest to a point and moves it to a point location # @param x the X coordinate of a point # @param y the Y coordinate of a point # @param z the Z coordinate of a point + # @param NodeID if specified (>0), the node with this ID is moved, + # otherwise, the node closest to point (@a x,@a y,@a z) is moved # @return the ID of a node # @ingroup l2_modif_throughp def MoveClosestNodeToPoint(self, x, y, z, NodeID): - x,y,z,Parameters = geompyDC.ParseParameters(x,y,z) - self.mesh.SetParameters(Parameters) + x,y,z,Parameters,hasVars = ParseParameters(x,y,z) + if hasVars: self.mesh.SetParameters(Parameters) return self.editor.MoveClosestNodeToPoint(x, y, z, NodeID) ## Finds the node closest to a point @@ -2121,8 +2493,31 @@ class Mesh: # @return the ID of a node # @ingroup l2_modif_throughp def FindNodeClosestTo(self, x, y, z): - preview = self.mesh.GetMeshEditPreviewer() - return preview.MoveClosestNodeToPoint(x, y, z, -1) + #preview = self.mesh.GetMeshEditPreviewer() + #return preview.MoveClosestNodeToPoint(x, y, z, -1) + return self.editor.FindNodeClosestTo(x, y, z) + + ## Finds the elements where a point lays IN or ON + # @param x the X coordinate of a point + # @param y the Y coordinate of a point + # @param z the Z coordinate of a point + # @param elementType type of elements to find (SMESH.ALL type + # means elements of any type excluding nodes, discrete and 0D elements) + # @param meshPart a part of mesh (group, sub-mesh) to search within + # @return list of IDs of found elements + # @ingroup l2_modif_throughp + def FindElementsByPoint(self, x, y, z, elementType = SMESH.ALL, meshPart=None): + if meshPart: + return self.editor.FindAmongElementsByPoint( meshPart, x, y, z, elementType ); + else: + return self.editor.FindElementsByPoint(x, y, z, elementType) + + # Return point state in a closed 2D mesh in terms of TopAbs_State enumeration: + # 0-IN, 1-OUT, 2-ON, 3-UNKNOWN + # TopAbs_UNKNOWN state means that either mesh is wrong or the analysis fails. + + def GetPointState(self, x, y, z): + return self.editor.GetPointState(x, y, z) ## Finds the node closest to a point and moves it to a point location # @param x the X coordinate of a point @@ -2169,6 +2564,41 @@ class Mesh: theObject = theObject.GetMesh() return self.editor.ReorientObject(theObject) + ## Reorient faces contained in \a the2DObject. + # @param the2DObject is a mesh, sub-mesh, group or list of IDs of 2D elements + # @param theDirection is a desired direction of normal of \a theFace. + # It can be either a GEOM vector or a list of coordinates [x,y,z]. + # @param theFaceOrPoint defines a face of \a the2DObject whose normal will be + # compared with theDirection. It can be either ID of face or a point + # by which the face will be found. The point can be given as either + # a GEOM vertex or a list of point coordinates. + # @return number of reoriented faces + # @ingroup l2_modif_changori + def Reorient2D(self, the2DObject, theDirection, theFaceOrPoint ): + # check the2DObject + if isinstance( the2DObject, Mesh ): + the2DObject = the2DObject.GetMesh() + if isinstance( the2DObject, list ): + the2DObject = self.GetIDSource( the2DObject, SMESH.FACE ) + # check theDirection + if isinstance( theDirection, geompyDC.GEOM._objref_GEOM_Object): + theDirection = self.smeshpyD.GetDirStruct( theDirection ) + if isinstance( theDirection, list ): + theDirection = self.smeshpyD.MakeDirStruct( *theDirection ) + # prepare theFace and thePoint + theFace = theFaceOrPoint + thePoint = PointStruct(0,0,0) + if isinstance( theFaceOrPoint, geompyDC.GEOM._objref_GEOM_Object): + thePoint = self.smeshpyD.GetPointStruct( theFaceOrPoint ) + theFace = -1 + if isinstance( theFaceOrPoint, list ): + thePoint = PointStruct( *theFaceOrPoint ) + theFace = -1 + if isinstance( theFaceOrPoint, PointStruct ): + thePoint = theFaceOrPoint + theFace = -1 + return self.editor.Reorient2D( the2DObject, theDirection, theFace, thePoint ) + ## Fuses the neighbouring triangles into quadrangles. # @param IDsOfElements The triangles to be fused, # @param theCriterion is FT_...; used to choose a neighbour to fuse with. @@ -2181,14 +2611,12 @@ class Mesh: flag = False if isinstance(MaxAngle,str): flag = True - MaxAngle,Parameters = geompyDC.ParseParameters(MaxAngle) - if flag: - MaxAngle = DegreesToRadians(MaxAngle) - if IDsOfElements == []: - IDsOfElements = self.GetElementsId() + MaxAngle,Parameters,hasVars = ParseAngles(MaxAngle) self.mesh.SetParameters(Parameters) + if not IDsOfElements: + IDsOfElements = self.GetElementsId() Functor = 0 - if ( isinstance( theCriterion, SMESH._objref_NumericalFunctor ) ): + if ( isinstance( theCriterion, SMESH._objref_NumericalFunctor ) ): Functor = theCriterion else: Functor = self.smeshpyD.GetFunctor(theCriterion) @@ -2202,6 +2630,8 @@ class Mesh: # @return TRUE in case of success, FALSE otherwise. # @ingroup l2_modif_unitetri def TriToQuadObject (self, theObject, theCriterion, MaxAngle): + MaxAngle,Parameters,hasVars = ParseAngles(MaxAngle) + self.mesh.SetParameters(Parameters) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() return self.editor.TriToQuadObject(theObject, self.smeshpyD.GetFunctor(theCriterion), MaxAngle) @@ -2255,6 +2685,18 @@ class Mesh: def BestSplit (self, IDOfQuad, theCriterion): return self.editor.BestSplit(IDOfQuad, self.smeshpyD.GetFunctor(theCriterion)) + ## Splits volumic elements into tetrahedrons + # @param elemIDs either list of elements or mesh or group or submesh + # @param method flags passing splitting method: Hex_5Tet, Hex_6Tet, Hex_24Tet + # Hex_5Tet - split the hexahedron into 5 tetrahedrons, etc + # @ingroup l2_modif_cutquadr + def SplitVolumesIntoTetra(self, elemIDs, method=Hex_5Tet ): + if isinstance( elemIDs, Mesh ): + elemIDs = elemIDs.GetMesh() + if ( isinstance( elemIDs, list )): + elemIDs = self.editor.MakeIDSource(elemIDs, SMESH.VOLUME) + self.editor.SplitVolumesIntoTetra(elemIDs, method) + ## Splits quadrangle faces near triangular facets of volumes # # @ingroup l1_auxiliary @@ -2410,7 +2852,7 @@ class Mesh: MaxNbOfIterations, MaxAspectRatio, Method): if IDsOfElements == []: IDsOfElements = self.GetElementsId() - MaxNbOfIterations,MaxAspectRatio,Parameters = geompyDC.ParseParameters(MaxNbOfIterations,MaxAspectRatio) + MaxNbOfIterations,MaxAspectRatio,Parameters,hasVars = ParseParameters(MaxNbOfIterations,MaxAspectRatio) self.mesh.SetParameters(Parameters) return self.editor.Smooth(IDsOfElements, IDsOfFixedNodes, MaxNbOfIterations, MaxAspectRatio, Method) @@ -2444,7 +2886,7 @@ class Mesh: MaxNbOfIterations, MaxAspectRatio, Method): if IDsOfElements == []: IDsOfElements = self.GetElementsId() - MaxNbOfIterations,MaxAspectRatio,Parameters = geompyDC.ParseParameters(MaxNbOfIterations,MaxAspectRatio) + MaxNbOfIterations,MaxAspectRatio,Parameters,hasVars = ParseParameters(MaxNbOfIterations,MaxAspectRatio) self.mesh.SetParameters(Parameters) return self.editor.SmoothParametric(IDsOfElements, IDsOfFixedNodes, MaxNbOfIterations, MaxAspectRatio, Method) @@ -2467,17 +2909,86 @@ class Mesh: ## Converts the mesh to quadratic, deletes old elements, replacing # them with quadratic with the same id. + # @param theForce3d new node creation method: + # 0 - the medium node lies at the geometrical entity from which the mesh element is built + # 1 - the medium node lies at the middle of the line segments connecting start and end node of a mesh element + # @param theSubMesh a group or a sub-mesh to convert; WARNING: in this case the mesh can become not conformal # @ingroup l2_modif_tofromqu - def ConvertToQuadratic(self, theForce3d): - self.editor.ConvertToQuadratic(theForce3d) + def ConvertToQuadratic(self, theForce3d, theSubMesh=None): + if theSubMesh: + self.editor.ConvertToQuadraticObject(theForce3d,theSubMesh) + else: + self.editor.ConvertToQuadratic(theForce3d) ## Converts the mesh from quadratic to ordinary, # deletes old quadratic elements, \n replacing # them with ordinary mesh elements with the same id. - # @return TRUE in case of success, FALSE otherwise. + # @param theSubMesh a group or a sub-mesh to convert; WARNING: in this case the mesh can become not conformal # @ingroup l2_modif_tofromqu - def ConvertFromQuadratic(self): - return self.editor.ConvertFromQuadratic() + def ConvertFromQuadratic(self, theSubMesh=None): + if theSubMesh: + self.editor.ConvertFromQuadraticObject(theSubMesh) + else: + return self.editor.ConvertFromQuadratic() + + ## Creates 2D mesh as skin on boundary faces of a 3D mesh + # @return TRUE if operation has been completed successfully, FALSE otherwise + # @ingroup l2_modif_edit + def Make2DMeshFrom3D(self): + return self.editor. Make2DMeshFrom3D() + + ## Creates missing boundary elements + # @param elements - elements whose boundary is to be checked: + # mesh, group, sub-mesh or list of elements + # if elements is mesh, it must be the mesh whose MakeBoundaryMesh() is called + # @param dimension - defines type of boundary elements to create: + # SMESH.BND_2DFROM3D, SMESH.BND_1DFROM3D, SMESH.BND_1DFROM2D + # SMESH.BND_1DFROM3D creates mesh edges on all borders of free facets of 3D cells + # @param groupName - a name of group to store created boundary elements in, + # "" means not to create the group + # @param meshName - a name of new mesh to store created boundary elements in, + # "" means not to create the new mesh + # @param toCopyElements - if true, the checked elements will be copied into + # the new mesh else only boundary elements will be copied into the new mesh + # @param toCopyExistingBondary - if true, not only new but also pre-existing + # boundary elements will be copied into the new mesh + # @return tuple (mesh, group) where bondary elements were added to + # @ingroup l2_modif_edit + def MakeBoundaryMesh(self, elements, dimension=SMESH.BND_2DFROM3D, groupName="", meshName="", + toCopyElements=False, toCopyExistingBondary=False): + if isinstance( elements, Mesh ): + elements = elements.GetMesh() + if ( isinstance( elements, list )): + elemType = SMESH.ALL + if elements: elemType = self.GetElementType( elements[0], iselem=True) + elements = self.editor.MakeIDSource(elements, elemType) + mesh, group = self.editor.MakeBoundaryMesh(elements,dimension,groupName,meshName, + toCopyElements,toCopyExistingBondary) + if mesh: mesh = self.smeshpyD.Mesh(mesh) + return mesh, group + + ## + # @brief Creates missing boundary elements around either the whole mesh or + # groups of 2D elements + # @param dimension - defines type of boundary elements to create + # @param groupName - a name of group to store all boundary elements in, + # "" means not to create the group + # @param meshName - a name of a new mesh, which is a copy of the initial + # mesh + created boundary elements; "" means not to create the new mesh + # @param toCopyAll - if true, the whole initial mesh will be copied into + # the new mesh else only boundary elements will be copied into the new mesh + # @param groups - groups of 2D elements to make boundary around + # @retval tuple( long, mesh, groups ) + # long - number of added boundary elements + # mesh - the mesh where elements were added to + # group - the group of boundary elements or None + # + def MakeBoundaryElements(self, dimension=SMESH.BND_2DFROM3D, groupName="", meshName="", + toCopyAll=False, groups=[]): + nb, mesh, group = self.editor.MakeBoundaryElements(dimension,groupName,meshName, + toCopyAll,groups) + if mesh: mesh = self.smeshpyD.Mesh(mesh) + return nb, mesh, group ## Renumber mesh nodes # @ingroup l2_modif_renumber @@ -2502,22 +3013,16 @@ class Mesh: # @ingroup l2_modif_extrurev def RotationSweep(self, IDsOfElements, Axis, AngleInRadians, NbOfSteps, Tolerance, MakeGroups=False, TotalAngle=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,AngleParameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if IDsOfElements == []: IDsOfElements = self.GetElementsId() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) + AngleInRadians,AngleParameters,hasVars = ParseAngles(AngleInRadians) + NbOfSteps,Tolerance,Parameters,hasVars = ParseParameters(NbOfSteps,Tolerance) + Parameters = Axis.parameters + var_separator + AngleParameters + var_separator + Parameters + self.mesh.SetParameters(Parameters) if TotalAngle and NbOfSteps: AngleInRadians /= NbOfSteps - NbOfSteps,Tolerance,Parameters = geompyDC.ParseParameters(NbOfSteps,Tolerance) - Parameters = AxisParameters + var_separator + AngleParameters + var_separator + Parameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.RotationSweepMakeGroups(IDsOfElements, Axis, AngleInRadians, NbOfSteps, Tolerance) @@ -2525,7 +3030,8 @@ class Mesh: return [] ## Generates new elements by rotation of the elements of object around the axis - # @param theObject object which elements should be sweeped + # @param theObject object which elements should be sweeped. + # It can be a mesh, a sub mesh or a group. # @param Axis the axis of rotation, AxisStruct or line(geom object) # @param AngleInRadians the angle of Rotation # @param NbOfSteps number of steps @@ -2537,22 +3043,16 @@ class Mesh: # @ingroup l2_modif_extrurev def RotationSweepObject(self, theObject, Axis, AngleInRadians, NbOfSteps, Tolerance, MakeGroups=False, TotalAngle=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,AngleParameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) + AngleInRadians,AngleParameters,hasVars = ParseAngles(AngleInRadians) + NbOfSteps,Tolerance,Parameters,hasVars = ParseParameters(NbOfSteps,Tolerance) + Parameters = Axis.parameters + var_separator + AngleParameters + var_separator + Parameters + self.mesh.SetParameters(Parameters) if TotalAngle and NbOfSteps: AngleInRadians /= NbOfSteps - NbOfSteps,Tolerance,Parameters = geompyDC.ParseParameters(NbOfSteps,Tolerance) - Parameters = AxisParameters + var_separator + AngleParameters + var_separator + Parameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.RotationSweepObjectMakeGroups(theObject, Axis, AngleInRadians, NbOfSteps, Tolerance) @@ -2560,7 +3060,8 @@ class Mesh: return [] ## Generates new elements by rotation of the elements of object around the axis - # @param theObject object which elements should be sweeped + # @param theObject object which elements should be sweeped. + # It can be a mesh, a sub mesh or a group. # @param Axis the axis of rotation, AxisStruct or line(geom object) # @param AngleInRadians the angle of Rotation # @param NbOfSteps number of steps @@ -2572,22 +3073,16 @@ class Mesh: # @ingroup l2_modif_extrurev def RotationSweepObject1D(self, theObject, Axis, AngleInRadians, NbOfSteps, Tolerance, MakeGroups=False, TotalAngle=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,AngleParameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) + AngleInRadians,AngleParameters,hasVars = ParseAngles(AngleInRadians) + NbOfSteps,Tolerance,Parameters,hasVars = ParseParameters(NbOfSteps,Tolerance) + Parameters = Axis.parameters + var_separator + AngleParameters + var_separator + Parameters + self.mesh.SetParameters(Parameters) if TotalAngle and NbOfSteps: AngleInRadians /= NbOfSteps - NbOfSteps,Tolerance,Parameters = geompyDC.ParseParameters(NbOfSteps,Tolerance) - Parameters = AxisParameters + var_separator + AngleParameters + var_separator + Parameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.RotationSweepObject1DMakeGroups(theObject, Axis, AngleInRadians, NbOfSteps, Tolerance) @@ -2595,7 +3090,8 @@ class Mesh: return [] ## Generates new elements by rotation of the elements of object around the axis - # @param theObject object which elements should be sweeped + # @param theObject object which elements should be sweeped. + # It can be a mesh, a sub mesh or a group. # @param Axis the axis of rotation, AxisStruct or line(geom object) # @param AngleInRadians the angle of Rotation # @param NbOfSteps number of steps @@ -2607,22 +3103,16 @@ class Mesh: # @ingroup l2_modif_extrurev def RotationSweepObject2D(self, theObject, Axis, AngleInRadians, NbOfSteps, Tolerance, MakeGroups=False, TotalAngle=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,AngleParameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) + AngleInRadians,AngleParameters,hasVars = ParseAngles(AngleInRadians) + NbOfSteps,Tolerance,Parameters,hasVars = ParseParameters(NbOfSteps,Tolerance) + Parameters = Axis.parameters + var_separator + AngleParameters + var_separator + Parameters + self.mesh.SetParameters(Parameters) if TotalAngle and NbOfSteps: AngleInRadians /= NbOfSteps - NbOfSteps,Tolerance,Parameters = geompyDC.ParseParameters(NbOfSteps,Tolerance) - Parameters = AxisParameters + var_separator + AngleParameters + var_separator + Parameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.RotationSweepObject2DMakeGroups(theObject, Axis, AngleInRadians, NbOfSteps, Tolerance) @@ -2631,23 +3121,29 @@ class Mesh: ## Generates new elements by extrusion of the elements with given ids # @param IDsOfElements the list of elements ids for extrusion - # @param StepVector vector, defining the direction and value of extrusion + # @param StepVector vector or DirStruct, defining the direction and value of extrusion for one step (the total extrusion length will be NbOfSteps * ||StepVector||) # @param NbOfSteps the number of steps # @param MakeGroups forces the generation of new groups from existing ones + # @param IsNodes is True if elements with given ids are nodes # @return the list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise # @ingroup l2_modif_extrurev - def ExtrusionSweep(self, IDsOfElements, StepVector, NbOfSteps, MakeGroups=False): + def ExtrusionSweep(self, IDsOfElements, StepVector, NbOfSteps, MakeGroups=False, IsNodes = False): if IDsOfElements == []: IDsOfElements = self.GetElementsId() if ( isinstance( StepVector, geompyDC.GEOM._objref_GEOM_Object)): StepVector = self.smeshpyD.GetDirStruct(StepVector) - StepVector,StepVectorParameters = ParseDirStruct(StepVector) - NbOfSteps,Parameters = geompyDC.ParseParameters(NbOfSteps) - Parameters = StepVectorParameters + var_separator + Parameters + NbOfSteps,Parameters,hasVars = ParseParameters(NbOfSteps) + Parameters = StepVector.PS.parameters + var_separator + Parameters self.mesh.SetParameters(Parameters) if MakeGroups: - return self.editor.ExtrusionSweepMakeGroups(IDsOfElements, StepVector, NbOfSteps) - self.editor.ExtrusionSweep(IDsOfElements, StepVector, NbOfSteps) + if(IsNodes): + return self.editor.ExtrusionSweepMakeGroups0D(IDsOfElements, StepVector, NbOfSteps) + else: + return self.editor.ExtrusionSweepMakeGroups(IDsOfElements, StepVector, NbOfSteps) + if(IsNodes): + self.editor.ExtrusionSweep0D(IDsOfElements, StepVector, NbOfSteps) + else: + self.editor.ExtrusionSweep(IDsOfElements, StepVector, NbOfSteps) return [] ## Generates new elements by extrusion of the elements with given ids @@ -2672,29 +3168,37 @@ class Mesh: return [] ## Generates new elements by extrusion of the elements which belong to the object - # @param theObject the object which elements should be processed - # @param StepVector vector, defining the direction and value of extrusion + # @param theObject the object which elements should be processed. + # It can be a mesh, a sub mesh or a group. + # @param StepVector vector, defining the direction and value of extrusion for one step (the total extrusion length will be NbOfSteps * ||StepVector||) # @param NbOfSteps the number of steps # @param MakeGroups forces the generation of new groups from existing ones + # @param IsNodes is True if elements which belong to the object are nodes # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise # @ingroup l2_modif_extrurev - def ExtrusionSweepObject(self, theObject, StepVector, NbOfSteps, MakeGroups=False): + def ExtrusionSweepObject(self, theObject, StepVector, NbOfSteps, MakeGroups=False, IsNodes=False): if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( StepVector, geompyDC.GEOM._objref_GEOM_Object)): StepVector = self.smeshpyD.GetDirStruct(StepVector) - StepVector,StepVectorParameters = ParseDirStruct(StepVector) - NbOfSteps,Parameters = geompyDC.ParseParameters(NbOfSteps) - Parameters = StepVectorParameters + var_separator + Parameters + NbOfSteps,Parameters,hasVars = ParseParameters(NbOfSteps) + Parameters = StepVector.PS.parameters + var_separator + Parameters self.mesh.SetParameters(Parameters) if MakeGroups: - return self.editor.ExtrusionSweepObjectMakeGroups(theObject, StepVector, NbOfSteps) - self.editor.ExtrusionSweepObject(theObject, StepVector, NbOfSteps) + if(IsNodes): + return self.editor.ExtrusionSweepObject0DMakeGroups(theObject, StepVector, NbOfSteps) + else: + return self.editor.ExtrusionSweepObjectMakeGroups(theObject, StepVector, NbOfSteps) + if(IsNodes): + self.editor.ExtrusionSweepObject0D(theObject, StepVector, NbOfSteps) + else: + self.editor.ExtrusionSweepObject(theObject, StepVector, NbOfSteps) return [] ## Generates new elements by extrusion of the elements which belong to the object - # @param theObject object which elements should be processed - # @param StepVector vector, defining the direction and value of extrusion + # @param theObject object which elements should be processed. + # It can be a mesh, a sub mesh or a group. + # @param StepVector vector, defining the direction and value of extrusion for one step (the total extrusion length will be NbOfSteps * ||StepVector||) # @param NbOfSteps the number of steps # @param MakeGroups to generate new groups from existing ones # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise @@ -2704,9 +3208,8 @@ class Mesh: theObject = theObject.GetMesh() if ( isinstance( StepVector, geompyDC.GEOM._objref_GEOM_Object)): StepVector = self.smeshpyD.GetDirStruct(StepVector) - StepVector,StepVectorParameters = ParseDirStruct(StepVector) - NbOfSteps,Parameters = geompyDC.ParseParameters(NbOfSteps) - Parameters = StepVectorParameters + var_separator + Parameters + NbOfSteps,Parameters,hasVars = ParseParameters(NbOfSteps) + Parameters = StepVector.PS.parameters + var_separator + Parameters self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionSweepObject1DMakeGroups(theObject, StepVector, NbOfSteps) @@ -2714,8 +3217,9 @@ class Mesh: return [] ## Generates new elements by extrusion of the elements which belong to the object - # @param theObject object which elements should be processed - # @param StepVector vector, defining the direction and value of extrusion + # @param theObject object which elements should be processed. + # It can be a mesh, a sub mesh or a group. + # @param StepVector vector, defining the direction and value of extrusion for one step (the total extrusion length will be NbOfSteps * ||StepVector||) # @param NbOfSteps the number of steps # @param MakeGroups forces the generation of new groups from existing ones # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise @@ -2725,15 +3229,63 @@ class Mesh: theObject = theObject.GetMesh() if ( isinstance( StepVector, geompyDC.GEOM._objref_GEOM_Object)): StepVector = self.smeshpyD.GetDirStruct(StepVector) - StepVector,StepVectorParameters = ParseDirStruct(StepVector) - NbOfSteps,Parameters = geompyDC.ParseParameters(NbOfSteps) - Parameters = StepVectorParameters + var_separator + Parameters + NbOfSteps,Parameters,hasVars = ParseParameters(NbOfSteps) + Parameters = StepVector.PS.parameters + var_separator + Parameters self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionSweepObject2DMakeGroups(theObject, StepVector, NbOfSteps) self.editor.ExtrusionSweepObject2D(theObject, StepVector, NbOfSteps) return [] + + + ## Generates new elements by extrusion of the given elements + # The path of extrusion must be a meshed edge. + # @param Base mesh or group, or submesh, or list of ids of elements for extrusion + # @param Path - 1D mesh or 1D sub-mesh, along which proceeds the extrusion + # @param NodeStart the start node from Path. Defines the direction of extrusion + # @param HasAngles allows the shape to be rotated around the path + # to get the resulting mesh in a helical fashion + # @param Angles list of angles in radians + # @param LinearVariation forces the computation of rotation angles as linear + # variation of the given Angles along path steps + # @param HasRefPoint allows using the reference point + # @param RefPoint the point around which the shape is rotated (the mass center of the shape by default). + # The User can specify any point as the Reference Point. + # @param MakeGroups forces the generation of new groups from existing ones + # @param ElemType type of elements for extrusion (if param Base is a mesh) + # @return list of created groups (SMESH_GroupBase) and SMESH::Extrusion_Error if MakeGroups=True, + # only SMESH::Extrusion_Error otherwise + # @ingroup l2_modif_extrurev + def ExtrusionAlongPathX(self, Base, Path, NodeStart, + HasAngles, Angles, LinearVariation, + HasRefPoint, RefPoint, MakeGroups, ElemType): + if ( isinstance( RefPoint, geompyDC.GEOM._objref_GEOM_Object)): + RefPoint = self.smeshpyD.GetPointStruct(RefPoint) + pass + Angles,AnglesParameters,hasVars = ParseAngles(Angles) + Parameters = AnglesParameters + var_separator + RefPoint.parameters + self.mesh.SetParameters(Parameters) + + if (isinstance(Path, Mesh)): Path = Path.GetMesh() + + if isinstance(Base, list): + IDsOfElements = [] + if Base == []: IDsOfElements = self.GetElementsId() + else: IDsOfElements = Base + return self.editor.ExtrusionAlongPathX(IDsOfElements, Path, NodeStart, + HasAngles, Angles, LinearVariation, + HasRefPoint, RefPoint, MakeGroups, ElemType) + else: + if isinstance(Base, Mesh): Base = Base.GetMesh() + if isinstance(Base, SMESH._objref_SMESH_Mesh) or isinstance(Base, SMESH._objref_SMESH_Group) or isinstance(Base, SMESH._objref_SMESH_subMesh): + return self.editor.ExtrusionAlongPathObjX(Base, Path, NodeStart, + HasAngles, Angles, LinearVariation, + HasRefPoint, RefPoint, MakeGroups, ElemType) + else: + raise RuntimeError, "Invalid Base for ExtrusionAlongPathX" + + ## Generates new elements by extrusion of the given elements # The path of extrusion must be a meshed edge. # @param IDsOfElements ids of elements @@ -2742,7 +3294,7 @@ class Mesh: # @param NodeStart the first or the last node on the edge. Defines the direction of extrusion # @param HasAngles allows the shape to be rotated around the path # to get the resulting mesh in a helical fashion - # @param Angles list of angles + # @param Angles list of angles in radians # @param HasRefPoint allows using the reference point # @param RefPoint the point around which the shape is rotated (the mass center of the shape by default). # The User can specify any point as the Reference Point. @@ -2755,8 +3307,6 @@ class Mesh: def ExtrusionAlongPath(self, IDsOfElements, PathMesh, PathShape, NodeStart, HasAngles, Angles, HasRefPoint, RefPoint, MakeGroups=False, LinearVariation=False): - Angles,AnglesParameters = ParseAngles(Angles) - RefPoint,RefPointParameters = ParsePointStruct(RefPoint) if IDsOfElements == []: IDsOfElements = self.GetElementsId() if ( isinstance( RefPoint, geompyDC.GEOM._objref_GEOM_Object)): @@ -2764,11 +3314,12 @@ class Mesh: pass if ( isinstance( PathMesh, Mesh )): PathMesh = PathMesh.GetMesh() + Angles,AnglesParameters,hasVars = ParseAngles(Angles) + Parameters = AnglesParameters + var_separator + RefPoint.parameters + self.mesh.SetParameters(Parameters) if HasAngles and Angles and LinearVariation: Angles = self.editor.LinearAnglesVariation( PathMesh, PathShape, Angles ) pass - Parameters = AnglesParameters + var_separator + RefPointParameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionAlongPathMakeGroups(IDsOfElements, PathMesh, PathShape, NodeStart, HasAngles, @@ -2778,7 +3329,8 @@ class Mesh: ## Generates new elements by extrusion of the elements which belong to the object # The path of extrusion must be a meshed edge. - # @param theObject the object which elements should be processed + # @param theObject the object which elements should be processed. + # It can be a mesh, a sub mesh or a group. # @param PathMesh mesh containing a 1D sub-mesh on the edge, along which the extrusion proceeds # @param PathShape shape(edge) defines the sub-mesh for the path # @param NodeStart the first or the last node on the edge. Defines the direction of extrusion @@ -2797,19 +3349,18 @@ class Mesh: def ExtrusionAlongPathObject(self, theObject, PathMesh, PathShape, NodeStart, HasAngles, Angles, HasRefPoint, RefPoint, MakeGroups=False, LinearVariation=False): - Angles,AnglesParameters = ParseAngles(Angles) - RefPoint,RefPointParameters = ParsePointStruct(RefPoint) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( RefPoint, geompyDC.GEOM._objref_GEOM_Object)): RefPoint = self.smeshpyD.GetPointStruct(RefPoint) if ( isinstance( PathMesh, Mesh )): PathMesh = PathMesh.GetMesh() + Angles,AnglesParameters,hasVars = ParseAngles(Angles) + Parameters = AnglesParameters + var_separator + RefPoint.parameters + self.mesh.SetParameters(Parameters) if HasAngles and Angles and LinearVariation: Angles = self.editor.LinearAnglesVariation( PathMesh, PathShape, Angles ) pass - Parameters = AnglesParameters + var_separator + RefPointParameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionAlongPathObjectMakeGroups(theObject, PathMesh, PathShape, NodeStart, HasAngles, @@ -2820,7 +3371,8 @@ class Mesh: ## Generates new elements by extrusion of the elements which belong to the object # The path of extrusion must be a meshed edge. - # @param theObject the object which elements should be processed + # @param theObject the object which elements should be processed. + # It can be a mesh, a sub mesh or a group. # @param PathMesh mesh containing a 1D sub-mesh on the edge, along which the extrusion proceeds # @param PathShape shape(edge) defines the sub-mesh for the path # @param NodeStart the first or the last node on the edge. Defines the direction of extrusion @@ -2839,19 +3391,18 @@ class Mesh: def ExtrusionAlongPathObject1D(self, theObject, PathMesh, PathShape, NodeStart, HasAngles, Angles, HasRefPoint, RefPoint, MakeGroups=False, LinearVariation=False): - Angles,AnglesParameters = ParseAngles(Angles) - RefPoint,RefPointParameters = ParsePointStruct(RefPoint) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( RefPoint, geompyDC.GEOM._objref_GEOM_Object)): RefPoint = self.smeshpyD.GetPointStruct(RefPoint) if ( isinstance( PathMesh, Mesh )): PathMesh = PathMesh.GetMesh() + Angles,AnglesParameters,hasVars = ParseAngles(Angles) + Parameters = AnglesParameters + var_separator + RefPoint.parameters + self.mesh.SetParameters(Parameters) if HasAngles and Angles and LinearVariation: Angles = self.editor.LinearAnglesVariation( PathMesh, PathShape, Angles ) pass - Parameters = AnglesParameters + var_separator + RefPointParameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionAlongPathObject1DMakeGroups(theObject, PathMesh, PathShape, NodeStart, HasAngles, @@ -2862,7 +3413,8 @@ class Mesh: ## Generates new elements by extrusion of the elements which belong to the object # The path of extrusion must be a meshed edge. - # @param theObject the object which elements should be processed + # @param theObject the object which elements should be processed. + # It can be a mesh, a sub mesh or a group. # @param PathMesh mesh containing a 1D sub-mesh on the edge, along which the extrusion proceeds # @param PathShape shape(edge) defines the sub-mesh for the path # @param NodeStart the first or the last node on the edge. Defines the direction of extrusion @@ -2881,19 +3433,18 @@ class Mesh: def ExtrusionAlongPathObject2D(self, theObject, PathMesh, PathShape, NodeStart, HasAngles, Angles, HasRefPoint, RefPoint, MakeGroups=False, LinearVariation=False): - Angles,AnglesParameters = ParseAngles(Angles) - RefPoint,RefPointParameters = ParsePointStruct(RefPoint) if ( isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if ( isinstance( RefPoint, geompyDC.GEOM._objref_GEOM_Object)): RefPoint = self.smeshpyD.GetPointStruct(RefPoint) if ( isinstance( PathMesh, Mesh )): PathMesh = PathMesh.GetMesh() + Angles,AnglesParameters,hasVars = ParseAngles(Angles) + Parameters = AnglesParameters + var_separator + RefPoint.parameters + self.mesh.SetParameters(Parameters) if HasAngles and Angles and LinearVariation: Angles = self.editor.LinearAnglesVariation( PathMesh, PathShape, Angles ) pass - Parameters = AnglesParameters + var_separator + RefPointParameters - self.mesh.SetParameters(Parameters) if MakeGroups: return self.editor.ExtrusionAlongPathObject2DMakeGroups(theObject, PathMesh, PathShape, NodeStart, HasAngles, @@ -2916,8 +3467,7 @@ class Mesh: IDsOfElements = self.GetElementsId() if ( isinstance( Mirror, geompyDC.GEOM._objref_GEOM_Object)): Mirror = self.smeshpyD.GetAxisStruct(Mirror) - Mirror,Parameters = ParseAxisStruct(Mirror) - self.mesh.SetParameters(Parameters) + self.mesh.SetParameters(Mirror.parameters) if Copy and MakeGroups: return self.editor.MirrorMakeGroups(IDsOfElements, Mirror, theMirrorType) self.editor.Mirror(IDsOfElements, Mirror, theMirrorType, Copy) @@ -2937,10 +3487,9 @@ class Mesh: IDsOfElements = self.GetElementsId() if ( isinstance( Mirror, geompyDC.GEOM._objref_GEOM_Object)): Mirror = self.smeshpyD.GetAxisStruct(Mirror) - Mirror,Parameters = ParseAxisStruct(Mirror) + self.mesh.SetParameters(Mirror.parameters) mesh = self.editor.MirrorMakeMesh(IDsOfElements, Mirror, theMirrorType, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) return Mesh(self.smeshpyD,self.geompyD,mesh) ## Creates a symmetrical copy of the object @@ -2957,8 +3506,7 @@ class Mesh: theObject = theObject.GetMesh() if ( isinstance( Mirror, geompyDC.GEOM._objref_GEOM_Object)): Mirror = self.smeshpyD.GetAxisStruct(Mirror) - Mirror,Parameters = ParseAxisStruct(Mirror) - self.mesh.SetParameters(Parameters) + self.mesh.SetParameters(Mirror.parameters) if Copy and MakeGroups: return self.editor.MirrorObjectMakeGroups(theObject, Mirror, theMirrorType) self.editor.MirrorObject(theObject, Mirror, theMirrorType, Copy) @@ -2978,10 +3526,9 @@ class Mesh: theObject = theObject.GetMesh() if (isinstance(Mirror, geompyDC.GEOM._objref_GEOM_Object)): Mirror = self.smeshpyD.GetAxisStruct(Mirror) - Mirror,Parameters = ParseAxisStruct(Mirror) + self.mesh.SetParameters(Mirror.parameters) mesh = self.editor.MirrorObjectMakeMesh(theObject, Mirror, theMirrorType, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) return Mesh( self.smeshpyD,self.geompyD,mesh ) ## Translates the elements @@ -2996,8 +3543,7 @@ class Mesh: IDsOfElements = self.GetElementsId() if ( isinstance( Vector, geompyDC.GEOM._objref_GEOM_Object)): Vector = self.smeshpyD.GetDirStruct(Vector) - Vector,Parameters = ParseDirStruct(Vector) - self.mesh.SetParameters(Parameters) + self.mesh.SetParameters(Vector.PS.parameters) if Copy and MakeGroups: return self.editor.TranslateMakeGroups(IDsOfElements, Vector) self.editor.Translate(IDsOfElements, Vector, Copy) @@ -3015,9 +3561,8 @@ class Mesh: IDsOfElements = self.GetElementsId() if ( isinstance( Vector, geompyDC.GEOM._objref_GEOM_Object)): Vector = self.smeshpyD.GetDirStruct(Vector) - Vector,Parameters = ParseDirStruct(Vector) + self.mesh.SetParameters(Vector.PS.parameters) mesh = self.editor.TranslateMakeMesh(IDsOfElements, Vector, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) return Mesh ( self.smeshpyD, self.geompyD, mesh ) ## Translates the object @@ -3032,8 +3577,7 @@ class Mesh: theObject = theObject.GetMesh() if ( isinstance( Vector, geompyDC.GEOM._objref_GEOM_Object)): Vector = self.smeshpyD.GetDirStruct(Vector) - Vector,Parameters = ParseDirStruct(Vector) - self.mesh.SetParameters(Parameters) + self.mesh.SetParameters(Vector.PS.parameters) if Copy and MakeGroups: return self.editor.TranslateObjectMakeGroups(theObject, Vector) self.editor.TranslateObject(theObject, Vector, Copy) @@ -3051,11 +3595,54 @@ class Mesh: theObject = theObject.GetMesh() if (isinstance(Vector, geompyDC.GEOM._objref_GEOM_Object)): Vector = self.smeshpyD.GetDirStruct(Vector) - Vector,Parameters = ParseDirStruct(Vector) + self.mesh.SetParameters(Vector.PS.parameters) mesh = self.editor.TranslateObjectMakeMesh(theObject, Vector, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) return Mesh( self.smeshpyD, self.geompyD, mesh ) + + + ## Scales the object + # @param theObject - the object to translate (mesh, submesh, or group) + # @param thePoint - base point for scale + # @param theScaleFact - list of 1-3 scale factors for axises + # @param Copy - allows copying the translated elements + # @param MakeGroups - forces the generation of new groups from existing + # ones (if Copy) + # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, + # empty list otherwise + def Scale(self, theObject, thePoint, theScaleFact, Copy, MakeGroups=False): + if ( isinstance( theObject, Mesh )): + theObject = theObject.GetMesh() + if ( isinstance( theObject, list )): + theObject = self.GetIDSource(theObject, SMESH.ALL) + + self.mesh.SetParameters(thePoint.parameters) + + if Copy and MakeGroups: + return self.editor.ScaleMakeGroups(theObject, thePoint, theScaleFact) + self.editor.Scale(theObject, thePoint, theScaleFact, Copy) + return [] + + ## Creates a new mesh from the translated object + # @param theObject - the object to translate (mesh, submesh, or group) + # @param thePoint - base point for scale + # @param theScaleFact - list of 1-3 scale factors for axises + # @param MakeGroups - forces the generation of new groups from existing ones + # @param NewMeshName - the name of the newly created mesh + # @return instance of Mesh class + def ScaleMakeMesh(self, theObject, thePoint, theScaleFact, MakeGroups=False, NewMeshName=""): + if (isinstance(theObject, Mesh)): + theObject = theObject.GetMesh() + if ( isinstance( theObject, list )): + theObject = self.GetIDSource(theObject,SMESH.ALL) + + self.mesh.SetParameters(thePoint.parameters) + mesh = self.editor.ScaleMakeMesh(theObject, thePoint, theScaleFact, + MakeGroups, NewMeshName) + return Mesh( self.smeshpyD, self.geompyD, mesh ) + + + ## Rotates the elements # @param IDsOfElements list of elements ids # @param Axis the axis of rotation (AxisStruct or geom line) @@ -3065,18 +3652,12 @@ class Mesh: # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise # @ingroup l2_modif_trsf def Rotate (self, IDsOfElements, Axis, AngleInRadians, Copy, MakeGroups=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,Parameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if IDsOfElements == []: IDsOfElements = self.GetElementsId() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) - Parameters = AxisParameters + var_separator + Parameters + AngleInRadians,Parameters,hasVars = ParseAngles(AngleInRadians) + Parameters = Axis.parameters + var_separator + Parameters self.mesh.SetParameters(Parameters) if Copy and MakeGroups: return self.editor.RotateMakeGroups(IDsOfElements, Axis, AngleInRadians) @@ -3092,21 +3673,15 @@ class Mesh: # @return instance of Mesh class # @ingroup l2_modif_trsf def RotateMakeMesh (self, IDsOfElements, Axis, AngleInRadians, MakeGroups=0, NewMeshName=""): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,Parameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if IDsOfElements == []: IDsOfElements = self.GetElementsId() if ( isinstance( Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) - Parameters = AxisParameters + var_separator + Parameters + AngleInRadians,Parameters,hasVars = ParseAngles(AngleInRadians) + Parameters = Axis.parameters + var_separator + Parameters + self.mesh.SetParameters(Parameters) mesh = self.editor.RotateMakeMesh(IDsOfElements, Axis, AngleInRadians, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) return Mesh( self.smeshpyD, self.geompyD, mesh ) ## Rotates the object @@ -3118,18 +3693,12 @@ class Mesh: # @return list of created groups (SMESH_GroupBase) if MakeGroups=True, empty list otherwise # @ingroup l2_modif_trsf def RotateObject (self, theObject, Axis, AngleInRadians, Copy, MakeGroups=False): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,Parameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if (isinstance(theObject, Mesh)): theObject = theObject.GetMesh() if (isinstance(Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) - Parameters = AxisParameters + ":" + Parameters + AngleInRadians,Parameters,hasVars = ParseAngles(AngleInRadians) + Parameters = Axis.parameters + ":" + Parameters self.mesh.SetParameters(Parameters) if Copy and MakeGroups: return self.editor.RotateObjectMakeGroups(theObject, Axis, AngleInRadians) @@ -3145,21 +3714,15 @@ class Mesh: # @return instance of Mesh class # @ingroup l2_modif_trsf def RotateObjectMakeMesh(self, theObject, Axis, AngleInRadians, MakeGroups=0,NewMeshName=""): - flag = False - if isinstance(AngleInRadians,str): - flag = True - AngleInRadians,Parameters = geompyDC.ParseParameters(AngleInRadians) - if flag: - AngleInRadians = DegreesToRadians(AngleInRadians) if (isinstance( theObject, Mesh )): theObject = theObject.GetMesh() if (isinstance(Axis, geompyDC.GEOM._objref_GEOM_Object)): Axis = self.smeshpyD.GetAxisStruct(Axis) - Axis,AxisParameters = ParseAxisStruct(Axis) - Parameters = AxisParameters + ":" + Parameters + AngleInRadians,Parameters,hasVars = ParseAngles(AngleInRadians) + Parameters = Axis.parameters + ":" + Parameters mesh = self.editor.RotateObjectMakeMesh(theObject, Axis, AngleInRadians, MakeGroups, NewMeshName) - mesh.SetParameters(Parameters) + self.mesh.SetParameters(Parameters) return Mesh( self.smeshpyD, self.geompyD, mesh ) ## Finds groups of ajacent nodes within Tolerance. @@ -3172,10 +3735,17 @@ class Mesh: ## Finds groups of ajacent nodes within Tolerance. # @param Tolerance the value of tolerance # @param SubMeshOrGroup SubMesh or Group + # @param exceptNodes list of either SubMeshes, Groups or node IDs to exclude from search # @return the list of groups of nodes # @ingroup l2_modif_trsf - def FindCoincidentNodesOnPart (self, SubMeshOrGroup, Tolerance): - return self.editor.FindCoincidentNodesOnPart(SubMeshOrGroup, Tolerance) + def FindCoincidentNodesOnPart (self, SubMeshOrGroup, Tolerance, exceptNodes=[]): + if (isinstance( SubMeshOrGroup, Mesh )): + SubMeshOrGroup = SubMeshOrGroup.GetMesh() + if not isinstance( exceptNodes, list): + exceptNodes = [ exceptNodes ] + if exceptNodes and isinstance( exceptNodes[0], int): + exceptNodes = [ self.GetIDSource( exceptNodes, SMESH.NODE)] + return self.editor.FindCoincidentNodesOnPartBut(SubMeshOrGroup, Tolerance,exceptNodes) ## Merges nodes # @param GroupsOfNodes the list of groups of nodes @@ -3188,6 +3758,8 @@ class Mesh: # @return a list of groups of equal elements # @ingroup l2_modif_trsf def FindEqualElements (self, MeshOrSubMeshOrGroup): + if ( isinstance( MeshOrSubMeshOrGroup, Mesh )): + MeshOrSubMeshOrGroup = MeshOrSubMeshOrGroup.GetMesh() return self.editor.FindEqualElements(MeshOrSubMeshOrGroup) ## Merges elements in each given group. @@ -3265,47 +3837,257 @@ class Mesh: # @ingroup l1_auxiliary def GetLastCreatedElems(self): return self.editor.GetLastCreatedElems() - - ## Creates a hole in a mesh by doubling the nodes of some particular elements + + ## Creates a hole in a mesh by doubling the nodes of some particular elements # @param theNodes identifiers of nodes to be doubled - # @param theModifiedElems identifiers of elements to be updated by the new (doubled) - # nodes. If list of element identifiers is empty then nodes are doubled but + # @param theModifiedElems identifiers of elements to be updated by the new (doubled) + # nodes. If list of element identifiers is empty then nodes are doubled but # they not assigned to elements # @return TRUE if operation has been completed successfully, FALSE otherwise # @ingroup l2_modif_edit def DoubleNodes(self, theNodes, theModifiedElems): return self.editor.DoubleNodes(theNodes, theModifiedElems) - + ## Creates a hole in a mesh by doubling the nodes of some particular elements # This method provided for convenience works as DoubleNodes() described above. - # @param theNodes identifiers of node to be doubled + # @param theNodeId identifiers of node to be doubled # @param theModifiedElems identifiers of elements to be updated # @return TRUE if operation has been completed successfully, FALSE otherwise # @ingroup l2_modif_edit def DoubleNode(self, theNodeId, theModifiedElems): return self.editor.DoubleNode(theNodeId, theModifiedElems) - + ## Creates a hole in a mesh by doubling the nodes of some particular elements # This method provided for convenience works as DoubleNodes() described above. # @param theNodes group of nodes to be doubled # @param theModifiedElems group of elements to be updated. - # @return TRUE if operation has been completed successfully, FALSE otherwise + # @param theMakeGroup forces the generation of a group containing new nodes. + # @return TRUE or a created group if operation has been completed successfully, + # FALSE or None otherwise # @ingroup l2_modif_edit - def DoubleNodeGroup(self, theNodes, theModifiedElems): + def DoubleNodeGroup(self, theNodes, theModifiedElems, theMakeGroup=False): + if theMakeGroup: + return self.editor.DoubleNodeGroupNew(theNodes, theModifiedElems) return self.editor.DoubleNodeGroup(theNodes, theModifiedElems) - + ## Creates a hole in a mesh by doubling the nodes of some particular elements # This method provided for convenience works as DoubleNodes() described above. # @param theNodes list of groups of nodes to be doubled # @param theModifiedElems list of groups of elements to be updated. + # @param theMakeGroup forces the generation of a group containing new nodes. # @return TRUE if operation has been completed successfully, FALSE otherwise # @ingroup l2_modif_edit - def DoubleNodeGroups(self, theNodes, theModifiedElems): + def DoubleNodeGroups(self, theNodes, theModifiedElems, theMakeGroup=False): + if theMakeGroup: + return self.editor.DoubleNodeGroupsNew(theNodes, theModifiedElems) return self.editor.DoubleNodeGroups(theNodes, theModifiedElems) + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # @param theElems - the list of elements (edges or faces) to be replicated + # The nodes for duplication could be found from these elements + # @param theNodesNot - list of nodes to NOT replicate + # @param theAffectedElems - the list of elements (cells and edges) to which the + # replicated nodes should be associated to. + # @return TRUE if operation has been completed successfully, FALSE otherwise + # @ingroup l2_modif_edit + def DoubleNodeElem(self, theElems, theNodesNot, theAffectedElems): + return self.editor.DoubleNodeElem(theElems, theNodesNot, theAffectedElems) + + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # @param theElems - the list of elements (edges or faces) to be replicated + # The nodes for duplication could be found from these elements + # @param theNodesNot - list of nodes to NOT replicate + # @param theShape - shape to detect affected elements (element which geometric center + # located on or inside shape). + # The replicated nodes should be associated to affected elements. + # @return TRUE if operation has been completed successfully, FALSE otherwise + # @ingroup l2_modif_edit + def DoubleNodeElemInRegion(self, theElems, theNodesNot, theShape): + return self.editor.DoubleNodeElemInRegion(theElems, theNodesNot, theShape) + + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # This method provided for convenience works as DoubleNodes() described above. + # @param theElems - group of of elements (edges or faces) to be replicated + # @param theNodesNot - group of nodes not to replicated + # @param theAffectedElems - group of elements to which the replicated nodes + # should be associated to. + # @param theMakeGroup forces the generation of a group containing new elements. + # @param theMakeNodeGroup forces the generation of a group containing new nodes. + # @return TRUE or created groups (one or two) if operation has been completed successfully, + # FALSE or None otherwise + # @ingroup l2_modif_edit + def DoubleNodeElemGroup(self, theElems, theNodesNot, theAffectedElems, + theMakeGroup=False, theMakeNodeGroup=False): + if theMakeGroup or theMakeNodeGroup: + twoGroups = self.editor.DoubleNodeElemGroup2New(theElems, theNodesNot, + theAffectedElems, + theMakeGroup, theMakeNodeGroup) + if theMakeGroup and theMakeNodeGroup: + return twoGroups + else: + return twoGroups[ int(theMakeNodeGroup) ] + return self.editor.DoubleNodeElemGroup(theElems, theNodesNot, theAffectedElems) + + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # This method provided for convenience works as DoubleNodes() described above. + # @param theElems - group of of elements (edges or faces) to be replicated + # @param theNodesNot - group of nodes not to replicated + # @param theShape - shape to detect affected elements (element which geometric center + # located on or inside shape). + # The replicated nodes should be associated to affected elements. + # @ingroup l2_modif_edit + def DoubleNodeElemGroupInRegion(self, theElems, theNodesNot, theShape): + return self.editor.DoubleNodeElemGroupInRegion(theElems, theNodesNot, theShape) + + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # This method provided for convenience works as DoubleNodes() described above. + # @param theElems - list of groups of elements (edges or faces) to be replicated + # @param theNodesNot - list of groups of nodes not to replicated + # @param theAffectedElems - group of elements to which the replicated nodes + # should be associated to. + # @param theMakeGroup forces the generation of a group containing new elements. + # @param theMakeNodeGroup forces the generation of a group containing new nodes. + # @return TRUE or created groups (one or two) if operation has been completed successfully, + # FALSE or None otherwise + # @ingroup l2_modif_edit + def DoubleNodeElemGroups(self, theElems, theNodesNot, theAffectedElems, + theMakeGroup=False, theMakeNodeGroup=False): + if theMakeGroup or theMakeNodeGroup: + twoGroups = self.editor.DoubleNodeElemGroups2New(theElems, theNodesNot, + theAffectedElems, + theMakeGroup, theMakeNodeGroup) + if theMakeGroup and theMakeNodeGroup: + return twoGroups + else: + return twoGroups[ int(theMakeNodeGroup) ] + return self.editor.DoubleNodeElemGroups(theElems, theNodesNot, theAffectedElems) + + ## Creates a hole in a mesh by doubling the nodes of some particular elements + # This method provided for convenience works as DoubleNodes() described above. + # @param theElems - list of groups of elements (edges or faces) to be replicated + # @param theNodesNot - list of groups of nodes not to replicated + # @param theShape - shape to detect affected elements (element which geometric center + # located on or inside shape). + # The replicated nodes should be associated to affected elements. + # @return TRUE if operation has been completed successfully, FALSE otherwise + # @ingroup l2_modif_edit + def DoubleNodeElemGroupsInRegion(self, theElems, theNodesNot, theShape): + return self.editor.DoubleNodeElemGroupsInRegion(theElems, theNodesNot, theShape) + + ## Double nodes on shared faces between groups of volumes and create flat elements on demand. + # The list of groups must describe a partition of the mesh volumes. + # The nodes of the internal faces at the boundaries of the groups are doubled. + # In option, the internal faces are replaced by flat elements. + # Triangles are transformed in prisms, and quadrangles in hexahedrons. + # @param theDomains - list of groups of volumes + # @param createJointElems - if TRUE, create the elements + # @return TRUE if operation has been completed successfully, FALSE otherwise + def DoubleNodesOnGroupBoundaries(self, theDomains, createJointElems ): + return self.editor.DoubleNodesOnGroupBoundaries( theDomains, createJointElems ) + + ## Double nodes on some external faces and create flat elements. + # Flat elements are mainly used by some types of mechanic calculations. + # + # Each group of the list must be constituted of faces. + # Triangles are transformed in prisms, and quadrangles in hexahedrons. + # @param theGroupsOfFaces - list of groups of faces + # @return TRUE if operation has been completed successfully, FALSE otherwise + def CreateFlatElementsOnFacesGroups(self, theGroupsOfFaces ): + return self.editor.CreateFlatElementsOnFacesGroups( theGroupsOfFaces ) + + def _valueFromFunctor(self, funcType, elemId): + fn = self.smeshpyD.GetFunctor(funcType) + fn.SetMesh(self.mesh) + if fn.GetElementType() == self.GetElementType(elemId, True): + val = fn.GetValue(elemId) + else: + val = 0 + return val + + ## Get length of 1D element. + # @param elemId mesh element ID + # @return element's length value + # @ingroup l1_measurements + def GetLength(self, elemId): + return self._valueFromFunctor(SMESH.FT_Length, elemId) + + ## Get area of 2D element. + # @param elemId mesh element ID + # @return element's area value + # @ingroup l1_measurements + def GetArea(self, elemId): + return self._valueFromFunctor(SMESH.FT_Area, elemId) + + ## Get volume of 3D element. + # @param elemId mesh element ID + # @return element's volume value + # @ingroup l1_measurements + def GetVolume(self, elemId): + return self._valueFromFunctor(SMESH.FT_Volume3D, elemId) + + ## Get maximum element length. + # @param elemId mesh element ID + # @return element's maximum length value + # @ingroup l1_measurements + def GetMaxElementLength(self, elemId): + if self.GetElementType(elemId, True) == SMESH.VOLUME: + ftype = SMESH.FT_MaxElementLength3D + else: + ftype = SMESH.FT_MaxElementLength2D + return self._valueFromFunctor(ftype, elemId) + + ## Get aspect ratio of 2D or 3D element. + # @param elemId mesh element ID + # @return element's aspect ratio value + # @ingroup l1_measurements + def GetAspectRatio(self, elemId): + if self.GetElementType(elemId, True) == SMESH.VOLUME: + ftype = SMESH.FT_AspectRatio3D + else: + ftype = SMESH.FT_AspectRatio + return self._valueFromFunctor(ftype, elemId) + + ## Get warping angle of 2D element. + # @param elemId mesh element ID + # @return element's warping angle value + # @ingroup l1_measurements + def GetWarping(self, elemId): + return self._valueFromFunctor(SMESH.FT_Warping, elemId) + + ## Get minimum angle of 2D element. + # @param elemId mesh element ID + # @return element's minimum angle value + # @ingroup l1_measurements + def GetMinimumAngle(self, elemId): + return self._valueFromFunctor(SMESH.FT_MinimumAngle, elemId) + + ## Get taper of 2D element. + # @param elemId mesh element ID + # @return element's taper value + # @ingroup l1_measurements + def GetTaper(self, elemId): + return self._valueFromFunctor(SMESH.FT_Taper, elemId) + + ## Get skew of 2D element. + # @param elemId mesh element ID + # @return element's skew value + # @ingroup l1_measurements + def GetSkew(self, elemId): + return self._valueFromFunctor(SMESH.FT_Skew, elemId) + ## The mother class to define algorithm, it is not recommended to use it directly. # -# More details. +# For each meshing algorithm, a python class inheriting from class Mesh_Algorithm +# should be defined. This descendant class sould have two attributes defining the way +# it is created by class Mesh (see e.g. class StdMeshersDC_Segment in StdMeshersDC.py). +# - meshMethod attribute defines name of method of class Mesh by calling which the +# python class of algorithm is created. E.g. if in class MyPlugin_Algorithm +# meshMethod = "MyAlgorithm", then an instance of MyPlugin_Algorithm is created +# by the following code: my_algo = mesh.MyAlgorithm() +# - algoType defines name of algorithm type and is used mostly to discriminate +# algorithms that are created by the same method of class Mesh. E.g. if +# MyPlugin_Algorithm.algoType = "MyPLUGIN" then it's creation code can be: +# my_algo = mesh.MyAlgorithm(algo="MyPLUGIN") # @ingroup l2_algorithms class Mesh_Algorithm: # @class Mesh_Algorithm @@ -3366,6 +4148,7 @@ class Mesh_Algorithm: # @return SMESH.SMESH_Algo def FindAlgorithm (self, algoname, smeshpyD): study = smeshpyD.GetCurrentStudy() + if not study: return None #to do: find component by smeshpyD object, not by its data type scomp = study.FindComponent(smeshpyD.ComponentDataType()) if scomp is not None: @@ -3444,20 +4227,22 @@ class Mesh_Algorithm: if geom is None: raise RuntimeError, "Attemp to create " + algo + " algoritm on None shape" self.mesh = mesh - piece = mesh.geom + name = "" if not geom: - self.geom = piece + self.geom = mesh.geom else: self.geom = geom - name = GetName(geom) - if name==NO_NAME: - name = mesh.geompyD.SubShapeName(geom, piece) - mesh.geompyD.addToStudyInFather(piece, geom, name) + AssureGeomPublished( mesh, geom ) + try: + name = GetName(geom) + pass + except: + pass self.subm = mesh.mesh.GetSubMesh(geom, algo.GetName()) - self.algo = algo status = mesh.mesh.AddHypothesis(self.geom, self.algo) - TreatHypoStatus( status, algo.GetName(), GetName(self.geom), True ) + TreatHypoStatus( status, algo.GetName(), name, True ) + return def CompareHyp (self, hyp, args): print "CompareHyp is not implemented for ", self.__class__.__name__, ":", hyp.GetName() @@ -3478,1521 +4263,183 @@ class Mesh_Algorithm: hypo = self.mesh.smeshpyD.CreateHypothesis(hyp, so) a = "" s = "=" - i = 0 - n = len(args) - while i 10: + argStr = argStr[:7]+"..." + if argStr[0] == '[': argStr += ']' + a = a + s + argStr s = "," - i = i + 1 pass + if len(a) > 50: + a = a[:47]+"..." self.mesh.smeshpyD.SetName(hypo, hyp + a) pass + geomName="" + if self.geom: + geomName = GetName(self.geom) status = self.mesh.mesh.AddHypothesis(self.geom, hypo) - TreatHypoStatus( status, GetName(hypo), GetName(self.geom), 0 ) + TreatHypoStatus( status, GetName(hypo), geomName, 0 ) return hypo - -# Public class: Mesh_Segment -# -------------------------- - -## Class to define a segment 1D algorithm for discretization -# -# More details. -# @ingroup l3_algos_basic -class Mesh_Segment(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Regular_1D") - - ## Defines "LocalLength" hypothesis to cut an edge in several segments with the same length - # @param l for the length of segments that cut an edge - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - creates a new one - # @param p precision, used for calculation of the number of segments. - # The precision should be a positive, meaningful value within the range [0,1]. - # In general, the number of segments is calculated with the formula: - # nb = ceil((edge_length / l) - p) - # Function ceil rounds its argument to the higher integer. - # So, p=0 means rounding of (edge_length / l) to the higher integer, - # p=0.5 means rounding of (edge_length / l) to the nearest integer, - # p=1 means rounding of (edge_length / l) to the lower integer. - # Default value is 1e-07. - # @return an instance of StdMeshers_LocalLength hypothesis - # @ingroup l3_hypos_1dhyps - def LocalLength(self, l, UseExisting=0, p=1e-07): - hyp = self.Hypothesis("LocalLength", [l,p], UseExisting=UseExisting, - CompareMethod=self.CompareLocalLength) - hyp.SetLength(l) - hyp.SetPrecision(p) - return hyp - - ## Private method - ## Checks if the given "LocalLength" hypothesis has the same parameters as the given arguments - def CompareLocalLength(self, hyp, args): - if IsEqual(hyp.GetLength(), args[0]): - return IsEqual(hyp.GetPrecision(), args[1]) - return False - - ## Defines "MaxSize" hypothesis to cut an edge into segments not longer than given value - # @param length is optional maximal allowed length of segment, if it is omitted - # the preestimated length is used that depends on geometry size - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - create a new one - # @return an instance of StdMeshers_MaxLength hypothesis - # @ingroup l3_hypos_1dhyps - def MaxSize(self, length=0.0, UseExisting=0): - hyp = self.Hypothesis("MaxLength", [length], UseExisting=UseExisting) - if length > 0.0: - # set given length - hyp.SetLength(length) - if not UseExisting: - # set preestimated length - gen = self.mesh.smeshpyD - initHyp = gen.GetHypothesisParameterValues("MaxLength", "libStdMeshersEngine.so", - self.mesh.GetMesh(), self.mesh.GetShape(), - False) # <- byMesh - preHyp = initHyp._narrow(StdMeshers.StdMeshers_MaxLength) - if preHyp: - hyp.SetPreestimatedLength( preHyp.GetPreestimatedLength() ) - pass - pass - hyp.SetUsePreestimatedLength( length == 0.0 ) - return hyp - - ## Defines "NumberOfSegments" hypothesis to cut an edge in a fixed number of segments - # @param n for the number of segments that cut an edge - # @param s for the scale factor (optional) - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - create a new one - # @return an instance of StdMeshers_NumberOfSegments hypothesis - # @ingroup l3_hypos_1dhyps - def NumberOfSegments(self, n, s=[], UseExisting=0): - if s == []: - hyp = self.Hypothesis("NumberOfSegments", [n], UseExisting=UseExisting, - CompareMethod=self.CompareNumberOfSegments) - else: - hyp = self.Hypothesis("NumberOfSegments", [n,s], UseExisting=UseExisting, - CompareMethod=self.CompareNumberOfSegments) - hyp.SetDistrType( 1 ) - hyp.SetScaleFactor(s) - hyp.SetNumberOfSegments(n) - return hyp - - ## Private method - ## Checks if the given "NumberOfSegments" hypothesis has the same parameters as the given arguments - def CompareNumberOfSegments(self, hyp, args): - if hyp.GetNumberOfSegments() == args[0]: - if len(args) == 1: - return True - else: - if hyp.GetDistrType() == 1: - if IsEqual(hyp.GetScaleFactor(), args[1]): - return True - return False - - ## Defines "Arithmetic1D" hypothesis to cut an edge in several segments with increasing arithmetic length - # @param start defines the length of the first segment - # @param end defines the length of the last segment - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - creates a new one - # @return an instance of StdMeshers_Arithmetic1D hypothesis - # @ingroup l3_hypos_1dhyps - def Arithmetic1D(self, start, end, UseExisting=0): - hyp = self.Hypothesis("Arithmetic1D", [start, end], UseExisting=UseExisting, - CompareMethod=self.CompareArithmetic1D) - hyp.SetLength(start, 1) - hyp.SetLength(end , 0) - return hyp - - ## Private method - ## Check if the given "Arithmetic1D" hypothesis has the same parameters as the given arguments - def CompareArithmetic1D(self, hyp, args): - if IsEqual(hyp.GetLength(1), args[0]): - if IsEqual(hyp.GetLength(0), args[1]): - return True - return False - - ## Defines "StartEndLength" hypothesis to cut an edge in several segments with increasing geometric length - # @param start defines the length of the first segment - # @param end defines the length of the last segment - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - creates a new one - # @return an instance of StdMeshers_StartEndLength hypothesis - # @ingroup l3_hypos_1dhyps - def StartEndLength(self, start, end, UseExisting=0): - hyp = self.Hypothesis("StartEndLength", [start, end], UseExisting=UseExisting, - CompareMethod=self.CompareStartEndLength) - hyp.SetLength(start, 1) - hyp.SetLength(end , 0) - return hyp - - ## Check if the given "StartEndLength" hypothesis has the same parameters as the given arguments - def CompareStartEndLength(self, hyp, args): - if IsEqual(hyp.GetLength(1), args[0]): - if IsEqual(hyp.GetLength(0), args[1]): - return True - return False - - ## Defines "Deflection1D" hypothesis - # @param d for the deflection - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - create a new one - # @ingroup l3_hypos_1dhyps - def Deflection1D(self, d, UseExisting=0): - hyp = self.Hypothesis("Deflection1D", [d], UseExisting=UseExisting, - CompareMethod=self.CompareDeflection1D) - hyp.SetDeflection(d) - return hyp - - ## Check if the given "Deflection1D" hypothesis has the same parameters as the given arguments - def CompareDeflection1D(self, hyp, args): - return IsEqual(hyp.GetDeflection(), args[0]) - - ## Defines "Propagation" hypothesis that propagates all other hypotheses on all other edges that are at - # the opposite side in case of quadrangular faces - # @ingroup l3_hypos_additi - def Propagation(self): - return self.Hypothesis("Propagation", UseExisting=1, CompareMethod=self.CompareEqualHyp) - - ## Defines "AutomaticLength" hypothesis - # @param fineness for the fineness [0-1] - # @param UseExisting if ==true - searches for an existing hypothesis created with the - # same parameters, else (default) - create a new one - # @ingroup l3_hypos_1dhyps - def AutomaticLength(self, fineness=0, UseExisting=0): - hyp = self.Hypothesis("AutomaticLength",[fineness],UseExisting=UseExisting, - CompareMethod=self.CompareAutomaticLength) - hyp.SetFineness( fineness ) - return hyp - - ## Checks if the given "AutomaticLength" hypothesis has the same parameters as the given arguments - def CompareAutomaticLength(self, hyp, args): - return IsEqual(hyp.GetFineness(), args[0]) - - ## Defines "SegmentLengthAroundVertex" hypothesis - # @param length for the segment length - # @param vertex for the length localization: the vertex index [0,1] | vertex object. - # Any other integer value means that the hypothesis will be set on the - # whole 1D shape, where Mesh_Segment algorithm is assigned. - # @param UseExisting if ==true - searches for an existing hypothesis created with - # the same parameters, else (default) - creates a new one - # @ingroup l3_algos_segmarv - def LengthNearVertex(self, length, vertex=0, UseExisting=0): - import types - store_geom = self.geom - if type(vertex) is types.IntType: - if vertex == 0 or vertex == 1: - vertex = self.mesh.geompyD.SubShapeAllSorted(self.geom, geompyDC.ShapeType["VERTEX"])[vertex] - self.geom = vertex - pass - pass - else: - self.geom = vertex - pass - ### 0D algorithm - if self.geom is None: - raise RuntimeError, "Attemp to create SegmentAroundVertex_0D algoritm on None shape" - name = GetName(self.geom) - if name == NO_NAME: - piece = self.mesh.geom - name = self.mesh.geompyD.SubShapeName(self.geom, piece) - self.mesh.geompyD.addToStudyInFather(piece, self.geom, name) - algo = self.FindAlgorithm("SegmentAroundVertex_0D", self.mesh.smeshpyD) - if algo is None: - algo = self.mesh.smeshpyD.CreateHypothesis("SegmentAroundVertex_0D", "libStdMeshersEngine.so") - pass - status = self.mesh.mesh.AddHypothesis(self.geom, algo) - TreatHypoStatus(status, "SegmentAroundVertex_0D", name, True) - ### - hyp = self.Hypothesis("SegmentLengthAroundVertex", [length], UseExisting=UseExisting, - CompareMethod=self.CompareLengthNearVertex) - self.geom = store_geom - hyp.SetLength( length ) - return hyp - - ## Checks if the given "LengthNearVertex" hypothesis has the same parameters as the given arguments - # @ingroup l3_algos_segmarv - def CompareLengthNearVertex(self, hyp, args): - return IsEqual(hyp.GetLength(), args[0]) - - ## Defines "QuadraticMesh" hypothesis, forcing construction of quadratic edges. - # If the 2D mesher sees that all boundary edges are quadratic, - # it generates quadratic faces, else it generates linear faces using - # medium nodes as if they are vertices. - # The 3D mesher generates quadratic volumes only if all boundary faces - # are quadratic, else it fails. - # + ## Returns entry of the shape to mesh in the study + def MainShapeEntry(self): + if not self.mesh or not self.mesh.GetMesh(): return "" + if not self.mesh.GetMesh().HasShapeToMesh(): return "" + shape = self.mesh.GetShape() + return shape.GetStudyEntry() + + ## Defines "ViscousLayers" hypothesis to give parameters of layers of prisms to build + # near mesh boundary. This hypothesis can be used by several 3D algorithms: + # NETGEN 3D, GHS3D, Hexahedron(i,j,k) + # @param thickness total thickness of layers of prisms + # @param numberOfLayers number of layers of prisms + # @param stretchFactor factor (>1.0) of growth of layer thickness towards inside of mesh + # @param ignoreFaces list of geometrical faces (or their ids) not to generate layers on # @ingroup l3_hypos_additi - def QuadraticMesh(self): - hyp = self.Hypothesis("QuadraticMesh", UseExisting=1, CompareMethod=self.CompareEqualHyp) + def ViscousLayers(self, thickness, numberOfLayers, stretchFactor, ignoreFaces=[]): + if not isinstance(self.algo, SMESH._objref_SMESH_3D_Algo): + raise TypeError, "ViscousLayers are supported by 3D algorithms only" + if not "ViscousLayers" in self.GetCompatibleHypothesis(): + raise TypeError, "ViscousLayers are not supported by %s"%self.algo.GetName() + if ignoreFaces and isinstance( ignoreFaces[0], geompyDC.GEOM._objref_GEOM_Object ): + ignoreFaces = [ self.mesh.geompyD.GetSubShapeID(self.mesh.geom, f) for f in ignoreFaces ] + hyp = self.Hypothesis("ViscousLayers", + [thickness, numberOfLayers, stretchFactor, ignoreFaces]) + hyp.SetTotalThickness(thickness) + hyp.SetNumberLayers(numberOfLayers) + hyp.SetStretchFactor(stretchFactor) + hyp.SetIgnoreFaces(ignoreFaces) return hyp -# Public class: Mesh_CompositeSegment -# -------------------------- - -## Defines a segment 1D algorithm for discretization -# -# @ingroup l3_algos_basic -class Mesh_CompositeSegment(Mesh_Segment): - - ## Private constructor. - def __init__(self, mesh, geom=0): - self.Create(mesh, geom, "CompositeSegment_1D") - - -# Public class: Mesh_Segment_Python -# --------------------------------- - -## Defines a segment 1D algorithm for discretization with python function -# -# @ingroup l3_algos_basic -class Mesh_Segment_Python(Mesh_Segment): - - ## Private constructor. - def __init__(self, mesh, geom=0): - import Python1dPlugin - self.Create(mesh, geom, "Python_1D", "libPython1dEngine.so") - - ## Defines "PythonSplit1D" hypothesis - # @param n for the number of segments that cut an edge - # @param func for the python function that calculates the length of all segments - # @param UseExisting if ==true - searches for the existing hypothesis created with - # the same parameters, else (default) - creates a new one + ## Transform a list of ether edges or tuples (edge, 1st_vertex_of_edge) + # into a list acceptable to SetReversedEdges() of some 1D hypotheses # @ingroup l3_hypos_1dhyps - def PythonSplit1D(self, n, func, UseExisting=0): - hyp = self.Hypothesis("PythonSplit1D", [n], "libPython1dEngine.so", - UseExisting=UseExisting, CompareMethod=self.ComparePythonSplit1D) - hyp.SetNumberOfSegments(n) - hyp.SetPythonLog10RatioFunction(func) - return hyp - - ## Checks if the given "PythonSplit1D" hypothesis has the same parameters as the given arguments - def ComparePythonSplit1D(self, hyp, args): - #if hyp.GetNumberOfSegments() == args[0]: - # if hyp.GetPythonLog10RatioFunction() == args[1]: - # return True - return False - -# Public class: Mesh_Triangle -# --------------------------- - -## Defines a triangle 2D algorithm -# -# @ingroup l3_algos_basic -class Mesh_Triangle(Mesh_Algorithm): - - # default values - algoType = 0 - params = 0 - - _angleMeshS = 8 - _gradation = 1.1 - - ## Private constructor. - def __init__(self, mesh, algoType, geom=0): - Mesh_Algorithm.__init__(self) - - self.algoType = algoType - if algoType == MEFISTO: - self.Create(mesh, geom, "MEFISTO_2D") - pass - elif algoType == BLSURF: - import BLSURFPlugin - self.Create(mesh, geom, "BLSURF", "libBLSURFEngine.so") - #self.SetPhysicalMesh() - PAL19680 - elif algoType == NETGEN: - if noNETGENPlugin: - print "Warning: NETGENPlugin module unavailable" - pass - self.Create(mesh, geom, "NETGEN_2D", "libNETGENEngine.so") - pass - elif algoType == NETGEN_2D: - if noNETGENPlugin: - print "Warning: NETGENPlugin module unavailable" - pass - self.Create(mesh, geom, "NETGEN_2D_ONLY", "libNETGENEngine.so") - pass - - ## Defines "MaxElementArea" hypothesis basing on the definition of the maximum area of each triangle - # @param area for the maximum area of each triangle - # @param UseExisting if ==true - searches for an existing hypothesis created with the - # same parameters, else (default) - creates a new one - # - # Only for algoType == MEFISTO || NETGEN_2D - # @ingroup l3_hypos_2dhyps - def MaxElementArea(self, area, UseExisting=0): - if self.algoType == MEFISTO or self.algoType == NETGEN_2D: - hyp = self.Hypothesis("MaxElementArea", [area], UseExisting=UseExisting, - CompareMethod=self.CompareMaxElementArea) - elif self.algoType == NETGEN: - hyp = self.Parameters(SIMPLE) - hyp.SetMaxElementArea(area) - return hyp - - ## Checks if the given "MaxElementArea" hypothesis has the same parameters as the given arguments - def CompareMaxElementArea(self, hyp, args): - return IsEqual(hyp.GetMaxElementArea(), args[0]) - - ## Defines "LengthFromEdges" hypothesis to build triangles - # based on the length of the edges taken from the wire - # - # Only for algoType == MEFISTO || NETGEN_2D - # @ingroup l3_hypos_2dhyps - def LengthFromEdges(self): - if self.algoType == MEFISTO or self.algoType == NETGEN_2D: - hyp = self.Hypothesis("LengthFromEdges", UseExisting=1, CompareMethod=self.CompareEqualHyp) - return hyp - elif self.algoType == NETGEN: - hyp = self.Parameters(SIMPLE) - hyp.LengthFromEdges() - return hyp - - ## Sets a way to define size of mesh elements to generate. - # @param thePhysicalMesh is: DefaultSize or Custom. - # @ingroup l3_hypos_blsurf - def SetPhysicalMesh(self, thePhysicalMesh=DefaultSize): - # Parameter of BLSURF algo - self.Parameters().SetPhysicalMesh(thePhysicalMesh) - - ## Sets size of mesh elements to generate. - # @ingroup l3_hypos_blsurf - def SetPhySize(self, theVal): - # Parameter of BLSURF algo - self.Parameters().SetPhySize(theVal) - - ## Sets lower boundary of mesh element size (PhySize). - # @ingroup l3_hypos_blsurf - def SetPhyMin(self, theVal=-1): - # Parameter of BLSURF algo - self.Parameters().SetPhyMin(theVal) - - ## Sets upper boundary of mesh element size (PhySize). - # @ingroup l3_hypos_blsurf - def SetPhyMax(self, theVal=-1): - # Parameter of BLSURF algo - self.Parameters().SetPhyMax(theVal) - - ## Sets a way to define maximum angular deflection of mesh from CAD model. - # @param theGeometricMesh is: DefaultGeom or Custom - # @ingroup l3_hypos_blsurf - def SetGeometricMesh(self, theGeometricMesh=0): - # Parameter of BLSURF algo - if self.Parameters().GetPhysicalMesh() == 0: theGeometricMesh = 1 - self.params.SetGeometricMesh(theGeometricMesh) - - ## Sets angular deflection (in degrees) of a mesh face from CAD surface. - # @ingroup l3_hypos_blsurf - def SetAngleMeshS(self, theVal=_angleMeshS): - # Parameter of BLSURF algo - if self.Parameters().GetGeometricMesh() == 0: theVal = self._angleMeshS - self.params.SetAngleMeshS(theVal) - - ## Sets angular deflection (in degrees) of a mesh edge from CAD curve. - # @ingroup l3_hypos_blsurf - def SetAngleMeshC(self, theVal=_angleMeshS): - # Parameter of BLSURF algo - if self.Parameters().GetGeometricMesh() == 0: theVal = self._angleMeshS - self.params.SetAngleMeshC(theVal) - - ## Sets lower boundary of mesh element size computed to respect angular deflection. - # @ingroup l3_hypos_blsurf - def SetGeoMin(self, theVal=-1): - # Parameter of BLSURF algo - self.Parameters().SetGeoMin(theVal) - - ## Sets upper boundary of mesh element size computed to respect angular deflection. - # @ingroup l3_hypos_blsurf - def SetGeoMax(self, theVal=-1): - # Parameter of BLSURF algo - self.Parameters().SetGeoMax(theVal) - - ## Sets maximal allowed ratio between the lengths of two adjacent edges. - # @ingroup l3_hypos_blsurf - def SetGradation(self, theVal=_gradation): - # Parameter of BLSURF algo - if self.Parameters().GetGeometricMesh() == 0: theVal = self._gradation - self.params.SetGradation(theVal) - - ## Sets topology usage way. - # @param way defines how mesh conformity is assured
                      - #
                    • FromCAD - mesh conformity is assured by conformity of a shape
                    • - #
                    • PreProcess or PreProcessPlus - by pre-processing a CAD model
                    - # @ingroup l3_hypos_blsurf - def SetTopology(self, way): - # Parameter of BLSURF algo - self.Parameters().SetTopology(way) - - ## To respect geometrical edges or not. - # @ingroup l3_hypos_blsurf - def SetDecimesh(self, toIgnoreEdges=False): - # Parameter of BLSURF algo - self.Parameters().SetDecimesh(toIgnoreEdges) - - ## Sets verbosity level in the range 0 to 100. - # @ingroup l3_hypos_blsurf - def SetVerbosity(self, level): - # Parameter of BLSURF algo - self.Parameters().SetVerbosity(level) - - ## Sets advanced option value. - # @ingroup l3_hypos_blsurf - def SetOptionValue(self, optionName, level): - # Parameter of BLSURF algo - self.Parameters().SetOptionValue(optionName,level) - - ## Sets QuadAllowed flag. - # Only for algoType == NETGEN || NETGEN_2D || BLSURF - # @ingroup l3_hypos_netgen l3_hypos_blsurf - def SetQuadAllowed(self, toAllow=True): - if self.algoType == NETGEN_2D: - if toAllow: # add QuadranglePreference - self.Hypothesis("QuadranglePreference", UseExisting=1, CompareMethod=self.CompareEqualHyp) - else: # remove QuadranglePreference - for hyp in self.mesh.GetHypothesisList( self.geom ): - if hyp.GetName() == "QuadranglePreference": - self.mesh.RemoveHypothesis( self.geom, hyp ) - pass - pass - pass - return - if self.Parameters(): - self.params.SetQuadAllowed(toAllow) - return - - ## Defines hypothesis having several parameters - # - # @ingroup l3_hypos_netgen - def Parameters(self, which=SOLE): - if self.params: - return self.params - if self.algoType == NETGEN: - if which == SIMPLE: - self.params = self.Hypothesis("NETGEN_SimpleParameters_2D", [], - "libNETGENEngine.so", UseExisting=0) + def ReversedEdgeIndices(self, reverseList): + resList = [] + geompy = self.mesh.geompyD + for i in reverseList: + if isinstance( i, int ): + s = geompy.SubShapes(self.mesh.geom, [i])[0] + if s.GetShapeType() != geompyDC.GEOM.EDGE: + raise TypeError, "Not EDGE index given" + resList.append( i ) + elif isinstance( i, geompyDC.GEOM._objref_GEOM_Object ): + if i.GetShapeType() != geompyDC.GEOM.EDGE: + raise TypeError, "Not an EDGE given" + resList.append( geompy.GetSubShapeID(self.mesh.geom, i )) + elif len( i ) > 1: + e = i[0] + v = i[1] + if not isinstance( e, geompyDC.GEOM._objref_GEOM_Object ) or \ + not isinstance( v, geompyDC.GEOM._objref_GEOM_Object ): + raise TypeError, "A list item must be a tuple (edge, 1st_vertex_of_edge)" + if v.GetShapeType() == geompyDC.GEOM.EDGE and \ + e.GetShapeType() == geompyDC.GEOM.VERTEX: + v,e = e,v + if e.GetShapeType() != geompyDC.GEOM.EDGE or \ + v.GetShapeType() != geompyDC.GEOM.VERTEX: + raise TypeError, "A list item must be a tuple (edge, 1st_vertex_of_edge)" + vFirst = FirstVertexOnCurve( e ) + tol = geompy.Tolerance( vFirst )[-1] + if geompy.MinDistance( v, vFirst ) > 1.5*tol: + resList.append( geompy.GetSubShapeID(self.mesh.geom, e )) else: - self.params = self.Hypothesis("NETGEN_Parameters_2D", [], - "libNETGENEngine.so", UseExisting=0) - return self.params - elif self.algoType == MEFISTO: - print "Mefisto algo support no multi-parameter hypothesis" - return None - elif self.algoType == NETGEN_2D: - print "NETGEN_2D_ONLY algo support no multi-parameter hypothesis" - print "NETGEN_2D_ONLY uses 'MaxElementArea' and 'LengthFromEdges' ones" - return None - elif self.algoType == BLSURF: - self.params = self.Hypothesis("BLSURF_Parameters", [], - "libBLSURFEngine.so", UseExisting=0) - return self.params - else: - print "Mesh_Triangle with algo type %s does not have such a parameter, check algo type"%self.algoType - return None - - ## Sets MaxSize - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetMaxSize(self, theSize): - if self.Parameters(): - self.params.SetMaxSize(theSize) - - ## Sets SecondOrder flag - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetSecondOrder(self, theVal): - if self.Parameters(): - self.params.SetSecondOrder(theVal) + raise TypeError, "Item must be either an edge or tuple (edge, 1st_vertex_of_edge)" + return resList - ## Sets Optimize flag - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetOptimize(self, theVal): - if self.Parameters(): - self.params.SetOptimize(theVal) - - ## Sets Fineness - # @param theFineness is: - # VeryCoarse, Coarse, Moderate, Fine, VeryFine or Custom - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetFineness(self, theFineness): - if self.Parameters(): - self.params.SetFineness(theFineness) - - ## Sets GrowthRate - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetGrowthRate(self, theRate): - if self.Parameters(): - self.params.SetGrowthRate(theRate) - - ## Sets NbSegPerEdge - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetNbSegPerEdge(self, theVal): - if self.Parameters(): - self.params.SetNbSegPerEdge(theVal) - - ## Sets NbSegPerRadius - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetNbSegPerRadius(self, theVal): - if self.Parameters(): - self.params.SetNbSegPerRadius(theVal) - - ## Sets number of segments overriding value set by SetLocalLength() - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetNumberOfSegments(self, theVal): - self.Parameters(SIMPLE).SetNumberOfSegments(theVal) - ## Sets number of segments overriding value set by SetNumberOfSegments() - # - # Only for algoType == NETGEN - # @ingroup l3_hypos_netgen - def SetLocalLength(self, theVal): - self.Parameters(SIMPLE).SetLocalLength(theVal) - - pass +class Pattern(SMESH._objref_SMESH_Pattern): + def ApplyToMeshFaces(self, theMesh, theFacesIDs, theNodeIndexOnKeyPoint1, theReverse): + decrFun = lambda i: i-1 + theNodeIndexOnKeyPoint1,Parameters,hasVars = ParseParameters(theNodeIndexOnKeyPoint1, decrFun) + theMesh.SetParameters(Parameters) + return SMESH._objref_SMESH_Pattern.ApplyToMeshFaces( self, theMesh, theFacesIDs, theNodeIndexOnKeyPoint1, theReverse ) -# Public class: Mesh_Quadrangle -# ----------------------------- + def ApplyToHexahedrons(self, theMesh, theVolumesIDs, theNode000Index, theNode001Index): + decrFun = lambda i: i-1 + theNode000Index,theNode001Index,Parameters,hasVars = ParseParameters(theNode000Index,theNode001Index, decrFun) + theMesh.SetParameters(Parameters) + return SMESH._objref_SMESH_Pattern.ApplyToHexahedrons( self, theMesh, theVolumesIDs, theNode000Index, theNode001Index ) -## Defines a quadrangle 2D algorithm -# -# @ingroup l3_algos_basic -class Mesh_Quadrangle(Mesh_Algorithm): +#Registering the new proxy for Pattern +omniORB.registerObjref(SMESH._objref_SMESH_Pattern._NP_RepositoryId, Pattern) - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Quadrangle_2D") - ## Defines "QuadranglePreference" hypothesis, forcing construction - # of quadrangles if the number of nodes on the opposite edges is not the same - # while the total number of nodes on edges is even - # - # @ingroup l3_hypos_additi - def QuadranglePreference(self): - hyp = self.Hypothesis("QuadranglePreference", UseExisting=1, - CompareMethod=self.CompareEqualHyp) - return hyp - ## Defines "TrianglePreference" hypothesis, forcing construction - # of triangles in the refinement area if the number of nodes - # on the opposite edges is not the same - # - # @ingroup l3_hypos_additi - def TrianglePreference(self): - hyp = self.Hypothesis("TrianglePreference", UseExisting=1, - CompareMethod=self.CompareEqualHyp) - return hyp -# Public class: Mesh_Tetrahedron -# ------------------------------ -## Defines a tetrahedron 3D algorithm +## Private class used to bind methods creating algorithms to the class Mesh # -# @ingroup l3_algos_basic -class Mesh_Tetrahedron(Mesh_Algorithm): - - params = 0 - algoType = 0 - - ## Private constructor. - def __init__(self, mesh, algoType, geom=0): - Mesh_Algorithm.__init__(self) - - if algoType == NETGEN: - self.Create(mesh, geom, "NETGEN_3D", "libNETGENEngine.so") - pass - - elif algoType == FULL_NETGEN: - if noNETGENPlugin: - print "Warning: NETGENPlugin module has not been imported." - self.Create(mesh, geom, "NETGEN_2D3D", "libNETGENEngine.so") - pass - - elif algoType == GHS3D: - import GHS3DPlugin - self.Create(mesh, geom, "GHS3D_3D" , "libGHS3DEngine.so") - pass - - elif algoType == GHS3DPRL: - import GHS3DPRLPlugin - self.Create(mesh, geom, "GHS3DPRL_3D" , "libGHS3DPRLEngine.so") - pass - - self.algoType = algoType - - ## Defines "MaxElementVolume" hypothesis to give the maximun volume of each tetrahedron - # @param vol for the maximum volume of each tetrahedron - # @param UseExisting if ==true - searches for the existing hypothesis created with - # the same parameters, else (default) - creates a new one - # @ingroup l3_hypos_maxvol - def MaxElementVolume(self, vol, UseExisting=0): - if self.algoType == NETGEN: - hyp = self.Hypothesis("MaxElementVolume", [vol], UseExisting=UseExisting, - CompareMethod=self.CompareMaxElementVolume) - hyp.SetMaxElementVolume(vol) - return hyp - elif self.algoType == FULL_NETGEN: - self.Parameters(SIMPLE).SetMaxElementVolume(vol) - return None - - ## Checks if the given "MaxElementVolume" hypothesis has the same parameters as the given arguments - def CompareMaxElementVolume(self, hyp, args): - return IsEqual(hyp.GetMaxElementVolume(), args[0]) - - ## Defines hypothesis having several parameters - # - # @ingroup l3_hypos_netgen - def Parameters(self, which=SOLE): - if self.params: - return self.params - - if self.algoType == FULL_NETGEN: - if which == SIMPLE: - self.params = self.Hypothesis("NETGEN_SimpleParameters_3D", [], - "libNETGENEngine.so", UseExisting=0) - else: - self.params = self.Hypothesis("NETGEN_Parameters", [], - "libNETGENEngine.so", UseExisting=0) - return self.params - - if self.algoType == GHS3D: - self.params = self.Hypothesis("GHS3D_Parameters", [], - "libGHS3DEngine.so", UseExisting=0) - return self.params - - if self.algoType == GHS3DPRL: - self.params = self.Hypothesis("GHS3DPRL_Parameters", [], - "libGHS3DPRLEngine.so", UseExisting=0) - return self.params - - print "Algo supports no multi-parameter hypothesis" +class algoCreator: + def __init__(self): + self.mesh = None + self.defaultAlgoType = "" + self.algoTypeToClass = {} + + # Stores a python class of algorithm + def add(self, algoClass): + if type( algoClass ).__name__ == 'classobj' and \ + hasattr( algoClass, "algoType"): + self.algoTypeToClass[ algoClass.algoType ] = algoClass + if not self.defaultAlgoType and \ + hasattr( algoClass, "isDefault") and algoClass.isDefault: + self.defaultAlgoType = algoClass.algoType + #print "Add",algoClass.algoType, "dflt",self.defaultAlgoType + + # creates a copy of self and assign mesh to the copy + def copy(self, mesh): + other = algoCreator() + other.defaultAlgoType = self.defaultAlgoType + other.algoTypeToClass = self.algoTypeToClass + other.mesh = mesh + return other + + # creates an instance of algorithm + def __call__(self,algo="",geom=0,*args): + algoType = self.defaultAlgoType + for arg in args + (algo,geom): + if isinstance( arg, geompyDC.GEOM._objref_GEOM_Object ): + geom = arg + if isinstance( arg, str ) and arg: + algoType = arg + if not algoType and self.algoTypeToClass: + algoType = self.algoTypeToClass.keys()[0] + if self.algoTypeToClass.has_key( algoType ): + #print "Create algo",algoType + return self.algoTypeToClass[ algoType ]( self.mesh, geom ) + raise RuntimeError, "No class found for algo type %s" % algoType return None - ## Sets MaxSize - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetMaxSize(self, theSize): - self.Parameters().SetMaxSize(theSize) - - ## Sets SecondOrder flag - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetSecondOrder(self, theVal): - self.Parameters().SetSecondOrder(theVal) - - ## Sets Optimize flag - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetOptimize(self, theVal): - self.Parameters().SetOptimize(theVal) - - ## Sets Fineness - # @param theFineness is: - # VeryCoarse, Coarse, Moderate, Fine, VeryFine or Custom - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetFineness(self, theFineness): - self.Parameters().SetFineness(theFineness) - - ## Sets GrowthRate - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetGrowthRate(self, theRate): - self.Parameters().SetGrowthRate(theRate) - - ## Sets NbSegPerEdge - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetNbSegPerEdge(self, theVal): - self.Parameters().SetNbSegPerEdge(theVal) - - ## Sets NbSegPerRadius - # Parameter of FULL_NETGEN - # @ingroup l3_hypos_netgen - def SetNbSegPerRadius(self, theVal): - self.Parameters().SetNbSegPerRadius(theVal) - - ## Sets number of segments overriding value set by SetLocalLength() - # Only for algoType == NETGEN_FULL - # @ingroup l3_hypos_netgen - def SetNumberOfSegments(self, theVal): - self.Parameters(SIMPLE).SetNumberOfSegments(theVal) - - ## Sets number of segments overriding value set by SetNumberOfSegments() - # Only for algoType == NETGEN_FULL - # @ingroup l3_hypos_netgen - def SetLocalLength(self, theVal): - self.Parameters(SIMPLE).SetLocalLength(theVal) - - ## Defines "MaxElementArea" parameter of NETGEN_SimpleParameters_3D hypothesis. - # Overrides value set by LengthFromEdges() - # Only for algoType == NETGEN_FULL - # @ingroup l3_hypos_netgen - def MaxElementArea(self, area): - self.Parameters(SIMPLE).SetMaxElementArea(area) - - ## Defines "LengthFromEdges" parameter of NETGEN_SimpleParameters_3D hypothesis - # Overrides value set by MaxElementArea() - # Only for algoType == NETGEN_FULL - # @ingroup l3_hypos_netgen - def LengthFromEdges(self): - self.Parameters(SIMPLE).LengthFromEdges() - - ## Defines "LengthFromFaces" parameter of NETGEN_SimpleParameters_3D hypothesis - # Overrides value set by MaxElementVolume() - # Only for algoType == NETGEN_FULL - # @ingroup l3_hypos_netgen - def LengthFromFaces(self): - self.Parameters(SIMPLE).LengthFromFaces() - - ## To mesh "holes" in a solid or not. Default is to mesh. - # @ingroup l3_hypos_ghs3dh - def SetToMeshHoles(self, toMesh): - # Parameter of GHS3D - self.Parameters().SetToMeshHoles(toMesh) - - ## Set Optimization level: - # None_Optimization, Light_Optimization, Medium_Optimization, Strong_Optimization. - # Default is Medium_Optimization - # @ingroup l3_hypos_ghs3dh - def SetOptimizationLevel(self, level): - # Parameter of GHS3D - self.Parameters().SetOptimizationLevel(level) - - ## Maximal size of memory to be used by the algorithm (in Megabytes). - # @ingroup l3_hypos_ghs3dh - def SetMaximumMemory(self, MB): - # Advanced parameter of GHS3D - self.Parameters().SetMaximumMemory(MB) - - ## Initial size of memory to be used by the algorithm (in Megabytes) in - # automatic memory adjustment mode. - # @ingroup l3_hypos_ghs3dh - def SetInitialMemory(self, MB): - # Advanced parameter of GHS3D - self.Parameters().SetInitialMemory(MB) - - ## Path to working directory. - # @ingroup l3_hypos_ghs3dh - def SetWorkingDirectory(self, path): - # Advanced parameter of GHS3D - self.Parameters().SetWorkingDirectory(path) - - ## To keep working files or remove them. Log file remains in case of errors anyway. - # @ingroup l3_hypos_ghs3dh - def SetKeepFiles(self, toKeep): - # Advanced parameter of GHS3D and GHS3DPRL - self.Parameters().SetKeepFiles(toKeep) - - ## To set verbose level [0-10].
                      - #
                    • 0 - no standard output, - #
                    • 2 - prints the data, quality statistics of the skin and final meshes and - # indicates when the final mesh is being saved. In addition the software - # gives indication regarding the CPU time. - #
                    • 10 - same as 2 plus the main steps in the computation, quality statistics - # histogram of the skin mesh, quality statistics histogram together with - # the characteristics of the final mesh.
                    - # @ingroup l3_hypos_ghs3dh - def SetVerboseLevel(self, level): - # Advanced parameter of GHS3D - self.Parameters().SetVerboseLevel(level) - - ## To create new nodes. - # @ingroup l3_hypos_ghs3dh - def SetToCreateNewNodes(self, toCreate): - # Advanced parameter of GHS3D - self.Parameters().SetToCreateNewNodes(toCreate) - - ## To use boundary recovery version which tries to create mesh on a very poor - # quality surface mesh. - # @ingroup l3_hypos_ghs3dh - def SetToUseBoundaryRecoveryVersion(self, toUse): - # Advanced parameter of GHS3D - self.Parameters().SetToUseBoundaryRecoveryVersion(toUse) - - ## Sets command line option as text. - # @ingroup l3_hypos_ghs3dh - def SetTextOption(self, option): - # Advanced parameter of GHS3D - self.Parameters().SetTextOption(option) - - ## Sets MED files name and path. - def SetMEDName(self, value): - self.Parameters().SetMEDName(value) - - ## Sets the number of partition of the initial mesh - def SetNbPart(self, value): - self.Parameters().SetNbPart(value) - - ## When big mesh, start tepal in background - def SetBackground(self, value): - self.Parameters().SetBackground(value) - -# Public class: Mesh_Hexahedron -# ------------------------------ - -## Defines a hexahedron 3D algorithm -# -# @ingroup l3_algos_basic -class Mesh_Hexahedron(Mesh_Algorithm): - - params = 0 - algoType = 0 - - ## Private constructor. - def __init__(self, mesh, algoType=Hexa, geom=0): - Mesh_Algorithm.__init__(self) - - self.algoType = algoType - - if algoType == Hexa: - self.Create(mesh, geom, "Hexa_3D") - pass - - elif algoType == Hexotic: - import HexoticPlugin - self.Create(mesh, geom, "Hexotic_3D", "libHexoticEngine.so") - pass - - ## Defines "MinMaxQuad" hypothesis to give three hexotic parameters - # @ingroup l3_hypos_hexotic - def MinMaxQuad(self, min=3, max=8, quad=True): - self.params = self.Hypothesis("Hexotic_Parameters", [], "libHexoticEngine.so", - UseExisting=0) - self.params.SetHexesMinLevel(min) - self.params.SetHexesMaxLevel(max) - self.params.SetHexoticQuadrangles(quad) - return self.params - -# Deprecated, only for compatibility! -# Public class: Mesh_Netgen -# ------------------------------ - -## Defines a NETGEN-based 2D or 3D algorithm -# that needs no discrete boundary (i.e. independent) -# -# This class is deprecated, only for compatibility! -# -# More details. -# @ingroup l3_algos_basic -class Mesh_Netgen(Mesh_Algorithm): - - is3D = 0 - - ## Private constructor. - def __init__(self, mesh, is3D, geom=0): - Mesh_Algorithm.__init__(self) - - if noNETGENPlugin: - print "Warning: NETGENPlugin module has not been imported." - - self.is3D = is3D - if is3D: - self.Create(mesh, geom, "NETGEN_2D3D", "libNETGENEngine.so") - pass - - else: - self.Create(mesh, geom, "NETGEN_2D", "libNETGENEngine.so") - pass - - ## Defines the hypothesis containing parameters of the algorithm - def Parameters(self): - if self.is3D: - hyp = self.Hypothesis("NETGEN_Parameters", [], - "libNETGENEngine.so", UseExisting=0) - else: - hyp = self.Hypothesis("NETGEN_Parameters_2D", [], - "libNETGENEngine.so", UseExisting=0) - return hyp - -# Public class: Mesh_Projection1D -# ------------------------------ - -## Defines a projection 1D algorithm -# @ingroup l3_algos_proj -# -class Mesh_Projection1D(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Projection_1D") - - ## Defines "Source Edge" hypothesis, specifying a meshed edge, from where - # a mesh pattern is taken, and, optionally, the association of vertices - # between the source edge and a target edge (to which a hypothesis is assigned) - # @param edge from which nodes distribution is taken - # @param mesh from which nodes distribution is taken (optional) - # @param srcV a vertex of \a edge to associate with \a tgtV (optional) - # @param tgtV a vertex of \a the edge to which the algorithm is assigned, - # to associate with \a srcV (optional) - # @param UseExisting if ==true - searches for the existing hypothesis created with - # the same parameters, else (default) - creates a new one - def SourceEdge(self, edge, mesh=None, srcV=None, tgtV=None, UseExisting=0): - hyp = self.Hypothesis("ProjectionSource1D", [edge,mesh,srcV,tgtV], - UseExisting=0) - #UseExisting=UseExisting, CompareMethod=self.CompareSourceEdge) - hyp.SetSourceEdge( edge ) - if not mesh is None and isinstance(mesh, Mesh): - mesh = mesh.GetMesh() - hyp.SetSourceMesh( mesh ) - hyp.SetVertexAssociation( srcV, tgtV ) - return hyp - - ## Checks if the given "SourceEdge" hypothesis has the same parameters as the given arguments - #def CompareSourceEdge(self, hyp, args): - # # it does not seem to be useful to reuse the existing "SourceEdge" hypothesis - # return False - - -# Public class: Mesh_Projection2D -# ------------------------------ - -## Defines a projection 2D algorithm -# @ingroup l3_algos_proj -# -class Mesh_Projection2D(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Projection_2D") - - ## Defines "Source Face" hypothesis, specifying a meshed face, from where - # a mesh pattern is taken, and, optionally, the association of vertices - # between the source face and the target face (to which a hypothesis is assigned) - # @param face from which the mesh pattern is taken - # @param mesh from which the mesh pattern is taken (optional) - # @param srcV1 a vertex of \a face to associate with \a tgtV1 (optional) - # @param tgtV1 a vertex of \a the face to which the algorithm is assigned, - # to associate with \a srcV1 (optional) - # @param srcV2 a vertex of \a face to associate with \a tgtV1 (optional) - # @param tgtV2 a vertex of \a the face to which the algorithm is assigned, - # to associate with \a srcV2 (optional) - # @param UseExisting if ==true - forces the search for the existing hypothesis created with - # the same parameters, else (default) - forces the creation a new one - # - # Note: all association vertices must belong to one edge of a face - def SourceFace(self, face, mesh=None, srcV1=None, tgtV1=None, - srcV2=None, tgtV2=None, UseExisting=0): - hyp = self.Hypothesis("ProjectionSource2D", [face,mesh,srcV1,tgtV1,srcV2,tgtV2], - UseExisting=0) - #UseExisting=UseExisting, CompareMethod=self.CompareSourceFace) - hyp.SetSourceFace( face ) - if not mesh is None and isinstance(mesh, Mesh): - mesh = mesh.GetMesh() - hyp.SetSourceMesh( mesh ) - hyp.SetVertexAssociation( srcV1, srcV2, tgtV1, tgtV2 ) - return hyp - - ## Checks if the given "SourceFace" hypothesis has the same parameters as the given arguments - #def CompareSourceFace(self, hyp, args): - # # it does not seem to be useful to reuse the existing "SourceFace" hypothesis - # return False - -# Public class: Mesh_Projection3D -# ------------------------------ - -## Defines a projection 3D algorithm -# @ingroup l3_algos_proj -# -class Mesh_Projection3D(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Projection_3D") - - ## Defines the "Source Shape 3D" hypothesis, specifying a meshed solid, from where - # the mesh pattern is taken, and, optionally, the association of vertices - # between the source and the target solid (to which a hipothesis is assigned) - # @param solid from where the mesh pattern is taken - # @param mesh from where the mesh pattern is taken (optional) - # @param srcV1 a vertex of \a solid to associate with \a tgtV1 (optional) - # @param tgtV1 a vertex of \a the solid where the algorithm is assigned, - # to associate with \a srcV1 (optional) - # @param srcV2 a vertex of \a solid to associate with \a tgtV1 (optional) - # @param tgtV2 a vertex of \a the solid to which the algorithm is assigned, - # to associate with \a srcV2 (optional) - # @param UseExisting - if ==true - searches for the existing hypothesis created with - # the same parameters, else (default) - creates a new one - # - # Note: association vertices must belong to one edge of a solid - def SourceShape3D(self, solid, mesh=0, srcV1=0, tgtV1=0, - srcV2=0, tgtV2=0, UseExisting=0): - hyp = self.Hypothesis("ProjectionSource3D", - [solid,mesh,srcV1,tgtV1,srcV2,tgtV2], - UseExisting=0) - #UseExisting=UseExisting, CompareMethod=self.CompareSourceShape3D) - hyp.SetSource3DShape( solid ) - if not mesh is None and isinstance(mesh, Mesh): - mesh = mesh.GetMesh() - hyp.SetSourceMesh( mesh ) - hyp.SetVertexAssociation( srcV1, srcV2, tgtV1, tgtV2 ) - return hyp - - ## Checks if the given "SourceShape3D" hypothesis has the same parameters as given arguments - #def CompareSourceShape3D(self, hyp, args): - # # seems to be not really useful to reuse existing "SourceShape3D" hypothesis - # return False - - -# Public class: Mesh_Prism -# ------------------------ - -## Defines a 3D extrusion algorithm -# @ingroup l3_algos_3dextr -# -class Mesh_Prism3D(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "Prism_3D") - -# Public class: Mesh_RadialPrism -# ------------------------------- - -## Defines a Radial Prism 3D algorithm -# @ingroup l3_algos_radialp -# -class Mesh_RadialPrism3D(Mesh_Algorithm): - - ## Private constructor. - def __init__(self, mesh, geom=0): - Mesh_Algorithm.__init__(self) - self.Create(mesh, geom, "RadialPrism_3D") - - self.distribHyp = self.Hypothesis("LayerDistribution", UseExisting=0) - self.nbLayers = None - - ## Return 3D hypothesis holding the 1D one - def Get3DHypothesis(self): - return self.distribHyp - - ## Private method creating a 1D hypothesis and storing it in the LayerDistribution - # hypothesis. Returns the created hypothesis - def OwnHypothesis(self, hypType, args=[], so="libStdMeshersEngine.so"): - #print "OwnHypothesis",hypType - if not self.nbLayers is None: - self.mesh.GetMesh().RemoveHypothesis( self.geom, self.nbLayers ) - self.mesh.GetMesh().AddHypothesis( self.geom, self.distribHyp ) - study = self.mesh.smeshpyD.GetCurrentStudy() # prevents publishing own 1D hypothesis - hyp = self.mesh.smeshpyD.CreateHypothesis(hypType, so) - self.mesh.smeshpyD.SetCurrentStudy( study ) # enables publishing - self.distribHyp.SetLayerDistribution( hyp ) - return hyp - - ## Defines "NumberOfLayers" hypothesis, specifying the number of layers of - # prisms to build between the inner and outer shells - # @param n number of layers - # @param UseExisting if ==true - searches for the existing hypothesis created with - # the same parameters, else (default) - creates a new one - def NumberOfLayers(self, n, UseExisting=0): - self.mesh.GetMesh().RemoveHypothesis( self.geom, self.distribHyp ) - self.nbLayers = self.Hypothesis("NumberOfLayers", [n], UseExisting=UseExisting, - CompareMethod=self.CompareNumberOfLayers) - self.nbLayers.SetNumberOfLayers( n ) - return self.nbLayers - - ## Checks if the given "NumberOfLayers" hypothesis has the same parameters as the given arguments - def CompareNumberOfLayers(self, hyp, args): - return IsEqual(hyp.GetNumberOfLayers(), args[0]) - - ## Defines "LocalLength" hypothesis, specifying the segment length - # to build between the inner and the outer shells - # @param l the length of segments - # @param p the precision of rounding - def LocalLength(self, l, p=1e-07): - hyp = self.OwnHypothesis("LocalLength", [l,p]) - hyp.SetLength(l) - hyp.SetPrecision(p) - return hyp - - ## Defines "NumberOfSegments" hypothesis, specifying the number of layers of - # prisms to build between the inner and the outer shells. - # @param n the number of layers - # @param s the scale factor (optional) - def NumberOfSegments(self, n, s=[]): - if s == []: - hyp = self.OwnHypothesis("NumberOfSegments", [n]) - else: - hyp = self.OwnHypothesis("NumberOfSegments", [n,s]) - hyp.SetDistrType( 1 ) - hyp.SetScaleFactor(s) - hyp.SetNumberOfSegments(n) - return hyp - - ## Defines "Arithmetic1D" hypothesis, specifying the distribution of segments - # to build between the inner and the outer shells with a length that changes in arithmetic progression - # @param start the length of the first segment - # @param end the length of the last segment - def Arithmetic1D(self, start, end ): - hyp = self.OwnHypothesis("Arithmetic1D", [start, end]) - hyp.SetLength(start, 1) - hyp.SetLength(end , 0) - return hyp - - ## Defines "StartEndLength" hypothesis, specifying distribution of segments - # to build between the inner and the outer shells as geometric length increasing - # @param start for the length of the first segment - # @param end for the length of the last segment - def StartEndLength(self, start, end): - hyp = self.OwnHypothesis("StartEndLength", [start, end]) - hyp.SetLength(start, 1) - hyp.SetLength(end , 0) - return hyp - - ## Defines "AutomaticLength" hypothesis, specifying the number of segments - # to build between the inner and outer shells - # @param fineness defines the quality of the mesh within the range [0-1] - def AutomaticLength(self, fineness=0): - hyp = self.OwnHypothesis("AutomaticLength") - hyp.SetFineness( fineness ) - return hyp - -# Private class: Mesh_UseExisting -# ------------------------------- -class Mesh_UseExisting(Mesh_Algorithm): - - def __init__(self, dim, mesh, geom=0): - if dim == 1: - self.Create(mesh, geom, "UseExisting_1D") - else: - self.Create(mesh, geom, "UseExisting_2D") - - -import salome_notebook -notebook = salome_notebook.notebook - -##Return values of the notebook variables -def ParseParameters(last, nbParams,nbParam, value): - result = None - strResult = "" - counter = 0 - listSize = len(last) - for n in range(0,nbParams): - if n+1 != nbParam: - if counter < listSize: - strResult = strResult + last[counter] - else: - strResult = strResult + "" - else: - if isinstance(value, str): - if notebook.isVariable(value): - result = notebook.get(value) - strResult=strResult+value - else: - raise RuntimeError, "Variable with name '" + value + "' doesn't exist!!!" - else: - strResult=strResult+str(value) - result = value - if nbParams - 1 != counter: - strResult=strResult+var_separator #":" - counter = counter+1 - return result, strResult - -#Wrapper class for StdMeshers_LocalLength hypothesis -class LocalLength(StdMeshers._objref_StdMeshers_LocalLength): - - ## Set Length parameter value - # @param length numerical value or name of variable from notebook - def SetLength(self, length): - length,parameters = ParseParameters(StdMeshers._objref_StdMeshers_LocalLength.GetLastParameters(self),2,1,length) - StdMeshers._objref_StdMeshers_LocalLength.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_LocalLength.SetLength(self,length) - - ## Set Precision parameter value - # @param precision numerical value or name of variable from notebook - def SetPrecision(self, precision): - precision,parameters = ParseParameters(StdMeshers._objref_StdMeshers_LocalLength.GetLastParameters(self),2,2,precision) - StdMeshers._objref_StdMeshers_LocalLength.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_LocalLength.SetPrecision(self, precision) - -#Registering the new proxy for LocalLength -omniORB.registerObjref(StdMeshers._objref_StdMeshers_LocalLength._NP_RepositoryId, LocalLength) - - -#Wrapper class for StdMeshers_LayerDistribution hypothesis -class LayerDistribution(StdMeshers._objref_StdMeshers_LayerDistribution): - - def SetLayerDistribution(self, hypo): - StdMeshers._objref_StdMeshers_LayerDistribution.SetParameters(self,hypo.GetParameters()) - hypo.ClearParameters(); - StdMeshers._objref_StdMeshers_LayerDistribution.SetLayerDistribution(self,hypo) - -#Registering the new proxy for LayerDistribution -omniORB.registerObjref(StdMeshers._objref_StdMeshers_LayerDistribution._NP_RepositoryId, LayerDistribution) - -#Wrapper class for StdMeshers_SegmentLengthAroundVertex hypothesis -class SegmentLengthAroundVertex(StdMeshers._objref_StdMeshers_SegmentLengthAroundVertex): - - ## Set Length parameter value - # @param length numerical value or name of variable from notebook - def SetLength(self, length): - length,parameters = ParseParameters(StdMeshers._objref_StdMeshers_SegmentLengthAroundVertex.GetLastParameters(self),1,1,length) - StdMeshers._objref_StdMeshers_SegmentLengthAroundVertex.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_SegmentLengthAroundVertex.SetLength(self,length) - -#Registering the new proxy for SegmentLengthAroundVertex -omniORB.registerObjref(StdMeshers._objref_StdMeshers_SegmentLengthAroundVertex._NP_RepositoryId, SegmentLengthAroundVertex) - - -#Wrapper class for StdMeshers_Arithmetic1D hypothesis -class Arithmetic1D(StdMeshers._objref_StdMeshers_Arithmetic1D): - - ## Set Length parameter value - # @param length numerical value or name of variable from notebook - # @param isStart true is length is Start Length, otherwise false - def SetLength(self, length, isStart): - nb = 2 - if isStart: - nb = 1 - length,parameters = ParseParameters(StdMeshers._objref_StdMeshers_Arithmetic1D.GetLastParameters(self),2,nb,length) - StdMeshers._objref_StdMeshers_Arithmetic1D.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_Arithmetic1D.SetLength(self,length,isStart) - -#Registering the new proxy for Arithmetic1D -omniORB.registerObjref(StdMeshers._objref_StdMeshers_Arithmetic1D._NP_RepositoryId, Arithmetic1D) - -#Wrapper class for StdMeshers_Deflection1D hypothesis -class Deflection1D(StdMeshers._objref_StdMeshers_Deflection1D): - - ## Set Deflection parameter value - # @param deflection numerical value or name of variable from notebook - def SetDeflection(self, deflection): - deflection,parameters = ParseParameters(StdMeshers._objref_StdMeshers_Deflection1D.GetLastParameters(self),1,1,deflection) - StdMeshers._objref_StdMeshers_Deflection1D.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_Deflection1D.SetDeflection(self,deflection) - -#Registering the new proxy for Deflection1D -omniORB.registerObjref(StdMeshers._objref_StdMeshers_Deflection1D._NP_RepositoryId, Deflection1D) - -#Wrapper class for StdMeshers_StartEndLength hypothesis -class StartEndLength(StdMeshers._objref_StdMeshers_StartEndLength): - - ## Set Length parameter value - # @param length numerical value or name of variable from notebook - # @param isStart true is length is Start Length, otherwise false - def SetLength(self, length, isStart): - nb = 2 - if isStart: - nb = 1 - length,parameters = ParseParameters(StdMeshers._objref_StdMeshers_StartEndLength.GetLastParameters(self),2,nb,length) - StdMeshers._objref_StdMeshers_StartEndLength.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_StartEndLength.SetLength(self,length,isStart) - -#Registering the new proxy for StartEndLength -omniORB.registerObjref(StdMeshers._objref_StdMeshers_StartEndLength._NP_RepositoryId, StartEndLength) - -#Wrapper class for StdMeshers_MaxElementArea hypothesis -class MaxElementArea(StdMeshers._objref_StdMeshers_MaxElementArea): - - ## Set Max Element Area parameter value - # @param area numerical value or name of variable from notebook - def SetMaxElementArea(self, area): - area ,parameters = ParseParameters(StdMeshers._objref_StdMeshers_MaxElementArea.GetLastParameters(self),1,1,area) - StdMeshers._objref_StdMeshers_MaxElementArea.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_MaxElementArea.SetMaxElementArea(self,area) - -#Registering the new proxy for MaxElementArea -omniORB.registerObjref(StdMeshers._objref_StdMeshers_MaxElementArea._NP_RepositoryId, MaxElementArea) - - -#Wrapper class for StdMeshers_MaxElementVolume hypothesis -class MaxElementVolume(StdMeshers._objref_StdMeshers_MaxElementVolume): - - ## Set Max Element Volume parameter value - # @param area numerical value or name of variable from notebook - def SetMaxElementVolume(self, volume): - volume ,parameters = ParseParameters(StdMeshers._objref_StdMeshers_MaxElementVolume.GetLastParameters(self),1,1,volume) - StdMeshers._objref_StdMeshers_MaxElementVolume.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_MaxElementVolume.SetMaxElementVolume(self,volume) - -#Registering the new proxy for MaxElementVolume -omniORB.registerObjref(StdMeshers._objref_StdMeshers_MaxElementVolume._NP_RepositoryId, MaxElementVolume) - - -#Wrapper class for StdMeshers_NumberOfLayers hypothesis -class NumberOfLayers(StdMeshers._objref_StdMeshers_NumberOfLayers): - - ## Set Number Of Layers parameter value - # @param nbLayers numerical value or name of variable from notebook - def SetNumberOfLayers(self, nbLayers): - nbLayers ,parameters = ParseParameters(StdMeshers._objref_StdMeshers_NumberOfLayers.GetLastParameters(self),1,1,nbLayers) - StdMeshers._objref_StdMeshers_NumberOfLayers.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_NumberOfLayers.SetNumberOfLayers(self,nbLayers) - -#Registering the new proxy for NumberOfLayers -omniORB.registerObjref(StdMeshers._objref_StdMeshers_NumberOfLayers._NP_RepositoryId, NumberOfLayers) - -#Wrapper class for StdMeshers_NumberOfSegments hypothesis -class NumberOfSegments(StdMeshers._objref_StdMeshers_NumberOfSegments): - - ## Set Number Of Segments parameter value - # @param nbSeg numerical value or name of variable from notebook - def SetNumberOfSegments(self, nbSeg): - lastParameters = StdMeshers._objref_StdMeshers_NumberOfSegments.GetLastParameters(self) - nbSeg , parameters = ParseParameters(lastParameters,1,1,nbSeg) - StdMeshers._objref_StdMeshers_NumberOfSegments.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_NumberOfSegments.SetNumberOfSegments(self,nbSeg) - - ## Set Scale Factor parameter value - # @param factor numerical value or name of variable from notebook - def SetScaleFactor(self, factor): - factor, parameters = ParseParameters(StdMeshers._objref_StdMeshers_NumberOfSegments.GetLastParameters(self),2,2,factor) - StdMeshers._objref_StdMeshers_NumberOfSegments.SetParameters(self,parameters) - StdMeshers._objref_StdMeshers_NumberOfSegments.SetScaleFactor(self,factor) - -#Registering the new proxy for NumberOfSegments -omniORB.registerObjref(StdMeshers._objref_StdMeshers_NumberOfSegments._NP_RepositoryId, NumberOfSegments) - - -#Wrapper class for NETGENPlugin_Hypothesis hypothesis -class NETGENPlugin_Hypothesis(NETGENPlugin._objref_NETGENPlugin_Hypothesis): - - ## Set Max Size parameter value - # @param maxsize numerical value or name of variable from notebook - def SetMaxSize(self, maxsize): - lastParameters = NETGENPlugin._objref_NETGENPlugin_Hypothesis.GetLastParameters(self) - maxsize, parameters = ParseParameters(lastParameters,4,1,maxsize) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetMaxSize(self,maxsize) - - ## Set Growth Rate parameter value - # @param value numerical value or name of variable from notebook - def SetGrowthRate(self, value): - lastParameters = NETGENPlugin._objref_NETGENPlugin_Hypothesis.GetLastParameters(self) - value, parameters = ParseParameters(lastParameters,4,2,value) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetGrowthRate(self,value) - - ## Set Number of Segments per Edge parameter value - # @param value numerical value or name of variable from notebook - def SetNbSegPerEdge(self, value): - lastParameters = NETGENPlugin._objref_NETGENPlugin_Hypothesis.GetLastParameters(self) - value, parameters = ParseParameters(lastParameters,4,3,value) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetNbSegPerEdge(self,value) - - ## Set Number of Segments per Radius parameter value - # @param value numerical value or name of variable from notebook - def SetNbSegPerRadius(self, value): - lastParameters = NETGENPlugin._objref_NETGENPlugin_Hypothesis.GetLastParameters(self) - value, parameters = ParseParameters(lastParameters,4,4,value) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_Hypothesis.SetNbSegPerRadius(self,value) - -#Registering the new proxy for NETGENPlugin_Hypothesis -omniORB.registerObjref(NETGENPlugin._objref_NETGENPlugin_Hypothesis._NP_RepositoryId, NETGENPlugin_Hypothesis) - - -#Wrapper class for NETGENPlugin_Hypothesis_2D hypothesis -class NETGENPlugin_Hypothesis_2D(NETGENPlugin_Hypothesis,NETGENPlugin._objref_NETGENPlugin_Hypothesis_2D): - pass - -#Registering the new proxy for NETGENPlugin_Hypothesis_2D -omniORB.registerObjref(NETGENPlugin._objref_NETGENPlugin_Hypothesis_2D._NP_RepositoryId, NETGENPlugin_Hypothesis_2D) - -#Wrapper class for NETGENPlugin_SimpleHypothesis_2D hypothesis -class NETGEN_SimpleParameters_2D(NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D): - - ## Set Number of Segments parameter value - # @param nbSeg numerical value or name of variable from notebook - def SetNumberOfSegments(self, nbSeg): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.GetLastParameters(self) - nbSeg, parameters = ParseParameters(lastParameters,2,1,nbSeg) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetNumberOfSegments(self, nbSeg) - - ## Set Local Length parameter value - # @param length numerical value or name of variable from notebook - def SetLocalLength(self, length): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.GetLastParameters(self) - length, parameters = ParseParameters(lastParameters,2,1,length) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetLocalLength(self, length) - - ## Set Max Element Area parameter value - # @param area numerical value or name of variable from notebook - def SetMaxElementArea(self, area): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.GetLastParameters(self) - area, parameters = ParseParameters(lastParameters,2,2,area) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetMaxElementArea(self, area) - - def LengthFromEdges(self): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.GetLastParameters(self) - value = 0; - value, parameters = ParseParameters(lastParameters,2,2,value) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D.LengthFromEdges(self) - -#Registering the new proxy for NETGEN_SimpleParameters_2D -omniORB.registerObjref(NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_2D._NP_RepositoryId, NETGEN_SimpleParameters_2D) - - -#Wrapper class for NETGENPlugin_SimpleHypothesis_3D hypothesis -class NETGEN_SimpleParameters_3D(NETGEN_SimpleParameters_2D,NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D): - ## Set Max Element Volume parameter value - # @param volume numerical value or name of variable from notebook - def SetMaxElementVolume(self, volume): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.GetLastParameters(self) - volume, parameters = ParseParameters(lastParameters,3,3,volume) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.SetMaxElementVolume(self, volume) - - def LengthFromFaces(self): - lastParameters = NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.GetLastParameters(self) - value = 0; - value, parameters = ParseParameters(lastParameters,3,3,value) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.SetParameters(self,parameters) - NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D.LengthFromFaces(self) - -#Registering the new proxy for NETGEN_SimpleParameters_3D -omniORB.registerObjref(NETGENPlugin._objref_NETGENPlugin_SimpleHypothesis_3D._NP_RepositoryId, NETGEN_SimpleParameters_3D) - -class Pattern(SMESH._objref_SMESH_Pattern): +# Private class used to substitute and store variable parameters of hypotheses. +class hypMethodWrapper: + def __init__(self, hyp, method): + self.hyp = hyp + self.method = method + #print "REBIND:", method.__name__ + return - def ApplyToMeshFaces(self, theMesh, theFacesIDs, theNodeIndexOnKeyPoint1, theReverse): - flag = False - if isinstance(theNodeIndexOnKeyPoint1,str): - flag = True - theNodeIndexOnKeyPoint1,Parameters = geompyDC.ParseParameters(theNodeIndexOnKeyPoint1) - if flag: - theNodeIndexOnKeyPoint1 -= 1 - theMesh.SetParameters(Parameters) - return SMESH._objref_SMESH_Pattern.ApplyToMeshFaces( self, theMesh, theFacesIDs, theNodeIndexOnKeyPoint1, theReverse ) + # call a method of hypothesis with calling SetVarParameter() before + def __call__(self,*args): + if not args: + return self.method( self.hyp, *args ) # hypothesis method with no args - def ApplyToHexahedrons(self, theMesh, theVolumesIDs, theNode000Index, theNode001Index): - flag0 = False - flag1 = False - if isinstance(theNode000Index,str): - flag0 = True - if isinstance(theNode001Index,str): - flag1 = True - theNode000Index,theNode001Index,Parameters = geompyDC.ParseParameters(theNode000Index,theNode001Index) - if flag0: - theNode000Index -= 1 - if flag1: - theNode001Index -= 1 - theMesh.SetParameters(Parameters) - return SMESH._objref_SMESH_Pattern.ApplyToHexahedrons( self, theMesh, theVolumesIDs, theNode000Index, theNode001Index ) + #print "MethWrapper.__call__",self.method.__name__, args + try: + parsed = ParseParameters(*args) # replace variables with their values + self.hyp.SetVarParameter( parsed[-2], self.method.__name__ ) + result = self.method( self.hyp, *parsed[:-2] ) # call hypothesis method + except omniORB.CORBA.BAD_PARAM: # raised by hypothesis method call + # maybe there is a replaced string arg which is not variable + result = self.method( self.hyp, *args ) + except ValueError, detail: # raised by ParseParameters() + try: + result = self.method( self.hyp, *args ) + except omniORB.CORBA.BAD_PARAM: + raise ValueError, detail # wrong variable name -#Registering the new proxy for Pattern -omniORB.registerObjref(SMESH._objref_SMESH_Pattern._NP_RepositoryId, Pattern) + return result diff --git a/src/SMESH_SWIG_WITHIHM/Makefile.am b/src/SMESH_SWIG_WITHIHM/Makefile.am index 69ae00da9..ee0248ac1 100644 --- a/src/SMESH_SWIG_WITHIHM/Makefile.am +++ b/src/SMESH_SWIG_WITHIHM/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH SMESHGUI : GUI for SMESH component # File : Makefile.am # Author : Alexander BORODIN, Open CASCADE S.A.S. @@ -71,9 +69,9 @@ _libSMESH_Swig_la_CPPFLAGS = \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ $(BOOST_CPPFLAGS) \ + -I$(srcdir) \ -I$(srcdir)/../SMESHGUI \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix + -I$(top_builddir)/idl _libSMESH_Swig_la_LDFLAGS = -module _libSMESH_Swig_la_LIBADD = ../SMESHGUI/libSMESH.la \ diff --git a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.cxx b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.cxx index 4766864a2..eca22e91e 100644 --- a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.cxx +++ b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : GUI for SMESH component // File : libSMESH_Swig.cxx // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -71,10 +69,10 @@ namespace inline SALOMEDS::SObject_var GetDomainRoot(const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder, - CORBA::Long theDomainRootTag, - const QString& theName, - const QString& thePixmap) + const SALOMEDS::StudyBuilder_var& theStudyBuilder, + CORBA::Long theDomainRootTag, + const QString& theName, + const QString& thePixmap) { SALOMEDS::SObject_var aDomainRoot; if (!theSComponentMesh->FindSubObject(theDomainRootTag,aDomainRoot)) { @@ -98,13 +96,13 @@ namespace inline SALOMEDS::SObject_var GetHypothesisRoot(const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { return GetDomainRoot(theSComponentMesh, - theStudyBuilder, - SMESH::Tag_HypothesisRoot, - QObject::tr("SMESH_MEN_HYPOTHESIS"), - "ICON_SMESH_TREE_HYPO"); + theStudyBuilder, + SMESH::Tag_HypothesisRoot, + QObject::tr("SMESH_MEN_HYPOTHESIS"), + "ICON_SMESH_TREE_HYPO"); } @@ -112,13 +110,13 @@ namespace inline SALOMEDS::SObject_var GetAlgorithmsRoot(const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { return GetDomainRoot(theSComponentMesh, - theStudyBuilder, - SMESH::Tag_AlgorithmsRoot, - QObject::tr("SMESH_MEN_ALGORITHMS"), - "ICON_SMESH_TREE_ALGO"); + theStudyBuilder, + SMESH::Tag_AlgorithmsRoot, + QObject::tr("SMESH_MEN_ALGORITHMS"), + "ICON_SMESH_TREE_ALGO"); } @@ -126,17 +124,17 @@ namespace inline SALOMEDS::SObject_var AddToDomain(const std::string& theIOR, - const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder, - CORBA::Long theDomainRootTag, - const QString& theDomainName, - const QString& theDomainPixmap) + const SALOMEDS::SComponent_var& theSComponentMesh, + const SALOMEDS::StudyBuilder_var& theStudyBuilder, + CORBA::Long theDomainRootTag, + const QString& theDomainName, + const QString& theDomainPixmap) { SALOMEDS::SObject_var aDomain = GetDomainRoot(theSComponentMesh, - theStudyBuilder, - SMESH::Tag_AlgorithmsRoot, - theDomainName, - theDomainPixmap); + theStudyBuilder, + SMESH::Tag_AlgorithmsRoot, + theDomainName, + theDomainPixmap); // Add New Hypothesis SALOMEDS::SObject_var aSObject = theStudyBuilder->NewObject(aDomain); SALOMEDS::GenericAttribute_var anAttr = theStudyBuilder->FindOrCreateAttribute(aSObject,"AttributePixMap"); @@ -157,42 +155,42 @@ namespace //--------------------------------------------------------------- SALOMEDS::SObject_var AddHypothesis(const std::string& theIOR, - const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const SALOMEDS::SComponent_var& theSComponentMesh, + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { return AddToDomain(theIOR, - theSComponentMesh, - theStudyBuilder, - SMESH::Tag_HypothesisRoot, - QObject::tr("SMESH_MEN_HYPOTHESIS"), - "ICON_SMESH_TREE_HYPO"); + theSComponentMesh, + theStudyBuilder, + SMESH::Tag_HypothesisRoot, + QObject::tr("SMESH_MEN_HYPOTHESIS"), + "ICON_SMESH_TREE_HYPO"); } //--------------------------------------------------------------- SALOMEDS::SObject_var AddAlgorithms(const std::string& theIOR, - const SALOMEDS::SComponent_var& theSComponentMesh, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const SALOMEDS::SComponent_var& theSComponentMesh, + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { return AddToDomain(theIOR, - theSComponentMesh, - theStudyBuilder, - SMESH::Tag_AlgorithmsRoot, - QObject::tr("SMESH_MEN_ALGORITHMS"), - "ICON_SMESH_TREE_ALGO"); + theSComponentMesh, + theStudyBuilder, + SMESH::Tag_AlgorithmsRoot, + QObject::tr("SMESH_MEN_ALGORITHMS"), + "ICON_SMESH_TREE_ALGO"); } //--------------------------------------------------------------- void SetDomain(const char* theMeshOrSubMeshEntry, - const char* theDomainEntry, - const SALOMEDS::Study_var& theStudy, - const SALOMEDS::StudyBuilder_var& theStudyBuilder, - long theRefOnAppliedDomainTag, - const QString& theAppliedDomainMEN, - const QString& theAppliedDomainICON) + const char* theDomainEntry, + const SALOMEDS::Study_var& theStudy, + const SALOMEDS::StudyBuilder_var& theStudyBuilder, + long theRefOnAppliedDomainTag, + const QString& theAppliedDomainMEN, + const QString& theAppliedDomainICON) { SALOMEDS::SObject_var aMeshOrSubMeshSO = theStudy->FindObjectID(theMeshOrSubMeshEntry); SALOMEDS::SObject_var aHypothesisSO = theStudy->FindObjectID(theDomainEntry); @@ -201,17 +199,17 @@ namespace //Find or Create Applied Hypothesis root SALOMEDS::SObject_var anAppliedDomainSO; if(!aMeshOrSubMeshSO->FindSubObject(theRefOnAppliedDomainTag,anAppliedDomainSO)){ - anAppliedDomainSO = theStudyBuilder->NewObjectToTag(aMeshOrSubMeshSO,theRefOnAppliedDomainTag); - SALOMEDS::GenericAttribute_var anAttr = - theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributeName"); - SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow(anAttr); - aName->SetValue(theAppliedDomainMEN.toLatin1().data()); - anAttr = theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributeSelectable"); - SALOMEDS::AttributeSelectable_var aSelAttr = SALOMEDS::AttributeSelectable::_narrow(anAttr); - aSelAttr->SetSelectable(false); - anAttr = theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributePixMap"); - SALOMEDS::AttributePixMap_var aPixmap = SALOMEDS::AttributePixMap::_narrow(anAttr); - aPixmap->SetPixMap(theAppliedDomainICON.toLatin1().data()); + anAppliedDomainSO = theStudyBuilder->NewObjectToTag(aMeshOrSubMeshSO,theRefOnAppliedDomainTag); + SALOMEDS::GenericAttribute_var anAttr = + theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributeName"); + SALOMEDS::AttributeName_var aName = SALOMEDS::AttributeName::_narrow(anAttr); + aName->SetValue(theAppliedDomainMEN.toLatin1().data()); + anAttr = theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributeSelectable"); + SALOMEDS::AttributeSelectable_var aSelAttr = SALOMEDS::AttributeSelectable::_narrow(anAttr); + aSelAttr->SetSelectable(false); + anAttr = theStudyBuilder->FindOrCreateAttribute(anAppliedDomainSO,"AttributePixMap"); + SALOMEDS::AttributePixMap_var aPixmap = SALOMEDS::AttributePixMap::_narrow(anAttr); + aPixmap->SetPixMap(theAppliedDomainICON.toLatin1().data()); } SALOMEDS::SObject_var aSObject = theStudyBuilder->NewObject(anAppliedDomainSO); theStudyBuilder->Addreference(aSObject,aHypothesisSO); @@ -222,34 +220,34 @@ namespace //--------------------------------------------------------------- void SetHypothesis(const char* theMeshOrSubMeshEntry, - const char* theDomainEntry, - const SALOMEDS::Study_var& theStudy, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const char* theDomainEntry, + const SALOMEDS::Study_var& theStudy, + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { SetDomain(theMeshOrSubMeshEntry, - theDomainEntry, - theStudy, - theStudyBuilder, - SMESH::Tag_RefOnAppliedHypothesis, - QObject::tr("SMESH_MEN_APPLIED_HYPOTHESIS"), - "ICON_SMESH_TREE_HYPO"); + theDomainEntry, + theStudy, + theStudyBuilder, + SMESH::Tag_RefOnAppliedHypothesis, + QObject::tr("SMESH_MEN_APPLIED_HYPOTHESIS"), + "ICON_SMESH_TREE_HYPO"); } //--------------------------------------------------------------- void SetAlgorithms(const char* theMeshOrSubMeshEntry, - const char* theDomainEntry, - const SALOMEDS::Study_var& theStudy, - const SALOMEDS::StudyBuilder_var& theStudyBuilder) + const char* theDomainEntry, + const SALOMEDS::Study_var& theStudy, + const SALOMEDS::StudyBuilder_var& theStudyBuilder) { SetDomain(theMeshOrSubMeshEntry, - theDomainEntry, - theStudy, - theStudyBuilder, - SMESH::Tag_RefOnAppliedAlgorithms, - QObject::tr("SMESH_MEN_APPLIED_ALGORIHTMS"), - "ICON_SMESH_TREE_ALGO"); + theDomainEntry, + theStudy, + theStudyBuilder, + SMESH::Tag_RefOnAppliedAlgorithms, + QObject::tr("SMESH_MEN_APPLIED_ALGORIHTMS"), + "ICON_SMESH_TREE_ALGO"); } } @@ -271,11 +269,11 @@ SMESH_Swig::SMESH_Swig() Execute() { try { - ORB_INIT &anORBInit = *SINGLETON_::Instance(); - ASSERT(SINGLETON_::IsAlreadyExisting()); - myORB = anORBInit( 0, 0 ); + ORB_INIT &anORBInit = *SINGLETON_::Instance(); + ASSERT(SINGLETON_::IsAlreadyExisting()); + myORB = anORBInit( 0, 0 ); } catch (...) { - INFOS("internal error : orb not found"); + INFOS("internal error : orb not found"); } } }; @@ -301,9 +299,9 @@ SMESH_Swig::Init(int theStudyID) SALOMEDS::SComponent_var& mySComponentMesh; public: TEvent(int theStudyID, - SALOMEDS::Study_var& theStudy, - SALOMEDS::StudyBuilder_var& theStudyBuilder, - SALOMEDS::SComponent_var& theSComponentMesh): + SALOMEDS::Study_var& theStudy, + SALOMEDS::StudyBuilder_var& theStudyBuilder, + SALOMEDS::SComponent_var& theSComponentMesh): myStudyID(theStudyID), myStudy(theStudy), myStudyBuilder(theStudyBuilder), @@ -334,28 +332,28 @@ SMESH_Swig::Init(int theStudyID) SALOMEDS::SComponent_var aSComponent = myStudy->FindComponent("SMESH"); if(aSComponent->_is_nil()){ - bool aLocked = myStudy->GetProperties()->IsLocked(); - if (aLocked) - myStudy->GetProperties()->SetLocked(false); - - aSComponent = myStudyBuilder->NewComponent("SMESH"); - anAttr = myStudyBuilder->FindOrCreateAttribute(aSComponent,"AttributeName"); - aName = SALOMEDS::AttributeName::_narrow(anAttr); - - SMESHGUI* aSMESHGUI = SMESHGUI::GetSMESHGUI(); //SRN: BugID IPAL9186, load a SMESH gui if it hasn't been loaded - if (!aSMESHGUI){ - CAM_Module* aModule = anApp->module("Mesh"); - if(!aModule) - aModule = anApp->loadModule("Mesh"); - aSMESHGUI = dynamic_cast(aModule); - } //SRN: BugID IPAL9186: end of a fix - aName->SetValue(aSMESHGUI->moduleName().toLatin1().data()); - anAttr = myStudyBuilder->FindOrCreateAttribute(aSComponent,"AttributePixMap"); - aPixmap = SALOMEDS::AttributePixMap::_narrow(anAttr); - aPixmap->SetPixMap( "ICON_OBJBROWSER_SMESH" ); - myStudyBuilder->DefineComponentInstance(aSComponent,aSMESHGen); - if (aLocked) - myStudy->GetProperties()->SetLocked(true); + bool aLocked = myStudy->GetProperties()->IsLocked(); + if (aLocked) + myStudy->GetProperties()->SetLocked(false); + + aSComponent = myStudyBuilder->NewComponent("SMESH"); + anAttr = myStudyBuilder->FindOrCreateAttribute(aSComponent,"AttributeName"); + aName = SALOMEDS::AttributeName::_narrow(anAttr); + + SMESHGUI* aSMESHGUI = SMESHGUI::GetSMESHGUI(); //SRN: BugID IPAL9186, load a SMESH gui if it hasn't been loaded + if (!aSMESHGUI){ + CAM_Module* aModule = anApp->module("Mesh"); + if(!aModule) + aModule = anApp->loadModule("Mesh"); + aSMESHGUI = dynamic_cast(aModule); + } //SRN: BugID IPAL9186: end of a fix + aName->SetValue(aSMESHGUI->moduleName().toLatin1().data()); + anAttr = myStudyBuilder->FindOrCreateAttribute(aSComponent,"AttributePixMap"); + aPixmap = SALOMEDS::AttributePixMap::_narrow(anAttr); + aPixmap->SetPixMap( "ICON_OBJBROWSER_SMESH" ); + myStudyBuilder->DefineComponentInstance(aSComponent,aSMESHGen); + if (aLocked) + myStudy->GetProperties()->SetLocked(true); } mySComponentMesh = SALOMEDS::SComponent::_narrow(aSComponent); @@ -367,9 +365,9 @@ SMESH_Swig::Init(int theStudyID) MESSAGE("Init"); ProcessVoidEvent(new TEvent(theStudyID, - myStudy, - myStudyBuilder, - mySComponentMesh)); + myStudy, + myStudyBuilder, + mySComponentMesh)); } @@ -414,8 +412,8 @@ const char* SMESH_Swig::AddNewHypothesis(const char* theIOR) MESSAGE("AddNewHypothesis"); SALOMEDS::SObject_var aSObject = ::AddHypothesis(theIOR, - mySComponentMesh, - myStudyBuilder); + mySComponentMesh, + myStudyBuilder); CORBA::String_var anEntry = aSObject->GetID(); return anEntry._retn(); } @@ -427,8 +425,8 @@ const char* SMESH_Swig::AddNewAlgorithms(const char* theIOR) MESSAGE("AddNewAlgorithms"); SALOMEDS::SObject_var aSObject = ::AddAlgorithms(theIOR, - mySComponentMesh, - myStudyBuilder); + mySComponentMesh, + myStudyBuilder); CORBA::String_var anEntry = aSObject->GetID(); return anEntry._retn(); } @@ -453,9 +451,9 @@ void SMESH_Swig::SetHypothesis(const char* theMeshOrSubMeshEntry, const char* theDomainEntry) { ::SetHypothesis(theMeshOrSubMeshEntry, - theDomainEntry, - myStudy, - myStudyBuilder); + theDomainEntry, + myStudy, + myStudyBuilder); } @@ -464,9 +462,9 @@ void SMESH_Swig::SetAlgorithms(const char* theMeshOrSubMeshEntry, const char* theDomainEntry) { ::SetAlgorithms(theMeshOrSubMeshEntry, - theDomainEntry, - myStudy, - myStudyBuilder); + theDomainEntry, + myStudy, + myStudyBuilder); } @@ -599,7 +597,7 @@ void SMESH_Swig::SetName(const char* theEntry, //================================================================================ void SMESH_Swig::SetMeshIcon(const char* theMeshEntry, - const bool theIsComputed, + const bool theIsComputed, const bool isEmpty) { class TEvent: public SALOME_Event @@ -609,8 +607,8 @@ void SMESH_Swig::SetMeshIcon(const char* theMeshEntry, bool myIsComputed, myIsEmpty; public: TEvent(const SALOMEDS::Study_var& theStudy, - const std::string& theMeshEntry, - const bool theIsComputed, + const std::string& theMeshEntry, + const bool theIsComputed, const bool isEmpty): myStudy(theStudy), myMeshEntry(theMeshEntry), @@ -624,13 +622,13 @@ void SMESH_Swig::SetMeshIcon(const char* theMeshEntry, { SALOMEDS::SObject_var aMeshSO = myStudy->FindObjectID(myMeshEntry.c_str()); if(!aMeshSO->_is_nil()) - if(_PTR(SObject) aMesh = ClientFactory::SObject(aMeshSO)) - SMESH::ModifiedMesh(aMesh,myIsComputed,myIsEmpty); + if(_PTR(SObject) aMesh = ClientFactory::SObject(aMeshSO)) + SMESH::ModifiedMesh(aMesh,myIsComputed,myIsEmpty); } }; ProcessVoidEvent(new TEvent(myStudy, - theMeshEntry, - theIsComputed, + theMeshEntry, + theIsComputed, isEmpty)); } diff --git a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.h b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.h index a288506a1..4a7b8e719 100644 --- a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.h +++ b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.h @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : GUI for SMESH component // File : libSMESH_Swig.h // Author : Nicolas REJNERI, Open CASCADE S.A.S. @@ -27,7 +25,7 @@ #define LIBSMESH_SWIG_H #ifdef WNT -#if defined SMESH_SWIG_EXPORTS +#if defined SMESH_SWIG_EXPORTS || defined _libSMESH_Swig_EXPORTS #define SMESH_SWIG_EXPORT __declspec( dllexport ) #else #define SMESH_SWIG_EXPORT __declspec( dllimport ) diff --git a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.i b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.i index a865a8e95..52d2bb87f 100644 --- a/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.i +++ b/src/SMESH_SWIG_WITHIHM/libSMESH_Swig.i @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESHGUI : GUI for SMESH component // File : libSMESH_Swig.i // Author : Nicolas REJNERI, Open CASCADE S.A.S. diff --git a/src/StdMeshers/Makefile.am b/src/StdMeshers/Makefile.am index 8b86e0958..e831115fa 100644 --- a/src/StdMeshers/Makefile.am +++ b/src/StdMeshers/Makefile.am @@ -1,24 +1,22 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH StdMeshers : implementaion of SMESH idl descriptions # File : Makefile.in # Author : Julia DOROVSKIKH @@ -32,6 +30,7 @@ salomeinclude_HEADERS = \ StdMeshers_LocalLength.hxx \ StdMeshers_StartEndLength.hxx \ StdMeshers_Arithmetic1D.hxx \ + StdMeshers_FixedPoints1D.hxx \ StdMeshers_NumberOfSegments.hxx \ StdMeshers_Deflection1D.hxx \ StdMeshers_Propagation.hxx \ @@ -49,6 +48,7 @@ salomeinclude_HEADERS = \ StdMeshers_QuadranglePreference.hxx \ StdMeshers_QuadraticMesh.hxx \ StdMeshers_NumberOfLayers.hxx \ + StdMeshers_NumberOfLayers2D.hxx \ StdMeshers_Prism_3D.hxx \ StdMeshers_ProjectionSource1D.hxx \ StdMeshers_ProjectionSource2D.hxx \ @@ -59,6 +59,7 @@ salomeinclude_HEADERS = \ StdMeshers_RadialPrism_3D.hxx \ StdMeshers_ProjectionUtils.hxx \ StdMeshers_LayerDistribution.hxx \ + StdMeshers_LayerDistribution2D.hxx \ StdMeshers_SegmentAroundVertex_0D.hxx \ StdMeshers_SegmentLengthAroundVertex.hxx \ StdMeshers_FaceSide.hxx \ @@ -66,9 +67,18 @@ salomeinclude_HEADERS = \ StdMeshers_UseExisting_1D2D.hxx \ StdMeshers_QuadToTriaAdaptor.hxx \ SMESH_StdMeshers.hxx \ - StdMeshers_TrianglePreference.hxx \ StdMeshers_CompositeHexa_3D.hxx \ - StdMeshers_MaxLength.hxx + StdMeshers_MaxLength.hxx \ + StdMeshers_QuadrangleParams.hxx \ + StdMeshers_RadialQuadrangle_1D2D.hxx \ + StdMeshers_HexaFromSkin_3D.hxx \ + StdMeshers_ImportSource.hxx \ + StdMeshers_Import_1D.hxx \ + StdMeshers_Import_1D2D.hxx \ + StdMeshers_ViscousLayers.hxx \ + StdMeshers_Projection_1D2D.hxx \ + StdMeshers_CartesianParameters3D.hxx \ + StdMeshers_Cartesian_3D.hxx # Libraries targets @@ -78,6 +88,7 @@ dist_libStdMeshers_la_SOURCES = \ StdMeshers_LocalLength.cxx \ StdMeshers_StartEndLength.cxx \ StdMeshers_Arithmetic1D.cxx \ + StdMeshers_FixedPoints1D.cxx \ StdMeshers_NumberOfSegments.cxx \ StdMeshers_Deflection1D.cxx \ StdMeshers_Propagation.cxx \ @@ -95,6 +106,7 @@ dist_libStdMeshers_la_SOURCES = \ StdMeshers_QuadranglePreference.cxx \ StdMeshers_QuadraticMesh.cxx \ StdMeshers_NumberOfLayers.cxx \ + StdMeshers_NumberOfLayers2D.cxx \ StdMeshers_Prism_3D.cxx \ StdMeshers_ProjectionSource1D.cxx \ StdMeshers_ProjectionSource2D.cxx \ @@ -105,25 +117,35 @@ dist_libStdMeshers_la_SOURCES = \ StdMeshers_RadialPrism_3D.cxx \ StdMeshers_ProjectionUtils.cxx \ StdMeshers_LayerDistribution.cxx \ + StdMeshers_LayerDistribution2D.cxx \ StdMeshers_SegmentAroundVertex_0D.cxx \ StdMeshers_SegmentLengthAroundVertex.cxx \ StdMeshers_FaceSide.cxx \ StdMeshers_CompositeSegment_1D.cxx \ StdMeshers_UseExisting_1D2D.cxx \ StdMeshers_QuadToTriaAdaptor.cxx \ - StdMeshers_TrianglePreference.cxx \ StdMeshers_CompositeHexa_3D.cxx \ - StdMeshers_MaxLength.cxx - + StdMeshers_MaxLength.cxx \ + StdMeshers_QuadrangleParams.cxx \ + StdMeshers_RadialQuadrangle_1D2D.cxx \ + StdMeshers_HexaFromSkin_3D.cxx \ + StdMeshers_ImportSource.cxx \ + StdMeshers_Import_1D.cxx \ + StdMeshers_Import_1D2D.cxx \ + StdMeshers_ViscousLayers.cxx \ + StdMeshers_Projection_1D2D.cxx \ + StdMeshers_CartesianParameters3D.cxx \ + StdMeshers_Cartesian_3D.cxx # additionnal information to compil and link file libStdMeshers_la_CPPFLAGS = \ $(CAS_CPPFLAGS) \ $(BOOST_CPPFLAGS) \ + $(VTK_INCLUDES) \ $(KERNEL_CXXFLAGS) \ $(GUI_CXXFLAGS) \ - -I$(srcdir)/../SMESHImpl \ -I$(srcdir)/../MEFISTO2 \ + -I$(srcdir)/../SMESHUtils \ -I$(srcdir)/../SMESH \ -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../SMDS \ @@ -132,8 +154,12 @@ libStdMeshers_la_CPPFLAGS = \ libStdMeshers_la_LDFLAGS = \ ../SMESH/libSMESHimpl.la \ - ../SMESH_I/libSMESHEngine.la \ ../SMESHDS/libSMESHDS.la \ ../MEFISTO2/libMEFISTO2D.la \ $(KERNEL_LDFLAGS) -lSALOMELocalTrace -lOpUtil \ $(CAS_LDPATH) -lTKAdvTools -lTKTopAlgo -lTKG3d + +if WITH_TBB + libStdMeshers_la_CPPFLAGS += $(TBB_INCLUDES) + libStdMeshers_la_LDFLAGS += $(TBB_LIBS) +endif diff --git a/src/StdMeshers/SMESH_StdMeshers.hxx b/src/StdMeshers/SMESH_StdMeshers.hxx index be440c1bd..50e53acdd 100755 --- a/src/StdMeshers/SMESH_StdMeshers.hxx +++ b/src/StdMeshers/SMESH_StdMeshers.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_StdMeshers.hxx // Author : Alexander BORODIN // Module : SMESH @@ -28,7 +29,7 @@ #define _SMESH_StdMeshers_HXX_ #ifdef WNT - #if defined STDMESHERS_EXPORTS + #if defined STDMESHERS_EXPORTS || defined StdMeshers_EXPORTS #define STDMESHERS_EXPORT __declspec( dllexport ) #else #define STDMESHERS_EXPORT __declspec( dllimport ) diff --git a/src/StdMeshers/StdMeshers_Arithmetic1D.cxx b/src/StdMeshers/StdMeshers_Arithmetic1D.cxx index f5ac03c5e..589972c8b 100644 --- a/src/StdMeshers/StdMeshers_Arithmetic1D.cxx +++ b/src/StdMeshers/StdMeshers_Arithmetic1D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Arithmetic1D.cxx // Author : Damien COQUERET, OCC @@ -104,9 +105,32 @@ double StdMeshers_Arithmetic1D::GetLength(bool isStartLength) const */ //============================================================================= +void StdMeshers_Arithmetic1D::SetReversedEdges( std::vector& ids ) +{ + if ( ids != _edgeIDs ) { + _edgeIDs = ids; + + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= + ostream & StdMeshers_Arithmetic1D::SaveTo(ostream & save) { - save << _begLength << " " << _endLength; + int listSize = _edgeIDs.size(); + save << _begLength << " " << _endLength << " " << listSize; + + if ( listSize > 0 ) { + for ( int i = 0; i < listSize; i++) + save << " " << _edgeIDs[i]; + save << " " << _objEntry; + } + return save; } @@ -119,12 +143,25 @@ ostream & StdMeshers_Arithmetic1D::SaveTo(ostream & save) istream & StdMeshers_Arithmetic1D::LoadFrom(istream & load) { bool isOK = true; + int intVal; isOK = (load >> _begLength); if (!isOK) load.clear(ios::badbit | load.rdstate()); isOK = (load >> _endLength); + if (!isOK) load.clear(ios::badbit | load.rdstate()); + + isOK = (load >> intVal); + if (isOK && intVal > 0) { + _edgeIDs.reserve( intVal ); + for (int i = 0; i < _edgeIDs.capacity() && isOK; i++) { + isOK = (load >> intVal); + if ( isOK ) _edgeIDs.push_back( intVal ); + } + isOK = (load >> _objEntry); + } + return load; } @@ -177,7 +214,7 @@ bool StdMeshers_Arithmetic1D::SetParametersByMesh(const SMESH_Mesh* theMesh, { const TopoDS_Edge& edge = TopoDS::Edge( edgeMap( i )); Handle(Geom_Curve) C = BRep_Tool::Curve(edge, L, UMin, UMax); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); vector< double > params; SMESHDS_Mesh* aMeshDS = const_cast< SMESH_Mesh* >( theMesh )->GetMeshDS(); @@ -206,6 +243,6 @@ bool StdMeshers_Arithmetic1D::SetParametersByMesh(const SMESH_Mesh* theMesh, bool StdMeshers_Arithmetic1D::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*mesh*/) { - return bool( _begLength = _endLength = dflts._elemLength ); + return ( _begLength = _endLength = dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_Arithmetic1D.hxx b/src/StdMeshers/StdMeshers_Arithmetic1D.hxx index 94952b774..168b6b657 100644 --- a/src/StdMeshers/StdMeshers_Arithmetic1D.hxx +++ b/src/StdMeshers/StdMeshers_Arithmetic1D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Arithmetic1D.hxx // Author : Damien COQUERET, OCC @@ -27,11 +28,15 @@ #ifndef _SMESH_ARITHMETIC1D_HXX_ #define _SMESH_ARITHMETIC1D_HXX_ + + #include "SMESH_StdMeshers.hxx" #include "SMESH_Hypothesis.hxx" #include "Utils_SALOME_Exception.hxx" +#include + class STDMESHERS_EXPORT StdMeshers_Arithmetic1D: public SMESH_Hypothesis { @@ -43,6 +48,14 @@ public: double GetLength(bool isStartLength) const; + void SetReversedEdges( std::vector& ids); + + void SetObjectEntry( const char* entry ) { _objEntry = entry; } + + const char* GetObjectEntry() { return _objEntry.c_str(); } + + const std::vector& GetReversedEdges() const { return _edgeIDs; } + virtual std::ostream & SaveTo(std::ostream & save); virtual std::istream & LoadFrom(std::istream & load); friend std::ostream& operator << (std::ostream & save, StdMeshers_Arithmetic1D & hyp); @@ -64,6 +77,8 @@ public: protected: double _begLength, _endLength; + std::vector _edgeIDs; + std::string _objEntry; }; #endif diff --git a/src/StdMeshers/StdMeshers_AutomaticLength.cxx b/src/StdMeshers/StdMeshers_AutomaticLength.cxx index 95710c94e..d12f51a34 100644 --- a/src/StdMeshers/StdMeshers_AutomaticLength.cxx +++ b/src/StdMeshers/StdMeshers_AutomaticLength.cxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_AutomaticLength.cxx // Author : Edward AGAPOV, OCC // Module : SMESH -// + #include "StdMeshers_AutomaticLength.hxx" #include "SMESH_Mesh.hxx" @@ -116,7 +117,7 @@ namespace { */ //================================================================================ - const double a14divPI = 14. / PI; + const double a14divPI = 14. / M_PI; inline double segLength(double S0, double edgeLen, double minLen ) { @@ -398,5 +399,3 @@ bool StdMeshers_AutomaticLength::SetParametersByDefaults(const TDefaults& /*the // return true; } - - diff --git a/src/StdMeshers/StdMeshers_AutomaticLength.hxx b/src/StdMeshers/StdMeshers_AutomaticLength.hxx index 627003aa6..089b935ac 100644 --- a/src/StdMeshers/StdMeshers_AutomaticLength.hxx +++ b/src/StdMeshers/StdMeshers_AutomaticLength.hxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_AutomaticLength.hxx // Author : Edward AGAPOV, OCC // Module : SMESH - +// #ifndef _SMESH_AutomaticLength_HXX_ #define _SMESH_AutomaticLength_HXX_ diff --git a/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx new file mode 100644 index 000000000..9446e123d --- /dev/null +++ b/src/StdMeshers/StdMeshers_CartesianParameters3D.cxx @@ -0,0 +1,453 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_CartesianParameters3D.cxx +// Author : Edward AGAPOV +// Module : SMESH +// +#include "StdMeshers_CartesianParameters3D.hxx" + +#include "StdMeshers_NumberOfSegments.hxx" +#include "StdMeshers_Distribution.hxx" +#include "SMESH_Gen.hxx" + +#include "utilities.h" + +#include +#include + +#include + +using namespace std; + +//======================================================================= +//function : StdMeshers_CartesianParameters3D +//purpose : Constructor +//======================================================================= + +StdMeshers_CartesianParameters3D::StdMeshers_CartesianParameters3D(int hypId, + int studyId, + SMESH_Gen * gen) + : SMESH_Hypothesis(hypId, studyId, gen), + _sizeThreshold( 4.0 ) // default according to the customer specification +{ + _name = "CartesianParameters3D"; // used by "Cartesian_3D" + _param_algo_dim = 3; // 3D +} + + +namespace +{ + const char* axisName[3] = { "X", "Y", "Z" }; + + //================================================================================ + /*! + * \brief Checks validity of an axis index, throws in case of invalidity + */ + //================================================================================ + + void checkAxis(const int axis) + { + if ( axis < 0 || axis > 2 ) + throw SALOME_Exception(SMESH_Comment("Invalid axis index ") << axis << + ". Valid axis indices are 0, 1 and 2"); + } + + //================================================================================ + /*! + * \brief Checks validity of spacing data, throws in case of invalidity + */ + //================================================================================ + + void checkGridSpacing(std::vector& spaceFunctions, + std::vector& internalPoints, + const std::string& axis) + throw ( SALOME_Exception ) + { + if ( spaceFunctions.empty() ) + throw SALOME_Exception(SMESH_Comment("Empty space function for ") << axis ); + + for ( size_t i = 1; i < internalPoints.size(); ++i ) + if ( internalPoints[i] - internalPoints[i-1] < 0 ) + throw SALOME_Exception(SMESH_Comment("Wrong order of internal points along ") << axis); + else if ( internalPoints[i] - internalPoints[i-1] < 1e-3 ) + throw SALOME_Exception(SMESH_Comment("Too close internal points along ") << axis ); + + const double tol = Precision::Confusion(); + if ( !internalPoints.empty() && + ( internalPoints.front() < -tol || internalPoints.back() > 1 + tol )) + throw SALOME_Exception(SMESH_Comment("Invalid internal points along ") << axis); + + if ( internalPoints.empty() || internalPoints.front() > tol ) + internalPoints.insert( internalPoints.begin(), 0. ); + if ( internalPoints.size() < 2 || internalPoints.back() < 1 - tol ) + internalPoints.push_back( 1. ); + + if ( internalPoints.size() != spaceFunctions.size() + 1 ) + throw SALOME_Exception + (SMESH_Comment("Numbre of internal points mismatch number of functions for ") << axis); + + for ( size_t i = 0; i < spaceFunctions.size(); ++i ) + spaceFunctions[i] = + StdMeshers_NumberOfSegments::CheckExpressionFunction( spaceFunctions[i], -1 ); + } +} + +//======================================================================= +//function : SetGrid +//purpose : Sets coordinates of node positions along an axes +//======================================================================= + +void StdMeshers_CartesianParameters3D::SetGrid(std::vector& coords, int axis) + throw ( SALOME_Exception ) +{ + checkAxis( axis ); + + if ( coords.size() < 2 ) + throw SALOME_Exception(LOCALIZED("Wrong number of grid coordinates")); + + std::sort( coords.begin(), coords.end() ); + + bool changed = ( _coords[axis] != coords ); + if ( changed ) + { + _coords[axis] = coords; + NotifySubMeshesHypothesisModification(); + } + + _spaceFunctions[axis].clear(); + _internalPoints[axis].clear(); +} + +//======================================================================= +//function : SetGridSpacing +//purpose : Set grid spacing along the three axes +//======================================================================= + +void StdMeshers_CartesianParameters3D::SetGridSpacing(std::vector& xSpaceFuns, + std::vector& xInternalPoints, + const int axis) + throw ( SALOME_Exception ) +{ + checkAxis( axis ); + + checkGridSpacing( xSpaceFuns, xInternalPoints, axisName[axis] ); + + bool changed = ( xSpaceFuns != _spaceFunctions[axis] || + xInternalPoints != _internalPoints[axis] ); + + _spaceFunctions[axis] = xSpaceFuns; + _internalPoints[axis] = xInternalPoints; + _coords[axis].clear(); + + if ( changed ) + NotifySubMeshesHypothesisModification(); +} + +//======================================================================= +//function : SetSizeThreshold +//purpose : Set size threshold +//======================================================================= + +void StdMeshers_CartesianParameters3D::SetSizeThreshold(const double threshold) + throw ( SALOME_Exception ) +{ + if ( threshold <= 1.0 ) + throw SALOME_Exception(LOCALIZED("threshold must be > 1.0")); + + bool changed = fabs( _sizeThreshold - threshold ) > 1e-6; + _sizeThreshold = threshold; + + if ( changed ) + NotifySubMeshesHypothesisModification(); +} + +//======================================================================= +//function : GetGridSpacing +//purpose : return spacing +//======================================================================= + +void StdMeshers_CartesianParameters3D::GetGridSpacing(std::vector& spaceFunctions, + std::vector& internalPoints, + const int axis) const + throw ( SALOME_Exception ) +{ + if ( !IsGridBySpacing(axis) ) + throw SALOME_Exception(LOCALIZED("The grid is defined by coordinates and not by spacing")); + + spaceFunctions = _spaceFunctions[axis]; + internalPoints = _internalPoints[axis]; +} + +//======================================================================= +//function : IsGridBySpacing +//======================================================================= + +bool StdMeshers_CartesianParameters3D::IsGridBySpacing(const int axis) const + throw ( SALOME_Exception ) +{ + checkAxis(axis); + return !_spaceFunctions[axis].empty(); +} + + +//======================================================================= +//function : ComputeCoordinates +//purpose : Computes node coordinates by spacing functions +//======================================================================= + +void StdMeshers_CartesianParameters3D::ComputeCoordinates(const double x0, + const double x1, + vector& spaceFuns, + vector& points, + vector& coords, + const std::string& axis ) + throw ( SALOME_Exception ) +{ + checkGridSpacing( spaceFuns, points, axis ); + + coords.clear(); + for ( size_t i = 0; i < spaceFuns.size(); ++i ) + { + FunctionExpr fun( spaceFuns[i].c_str(), /*convMode=*/-1 ); + + const double p0 = x0 * ( 1. - points[i]) + x1 * points[i]; + const double p1 = x0 * ( 1. - points[i+1]) + x1 * points[i+1]; + const double length = p1 - p0; + + const size_t nbSections = 1000; + const double sectionLen = ( p1 - p0 ) / nbSections; + vector< double > nbSegments( nbSections + 1 ); + nbSegments[ 0 ] = 0.; + + double t, spacing = 0; + for ( size_t i = 1; i <= nbSections; ++i ) + { + t = double( i ) / nbSections; + if ( !fun.value( t, spacing ) || spacing < std::numeric_limits::min() ) + throw SALOME_Exception(LOCALIZED("Invalid spacing function")); + nbSegments[ i ] = nbSegments[ i-1 ] + std::min( 1., sectionLen / spacing ); + } + + const int nbCells = max (1, int(floor(nbSegments.back()+0.5))); + const double corr = nbCells / nbSegments.back(); + + if ( coords.empty() ) coords.push_back( p0 ); + + for ( size_t iCell = 1, i = 1; i <= nbSections; ++i ) + { + if ( nbSegments[i]*corr >= iCell ) + { + t = (i - ( nbSegments[i] - iCell/corr )/( nbSegments[i] - nbSegments[i-1] )) / nbSections; + coords.push_back( p0 + t * length ); + ++iCell; + } + } + const double lastCellLen = coords.back() - coords[ coords.size() - 2 ]; + if ( fabs( coords.back() - p1 ) > 0.5 * lastCellLen ) + coords.push_back ( p1 ); + } +} + +//======================================================================= +//function : GetCoordinates +//purpose : Return coordinates of node positions along the three axes. +// If the grid is defined by spacing functions, the coordinates are computed +//======================================================================= + +void StdMeshers_CartesianParameters3D::GetCoordinates(std::vector& xNodes, + std::vector& yNodes, + std::vector& zNodes, + const Bnd_Box& bndBox) const + throw ( SALOME_Exception ) +{ + double x0,y0,z0, x1,y1,z1; + if ( IsGridBySpacing(0) || IsGridBySpacing(1) || IsGridBySpacing(2)) + { + if ( bndBox.IsVoid() || + bndBox.IsXThin( Precision::Confusion() ) || + bndBox.IsYThin( Precision::Confusion() ) || + bndBox.IsZThin( Precision::Confusion() ) ) + throw SALOME_Exception(LOCALIZED("Invalid bounding box")); + bndBox.Get(x0,y0,z0, x1,y1,z1); + } + + StdMeshers_CartesianParameters3D* me = const_cast(this); + if ( IsGridBySpacing(0) ) + ComputeCoordinates( x0, x1, me->_spaceFunctions[0], me->_internalPoints[0], xNodes, "X" ); + else + xNodes = _coords[0]; + + if ( IsGridBySpacing(1) ) + ComputeCoordinates( y0, y1, me->_spaceFunctions[1], me->_internalPoints[1], yNodes, "Y" ); + else + yNodes = _coords[1]; + + if ( IsGridBySpacing(2) ) + ComputeCoordinates( z0, z1, me->_spaceFunctions[2], me->_internalPoints[2], zNodes, "Z" ); + else + zNodes = _coords[2]; +} + +//======================================================================= +//function : GetGrid +//purpose : Return coordinates of node positions along the three axes +//======================================================================= + +void StdMeshers_CartesianParameters3D::GetGrid(std::vector& coords, int axis) const + throw ( SALOME_Exception ) +{ + if ( IsGridBySpacing(axis) ) + throw SALOME_Exception(LOCALIZED("The grid is defined by spacing and not by coordinates")); + + coords = _coords[axis]; +} + +//======================================================================= +//function : GetSizeThreshold +//purpose : Return size threshold +//======================================================================= + +double StdMeshers_CartesianParameters3D::GetSizeThreshold() const +{ + return _sizeThreshold; +} + +//======================================================================= +//function : IsDefined +//purpose : Return true if parameters are well defined +//======================================================================= + +bool StdMeshers_CartesianParameters3D::IsDefined() const +{ + for ( int i = 0; i < 3; ++i ) + if (_coords[i].empty() && (_spaceFunctions[i].empty() || _internalPoints[i].empty())) + return false; + + return ( _sizeThreshold > 1.0 ); +} + +//======================================================================= +//function : SaveTo +//purpose : store my parameters into a stream +//======================================================================= + +std::ostream & StdMeshers_CartesianParameters3D::SaveTo(std::ostream & save) +{ + save << _sizeThreshold << " "; + + for ( int i = 0; i < 3; ++i ) + { + save << _coords[i].size() << " "; + for ( size_t j = 0; j < _coords[i].size(); ++j ) + save << _coords[i][j] << " "; + + save << _internalPoints[i].size() << " "; + for ( size_t j = 0; j < _internalPoints[i].size(); ++j ) + save << _internalPoints[i][j] << " "; + + save << _spaceFunctions[i].size() << " "; + for ( size_t j = 0; j < _spaceFunctions[i].size(); ++j ) + save << _spaceFunctions[i][j] << " "; + } + + return save; +} + +//======================================================================= +//function : LoadFrom +//purpose : resore my parameters from a stream +//======================================================================= + +std::istream & StdMeshers_CartesianParameters3D::LoadFrom(std::istream & load) +{ + bool ok; + + ok = (load >> _sizeThreshold ); + for ( int ax = 0; ax < 3; ++ax ) + { + if (ok) + { + size_t i = 0; + ok = (load >> i ); + if ( i > 0 && ok ) + { + _coords[ax].resize( i ); + for ( i = 0; i < _coords[ax].size() && ok; ++i ) + ok = (load >> _coords[ax][i] ); + } + } + if (ok) + { + size_t i = 0; + ok = (load >> i ); + if ( i > 0 && ok ) + { + _internalPoints[ax].resize( i ); + for ( i = 0; i < _internalPoints[ax].size() && ok; ++i ) + ok = (load >> _internalPoints[ax][i] ); + } + } + if (ok) + { + size_t i = 0; + ok = (load >> i ); + if ( i > 0 && ok ) + { + _spaceFunctions[ax].resize( i ); + for ( i = 0; i < _spaceFunctions[ax].size() && ok; ++i ) + ok = (load >> _spaceFunctions[ax][i] ); + } + } + } + return load; +} + +//======================================================================= +//function : SetParametersByMesh +//======================================================================= + +bool StdMeshers_CartesianParameters3D::SetParametersByMesh(const SMESH_Mesh* , + const TopoDS_Shape& ) +{ + return false; +} + +//======================================================================= +//function : SetParametersByDefaults +//======================================================================= + +bool StdMeshers_CartesianParameters3D::SetParametersByDefaults(const TDefaults& dflts, + const SMESH_Mesh* /*theMesh*/) +{ + if ( dflts._elemLength > 1e-100 ) + { + vector spacing( 1, SMESH_Comment(dflts._elemLength)); + vector intPnts; + SetGridSpacing( spacing, intPnts, 0 ); + SetGridSpacing( spacing, intPnts, 1 ); + SetGridSpacing( spacing, intPnts, 2 ); + return true; + } + return false; +} + diff --git a/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx b/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx new file mode 100644 index 000000000..682ce4c53 --- /dev/null +++ b/src/StdMeshers/StdMeshers_CartesianParameters3D.hxx @@ -0,0 +1,145 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_CartesianParameters3D.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_CartesianParameters3D_HXX_ +#define _SMESH_CartesianParameters3D_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_Hypothesis.hxx" +#include "Utils_SALOME_Exception.hxx" + +#include + +class SMESH_Gen; +class Bnd_Box; + +// ========================================================= +/*! + * This hypothesis specifies + * - Definition of the Cartesian grid + * - Size threshold + */ +// ========================================================= + +class STDMESHERS_EXPORT StdMeshers_CartesianParameters3D: public SMESH_Hypothesis +{ +public: + // Constructor + StdMeshers_CartesianParameters3D( int hypId, int studyId, SMESH_Gen * gen ); + + /*! + * Sets coordinates of node positions along an axis (countered from 0) + */ + void SetGrid(std::vector& xNodes, int axis) throw ( SALOME_Exception ); + /*! + * Return coordinates of node positions along the three axes + */ + void GetGrid(std::vector& xNodes, int axis) const throw ( SALOME_Exception ); + + /*! + * \brief Set grid spacing along the three axes + * \param spaceFunctions - functions defining spacing values at given point on axis + * \param internalPoints - points dividing a grid into parts along each direction + * + * Parameter t of spaceFunction f(t) is a position [0,1] withing bounding box of + * the shape to mesh + */ + void SetGridSpacing(std::vector& spaceFunctions, + std::vector& internalPoints, + const int axis) throw ( SALOME_Exception ); + + void GetGridSpacing(std::vector& spaceFunctions, + std::vector& internalPoints, + const int axis) const throw ( SALOME_Exception ); + + bool IsGridBySpacing(const int axis) const throw ( SALOME_Exception ); + + /*! + * \brief Computes node coordinates by spacing functions + * \param x0 - lower coordinate + * \param x1 - upper coordinate + * \param spaceFuns - space functions + * \param points - internal points + * \param coords - the computed coordinates + */ + static void ComputeCoordinates(const double x0, + const double x1, + std::vector& spaceFuns, + std::vector& points, + std::vector& coords, + const std::string& axis ) throw (SALOME_Exception); + /*! + * Return coordinates of node positions along the three axes. + * If the grid is defined by spacing functions, the coordinates are computed + */ + void GetCoordinates(std::vector& xNodes, + std::vector& yNodes, + std::vector& zNodes, + const Bnd_Box& bndBox) const throw ( SALOME_Exception ); + /*! + * Set size threshold. A polyhedral cell got by cutting an initial + * hexahedron by geometry boundary is considered small and is removed if + * it's size is \athreshold times less than the size of the initial hexahedron. + */ + void SetSizeThreshold(const double threshold) throw ( SALOME_Exception ); + /*! + * \brief Return size threshold + */ + double GetSizeThreshold() const; + + /*! + * \brief Return true if parameters are well defined + */ + bool IsDefined() const; + + /*! + * \brief Persistence methods + */ + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + + /*! + * \brief Initialize my parameter values by the mesh built on the geometry + */ + virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape); + + /*! + * \brief Initialize my parameter values by default parameters. + */ + virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0); + + protected: + + std::vector _coords[3]; + std::vector _spaceFunctions[3]; + std::vector _internalPoints[3]; + + double _sizeThreshold; +}; + +#endif + diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.cxx b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx new file mode 100644 index 000000000..c1e57198f --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.cxx @@ -0,0 +1,2185 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_Cartesian_3D.cxx +// Module : SMESH +// +#include "StdMeshers_Cartesian_3D.hxx" + +#include "SMDS_MeshNode.hxx" +#include "SMESH_Block.hxx" +#include "SMESH_Comment.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_subMeshEventListener.hxx" +#include "StdMeshers_CartesianParameters3D.hxx" + +#include "utilities.h" +#include "Utils_ExceptHandlers.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#undef WITH_TBB +#ifdef WITH_TBB +#include +//#include +#endif + +using namespace std; + +//#define _MY_DEBUG_ + +#define ELLIPSOLID_WORKAROUND // remove it as soon as http://tracker.dev.opencascade.org/view.php?id=22809 is solved + +#ifdef ELLIPSOLID_WORKAROUND +#include +#include +#include +#endif + +//============================================================================= +/*! + * Constructor + */ +//============================================================================= + +StdMeshers_Cartesian_3D::StdMeshers_Cartesian_3D(int hypId, int studyId, SMESH_Gen * gen) + :SMESH_3D_Algo(hypId, studyId, gen) +{ + _name = "Cartesian_3D"; + _shapeType = (1 << TopAbs_SOLID); // 1 bit /shape type + _compatibleHypothesis.push_back("CartesianParameters3D"); + + _onlyUnaryInput = false; // to mesh all SOLIDs at once + _requireDiscreteBoundary = false; // 2D mesh not needed + _supportSubmeshes = false; // do not use any existing mesh +} + +//============================================================================= +/*! + * Check presence of a hypothesis + */ +//============================================================================= + +bool StdMeshers_Cartesian_3D::CheckHypothesis (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + Hypothesis_Status& aStatus) +{ + aStatus = SMESH_Hypothesis::HYP_MISSING; + + const list& hyps = GetUsedHypothesis(aMesh, aShape); + list ::const_iterator h = hyps.begin(); + if ( h == hyps.end()) + { + return false; + } + + for ( ; h != hyps.end(); ++h ) + { + if (( _hyp = dynamic_cast( *h ))) + { + aStatus = _hyp->IsDefined() ? HYP_OK : HYP_BAD_PARAMETER; + break; + } + } + + return aStatus == HYP_OK; +} + +namespace +{ + //============================================================================= + // Definitions of internal utils + // -------------------------------------------------------------------------- + enum Transition { + Trans_TANGENT = IntCurveSurface_Tangent, + Trans_IN = IntCurveSurface_In, + Trans_OUT = IntCurveSurface_Out, + Trans_APEX + }; + // -------------------------------------------------------------------------- + /*! + * \brief Data of intersection between a GridLine and a TopoDS_Face + */ + struct IntersectionPoint + { + double _paramOnLine; + mutable Transition _transition; + mutable const SMDS_MeshNode* _node; + mutable size_t _indexOnLine; + + IntersectionPoint(): _node(0) {} + bool operator< ( const IntersectionPoint& o ) const { return _paramOnLine < o._paramOnLine; } + }; + // -------------------------------------------------------------------------- + /*! + * \brief A line of the grid and its intersections with 2D geometry + */ + struct GridLine + { + gp_Lin _line; + double _length; // line length + multiset< IntersectionPoint > _intPoints; + + void RemoveExcessIntPoints( const double tol ); + bool GetIsOutBefore( multiset< IntersectionPoint >::iterator ip, bool prevIsOut ); + }; + // -------------------------------------------------------------------------- + /*! + * \brief Iterator on the parallel grid lines of one direction + */ + struct LineIndexer + { + size_t _size [3]; + size_t _curInd[3]; + size_t _iVar1, _iVar2, _iConst; + string _name1, _name2, _nameConst; + LineIndexer() {} + LineIndexer( size_t sz1, size_t sz2, size_t sz3, + size_t iv1, size_t iv2, size_t iConst, + const string& nv1, const string& nv2, const string& nConst ) + { + _size[0] = sz1; _size[1] = sz2; _size[2] = sz3; + _curInd[0] = _curInd[1] = _curInd[2] = 0; + _iVar1 = iv1; _iVar2 = iv2; _iConst = iConst; + _name1 = nv1; _name2 = nv2; _nameConst = nConst; + } + + size_t I() const { return _curInd[0]; } + size_t J() const { return _curInd[1]; } + size_t K() const { return _curInd[2]; } + void SetIJK( size_t i, size_t j, size_t k ) + { + _curInd[0] = i; _curInd[1] = j; _curInd[2] = k; + } + void operator++() + { + if ( ++_curInd[_iVar1] == _size[_iVar1] ) + _curInd[_iVar1] = 0, ++_curInd[_iVar2]; + } + bool More() const { return _curInd[_iVar2] < _size[_iVar2]; } + size_t LineIndex () const { return _curInd[_iVar1] + _curInd[_iVar2]* _size[_iVar1]; } + size_t LineIndex10 () const { return (_curInd[_iVar1] + 1 ) + _curInd[_iVar2]* _size[_iVar1]; } + size_t LineIndex01 () const { return _curInd[_iVar1] + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } + size_t LineIndex11 () const { return (_curInd[_iVar1] + 1 ) + (_curInd[_iVar2] + 1 )* _size[_iVar1]; } + void SetIndexOnLine (size_t i) { _curInd[ _iConst ] = i; } + size_t NbLines() const { return _size[_iVar1] * _size[_iVar2]; } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Container of GridLine's + */ + struct Grid + { + vector< double > _coords[3]; // coordinates of grid nodes + vector< GridLine > _lines [3]; // in 3 directions + double _tol, _minCellSize; + + vector< const SMDS_MeshNode* > _nodes; // mesh nodes at grid nodes + vector< bool > _isBndNode; // is mesh node at intersection with geometry + + size_t CellIndex( size_t i, size_t j, size_t k ) const + { + return i + j*(_coords[0].size()-1) + k*(_coords[0].size()-1)*(_coords[1].size()-1); + } + size_t NodeIndex( size_t i, size_t j, size_t k ) const + { + return i + j*_coords[0].size() + k*_coords[0].size()*_coords[1].size(); + } + size_t NodeIndexDX() const { return 1; } + size_t NodeIndexDY() const { return _coords[0].size(); } + size_t NodeIndexDZ() const { return _coords[0].size() * _coords[1].size(); } + + LineIndexer GetLineIndexer(size_t iDir) const; + + void SetCoordinates(const vector& xCoords, + const vector& yCoords, + const vector& zCoords, + const TopoDS_Shape& shape ); + void ComputeNodes(SMESH_MesherHelper& helper); + }; +#ifdef ELLIPSOLID_WORKAROUND + // -------------------------------------------------------------------------- + /*! + * \brief struct temporary replacing IntCurvesFace_Intersector until + * OCCT bug 0022809 is fixed + * http://tracker.dev.opencascade.org/view.php?id=22809 + */ + struct TMP_IntCurvesFace_Intersector + { + BRepAdaptor_Surface _surf; + double _tol; + BRepIntCurveSurface_Inter _intcs; + vector _points; + BRepTopAdaptor_TopolTool _clsf; + + TMP_IntCurvesFace_Intersector(const TopoDS_Face& face, const double tol) + :_surf( face ), _tol( tol ), _clsf( new BRepAdaptor_HSurface(_surf) ) {} + Bnd_Box Bounding() const { Bnd_Box b; BRepBndLib::Add (_surf.Face(), b); return b; } + void Perform( const gp_Lin& line, const double w0, const double w1 ) + { + _points.clear(); + for ( _intcs.Init( _surf.Face(), line, _tol ); _intcs.More(); _intcs.Next() ) + if ( w0 <= _intcs.W() && _intcs.W() <= w1 ) + _points.push_back( _intcs.Point() ); + } + bool IsDone() const { return true; } + int NbPnt() const { return _points.size(); } + IntCurveSurface_TransitionOnCurve Transition( const int i ) const { return _points[ i-1 ].Transition(); } + double WParameter( const int i ) const { return _points[ i-1 ].W(); } + TopAbs_State ClassifyUVPoint(const gp_Pnt2d& p) { return _clsf.Classify( p, _tol ); } + }; +#define __IntCurvesFace_Intersector TMP_IntCurvesFace_Intersector +#else +#define __IntCurvesFace_Intersector IntCurvesFace_Intersector +#endif + // -------------------------------------------------------------------------- + /*! + * \brief Intersector of TopoDS_Face with all GridLine's + */ + struct FaceGridIntersector + { + TopoDS_Face _face; + Grid* _grid; + Bnd_Box _bndBox; + __IntCurvesFace_Intersector* _surfaceInt; + vector< std::pair< GridLine*, IntersectionPoint > > _intersections; + + FaceGridIntersector(): _grid(0), _surfaceInt(0) {} + void Intersect(); + bool IsInGrid(const Bnd_Box& gridBox); + + void StoreIntersections() + { + for ( size_t i = 0; i < _intersections.size(); ++i ) + _intersections[i].first->_intPoints.insert( _intersections[i].second ); + } + const Bnd_Box& GetFaceBndBox() + { + GetCurveFaceIntersector(); + return _bndBox; + } + __IntCurvesFace_Intersector* GetCurveFaceIntersector() + { + if ( !_surfaceInt ) + { + _surfaceInt = new __IntCurvesFace_Intersector( _face, Precision::PConfusion() ); + _bndBox = _surfaceInt->Bounding(); + if ( _bndBox.IsVoid() ) + BRepBndLib::Add (_face, _bndBox); + } + return _surfaceInt; + } + bool IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const; + }; + // -------------------------------------------------------------------------- + /*! + * \brief Intersector of a surface with a GridLine + */ + struct FaceLineIntersector + { + double _tol; + double _u, _v, _w; // params on the face and the line + Transition _transition; // transition of at intersection (see IntCurveSurface.cdl) + Transition _transIn, _transOut; // IN and OUT transitions depending of face orientation + + gp_Pln _plane; + gp_Cylinder _cylinder; + gp_Cone _cone; + gp_Sphere _sphere; + gp_Torus _torus; + __IntCurvesFace_Intersector* _surfaceInt; + + vector< IntersectionPoint > _intPoints; + + void IntersectWithPlane (const GridLine& gridLine); + void IntersectWithCylinder(const GridLine& gridLine); + void IntersectWithCone (const GridLine& gridLine); + void IntersectWithSphere (const GridLine& gridLine); + void IntersectWithTorus (const GridLine& gridLine); + void IntersectWithSurface (const GridLine& gridLine); + + bool UVIsOnFace() const; + void addIntPoint(const bool toClassify=true); + bool isParamOnLineOK( const double linLength ) + { + return -_tol < _w && _w < linLength + _tol; + } + FaceLineIntersector():_surfaceInt(0) {} + ~FaceLineIntersector() { if (_surfaceInt ) delete _surfaceInt; _surfaceInt = 0; } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Class representing topology of the hexahedron and creating a mesh + * volume basing on analysis of hexahedron intersection with geometry + */ + class Hexahedron + { + // -------------------------------------------------------------------------------- + struct _Face; + struct _Link; + // -------------------------------------------------------------------------------- + struct _Node //!< node either at a hexahedron corner or at GridLine intersection + { + const SMDS_MeshNode* _node; // mesh node at hexahedron corner + const IntersectionPoint* _intPoint; + + _Node(const SMDS_MeshNode* n=0, const IntersectionPoint* ip=0):_node(n), _intPoint(ip) {} + const SMDS_MeshNode* Node() const { return _intPoint ? _intPoint->_node : _node; } + //bool IsCorner() const { return _node; } + }; + // -------------------------------------------------------------------------------- + struct _Link // link connecting two _Node's + { + _Node* _nodes[2]; + vector< _Node> _intNodes; // _Node's at GridLine intersections + vector< _Link > _splits; + vector< _Face*> _faces; + }; + // -------------------------------------------------------------------------------- + struct _OrientedLink + { + _Link* _link; + bool _reverse; + _OrientedLink( _Link* link=0, bool reverse=false ): _link(link), _reverse(reverse) {} + void Reverse() { _reverse = !_reverse; } + int NbResultLinks() const { return _link->_splits.size(); } + _OrientedLink ResultLink(int i) const + { + return _OrientedLink(&_link->_splits[_reverse ? NbResultLinks()-i-1 : i],_reverse); + } + _Node* FirstNode() const { return _link->_nodes[ _reverse ]; } + _Node* LastNode() const { return _link->_nodes[ !_reverse ]; } + }; + // -------------------------------------------------------------------------------- + struct _Face + { + vector< _OrientedLink > _links; + vector< _Link > _polyLinks; // links added to close a polygonal face + }; + // -------------------------------------------------------------------------------- + struct _volumeDef // holder of nodes of a volume mesh element + { + vector< const SMDS_MeshNode* > _nodes; + vector< int > _quantities; + typedef boost::shared_ptr<_volumeDef> Ptr; + void set( const vector< const SMDS_MeshNode* >& nodes, + const vector< int > quant = vector< int >() ) + { _nodes = nodes; _quantities = quant; } + // static Ptr New( const vector< const SMDS_MeshNode* >& nodes, + // const vector< int > quant = vector< int >() ) + // { + // _volumeDef* def = new _volumeDef; + // def->_nodes = nodes; + // def->_quantities = quant; + // return Ptr( def ); + // } + }; + + // topology of a hexahedron + int _nodeShift[8]; + _Node _hexNodes[8]; + _Link _hexLinks[12]; + _Face _hexQuads[6]; + + // faces resulted from hexahedron intersection + vector< _Face > _polygons; + + // computed volume elements + //vector< _volumeDef::Ptr > _volumeDefs; + _volumeDef _volumeDefs; + + Grid* _grid; + double _sizeThreshold, _sideLength[3]; + int _nbCornerNodes, _nbIntNodes, _nbBndNodes; + int _origNodeInd; // index of _hexNodes[0] node within the _grid + size_t _i,_j,_k; + + public: + Hexahedron(const double sizeThreshold, Grid* grid); + int MakeElements(SMESH_MesherHelper& helper); + void ComputeElements(); + void Init() { init( _i, _j, _k ); } + + private: + Hexahedron(const Hexahedron& other ); + void init( size_t i, size_t j, size_t k ); + void init( size_t i ); + int addElements(SMESH_MesherHelper& helper); + bool isInHole() const; + bool checkPolyhedronSize() const; + bool addHexa (); + bool addTetra(); + bool addPenta(); + bool addPyra (); + }; + +#ifdef WITH_TBB + // -------------------------------------------------------------------------- + /*! + * \brief Hexahedron computing volumes in one thread + */ + struct ParallelHexahedron + { + vector< Hexahedron* >& _hexVec; + vector& _index; + ParallelHexahedron( vector< Hexahedron* >& hv, vector& ind): _hexVec(hv), _index(ind) {} + void operator() ( const tbb::blocked_range& r ) const + { + for ( size_t i = r.begin(); i != r.end(); ++i ) + if ( Hexahedron* hex = _hexVec[ _index[i]] ) + hex->ComputeElements(); + } + }; + // -------------------------------------------------------------------------- + /*! + * \brief Structure intersecting certain nb of faces with GridLine's in one thread + */ + struct ParallelIntersector + { + vector< FaceGridIntersector >& _faceVec; + ParallelIntersector( vector< FaceGridIntersector >& faceVec): _faceVec(faceVec){} + void operator() ( const tbb::blocked_range& r ) const + { + for ( size_t i = r.begin(); i != r.end(); ++i ) + _faceVec[i].Intersect(); + } + }; + +#endif + //============================================================================= + // Implementation of internal utils + //============================================================================= + /* + * Remove coincident intersection points + */ + void GridLine::RemoveExcessIntPoints( const double tol ) + { + if ( _intPoints.size() < 2 ) return; + + set< Transition > tranSet; + multiset< IntersectionPoint >::iterator ip1, ip2 = _intPoints.begin(); + while ( ip2 != _intPoints.end() ) + { + tranSet.clear(); + ip1 = ip2++; + while ( ip2->_paramOnLine - ip1->_paramOnLine <= tol && ip2 != _intPoints.end()) + { + tranSet.insert( ip1->_transition ); + tranSet.insert( ip2->_transition ); + _intPoints.erase( ip1 ); + ip1 = ip2++; + } + if ( tranSet.size() > 1 ) // points with different transition coincide + { + bool isIN = tranSet.count( Trans_IN ); + bool isOUT = tranSet.count( Trans_OUT ); + if ( isIN && isOUT ) + (*ip1)._transition = Trans_TANGENT; + else + (*ip1)._transition = isIN ? Trans_IN : Trans_OUT; + } + } + } + //================================================================================ + /* + * Return "is OUT" state for nodes before the given intersection point + */ + bool GridLine::GetIsOutBefore( multiset< IntersectionPoint >::iterator ip, bool prevIsOut ) + { + if ( ip->_transition == Trans_IN ) + return true; + if ( ip->_transition == Trans_OUT ) + return false; + if ( ip->_transition == Trans_APEX ) + { + // singularity point (apex of a cone) + if ( _intPoints.size() == 1 || ip == _intPoints.begin() ) + return true; + multiset< IntersectionPoint >::iterator ipBef = ip, ipAft = ++ip; + if ( ipAft == _intPoints.end() ) + return false; + --ipBef; + if ( ipBef->_transition != ipAft->_transition ) + return ( ipBef->_transition == Trans_OUT ); + return ( ipBef->_transition != Trans_OUT ); + } + return prevIsOut; // _transition == Trans_TANGENT + } + //================================================================================ + /* + * Return an iterator on GridLine's in a given direction + */ + LineIndexer Grid::GetLineIndexer(size_t iDir) const + { + const size_t indices[] = { 1,2,0, 0,2,1, 0,1,2 }; + const string s[] = { "X", "Y", "Z" }; + LineIndexer li( _coords[0].size(), _coords[1].size(), _coords[2].size(), + indices[iDir*3], indices[iDir*3+1], indices[iDir*3+2], + s[indices[iDir*3]], s[indices[iDir*3+1]], s[indices[iDir*3+2]]); + return li; + } + //============================================================================= + /* + * Creates GridLine's of the grid + */ + void Grid::SetCoordinates(const vector& xCoords, + const vector& yCoords, + const vector& zCoords, + const TopoDS_Shape& shape) + { + _coords[0] = xCoords; + _coords[1] = yCoords; + _coords[2] = zCoords; + + // compute tolerance + _minCellSize = Precision::Infinite(); + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + for ( size_t i = 1; i < _coords[ iDir ].size(); ++i ) + { + double cellLen = _coords[ iDir ][ i ] - _coords[ iDir ][ i-1 ]; + if ( cellLen < _minCellSize ) + _minCellSize = cellLen; + } + } + if ( _minCellSize < Precision::Confusion() ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Too small cell size: ") << _tol ); + _tol = _minCellSize / 1000.; + + // attune grid extremities to shape bounding box computed by vertices + Bnd_Box shapeBox; + for ( TopExp_Explorer vExp( shape, TopAbs_VERTEX ); vExp.More(); vExp.Next() ) + shapeBox.Add( BRep_Tool::Pnt( TopoDS::Vertex( vExp.Current() ))); + + double sP[6]; // aXmin, aYmin, aZmin, aXmax, aYmax, aZmax + shapeBox.Get(sP[0],sP[1],sP[2],sP[3],sP[4],sP[5]); + double* cP[6] = { &_coords[0].front(), &_coords[1].front(), &_coords[2].front(), + &_coords[0].back(), &_coords[1].back(), &_coords[2].back() }; + for ( int i = 0; i < 6; ++i ) + if ( fabs( sP[i] - *cP[i] ) < _tol ) + *cP[i] = sP[i] + _tol/1000. * ( i < 3 ? +1 : -1 ); + + // create lines + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + _lines[iDir].resize( li.NbLines() ); + double len = _coords[ iDir ].back() - _coords[iDir].front(); + gp_Vec dir( iDir==0, iDir==1, iDir==2 ); + for ( ; li.More(); ++li ) + { + GridLine& gl = _lines[iDir][ li.LineIndex() ]; + gl._line.SetLocation(gp_Pnt(_coords[0][li.I()], _coords[1][li.J()], _coords[2][li.K()])); + gl._line.SetDirection( dir ); + gl._length = len; + } + } + } + //================================================================================ + /* + * Creates all nodes + */ + void Grid::ComputeNodes(SMESH_MesherHelper& helper) + { + // state of each node of the grid relative to the geomerty + const size_t nbGridNodes = _coords[0].size() * _coords[1].size() * _coords[2].size(); + vector< bool > isNodeOut( nbGridNodes, false ); + _nodes.resize( nbGridNodes, 0 ); + _isBndNode.resize( nbGridNodes, false ); + + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + + // find out a shift of node index while walking along a GridLine in this direction + li.SetIndexOnLine( 0 ); + size_t nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); + li.SetIndexOnLine( 1 ); + const size_t nShift = NodeIndex( li.I(), li.J(), li.K() ) - nIndex0; + + const vector & coords = _coords[ iDir ]; + for ( ; li.More(); ++li ) // loop on lines in iDir + { + li.SetIndexOnLine( 0 ); + nIndex0 = NodeIndex( li.I(), li.J(), li.K() ); + + GridLine& line = _lines[ iDir ][ li.LineIndex() ]; + line.RemoveExcessIntPoints( _tol ); + multiset< IntersectionPoint >& intPnts = _lines[ iDir ][ li.LineIndex() ]._intPoints; + multiset< IntersectionPoint >::iterator ip = intPnts.begin(); + + bool isOut = true; + const double* nodeCoord = & coords[0], *coord0 = nodeCoord, *coordEnd = coord0 + coords.size(); + double nodeParam = 0; + for ( ; ip != intPnts.end(); ++ip ) + { + // set OUT state or just skip IN nodes before ip + if ( nodeParam < ip->_paramOnLine - _tol ) + { + isOut = line.GetIsOutBefore( ip, isOut ); + + while ( nodeParam < ip->_paramOnLine - _tol ) + { + if ( isOut ) + isNodeOut[ nIndex0 + nShift * ( nodeCoord-coord0 ) ] = isOut; + if ( ++nodeCoord < coordEnd ) + nodeParam = *nodeCoord - *coord0; + else + break; + } + if ( nodeCoord == coordEnd ) break; + } + // create a mesh node on a GridLine at ip if it does not coincide with a grid node + if ( nodeParam > ip->_paramOnLine + _tol ) + { + li.SetIndexOnLine( 0 ); + double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]}; + xyz[ li._iConst ] += ip->_paramOnLine; + ip->_node = helper.AddNode( xyz[0], xyz[1], xyz[2] ); + ip->_indexOnLine = nodeCoord-coord0-1; + } + // create a mesh node at ip concident with a grid node + else + { + int nodeIndex = nIndex0 + nShift * ( nodeCoord-coord0 ); + if ( ! _nodes[ nodeIndex ] ) + { + li.SetIndexOnLine( nodeCoord-coord0 ); + double xyz[3] = { _coords[0][ li.I() ], _coords[1][ li.J() ], _coords[2][ li.K() ]}; + _nodes[ nodeIndex ] = helper.AddNode( xyz[0], xyz[1], xyz[2] ); + _isBndNode[ nodeIndex ] = true; + } + //ip->_node = _nodes[ nodeIndex ]; + ip->_indexOnLine = nodeCoord-coord0; + if ( ++nodeCoord < coordEnd ) + nodeParam = *nodeCoord - *coord0; + } + } + // set OUT state to nodes after the last ip + for ( ; nodeCoord < coordEnd; ++nodeCoord ) + isNodeOut[ nIndex0 + nShift * ( nodeCoord-coord0 ) ] = true; + } + } + + // Create mesh nodes at !OUT nodes of the grid + + for ( size_t z = 0; z < _coords[2].size(); ++z ) + for ( size_t y = 0; y < _coords[1].size(); ++y ) + for ( size_t x = 0; x < _coords[0].size(); ++x ) + { + size_t nodeIndex = NodeIndex( x, y, z ); + if ( !isNodeOut[ nodeIndex ] && !_nodes[ nodeIndex] ) + _nodes[ nodeIndex ] = helper.AddNode( _coords[0][x], _coords[1][y], _coords[2][z] ); + } + +#ifdef _MY_DEBUG_ + // check validity of transitions + const char* trName[] = { "TANGENT", "IN", "OUT", "APEX" }; + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + LineIndexer li = GetLineIndexer( iDir ); + for ( ; li.More(); ++li ) + { + multiset< IntersectionPoint >& intPnts = _lines[ iDir ][ li.LineIndex() ]._intPoints; + if ( intPnts.empty() ) continue; + if ( intPnts.size() == 1 ) + { + if ( intPnts.begin()->_transition != Trans_TANGENT && + intPnts.begin()->_transition != Trans_APEX ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong SOLE transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.begin()->_transition] ); + } + else + { + if ( intPnts.begin()->_transition == Trans_OUT ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong START transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.begin()->_transition ]); + if ( intPnts.rbegin()->_transition == Trans_IN ) + throw SMESH_ComputeError (COMPERR_ALGO_FAILED, + SMESH_Comment("Wrong END transition of GridLine (") + << li._curInd[li._iVar1] << ", " << li._curInd[li._iVar2] + << ") along " << li._nameConst + << ": " << trName[ intPnts.rbegin()->_transition ]); + } + } + } +#endif + } + + //============================================================================= + /* + * Checks if the face is encosed by the grid + */ + bool FaceGridIntersector::IsInGrid(const Bnd_Box& gridBox) + { + double x0,y0,z0, x1,y1,z1; + const Bnd_Box& faceBox = GetFaceBndBox(); + faceBox.Get(x0,y0,z0, x1,y1,z1); + + if ( !gridBox.IsOut( gp_Pnt( x0,y0,z0 )) && + !gridBox.IsOut( gp_Pnt( x1,y1,z1 ))) + return true; + + double X0,Y0,Z0, X1,Y1,Z1; + gridBox.Get(X0,Y0,Z0, X1,Y1,Z1); + double faceP[6] = { x0,y0,z0, x1,y1,z1 }; + double gridP[6] = { X0,Y0,Z0, X1,Y1,Z1 }; + gp_Dir axes[3] = { gp::DX(), gp::DY(), gp::DZ() }; + for ( int iDir = 0; iDir < 6; ++iDir ) + { + if ( iDir < 3 && gridP[ iDir ] <= faceP[ iDir ] ) continue; + if ( iDir >= 3 && gridP[ iDir ] >= faceP[ iDir ] ) continue; + + // check if the face intersects a side of a gridBox + + gp_Pnt p = iDir < 3 ? gp_Pnt( X0,Y0,Z0 ) : gp_Pnt( X1,Y1,Z1 ); + gp_Ax1 norm( p, axes[ iDir % 3 ] ); + if ( iDir < 3 ) norm.Reverse(); + + gp_XYZ O = norm.Location().XYZ(), N = norm.Direction().XYZ(); + + TopLoc_Location loc = _face.Location(); + Handle(Poly_Triangulation) aPoly = BRep_Tool::Triangulation(_face,loc); + if ( !aPoly.IsNull() ) + { + if ( !loc.IsIdentity() ) + { + norm.Transform( loc.Transformation().Inverted() ); + O = norm.Location().XYZ(), N = norm.Direction().XYZ(); + } + const double deflection = aPoly->Deflection(); + + const TColgp_Array1OfPnt& nodes = aPoly->Nodes(); + for ( int i = nodes.Lower(); i <= nodes.Upper(); ++i ) + if (( nodes( i ).XYZ() - O ) * N > _grid->_tol + deflection ) + return false; + } + else + { + BRepAdaptor_Surface surf( _face ); + double u0, u1, v0, v1, du, dv, u, v; + BRepTools::UVBounds( _face, u0, u1, v0, v1); + if ( surf.GetType() == GeomAbs_Plane ) { + du = u1 - u0, dv = v1 - v0; + } + else { + du = surf.UResolution( _grid->_minCellSize / 10. ); + dv = surf.VResolution( _grid->_minCellSize / 10. ); + } + for ( u = u0, v = v0; u <= u1 && v <= v1; u += du, v += dv ) + { + gp_Pnt p = surf.Value( u, v ); + if (( p.XYZ() - O ) * N > _grid->_tol ) + { + TopAbs_State state = GetCurveFaceIntersector()->ClassifyUVPoint(gp_Pnt2d( u, v )); + if ( state == TopAbs_IN || state == TopAbs_ON ) + return false; + } + } + } + } + return true; + } + //============================================================================= + /* + * Intersects TopoDS_Face with all GridLine's + */ + void FaceGridIntersector::Intersect() + { + FaceLineIntersector intersector; + intersector._surfaceInt = GetCurveFaceIntersector(); + intersector._tol = _grid->_tol; + intersector._transOut = _face.Orientation() == TopAbs_REVERSED ? Trans_IN : Trans_OUT; + intersector._transIn = _face.Orientation() == TopAbs_REVERSED ? Trans_OUT : Trans_IN; + + typedef void (FaceLineIntersector::* PIntFun )(const GridLine& gridLine); + PIntFun interFunction; + + BRepAdaptor_Surface surf( _face ); + switch ( surf.GetType() ) { + case GeomAbs_Plane: + intersector._plane = surf.Plane(); + interFunction = &FaceLineIntersector::IntersectWithPlane; + break; + case GeomAbs_Cylinder: + intersector._cylinder = surf.Cylinder(); + interFunction = &FaceLineIntersector::IntersectWithCylinder; + break; + case GeomAbs_Cone: + intersector._cone = surf.Cone(); + interFunction = &FaceLineIntersector::IntersectWithCone; + break; + case GeomAbs_Sphere: + intersector._sphere = surf.Sphere(); + interFunction = &FaceLineIntersector::IntersectWithSphere; + break; + case GeomAbs_Torus: + intersector._torus = surf.Torus(); + interFunction = &FaceLineIntersector::IntersectWithTorus; + break; + default: + interFunction = &FaceLineIntersector::IntersectWithSurface; + } + + _intersections.clear(); + for ( int iDir = 0; iDir < 3; ++iDir ) // loop on 3 line directions + { + if ( surf.GetType() == GeomAbs_Plane ) + { + // check if all lines in this direction are parallel to a plane + if ( intersector._plane.Axis().IsNormal( _grid->_lines[iDir][0]._line.Position(), + Precision::Angular())) + continue; + // find out a transition, that is the same for all lines of a direction + gp_Dir plnNorm = intersector._plane.Axis().Direction(); + gp_Dir lineDir = _grid->_lines[iDir][0]._line.Direction(); + intersector._transition = + ( plnNorm * lineDir < 0 ) ? intersector._transIn : intersector._transOut; + } + if ( surf.GetType() == GeomAbs_Cylinder ) + { + // check if all lines in this direction are parallel to a cylinder + if ( intersector._cylinder.Axis().IsParallel( _grid->_lines[iDir][0]._line.Position(), + Precision::Angular())) + continue; + } + + // intersect the grid lines with the face + for ( size_t iL = 0; iL < _grid->_lines[iDir].size(); ++iL ) + { + GridLine& gridLine = _grid->_lines[iDir][iL]; + if ( _bndBox.IsOut( gridLine._line )) continue; + + intersector._intPoints.clear(); + (intersector.*interFunction)( gridLine ); + for ( size_t i = 0; i < intersector._intPoints.size(); ++i ) + _intersections.push_back( make_pair( &gridLine, intersector._intPoints[i] )); + } + } + } + //================================================================================ + /* + * Return true if (_u,_v) is on the face + */ + bool FaceLineIntersector::UVIsOnFace() const + { + TopAbs_State state = _surfaceInt->ClassifyUVPoint(gp_Pnt2d( _u,_v )); + return ( state == TopAbs_IN || state == TopAbs_ON ); + } + //================================================================================ + /* + * Store an intersection if it is IN or ON the face + */ + void FaceLineIntersector::addIntPoint(const bool toClassify) + { + if ( !toClassify || UVIsOnFace() ) + { + IntersectionPoint p; + p._paramOnLine = _w; + p._transition = _transition; + _intPoints.push_back( p ); + } + } + //================================================================================ + /* + * Intersect a line with a plane + */ + void FaceLineIntersector::IntersectWithPlane (const GridLine& gridLine) + { + IntAna_IntConicQuad linPlane( gridLine._line, _plane, Precision::Angular()); + _w = linPlane.ParamOnConic(1); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_plane, linPlane.Point(1) ,_u,_v); + addIntPoint(); + } + } + //================================================================================ + /* + * Intersect a line with a cylinder + */ + void FaceLineIntersector::IntersectWithCylinder(const GridLine& gridLine) + { + IntAna_IntConicQuad linCylinder( gridLine._line,_cylinder); + if ( linCylinder.IsDone() && linCylinder.NbPoints() > 0 ) + { + _w = linCylinder.ParamOnConic(1); + if ( linCylinder.NbPoints() == 1 ) + _transition = Trans_TANGENT; + else + _transition = _w < linCylinder.ParamOnConic(2) ? _transIn : _transOut; + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_cylinder, linCylinder.Point(1) ,_u,_v); + addIntPoint(); + } + if ( linCylinder.NbPoints() > 1 ) + { + _w = linCylinder.ParamOnConic(2); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_cylinder, linCylinder.Point(2) ,_u,_v); + _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; + addIntPoint(); + } + } + } + } + //================================================================================ + /* + * Intersect a line with a cone + */ + void FaceLineIntersector::IntersectWithCone (const GridLine& gridLine) + { + IntAna_IntConicQuad linCone(gridLine._line,_cone); + if ( !linCone.IsDone() ) return; + gp_Pnt P; + gp_Vec du, dv, norm; + for ( int i = 1; i <= linCone.NbPoints(); ++i ) + { + _w = linCone.ParamOnConic( i ); + if ( !isParamOnLineOK( gridLine._length )) continue; + ElSLib::Parameters(_cone, linCone.Point(i) ,_u,_v); + if ( UVIsOnFace() ) + { + ElSLib::D1( _u, _v, _cone, P, du, dv ); + norm = du ^ dv; + double normSize2 = norm.SquareMagnitude(); + if ( normSize2 > Precision::Angular() * Precision::Angular() ) + { + double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); + cos /= sqrt( normSize2 ); + if ( cos < -Precision::Angular() ) + _transition = _transIn; + else if ( cos > Precision::Angular() ) + _transition = _transOut; + else + _transition = Trans_TANGENT; + } + else + { + _transition = Trans_APEX; + } + addIntPoint( /*toClassify=*/false); + } + } + } + //================================================================================ + /* + * Intersect a line with a sphere + */ + void FaceLineIntersector::IntersectWithSphere (const GridLine& gridLine) + { + IntAna_IntConicQuad linSphere(gridLine._line,_sphere); + if ( linSphere.IsDone() && linSphere.NbPoints() > 0 ) + { + _w = linSphere.ParamOnConic(1); + if ( linSphere.NbPoints() == 1 ) + _transition = Trans_TANGENT; + else + _transition = _w < linSphere.ParamOnConic(2) ? _transIn : _transOut; + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_sphere, linSphere.Point(1) ,_u,_v); + addIntPoint(); + } + if ( linSphere.NbPoints() > 1 ) + { + _w = linSphere.ParamOnConic(2); + if ( isParamOnLineOK( gridLine._length )) + { + ElSLib::Parameters(_sphere, linSphere.Point(2) ,_u,_v); + _transition = ( _transition == Trans_OUT ) ? Trans_IN : Trans_OUT; + addIntPoint(); + } + } + } + } + //================================================================================ + /* + * Intersect a line with a torus + */ + void FaceLineIntersector::IntersectWithTorus (const GridLine& gridLine) + { + IntAna_IntLinTorus linTorus(gridLine._line,_torus); + if ( !linTorus.IsDone()) return; + gp_Pnt P; + gp_Vec du, dv, norm; + for ( int i = 1; i <= linTorus.NbPoints(); ++i ) + { + _w = linTorus.ParamOnLine( i ); + if ( !isParamOnLineOK( gridLine._length )) continue; + linTorus.ParamOnTorus( i, _u,_v ); + if ( UVIsOnFace() ) + { + ElSLib::D1( _u, _v, _torus, P, du, dv ); + norm = du ^ dv; + double normSize = norm.Magnitude(); + double cos = norm.XYZ() * gridLine._line.Direction().XYZ(); + cos /= normSize; + if ( cos < -Precision::Angular() ) + _transition = _transIn; + else if ( cos > Precision::Angular() ) + _transition = _transOut; + else + _transition = Trans_TANGENT; + addIntPoint( /*toClassify=*/false); + } + } + } + //================================================================================ + /* + * Intersect a line with a non-analytical surface + */ + void FaceLineIntersector::IntersectWithSurface (const GridLine& gridLine) + { + _surfaceInt->Perform( gridLine._line, 0.0, gridLine._length ); + if ( !_surfaceInt->IsDone() ) return; + for ( int i = 1; i <= _surfaceInt->NbPnt(); ++i ) + { + _transition = Transition( _surfaceInt->Transition( i ) ); + _w = _surfaceInt->WParameter( i ); + addIntPoint(/*toClassify=*/false); + } + } + //================================================================================ + /* + * check if its face can be safely intersected in a thread + */ + bool FaceGridIntersector::IsThreadSafe(set< const Standard_Transient* >& noSafeTShapes) const + { + bool isSafe = true; + + // check surface + TopLoc_Location loc; + Handle(Geom_Surface) surf = BRep_Tool::Surface( _face, loc ); + Handle(Geom_RectangularTrimmedSurface) ts = + Handle(Geom_RectangularTrimmedSurface)::DownCast( surf ); + while( !ts.IsNull() ) { + surf = ts->BasisSurface(); + ts = Handle(Geom_RectangularTrimmedSurface)::DownCast(surf); + } + if ( surf->IsKind( STANDARD_TYPE(Geom_BSplineSurface )) || + surf->IsKind( STANDARD_TYPE(Geom_BezierSurface ))) + if ( !noSafeTShapes.insert((const Standard_Transient*) _face.TShape() ).second ) + isSafe = false; + + double f, l; + TopExp_Explorer exp( _face, TopAbs_EDGE ); + for ( ; exp.More(); exp.Next() ) + { + bool edgeIsSafe = true; + const TopoDS_Edge& e = TopoDS::Edge( exp.Current() ); + // check 3d curve + { + Handle(Geom_Curve) c = BRep_Tool::Curve( e, loc, f, l); + if ( !c.IsNull() ) + { + Handle(Geom_TrimmedCurve) tc = Handle(Geom_TrimmedCurve)::DownCast(c); + while( !tc.IsNull() ) { + c = tc->BasisCurve(); + tc = Handle(Geom_TrimmedCurve)::DownCast(c); + } + if ( c->IsKind( STANDARD_TYPE(Geom_BSplineCurve )) || + c->IsKind( STANDARD_TYPE(Geom_BezierCurve ))) + edgeIsSafe = false; + } + } + // check 2d curve + if ( edgeIsSafe ) + { + Handle(Geom2d_Curve) c2 = BRep_Tool::CurveOnSurface( e, surf, loc, f, l); + if ( !c2.IsNull() ) + { + Handle(Geom2d_TrimmedCurve) tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); + while( !tc.IsNull() ) { + c2 = tc->BasisCurve(); + tc = Handle(Geom2d_TrimmedCurve)::DownCast(c2); + } + if ( c2->IsKind( STANDARD_TYPE(Geom2d_BSplineCurve )) || + c2->IsKind( STANDARD_TYPE(Geom2d_BezierCurve ))) + edgeIsSafe = false; + } + } + if ( !edgeIsSafe && !noSafeTShapes.insert((const Standard_Transient*) e.TShape() ).second ) + isSafe = false; + } + return isSafe; + } + //================================================================================ + /*! + * \brief Creates topology of the hexahedron + */ + Hexahedron::Hexahedron(const double sizeThreshold, Grid* grid) + : _grid( grid ), _sizeThreshold( sizeThreshold ), _nbIntNodes(0) + { + _polygons.reserve(100); // to avoid reallocation; + + //set nodes shift within grid->_nodes from the node 000 + size_t dx = _grid->NodeIndexDX(); + size_t dy = _grid->NodeIndexDY(); + size_t dz = _grid->NodeIndexDZ(); + size_t i000 = 0; + size_t i100 = i000 + dx; + size_t i010 = i000 + dy; + size_t i110 = i010 + dx; + size_t i001 = i000 + dz; + size_t i101 = i100 + dz; + size_t i011 = i010 + dz; + size_t i111 = i110 + dz; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V000 )] = i000; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V100 )] = i100; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V010 )] = i010; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V110 )] = i110; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V001 )] = i001; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V101 )] = i101; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V011 )] = i011; + _nodeShift[ SMESH_Block::ShapeIndex( SMESH_Block::ID_V111 )] = i111; + + vector< int > idVec; + // set nodes to links + for ( int linkID = SMESH_Block::ID_Ex00; linkID <= SMESH_Block::ID_E11z; ++linkID ) + { + SMESH_Block::GetEdgeVertexIDs( linkID, idVec ); + _Link& link = _hexLinks[ SMESH_Block::ShapeIndex( linkID )]; + link._nodes[0] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[0] )]; + link._nodes[1] = &_hexNodes[ SMESH_Block::ShapeIndex( idVec[1] )]; + link._intNodes.reserve( 10 ); // to avoid reallocation + link._splits.reserve( 10 ); + } + + // set links to faces + int interlace[4] = { 0, 3, 1, 2 }; // to walk by links around a face: { u0, 1v, u1, 0v } + for ( int faceID = SMESH_Block::ID_Fxy0; faceID <= SMESH_Block::ID_F1yz; ++faceID ) + { + SMESH_Block::GetFaceEdgesIDs( faceID, idVec ); + _Face& quad = _hexQuads[ SMESH_Block::ShapeIndex( faceID )]; + bool revFace = ( faceID == SMESH_Block::ID_Fxy0 || + faceID == SMESH_Block::ID_Fx1z || + faceID == SMESH_Block::ID_F0yz ); + quad._links.resize(4); + vector<_OrientedLink>::iterator frwLinkIt = quad._links.begin(); + vector<_OrientedLink>::reverse_iterator revLinkIt = quad._links.rbegin(); + for ( int i = 0; i < 4; ++i ) + { + bool revLink = revFace; + if ( i > 1 ) // reverse links u1 and v0 + revLink = !revLink; + _OrientedLink& link = revFace ? *revLinkIt++ : *frwLinkIt++; + link = _OrientedLink( & _hexLinks[ SMESH_Block::ShapeIndex( idVec[interlace[i]] )], + revLink ); + } + } + } + //================================================================================ + /*! + * \brief Copy constructor + */ + Hexahedron::Hexahedron( const Hexahedron& other ) + :_grid( other._grid ), _sizeThreshold( other._sizeThreshold ), _nbIntNodes(0) + { + _polygons.reserve(100); // to avoid reallocation; + + for ( int i = 0; i < 8; ++i ) + _nodeShift[i] = other._nodeShift[i]; + + for ( int i = 0; i < 12; ++i ) + { + const _Link& srcLink = other._hexLinks[ i ]; + _Link& tgtLink = this->_hexLinks[ i ]; + tgtLink._nodes[0] = _hexNodes + ( srcLink._nodes[0] - other._hexNodes ); + tgtLink._nodes[1] = _hexNodes + ( srcLink._nodes[1] - other._hexNodes ); + tgtLink._intNodes.reserve( 10 ); // to avoid reallocation + tgtLink._splits.reserve( 10 ); + } + + for ( int i = 0; i < 6; ++i ) + { + const _Face& srcQuad = other._hexQuads[ i ]; + _Face& tgtQuad = this->_hexQuads[ i ]; + tgtQuad._links.resize(4); + for ( int j = 0; j < 4; ++j ) + { + const _OrientedLink& srcLink = srcQuad._links[ j ]; + _OrientedLink& tgtLink = tgtQuad._links[ j ]; + tgtLink._reverse = srcLink._reverse; + tgtLink._link = _hexLinks + ( srcLink._link - other._hexLinks ); + } + } + } + + //================================================================================ + /*! + * \brief Initializes its data by given grid cell + */ + void Hexahedron::init( size_t i, size_t j, size_t k ) + { + _i = i; _j = j; _k = k; + // set nodes of grid to nodes of the hexahedron and + // count nodes at hexahedron corners located IN and ON geometry + _nbCornerNodes = _nbBndNodes = 0; + _origNodeInd = _grid->NodeIndex( i,j,k ); + for ( int iN = 0; iN < 8; ++iN ) + { + _hexNodes[iN]._node = _grid->_nodes[ _origNodeInd + _nodeShift[iN] ]; + _nbCornerNodes += bool( _hexNodes[iN]._node ); + _nbBndNodes += _grid->_isBndNode[ _origNodeInd + _nodeShift[iN] ]; + } + + _sideLength[0] = _grid->_coords[0][i+1] - _grid->_coords[0][i]; + _sideLength[1] = _grid->_coords[1][j+1] - _grid->_coords[1][j]; + _sideLength[2] = _grid->_coords[2][k+1] - _grid->_coords[2][k]; + + if ( _nbCornerNodes < 8 && _nbIntNodes + _nbCornerNodes > 3) + { + _Link split; + // create sub-links (_splits) by splitting links with _intNodes + for ( int iLink = 0; iLink < 12; ++iLink ) + { + _Link& link = _hexLinks[ iLink ]; + link._splits.clear(); + split._nodes[ 0 ] = link._nodes[0]; + for ( size_t i = 0; i < link._intNodes.size(); ++ i ) + { + if ( split._nodes[ 0 ]->Node() ) + { + split._nodes[ 1 ] = &link._intNodes[i]; + link._splits.push_back( split ); + } + split._nodes[ 0 ] = &link._intNodes[i]; + } + if ( link._nodes[ 1 ]->Node() && split._nodes[ 0 ]->Node() ) + { + split._nodes[ 1 ] = link._nodes[1]; + link._splits.push_back( split ); + } + } + } + } + //================================================================================ + /*! + * \brief Initializes its data by given grid cell (countered from zero) + */ + void Hexahedron::init( size_t iCell ) + { + size_t iNbCell = _grid->_coords[0].size() - 1; + size_t jNbCell = _grid->_coords[1].size() - 1; + _i = iCell % iNbCell; + _j = ( iCell % ( iNbCell * jNbCell )) / iNbCell; + _k = iCell / iNbCell / jNbCell; + init( _i, _j, _k ); + } + + //================================================================================ + /*! + * \brief Compute mesh volumes resulted from intersection of the Hexahedron + */ + void Hexahedron::ComputeElements() + { + Init(); + + if ( _nbCornerNodes + _nbIntNodes < 4 ) + return; + + if ( _nbBndNodes == _nbCornerNodes && isInHole() ) + return; + + _polygons.clear(); + + vector polyhedraNodes; + vector quantities; + + // create polygons from quadrangles and get their nodes + + vector<_Node*> nodes; + nodes.reserve( _nbCornerNodes + _nbIntNodes ); + + _Link polyLink; + polyLink._faces.reserve( 1 ); + + for ( int iF = 0; iF < 6; ++iF ) // loop on 6 sides of a hexahedron + { + const _Face& quad = _hexQuads[ iF ] ; + + _polygons.resize( _polygons.size() + 1 ); + _Face& polygon = _polygons.back(); + polygon._links.clear(); + polygon._polyLinks.clear(); polygon._polyLinks.reserve( 10 ); + + // add splits of a link to a polygon and collect info on nodes + //int nbIn = 0, nbOut = 0, nbCorners = 0; + nodes.clear(); + for ( int iE = 0; iE < 4; ++iE ) // loop on 4 sides of a quadrangle + { + int nbSpits = quad._links[ iE ].NbResultLinks(); + for ( int iS = 0; iS < nbSpits; ++iS ) + { + _OrientedLink split = quad._links[ iE ].ResultLink( iS ); + _Node* n = split.FirstNode(); + if ( !polygon._links.empty() ) + { + _Node* nPrev = polygon._links.back().LastNode(); + if ( nPrev != n ) + { + polyLink._nodes[0] = nPrev; + polyLink._nodes[1] = n; + polygon._polyLinks.push_back( polyLink ); + polygon._links.push_back( _OrientedLink( &polygon._polyLinks.back() )); + nodes.push_back( nPrev ); + } + } + polygon._links.push_back( split ); + nodes.push_back( n ); + } + } + if ( polygon._links.size() > 1 ) + { + _Node* n1 = polygon._links.back().LastNode(); + _Node* n2 = polygon._links.front().FirstNode(); + if ( n1 != n2 ) + { + polyLink._nodes[0] = n1; + polyLink._nodes[1] = n2; + polygon._polyLinks.push_back( polyLink ); + polygon._links.push_back( _OrientedLink( &polygon._polyLinks.back() )); + nodes.push_back( n1 ); + } + // add polygon to its links + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + polygon._links[ iL ]._link->_faces.push_back( &polygon ); + // store polygon nodes + quantities.push_back( nodes.size() ); + for ( size_t i = 0; i < nodes.size(); ++i ) + polyhedraNodes.push_back( nodes[i]->Node() ); + } + else + { + _polygons.resize( _polygons.size() - 1 ); + } + } + + // create polygons closing holes in a polyhedron + + // find free links + vector< _OrientedLink* > freeLinks; + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + _Face& polygon = _polygons[ iP ]; + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + if ( polygon._links[ iL ]._link->_faces.size() < 2 ) + freeLinks.push_back( & polygon._links[ iL ]); + } + // make closed chains of free links + int nbFreeLinks = freeLinks.size(); + if ( 0 < nbFreeLinks && nbFreeLinks < 3 ) return; + while ( nbFreeLinks > 0 ) + { + nodes.clear(); + _polygons.resize( _polygons.size() + 1 ); + _Face& polygon = _polygons.back(); + polygon._links.clear(); + + // get a remaining link to start from + _OrientedLink* curLink = 0; + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if (( curLink = freeLinks[ iL ] )) + freeLinks[ iL ] = 0; + nodes.push_back( curLink->LastNode() ); + polygon._links.push_back( *curLink ); + + // find all links connected to curLink + _Node* curNode = 0; + do + { + curNode = curLink->FirstNode(); + curLink = 0; + for ( size_t iL = 0; iL < freeLinks.size() && !curLink; ++iL ) + if ( freeLinks[ iL ] && freeLinks[ iL ]->LastNode() == curNode ) + { + curLink = freeLinks[ iL ]; + freeLinks[ iL ] = 0; + nodes.push_back( curNode ); + polygon._links.push_back( *curLink ); + } + } while ( curLink ); + + nbFreeLinks -= polygon._links.size(); + + if ( curNode != nodes.front() || polygon._links.size() < 3 ) + return; // closed polygon not found -> invalid polyhedron + + quantities.push_back( nodes.size() ); + for ( size_t i = 0; i < nodes.size(); ++i ) + polyhedraNodes.push_back( nodes[i]->Node() ); + + // add polygon to its links and reverse links + for ( size_t i = 0; i < polygon._links.size(); ++i ) + { + polygon._links[i].Reverse(); + polygon._links[i]._link->_faces.push_back( &polygon ); + } + + //const size_t firstPoly = _polygons.size(); + } + + if ( ! checkPolyhedronSize() ) + { + return; + } + + // create a classic cell if possible + const int nbNodes = _nbCornerNodes + _nbIntNodes; + bool isClassicElem = false; + if ( nbNodes == 8 && _polygons.size() == 6 ) isClassicElem = addHexa(); + else if ( nbNodes == 4 && _polygons.size() == 4 ) isClassicElem = addTetra(); + else if ( nbNodes == 6 && _polygons.size() == 5 ) isClassicElem = addPenta(); + else if ( nbNodes == 5 && _polygons.size() == 5 ) isClassicElem = addPyra (); + if ( !isClassicElem ) + _volumeDefs.set( polyhedraNodes, quantities ); + } + //================================================================================ + /*! + * \brief Create elements in the mesh + */ + int Hexahedron::MakeElements(SMESH_MesherHelper& helper) + { + SMESHDS_Mesh* mesh = helper.GetMeshDS(); + + size_t nbCells[3] = { _grid->_coords[0].size() - 1, + _grid->_coords[1].size() - 1, + _grid->_coords[2].size() - 1 }; + const size_t nbGridCells = nbCells[0] *nbCells [1] * nbCells[2]; + vector< Hexahedron* > intersectedHex( nbGridCells, 0 ); + int nbIntHex = 0; + + // set intersection nodes from GridLine's to links of intersectedHex + int i,j,k, iDirOther[3][2] = {{ 1,2 },{ 0,2 },{ 0,1 }}; + for ( int iDir = 0; iDir < 3; ++iDir ) + { + int dInd[4][3] = { {0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} }; + dInd[1][ iDirOther[iDir][0] ] = -1; + dInd[2][ iDirOther[iDir][1] ] = -1; + dInd[3][ iDirOther[iDir][0] ] = -1; dInd[3][ iDirOther[iDir][1] ] = -1; + // loop on GridLine's parallel to iDir + LineIndexer lineInd = _grid->GetLineIndexer( iDir ); + for ( ; lineInd.More(); ++lineInd ) + { + GridLine& line = _grid->_lines[ iDir ][ lineInd.LineIndex() ]; + multiset< IntersectionPoint >::const_iterator ip = line._intPoints.begin(); + for ( ; ip != line._intPoints.end(); ++ip ) + { + if ( !ip->_node ) continue; + lineInd.SetIndexOnLine( ip->_indexOnLine ); + for ( int iL = 0; iL < 4; ++iL ) // loop on 4 cells sharing a link + { + i = int(lineInd.I()) + dInd[iL][0]; + j = int(lineInd.J()) + dInd[iL][1]; + k = int(lineInd.K()) + dInd[iL][2]; + if ( i < 0 || i >= nbCells[0] || + j < 0 || j >= nbCells[1] || + k < 0 || k >= nbCells[2] ) continue; + + const size_t hexIndex = _grid->CellIndex( i,j,k ); + Hexahedron *& hex = intersectedHex[ hexIndex ]; + if ( !hex) + { + hex = new Hexahedron( *this ); + hex->_i = i; + hex->_j = j; + hex->_k = k; + ++nbIntHex; + } + const int iLink = iL + iDir * 4; + hex->_hexLinks[iLink]._intNodes.push_back( _Node( 0, &(*ip) )); + hex->_nbIntNodes++; + } + } + } + } + + // add not split hexadrons to the mesh + int nbAdded = 0; + vector intHexInd( nbIntHex ); + nbIntHex = 0; + for ( size_t i = 0; i < intersectedHex.size(); ++i ) + { + Hexahedron * & hex = intersectedHex[ i ]; + if ( hex ) + { + intHexInd[ nbIntHex++ ] = i; + if ( hex->_nbIntNodes > 0 ) continue; + init( hex->_i, hex->_j, hex->_k ); + } + else + { + init( i ); + } + if ( _nbCornerNodes == 8 && ( _nbBndNodes < _nbCornerNodes || !isInHole() )) + { + // order of _hexNodes is defined by enum SMESH_Block::TShapeID + SMDS_MeshElement* el = + mesh->AddVolume( _hexNodes[0].Node(), _hexNodes[2].Node(), + _hexNodes[3].Node(), _hexNodes[1].Node(), + _hexNodes[4].Node(), _hexNodes[6].Node(), + _hexNodes[7].Node(), _hexNodes[5].Node() ); + mesh->SetMeshElementOnShape( el, helper.GetSubShapeID() ); + ++nbAdded; + if ( hex ) + { + delete hex; + intersectedHex[ i ] = 0; + --nbIntHex; + } + } + else if ( _nbCornerNodes > 3 && !hex ) + { + // all intersection of hex with geometry are at grid nodes + hex = new Hexahedron( *this ); + hex->init( i ); + intHexInd.push_back(0); + intHexInd[ nbIntHex++ ] = i; + } + } + + // add elements resulted from hexadron intersection +#ifdef WITH_TBB + intHexInd.resize( nbIntHex ); + tbb::parallel_for ( tbb::blocked_range( 0, nbIntHex ), + ParallelHexahedron( intersectedHex, intHexInd ), + tbb::simple_partitioner()); // ComputeElements() is called here + for ( size_t i = 0; i < intHexInd.size(); ++i ) + if ( Hexahedron * hex = intersectedHex[ intHexInd[ i ]] ) + nbAdded += hex->addElements( helper ); +#else + for ( size_t i = 0; i < intHexInd.size(); ++i ) + if ( Hexahedron * hex = intersectedHex[ intHexInd[ i ]] ) + { + hex->ComputeElements(); + nbAdded += hex->addElements( helper ); + } +#endif + + for ( size_t i = 0; i < intersectedHex.size(); ++i ) + if ( intersectedHex[ i ] ) + delete intersectedHex[ i ]; + + return nbAdded; + } + + //================================================================================ + /*! + * \brief Adds computed elements to the mesh + */ + int Hexahedron::addElements(SMESH_MesherHelper& helper) + { + int nbAdded = 0; + // add elements resulted from hexahedron intersection + //for ( size_t i = 0; i < _volumeDefs.size(); ++i ) + { + vector< const SMDS_MeshNode* >& nodes = _volumeDefs._nodes; + + if ( !_volumeDefs._quantities.empty() ) + { + helper.AddPolyhedralVolume( nodes, _volumeDefs._quantities ); + } + else + { + switch ( nodes.size() ) + { + case 8: helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3], + nodes[4],nodes[5],nodes[6],nodes[7] ); + break; + case 4: helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3] ); + break; + case 6: helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3], nodes[4],nodes[5] ); + break; + case 5: + helper.AddVolume( nodes[0],nodes[1],nodes[2],nodes[3],nodes[4] ); + break; + } + } + nbAdded += int ( _volumeDefs._nodes.size() > 0 ); + } + + return nbAdded; + } + //================================================================================ + /*! + * \brief Return true if the element is in a hole + */ + bool Hexahedron::isInHole() const + { + const int ijk[3] = { _i, _j, _k }; + IntersectionPoint curIntPnt; + + // consider a cell to be in a hole if all links in any direction + // comes OUT of geometry + for ( int iDir = 0; iDir < 3; ++iDir ) + { + const vector& coords = _grid->_coords[ iDir ]; + LineIndexer li = _grid->GetLineIndexer( iDir ); + li.SetIJK( _i,_j,_k ); + size_t lineIndex[4] = { li.LineIndex (), + li.LineIndex10(), + li.LineIndex01(), + li.LineIndex11() }; + bool allLinksOut = true, hasLinks = false; + for ( int iL = 0; iL < 4 && allLinksOut; ++iL ) // loop on 4 links parallel to iDir + { + const _Link& link = _hexLinks[ iL + 4*iDir ]; + // check transition of the first node of a link + const IntersectionPoint* firstIntPnt = 0; + if ( link._nodes[0]->Node() ) // 1st node is a hexa corner + { + curIntPnt._paramOnLine = coords[ ijk[ iDir ]] - coords[0]; + const GridLine& line = _grid->_lines[ iDir ][ lineIndex[ iL ]]; + multiset< IntersectionPoint >::const_iterator ip = + line._intPoints.upper_bound( curIntPnt ); + --ip; + firstIntPnt = &(*ip); + } + else if ( !link._intNodes.empty() ) + { + firstIntPnt = link._intNodes[0]._intPoint; + } + + if ( firstIntPnt ) + { + hasLinks = true; + allLinksOut = ( firstIntPnt->_transition == Trans_OUT ); + } + } + if ( hasLinks && allLinksOut ) + return true; + } + return false; + } + + //================================================================================ + /*! + * \brief Return true if a polyhedron passes _sizeThreshold criterion + */ + bool Hexahedron::checkPolyhedronSize() const + { + double volume = 0; + for ( size_t iP = 0; iP < _polygons.size(); ++iP ) + { + const _Face& polygon = _polygons[iP]; + gp_XYZ area (0,0,0); + SMESH_TNodeXYZ p1 ( polygon._links[ 0 ].FirstNode()->Node() ); + for ( size_t iL = 0; iL < polygon._links.size(); ++iL ) + { + SMESH_TNodeXYZ p2 ( polygon._links[ iL ].LastNode()->Node() ); + area += p1 ^ p2; + p1 = p2; + } + volume += p1 * area; + } + volume /= 6; + + double initVolume = _sideLength[0] * _sideLength[1] * _sideLength[2]; + + return volume > initVolume / _sizeThreshold; + } + //================================================================================ + /*! + * \brief Tries to create a hexahedron + */ + bool Hexahedron::addHexa() + { + if ( _polygons[0]._links.size() != 4 || + _polygons[1]._links.size() != 4 || + _polygons[2]._links.size() != 4 || + _polygons[3]._links.size() != 4 || + _polygons[4]._links.size() != 4 || + _polygons[5]._links.size() != 4 ) + return false; + const SMDS_MeshNode* nodes[8]; + int nbN = 0; + for ( int iL = 0; iL < 4; ++iL ) + { + // a base node + nodes[iL] = _polygons[0]._links[iL].FirstNode()->Node(); + ++nbN; + + // find a top node above the base node + _Link* link = _polygons[0]._links[iL]._link; + ASSERT( link->_faces.size() > 1 ); + // a quadrangle sharing with _polygons[0] + _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[0] )]; + for ( int i = 0; i < 4; ++i ) + if ( quad->_links[i]._link == link ) + { + // 1st node of a link opposite to in + nodes[iL+4] = quad->_links[(i+2)%4].FirstNode()->Node(); + ++nbN; + break; + } + } + if ( nbN == 8 ) + _volumeDefs.set( vector< const SMDS_MeshNode* >( nodes, nodes+8 )); + + return nbN == 8; + } + //================================================================================ + /*! + * \brief Tries to create a tetrahedron + */ + bool Hexahedron::addTetra() + { + const SMDS_MeshNode* nodes[4]; + nodes[0] = _polygons[0]._links[0].FirstNode()->Node(); + nodes[1] = _polygons[0]._links[1].FirstNode()->Node(); + nodes[2] = _polygons[0]._links[2].FirstNode()->Node(); + + _Link* link = _polygons[0]._links[0]._link; + ASSERT( link->_faces.size() > 1 ); + + // a triangle sharing with _polygons[0] + _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[0] )]; + for ( int i = 0; i < 3; ++i ) + if ( tria->_links[i]._link == link ) + { + nodes[3] = tria->_links[(i+1)%3].LastNode()->Node(); + _volumeDefs.set( vector< const SMDS_MeshNode* >( nodes, nodes+4 )); + return true; + } + + return false; + } + //================================================================================ + /*! + * \brief Tries to create a pentahedron + */ + bool Hexahedron::addPenta() + { + // find a base triangular face + int iTri = -1; + for ( int iF = 0; iF < 5 && iTri < 0; ++iF ) + if ( _polygons[ iF ]._links.size() == 3 ) + iTri = iF; + if ( iTri < 0 ) return false; + + // find nodes + const SMDS_MeshNode* nodes[6]; + int nbN = 0; + for ( int iL = 0; iL < 3; ++iL ) + { + // a base node + nodes[iL] = _polygons[ iTri ]._links[iL].FirstNode()->Node(); + ++nbN; + + // find a top node above the base node + _Link* link = _polygons[ iTri ]._links[iL]._link; + ASSERT( link->_faces.size() > 1 ); + // a quadrangle sharing with a base triangle + _Face* quad = link->_faces[ bool( link->_faces[0] == & _polygons[ iTri ] )]; + if ( quad->_links.size() != 4 ) return false; + for ( int i = 0; i < 4; ++i ) + if ( quad->_links[i]._link == link ) + { + // 1st node of a link opposite to in + nodes[iL+3] = quad->_links[(i+2)%4].FirstNode()->Node(); + ++nbN; + break; + } + } + if ( nbN == 6 ) + _volumeDefs.set( vector< const SMDS_MeshNode* >( nodes, nodes+6 )); + + return ( nbN == 6 ); + } + //================================================================================ + /*! + * \brief Tries to create a pyramid + */ + bool Hexahedron::addPyra() + { + // find a base quadrangle + int iQuad = -1; + for ( int iF = 0; iF < 5 && iQuad < 0; ++iF ) + if ( _polygons[ iF ]._links.size() == 4 ) + iQuad = iF; + if ( iQuad < 0 ) return false; + + // find nodes + const SMDS_MeshNode* nodes[5]; + nodes[0] = _polygons[iQuad]._links[0].FirstNode()->Node(); + nodes[1] = _polygons[iQuad]._links[1].FirstNode()->Node(); + nodes[2] = _polygons[iQuad]._links[2].FirstNode()->Node(); + nodes[3] = _polygons[iQuad]._links[3].FirstNode()->Node(); + + _Link* link = _polygons[iQuad]._links[0]._link; + ASSERT( link->_faces.size() > 1 ); + + // a triangle sharing with a base quadrangle + _Face* tria = link->_faces[ bool( link->_faces[0] == & _polygons[ iQuad ] )]; + if ( tria->_links.size() != 3 ) return false; + for ( int i = 0; i < 3; ++i ) + if ( tria->_links[i]._link == link ) + { + nodes[4] = tria->_links[(i+1)%3].LastNode()->Node(); + _volumeDefs.set( vector< const SMDS_MeshNode* >( nodes, nodes+5 )); + return true; + } + + return false; + } + +} // namespace + +//============================================================================= +/*! + * \brief Generates 3D structured Cartesian mesh in the internal part of + * solid shapes and polyhedral volumes near the shape boundary. + * \param theMesh - mesh to fill in + * \param theShape - a compound of all SOLIDs to mesh + * \retval bool - true in case of success + */ +//============================================================================= + +bool StdMeshers_Cartesian_3D::Compute(SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape) +{ + // The algorithm generates the mesh in following steps: + + // 1) Intersection of grid lines with the geometry boundary. + // This step allows to find out if a given node of the initial grid is + // inside or outside the geometry. + + // 2) For each cell of the grid, check how many of it's nodes are outside + // of the geometry boundary. Depending on a result of this check + // - skip a cell, if all it's nodes are outside + // - skip a cell, if it is too small according to the size threshold + // - add a hexahedron in the mesh, if all nodes are inside + // - add a polyhedron in the mesh, if some nodes are inside and some outside + + _computeCanceled = false; + + try + { + Grid grid; + + TopTools_MapOfShape faceMap; + for ( TopExp_Explorer fExp( theShape, TopAbs_FACE ); fExp.More(); fExp.Next() ) + if ( !faceMap.Add( fExp.Current() )) + faceMap.Remove( fExp.Current() ); // remove a face shared by two solids + + Bnd_Box shapeBox; + vector facesItersectors( faceMap.Extent() ); + TopTools_MapIteratorOfMapOfShape faceMppIt( faceMap ); + for ( int i = 0; faceMppIt.More(); faceMppIt.Next(), ++i ) + { + facesItersectors[i]._face = TopoDS::Face( faceMppIt.Key() ); + facesItersectors[i]._grid = &grid; + shapeBox.Add( facesItersectors[i].GetFaceBndBox() ); + } + + vector xCoords, yCoords, zCoords; + _hyp->GetCoordinates( xCoords, yCoords, zCoords, shapeBox ); + + grid.SetCoordinates( xCoords, yCoords, zCoords, theShape ); + + // check if the grid encloses the shape + if ( !_hyp->IsGridBySpacing(0) || + !_hyp->IsGridBySpacing(1) || + !_hyp->IsGridBySpacing(2) ) + { + Bnd_Box gridBox; + gridBox.Add( gp_Pnt( xCoords[0], yCoords[0], zCoords[0] )); + gridBox.Add( gp_Pnt( xCoords.back(), yCoords.back(), zCoords.back() )); + double x0,y0,z0, x1,y1,z1; + shapeBox.Get(x0,y0,z0, x1,y1,z1); + if ( gridBox.IsOut( gp_Pnt( x0,y0,z0 )) || + gridBox.IsOut( gp_Pnt( x1,y1,z1 ))) + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + { + if ( !facesItersectors[i].IsInGrid( gridBox )) + return error("The grid doesn't enclose the geometry"); +#ifdef ELLIPSOLID_WORKAROUND + delete facesItersectors[i]._surfaceInt, facesItersectors[i]._surfaceInt = 0; +#endif + } + } + if ( _computeCanceled ) return false; + +#ifdef WITH_TBB + { // copy partner faces and curves of not thread-safe types + set< const Standard_Transient* > tshapes; + BRepBuilderAPI_Copy copier; + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + { + if ( !facesItersectors[i].IsThreadSafe(tshapes) ) + { + copier.Perform( facesItersectors[i]._face ); + facesItersectors[i]._face = TopoDS::Face( copier ); + } + } + } + // Intersection of grid lines with the geometry boundary. + tbb::parallel_for ( tbb::blocked_range( 0, facesItersectors.size() ), + ParallelIntersector( facesItersectors ), + tbb::simple_partitioner()); +#else + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].Intersect(); +#endif + + // put interesection points onto the GridLine's; this is done after intersection + // to avoid contention of facesItersectors for writing into the same GridLine + // in case of parallel work of facesItersectors + for ( size_t i = 0; i < facesItersectors.size(); ++i ) + facesItersectors[i].StoreIntersections(); + + SMESH_MesherHelper helper( theMesh ); + TopExp_Explorer solidExp (theShape, TopAbs_SOLID); + helper.SetSubShape( solidExp.Current() ); + helper.SetElementsOnShape( true ); + + if ( _computeCanceled ) return false; + + // create nodes on the geometry + grid.ComputeNodes(helper); + + if ( _computeCanceled ) return false; + + // create volume elements + Hexahedron hex( _hyp->GetSizeThreshold(), &grid ); + int nbAdded = hex.MakeElements( helper ); + + SMESHDS_Mesh* meshDS = theMesh.GetMeshDS(); + if ( nbAdded > 0 ) + { + // make all SOLIDS computed + if ( SMESHDS_SubMesh* sm1 = meshDS->MeshElements( solidExp.Current()) ) + { + SMDS_ElemIteratorPtr volIt = sm1->GetElements(); + for ( ; solidExp.More() && volIt->more(); solidExp.Next() ) + { + const SMDS_MeshElement* vol = volIt->next(); + sm1->RemoveElement( vol, /*isElemDeleted=*/false ); + meshDS->SetMeshElementOnShape( vol, solidExp.Current() ); + } + } + // make other sub-shapes computed + setSubmeshesComputed( theMesh, theShape ); + } + + // remove free nodes + if ( SMESHDS_SubMesh * smDS = meshDS->MeshElements( helper.GetSubShapeID() )) + { + // intersection nodes + for ( int iDir = 0; iDir < 3; ++iDir ) + { + vector< GridLine >& lines = grid._lines[ iDir ]; + for ( size_t i = 0; i < lines.size(); ++i ) + { + multiset< IntersectionPoint >::iterator ip = lines[i]._intPoints.begin(); + for ( ; ip != lines[i]._intPoints.end(); ++ip ) + if ( ip->_node && ip->_node->NbInverseElements() == 0 ) + meshDS->RemoveFreeNode( ip->_node, smDS, /*fromGroups=*/false ); + } + } + // grid nodes + for ( size_t i = 0; i < grid._nodes.size(); ++i ) + if ( !grid._isBndNode[i] ) // nodes on boundary are already removed + if ( grid._nodes[i] && grid._nodes[i]->NbInverseElements() == 0 ) + meshDS->RemoveFreeNode( grid._nodes[i], smDS, /*fromGroups=*/false ); + } + + return nbAdded; + + } + // SMESH_ComputeError is not caught at SMESH_submesh level for an unknown reason + catch ( SMESH_ComputeError& e) + { + return error( SMESH_ComputeErrorPtr( new SMESH_ComputeError( e ))); + } + return false; +} + +//============================================================================= +/*! + * Evaluate + */ +//============================================================================= + +bool StdMeshers_Cartesian_3D::Evaluate(SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape, + MapShapeNbElems& theResMap) +{ + // TODO +// std::vector aResVec(SMDSEntity_Last); +// for(int i=SMDSEntity_Node; igetDependsOnIterator(/*includeSelf=*/false, /*complexShapeFirst=*/false); + while ( smIt->more() ) + { + SMESH_subMesh* sm = smIt->next(); + sm->SetIsAlwaysComputed( isComputed ); + } + } + + // -------------------------------------------------------------------------------- + // unsetting _alwaysComputed flag if "Cartesian_3D" was removed + // + virtual void ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* subMeshOfSolid, + SMESH_subMeshEventListenerData* data, + const SMESH_Hypothesis* hyp = 0) + { + if ( eventType == SMESH_subMesh::COMPUTE_EVENT ) + { + setAlwaysComputed( subMeshOfSolid->GetComputeState() == SMESH_subMesh::COMPUTE_OK, + subMeshOfSolid ); + } + else + { + SMESH_Algo* algo3D = subMeshOfSolid->GetAlgo(); + if ( !algo3D || _algoName != algo3D->GetName() ) + setAlwaysComputed( false, subMeshOfSolid ); + } + } + + // -------------------------------------------------------------------------------- + // set the event listener + // + static void SetOn( SMESH_subMesh* subMeshOfSolid, const string& algoName ) + { + subMeshOfSolid->SetEventListener( new _EventListener( algoName ), + /*data=*/0, + subMeshOfSolid ); + } + + }; // struct _EventListener + +} // namespace + +//================================================================================ +/*! + * \brief Sets event listener to submeshes if necessary + * \param subMesh - submesh where algo is set + * This method is called when a submesh gets HYP_OK algo_state. + * After being set, event listener is notified on each event of a submesh. + */ +//================================================================================ + +void StdMeshers_Cartesian_3D::SetEventListener(SMESH_subMesh* subMesh) +{ + _EventListener::SetOn( subMesh, GetName() ); +} + +//================================================================================ +/*! + * \brief Set _alwaysComputed flag to submeshes of inferior levels to avoid their computing + */ +//================================================================================ + +void StdMeshers_Cartesian_3D::setSubmeshesComputed(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape) +{ + for ( TopExp_Explorer soExp( theShape, TopAbs_SOLID ); soExp.More(); soExp.Next() ) + _EventListener::setAlwaysComputed( true, theMesh.GetSubMesh( soExp.Current() )); +} + diff --git a/src/StdMeshers/StdMeshers_Cartesian_3D.hxx b/src/StdMeshers/StdMeshers_Cartesian_3D.hxx new file mode 100644 index 000000000..4aefa321f --- /dev/null +++ b/src/StdMeshers/StdMeshers_Cartesian_3D.hxx @@ -0,0 +1,64 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_Cartesian_3D.hxx +// Module : SMESH +// +#ifndef _SMESH_Cartesian_3D_HXX_ +#define _SMESH_Cartesian_3D_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_3D_Algo.hxx" + +/*! + * \brief A 3D algorithm generating 3D structured Cartesian mesh in the + * internal part of a solid shape and polyhedral volumes near the shape boundary. + * + * Issue 0021336 + */ +class StdMeshers_CartesianParameters3D; + +class STDMESHERS_EXPORT StdMeshers_Cartesian_3D : public SMESH_3D_Algo +{ +public: + StdMeshers_Cartesian_3D(int hypId, int studyId, SMESH_Gen* gen); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + Hypothesis_Status& aStatus); + + virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + + virtual void SetEventListener(SMESH_subMesh* subMesh); + + private: + + void setSubmeshesComputed(SMESH_Mesh& aMesh, const TopoDS_Shape& theShape ); + + const StdMeshers_CartesianParameters3D* _hyp; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx index 42acee19e..11a3cc57a 100644 --- a/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_CompositeHexa_3D.cxx @@ -1,24 +1,23 @@ -// SMESH SMESH : implementaion of SMESH idl descriptions +// Copyright (C) 2007-2012 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 // -// Copyright (C) 2003 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 // + +// SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_CompositeHexa_3D.cxx // Module : SMESH // Created : Tue Nov 25 11:04:59 2008 @@ -33,7 +32,6 @@ #include "SMESH_Comment.hxx" #include "SMESH_ComputeError.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" @@ -44,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -60,8 +59,8 @@ #ifdef _DEBUG_ -// #define DEB_FACES -// #define DEB_GRID +//#define DEB_FACES +//#define DEB_GRID #define DUMP_VERT(msg,V) \ // { TopoDS_Vertex v = V; gp_Pnt p = BRep_Tool::Pnt(v);\ // cout << msg << "( "<< p.X()<<", "<& edges); _FaceSide* GetSide(const int i); const _FaceSide* GetSide(const int i) const; - int size() { return myChildren.size(); } + int size() const { return myChildren.size(); } int NbVertices() const; TopoDS_Vertex FirstVertex() const; TopoDS_Vertex LastVertex() const; TopoDS_Vertex Vertex(int i) const; + TopoDS_Edge Edge(int i) const; bool Contain( const _FaceSide& side, int* which=0 ) const; bool Contain( const TopoDS_Vertex& vertex ) const; void AppendSide( const _FaceSide& side ); @@ -128,7 +128,6 @@ private: list< _FaceSide > myChildren; int myNbChildren; - //set myVertices; TopTools_MapOfShape myVertices; EQuadSides myID; // debug @@ -155,15 +154,21 @@ public: //** Methods to find and orient faces of 6 sides of the box **// //!< Try to set the side as bottom hirizontal side bool SetBottomSide(const _FaceSide& side, int* sideIndex=0); - //!< Return face adjacent to i-th side of this face - _QuadFaceGrid* FindAdjacentForSide(int i, vector<_QuadFaceGrid>& faces) const; // (0& faces, EBoxSides id) const; //!< Reverse edges in order to have the bottom edge going along axes of the unit box void ReverseEdges(/*int e1, int e2*/); bool IsComplex() const { return !myChildren.empty(); } - typedef SMDS_SetIterator< const _QuadFaceGrid&, TChildren::const_iterator > TChildIterator; + int NbChildren() const { return myChildren.size(); } + + typedef SMDS_SetIterator< const _QuadFaceGrid&, + TChildren::const_iterator, + SMDS::SimpleAccessor, + SMDS::PassAllValueFilter<_QuadFaceGrid> > + TChildIterator; TChildIterator GetChildren() const { return TChildIterator( myChildren.begin(), myChildren.end()); } @@ -179,6 +184,9 @@ public: //** Loading and access to mesh **// //!< Return number of segments on the vertical sides int GetNbVertSegments(SMESH_Mesh& mesh, bool withBrothers=false) const; + //!< Return edge on the hirizontal bottom sides + int GetHoriEdges(vector & edges) const; + //!< Return a node by its position const SMDS_MeshNode* GetNode(int iHori, int iVert) const; @@ -206,7 +214,7 @@ public: //** Access to member fields **// private: - bool error(std::string& text, int code = COMPERR_ALGO_FAILED) + bool error(const std::string& text, int code = COMPERR_ALGO_FAILED) { myError = SMESH_ComputeError::New( code, text ); return false; } bool error(const SMESH_ComputeErrorPtr& err) @@ -252,7 +260,7 @@ StdMeshers_CompositeHexa_3D::StdMeshers_CompositeHexa_3D(int hypId, int studyId, :SMESH_3D_Algo(hypId, studyId, gen) { _name = "CompositeHexa_3D"; - _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type + _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type } //================================================================================ @@ -271,35 +279,42 @@ bool StdMeshers_CompositeHexa_3D::CheckHypothesis(SMESH_Mesh& aMesh, //================================================================================ /*! - * \brief Computes hexahedral mesh on a box with composite sides - * \param aMesh - mesh to compute - * \param aShape - shape to mesh - * \retval bool - succes sign + * \brief Tries to find 6 sides of a box */ //================================================================================ -bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, - const TopoDS_Shape& theShape) +bool StdMeshers_CompositeHexa_3D::findBoxFaces( const TopoDS_Shape& shape, + list< _QuadFaceGrid >& boxFaces, + _QuadFaceGrid * & fBottom, + _QuadFaceGrid * & fTop, + _QuadFaceGrid * & fFront, + _QuadFaceGrid * & fBack, + _QuadFaceGrid * & fLeft, + _QuadFaceGrid * & fRight) { - SMESH_MesherHelper helper( theMesh ); - _quadraticMesh = helper.IsQuadraticSubMesh( theShape ); - helper.SetElementsOnShape( true ); - - // ------------------------- - // Try to find 6 side faces - // ------------------------- - vector< _QuadFaceGrid > boxFaces; boxFaces.reserve( 6 ); + list< _QuadFaceGrid >::iterator boxFace; TopExp_Explorer exp; - int iFace, nbFaces = 0; - for ( exp.Init(theShape, TopAbs_FACE); exp.More(); exp.Next(), ++nbFaces ) + int nbFaces = 0; + for ( exp.Init( shape, TopAbs_FACE); exp.More(); exp.Next(), ++nbFaces ) { _QuadFaceGrid f; if ( !f.Init( TopoDS::Face( exp.Current() ))) return error (COMPERR_BAD_SHAPE); - bool isContinuous = false; - for ( int i=0; i < boxFaces.size() && !isContinuous; ++i ) - isContinuous = boxFaces[ i ].AddContinuousFace( f ); - if ( !isContinuous ) + + _QuadFaceGrid* prevContinuous = 0; + for ( boxFace = boxFaces.begin(); boxFace != boxFaces.end(); ++boxFace ) + { + if ( prevContinuous ) + { + if ( prevContinuous->AddContinuousFace( *boxFace )) + boxFace = --boxFaces.erase( boxFace ); + } + else if ( boxFace->AddContinuousFace( f )) + { + prevContinuous = & (*boxFace); + } + } + if ( !prevContinuous ) boxFaces.push_back( f ); } // Check what we have @@ -310,29 +325,30 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, if ( boxFaces.size() != 6 && nbFaces == 6 ) { // strange ordinary box with continuous faces boxFaces.resize( 6 ); - iFace = 0; - for ( exp.Init(theShape, TopAbs_FACE); exp.More(); exp.Next(), ++iFace ) - boxFaces[ iFace ].Init( TopoDS::Face( exp.Current() ) ); + boxFace = boxFaces.begin(); + for ( exp.Init( shape, TopAbs_FACE); exp.More(); exp.Next(), ++boxFace ) + boxFace->Init( TopoDS::Face( exp.Current() ) ); } // ---------------------------------------- // Find out position of faces within a box // ---------------------------------------- - - _QuadFaceGrid *fBottom, *fTop, *fFront, *fBack, *fLeft, *fRight; // start from a bottom face - fBottom = &boxFaces[0]; + fBottom = &boxFaces.front(); + fBottom->SetID( B_BOTTOM ); // find vertical faces - fFront = fBottom->FindAdjacentForSide( Q_BOTTOM, boxFaces ); - fLeft = fBottom->FindAdjacentForSide( Q_RIGHT, boxFaces ); - fBack = fBottom->FindAdjacentForSide( Q_TOP, boxFaces ); - fRight = fBottom->FindAdjacentForSide( Q_LEFT, boxFaces ); + fFront = fBottom->FindAdjacentForSide( Q_BOTTOM, boxFaces, B_FRONT ); + fLeft = fBottom->FindAdjacentForSide( Q_RIGHT, boxFaces, B_LEFT ); + fBack = fBottom->FindAdjacentForSide( Q_TOP, boxFaces, B_BACK ); + fRight = fBottom->FindAdjacentForSide( Q_LEFT, boxFaces, B_RIGHT ); // check the found if ( !fFront || !fBack || !fLeft || !fRight ) return error(COMPERR_BAD_SHAPE); - // top face + // find a top face fTop = 0; - for ( int i=1; i < boxFaces.size() && !fTop; ++i ) { - fTop = & boxFaces[ i ]; + for ( boxFace = ++boxFaces.begin(); boxFace != boxFaces.end() && !fTop; ++boxFace ) + { + fTop = & (*boxFace); + fTop->SetID( B_TOP ); if ( fTop==fFront || fTop==fLeft || fTop==fBack || fTop==fRight ) fTop = 0; } @@ -352,18 +368,39 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, if ( !fTop ) return error(COMPERR_BAD_SHAPE); - fBottom->SetID( B_BOTTOM ); - fBack ->SetID( B_BACK ); - fLeft ->SetID( B_LEFT ); - fFront ->SetID( B_FRONT ); - fRight ->SetID( B_RIGHT ); - fTop ->SetID( B_TOP ); - // orient bottom egde of faces along axes of the unit box fBottom->ReverseEdges(); fBack ->ReverseEdges(); fLeft ->ReverseEdges(); + return true; +} + +//================================================================================ +/*! + * \brief Computes hexahedral mesh on a box with composite sides + * \param aMesh - mesh to compute + * \param aShape - shape to mesh + * \retval bool - succes sign + */ +//================================================================================ + +bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape) +{ + SMESH_MesherHelper helper( theMesh ); + _quadraticMesh = helper.IsQuadraticSubMesh( theShape ); + helper.SetElementsOnShape( true ); + + // ------------------------- + // Try to find 6 side faces + // ------------------------- + list< _QuadFaceGrid > boxFaceContainer; + _QuadFaceGrid *fBottom, *fTop, *fFront, *fBack, *fLeft, *fRight; + if ( ! findBoxFaces( theShape, boxFaceContainer, + fBottom, fTop, fFront, fBack, fLeft, fRight)) + return false; + // ------------------------------------------ // Fill columns of nodes with existing nodes // ------------------------------------------ @@ -417,7 +454,7 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, // ---------------------------- // Add internal nodes of a box // ---------------------------- - // projection points of internal nodes on box subshapes by which + // projection points of internal nodes on box sub-shapes by which // coordinates of internal nodes are computed vector pointsOnShapes( SMESH_Block::ID_Shell ); @@ -486,7 +523,7 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, } } // faces no more needed, free memory - boxFaces.clear(); + boxFaceContainer.clear(); // ---------------- // Add hexahedrons @@ -508,6 +545,90 @@ bool StdMeshers_CompositeHexa_3D::Compute(SMESH_Mesh& theMesh, return true; } +//================================================================================ +/*! + * Evaluate + */ +//================================================================================ + +bool StdMeshers_CompositeHexa_3D::Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& aResMap) +{ + // ------------------------- + // Try to find 6 side faces + // ------------------------- + list< _QuadFaceGrid > boxFaceContainer; + _QuadFaceGrid *fBottom, *fTop, *fFront, *fBack, *fLeft, *fRight; + if ( ! findBoxFaces( theShape, boxFaceContainer, + fBottom, fTop, fFront, fBack, fLeft, fRight)) + return false; + + // Find a less complex side + _QuadFaceGrid * lessComplexSide = & boxFaceContainer.front(); + list< _QuadFaceGrid >::iterator face = boxFaceContainer.begin(); + for ( ++face; face != boxFaceContainer.end() && lessComplexSide->IsComplex(); ++face ) + if ( face->NbChildren() < lessComplexSide->NbChildren() ) + lessComplexSide = & *face; + + // Get an 1D size of lessComplexSide + int nbSeg1 = 0; + vector edges; + if ( !lessComplexSide->GetHoriEdges(edges) ) + return false; + for ( size_t i = 0; i < edges.size(); ++i ) + { + const vector& nbElems = aResMap[ theMesh.GetSubMesh( edges[i] )]; + if ( !nbElems.empty() ) + nbSeg1 += Max( nbElems[ SMDSEntity_Edge ], nbElems[ SMDSEntity_Quad_Edge ]); + } + + // Get an 1D size of a box side ortogonal to lessComplexSide + int nbSeg2 = 0; + _QuadFaceGrid* ortoSide = + lessComplexSide->FindAdjacentForSide( Q_LEFT, boxFaceContainer, B_UNDEFINED ); + edges.clear(); + if ( !ortoSide || !ortoSide->GetHoriEdges(edges) ) return false; + for ( size_t i = 0; i < edges.size(); ++i ) + { + const vector& nbElems = aResMap[ theMesh.GetSubMesh( edges[i] )]; + if ( !nbElems.empty() ) + nbSeg2 += Max( nbElems[ SMDSEntity_Edge ], nbElems[ SMDSEntity_Quad_Edge ]); + } + + // Get an 2D size of a box side ortogonal to lessComplexSide + int nbFaces = 0, nbQuadFace = 0; + list< TopoDS_Face > sideFaces; + if ( ortoSide->IsComplex() ) + for ( _QuadFaceGrid::TChildIterator child = ortoSide->GetChildren(); child.more(); ) + sideFaces.push_back( child.next().GetFace() ); + else + sideFaces.push_back( ortoSide->GetFace() ); + // + list< TopoDS_Face >::iterator f = sideFaces.begin(); + for ( ; f != sideFaces.end(); ++f ) + { + const vector& nbElems = aResMap[ theMesh.GetSubMesh( *f )]; + if ( !nbElems.empty() ) + { + nbFaces = nbElems[ SMDSEntity_Quadrangle ]; + nbQuadFace = nbElems[ SMDSEntity_Quad_Quadrangle ]; + } + } + + // Fill nb of elements + vector aResVec(SMDSEntity_Last,0); + int nbSeg3 = ( nbFaces + nbQuadFace ) / nbSeg2; + aResVec[SMDSEntity_Node] = (nbSeg1-1) * (nbSeg2-1) * (nbSeg3-1); + aResVec[SMDSEntity_Hexa] = nbSeg1 * nbFaces; + aResVec[SMDSEntity_Quad_Hexa] = nbSeg1 * nbQuadFace; + + aResMap.insert( make_pair( theMesh.GetSubMesh(theShape), aResVec )); + + return true; +} + + //================================================================================ /*! * \brief constructor of non-initialized _QuadFaceGrid @@ -589,12 +710,13 @@ bool _QuadFaceGrid::Init(const TopoDS_Face& f) bool _QuadFaceGrid::AddContinuousFace( const _QuadFaceGrid& other ) { - for ( int i = 0; i < 4; ++i ) { + for ( int i = 0; i < 4; ++i ) + { const _FaceSide& otherSide = other.GetSide( i ); int iMyCommon; if ( mySides.Contain( otherSide, &iMyCommon ) ) { // check if normals of two faces are collinear at all vertices of a otherSide - const double angleTol = PI / 180 / 2; + const double angleTol = M_PI / 180. / 2.; int iV, nbV = otherSide.NbVertices(), nbCollinear = 0; for ( iV = 0; iV < nbV; ++iV ) { @@ -618,14 +740,28 @@ bool _QuadFaceGrid::AddContinuousFace( const _QuadFaceGrid& other ) myChildren.push_back( *this ); myFace.Nullify(); } - myChildren.push_back( other ); - int otherBottomIndex = ( 4 + i - iMyCommon + 2 ) % 4; - myChildren.back().SetBottomSide( other.GetSide( otherBottomIndex )); + if ( other.IsComplex() ) + for ( TChildIterator children = other.GetChildren(); children.more(); ) + myChildren.push_back( children.next() ); + else + myChildren.push_back( other ); + + myLeftBottomChild = 0; + //int otherBottomIndex = ( 4 + i - iMyCommon + 2 ) % 4; + //myChildren.back().SetBottomSide( other.GetSide( otherBottomIndex )); + // collect vertices in mySides - mySides.AppendSide( other.GetSide(0) ); - mySides.AppendSide( other.GetSide(1) ); - mySides.AppendSide( other.GetSide(2) ); - mySides.AppendSide( other.GetSide(3) ); + if ( other.IsComplex() ) + for ( TChildIterator children = other.GetChildren(); children.more(); ) + { + const _QuadFaceGrid& child = children.next(); + for ( int i = 0; i < 4; ++i ) + mySides.AppendSide( child.GetSide(i) ); + } + else + for ( int i = 0; i < 4; ++i ) + mySides.AppendSide( other.GetSide(i) ); + return true; } } @@ -680,12 +816,17 @@ bool _QuadFaceGrid::SetBottomSide(const _FaceSide& bottom, int* sideIndex) */ //================================================================================ -_QuadFaceGrid* _QuadFaceGrid::FindAdjacentForSide(int i, vector<_QuadFaceGrid>& faces) const +_QuadFaceGrid* _QuadFaceGrid::FindAdjacentForSide(int i, + list<_QuadFaceGrid>& faces, + EBoxSides id) const { - for ( int iF = 0; iF < faces.size(); ++iF ) { - _QuadFaceGrid* f = &faces[ iF ]; - if ( f != this && f->SetBottomSide( GetSide( i ))) - return f; + const _FaceSide & iSide = GetSide( i ); + list< _QuadFaceGrid >::iterator boxFace = faces.begin(); + for ( ; boxFace != faces.end(); ++boxFace ) + { + _QuadFaceGrid* f = & (*boxFace); + if ( f != this && f->SetBottomSide( iSide )) + return f->SetID( id ), f; } return (_QuadFaceGrid*) 0; } @@ -781,6 +922,13 @@ bool _QuadFaceGrid::LoadGrid( SMESH_Mesh& mesh ) if ( !myGrid.empty() ) return true; + SMESHDS_SubMesh* faceSubMesh = mesh.GetSubMesh( myFace )->GetSubMeshDS(); + // check that all faces are quadrangular + SMDS_ElemIteratorPtr fIt = faceSubMesh->GetElements(); + while ( fIt->more() ) + if ( fIt->next()->NbNodes() % 4 > 0 ) + return error("Non-quadrangular mesh faces are not allowed on sides of a composite block"); + myIndexer._xSize = 1 + mySides.GetSide( Q_BOTTOM )->GetNbSegments( mesh ); myIndexer._ySize = 1 + mySides.GetSide( Q_LEFT )->GetNbSegments( mesh ); @@ -791,10 +939,8 @@ bool _QuadFaceGrid::LoadGrid( SMESH_Mesh& mesh ) // store the rest nodes row by row - SMESHDS_SubMesh* faceSubMesh = mesh.GetSubMesh( myFace )->GetSubMeshDS(); - - SMDS_MeshNode dummy(0,0,0); - const SMDS_MeshElement* firstQuad = &dummy;// most left face above the last row of found nodes + const SMDS_MeshNode* dummy = mesh.GetMeshDS()->AddNode(0,0,0); + const SMDS_MeshElement* firstQuad = dummy; // most left face above the last row of found nodes int nbFoundNodes = myIndexer._xSize; while ( nbFoundNodes != myGrid.size() ) @@ -815,7 +961,7 @@ bool _QuadFaceGrid::LoadGrid( SMESH_Mesh& mesh ) TIDSortedElemSet emptySet, avoidSet; avoidSet.insert( firstQuad ); firstQuad = SMESH_MeshEditor::FindFaceInSet( n1down, n2down, emptySet, avoidSet); - if ( firstQuad && !faceSubMesh->Contains( firstQuad )) { + while ( firstQuad && !faceSubMesh->Contains( firstQuad )) { avoidSet.insert( firstQuad ); firstQuad = SMESH_MeshEditor::FindFaceInSet( n1down, n2down, emptySet, avoidSet); } @@ -861,7 +1007,7 @@ bool _QuadFaceGrid::LoadGrid( SMESH_Mesh& mesh ) n1up = n2up; } } - + mesh.GetMeshDS()->RemoveNode(dummy); DumpGrid(); // debug return true; @@ -954,21 +1100,22 @@ void _QuadFaceGrid::setBrothers( set< _QuadFaceGrid* >& notLocatedBrothers ) TopoDS_Vertex rightVertex = GetSide( Q_BOTTOM ).LastVertex(); DUMP_VERT("1 right bottom Vertex: ",rightVertex ); set< _QuadFaceGrid* >::iterator brIt, brEnd = notLocatedBrothers.end(); - for ( brIt = notLocatedBrothers.begin(); !myRightBrother && brIt != brEnd; ++brIt ) + for ( brIt = notLocatedBrothers.begin(); brIt != brEnd; ++brIt ) { _QuadFaceGrid* brother = *brIt; TopoDS_Vertex brotherLeftVertex = brother->GetSide( Q_BOTTOM ).FirstVertex(); DUMP_VERT( "brother left bottom: ", brotherLeftVertex ); if ( rightVertex.IsSame( brotherLeftVertex )) { myRightBrother = brother; - notLocatedBrothers.erase( myRightBrother ); + notLocatedBrothers.erase( brIt ); + break; } } // find upper brother TopoDS_Vertex upVertex = GetSide( Q_LEFT ).FirstVertex(); DUMP_VERT("1 left up Vertex: ",upVertex); brIt = notLocatedBrothers.begin(), brEnd = notLocatedBrothers.end(); - for ( ; !myUpBrother && brIt != brEnd; ++brIt ) + for ( ; brIt != brEnd; ++brIt ) { _QuadFaceGrid* brother = *brIt; TopoDS_Vertex brotherLeftVertex = brother->GetSide( Q_BOTTOM ).FirstVertex(); @@ -976,6 +1123,7 @@ void _QuadFaceGrid::setBrothers( set< _QuadFaceGrid* >& notLocatedBrothers ) if ( upVertex.IsSame( brotherLeftVertex )) { myUpBrother = brother; notLocatedBrothers.erase( myUpBrother ); + break; } } // recursive call @@ -1073,6 +1221,35 @@ int _QuadFaceGrid::GetNbVertSegments(SMESH_Mesh& mesh, bool withBrothers) const return nbSegs; } +//================================================================================ +/*! + * \brief Return edge on the hirizontal bottom sides + */ +//================================================================================ + +int _QuadFaceGrid::GetHoriEdges(vector & edges) const +{ + if ( myLeftBottomChild ) + { + return myLeftBottomChild->GetHoriEdges( edges ); + } + else + { + const _FaceSide* bottom = mySides.GetSide( Q_BOTTOM ); + int i = 0; + while ( true ) { + TopoDS_Edge e = bottom->Edge( i++ ); + if ( e.IsNull() ) + break; + else + edges.push_back( e ); + } + if ( myRightBrother ) + myRightBrother->GetHoriEdges( edges ); + } + return edges.size(); +} + //================================================================================ /*! * \brief Return a node by its position @@ -1287,7 +1464,6 @@ int _FaceSide::NbVertices() const { if ( myChildren.empty() ) return myVertices.Extent(); -// return myVertices.size(); return myNbChildren + 1; } @@ -1334,6 +1510,23 @@ TopoDS_Vertex _FaceSide::Vertex(int i) const return GetSide(i)->FirstVertex(); } +//================================================================================ +/*! + * \brief Return i-the zero-based edge of the side + */ +//================================================================================ + +TopoDS_Edge _FaceSide::Edge(int i) const +{ + if ( i == 0 && !myEdge.IsNull() ) + return myEdge; + + if ( const _FaceSide* iSide = GetSide( i )) + return iSide->myEdge; + + return TopoDS_Edge(); +} + //======================================================================= //function : Contain //purpose : @@ -1346,9 +1539,6 @@ bool _FaceSide::Contain( const _FaceSide& side, int* which ) const if ( which ) *which = 0; int nbCommon = 0; -// set::iterator v, vEnd = side.myVertices.end(); -// for ( v = side.myVertices.begin(); v != vEnd; ++v ) -// nbCommon += ( myVertices.find( *v ) != myVertices.end() ); TopTools_MapIteratorOfMapOfShape vIt ( side.myVertices ); for ( ; vIt.More(); vIt.Next() ) nbCommon += ( myVertices.Contains( vIt.Key() )); @@ -1372,7 +1562,6 @@ bool _FaceSide::Contain( const _FaceSide& side, int* which ) const bool _FaceSide::Contain( const TopoDS_Vertex& vertex ) const { return myVertices.Contains( vertex ); -// return myVertices.find( ptr( vertex )) != myVertices.end(); } //======================================================================= @@ -1390,7 +1579,6 @@ void _FaceSide::AppendSide( const _FaceSide& side ) } myChildren.push_back( side ); myNbChildren++; - //myVertices.insert( side.myVertices.begin(), side.myVertices.end() ); TopTools_MapIteratorOfMapOfShape vIt ( side.myVertices ); for ( ; vIt.More(); vIt.Next() ) myVertices.Add( vIt.Key() ); diff --git a/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx b/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx index 09b687fc1..d5b9f8b4e 100644 --- a/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx +++ b/src/StdMeshers/StdMeshers_CompositeHexa_3D.hxx @@ -1,29 +1,26 @@ -// SMESH SMESH : implementaion of SMESH idl descriptions +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE // -// Copyright (C) 2003 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 +// 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_CompositeBlock_3D.hxx // Module : SMESH - +// #ifndef _SMESH_CompositeSegment_1D_HXX_ #define _SMESH_CompositeSegment_1D_HXX_ @@ -34,6 +31,7 @@ class SMESH_Mesh; class StdMeshers_FaceSide; class TopoDS_Edge; class TopoDS_Face; +struct _QuadFaceGrid; /*! * \brief Computes hexahedral mesh on a box with composite sides @@ -48,14 +46,25 @@ public: //virtual ~StdMeshers_CompositeHexa_3D(); virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape); + const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); virtual bool CheckHypothesis(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape, Hypothesis_Status& aStatus); private: - // private fields + + bool findBoxFaces( const TopoDS_Shape& shape, + list< _QuadFaceGrid >& boxFaceContainer, + _QuadFaceGrid * & fBottom, + _QuadFaceGrid * & fTop, + _QuadFaceGrid * & fFront, + _QuadFaceGrid * & fBack, + _QuadFaceGrid * & fLeft, + _QuadFaceGrid * & fRight); }; #endif diff --git a/src/StdMeshers/StdMeshers_CompositeSegment_1D.cxx b/src/StdMeshers/StdMeshers_CompositeSegment_1D.cxx index f5cf86cd9..2b5847038 100644 --- a/src/StdMeshers/StdMeshers_CompositeSegment_1D.cxx +++ b/src/StdMeshers/StdMeshers_CompositeSegment_1D.cxx @@ -1,30 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions -// File : StdMeshers_Regular_1D.cxx -// Moved here from SMESH_Regular_1D.cxx -// Author : Paul RASCLE, EDF +// File : StdMeshers_CompositeSegment_1D.cxx // Module : SMESH -// $Header$ // #include "StdMeshers_CompositeSegment_1D.hxx" #include "StdMeshers_FaceSide.hxx" @@ -73,15 +71,16 @@ namespace { */ //================================================================================ - TopoDS_Edge nextC1Edge(const TopoDS_Edge& edge, - SMESH_Mesh & aMesh, - const bool forward) + TopoDS_Edge nextC1Edge(TopoDS_Edge edge, + SMESH_Mesh & aMesh, + const bool forward) { + if (edge.Orientation() > TopAbs_REVERSED) // INTERNAL + edge.Orientation( TopAbs_FORWARD ); TopoDS_Edge eNext; TopTools_MapOfShape edgeCounter; edgeCounter.Add( edge ); - TopoDS_Vertex v; - v = forward ? TopExp::LastVertex( edge,1 ) : TopExp::FirstVertex( edge,1 ); + TopoDS_Vertex v = forward ? TopExp::LastVertex(edge,true) : TopExp::FirstVertex(edge,true); TopTools_ListIteratorOfListOfShape ancestIt = aMesh.GetAncestors( v ); for ( ; ancestIt.More(); ancestIt.Next() ) { @@ -92,11 +91,11 @@ namespace { if ( edgeCounter.Extent() < 3 && !eNext.IsNull() ) { if ( SMESH_Algo::IsContinuous( edge, eNext )) { // care of orientation - bool reverse; - if ( forward ) - reverse = ( !v.IsSame( TopExp::FirstVertex( eNext, true ))); - else - reverse = ( !v.IsSame( TopExp::LastVertex( eNext, true ))); + if (eNext.Orientation() > TopAbs_REVERSED) // INTERNAL + eNext.Orientation( TopAbs_FORWARD ); + TopoDS_Vertex vn = + forward ? TopExp::FirstVertex(eNext,true) : TopExp::LastVertex(eNext,true); + bool reverse = (!v.IsSame(vn)); if ( reverse ) eNext.Reverse(); return eNext; @@ -151,7 +150,9 @@ namespace { struct VertexNodesRestoringListener : public SMESH_subMeshEventListener { - VertexNodesRestoringListener():SMESH_subMeshEventListener(0) // won't be deleted by submesh + VertexNodesRestoringListener(): + SMESH_subMeshEventListener(0, // won't be deleted by submesh + "StdMeshers_CompositeSegment_1D::VertexNodesRestoringListener") {} /*! * \brief Restore nodes on internal vertices of a complex side @@ -167,17 +168,23 @@ namespace { EventListenerData* data, const SMESH_Hypothesis* /*hyp*/) { - bool hypRemoved = ( eventType == SMESH_subMesh::ALGO_EVENT && - subMesh->GetAlgoState() != SMESH_subMesh::HYP_OK ); - if ( hypRemoved && data ) + if ( data && eventType == SMESH_subMesh::ALGO_EVENT ) { - list::iterator smIt = data->mySubMeshes.begin(); - for ( ; smIt != data->mySubMeshes.end(); ++smIt ) + bool hypRemoved; + if ( subMesh->GetAlgoState() != SMESH_subMesh::HYP_OK ) + hypRemoved = true; + else { + SMESH_Algo* algo = subMesh->GetAlgo(); + hypRemoved = ( string( algo->GetName() ) != StdMeshers_CompositeSegment_1D::AlgoName()); + } + if ( hypRemoved ) { - if ( SMESH_subMesh* sm = *smIt ) { - sm->SetIsAlwaysComputed( false ); - sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); - } + list::iterator smIt = data->mySubMeshes.begin(); + for ( ; smIt != data->mySubMeshes.end(); ++smIt ) + if ( SMESH_subMesh* sm = *smIt ) { + sm->SetIsAlwaysComputed( false ); + sm->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); + } } } // at study restoration: @@ -224,10 +231,19 @@ StdMeshers_CompositeSegment_1D::StdMeshers_CompositeSegment_1D(int hypId :StdMeshers_Regular_1D(hypId, studyId, gen) { MESSAGE("StdMeshers_CompositeSegment_1D::StdMeshers_CompositeSegment_1D"); - _name = "CompositeSegment_1D"; + _name = AlgoName(); _EventListener = new VertexNodesRestoringListener(); } +//======================================================================= +//function : AlgoName +//purpose : Returns algo type name +//======================================================================= + +std::string StdMeshers_CompositeSegment_1D::AlgoName() +{ + return "CompositeSegment_1D"; +} //============================================================================= /*! * @@ -251,6 +267,35 @@ StdMeshers_CompositeSegment_1D::~StdMeshers_CompositeSegment_1D() void StdMeshers_CompositeSegment_1D::SetEventListener(SMESH_subMesh* subMesh) { + // issue 0020279. Set "_alwaysComputed" flag to the submeshes of internal + // vertices of composite edge in order to avoid creation of vertices on + // them for the sake of stability. + + // check if "_alwaysComputed" is not yet set + bool isAlwaysComputed = false; + SMESH_subMeshIteratorPtr smIt = subMesh->getDependsOnIterator(false,false); + while ( !isAlwaysComputed && smIt->more() ) + isAlwaysComputed = smIt->next()->IsAlwaysComputed(); + + if ( !isAlwaysComputed ) + { + // check if an edge is a part of a complex side + TopoDS_Face face; + TopoDS_Edge edge = TopoDS::Edge( subMesh->GetSubShape() ); + auto_ptr< StdMeshers_FaceSide > side + ( StdMeshers_CompositeSegment_1D::GetFaceSide(*subMesh->GetFather(),edge, face, false )); + if ( side->NbEdges() > 1 ) { // complex + + // set _alwaysComputed to vertices + for ( int iE = 1; iE < side->NbEdges(); ++iE ) + { + TopoDS_Vertex V = side->FirstVertex( iE ); + SMESH_subMesh* sm = side->GetMesh()->GetSubMesh( V ); + sm->SetIsAlwaysComputed( true ); + } + } + } + // set listener that will remove _alwaysComputed from submeshes at algorithm change subMesh->SetEventListener( _EventListener, 0, subMesh); StdMeshers_Regular_1D::SetEventListener( subMesh ); } @@ -268,14 +313,17 @@ StdMeshers_CompositeSegment_1D::GetFaceSide(SMESH_Mesh& aMesh, const bool ignoreMeshed) { list< TopoDS_Edge > edges; - edges.push_back( anEdge ); + if ( anEdge.Orientation() <= TopAbs_REVERSED ) + edges.push_back( anEdge ); + else + edges.push_back( TopoDS::Edge( anEdge.Oriented( TopAbs_FORWARD ))); // PAL21718 list hypList; SMESH_Algo* theAlgo = aMesh.GetGen()->GetAlgo( aMesh, anEdge ); if ( theAlgo ) hypList = theAlgo->GetUsedHypothesis(aMesh, anEdge, false); for ( int forward = 0; forward < 2; ++forward ) { - TopoDS_Edge eNext = nextC1Edge( anEdge, aMesh, forward ); + TopoDS_Edge eNext = nextC1Edge( edges.back(), aMesh, forward ); while ( !eNext.IsNull() ) { if ( ignoreMeshed ) { // eNext must not have computed mesh @@ -289,6 +337,8 @@ StdMeshers_CompositeSegment_1D::GetFaceSide(SMESH_Mesh& aMesh, string(theAlgo->GetName()) != algo->GetName() || hypList != algo->GetUsedHypothesis(aMesh, eNext, false)) break; + if ( std::find( edges.begin(), edges.end(), eNext ) != edges.end() ) + break; if ( forward ) edges.push_back( eNext ); else diff --git a/src/StdMeshers/StdMeshers_CompositeSegment_1D.hxx b/src/StdMeshers/StdMeshers_CompositeSegment_1D.hxx index 1ec160335..c1beab206 100644 --- a/src/StdMeshers/StdMeshers_CompositeSegment_1D.hxx +++ b/src/StdMeshers/StdMeshers_CompositeSegment_1D.hxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_CompositeSegment_1D.hxx // Module : SMESH -// $Header$ // #ifndef _SMESH_CompositeSegment_1D_HXX_ #define _SMESH_CompositeSegment_1D_HXX_ @@ -43,7 +43,7 @@ public: virtual ~StdMeshers_CompositeSegment_1D(); virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape); + const TopoDS_Shape& aShape); /*! * \brief Sets event listener to submeshes if necessary * \param subMesh - submesh where algo is set @@ -61,6 +61,11 @@ public: const TopoDS_Face& aFace, const bool ignoreMeshed); + /*! + * \brief Returns algo type name + */ + static std::string AlgoName(); + protected: SMESH_subMeshEventListener* _EventListener; }; diff --git a/src/StdMeshers/StdMeshers_Deflection1D.cxx b/src/StdMeshers/StdMeshers_Deflection1D.cxx index 42ad9219a..92e8589e7 100644 --- a/src/StdMeshers/StdMeshers_Deflection1D.cxx +++ b/src/StdMeshers/StdMeshers_Deflection1D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_Deflection1D : implementaion of SMESH idl descriptions // File : StdMeshers_Deflection1D.cxx // Module : SMESH @@ -203,7 +204,7 @@ bool StdMeshers_Deflection1D::SetParametersByMesh(const SMESH_Mesh* theMesh, { const TopoDS_Edge& edge = TopoDS::Edge( edgeMap( iE )); Handle(Geom_Curve) C = BRep_Tool::Curve( edge, L, UMin, UMax ); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); if ( AdaptCurve.GetType() != GeomAbs_Line ) { vector< double > params; diff --git a/src/StdMeshers/StdMeshers_Deflection1D.hxx b/src/StdMeshers/StdMeshers_Deflection1D.hxx index a3cf19b70..4f7ea0975 100644 --- a/src/StdMeshers/StdMeshers_Deflection1D.hxx +++ b/src/StdMeshers/StdMeshers_Deflection1D.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_Deflection1D.hxx // Module : SMESH // - #ifndef _StdMeshers_Deflection1D_HXX_ #define _StdMeshers_Deflection1D_HXX_ diff --git a/src/StdMeshers/StdMeshers_Distribution.cxx b/src/StdMeshers/StdMeshers_Distribution.cxx index f2e591f34..fca2976f7 100644 --- a/src/StdMeshers/StdMeshers_Distribution.cxx +++ b/src/StdMeshers/StdMeshers_Distribution.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of point distribution algorithm // File : StdMeshers_Distribution.cxx // Author : Alexandre SOLOVYOV @@ -303,21 +304,21 @@ double dihotomySolve( Function& f, const double val, const double _start, const } bool buildDistribution( const TCollection_AsciiString& f, const int conv, const double start, const double end, - const int nbSeg, vector& data, const double eps ) + const int nbSeg, vector& data, const double eps ) { FunctionExpr F( f.ToCString(), conv ); return buildDistribution( F, start, end, nbSeg, data, eps ); } bool buildDistribution( const std::vector& f, const int conv, const double start, const double end, - const int nbSeg, vector& data, const double eps ) + const int nbSeg, vector& data, const double eps ) { FunctionTable F( f, conv ); return buildDistribution( F, start, end, nbSeg, data, eps ); } bool buildDistribution( const Function& func, const double start, const double end, const int nbSeg, - vector& data, const double eps ) + vector& data, const double eps ) { if( nbSeg<=0 ) return false; diff --git a/src/StdMeshers/StdMeshers_Distribution.hxx b/src/StdMeshers/StdMeshers_Distribution.hxx index 373305b63..10ba4cde8 100644 --- a/src/StdMeshers/StdMeshers_Distribution.hxx +++ b/src/StdMeshers/StdMeshers_Distribution.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of point distribution algorithm // File : StdMeshers_Distribution.hxx // Author : Alexandre SOLOVYOV @@ -102,16 +103,16 @@ private: STDMESHERS_EXPORT bool buildDistribution( const Function& f, - const double start, const double end, - const int nbSeg, - std::vector& data, - const double eps ); + const double start, const double end, + const int nbSeg, + std::vector& data, + const double eps ); STDMESHERS_EXPORT bool buildDistribution( const TCollection_AsciiString& f, const int conv, const double start, const double end, - const int nbSeg, std::vector& data, const double eps ); + const int nbSeg, std::vector& data, const double eps ); STDMESHERS_EXPORT bool buildDistribution( const std::vector& f, const int conv, const double start, const double end, - const int nbSeg, std::vector& data, const double eps ); + const int nbSeg, std::vector& data, const double eps ); #endif diff --git a/src/StdMeshers/StdMeshers_FaceSide.cxx b/src/StdMeshers/StdMeshers_FaceSide.cxx index 7719b4f6e..13c2d6263 100644 --- a/src/StdMeshers/StdMeshers_FaceSide.cxx +++ b/src/StdMeshers/StdMeshers_FaceSide.cxx @@ -1,25 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_FaceSide.hxx // Created : Wed Jan 31 18:41:25 2007 // Author : Edward AGAPOV (eap) @@ -31,19 +31,21 @@ #include "SMDS_MeshNode.hxx" #include "SMESHDS_Mesh.hxx" #include "SMESHDS_SubMesh.hxx" -//#include "SMESH_Algo.hxx" +#include "SMESH_Algo.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_ComputeError.hxx" #include "SMESH_Block.hxx" #include #include -#include #include #include +#include +#include #include #include +#include #include #include #include @@ -85,30 +87,36 @@ StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face& theFace, const bool theIgnoreMediumNodes) { int nbEdges = theEdges.size(); - myEdge.resize( nbEdges ); - myC2d.resize( nbEdges ); - myFirst.resize( nbEdges ); - myLast.resize( nbEdges ); - myNormPar.resize( nbEdges ); - myLength = 0; - myNbPonits = myNbSegments = 0; - myMesh = theMesh; + myEdge.resize ( nbEdges ); + myEdgeID.resize ( nbEdges ); + myC2d.resize ( nbEdges ); + myC3dAdaptor.resize( nbEdges ); + myFirst.resize ( nbEdges ); + myLast.resize ( nbEdges ); + myNormPar.resize ( nbEdges ); + myEdgeLength.resize( nbEdges ); + myIsUniform.resize ( nbEdges, true ); + myLength = 0; + myNbPonits = myNbSegments = 0; + myMesh = theMesh; myMissingVertexNodes = false; - myIgnoreMediumNodes = theIgnoreMediumNodes; + myIgnoreMediumNodes = theIgnoreMediumNodes; + myDefaultPnt2d = gp_Pnt2d( 1e+100, 1e+100 ); if ( nbEdges == 0 ) return; SMESHDS_Mesh* meshDS = theMesh->GetMeshDS(); - vector len( nbEdges ); int nbDegen = 0; list::iterator edge = theEdges.begin(); + TopoDS_Iterator vExp; for ( int index = 0; edge != theEdges.end(); ++index, ++edge ) { - int i = theIsForward ? index : nbEdges - index - 1; - len[i] = SMESH_Algo::EdgeLength( *edge ); - if ( len[i] < DBL_MIN ) nbDegen++; - myLength += len[i]; + int i = theIsForward ? index : nbEdges-index-1; + myEdgeLength[i] = SMESH_Algo::EdgeLength( *edge ); + if ( myEdgeLength[i] < DBL_MIN ) nbDegen++; + myLength += myEdgeLength[i]; myEdge[i] = *edge; + myEdgeID[i] = meshDS->ShapeToIndex( *edge ); if ( !theIsForward ) myEdge[i].Reverse(); if ( theFace.IsNull() ) @@ -128,16 +136,43 @@ StdMeshers_FaceSide::StdMeshers_FaceSide(const TopoDS_Face& theFace, myNbPonits += nbN; myNbSegments += sm->NbElements(); } - if ( SMESH_Algo::VertexNode( TopExp::FirstVertex( *edge, 1), meshDS )) + // TopExp::FirstVertex() and TopExp::LastVertex() return NULL from INTERNAL edge + vExp.Initialize( *edge ); + if ( vExp.Value().Orientation() == TopAbs_REVERSED ) vExp.Next(); + if ( SMESH_Algo::VertexNode( TopoDS::Vertex( vExp.Value()), meshDS )) myNbPonits += 1; // for the first end else myMissingVertexNodes = true; - } - if ( SMESH_Algo::VertexNode( TopExp::LastVertex( theEdges.back(), 1), meshDS )) - myNbPonits++; // for the last end - else - myMissingVertexNodes = true; + // check if edge has non-uniform parametrization (issue 0020705) + if ( !myC2d[i].IsNull() && myEdgeLength[i] > DBL_MIN) + { + Geom2dAdaptor_Curve A2dC( myC2d[i], + std::min( myFirst[i], myLast[i] ), + std::max( myFirst[i], myLast[i] )); + double p2 = myFirst[i]+(myLast[i]-myFirst[i])/2., p4 = myFirst[i]+(myLast[i]-myFirst[i])/4.; + double d2 = GCPnts_AbscissaPoint::Length( A2dC, myFirst[i], p2 ); + double d4 = GCPnts_AbscissaPoint::Length( A2dC, myFirst[i], p4 ); + //cout<<"len = "<MeshElements( myEdge[i] )) + { + vector< pair< double, const SMDS_MeshNode*> > u2nodeVec; + u2nodeVec.reserve( sm->NbNodes() ); + SMDS_NodeIteratorPtr nItr = sm->GetNodes(); + double paramSize = myLast[i] - myFirst[i]; + double r = myNormPar[i] - prevNormPar; + helper.SetSubShape( myEdge[i] ); + helper.ToFixNodeParameters( true ); + if ( !myIsUniform[i] ) + while ( nItr->more() ) + { + const SMDS_MeshNode* node = nItr->next(); + if ( myIgnoreMediumNodes && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Edge )) + continue; + double u = helper.GetNodeU( myEdge[i], node, 0, ¶mOK ); + double aLenU = GCPnts_AbscissaPoint::Length + ( const_cast( myC3dAdaptor[i]), myFirst[i], u ); + if ( myEdgeLength[i] < aLenU ) // nonregression test "3D_mesh_NETGEN/G6" + { + u2nodeVec.clear(); + break; + } + double normPar = prevNormPar + r*aLenU/myEdgeLength[i]; + u2nodeVec.push_back( make_pair( normPar, node )); + } + nItr = sm->GetNodes(); + if ( u2nodeVec.empty() ) + while ( nItr->more() ) + { + const SMDS_MeshNode* node = nItr->next(); + if ( myIgnoreMediumNodes && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Edge )) + continue; + double u = helper.GetNodeU( myEdge[i], node, 0, ¶mOK ); + + // paramSize is signed so orientation is taken into account + double normPar = prevNormPar + r * ( u - myFirst[i] ) / paramSize; + u2nodeVec.push_back( make_pair( normPar, node )); + } + for ( size_t j = 0; j < u2nodeVec.size(); ++j ) + u2node.insert( u2node.end(), u2nodeVec[j] ); + } + + // Put 2nd vertex node for a last edge if ( i+1 == myEdge.size() ) { - node = SMESH_Algo::VertexNode( VLast, meshDS ); + node = SMESH_Algo::VertexNode( VV[1], meshDS ); if ( !node ) { MESSAGE(" NO NODE on VERTEX" ); return myPoints; } - u2node.insert( make_pair( 1., node )); - } - - // put internal nodes - SMESHDS_SubMesh* sm = meshDS->MeshElements( myEdge[i] ); - if ( !sm ) continue; - SMDS_NodeIteratorPtr nItr = sm->GetNodes(); - double paramSize = myLast[i] - myFirst[i], r = myNormPar[i] - prevNormPar; - while ( nItr->more() ) { - const SMDS_MeshNode* node = nItr->next(); - if ( myIgnoreMediumNodes && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Edge )) - continue; - const SMDS_EdgePosition* epos = - static_cast(node->GetPosition().get()); - double u = epos->GetUParameter(); - // paramSize is signed so orientation is taken into account - double normPar = prevNormPar + r * ( u - myFirst[i] ) / paramSize; -#ifdef _DEBUG_ - if ( normPar > 1 || normPar < 0) { - dump("DEBUG"); - MESSAGE ( "WRONG normPar: "<MeshElements( myEdge[i] )) + { + SMDS_NodeIteratorPtr nItr = sm->GetNodes(); + double paramSize = myLast[i] - myFirst[i]; + double r = myNormPar[i] - prevNormPar; + helper.SetSubShape( myEdge[i] ); + helper.ToFixNodeParameters( true ); + while ( nItr->more() ) + { + const SMDS_MeshNode* node = nItr->next(); + if ( myIgnoreMediumNodes && SMESH_MeshEditor::IsMedium( node, SMDSAbs_Edge )) + continue; + double u = helper.GetNodeU( myEdge[i], node, 0, ¶mOK ); + + // paramSize is signed so orientation is taken into account + double normPar = prevNormPar + r * ( u - myFirst[i] ) / paramSize; + u2node.insert( u2node.end(), make_pair( normPar, node )); + } + } + + // Put 2nd vertex node for a last edge + if ( i+1 == myEdge.size() ) { + node = SMESH_Algo::VertexNode( VV[1], meshDS ); + if ( !node ) { + return resultNodes; + } + u2node.insert( u2node.end(), make_pair( 1., node )); + } + } + + // Fill the result vector + + if ( u2node.size() == myNbPonits ) + { + resultNodes.reserve( u2node.size() ); + map< double, const SMDS_MeshNode*>::iterator u2n = u2node.begin(); + for ( ; u2n != u2node.end(); ++u2n ) + resultNodes.push_back( u2n->second ); + } + } + else + { + resultNodes.resize( myPoints.size() ); + for ( size_t i = 0; i < myPoints.size(); ++i ) + resultNodes[i] = myPoints[i].node; + } + + return resultNodes; +} //================================================================================ /*! @@ -362,14 +533,21 @@ void StdMeshers_FaceSide::Reverse() } if ( nbEdges > 1 ) { reverse( myEdge ); + reverse( myEdgeID ); reverse( myC2d ); + reverse( myC3dAdaptor ); reverse( myFirst ); reverse( myLast ); reverse( myNormPar ); + reverse( myEdgeLength ); + reverse( myIsUniform ); + } + if ( nbEdges > 0 ) + { + myNormPar[nbEdges-1]=1.; + myPoints.clear(); + myFalsePoints.clear(); } - myNormPar[nbEdges-1]=1.; - myPoints.clear(); - myFalsePoints.clear(); } //================================================================================ @@ -443,17 +621,19 @@ BRepAdaptor_CompCurve* StdMeshers_FaceSide::GetCurve3d() const if ( myEdge.empty() ) return 0; -// if ( myEdge.size() == 1 ) -// return new BRepAdaptor_Curve( myEdge[0] ); - TopoDS_Wire aWire; BRep_Builder aBuilder; aBuilder.MakeWire(aWire); for ( int i=0; iValue( myFirst[i] * ( 1 - r ) + myLast[i] * r ); + + double par = myFirst[i] * ( 1 - r ) + myLast[i] * r; + + // check parametrization of curve + if( !myIsUniform[i] ) + { + double aLen3dU = r * myEdgeLength[i] * ( myFirst[i]>myLast[i] ? -1. : 1.); + GCPnts_AbscissaPoint AbPnt + ( const_cast( myC3dAdaptor[i]), aLen3dU, myFirst[i] ); + if( AbPnt.IsDone() ) { + par = AbPnt.Parameter(); + } + } + return myC2d[ i ]->Value(par); + } - return gp_Pnt2d( 1e+100, 1e+100 ); + return myDefaultPnt2d; } //================================================================================ @@ -485,46 +679,97 @@ TSideVector StdMeshers_FaceSide::GetFaceWires(const TopoDS_Face& theFace, TError & theError) { TopoDS_Vertex V1; - list< TopoDS_Edge > edges; + list< TopoDS_Edge > edges, internalEdges; list< int > nbEdgesInWires; int nbWires = SMESH_Block::GetOrderedEdges (theFace, V1, edges, nbEdgesInWires); // split list of all edges into separate wires TSideVector wires( nbWires ); list< int >::iterator nbE = nbEdgesInWires.begin(); - list< TopoDS_Edge >::iterator from, to; - from = to = edges.begin(); - for ( int iW = 0; iW < nbWires; ++iW ) + list< TopoDS_Edge >::iterator from = edges.begin(), to = from; + for ( int iW = 0; iW < nbWires; ++iW, ++nbE ) { - std::advance( to, *nbE++ ); + std::advance( to, *nbE ); + if ( *nbE == 0 ) // Issue 0020676 + { + --nbWires; + --iW; + wires.resize( nbWires ); + continue; + } list< TopoDS_Edge > wireEdges( from, to ); // assure that there is a node on the first vertex // as StdMeshers_FaceSide::GetUVPtStruct() requires - while ( !SMESH_Algo::VertexNode( TopExp::FirstVertex( wireEdges.front(), true), - theMesh.GetMeshDS())) + if ( wireEdges.front().Orientation() != TopAbs_INTERNAL ) // Issue 0020676 { - wireEdges.splice(wireEdges.end(), wireEdges, - wireEdges.begin(), ++wireEdges.begin()); - if ( from->IsSame( wireEdges.front() )) { - theError = TError - ( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"No nodes on vertices")); - return TSideVector(0); + while ( !SMESH_Algo::VertexNode( TopExp::FirstVertex( wireEdges.front(), true), + theMesh.GetMeshDS())) + { + wireEdges.splice(wireEdges.end(), wireEdges, + wireEdges.begin(), ++wireEdges.begin()); + if ( from->IsSame( wireEdges.front() )) { + theError = TError + ( new SMESH_ComputeError(COMPERR_BAD_INPUT_MESH,"No nodes on vertices")); + return TSideVector(0); + } } } - // find out side orientation, which is important if there are several wires (PAL19080) - bool isForward = true; - if ( nbWires > 1 ) { - TopExp_Explorer e( theFace, TopAbs_EDGE ); - while ( ! e.Current().IsSame( wireEdges.back() )) - e.Next(); - isForward = ( e.Current().Orientation() == wireEdges.back().Orientation() ); + else if ( *nbE > 1 ) // Issue 0020676 (Face_pb_netgen.brep) - several internal edges in a wire + { + internalEdges.splice( internalEdges.end(), wireEdges, ++wireEdges.begin(), wireEdges.end()); } StdMeshers_FaceSide* wire = new StdMeshers_FaceSide( theFace, wireEdges, &theMesh, - isForward, theIgnoreMediumNodes); + /*isForward=*/true, theIgnoreMediumNodes); wires[ iW ] = StdMeshers_FaceSidePtr( wire ); from = to; } + while ( !internalEdges.empty() ) + { + StdMeshers_FaceSide* wire = new StdMeshers_FaceSide( theFace, internalEdges.back(), &theMesh, + /*isForward=*/true, theIgnoreMediumNodes); + wires.push_back( StdMeshers_FaceSidePtr( wire )); + internalEdges.pop_back(); + } return wires; } +//================================================================================ +/*! + * \brief Return 1st vertex of the i-the edge + */ +//================================================================================ + +TopoDS_Vertex StdMeshers_FaceSide::FirstVertex(int i) const +{ + TopoDS_Vertex v; + if ( i < NbEdges() ) + { + v = myEdge[i].Orientation() <= TopAbs_REVERSED ? // FORWARD || REVERSED + TopExp::FirstVertex( myEdge[i], 1 ) : + TopoDS::Vertex( TopoDS_Iterator( myEdge[i] ).Value() ); + } + return v; +} + +//================================================================================ +/*! + * \brief Return last vertex of the i-the edge + */ +//================================================================================ + +TopoDS_Vertex StdMeshers_FaceSide::LastVertex(int i) const +{ + TopoDS_Vertex v; + if ( i < NbEdges() ) + { + const TopoDS_Edge& edge = i<0 ? myEdge[ NbEdges() + i ] : myEdge[i]; + if ( edge.Orientation() <= TopAbs_REVERSED ) // FORWARD || REVERSED + v = TopExp::LastVertex( edge, 1 ); + else + for ( TopoDS_Iterator vIt( edge ); vIt.More(); vIt.Next() ) + v = TopoDS::Vertex( vIt.Value() ); + } + return v; +} + diff --git a/src/StdMeshers/StdMeshers_FaceSide.hxx b/src/StdMeshers/StdMeshers_FaceSide.hxx index 202b6a6d4..4a119ed84 100644 --- a/src/StdMeshers/StdMeshers_FaceSide.hxx +++ b/src/StdMeshers/StdMeshers_FaceSide.hxx @@ -1,25 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_FaceSide.hxx // Created : Wed Jan 31 18:41:25 2007 // Author : Edward AGAPOV (eap) @@ -28,14 +28,13 @@ #ifndef StdMeshers_FaceSide_HeaderFile #define StdMeshers_FaceSide_HeaderFile -#include +#include "SMESH_StdMeshers.hxx" + +#include +#include #include #include -#include -#include - -#include "SMESH_StdMeshers.hxx" -#include "SMESH_Algo.hxx" +#include #include #include @@ -47,7 +46,7 @@ class Adaptor2d_Curve2d; class Adaptor3d_Curve; class BRepAdaptor_CompCurve; class TopoDS_Face; -class SMESH_ComputeError; +struct SMESH_ComputeError; typedef struct uvPtStruct { @@ -63,9 +62,8 @@ typedef struct uvPtStruct class StdMeshers_FaceSide; typedef boost::shared_ptr< StdMeshers_FaceSide > StdMeshers_FaceSidePtr; -typedef boost::shared_ptr< uvPtStruct > UVPtStructPtr; -typedef std::vector< StdMeshers_FaceSidePtr > TSideVector; -typedef boost::shared_ptr< SMESH_ComputeError > TError; +typedef std::vector< StdMeshers_FaceSidePtr > TSideVector; +typedef boost::shared_ptr< SMESH_ComputeError > TError; //================================================================================ /*! @@ -88,12 +86,17 @@ public: /*! * \brief Wrap several edges. Edges must be properly ordered and oriented. */ - StdMeshers_FaceSide(const TopoDS_Face& theFace, + StdMeshers_FaceSide(const TopoDS_Face& theFace, std::list& theEdges, - SMESH_Mesh* theMesh, - const bool theIsForward, - const bool theIgnoreMediumNodes); - + SMESH_Mesh* theMesh, + const bool theIsForward, + const bool theIgnoreMediumNodes); + /*! + * \brief Simulate a side from a vertex using data from other FaceSide + */ + StdMeshers_FaceSide(const SMDS_MeshNode* theNode, + const gp_Pnt2d thePnt2d, + const StdMeshers_FaceSide* theSide); /*! * \brief Return wires of a face as StdMeshers_FaceSide's */ @@ -119,7 +122,7 @@ public: */ SMESH_Mesh* GetMesh() const { return myMesh; } /*! - * \brief Return true if there vertices without nodes + * \brief Return true if there are vertices without nodes */ bool MissVertexNode() const { return myMissingVertexNodes; } /*! @@ -127,7 +130,8 @@ public: * \param isXConst - true if normalized parameter X is constant * \param constValue - constant parameter value * - * Missing nodes are allowed only on internal vertices + * Missing nodes are allowed only on internal vertices. + * For a closed side, the 1st point repeats at end */ const std::vector& GetUVPtStruct(bool isXConst =0, double constValue =0) const; /*! @@ -136,8 +140,13 @@ public: * \param constValue - constant parameter value */ const std::vector& SimulateUVPtStruct(int nbSeg, - bool isXConst = 0, - double constValue = 0) const; + bool isXConst = 0, + double constValue = 0) const; + /*! + * \brief Return nodes in the order they encounter while walking along the side. + * For a closed side, the 1st point repeats at end + */ + std::vector GetOrderedNodes() const; /*! * \brief Return edge and parameter on edge by normalized parameter */ @@ -162,14 +171,18 @@ public: * \brief Return i-th wrapped edge (count starts from zero) */ const TopoDS_Edge& Edge(int i) const { return myEdge[i]; } + /*! + * \brief Return all edges + */ + const std::vector& Edges() const { return myEdge; } /*! * \brief Return 1st vertex of the i-the edge (count starts from zero) */ - inline TopoDS_Vertex FirstVertex(int i=0) const; + TopoDS_Vertex FirstVertex(int i=0) const; /*! * \brief Return last vertex of the i-the edge (count starts from zero) */ - inline TopoDS_Vertex LastVertex(int i=-1) const; + TopoDS_Vertex LastVertex(int i=-1) const; /*! * \brief Return first normalized parameter of the i-the edge (count starts from zero) */ @@ -193,15 +206,22 @@ public: protected: + + // DON't FORGET to update Reverse() when adding one more vector! std::vector myPoints, myFalsePoints; std::vector myEdge; + std::vector myEdgeID; std::vector myC2d; + std::vector myC3dAdaptor; std::vector myFirst, myLast; std::vector myNormPar; + std::vector myEdgeLength; + std::vector myIsUniform; double myLength; int myNbPonits, myNbSegments; SMESH_Mesh* myMesh; bool myMissingVertexNodes, myIgnoreMediumNodes; + gp_Pnt2d myDefaultPnt2d; }; @@ -237,28 +257,6 @@ inline double StdMeshers_FaceSide::Parameter(double U, TopoDS_Edge & edge) const return myFirst[i] * ( 1 - r ) + myLast[i] * r; } -//================================================================================ -/*! - * \brief Return 1st vertex of the i-the edge - */ -//================================================================================ - -inline TopoDS_Vertex StdMeshers_FaceSide::FirstVertex(int i) const -{ - return i < myEdge.size() ? TopExp::FirstVertex( myEdge[i], 1 ) : TopoDS_Vertex(); -} - -//================================================================================ -/*! - * \brief Return last vertex of the i-the edge - */ -//================================================================================ - -inline TopoDS_Vertex StdMeshers_FaceSide::LastVertex(int i) const -{ - return i<0 ? TopExp::LastVertex( myEdge.back(), 1) : i +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +using namespace std; + +//============================================================================= +/*! + * + */ +//============================================================================= + +StdMeshers_FixedPoints1D::StdMeshers_FixedPoints1D(int hypId, int studyId, + SMESH_Gen * gen) + :SMESH_Hypothesis(hypId, studyId, gen) +{ + _name = "FixedPoints1D"; + _param_algo_dim = 1; + _nbsegs.reserve( 1 ); + _nbsegs.push_back( 1 ); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +StdMeshers_FixedPoints1D::~StdMeshers_FixedPoints1D() +{ +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +void StdMeshers_FixedPoints1D::SetPoints(std::vector& listParams) + throw(SALOME_Exception) +{ + _params = listParams; + NotifySubMeshesHypothesisModification(); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +void StdMeshers_FixedPoints1D::SetNbSegments(std::vector& listNbSeg) + throw(SALOME_Exception) +{ + _nbsegs = listNbSeg; + NotifySubMeshesHypothesisModification(); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +void StdMeshers_FixedPoints1D::SetReversedEdges( std::vector& ids ) +{ + if ( ids != _edgeIDs ) { + _edgeIDs = ids; + + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +ostream & StdMeshers_FixedPoints1D::SaveTo(ostream & save) +{ + int listSize = _params.size(); + save << listSize; + if ( listSize > 0 ) { + for ( int i = 0; i < listSize; i++) save << " " << _params[i]; + } + + listSize = _nbsegs.size(); + save << " " << listSize; + if ( listSize > 0 ) { + for ( int i = 0; i < listSize; i++) save << " " << _nbsegs[i]; + } + + listSize = _edgeIDs.size(); + save << " " << listSize; + if ( listSize > 0 ) { + for ( int i = 0; i < listSize; i++) + save << " " << _edgeIDs[i]; + } + + save << " " << _objEntry; + + return save; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +istream & StdMeshers_FixedPoints1D::LoadFrom(istream & load) +{ + bool isOK = true; + int intVal; + double dblVal; + + isOK = (load >> intVal); + if (isOK && intVal > 0) { + _params.clear(); + _params.reserve( intVal ); + for (int i = 0; i < _params.capacity() && isOK; i++) { + isOK = (load >> dblVal); + if ( isOK ) _params.push_back( dblVal ); + } + } + + isOK = (load >> intVal); + if (isOK && intVal > 0) { + _nbsegs.clear(); + _nbsegs.reserve( intVal ); + for (int i = 0; i < _nbsegs.capacity() && isOK; i++) { + isOK = (load >> intVal); + if ( isOK ) _nbsegs.push_back( intVal ); + } + } + + isOK = (load >> intVal); + if (isOK && intVal > 0) { + _edgeIDs.clear(); + _edgeIDs.reserve( intVal ); + for (int i = 0; i < _edgeIDs.capacity() && isOK; i++) { + isOK = (load >> intVal); + if ( isOK ) _edgeIDs.push_back( intVal ); + } + } + + isOK = (load >> _objEntry); + + return load; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +ostream & operator <<(ostream & save, StdMeshers_FixedPoints1D & hyp) +{ + return hyp.SaveTo( save ); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +istream & operator >>(istream & load, StdMeshers_FixedPoints1D & hyp) +{ + return hyp.LoadFrom( load ); +} + +//================================================================================ +/*! + * \brief Initialize start and end length by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ + +bool StdMeshers_FixedPoints1D::SetParametersByMesh(const SMESH_Mesh* theMesh, + const TopoDS_Shape& theShape) +{ + if ( !theMesh || theShape.IsNull() ) + return false; + + _nbsegs.reserve( 1 ); + _nbsegs.push_back( 1 ); + return true; +} + +//================================================================================ +/*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ + +bool StdMeshers_FixedPoints1D::SetParametersByDefaults(const TDefaults& dflts, + const SMESH_Mesh* /*mesh*/) +{ + _nbsegs.reserve( 1 ); + _nbsegs.push_back( 1 ); + return true; +} + diff --git a/src/StdMeshers/StdMeshers_FixedPoints1D.hxx b/src/StdMeshers/StdMeshers_FixedPoints1D.hxx new file mode 100644 index 000000000..384ad9c93 --- /dev/null +++ b/src/StdMeshers/StdMeshers_FixedPoints1D.hxx @@ -0,0 +1,88 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : StdMeshers_FixedPoints1D.hxx +// Author : Damien COQUERET, OCC +// Module : SMESH +// +#ifndef _SMESH_FIXEDPOINTS1D_HXX_ +#define _SMESH_FIXEDPOINTS1D_HXX_ + + + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_Hypothesis.hxx" +#include "Utils_SALOME_Exception.hxx" + +#include + +class STDMESHERS_EXPORT StdMeshers_FixedPoints1D: + public SMESH_Hypothesis +{ +public: + StdMeshers_FixedPoints1D(int hypId, int studyId, SMESH_Gen* gen); + virtual ~StdMeshers_FixedPoints1D(); + + void SetPoints(std::vector& listParams) + throw(SALOME_Exception); + + void SetNbSegments(std::vector& listNbSeg) + throw(SALOME_Exception); + + const std::vector& GetPoints() const { return _params; } + + const std::vector& GetNbSegments() const { return _nbsegs; } + + void SetReversedEdges( std::vector& ids); + + void SetObjectEntry( const char* entry ) { _objEntry = entry; } + + const char* GetObjectEntry() { return _objEntry.c_str(); } + + const std::vector& GetReversedEdges() const { return _edgeIDs; } + + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + friend std::ostream& operator << (std::ostream & save, StdMeshers_FixedPoints1D & hyp); + friend std::istream& operator >> (std::istream & load, StdMeshers_FixedPoints1D & hyp); + + /*! + * \brief Initialize start and end length by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ + virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape); + + /*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ + virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0); + +protected: + std::vector _params; + std::vector _nbsegs; + std::vector _edgeIDs; + std::string _objEntry; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_HexaFromSkin_3D.cxx b/src/StdMeshers/StdMeshers_HexaFromSkin_3D.cxx new file mode 100644 index 000000000..58a90931c --- /dev/null +++ b/src/StdMeshers/StdMeshers_HexaFromSkin_3D.cxx @@ -0,0 +1,1303 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_HexaFromSkin_3D.cxx +// Created : Wed Jan 27 12:28:07 2010 +// Author : Edward AGAPOV (eap) + +#include "StdMeshers_HexaFromSkin_3D.hxx" + +#include "SMDS_VolumeOfNodes.hxx" +#include "SMDS_VolumeTool.hxx" +#include "SMESH_Block.hxx" +#include "SMESH_MesherHelper.hxx" + +#include + +//#include "utilities.h" +#include + +// Define error message and _MYDEBUG_ if needed +#ifdef _DEBUG_ +#define BAD_MESH_ERR \ + error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh.\n" \ + __FILE__ ":" )<<__LINE__) +//#define _MYDEBUG_ +#else +#define BAD_MESH_ERR \ + error(SMESH_Comment("Can't detect block-wise structure of the input 2D mesh")) +#endif + + +// Debug output +#ifdef _MYDEBUG_ +#define _DUMP_(msg) cout << msg << endl +#else +#define _DUMP_(msg) +#endif + + +namespace +{ + enum EBoxSides //!< sides of the block + { + B_BOTTOM=0, B_RIGHT, B_TOP, B_LEFT, B_FRONT, B_BACK, NB_BLOCK_SIDES + }; +#ifdef _MYDEBUG_ + const char* SBoxSides[] = //!< names of block sides -- needed for DEBUG only + { + "BOTTOM", "RIGHT", "TOP", "LEFT", "FRONT", "BACK", "UNDEFINED" + }; +#endif + enum EQuadEdge //!< edges of quadrangle side + { + Q_BOTTOM = 0, Q_RIGHT, Q_TOP, Q_LEFT, NB_QUAD_SIDES + }; + + + //================================================================================ + /*! + * \brief return logical coordinates (i.e. min or max) of ends of edge + */ + //================================================================================ + + bool getEdgeEnds(EQuadEdge edge, bool& xMax1, bool& yMax1, bool& xMax2, bool& yMax2 ) + { + xMax1=0, yMax1=0, xMax2=1, yMax2=1; + switch( edge ) + { + case Q_BOTTOM: yMax2 = 0; break; + case Q_RIGHT: xMax1 = 1; break; + case Q_TOP: yMax1 = 1; break; + case Q_LEFT: xMax2 = 0; break; + default: + return false; + } + return true; + } + + //================================================================================ + /*! + * \brief return true if a node is at block corner + * + * This check is valid for simple cases only + */ + //================================================================================ + + bool isCornerNode( const SMDS_MeshNode* n ) + { + int nbF = n ? n->NbInverseElements( SMDSAbs_Face ) : 1; + if ( nbF % 2 ) + return true; + + set nodesInInverseFaces; + SMDS_ElemIteratorPtr fIt = n->GetInverseElementIterator(SMDSAbs_Face ); + while ( fIt->more() ) + { + const SMDS_MeshElement* face = fIt->next(); + nodesInInverseFaces.insert( face->begin_nodes(), face->end_nodes() ); + } + + return nodesInInverseFaces.size() != ( 6 + (nbF/2-1)*3 ); + } + + //================================================================================ + /*! + * \brief check element type + */ + //================================================================================ + + bool isQuadrangle(const SMDS_MeshElement* e) + { + return ( e && e->NbCornerNodes() == 4 ); + } + + //================================================================================ + /*! + * \brief return opposite node of a quadrangle face + */ + //================================================================================ + + const SMDS_MeshNode* oppositeNode(const SMDS_MeshElement* quad, int iNode) + { + return quad->GetNode( (iNode+2) % 4 ); + } + + //================================================================================ + /*! + * \brief Convertor of a pair of integers to a sole index + */ + struct _Indexer + { + int _xSize, _ySize; + _Indexer( int xSize=0, int ySize=0 ): _xSize(xSize), _ySize(ySize) {} + int size() const { return _xSize * _ySize; } + int operator()(int x, int y) const { return y * _xSize + x; } + }; + //================================================================================ + /*! + * \brief Oriented convertor of a pair of integers to a sole index + */ + class _OrientedIndexer : public _Indexer + { + public: + enum OriFlags //!< types of block side orientation + { + REV_X = 1, REV_Y = 2, SWAP_XY = 4, MAX_ORI = REV_X|REV_Y|SWAP_XY + }; + _OrientedIndexer( const _Indexer& indexer, const int oriFlags ): + _Indexer( indexer._xSize, indexer._ySize ), + _xSize (indexer._xSize), _ySize(indexer._ySize), + _xRevFun((oriFlags & REV_X) ? & reverse : & lazy), + _yRevFun((oriFlags & REV_Y) ? & reverse : & lazy), + _swapFun((oriFlags & SWAP_XY ) ? & swap : & lazy) + { + (*_swapFun)( _xSize, _ySize ); + } + //!< Return index by XY + int operator()(int x, int y) const + { + (*_xRevFun)( x, const_cast( _xSize )); + (*_yRevFun)( y, const_cast( _ySize )); + (*_swapFun)( x, y ); + return _Indexer::operator()(x,y); + } + //!< Return index for a corner + int corner(bool xMax, bool yMax) const + { + int x = xMax, y = yMax, size = 2; + (*_xRevFun)( x, size ); + (*_yRevFun)( y, size ); + (*_swapFun)( x, y ); + return _Indexer::operator()(x ? _Indexer::_xSize-1 : 0 , y ? _Indexer::_ySize-1 : 0); + } + int xSize() const { return _xSize; } + int ySize() const { return _ySize; } + private: + _Indexer _indexer; + int _xSize, _ySize; + + typedef void (*TFun)(int& x, int& y); + TFun _xRevFun, _yRevFun, _swapFun; + + static void lazy (int&, int&) {} + static void reverse(int& x, int& size) { x = size - x - 1; } + static void swap (int& x, int& y) { std::swap(x,y); } + }; + //================================================================================ + /*! + * \brief Structure corresponding to the meshed side of block + */ + struct _BlockSide + { + vector _grid; + _Indexer _index; + int _nbBlocksExpected; + int _nbBlocksFound; + +#ifdef _DEBUG_ // want to get SIGSEGV in case of invalid index +#define _grid_access_(pobj, i) pobj->_grid[ ((i) < pobj->_grid.size()) ? i : int(1e100)] +#else +#define _grid_access_(pobj, i) pobj->_grid[ i ] +#endif + //!< Return node at XY + const SMDS_MeshNode* getNode(int x, int y) const { return _grid_access_(this, _index( x,y ));} + //!< Set node at XY + void setNode(int x, int y, const SMDS_MeshNode* n) { _grid_access_(this, _index( x,y )) = n; } + //!< Return an edge + SMESH_OrientedLink getEdge(EQuadEdge edge) const + { + bool x1, y1, x2, y2; getEdgeEnds( edge, x1, y1, x2, y2 ); + return SMESH_OrientedLink( getCornerNode ( x1, y1 ), getCornerNode( x2, y2 )); + } + //!< Return a corner node + const SMDS_MeshNode* getCornerNode(bool isXMax, bool isYMax) const + { + return getNode( isXMax ? _index._xSize-1 : 0 , isYMax ? _index._ySize-1 : 0 ); + } + const SMDS_MeshElement* getCornerFace(const SMDS_MeshNode* cornerNode) const; + //!< True if all blocks this side belongs to have been found + bool isBound() const { return _nbBlocksExpected <= _nbBlocksFound; } + //!< Return coordinates of node at XY + gp_XYZ getXYZ(int x, int y) const { return SMESH_TNodeXYZ( getNode( x, y )); } + //!< Return gravity center of the four corners and the middle node + gp_XYZ getGC() const + { + gp_XYZ xyz = + getXYZ( 0, 0 ) + + getXYZ( _index._xSize-1, 0 ) + + getXYZ( 0, _index._ySize-1 ) + + getXYZ( _index._xSize-1, _index._ySize-1 ) + + getXYZ( _index._xSize/2, _index._ySize/2 ); + return xyz / 5; + } + //!< Return number of mesh faces + int getNbFaces() const { return (_index._xSize-1) * (_index._ySize-1); } + }; + //================================================================================ + /*! + * \brief _BlockSide with changed orientation + */ + struct _OrientedBlockSide + { + _BlockSide* _side; + _OrientedIndexer _index; + + _OrientedBlockSide( _BlockSide* side=0, const int oriFlags=0 ): + _side(side), _index(side ? side->_index : _Indexer(), oriFlags ) {} + //!< return coordinates by XY + gp_XYZ xyz(int x, int y) const + { + return SMESH_TNodeXYZ( _grid_access_(_side, _index( x, y )) ); + } + //!< safely return a node by XY + const SMDS_MeshNode* node(int x, int y) const + { + int i = _index( x, y ); + return ( i < 0 || i >= _side->_grid.size()) ? 0 : _side->_grid[i]; + } + //!< Return an edge + SMESH_OrientedLink edge(EQuadEdge edge) const + { + bool x1, y1, x2, y2; getEdgeEnds( edge, x1, y1, x2, y2 ); + return SMESH_OrientedLink( cornerNode ( x1, y1 ), cornerNode( x2, y2 )); + } + //!< Return a corner node + const SMDS_MeshNode* cornerNode(bool isXMax, bool isYMax) const + { + return _grid_access_(_side, _index.corner( isXMax, isYMax )); + } + //!< return its size in nodes + int getHoriSize() const { return _index.xSize(); } + int getVertSize() const { return _index.ySize(); } + //!< True if _side has been initialized + operator bool() const { return _side; } + //! Direct access to _side + const _BlockSide* operator->() const { return _side; } + _BlockSide* operator->() { return _side; } + }; + //================================================================================ + /*! + * \brief Meshed skin of block + */ + struct _Block + { + _OrientedBlockSide _side[6]; // 6 sides of a sub-block + set _corners; + + const _OrientedBlockSide& getSide(int i) const { return _side[i]; } + bool setSide( int i, const _OrientedBlockSide& s) + { + if (( _side[i] = s )) + { + _corners.insert( s.cornerNode(0,0)); + _corners.insert( s.cornerNode(1,0)); + _corners.insert( s.cornerNode(0,1)); + _corners.insert( s.cornerNode(1,1)); + } + return s; + } + void clear() { for (int i=0;i<6;++i) _side[i]=0; _corners.clear(); } + bool hasSide( const _OrientedBlockSide& s) const + { + if ( s ) for (int i=0;i<6;++i) if ( _side[i] && _side[i]._side == s._side ) return true; + return false; + } + int nbSides() const { int n=0; for (int i=0;i<6;++i) if ( _side[i] ) ++n; return n; } + bool isValid() const; + }; + //================================================================================ + /*! + * \brief Skin mesh possibly containing several meshed blocks + */ + class _Skin + { + public: + + int findBlocks(SMESH_Mesh& mesh); + //!< return i-th block + const _Block& getBlock(int i) const { return _blocks[i]; } + //!< return error description + const SMESH_Comment& error() const { return _error; } + + private: + bool fillSide( _BlockSide& side, + const SMDS_MeshElement* cornerQuad, + const SMDS_MeshNode* cornerNode); + bool fillRowsUntilCorner(const SMDS_MeshElement* quad, + const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + vector& verRow1, + vector& verRow2, + bool alongN1N2 ); + _OrientedBlockSide findBlockSide( EBoxSides startBlockSide, + EQuadEdge sharedSideEdge1, + EQuadEdge sharedSideEdge2, + bool withGeometricAnalysis, + set< _BlockSide* >& sidesAround); + //!< update own data and data of the side bound to block + void setSideBoundToBlock( _BlockSide& side ) + { + if ( side._nbBlocksFound++, side.isBound() ) + for ( int e = 0; e < int(NB_QUAD_SIDES); ++e ) + _edge2sides[ side.getEdge( (EQuadEdge) e ) ].erase( &side ); + } + //!< store reason of error + int error(const SMESH_Comment& reason) { _error = reason; return 0; } + + SMESH_Comment _error; + + list< _BlockSide > _allSides; + vector< _Block > _blocks; + + //map< const SMDS_MeshNode*, set< _BlockSide* > > _corner2sides; + map< SMESH_OrientedLink, set< _BlockSide* > > _edge2sides; + }; + + //================================================================================ + /*! + * \brief Find and return number of submeshes corresponding to blocks + */ + //================================================================================ + + int _Skin::findBlocks(SMESH_Mesh& mesh) + { + SMESHDS_Mesh* meshDS = mesh.GetMeshDS(); + + // Find a node at any block corner + + SMDS_NodeIteratorPtr nIt = meshDS->nodesIterator(/*idInceasingOrder=*/true); + if ( !nIt->more() ) return error("Empty mesh"); + + const SMDS_MeshNode* nCorner = 0; + while ( nIt->more() ) + { + nCorner = nIt->next(); + if ( isCornerNode( nCorner )) + break; + else + nCorner = 0; + } + if ( !nCorner ) + return BAD_MESH_ERR; + + // -------------------------------------------------------------------- + // Find all block sides starting from mesh faces sharing the corner node + // -------------------------------------------------------------------- + + int nbFacesOnSides = 0; + TIDSortedElemSet cornerFaces; // corner faces of found _BlockSide's + list< const SMDS_MeshNode* > corners( 1, nCorner ); + list< const SMDS_MeshNode* >::iterator corner = corners.begin(); + while ( corner != corners.end() ) + { + SMDS_ElemIteratorPtr faceIt = (*corner)->GetInverseElementIterator( SMDSAbs_Face ); + while ( faceIt->more() ) + { + const SMDS_MeshElement* face = faceIt->next(); + if ( !cornerFaces.insert( face ).second ) + continue; // already loaded block side + + if ( !isQuadrangle( face )) + return error("Non-quadrangle elements in the input mesh"); + + if ( _allSides.empty() || !_allSides.back()._grid.empty() ) + _allSides.push_back( _BlockSide() ); + + _BlockSide& side = _allSides.back(); + if ( !fillSide( side, face, *corner ) ) + { + if ( !_error.empty() ) + return false; + } + else + { + for ( int isXMax = 0; isXMax < 2; ++isXMax ) + for ( int isYMax = 0; isYMax < 2; ++isYMax ) + { + const SMDS_MeshNode* nCorner = side.getCornerNode(isXMax,isYMax ); + corners.push_back( nCorner ); + cornerFaces.insert( side.getCornerFace( nCorner )); + } + for ( int e = 0; e < int(NB_QUAD_SIDES); ++e ) + _edge2sides[ side.getEdge( (EQuadEdge) e ) ].insert( &side ); + + nbFacesOnSides += side.getNbFaces(); + } + } + ++corner; + + // find block sides of other domains if any + if ( corner == corners.end() && nbFacesOnSides < mesh.NbQuadrangles() ) + { + while ( nIt->more() ) + { + nCorner = nIt->next(); + if ( isCornerNode( nCorner )) + corner = corners.insert( corner, nCorner ); + } + nbFacesOnSides = mesh.NbQuadrangles(); + } + } + + if ( _allSides.empty() ) + return BAD_MESH_ERR; + if ( _allSides.back()._grid.empty() ) + _allSides.pop_back(); + _DUMP_("Nb detected sides "<< _allSides.size()); + + // --------------------------- + // Organize sides into blocks + // --------------------------- + + // analyse sharing of sides by blocks and sort sides by nb of adjacent sides + int nbBlockSides = 0; // total nb of block sides taking into account their sharing + multimap sortedSides; + { + list < _BlockSide >::iterator sideIt = _allSides.begin(); + for ( ; sideIt != _allSides.end(); ++sideIt ) + { + _BlockSide& side = *sideIt; + bool isSharedSide = true; + int nbAdjacent = 0; + for ( int e = 0; e < int(NB_QUAD_SIDES) && isSharedSide; ++e ) + { + int nbAdj = _edge2sides[ side.getEdge( (EQuadEdge) e ) ].size(); + nbAdjacent += nbAdj; + isSharedSide = ( nbAdj > 2 ); + } + side._nbBlocksFound = 0; + side._nbBlocksExpected = isSharedSide ? 2 : 1; + nbBlockSides += side._nbBlocksExpected; + sortedSides.insert( make_pair( nbAdjacent, & side )); + } + } + + // find sides of each block + int nbBlocks = 0; + while ( nbBlockSides >= 6 ) + { + // get any side not bound to all blocks it belongs to + multimap::iterator i_side = sortedSides.begin(); + while ( i_side != sortedSides.end() && i_side->second->isBound()) + ++i_side; + + // start searching for block sides from the got side + bool ok = true; + if ( _blocks.empty() || _blocks.back()._side[B_FRONT] ) + _blocks.resize( _blocks.size() + 1 ); + + _Block& block = _blocks.back(); + block.setSide( B_FRONT, i_side->second ); + setSideBoundToBlock( *i_side->second ); + nbBlockSides--; + + // edges of adjacent sides of B_FRONT corresponding to front's edges + EQuadEdge edgeOfFront[4] = { Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT }; + EQuadEdge edgeOfAdj [4] = { Q_BOTTOM, Q_LEFT, Q_BOTTOM, Q_LEFT }; + // first find all sides detectable w/o advanced analysis, + // then repeat the search, which then may pass without advanced analysis + set< _BlockSide* > sidesAround; + for ( int advAnalys = 0; advAnalys < 2; ++advAnalys ) + { + // try to find 4 sides adjacent to a FRONT side + for ( int i = 0; (ok || !advAnalys) && i < NB_QUAD_SIDES; ++i ) + if ( !block._side[i] ) + ok = block.setSide( i, findBlockSide( B_FRONT, edgeOfFront[i], edgeOfAdj[i], + advAnalys, sidesAround)); + // try to find a BACK side by a TOP one + if ( ok || !advAnalys) + if ( !block._side[B_BACK] && block._side[B_TOP] ) + ok = block.setSide( B_BACK, findBlockSide( B_TOP, Q_TOP, Q_TOP, + advAnalys, sidesAround )); + if ( !advAnalys ) ok = true; + } + ok = block.isValid(); + if ( ok ) + { + // check if just found block is same as one of previously found blocks + bool isSame = false; + for ( int i = 1; i < _blocks.size() && !isSame; ++i ) + isSame = ( block._corners == _blocks[i-1]._corners ); + ok = !isSame; + } + + // count the found sides + _DUMP_(endl << "** Block " << _blocks.size() << " valid: " << block.isValid()); + for (int i = 0; i < NB_BLOCK_SIDES; ++i ) + { + _DUMP_("\tSide "<< SBoxSides[i] <<" "<< block._side[ i ]._side); + if ( block._side[ i ] ) + { + if ( ok && i != B_FRONT) + { + setSideBoundToBlock( *block._side[ i ]._side ); + nbBlockSides--; + } + _DUMP_("\t corners "<< + block._side[ i ].cornerNode(0,0)->GetID() << ", " << + block._side[ i ].cornerNode(1,0)->GetID() << ", " << + block._side[ i ].cornerNode(1,1)->GetID() << ", " << + block._side[ i ].cornerNode(0,1)->GetID() << ", "<GetNodeIndex( nCorner ); + const SMDS_MeshNode* nOnEdge = firstQuad->GetNode( (iCorner+1) % 4); + + // find out size of block side + vector horRow1, horRow2, verRow1, verRow2; + if ( !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, horRow1, horRow2, true ) || + !fillRowsUntilCorner( firstQuad, nCorner, nOnEdge, verRow1, verRow2, false )) + return false; + nbX = horRow1.size(), nbY = verRow1.size(); + + // store found nodes + side._index._xSize = horRow1.size(); + side._index._ySize = verRow1.size(); + side._grid.resize( side._index.size(), NULL ); + + for ( x = 0; x < horRow1.size(); ++x ) + { + side.setNode( x, 0, horRow1[x] ); + side.setNode( x, 1, horRow2[x] ); + } + for ( y = 0; y < verRow1.size(); ++y ) + { + side.setNode( 0, y, verRow1[y] ); + side.setNode( 1, y, verRow2[y] ); + } + } + // Find the rest nodes + + y = 1; // y of the row to fill + TIDSortedElemSet emptySet, avoidSet; + while ( ++y < nbY ) + { + // get next firstQuad in the next row of quadrangles + // + // n2up + // o---o <- y row + // | | + // o---o o o o o <- found nodes + //n1down n2down + // + int i1down, i2down, i2up; + const SMDS_MeshNode* n1down = side.getNode( 0, y-1 ); + const SMDS_MeshNode* n2down = side.getNode( 1, y-1 ); + avoidSet.clear(); avoidSet.insert( firstQuad ); + firstQuad = SMESH_MeshEditor::FindFaceInSet( n1down, n2down, emptySet, avoidSet, + &i1down, &i2down); + if ( !isQuadrangle( firstQuad )) + return BAD_MESH_ERR; + + const SMDS_MeshNode* n2up = oppositeNode( firstQuad, i1down ); + avoidSet.clear(); avoidSet.insert( firstQuad ); + + // find the rest nodes in the y-th row by faces in the row + + x = 1; + while ( ++x < nbX ) + { + const SMDS_MeshElement* quad = SMESH_MeshEditor::FindFaceInSet( n2up, n2down, emptySet, + avoidSet, &i2up, &i2down); + if ( !isQuadrangle( quad )) + return BAD_MESH_ERR; + + n2up = oppositeNode( quad, i2down ); + n2down = oppositeNode( quad, i2up ); + avoidSet.clear(); avoidSet.insert( quad ); + + side.setNode( x, y, n2up ); + } + } + + // check side validity + bool ok = + side.getCornerFace( side.getCornerNode( 0, 0 )) && + side.getCornerFace( side.getCornerNode( 1, 0 )) && + side.getCornerFace( side.getCornerNode( 0, 1 )) && + side.getCornerFace( side.getCornerNode( 1, 1 )); + + return ok; + } + + //================================================================================ + /*! + * \brief Return true if it's possible to make a loop over corner2Sides starting + * from the startSide + */ + //================================================================================ + + bool isClosedChainOfSides( _BlockSide* startSide, + map< const SMDS_MeshNode*, list< _BlockSide* > > & corner2Sides ) + { + // get start and end nodes + const SMDS_MeshNode *n1 = 0, *n2 = 0, *n; + for ( int y = 0; y < 2; ++y ) + for ( int x = 0; x < 2; ++x ) + { + n = startSide->getCornerNode(x,y); + if ( !corner2Sides.count( n )) continue; + if ( n1 ) + n2 = n; + else + n1 = n; + } + if ( !n2 ) return false; + + map< const SMDS_MeshNode*, list< _BlockSide* > >::iterator + c2sides = corner2Sides.find( n1 ); + if ( c2sides == corner2Sides.end() ) return false; + + int nbChainLinks = 1; + n = n1; + _BlockSide* prevSide = startSide; + while ( n != n2 ) + { + // get the next side sharing n + list< _BlockSide* > & sides = c2sides->second; + _BlockSide* nextSide = ( sides.back() == prevSide ? sides.front() : sides.back() ); + if ( nextSide == prevSide ) return false; + + // find the next corner of the nextSide being in corner2Sides + n1 = n; + n = 0; + for ( int y = 0; y < 2 && !n; ++y ) + for ( int x = 0; x < 2; ++x ) + { + n = nextSide->getCornerNode(x,y); + c2sides = corner2Sides.find( n ); + if ( n == n1 || c2sides == corner2Sides.end() ) + n = 0; + else + break; + } + if ( !n ) return false; + + prevSide = nextSide; + nbChainLinks++; + } + + return ( n == n2 && nbChainLinks == NB_QUAD_SIDES ); + } + + //================================================================================ + /*! + * \brief Try to find a block side adjacent to the given side by given edge + */ + //================================================================================ + + _OrientedBlockSide _Skin::findBlockSide( EBoxSides startBlockSide, + EQuadEdge sharedSideEdge1, + EQuadEdge sharedSideEdge2, + bool withGeometricAnalysis, + set< _BlockSide* >& sidesAround) + { + _Block& block = _blocks.back(); + _OrientedBlockSide& side1 = block._side[ startBlockSide ]; + + // get corner nodes of the given block edge + SMESH_OrientedLink edge = side1.edge( sharedSideEdge1 ); + const SMDS_MeshNode* n1 = edge.node1(); + const SMDS_MeshNode* n2 = edge.node2(); + if ( edge._reversed ) swap( n1, n2 ); + + // find all sides sharing both nodes n1 and n2 + set< _BlockSide* > sidesOnEdge = _edge2sides[ edge ]; // copy a set + + // exclude loaded sides of block from sidesOnEdge + for (int i = 0; i < NB_BLOCK_SIDES; ++i ) + if ( block._side[ i ] ) + sidesOnEdge.erase( block._side[ i ]._side ); + + int nbSidesOnEdge = sidesOnEdge.size(); + _DUMP_("nbSidesOnEdge "<< nbSidesOnEdge << " " << n1->GetID() << "-" << n2->GetID() ); + if ( nbSidesOnEdge == 0 ) + return 0; + + _BlockSide* foundSide = 0; + if ( nbSidesOnEdge == 1 ) + { + foundSide = *sidesOnEdge.begin(); + } + else + { + set< _BlockSide* >::iterator sideIt = sidesOnEdge.begin(); + int nbLoadedSides = block.nbSides(); + if ( nbLoadedSides > 1 ) + { + // Find the side having more than 2 corners common with already loaded sides + for (; !foundSide && sideIt != sidesOnEdge.end(); ++sideIt ) + { + _BlockSide* sideI = *sideIt; + int nbCommonCorners = + block._corners.count( sideI->getCornerNode(0,0)) + + block._corners.count( sideI->getCornerNode(1,0)) + + block._corners.count( sideI->getCornerNode(0,1)) + + block._corners.count( sideI->getCornerNode(1,1)); + if ( nbCommonCorners > 2 ) + foundSide = sideI; + } + } + + if ( !foundSide ) + { + if ( !withGeometricAnalysis ) + { + sidesAround.insert( sidesOnEdge.begin(), sidesOnEdge.end() ); + return 0; + } + if ( nbLoadedSides == 1 ) + { + // Issue 0021529. There are at least 2 sides by each edge and + // position of block gravity center is undefined. + // Find a side starting from which we can walk around the startBlockSide + + // fill in corner2Sides + map< const SMDS_MeshNode*, list< _BlockSide* > > corner2Sides; + for ( sideIt = sidesAround.begin(); sideIt != sidesAround.end(); ++sideIt ) + { + _BlockSide* sideI = *sideIt; + corner2Sides[ sideI->getCornerNode(0,0) ].push_back( sideI ); + corner2Sides[ sideI->getCornerNode(1,0) ].push_back( sideI ); + corner2Sides[ sideI->getCornerNode(0,1) ].push_back( sideI ); + corner2Sides[ sideI->getCornerNode(1,1) ].push_back( sideI ); + } + // remove corners of startBlockSide from corner2Sides + set::iterator nIt = block._corners.begin(); + for ( ; nIt != block._corners.end(); ++nIt ) + corner2Sides.erase( *nIt ); + + // select a side + for ( sideIt = sidesOnEdge.begin(); sideIt != sidesOnEdge.end(); ++sideIt ) + { + if ( isClosedChainOfSides( *sideIt, corner2Sides )) + { + foundSide = *sideIt; + break; + } + } + if ( !foundSide ) + return 0; + } + else + { + // Select one of found sides most close to startBlockSide + + gp_XYZ p1 ( n1->X(),n1->Y(),n1->Z()), p2 (n2->X(),n2->Y(),n2->Z()); + gp_Vec p1p2( p1, p2 ); + + const SMDS_MeshElement* face1 = side1->getCornerFace( n1 ); + gp_XYZ p1Op = SMESH_TNodeXYZ( oppositeNode( face1, face1->GetNodeIndex(n1))); + gp_Vec side1Dir( p1, p1Op ); + gp_Ax2 pln( p1, p1p2, side1Dir ); // plane with normal p1p2 and X dir side1Dir + _DUMP_(" Select adjacent for "<< side1._side << " - side dir (" + << side1Dir.X() << ", " << side1Dir.Y() << ", " << side1Dir.Z() << ")" ); + + map < double , _BlockSide* > angleOfSide; + for (sideIt = sidesOnEdge.begin(); sideIt != sidesOnEdge.end(); ++sideIt ) + { + _BlockSide* sideI = *sideIt; + const SMDS_MeshElement* faceI = sideI->getCornerFace( n1 ); + gp_XYZ p1Op = SMESH_TNodeXYZ( oppositeNode( faceI, faceI->GetNodeIndex(n1))); + gp_Vec sideIDir( p1, p1Op ); + // compute angle of (sideIDir projection to pln) and (X dir of pln) + gp_Vec2d sideIDirProj( sideIDir * pln.XDirection(), sideIDir * pln.YDirection()); + double angle = sideIDirProj.Angle( gp::DX2d() ); + if ( angle < 0 ) angle += 2. * M_PI; // angle [0-2*PI] + angleOfSide.insert( make_pair( angle, sideI )); + _DUMP_(" "<< sideI << " - side dir (" + << sideIDir.X() << ", " << sideIDir.Y() << ", " << sideIDir.Z() << ")" + << " angle " << angle); + } + + gp_XYZ gc(0,0,0); // gravity center of already loaded block sides + for (int i = 0; i < NB_BLOCK_SIDES; ++i ) + if ( block._side[ i ] ) + gc += block._side[ i ]._side->getGC(); + gc /= nbLoadedSides; + + gp_Vec gcDir( p1, gc ); + gp_Vec2d gcDirProj( gcDir * pln.XDirection(), gcDir * pln.YDirection()); + double gcAngle = gcDirProj.Angle( gp::DX2d() ); + foundSide = gcAngle < 0 ? angleOfSide.rbegin()->second : angleOfSide.begin()->second; + } + } + _DUMP_(" selected "<< foundSide ); + } + + // Orient the found side correctly + + // corners of found side corresponding to nodes n1 and n2 + bool xMax1, yMax1, xMax2, yMax2; + if ( !getEdgeEnds( sharedSideEdge2, xMax1, yMax1, xMax2, yMax2 )) + return error(SMESH_Comment("Internal error at ")<<__FILE__<<":"<<__LINE__), + _OrientedBlockSide(0); + + for ( int ori = 0; ori < _OrientedIndexer::MAX_ORI+1; ++ori ) + { + _OrientedBlockSide orientedSide( foundSide, ori ); + const SMDS_MeshNode* n12 = orientedSide.cornerNode( xMax1, yMax1); + const SMDS_MeshNode* n22 = orientedSide.cornerNode( xMax2, yMax2); + if ( n1 == n12 && n2 == n22 ) + return orientedSide; + } + error(SMESH_Comment("Failed to orient a block side found by edge ")<& row1, + vector& row2, + const bool alongN1N2 ) + { + const SMDS_MeshNode* corner1 = n1; + + // Store nodes of quad in the rows and find new n1 and n2 to get + // the next face so that new n2 is on block edge + int i1 = quad->GetNodeIndex( n1 ); + int i2 = quad->GetNodeIndex( n2 ); + row1.clear(); row2.clear(); + row1.push_back( n1 ); + if ( alongN1N2 ) + { + row1.push_back( n2 ); + row2.push_back( oppositeNode( quad, i2 )); + row2.push_back( n1 = oppositeNode( quad, i1 )); + } + else + { + row2.push_back( n2 ); + row1.push_back( n2 = oppositeNode( quad, i2 )); + row2.push_back( n1 = oppositeNode( quad, i1 )); + } + + if ( isCornerNode( row1[1] )) + return true; + + // Find the rest nodes + TIDSortedElemSet emptySet, avoidSet; + while ( !isCornerNode( n2 ) ) + { + avoidSet.clear(); avoidSet.insert( quad ); + quad = SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, avoidSet, &i1, &i2 ); + if ( !isQuadrangle( quad )) + return BAD_MESH_ERR; + + row1.push_back( n2 = oppositeNode( quad, i1 )); + row2.push_back( n1 = oppositeNode( quad, i2 )); + } + return n1 != corner1; + } + + //================================================================================ + /*! + * \brief Return a corner face by a corner node + */ + //================================================================================ + + const SMDS_MeshElement* _BlockSide::getCornerFace(const SMDS_MeshNode* cornerNode) const + { + int x, y, isXMax, isYMax, found = 0; + for ( isXMax = 0; isXMax < 2; ++isXMax ) + { + for ( isYMax = 0; isYMax < 2; ++isYMax ) + { + x = isXMax ? _index._xSize-1 : 0; + y = isYMax ? _index._ySize-1 : 0; + found = ( getNode(x,y) == cornerNode ); + if ( found ) break; + } + if ( found ) break; + } + if ( !found ) return 0; + int dx = isXMax ? -1 : +1; + int dy = isYMax ? -1 : +1; + const SMDS_MeshNode* n1 = getNode(x,y); + const SMDS_MeshNode* n2 = getNode(x+dx,y); + const SMDS_MeshNode* n3 = getNode(x,y+dy); + const SMDS_MeshNode* n4 = getNode(x+dx,y+dy); + return SMDS_Mesh::FindFace(n1, n2, n3, n4 ); + } + + //================================================================================ + /*! + * \brief Checks own validity + */ + //================================================================================ + + bool _Block::isValid() const + { + bool ok = ( nbSides() == 6 ); + + // check only corners depending on side selection + EBoxSides adjacent[4] = { B_BOTTOM, B_RIGHT, B_TOP, B_LEFT }; + EQuadEdge edgeAdj [4] = { Q_TOP, Q_RIGHT, Q_TOP, Q_RIGHT }; + EQuadEdge edgeBack[4] = { Q_BOTTOM, Q_RIGHT, Q_TOP, Q_LEFT }; + + for ( int i=0; ok && i < NB_QUAD_SIDES; ++i ) + { + SMESH_OrientedLink eBack = _side[ B_BACK ].edge( edgeBack[i] ); + SMESH_OrientedLink eAdja = _side[ adjacent[i] ].edge( edgeAdj[i] ); + ok = ( eBack == eAdja ); + } + return ok; + } + +} // namespace + +//======================================================================= +//function : StdMeshers_HexaFromSkin_3D +//purpose : +//======================================================================= + +StdMeshers_HexaFromSkin_3D::StdMeshers_HexaFromSkin_3D(int hypId, int studyId, SMESH_Gen* gen) + :SMESH_3D_Algo(hypId, studyId, gen) +{ + MESSAGE("StdMeshers_HexaFromSkin_3D::StdMeshers_HexaFromSkin_3D"); + _name = "HexaFromSkin_3D"; +} + +StdMeshers_HexaFromSkin_3D::~StdMeshers_HexaFromSkin_3D() +{ + MESSAGE("StdMeshers_HexaFromSkin_3D::~StdMeshers_HexaFromSkin_3D"); +} + +//================================================================================ +/*! + * \brief Main method, which generates hexaheda + */ +//================================================================================ + +bool StdMeshers_HexaFromSkin_3D::Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper) +{ + _Skin skin; + int nbBlocks = skin.findBlocks(aMesh); + if ( nbBlocks == 0 ) + return error( skin.error()); + + vector< vector< const SMDS_MeshNode* > > columns; + int x, xSize, y, ySize, z, zSize; + _Indexer colIndex; + + for ( int i = 0; i < nbBlocks; ++i ) + { + const _Block& block = skin.getBlock( i ); + + // ------------------------------------------ + // Fill columns of nodes with existing nodes + // ------------------------------------------ + + xSize = block.getSide(B_BOTTOM).getHoriSize(); + ySize = block.getSide(B_BOTTOM).getVertSize(); + zSize = block.getSide(B_FRONT ).getVertSize(); + int X = xSize - 1, Y = ySize - 1, Z = zSize - 1; + colIndex = _Indexer( xSize, ySize ); + columns.resize( colIndex.size() ); + + // fill node columns by front and back box sides + for ( x = 0; x < xSize; ++x ) { + vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( x, 0 )]; + vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( x, Y )]; + column0.resize( zSize ); + column1.resize( zSize ); + for ( z = 0; z < zSize; ++z ) { + column0[ z ] = block.getSide(B_FRONT).node( x, z ); + column1[ z ] = block.getSide(B_BACK) .node( x, z ); + } + } + // fill node columns by left and right box sides + for ( y = 1; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( 0, y )]; + vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( X, y )]; + column0.resize( zSize ); + column1.resize( zSize ); + for ( z = 0; z < zSize; ++z ) { + column0[ z ] = block.getSide(B_LEFT) .node( y, z ); + column1[ z ] = block.getSide(B_RIGHT).node( y, z ); + } + } + // get nodes from top and bottom box sides + for ( x = 1; x < xSize-1; ++x ) { + for ( y = 1; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )]; + column.resize( zSize ); + column.front() = block.getSide(B_BOTTOM).node( x, y ); + column.back() = block.getSide(B_TOP) .node( x, y ); + } + } + + // ---------------------------- + // Add internal nodes of a box + // ---------------------------- + // projection points of internal nodes on box sub-shapes by which + // coordinates of internal nodes are computed + vector pointOnShape( SMESH_Block::ID_Shell ); + + // projections on vertices are constant + pointOnShape[ SMESH_Block::ID_V000 ] = block.getSide(B_BOTTOM).xyz( 0, 0 ); + pointOnShape[ SMESH_Block::ID_V100 ] = block.getSide(B_BOTTOM).xyz( X, 0 ); + pointOnShape[ SMESH_Block::ID_V010 ] = block.getSide(B_BOTTOM).xyz( 0, Y ); + pointOnShape[ SMESH_Block::ID_V110 ] = block.getSide(B_BOTTOM).xyz( X, Y ); + pointOnShape[ SMESH_Block::ID_V001 ] = block.getSide(B_TOP).xyz( 0, 0 ); + pointOnShape[ SMESH_Block::ID_V101 ] = block.getSide(B_TOP).xyz( X, 0 ); + pointOnShape[ SMESH_Block::ID_V011 ] = block.getSide(B_TOP).xyz( 0, Y ); + pointOnShape[ SMESH_Block::ID_V111 ] = block.getSide(B_TOP).xyz( X, Y ); + + for ( x = 1; x < xSize-1; ++x ) + { + gp_XYZ params; // normalized parameters of internal node within a unit box + params.SetCoord( 1, x / double(X) ); + for ( y = 1; y < ySize-1; ++y ) + { + params.SetCoord( 2, y / double(Y) ); + // column to fill during z loop + vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )]; + // projections on horizontal edges + pointOnShape[ SMESH_Block::ID_Ex00 ] = block.getSide(B_BOTTOM).xyz( x, 0 ); + pointOnShape[ SMESH_Block::ID_Ex10 ] = block.getSide(B_BOTTOM).xyz( x, Y ); + pointOnShape[ SMESH_Block::ID_E0y0 ] = block.getSide(B_BOTTOM).xyz( 0, y ); + pointOnShape[ SMESH_Block::ID_E1y0 ] = block.getSide(B_BOTTOM).xyz( X, y ); + pointOnShape[ SMESH_Block::ID_Ex01 ] = block.getSide(B_TOP).xyz( x, 0 ); + pointOnShape[ SMESH_Block::ID_Ex11 ] = block.getSide(B_TOP).xyz( x, Y ); + pointOnShape[ SMESH_Block::ID_E0y1 ] = block.getSide(B_TOP).xyz( 0, y ); + pointOnShape[ SMESH_Block::ID_E1y1 ] = block.getSide(B_TOP).xyz( X, y ); + // projections on horizontal sides + pointOnShape[ SMESH_Block::ID_Fxy0 ] = block.getSide(B_BOTTOM).xyz( x, y ); + pointOnShape[ SMESH_Block::ID_Fxy1 ] = block.getSide(B_TOP) .xyz( x, y ); + for ( z = 1; z < zSize-1; ++z ) // z loop + { + params.SetCoord( 3, z / double(Z) ); + // projections on vertical edges + pointOnShape[ SMESH_Block::ID_E00z ] = block.getSide(B_FRONT).xyz( 0, z ); + pointOnShape[ SMESH_Block::ID_E10z ] = block.getSide(B_FRONT).xyz( X, z ); + pointOnShape[ SMESH_Block::ID_E01z ] = block.getSide(B_BACK).xyz( 0, z ); + pointOnShape[ SMESH_Block::ID_E11z ] = block.getSide(B_BACK).xyz( X, z ); + // projections on vertical sides + pointOnShape[ SMESH_Block::ID_Fx0z ] = block.getSide(B_FRONT).xyz( x, z ); + pointOnShape[ SMESH_Block::ID_Fx1z ] = block.getSide(B_BACK) .xyz( x, z ); + pointOnShape[ SMESH_Block::ID_F0yz ] = block.getSide(B_LEFT) .xyz( y, z ); + pointOnShape[ SMESH_Block::ID_F1yz ] = block.getSide(B_RIGHT).xyz( y, z ); + + // compute internal node coordinates + gp_XYZ coords; + SMESH_Block::ShellPoint( params, pointOnShape, coords ); + column[ z ] = aHelper->AddNode( coords.X(), coords.Y(), coords.Z() ); + +#ifdef DEB_GRID + // debug + //cout << "----------------------------------------------------------------------"<::max(); + bool isForw = true; + for ( int xMax = 0; xMax < 2; ++xMax ) + for ( int yMax = 0; yMax < 2; ++yMax ) + for ( int zMax = 0; zMax < 2; ++zMax ) + { + x = xMax ? xSize-1 : 1; + y = yMax ? ySize-1 : 1; + z = zMax ? zSize-1 : 1; + vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x-1, y-1 )]; + vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x , y-1 )]; + vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x-1, y )]; + vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x , y )]; + + const SMDS_MeshNode* n000 = col00[z-1]; + const SMDS_MeshNode* n100 = col10[z-1]; + const SMDS_MeshNode* n010 = col01[z-1]; + const SMDS_MeshNode* n110 = col11[z-1]; + const SMDS_MeshNode* n001 = col00[z]; + const SMDS_MeshNode* n101 = col10[z]; + const SMDS_MeshNode* n011 = col01[z]; + const SMDS_MeshNode* n111 = col11[z]; + SMDS_VolumeOfNodes probeVolume (n000,n010,n110,n100, + n001,n011,n111,n101); + SMDS_VolumeTool volTool( &probeVolume ); + double Nx=0.,Ny=0.,Nz=0.; + for ( int iFace = 0; iFace < volTool.NbFaces(); ++iFace ) + { + double nx,ny,nz; + volTool.GetFaceNormal( iFace, nx,ny,nz ); + Nx += nx; + Ny += ny; + Nz += nz; + } + double quality = Nx*Nx + Ny*Ny + Nz*Nz; + if ( quality < badness ) + { + badness = quality; + isForw = volTool.IsForward(); + } + } + + // add elements + for ( x = 0; x < xSize-1; ++x ) { + for ( y = 0; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )]; + vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )]; + vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x, y+1 )]; + vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x+1, y+1 )]; + // bottom face normal of a hexa mush point outside the volume + if ( isForw ) + for ( z = 0; z < zSize-1; ++z ) + aHelper->AddVolume(col00[z], col01[z], col11[z], col10[z], + col00[z+1], col01[z+1], col11[z+1], col10[z+1]); + else + for ( z = 0; z < zSize-1; ++z ) + aHelper->AddVolume(col00[z], col10[z], col11[z], col01[z], + col00[z+1], col10[z+1], col11[z+1], col01[z+1]); + } + } + } // loop on blocks + + return true; +} + +//================================================================================ +/*! + * \brief Evaluate nb of hexa + */ +//================================================================================ + +bool StdMeshers_HexaFromSkin_3D::Evaluate(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap) +{ + _Skin skin; + int nbBlocks = skin.findBlocks(aMesh); + if ( nbBlocks == 0 ) + return error( skin.error()); + + bool secondOrder = aMesh.NbFaces( ORDER_QUADRATIC ); + + int entity = secondOrder ? SMDSEntity_Quad_Hexa : SMDSEntity_Hexa; + vector& nbByType = aResMap[ aMesh.GetSubMesh( aShape )]; + if ( entity >= nbByType.size() ) + nbByType.resize( SMDSEntity_Last, 0 ); + + for ( int i = 0; i < nbBlocks; ++i ) + { + const _Block& block = skin.getBlock( i ); + + int nbX = block.getSide(B_BOTTOM).getHoriSize(); + int nbY = block.getSide(B_BOTTOM).getVertSize(); + int nbZ = block.getSide(B_FRONT ).getVertSize(); + + int nbHexa = (nbX-1) * (nbY-1) * (nbZ-1); + int nbNodes = (nbX-2) * (nbY-2) * (nbZ-2); + if ( secondOrder ) + nbNodes += + (nbX-2) * (nbY-2) * (nbZ-1) + + (nbX-2) * (nbY-1) * (nbZ-2) + + (nbX-1) * (nbY-2) * (nbZ-2); + + + nbByType[ entity ] += nbHexa; + nbByType[ SMDSEntity_Node ] += nbNodes; + } + + return true; +} + +//================================================================================ +/*! + * \brief Abstract method must be defined but does nothing + */ +//================================================================================ + +bool StdMeshers_HexaFromSkin_3D::CheckHypothesis(SMESH_Mesh&, const TopoDS_Shape&, + Hypothesis_Status& aStatus) +{ + aStatus = SMESH_Hypothesis::HYP_OK; + return true; +} + +//================================================================================ +/*! + * \brief Abstract method must be defined but just reports an error as this + * algo is not intended to work with shapes + */ +//================================================================================ + +bool StdMeshers_HexaFromSkin_3D::Compute(SMESH_Mesh&, const TopoDS_Shape&) +{ + return error("Algorithm can't work with geometrical shapes"); +} diff --git a/src/StdMeshers/StdMeshers_HexaFromSkin_3D.hxx b/src/StdMeshers/StdMeshers_HexaFromSkin_3D.hxx new file mode 100644 index 000000000..0912a4d03 --- /dev/null +++ b/src/StdMeshers/StdMeshers_HexaFromSkin_3D.hxx @@ -0,0 +1,54 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_HexaFromSkin_3D.hxx +// Created : Wed Jan 27 12:23:21 2010 +// Author : Edward AGAPOV (eap) +// +#ifndef __StdMeshers_HexaFromSkin_3D_HXX__ +#define __StdMeshers_HexaFromSkin_3D_HXX__ + +#include "SMESH_StdMeshers.hxx" +#include "SMESH_3D_Algo.hxx" + +/*! + * \brief Alorithm generating hexahedral mesh from 2D skin of block + */ + +class STDMESHERS_EXPORT StdMeshers_HexaFromSkin_3D : public SMESH_3D_Algo +{ +public: + StdMeshers_HexaFromSkin_3D(int hypId, int studyId, SMESH_Gen* gen); + virtual ~StdMeshers_HexaFromSkin_3D(); + + virtual bool Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + Hypothesis_Status& aStatus); + + virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.cxx b/src/StdMeshers/StdMeshers_Hexa_3D.cxx index b36acde9a..92f00af3a 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.cxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Hexa_3D.cxx // Moved here from SMESH_Hexa_3D.cxx @@ -26,30 +27,28 @@ // Module : SMESH // #include "StdMeshers_Hexa_3D.hxx" + #include "StdMeshers_CompositeHexa_3D.hxx" #include "StdMeshers_FaceSide.hxx" +#include "StdMeshers_HexaFromSkin_3D.hxx" #include "StdMeshers_Penta_3D.hxx" #include "StdMeshers_Prism_3D.hxx" #include "StdMeshers_Quadrangle_2D.hxx" +#include "StdMeshers_ViscousLayers.hxx" +#include "SMESH_Comment.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" -#include "SMESH_Comment.hxx" -#include "SMDS_MeshElement.hxx" #include "SMDS_MeshNode.hxx" -#include "SMDS_FacePosition.hxx" -#include "SMDS_VolumeTool.hxx" -#include "SMDS_VolumeOfNodes.hxx" #include #include -#include -#include -#include +#include +#include #include -#include #include "utilities.h" #include "Utils_ExceptHandlers.hxx" @@ -58,11 +57,16 @@ typedef SMESH_Comment TComm; using namespace std; -static SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh &, const TopoDS_Shape &); +static SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh &, + const TopoDS_Shape &, + SMESH_ProxyMesh* proxyMesh=0); + +static bool EvaluatePentahedralMesh(SMESH_Mesh &, const TopoDS_Shape &, + MapShapeNbElems &); //============================================================================= /*! - * + * Constructor */ //============================================================================= @@ -71,12 +75,14 @@ StdMeshers_Hexa_3D::StdMeshers_Hexa_3D(int hypId, int studyId, SMESH_Gen * gen) { MESSAGE("StdMeshers_Hexa_3D::StdMeshers_Hexa_3D"); _name = "Hexa_3D"; - _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type + _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit /shape type + _requireShape = false; + _compatibleHypothesis.push_back("ViscousLayers"); } //============================================================================= /*! - * + * Destructor */ //============================================================================= @@ -85,27 +91,9 @@ StdMeshers_Hexa_3D::~StdMeshers_Hexa_3D() MESSAGE("StdMeshers_Hexa_3D::~StdMeshers_Hexa_3D"); } -//================================================================================ -/*! - * \brief Clear fields and return the argument - * \param res - the value to return - * \retval bool - the argument value - */ -//================================================================================ - -bool StdMeshers_Hexa_3D::ClearAndReturn(FaceQuadStruct* theQuads[6], const bool res) -{ - for (int i = 0; i < 6; i++) { - delete theQuads[i]; - theQuads[i] = NULL; - } - return res; -} - - //============================================================================= /*! - * + * Retrieves defined hypotheses */ //============================================================================= @@ -124,82 +112,181 @@ bool StdMeshers_Hexa_3D::CheckHypothesis if ( nbFaces != 6 ) return false; */ - aStatus = SMESH_Hypothesis::HYP_OK; - return true; -} -//======================================================================= -//function : isCloser -//purpose : -//======================================================================= + _viscousLayersHyp = NULL; -inline bool isCloser(const int i, const int j, const int nbhoriz, - const FaceQuadStruct* quad, const gp_Pnt2d uv, - double & minDist) -{ - int ij = j * nbhoriz + i; - gp_Pnt2d uv2( quad->uv_grid[ij].u, quad->uv_grid[ij].v ); - double dist = uv.SquareDistance( uv2 ); - if ( dist < minDist ) { - minDist = dist; + const list& hyps = + GetUsedHypothesis(aMesh, aShape, /*ignoreAuxiliary=*/false); + list ::const_iterator h = hyps.begin(); + if ( h == hyps.end()) + { + aStatus = SMESH_Hypothesis::HYP_OK; return true; } - return false; -} -//======================================================================= -//function : findIJ -//purpose : return i,j of the node -//======================================================================= + aStatus = HYP_OK; + for ( ; h != hyps.end(); ++h ) + { + string hypName = (*h)->GetName(); + if ( find( _compatibleHypothesis.begin(),_compatibleHypothesis.end(),hypName ) + != _compatibleHypothesis.end() ) + { + _viscousLayersHyp = dynamic_cast< const StdMeshers_ViscousLayers*> ( *h ); + } + else + { + aStatus = HYP_INCOMPATIBLE; + } + } -static bool findIJ (const SMDS_MeshNode* node, const FaceQuadStruct * quad, int& I, int& J) + if ( !_viscousLayersHyp ) + aStatus = HYP_INCOMPATIBLE; + + return aStatus == HYP_OK; +} + +namespace { - const SMDS_FacePosition* fpos = - static_cast(node->GetPosition().get()); - if ( ! fpos ) return false; - gp_Pnt2d uv( fpos->GetUParameter(), fpos->GetVParameter() ); - - double minDist = DBL_MAX; - const int nbhoriz = quad->side[0]->NbPoints(); - const int nbvertic = quad->side[1]->NbPoints(); - I = nbhoriz/2; J = nbvertic/2; - int oldI, oldJ; - do { - oldI = I; oldJ = J; - while ( I + 2 < nbhoriz && isCloser( I + 1, J, nbhoriz, quad, uv, minDist )) - I += 1; - if ( I == oldI ) - while ( I - 1 > 0 && isCloser( I - 1, J, nbhoriz, quad, uv, minDist )) - I -= 1; - if ( minDist < DBL_MIN ) - break; + //============================================================================= - while ( J + 2 < nbvertic && isCloser( I, J + 1, nbhoriz, quad, uv, minDist )) - J += 1; - if ( J == oldJ ) - while ( J - 1 > 0 && isCloser( I, J - 1, nbhoriz, quad, uv, minDist )) - J -= 1; - if ( minDist < DBL_MIN ) - break; + typedef boost::shared_ptr< FaceQuadStruct > FaceQuadStructPtr; + + // symbolic names of box sides + enum EBoxSides{ B_BOTTOM=0, B_RIGHT, B_TOP, B_LEFT, B_FRONT, B_BACK, B_NB_SIDES }; + + // symbolic names of sides of quadrangle + enum EQuadSides{ Q_BOTTOM=0, Q_RIGHT, Q_TOP, Q_LEFT, Q_NB_SIDES }; + + //============================================================================= + /*! + * \brief Container of nodes of structured mesh on a qudrangular geom FACE + */ + struct _FaceGrid + { + // face sides + FaceQuadStructPtr _quad; + + // map of (node parameter on EDGE) to (column (vector) of nodes) + TParam2ColumnMap _u2nodesMap; + + // node column's taken form _u2nodesMap taking into account sub-shape orientation + vector _columns; + + // geometry of a cube side + TopoDS_Face _sideF; + + const SMDS_MeshNode* GetNode(int iCol, int iRow) const + { + return _columns[iCol][iRow]; + } + gp_XYZ GetXYZ(int iCol, int iRow) const + { + return SMESH_TNodeXYZ( GetNode( iCol, iRow )); + } + }; - } while ( I != oldI || J != oldJ ); + //================================================================================ + /*! + * \brief Convertor of a pair of integers to a sole index + */ + struct _Indexer + { + int _xSize, _ySize; + _Indexer( int xSize, int ySize ): _xSize(xSize), _ySize(ySize) {} + int size() const { return _xSize * _ySize; } + int operator()(const int x, const int y) const { return y * _xSize + x; } + }; + + //================================================================================ + /*! + * \brief Appends a range of node columns from a map to another map + */ + template< class TMapIterator > + void append( TParam2ColumnMap& toMap, TMapIterator from, TMapIterator to ) + { + const SMDS_MeshNode* lastNode = toMap.rbegin()->second[0]; + const SMDS_MeshNode* firstNode = from->second[0]; + if ( lastNode == firstNode ) + from++; + double u = toMap.rbegin()->first; + for (; from != to; ++from ) + { + u += 1; + TParam2ColumnMap::iterator u2nn = toMap.insert( toMap.end(), make_pair ( u, TNodeColumn())); + u2nn->second.swap( from->second ); + } + } - if ( minDist > DBL_MIN ) { - for (int i = 1; i < nbhoriz - 1; i++) - for (int j = 1; j < nbvertic - 1; j++) - if ( isCloser( i, j, nbhoriz, quad, uv, minDist )) - I = i, J = j; + //================================================================================ + /*! + * \brief Finds FaceQuadStruct having a side equal to a given one and rearranges + * the found FaceQuadStruct::side to have the given side at a Q_BOTTOM place + */ + FaceQuadStructPtr getQuadWithBottom( StdMeshers_FaceSide* side, + FaceQuadStructPtr quad[ 6 ]) + { + FaceQuadStructPtr foundQuad; + for ( int i = 1; i < 6; ++i ) + { + if ( !quad[i] ) continue; + for ( unsigned iS = 0; iS < quad[i]->side.size(); ++iS ) + { + const StdMeshers_FaceSide* side2 = quad[i]->side[iS]; + if (( side->FirstVertex().IsSame( side2->FirstVertex() ) || + side->FirstVertex().IsSame( side2->LastVertex() )) + && + ( side->LastVertex().IsSame( side2->FirstVertex() ) || + side->LastVertex().IsSame( side2->LastVertex() )) + ) + { + if ( iS != Q_BOTTOM ) + { + vector< StdMeshers_FaceSide*> newSides; + for ( unsigned j = iS; j < quad[i]->side.size(); ++j ) + newSides.push_back( quad[i]->side[j] ); + for ( unsigned j = 0; j < iS; ++j ) + newSides.push_back( quad[i]->side[j] ); + quad[i]->side.swap( newSides ); + } + foundQuad.swap(quad[i]); + return foundQuad; + } + } + } + return foundQuad; + } + //================================================================================ + /*! + * \brief Returns true if the 1st base node of sideGrid1 belongs to sideGrid2 + */ + //================================================================================ + + bool beginsAtSide( const _FaceGrid& sideGrid1, + const _FaceGrid& sideGrid2, + SMESH_ProxyMesh::Ptr proxymesh ) + { + const TNodeColumn& col0 = sideGrid2._u2nodesMap.begin()->second; + const TNodeColumn& col1 = sideGrid2._u2nodesMap.rbegin()->second; + const SMDS_MeshNode* n00 = col0.front(); + const SMDS_MeshNode* n01 = col0.back(); + const SMDS_MeshNode* n10 = col1.front(); + const SMDS_MeshNode* n11 = col1.back(); + const SMDS_MeshNode* n = (sideGrid1._u2nodesMap.begin()->second)[0]; + if ( proxymesh ) + { + n00 = proxymesh->GetProxyNode( n00 ); + n10 = proxymesh->GetProxyNode( n10 ); + n01 = proxymesh->GetProxyNode( n01 ); + n11 = proxymesh->GetProxyNode( n11 ); + n = proxymesh->GetProxyNode( n ); + } + return ( n == n00 || n == n01 || n == n10 || n == n11 ); } - return true; } - //============================================================================= /*! - * Hexahedron mesh on hexaedron like form - * -0. - shape and face mesh verification - * -1. - identify faces and vertices of the "cube" - * -2. - Algorithm from: + * Generates hexahedron mesh on hexaedron like form using algorithm from * "Application de l'interpolation transfinie à la création de maillages * C0 ou G1 continus sur des triangles, quadrangles, tetraedres, pentaedres * et hexaedres déformés." @@ -215,823 +302,470 @@ bool StdMeshers_Hexa_3D::Compute(SMESH_Mesh & aMesh, MESSAGE("StdMeshers_Hexa_3D::Compute"); SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); - // 0. - shape and face mesh verification - // 0.1 - shape must be a solid (or a shell) with 6 faces + // Shape verification + // ---------------------- - vector < SMESH_subMesh * >meshFaces; - for (TopExp_Explorer exp(aShape, TopAbs_FACE); exp.More(); exp.Next()) { - SMESH_subMesh *aSubMesh = aMesh.GetSubMeshContaining(exp.Current()); - ASSERT(aSubMesh); - meshFaces.push_back(aSubMesh); - } - if (meshFaces.size() != 6) { - //return error(COMPERR_BAD_SHAPE, TComm(meshFaces.size())<<" instead of 6 faces in a block"); - static StdMeshers_CompositeHexa_3D compositeHexa(-10, 0, aMesh.GetGen()); + // shape must be a solid (or a shell) with 6 faces + TopExp_Explorer exp(aShape,TopAbs_SHELL); + if ( !exp.More() ) + return error(COMPERR_BAD_SHAPE, "No SHELL in the geometry"); + if ( exp.Next(), exp.More() ) + return error(COMPERR_BAD_SHAPE, "More than one SHELL in the geometry"); + + TopTools_IndexedMapOfShape FF; + TopExp::MapShapes( aShape, TopAbs_FACE, FF); + if ( FF.Extent() != 6) + { + static StdMeshers_CompositeHexa_3D compositeHexa(_gen->GetANewId(), 0, _gen); if ( !compositeHexa.Compute( aMesh, aShape )) return error( compositeHexa.GetComputeError() ); return true; } - // 0.2 - is each face meshed with Quadrangle_2D? (so, with a wire of 4 edges) - - // tool for working with quadratic elements - SMESH_MesherHelper aTool (aMesh); - _quadraticMesh = aTool.IsQuadraticSubMesh(aShape); - - // cube structure - typedef struct cubeStruct - { - TopoDS_Vertex V000; - TopoDS_Vertex V001; - TopoDS_Vertex V010; - TopoDS_Vertex V011; - TopoDS_Vertex V100; - TopoDS_Vertex V101; - TopoDS_Vertex V110; - TopoDS_Vertex V111; - faceQuadStruct* quad_X0; - faceQuadStruct* quad_X1; - faceQuadStruct* quad_Y0; - faceQuadStruct* quad_Y1; - faceQuadStruct* quad_Z0; - faceQuadStruct* quad_Z1; - Point3DStruct* np; // normalised 3D coordinates - } CubeStruct; - - CubeStruct aCube; - - // bounding faces - FaceQuadStruct* aQuads[6]; - for (int i = 0; i < 6; i++) - aQuads[i] = 0; - - for (int i = 0; i < 6; i++) + // Find sides of a cube + // --------------------- + + FaceQuadStructPtr quad[ 6 ]; + StdMeshers_Quadrangle_2D quadAlgo( _gen->GetANewId(), GetStudyId(), _gen); + for ( int i = 0; i < 6; ++i ) { - TopoDS_Shape aFace = meshFaces[i]->GetSubShape(); - SMESH_Algo *algo = _gen->GetAlgo(aMesh, aFace); - string algoName = algo->GetName(); - bool isAllQuad = false; - if (algoName == "Quadrangle_2D") { - SMESHDS_SubMesh * sm = meshDS->MeshElements( aFace ); - if ( sm ) { - isAllQuad = true; - SMDS_ElemIteratorPtr eIt = sm->GetElements(); - while ( isAllQuad && eIt->more() ) { - const SMDS_MeshElement* elem = eIt->next(); - isAllQuad = ( elem->NbNodes()==4 ||(_quadraticMesh && elem->NbNodes()==8) ); - } - } - } - if ( ! isAllQuad ) { - SMESH_ComputeErrorPtr err = ComputePentahedralMesh(aMesh, aShape); - return ClearAndReturn( aQuads, error(err)); - } - StdMeshers_Quadrangle_2D *quadAlgo = - dynamic_cast < StdMeshers_Quadrangle_2D * >(algo); - ASSERT(quadAlgo); - try { - aQuads[i] = quadAlgo->CheckAnd2Dcompute(aMesh, aFace, _quadraticMesh); - if(!aQuads[i]) { - return error( quadAlgo->GetComputeError()); - } - } - catch(SALOME_Exception & S_ex) { - return ClearAndReturn( aQuads, error(COMPERR_SLM_EXCEPTION,TComm(S_ex.what()) << - " Raised by StdMeshers_Quadrangle_2D " - " on face #" << meshDS->ShapeToIndex( aFace ))); - } - - // 0.2.1 - number of points on the opposite edges must be the same - if (aQuads[i]->side[0]->NbPoints() != aQuads[i]->side[2]->NbPoints() || - aQuads[i]->side[1]->NbPoints() != aQuads[i]->side[3]->NbPoints() - /*aQuads[i]->side[0]->NbEdges() != 1 || - aQuads[i]->side[1]->NbEdges() != 1 || - aQuads[i]->side[2]->NbEdges() != 1 || - aQuads[i]->side[3]->NbEdges() != 1*/) { - MESSAGE("different number of points on the opposite edges of face " << i); - // Try to go into penta algorithm 'cause it has been improved. - SMESH_ComputeErrorPtr err = ComputePentahedralMesh(aMesh, aShape); - return ClearAndReturn( aQuads, error(err)); - } + if ( !( quad[i] = FaceQuadStructPtr( quadAlgo.CheckNbEdges( aMesh, FF( i+1 ))))) + return error( quadAlgo.GetComputeError() ); + if ( quad[i]->side.size() != 4 ) + return error( COMPERR_BAD_SHAPE, "Not a quadrangular box side" ); } - // 1. - identify faces and vertices of the "cube" - // 1.1 - ancestor maps vertex->edges in the cube - - TopTools_IndexedDataMapOfShapeListOfShape MS; - TopExp::MapShapesAndAncestors(aShape, TopAbs_VERTEX, TopAbs_EDGE, MS); - - // 1.2 - first face is choosen as face Y=0 of the unit cube - - const TopoDS_Shape & aFace = meshFaces[0]->GetSubShape(); - const TopoDS_Face & F = TopoDS::Face(aFace); - - // 1.3 - identify the 4 vertices of the face Y=0: V000, V100, V101, V001 - - aCube.V000 = aQuads[0]->side[0]->FirstVertex(); // will be (0,0,0) on the unit cube - aCube.V100 = aQuads[0]->side[0]->LastVertex(); // will be (1,0,0) on the unit cube - aCube.V001 = aQuads[0]->side[2]->FirstVertex(); // will be (0,0,1) on the unit cube - aCube.V101 = aQuads[0]->side[2]->LastVertex(); // will be (1,0,1) on the unit cube - - TopTools_IndexedMapOfShape MV0; - TopExp::MapShapes(F, TopAbs_VERTEX, MV0); - - aCube.V010 = OppositeVertex( aCube.V000, MV0, aQuads); - aCube.V110 = OppositeVertex( aCube.V100, MV0, aQuads); - aCube.V011 = OppositeVertex( aCube.V001, MV0, aQuads); - aCube.V111 = OppositeVertex( aCube.V101, MV0, aQuads); - - // 1.6 - find remaining faces given 4 vertices - - int _indY0 = 0; - aCube.quad_Y0 = aQuads[_indY0]; - - int _indY1 = GetFaceIndex(aMesh, aShape, meshFaces, - aCube.V010, aCube.V011, aCube.V110, aCube.V111); - aCube.quad_Y1 = aQuads[_indY1]; - - int _indZ0 = GetFaceIndex(aMesh, aShape, meshFaces, - aCube.V000, aCube.V010, aCube.V100, aCube.V110); - aCube.quad_Z0 = aQuads[_indZ0]; - - int _indZ1 = GetFaceIndex(aMesh, aShape, meshFaces, - aCube.V001, aCube.V011, aCube.V101, aCube.V111); - aCube.quad_Z1 = aQuads[_indZ1]; - - int _indX0 = GetFaceIndex(aMesh, aShape, meshFaces, - aCube.V000, aCube.V001, aCube.V010, aCube.V011); - aCube.quad_X0 = aQuads[_indX0]; + _FaceGrid aCubeSide[ 6 ]; - int _indX1 = GetFaceIndex(aMesh, aShape, meshFaces, - aCube.V100, aCube.V101, aCube.V110, aCube.V111); - aCube.quad_X1 = aQuads[_indX1]; + swap( aCubeSide[B_BOTTOM]._quad, quad[0] ); + swap( aCubeSide[B_BOTTOM]._quad->side[ Q_RIGHT],// direct the normal of bottom quad inside cube + aCubeSide[B_BOTTOM]._quad->side[ Q_LEFT ] ); - // 1.7 - get convertion coefs from face 2D normalized to 3D normalized + aCubeSide[B_FRONT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_BOTTOM], quad ); + aCubeSide[B_RIGHT]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_RIGHT ], quad ); + aCubeSide[B_BACK ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_TOP ], quad ); + aCubeSide[B_LEFT ]._quad = getQuadWithBottom( aCubeSide[B_BOTTOM]._quad->side[Q_LEFT ], quad ); + if ( aCubeSide[B_FRONT ]._quad ) + aCubeSide[B_TOP]._quad = getQuadWithBottom( aCubeSide[B_FRONT ]._quad->side[Q_TOP ], quad ); - Conv2DStruct cx0; // for face X=0 - Conv2DStruct cx1; // for face X=1 - Conv2DStruct cy0; - Conv2DStruct cy1; - Conv2DStruct cz0; - Conv2DStruct cz1; + for ( int i = 1; i < 6; ++i ) + if ( !aCubeSide[i]._quad ) + return error( COMPERR_BAD_SHAPE ); - GetConv2DCoefs(*aCube.quad_X0, meshFaces[_indX0]->GetSubShape(), - aCube.V000, aCube.V010, aCube.V011, aCube.V001, cx0); - GetConv2DCoefs(*aCube.quad_X1, meshFaces[_indX1]->GetSubShape(), - aCube.V100, aCube.V110, aCube.V111, aCube.V101, cx1); - GetConv2DCoefs(*aCube.quad_Y0, meshFaces[_indY0]->GetSubShape(), - aCube.V000, aCube.V100, aCube.V101, aCube.V001, cy0); - GetConv2DCoefs(*aCube.quad_Y1, meshFaces[_indY1]->GetSubShape(), - aCube.V010, aCube.V110, aCube.V111, aCube.V011, cy1); - GetConv2DCoefs(*aCube.quad_Z0, meshFaces[_indZ0]->GetSubShape(), - aCube.V000, aCube.V100, aCube.V110, aCube.V010, cz0); - GetConv2DCoefs(*aCube.quad_Z1, meshFaces[_indZ1]->GetSubShape(), - aCube.V001, aCube.V101, aCube.V111, aCube.V011, cz1); + // Make viscous layers + // -------------------- - // 1.8 - create a 3D structure for normalized values - - int nbx = aCube.quad_Z0->side[0]->NbPoints(); - if (cz0.a1 == 0.) nbx = aCube.quad_Z0->side[1]->NbPoints(); - - int nby = aCube.quad_X0->side[0]->NbPoints(); - if (cx0.a1 == 0.) nby = aCube.quad_X0->side[1]->NbPoints(); - - int nbz = aCube.quad_Y0->side[0]->NbPoints(); - if (cy0.a1 != 0.) nbz = aCube.quad_Y0->side[1]->NbPoints(); - - int i1, j1, nbxyz = nbx * nby * nbz; - Point3DStruct *np = new Point3DStruct[nbxyz]; + SMESH_ProxyMesh::Ptr proxymesh; + if ( _viscousLayersHyp ) + { + proxymesh = _viscousLayersHyp->Compute( aMesh, aShape, /*makeN2NMap=*/ true ); + if ( !proxymesh ) + return false; + } - // 1.9 - store node indexes of faces + // Check if there are triangles on cube sides + // ------------------------------------------- + if ( aMesh.NbTriangles() > 0 ) { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indX0]->GetSubShape()); - - faceQuadStruct *quad = aCube.quad_X0; - int i = 0; // j = x/face , k = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; - } - - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int j = cx0.ia * i1 + cx0.ib * j1 + cx0.ic; // j = x/face - int k = cx0.ja * i1 + cx0.jb * j1 + cx0.jc; // k = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); + for ( int i = 0; i < 6; ++i ) + { + const TopoDS_Face& sideF = aCubeSide[i]._quad->face; + if ( SMESHDS_SubMesh* smDS = meshDS->MeshElements( sideF )) + { + bool isAllQuad = true; + SMDS_ElemIteratorPtr fIt = smDS->GetElements(); + while ( fIt->more() && isAllQuad ) + { + const SMDS_MeshElement* f = fIt->next(); + isAllQuad = ( f->NbCornerNodes() == 4 ); + } + if ( !isAllQuad ) + { + SMESH_ComputeErrorPtr err = ComputePentahedralMesh(aMesh, aShape, proxymesh.get()); + return error( err ); + } } + } } - { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indX1]->GetSubShape()); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - faceQuadStruct *quad = aCube.quad_X1; - int i = nbx - 1; // j = x/face , k = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; - } + // Check presence of regular grid mesh on FACEs of the cube + // ------------------------------------------------------------ - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int j = cx1.ia * i1 + cx1.ib * j1 + cx1.ic; // j = x/face - int k = cx1.ja * i1 + cx1.jb * j1 + cx1.jc; // k = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); - } - } + // tool creating quadratic elements if needed + SMESH_MesherHelper helper (aMesh); + _quadraticMesh = helper.IsQuadraticSubMesh(aShape); + for ( int i = 0; i < 6; ++i ) { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indY0]->GetSubShape()); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - faceQuadStruct *quad = aCube.quad_Y0; - int j = 0; // i = x/face , k = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; + const TopoDS_Face& F = aCubeSide[i]._quad->face; + StdMeshers_FaceSide* baseQuadSide = aCubeSide[i]._quad->side[ Q_BOTTOM ]; + list baseEdges( baseQuadSide->Edges().begin(), baseQuadSide->Edges().end() ); + + // assure correctness of node positions on baseE: + // helper.GetNodeU() will fix positions if they are wrong + helper.ToFixNodeParameters( true ); + for ( int iE = 0; iE < baseQuadSide->NbEdges(); ++iE ) + { + const TopoDS_Edge& baseE = baseQuadSide->Edge( iE ); + if ( SMESHDS_SubMesh* smDS = meshDS->MeshElements( baseE )) + { + bool ok; + helper.SetSubShape( baseE ); + SMDS_ElemIteratorPtr eIt = smDS->GetElements(); + while ( eIt->more() ) + { + const SMDS_MeshElement* e = eIt->next(); + // expect problems on a composite side + try { helper.GetNodeU( baseE, e->GetNode(0), e->GetNode(1), &ok); } + catch (...) {} + try { helper.GetNodeU( baseE, e->GetNode(1), e->GetNode(0), &ok); } + catch (...) {} + } + } } - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int i = cy0.ia * i1 + cy0.ib * j1 + cy0.ic; // i = x/face - int k = cy0.ja * i1 + cy0.jb * j1 + cy0.jc; // k = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); - } + // load grid + bool ok = + helper.LoadNodeColumns( aCubeSide[i]._u2nodesMap, F, baseEdges, meshDS, proxymesh.get()); + if ( ok ) + { + // check if the loaded grid corresponds to nb of quadrangles on the FACE + const SMESHDS_SubMesh* faceSubMesh = + proxymesh ? proxymesh->GetSubMesh( F ) : meshDS->MeshElements( F ); + const int nbQuads = faceSubMesh->NbElements(); + const int nbHor = aCubeSide[i]._u2nodesMap.size() - 1; + const int nbVer = aCubeSide[i]._u2nodesMap.begin()->second.size() - 1; + ok = ( nbQuads == nbHor * nbVer ); + } + if ( !ok ) + { + SMESH_ComputeErrorPtr err = ComputePentahedralMesh(aMesh, aShape, proxymesh.get()); + return error( err ); + } } + // Orient loaded grids of cube sides along axis of the unitary cube coord system + bool isReverse[6]; + isReverse[B_BOTTOM] = beginsAtSide( aCubeSide[B_BOTTOM], aCubeSide[B_RIGHT ], proxymesh ); + isReverse[B_TOP ] = beginsAtSide( aCubeSide[B_TOP ], aCubeSide[B_RIGHT ], proxymesh ); + isReverse[B_FRONT ] = beginsAtSide( aCubeSide[B_FRONT ], aCubeSide[B_RIGHT ], proxymesh ); + isReverse[B_BACK ] = beginsAtSide( aCubeSide[B_BACK ], aCubeSide[B_RIGHT ], proxymesh ); + isReverse[B_LEFT ] = beginsAtSide( aCubeSide[B_LEFT ], aCubeSide[B_BACK ], proxymesh ); + isReverse[B_RIGHT ] = beginsAtSide( aCubeSide[B_RIGHT ], aCubeSide[B_BACK ], proxymesh ); + for ( int i = 0; i < 6; ++i ) { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indY1]->GetSubShape()); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - faceQuadStruct *quad = aCube.quad_Y1; - int j = nby - 1; // i = x/face , k = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; - } + aCubeSide[i]._columns.resize( aCubeSide[i]._u2nodesMap.size() ); - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int i = cy1.ia * i1 + cy1.ib * j1 + cy1.ic; // i = x/face - int k = cy1.ja * i1 + cy1.jb * j1 + cy1.jc; // k = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); - } + int iFwd = 0, iRev = aCubeSide[i]._columns.size()-1; + int* pi = isReverse[i] ? &iRev : &iFwd; + TParam2ColumnMap::iterator u2nn = aCubeSide[i]._u2nodesMap.begin(); + for ( ; iFwd < aCubeSide[i]._columns.size(); --iRev, ++iFwd, ++u2nn ) + aCubeSide[i]._columns[ *pi ].swap( u2nn->second ); + + aCubeSide[i]._u2nodesMap.clear(); } + + if ( proxymesh ) + for ( int i = 0; i < 6; ++i ) + for ( unsigned j = 0; j < aCubeSide[i]._columns.size(); ++j) + for ( unsigned k = 0; k < aCubeSide[i]._columns[j].size(); ++k) + { + const SMDS_MeshNode* & n = aCubeSide[i]._columns[j][k]; + n = proxymesh->GetProxyNode( n ); + } - { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indZ0]->GetSubShape()); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - faceQuadStruct *quad = aCube.quad_Z0; - int k = 0; // i = x/face , j = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; + // 4) Create internal nodes of the cube + // ------------------------------------- + + helper.SetSubShape( aShape ); + helper.SetElementsOnShape(true); + + // shortcuts to sides + _FaceGrid* fBottom = & aCubeSide[ B_BOTTOM ]; + _FaceGrid* fRight = & aCubeSide[ B_RIGHT ]; + _FaceGrid* fTop = & aCubeSide[ B_TOP ]; + _FaceGrid* fLeft = & aCubeSide[ B_LEFT ]; + _FaceGrid* fFront = & aCubeSide[ B_FRONT ]; + _FaceGrid* fBack = & aCubeSide[ B_BACK ]; + + // cube size measured in nb of nodes + int x, xSize = fBottom->_columns.size() , X = xSize - 1; + int y, ySize = fLeft->_columns.size() , Y = ySize - 1; + int z, zSize = fLeft->_columns[0].size(), Z = zSize - 1; + + // columns of internal nodes "rising" from nodes of fBottom + _Indexer colIndex( xSize, ySize ); + vector< vector< const SMDS_MeshNode* > > columns( colIndex.size() ); + + // fill node columns by front and back box sides + for ( x = 0; x < xSize; ++x ) { + vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( x, 0 )]; + vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( x, Y )]; + column0.resize( zSize ); + column1.resize( zSize ); + for ( z = 0; z < zSize; ++z ) { + column0[ z ] = fFront->GetNode( x, z ); + column1[ z ] = fBack ->GetNode( x, z ); } - - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int i = cz0.ia * i1 + cz0.ib * j1 + cz0.ic; // i = x/face - int j = cz0.ja * i1 + cz0.jb * j1 + cz0.jc; // j = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); - } } - - { - const TopoDS_Face & F = TopoDS::Face(meshFaces[_indZ1]->GetSubShape()); - - SMDS_NodeIteratorPtr itf= aMesh.GetSubMesh(F)->GetSubMeshDS()->GetNodes(); - - faceQuadStruct *quad = aCube.quad_Z1; - int k = nbz - 1; // i = x/face , j = y/face - int nbdown = quad->side[0]->NbPoints(); - int nbright = quad->side[1]->NbPoints(); - - while(itf->more()) { - const SMDS_MeshNode * node = itf->next(); - if(aTool.IsMedium(node)) - continue; - if ( !findIJ( node, quad, i1, j1 )) - return ClearAndReturn( aQuads, false ); - int ij1 = j1 * nbdown + i1; - quad->uv_grid[ij1].node = node; + // fill node columns by left and right box sides + for ( y = 1; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& column0 = columns[ colIndex( 0, y )]; + vector< const SMDS_MeshNode* >& column1 = columns[ colIndex( X, y )]; + column0.resize( zSize ); + column1.resize( zSize ); + for ( z = 0; z < zSize; ++z ) { + column0[ z ] = fLeft ->GetNode( y, z ); + column1[ z ] = fRight->GetNode( y, z ); } - - for (int i1 = 0; i1 < nbdown; i1++) - for (int j1 = 0; j1 < nbright; j1++) { - int ij1 = j1 * nbdown + i1; - int i = cz1.ia * i1 + cz1.ib * j1 + cz1.ic; // i = x/face - int j = cz1.ja * i1 + cz1.jb * j1 + cz1.jc; // j = y/face - int ijk = k * nbx * nby + j * nbx + i; - //MESSAGE(" "<uv_grid[ij1].node; - //SCRUTE(np[ijk].nodeId); - } } - - // 2.0 - for each node of the cube: - // - get the 8 points 3D = 8 vertices of the cube - // - get the 12 points 3D on the 12 edges of the cube - // - get the 6 points 3D on the 6 faces with their ID - // - compute the point 3D - // - store the point 3D in SMESHDS, store its ID in 3D structure - - int shapeID = meshDS->ShapeToIndex( aShape ); - - Pt3 p000, p001, p010, p011, p100, p101, p110, p111; - Pt3 px00, px01, px10, px11; - Pt3 p0y0, p0y1, p1y0, p1y1; - Pt3 p00z, p01z, p10z, p11z; - Pt3 pxy0, pxy1, px0z, px1z, p0yz, p1yz; - - GetPoint(p000, 0, 0, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p001, 0, 0, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(p010, 0, nby - 1, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p011, 0, nby - 1, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(p100, nbx - 1, 0, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p101, nbx - 1, 0, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(p110, nbx - 1, nby - 1, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p111, nbx - 1, nby - 1, nbz - 1, nbx, nby, nbz, np, meshDS); - - for (int i = 1; i < nbx - 1; i++) { - for (int j = 1; j < nby - 1; j++) { - for (int k = 1; k < nbz - 1; k++) { - // *** seulement maillage regulier - // 12 points on edges - GetPoint(px00, i, 0, 0, nbx, nby, nbz, np, meshDS); - GetPoint(px01, i, 0, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(px10, i, nby - 1, 0, nbx, nby, nbz, np, meshDS); - GetPoint(px11, i, nby - 1, nbz - 1, nbx, nby, nbz, np, meshDS); - - GetPoint(p0y0, 0, j, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p0y1, 0, j, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(p1y0, nbx - 1, j, 0, nbx, nby, nbz, np, meshDS); - GetPoint(p1y1, nbx - 1, j, nbz - 1, nbx, nby, nbz, np, meshDS); - - GetPoint(p00z, 0, 0, k, nbx, nby, nbz, np, meshDS); - GetPoint(p01z, 0, nby - 1, k, nbx, nby, nbz, np, meshDS); - GetPoint(p10z, nbx - 1, 0, k, nbx, nby, nbz, np, meshDS); - GetPoint(p11z, nbx - 1, nby - 1, k, nbx, nby, nbz, np, meshDS); - - // 12 points on faces - GetPoint(pxy0, i, j, 0, nbx, nby, nbz, np, meshDS); - GetPoint(pxy1, i, j, nbz - 1, nbx, nby, nbz, np, meshDS); - GetPoint(px0z, i, 0, k, nbx, nby, nbz, np, meshDS); - GetPoint(px1z, i, nby - 1, k, nbx, nby, nbz, np, meshDS); - GetPoint(p0yz, 0, j, k, nbx, nby, nbz, np, meshDS); - GetPoint(p1yz, nbx - 1, j, k, nbx, nby, nbz, np, meshDS); - - int ijk = k * nbx * nby + j * nbx + i; - double x = double (i) / double (nbx - 1); // *** seulement - double y = double (j) / double (nby - 1); // *** maillage - double z = double (k) / double (nbz - 1); // *** regulier - - Pt3 X; - for (int i = 0; i < 3; i++) { - X[i] = (1 - x) * p0yz[i] + x * p1yz[i] - + (1 - y) * px0z[i] + y * px1z[i] - + (1 - z) * pxy0[i] + z * pxy1[i] - - (1 - x) * ((1 - y) * p00z[i] + y * p01z[i]) - - x * ((1 - y) * p10z[i] + y * p11z[i]) - - (1 - y) * ((1 - z) * px00[i] + z * px01[i]) - - y * ((1 - z) * px10[i] + z * px11[i]) - - (1 - z) * ((1 - x) * p0y0[i] + x * p1y0[i]) - - z * ((1 - x) * p0y1[i] + x * p1y1[i]) - + (1 - x) * ((1 - y) * ((1 - z) * p000[i] + z * p001[i]) - + y * ((1 - z) * p010[i] + z * p011[i])) - + x * ((1 - y) * ((1 - z) * p100[i] + z * p101[i]) - + y * ((1 - z) * p110[i] + z * p111[i])); - } - - SMDS_MeshNode * node = meshDS->AddNode(X[0], X[1], X[2]); - np[ijk].node = node; - meshDS->SetNodeInVolume(node, shapeID); - } + // get nodes from top and bottom box sides + for ( x = 1; x < xSize-1; ++x ) { + for ( y = 1; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )]; + column.resize( zSize ); + column.front() = fBottom->GetNode( x, y ); + column.back() = fTop ->GetNode( x, y ); } } - // find orientation of furute volumes according to MED convention - vector< bool > forward( nbx * nby ); - SMDS_VolumeTool vTool; - for (int i = 0; i < nbx - 1; i++) { - for (int j = 0; j < nby - 1; j++) { - int n1 = j * nbx + i; - int n2 = j * nbx + i + 1; - int n3 = (j + 1) * nbx + i + 1; - int n4 = (j + 1) * nbx + i; - int n5 = nbx * nby + j * nbx + i; - int n6 = nbx * nby + j * nbx + i + 1; - int n7 = nbx * nby + (j + 1) * nbx + i + 1; - int n8 = nbx * nby + (j + 1) * nbx + i; - - SMDS_VolumeOfNodes tmpVol (np[n1].node,np[n2].node,np[n3].node,np[n4].node, - np[n5].node,np[n6].node,np[n7].node,np[n8].node); - vTool.Set( &tmpVol ); - forward[ n1 ] = vTool.IsForward(); + // projection points of the internal node on cube sub-shapes by which + // coordinates of the internal node are computed + vector pointsOnShapes( SMESH_Block::ID_Shell ); + + // projections on vertices are constant + pointsOnShapes[ SMESH_Block::ID_V000 ] = fBottom->GetXYZ( 0, 0 ); + pointsOnShapes[ SMESH_Block::ID_V100 ] = fBottom->GetXYZ( X, 0 ); + pointsOnShapes[ SMESH_Block::ID_V010 ] = fBottom->GetXYZ( 0, Y ); + pointsOnShapes[ SMESH_Block::ID_V110 ] = fBottom->GetXYZ( X, Y ); + pointsOnShapes[ SMESH_Block::ID_V001 ] = fTop->GetXYZ( 0, 0 ); + pointsOnShapes[ SMESH_Block::ID_V101 ] = fTop->GetXYZ( X, 0 ); + pointsOnShapes[ SMESH_Block::ID_V011 ] = fTop->GetXYZ( 0, Y ); + pointsOnShapes[ SMESH_Block::ID_V111 ] = fTop->GetXYZ( X, Y ); + + for ( x = 1; x < xSize-1; ++x ) + { + gp_XYZ params; // normalized parameters of internal node within a unit box + params.SetCoord( 1, x / double(X) ); + for ( y = 1; y < ySize-1; ++y ) + { + params.SetCoord( 2, y / double(Y) ); + // a column to fill in during z loop + vector< const SMDS_MeshNode* >& column = columns[ colIndex( x, y )]; + // projection points on horizontal edges + pointsOnShapes[ SMESH_Block::ID_Ex00 ] = fBottom->GetXYZ( x, 0 ); + pointsOnShapes[ SMESH_Block::ID_Ex10 ] = fBottom->GetXYZ( x, Y ); + pointsOnShapes[ SMESH_Block::ID_E0y0 ] = fBottom->GetXYZ( 0, y ); + pointsOnShapes[ SMESH_Block::ID_E1y0 ] = fBottom->GetXYZ( X, y ); + pointsOnShapes[ SMESH_Block::ID_Ex01 ] = fTop->GetXYZ( x, 0 ); + pointsOnShapes[ SMESH_Block::ID_Ex11 ] = fTop->GetXYZ( x, Y ); + pointsOnShapes[ SMESH_Block::ID_E0y1 ] = fTop->GetXYZ( 0, y ); + pointsOnShapes[ SMESH_Block::ID_E1y1 ] = fTop->GetXYZ( X, y ); + // projection points on horizontal faces + pointsOnShapes[ SMESH_Block::ID_Fxy0 ] = fBottom->GetXYZ( x, y ); + pointsOnShapes[ SMESH_Block::ID_Fxy1 ] = fTop ->GetXYZ( x, y ); + for ( z = 1; z < zSize-1; ++z ) // z loop + { + params.SetCoord( 3, z / double(Z) ); + // projection points on vertical edges + pointsOnShapes[ SMESH_Block::ID_E00z ] = fFront->GetXYZ( 0, z ); + pointsOnShapes[ SMESH_Block::ID_E10z ] = fFront->GetXYZ( X, z ); + pointsOnShapes[ SMESH_Block::ID_E01z ] = fBack->GetXYZ( 0, z ); + pointsOnShapes[ SMESH_Block::ID_E11z ] = fBack->GetXYZ( X, z ); + // projection points on vertical faces + pointsOnShapes[ SMESH_Block::ID_Fx0z ] = fFront->GetXYZ( x, z ); + pointsOnShapes[ SMESH_Block::ID_Fx1z ] = fBack ->GetXYZ( x, z ); + pointsOnShapes[ SMESH_Block::ID_F0yz ] = fLeft ->GetXYZ( y, z ); + pointsOnShapes[ SMESH_Block::ID_F1yz ] = fRight->GetXYZ( y, z ); + + // compute internal node coordinates + gp_XYZ coords; + SMESH_Block::ShellPoint( params, pointsOnShapes, coords ); + column[ z ] = helper.AddNode( coords.X(), coords.Y(), coords.Z() ); + + } } } - //2.1 - for each node of the cube (less 3 *1 Faces): - // - store hexahedron in SMESHDS - MESSAGE("Storing hexahedron into the DS"); - for (int i = 0; i < nbx - 1; i++) { - for (int j = 0; j < nby - 1; j++) { - bool isForw = forward.at( j * nbx + i ); - for (int k = 0; k < nbz - 1; k++) { - int n1 = k * nbx * nby + j * nbx + i; - int n2 = k * nbx * nby + j * nbx + i + 1; - int n3 = k * nbx * nby + (j + 1) * nbx + i + 1; - int n4 = k * nbx * nby + (j + 1) * nbx + i; - int n5 = (k + 1) * nbx * nby + j * nbx + i; - int n6 = (k + 1) * nbx * nby + j * nbx + i + 1; - int n7 = (k + 1) * nbx * nby + (j + 1) * nbx + i + 1; - int n8 = (k + 1) * nbx * nby + (j + 1) * nbx + i; - - SMDS_MeshVolume * elt; - if ( isForw ) { - elt = aTool.AddVolume(np[n1].node, np[n2].node, - np[n3].node, np[n4].node, - np[n5].node, np[n6].node, - np[n7].node, np[n8].node); - } - else { - elt = aTool.AddVolume(np[n1].node, np[n4].node, - np[n3].node, np[n2].node, - np[n5].node, np[n8].node, - np[n7].node, np[n6].node); - } - - meshDS->SetMeshElementOnShape(elt, shapeID); + // side data no more needed, free memory + for ( int i = 0; i < 6; ++i ) + aCubeSide[i]._columns.clear(); + + // 5) Create hexahedrons + // --------------------- + + for ( x = 0; x < xSize-1; ++x ) { + for ( y = 0; y < ySize-1; ++y ) { + vector< const SMDS_MeshNode* >& col00 = columns[ colIndex( x, y )]; + vector< const SMDS_MeshNode* >& col10 = columns[ colIndex( x+1, y )]; + vector< const SMDS_MeshNode* >& col01 = columns[ colIndex( x, y+1 )]; + vector< const SMDS_MeshNode* >& col11 = columns[ colIndex( x+1, y+1 )]; + for ( z = 0; z < zSize-1; ++z ) + { + // bottom face normal of a hexa mush point outside the volume + helper.AddVolume(col00[z], col01[z], col11[z], col10[z], + col00[z+1], col01[z+1], col11[z+1], col10[z+1]); } } } - if ( np ) delete [] np; - return ClearAndReturn( aQuads, true ); -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -void StdMeshers_Hexa_3D::GetPoint(Pt3 p, int i, int j, int k, int nbx, int nby, int nbz, - Point3DStruct * np, const SMESHDS_Mesh * meshDS) -{ - int ijk = k * nbx * nby + j * nbx + i; - const SMDS_MeshNode * node = np[ijk].node; - p[0] = node->X(); - p[1] = node->Y(); - p[2] = node->Z(); - //MESSAGE(" "<&meshFaces, - const TopoDS_Vertex & V0, - const TopoDS_Vertex & V1, - const TopoDS_Vertex & V2, const TopoDS_Vertex & V3) -{ - //MESSAGE("StdMeshers_Hexa_3D::GetFaceIndex"); - int faceIndex = -1; - for (int i = 1; i < 6; i++) - { - const TopoDS_Shape & aFace = meshFaces[i]->GetSubShape(); - //const TopoDS_Face& F = TopoDS::Face(aFace); - TopTools_IndexedMapOfShape M; - TopExp::MapShapes(aFace, TopAbs_VERTEX, M); - bool verticesInShape = false; - if (M.Contains(V0)) - if (M.Contains(V1)) - if (M.Contains(V2)) - if (M.Contains(V3)) - verticesInShape = true; - if (verticesInShape) - { - faceIndex = i; - break; - } - } - ASSERT(faceIndex > 0); - //SCRUTE(faceIndex); - return faceIndex; -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -TopoDS_Edge - StdMeshers_Hexa_3D::EdgeNotInFace(SMESH_Mesh & aMesh, - const TopoDS_Shape & aShape, - const TopoDS_Face & aFace, - const TopoDS_Vertex & aVertex, - const TopTools_IndexedDataMapOfShapeListOfShape & MS) -{ - //MESSAGE("StdMeshers_Hexa_3D::EdgeNotInFace"); - TopTools_IndexedDataMapOfShapeListOfShape MF; - TopExp::MapShapesAndAncestors(aFace, TopAbs_VERTEX, TopAbs_EDGE, MF); - const TopTools_ListOfShape & ancestorsInSolid = MS.FindFromKey(aVertex); - const TopTools_ListOfShape & ancestorsInFace = MF.FindFromKey(aVertex); -// SCRUTE(ancestorsInSolid.Extent()); -// SCRUTE(ancestorsInFace.Extent()); - ASSERT(ancestorsInSolid.Extent() == 6); // 6 (edges doublees) - ASSERT(ancestorsInFace.Extent() == 2); - - TopoDS_Edge E; - E.Nullify(); - TopTools_ListIteratorOfListOfShape its(ancestorsInSolid); - for (; its.More(); its.Next()) - { - TopoDS_Shape ancestor = its.Value(); - TopTools_ListIteratorOfListOfShape itf(ancestorsInFace); - bool isInFace = false; - for (; itf.More(); itf.Next()) - { - TopoDS_Shape ancestorInFace = itf.Value(); - if (ancestorInFace.IsSame(ancestor)) - { - isInFace = true; - break; - } - } - if (!isInFace) - { - E = TopoDS::Edge(ancestor); - break; - } - } - return E; + return true; } //============================================================================= /*! - * + * Evaluate */ //============================================================================= -void StdMeshers_Hexa_3D::GetConv2DCoefs(const faceQuadStruct & quad, - const TopoDS_Shape & aShape, - const TopoDS_Vertex & V0, - const TopoDS_Vertex & V1, - const TopoDS_Vertex & V2, const TopoDS_Vertex & V3, Conv2DStruct & conv) +bool StdMeshers_Hexa_3D::Evaluate(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap) { -// MESSAGE("StdMeshers_Hexa_3D::GetConv2DCoefs"); -// const TopoDS_Face & F = TopoDS::Face(aShape); -// TopoDS_Edge E = quad.edge[0]; -// double f, l; -// Handle(Geom2d_Curve) C2d = BRep_Tool::CurveOnSurface(E, F, f, l); -// TopoDS_Vertex VFirst, VLast; -// TopExp::Vertices(E, VFirst, VLast); // corresponds to f and l -// bool isForward = (((l - f) * (quad.last[0] - quad.first[0])) > 0); - TopoDS_Vertex VA, VB; -// if (isForward) -// { -// VA = VFirst; -// VB = VLast; -// } -// else -// { -// VA = VLast; -// VB = VFirst; -// } - VA = quad.side[0]->FirstVertex(); - VB = quad.side[0]->LastVertex(); + vector < SMESH_subMesh * >meshFaces; + TopTools_SequenceOfShape aFaces; + for (TopExp_Explorer exp(aShape, TopAbs_FACE); exp.More(); exp.Next()) { + aFaces.Append(exp.Current()); + SMESH_subMesh *aSubMesh = aMesh.GetSubMeshContaining(exp.Current()); + ASSERT(aSubMesh); + meshFaces.push_back(aSubMesh); + } + if (meshFaces.size() != 6) { + //return error(COMPERR_BAD_SHAPE, TComm(meshFaces.size())<<" instead of 6 faces in a block"); + static StdMeshers_CompositeHexa_3D compositeHexa(-10, 0, aMesh.GetGen()); + return compositeHexa.Evaluate(aMesh, aShape, aResMap); + } + + int i = 0; + for(; i<6; i++) { + //TopoDS_Shape aFace = meshFaces[i]->GetSubShape(); + TopoDS_Shape aFace = aFaces.Value(i+1); + SMESH_Algo *algo = _gen->GetAlgo(aMesh, aFace); + if( !algo ) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + string algoName = algo->GetName(); + bool isAllQuad = false; + if (algoName == "Quadrangle_2D") { + MapShapeNbElemsItr anIt = aResMap.find(meshFaces[i]); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + int nbtri = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + if( nbtri == 0 ) + isAllQuad = true; + } + if ( ! isAllQuad ) { + return EvaluatePentahedralMesh(aMesh, aShape, aResMap); + } + } - int a1, b1, c1, a2, b2, c2; - if (VA.IsSame(V0)) - if (VB.IsSame(V1)) - { - a1 = 1; - b1 = 0; - c1 = 0; // x - a2 = 0; - b2 = 1; - c2 = 0; // y - } - else - { - ASSERT(VB.IsSame(V3)); - a1 = 0; - b1 = 1; - c1 = 0; // y - a2 = 1; - b2 = 0; - c2 = 0; // x - } - if (VA.IsSame(V1)) - if (VB.IsSame(V2)) - { - a1 = 0; - b1 = -1; - c1 = 1; // 1-y - a2 = 1; - b2 = 0; - c2 = 0; // x - } - else - { - ASSERT(VB.IsSame(V0)); - a1 = -1; - b1 = 0; - c1 = 1; // 1-x - a2 = 0; - b2 = 1; - c2 = 0; // y - } - if (VA.IsSame(V2)) - if (VB.IsSame(V3)) - { - a1 = -1; - b1 = 0; - c1 = 1; // 1-x - a2 = 0; - b2 = -1; - c2 = 1; // 1-y - } - else - { - ASSERT(VB.IsSame(V1)); - a1 = 0; - b1 = -1; - c1 = 1; // 1-y - a2 = -1; - b2 = 0; - c2 = 1; // 1-x - } - if (VA.IsSame(V3)) - if (VB.IsSame(V0)) - { - a1 = 0; - b1 = 1; - c1 = 0; // y - a2 = -1; - b2 = 0; - c2 = 1; // 1-x - } - else - { - ASSERT(VB.IsSame(V2)); - a1 = 1; - b1 = 0; - c1 = 0; // x - a2 = 0; - b2 = -1; - c2 = 1; // 1-y - } -// MESSAGE("X = " << c1 << "+ " << a1 << "*x + " << b1 << "*y"); -// MESSAGE("Y = " << c2 << "+ " << a2 << "*x + " << b2 << "*y"); - conv.a1 = a1; - conv.b1 = b1; - conv.c1 = c1; - conv.a2 = a2; - conv.b2 = b2; - conv.c2 = c2; - - int nbdown = quad.side[0]->NbPoints(); - int nbright = quad.side[1]->NbPoints(); - conv.ia = int (a1); - conv.ib = int (b1); - conv.ic = - int (c1 * a1 * a1) * (nbdown - 1) + int (c1 * b1 * b1) * (nbright - 1); - conv.ja = int (a2); - conv.jb = int (b2); - conv.jc = - int (c2 * a2 * a2) * (nbdown - 1) + int (c2 * b2 * b2) * (nbright - 1); -// MESSAGE("I " << conv.ia << " " << conv.ib << " " << conv.ic); -// MESSAGE("J " << conv.ja << " " << conv.jb << " " << conv.jc); + // find number of 1d elems for 1 face + int nb1d = 0; + TopTools_MapOfShape Edges1; + bool IsQuadratic = false; + bool IsFirst = true; + for (TopExp_Explorer exp(aFaces.Value(1), TopAbs_EDGE); exp.More(); exp.Next()) { + Edges1.Add(exp.Current()); + SMESH_subMesh *sm = aMesh.GetSubMesh(exp.Current()); + if( sm ) { + MapShapeNbElemsItr anIt = aResMap.find(sm); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + if(IsFirst) { + IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]); + IsFirst = false; + } + } + } + // find face opposite to 1 face + int OppNum = 0; + for(i=2; i<=6; i++) { + bool IsOpposite = true; + for(TopExp_Explorer exp(aFaces.Value(i), TopAbs_EDGE); exp.More(); exp.Next()) { + if( Edges1.Contains(exp.Current()) ) { + IsOpposite = false; + break; + } + } + if(IsOpposite) { + OppNum = i; + break; + } + } + // find number of 2d elems on side faces + int nb2d = 0; + for(i=2; i<=6; i++) { + if( i == OppNum ) continue; + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[i-1] ); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb2d += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + } + + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[0] ); + std::vector aVec = (*anIt).second; + int nb2d_face0 = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + int nb0d_face0 = aVec[SMDSEntity_Node]; + + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iside[0]->FirstVertex(), - aQuads[i]->side[0]->LastVertex() , - aQuads[i]->side[2]->LastVertex() , - aQuads[i]->side[2]->FirstVertex() }; - for ( j = 0; j < 4; ++j ) - if ( aVertex.IsSame( VV[ j ])) - break; - if ( j < 4 ) { - int jPrev = j ? j - 1 : 3; - int jNext = (j + 1) % 4; - if ( aQuads0Vertices.Contains( VV[ jPrev ] )) - return VV[ jNext ]; - else - return VV[ jPrev ]; - } + static StdMeshers_HexaFromSkin_3D * algo = 0; + if ( !algo ) { + SMESH_Gen* gen = aMesh.GetGen(); + algo = new StdMeshers_HexaFromSkin_3D( gen->GetANewId(), 0, gen ); } - return TopoDS_Vertex(); + algo->InitComputeError(); + algo->Compute( aMesh, aHelper ); + return error( algo->GetComputeError()); } -//modified by NIZNHY-PKV Wed Nov 17 15:34:13 2004 f -/////////////////////////////////////////////////////////////////////////////// -//ZZ -//#include - //======================================================================= //function : ComputePentahedralMesh //purpose : //======================================================================= -SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh & aMesh, - const TopoDS_Shape & aShape) +SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + SMESH_ProxyMesh* proxyMesh) { - //printf(" ComputePentahedralMesh HERE\n"); - // - bool bOK; SMESH_ComputeErrorPtr err = SMESH_ComputeError::New(); - //int iErr; + if ( proxyMesh ) + { + err->myName = COMPERR_BAD_INPUT_MESH; + err->myComment = "Can't build pentahedral mesh on viscous layers"; + return err; + } + bool bOK; StdMeshers_Penta_3D anAlgo; // bOK=anAlgo.Compute(aMesh, aShape); @@ -1047,6 +781,7 @@ SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh & aMesh, } SMESH_Hypothesis::Hypothesis_Status aStatus; if ( aPrism3D->CheckHypothesis( aMesh, aShape, aStatus ) ) { + aPrism3D->InitComputeError(); bOK = aPrism3D->Compute( aMesh, aShape ); err = aPrism3D->GetComputeError(); } @@ -1055,3 +790,31 @@ SMESH_ComputeErrorPtr ComputePentahedralMesh(SMESH_Mesh & aMesh, } +//======================================================================= +//function : EvaluatePentahedralMesh +//purpose : +//======================================================================= + +bool EvaluatePentahedralMesh(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap) +{ + StdMeshers_Penta_3D anAlgo; + bool bOK = anAlgo.Evaluate(aMesh, aShape, aResMap); + + //err = anAlgo.GetComputeError(); + //if ( !bOK && anAlgo.ErrorStatus() == 5 ) + if( !bOK ) { + static StdMeshers_Prism_3D * aPrism3D = 0; + if ( !aPrism3D ) { + SMESH_Gen* gen = aMesh.GetGen(); + aPrism3D = new StdMeshers_Prism_3D( gen->GetANewId(), 0, gen ); + } + SMESH_Hypothesis::Hypothesis_Status aStatus; + if ( aPrism3D->CheckHypothesis( aMesh, aShape, aStatus ) ) { + return aPrism3D->Evaluate(aMesh, aShape, aResMap); + } + } + + return bOK; +} diff --git a/src/StdMeshers/StdMeshers_Hexa_3D.hxx b/src/StdMeshers/StdMeshers_Hexa_3D.hxx index 24927ef5a..48f1eebbe 100644 --- a/src/StdMeshers/StdMeshers_Hexa_3D.hxx +++ b/src/StdMeshers/StdMeshers_Hexa_3D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Hexa_3D.hxx // Moved here from SMESH_Hexa_3D.hxx @@ -31,39 +32,12 @@ #include "SMESH_StdMeshers.hxx" #include "SMESH_3D_Algo.hxx" -#include "SMESH_Mesh.hxx" -#include "StdMeshers_Quadrangle_2D.hxx" -#include "Utils_SALOME_Exception.hxx" - -#include "SMESH_MesherHelper.hxx" - -class TopTools_IndexedMapOfShape; - -typedef struct point3Dstruct -{ - const SMDS_MeshNode * node; -} Point3DStruct; -typedef double Pt3[3]; -typedef struct conv2dstruct -{ - double a1; // X = a1*x + b1*y + c1 - double b1; // Y = a2*x + b2*y + c2 - double c1; // a1, b1 a2, b2 in {-1,0,1} - double a2; // c1, c2 in {0,1} - double b2; - double c2; - int ia; // I = ia*i + ib*j + ic - int ib; - int ic; - int ja; // J = ja*i + jb*j + jc - int jb; - int jc; -} Conv2DStruct; +class StdMeshers_ViscousLayers; +class SMESH_MesherHelper; -class STDMESHERS_EXPORT StdMeshers_Hexa_3D: - public SMESH_3D_Algo +class STDMESHERS_EXPORT StdMeshers_Hexa_3D : public SMESH_3D_Algo { public: StdMeshers_Hexa_3D(int hypId, int studyId, SMESH_Gen* gen); @@ -73,45 +47,16 @@ public: const TopoDS_Shape& aShape, SMESH_Hypothesis::Hypothesis_Status& aStatus); - virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape) - /*throw (SALOME_Exception)*/; - - static TopoDS_Vertex OppositeVertex(const TopoDS_Vertex& aVertex, - const TopTools_IndexedMapOfShape& aQuads0Vertices, - FaceQuadStruct* aQuads[6]); + virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); -protected: - TopoDS_Edge - EdgeNotInFace(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, - const TopoDS_Face& aFace, - const TopoDS_Vertex& aVertex, - const TopTools_IndexedDataMapOfShapeListOfShape& MS); + virtual bool Compute(SMESH_Mesh & aMesh, SMESH_MesherHelper* aHelper); - int GetFaceIndex(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, - const std::vector& meshFaces, - const TopoDS_Vertex& V0, - const TopoDS_Vertex& V1, - const TopoDS_Vertex& V2, - const TopoDS_Vertex& V3); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); - void GetConv2DCoefs(const faceQuadStruct& quad, - const TopoDS_Shape& aShape, - const TopoDS_Vertex& V0, - const TopoDS_Vertex& V1, - const TopoDS_Vertex& V2, - const TopoDS_Vertex& V3, - Conv2DStruct& conv); - - void GetPoint(Pt3 p, - int i, int j, int k, - int nbx, int nby, int nbz, - Point3DStruct *np, - const SMESHDS_Mesh* meshDS); +protected: - bool ClearAndReturn(FaceQuadStruct* theQuads[6], const bool res); + const StdMeshers_ViscousLayers* _viscousLayersHyp; }; #endif diff --git a/src/StdMeshers/StdMeshers_ImportSource.cxx b/src/StdMeshers/StdMeshers_ImportSource.cxx new file mode 100644 index 000000000..e93b83a61 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ImportSource.cxx @@ -0,0 +1,490 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH StdMeshers_ImportSource1D : implementaion of SMESH idl descriptions +// File : StdMeshers_ImportSource1D.cxx +// Module : SMESH +// +#include "StdMeshers_ImportSource.hxx" + +#include "SMESHDS_GroupOnGeom.hxx" +#include "SMESHDS_Mesh.hxx" +#include "SMESH_Algo.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_subMeshEventListener.hxx" + +#include "utilities.h" + +#include + +#include + +using namespace std; + +//============================================================================= +/*! + * Creates StdMeshers_ImportSource1D + */ +//============================================================================= + +StdMeshers_ImportSource1D::StdMeshers_ImportSource1D(int hypId, + int studyId, + SMESH_Gen * gen) + :SMESH_Hypothesis(hypId, studyId, gen), + _toCopyMesh(false), + _toCopyGroups(false) +{ + _name = "ImportSource1D"; + _param_algo_dim = 1; // is used by StdMeshers_Import_1D; +} + +//============================================================================= +/*! + * Creates StdMeshers_ImportSource2D + */ +//============================================================================= + +StdMeshers_ImportSource2D::StdMeshers_ImportSource2D(int hypId, + int studyId, + SMESH_Gen * gen) + :StdMeshers_ImportSource1D(hypId, studyId, gen) +{ + _name = "ImportSource2D"; + _param_algo_dim = 2; // is used by StdMeshers_Import_2D; +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +StdMeshers_ImportSource1D::~StdMeshers_ImportSource1D() +{ +} +//============================================================================= +/*! + * Sets groups to import elements from + */ +//============================================================================= + +void StdMeshers_ImportSource1D::SetGroups(const std::vector& groups) +{ + if (_groups != groups) + { + _groups = groups; + NotifySubMeshesHypothesisModification(); + } +} + +void StdMeshers_ImportSource1D::SetCopySourceMesh(bool toCopyMesh, bool toCopyGroups) +{ + if ( !toCopyMesh ) toCopyGroups = false; + if ( _toCopyMesh != toCopyMesh || _toCopyGroups != toCopyGroups ) + { + _toCopyMesh = toCopyMesh; _toCopyGroups = toCopyGroups; + NotifySubMeshesHypothesisModification(); + } +} +void StdMeshers_ImportSource1D::GetCopySourceMesh(bool& toCopyMesh, bool& toCopyGroups) const +{ + toCopyMesh = _toCopyMesh; toCopyGroups = _toCopyGroups; +} + +namespace +{ + //================================================================================ + /*! + * \brief Return only alive groups + */ + //================================================================================ + + vector getValidGroups(const vector& groups, + StudyContextStruct* studyContext) + { + vector okGroups; + for ( int i = 0; i < groups.size(); ++i ) + { + try + { + // we expect SIGSEGV on a dead group + OCC_CATCH_SIGNALS; + SMESH_Group* okGroup = 0; + map::iterator itm = itm = studyContext->mapMesh.begin(); + for ( ; !okGroup && itm != studyContext->mapMesh.end(); itm++) + { + SMESH_Mesh::GroupIteratorPtr gIt = itm->second->GetGroups(); + while ( gIt->more() && !okGroup ) + if ( gIt->next() == groups[i] ) + okGroup = groups[i]; + } + if ( okGroup ) + okGroups.push_back( okGroup ); + } + catch(...) + { + } + } + return okGroups; + } + //================================================================================ + /*! + * \brief Pack meshes into a pair of ints + */ + //================================================================================ + + pair getResMapKey(const SMESHDS_Mesh& srcMesh, const SMESHDS_Mesh& tgtMesh) + { + return make_pair( srcMesh.GetPersistentId() , tgtMesh.GetPersistentId() ); + } + //================================================================================ + /*! + * \brief Return a target mesh by a pair of ints + */ + //================================================================================ + + SMESH_Mesh* getTgtMeshByKey( const pair & resMapKey, + StudyContextStruct* studyContext) + { + int tgtID = resMapKey.second; + SMESH_Mesh* tgtMesh = 0; + map::iterator itm = itm = studyContext->mapMesh.begin(); + for ( ; !tgtMesh && itm != studyContext->mapMesh.end(); itm++) + { + tgtMesh = (*itm).second; + if ( tgtMesh->GetMeshDS()->GetPersistentId() != tgtID ) + tgtMesh = 0; + } + return tgtMesh; + } + //================================================================================ + /*! + * \brief Return a target mesh by a pair of ints + */ + //================================================================================ + + int getSrcMeshID( const pair & resMapKey ) + { + return resMapKey.first; + } +} + +//============================================================================= +/*! + * Returns groups to import elements from + */ +//============================================================================= + +const std::vector& StdMeshers_ImportSource1D::GetGroups() const +{ + // filter off deleted groups + vector okGroups = getValidGroups( _groups, + _gen->GetStudyContext(_studyId) ); + if ( okGroups.size() != _groups.size() ) + ((StdMeshers_ImportSource1D*)this)->_groups = okGroups; + + return _groups; +} + +//================================================================================ +/*! + * \brief Return source meshes + */ +//================================================================================ + +std::vector StdMeshers_ImportSource1D::GetSourceMeshes() const +{ + // GetPersistentId()'s of meshes + set meshIDs; + const vector& groups = GetGroups(); + if ( !groups.empty() ) + { + for ( unsigned i = 0; i < groups.size(); ++i ) + { + const SMESHDS_GroupBase* gDS = groups[i]->GetGroupDS(); + int id = gDS->GetMesh()->GetPersistentId(); + meshIDs.insert( id ); + } + } + else + { + if ( _resultGroups.empty() ) + ((StdMeshers_ImportSource1D*)this)->RestoreGroups(_groups); + TResGroupMap::const_iterator key_groups = _resultGroups.begin(); + for ( ; key_groups != _resultGroups.end(); ++key_groups ) + meshIDs.insert( getSrcMeshID( key_groups->first )); + } + + // Find corresponding meshes + vector meshes; + if ( !meshIDs.empty() ) + { + StudyContextStruct* studyContext = _gen->GetStudyContext(_studyId); + for ( set::iterator id = meshIDs.begin(); id != meshIDs.end(); ++id ) + { + map::iterator itm = itm = studyContext->mapMesh.begin(); + for ( ; itm != studyContext->mapMesh.end(); itm++) + { + SMESH_Mesh* mesh = (*itm).second; + if ( mesh->GetMeshDS()->GetPersistentId() == *id ) + { + meshes.push_back( mesh ); + break; + } + } + } + } + return meshes; +} + +//================================================================================ +/*! + * \brief Return submeshes whose events affect the target mesh + */ +//================================================================================ + +std::vector +StdMeshers_ImportSource1D::GetSourceSubMeshes(const SMESH_Mesh* srcMesh) const +{ + if ( !srcMesh->HasShapeToMesh() ) + { + SMESH_Mesh* srcM = const_cast< SMESH_Mesh* >( srcMesh ); + return vector(1, srcM->GetSubMesh( srcM->GetShapeToMesh())); + } + set shapeIDs; + const vector& groups = GetGroups(); + const SMESHDS_Mesh * srcMeshDS = srcMesh->GetMeshDS(); + for ( size_t i = 0; i < groups.size(); ++i ) + { + SMESHDS_GroupBase * grDS = groups[i]->GetGroupDS(); + if ( grDS->GetMesh() != srcMeshDS ) + continue; + if ( SMESHDS_GroupOnGeom* gog = dynamic_cast( grDS )) + { + shapeIDs.insert( srcMeshDS->ShapeToIndex( gog->GetShape() )); + } + else + { + SMDS_ElemIteratorPtr elIt = grDS->GetElements(); + while ( elIt->more() ) + shapeIDs.insert( elIt->next()->getshapeId() ); + } + } + if ( !shapeIDs.empty() && *shapeIDs.begin() < 1 ) + { + shapeIDs.erase( shapeIDs.begin() ); + shapeIDs.insert( 1 ); + } + + vector smVec( shapeIDs.size()); + set::iterator sID = shapeIDs.begin(); + for ( int i = 0; sID != shapeIDs.end(); ++sID, ++i ) + smVec[i] = srcMesh->GetSubMeshContaining( *sID ); + + return smVec; +} + +//============================================================================= +/*! + * Save _toCopyMesh and _toCopyGroups to a stream + */ +//============================================================================= + +ostream & StdMeshers_ImportSource1D::SaveTo(ostream & save) +{ + resultGroupsToIntVec(); + + save << " " << _toCopyMesh << " " << _toCopyGroups; + save << " " << _resultGroupsStorage.size(); + for ( unsigned i = 0; i < _resultGroupsStorage.size(); ++i ) + save << " " << _resultGroupsStorage[i]; + + return save; +} + +//============================================================================= +/*! + * Load _toCopyMesh and _toCopyGroups from a stream + */ +//============================================================================= + +istream & StdMeshers_ImportSource1D::LoadFrom(istream & load) +{ + load >> _toCopyMesh >> _toCopyGroups; + + _resultGroupsStorage.clear(); + int val; + if ( load >> val ) + { + _resultGroupsStorage.reserve(val); + while ( _resultGroupsStorage.size() < _resultGroupsStorage.capacity() && load >> val ) + _resultGroupsStorage.push_back( val ); + } + return load; +} + +//================================================================================ +/*! + * \brief Convert result groups into _resultGroupsStorage + */ +//================================================================================ + +void StdMeshers_ImportSource1D::resultGroupsToIntVec() +{ + _resultGroupsStorage.clear(); + + // store result groups + TResGroupMap::iterator key2groups = _resultGroups.begin(); + for ( ; key2groups != _resultGroups.end(); ++key2groups ) + { + const pair& key = key2groups->first; + const vector& groups = key2groups->second; + // mesh ids, nb groups + _resultGroupsStorage.push_back( key.first ); + _resultGroupsStorage.push_back( key.second ); + _resultGroupsStorage.push_back( groups.size() ); + for ( unsigned i = 0; i < groups.size(); ++i ) + { + // store group names as sequence of ints each standing for a char + // of a name; that is to avoid pb with names containing white spaces + string name = groups[i]->GetGroupDS()->GetStoreName(); + _resultGroupsStorage.push_back( name.size() ); + for ( unsigned j = 0; j < name.size(); ++j ) + _resultGroupsStorage.push_back( name[j] ); + } + } +} + +//================================================================================ +/*! + * \brief Restore source groups and result groups by _resultGroupsStorage + */ +//================================================================================ + +void StdMeshers_ImportSource1D::RestoreGroups(const std::vector& groups) +{ + _groups = groups; + + _resultGroups.clear(); + int i = 0; + while ( i < _resultGroupsStorage.size() ) + { + int key1 = _resultGroupsStorage[i++]; + int key2 = _resultGroupsStorage[i++]; + pair resMapKey( key1, key2 ); + SMESH_Mesh* mesh = getTgtMeshByKey( resMapKey, _gen->GetStudyContext(_studyId)); + // restore mesh ids at least + _resultGroups.insert( make_pair (resMapKey,vector() )); + + int nbGroups = _resultGroupsStorage[i++]; + for ( int j = 0; j < nbGroups; ++j ) + { + string::size_type nameSize = _resultGroupsStorage[i++]; + string groupName(nameSize, '\0'); + for ( unsigned k = 0; k < nameSize; ++k ) + groupName[k] = (char) _resultGroupsStorage[i++]; + + // find a group by name + if ( mesh ) + { + SMESH_Group* group = 0; + SMESH_Mesh::GroupIteratorPtr gIt = mesh->GetGroups(); + while ( !group && gIt->more() ) + { + group = gIt->next(); + if ( !group->GetGroupDS() || groupName != group->GetGroupDS()->GetStoreName() ) + group = 0; + } + if ( group ) + _resultGroups[ resMapKey ].push_back( group ); + } + } + } +} + +//================================================================================ +/*! + * \brief Remember groups imported from other mesh + * \param groups - result groups + * \param srcMesh - source mesh + * \param tgtMesh - destination mesh + */ +//================================================================================ + +void StdMeshers_ImportSource1D::StoreResultGroups(const std::vector& groups, + const SMESHDS_Mesh& srcMesh, + const SMESHDS_Mesh& tgtMesh) +{ + _resultGroups[ getResMapKey(srcMesh,tgtMesh) ] = groups; +} + +//================================================================================ +/*! + * \brief Return groups imported from other mesh + * \param srcMesh - source mesh + * \param tgtMesh - destination mesh + * \retval const std::vector& - groups + */ +//================================================================================ + +std::vector* +StdMeshers_ImportSource1D::GetResultGroups(const SMESHDS_Mesh& srcMesh, + const SMESHDS_Mesh& tgtMesh) +{ + TResGroupMap::iterator key2groups = _resultGroups.find( getResMapKey(srcMesh,tgtMesh )); + if ( key2groups == _resultGroups.end() ) + return 0; + vector vec = getValidGroups((*key2groups).second, + _gen->GetStudyContext(_studyId) ); + if ( vec.size() != key2groups->second.size()) + key2groups->second = vec; + + return & key2groups->second; +} + +//================================================================================ +/*! + * \brief Initialize ImportSource value by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ + +bool StdMeshers_ImportSource1D::SetParametersByMesh(const SMESH_Mesh*, const TopoDS_Shape&) +{ + return false; +} + +//================================================================================ +/*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ + +bool StdMeshers_ImportSource1D::SetParametersByDefaults(const TDefaults&, const SMESH_Mesh* ) +{ + return false; +} diff --git a/src/StdMeshers/StdMeshers_ImportSource.hxx b/src/StdMeshers/StdMeshers_ImportSource.hxx new file mode 100644 index 000000000..d3347de15 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ImportSource.hxx @@ -0,0 +1,99 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH StdMeshers : implementaion of SMESH idl descriptions +// File : StdMeshers_ImportSource1D.hxx +// Module : SMESH +// +#ifndef _StdMeshers_ImportSource_HXX_ +#define _StdMeshers_ImportSource_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_Hypothesis.hxx" +#include "Utils_SALOME_Exception.hxx" + +#include +#include + +class SMESH_Group; +class SMESHDS_Mesh; +class SMESH_subMesh; + +//============================================================================== +/*! + * \brief Stores groups to import elements from + */ +//============================================================================== + +class STDMESHERS_EXPORT StdMeshers_ImportSource1D : public SMESH_Hypothesis +{ + public: + StdMeshers_ImportSource1D(int hypId, int studyId, SMESH_Gen * gen); + virtual ~ StdMeshers_ImportSource1D(); + + void SetGroups(const std::vector& groups); + const std::vector& GetGroups() const; + + void SetCopySourceMesh(bool toCopyMesh, bool toCopyGroups); + void GetCopySourceMesh(bool& toCopyMesh, bool& toCopyGroups) const; + + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape); + virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0); + void RestoreGroups(const std::vector& groups); + + void StoreResultGroups(const std::vector& groups, + const SMESHDS_Mesh& srcMesh, + const SMESHDS_Mesh& tgtMesh); + std::vector* GetResultGroups(const SMESHDS_Mesh& srcMesh, + const SMESHDS_Mesh& tgtMesh); + + std::vector GetSourceMeshes() const; + std::vector GetSourceSubMeshes(const SMESH_Mesh* srcMesh) const; + +private: + + std::vector _groups; + bool _toCopyMesh, _toCopyGroups; + + // groups imported using this hypothesis + typedef std::map< std::pair, std::vector > TResGroupMap; + TResGroupMap _resultGroups; + std::vector _resultGroupsStorage; // persistent representation of _resultGroups + + void resultGroupsToIntVec(); +}; + +//============================================================================== +/*! + * \brief Redefines name and dimension of inherited StdMeshers_ImportSource1D + */ +//============================================================================== + +class STDMESHERS_EXPORT StdMeshers_ImportSource2D : public StdMeshers_ImportSource1D +{ + public: + StdMeshers_ImportSource2D(int hypId, int studyId, SMESH_Gen * gen); +}; +#endif diff --git a/src/StdMeshers/StdMeshers_Import_1D.cxx b/src/StdMeshers/StdMeshers_Import_1D.cxx new file mode 100644 index 000000000..b8a5076b8 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Import_1D.cxx @@ -0,0 +1,1055 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : StdMeshers_Import_1D.cxx +// Module : SMESH +// +#include "StdMeshers_Import_1D.hxx" +#include "StdMeshers_ImportSource.hxx" + +#include "SMDS_MeshElement.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMESHDS_Group.hxx" +#include "SMESHDS_Mesh.hxx" +#include "SMESH_Comment.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_HypoFilter.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_subMeshEventListener.hxx" + +#include "Utils_SALOME_Exception.hxx" +#include "utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +//============================================================================= +/*! + * Creates StdMeshers_Import_1D + */ +//============================================================================= + +StdMeshers_Import_1D::StdMeshers_Import_1D(int hypId, int studyId, SMESH_Gen * gen) + :SMESH_1D_Algo(hypId, studyId, gen), _sourceHyp(0) +{ + MESSAGE("StdMeshers_Import_1D::StdMeshers_Import_1D"); + _name = "Import_1D"; + _shapeType = (1 << TopAbs_EDGE); + + _compatibleHypothesis.push_back("ImportSource1D"); +} + +//============================================================================= +/*! + * Check presence of a hypothesis + */ +//============================================================================= + +bool StdMeshers_Import_1D::CheckHypothesis + (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus) +{ + _sourceHyp = 0; + + const list &hyps = GetUsedHypothesis(aMesh, aShape); + if ( hyps.size() == 0 ) + { + aStatus = SMESH_Hypothesis::HYP_MISSING; + return false; // can't work with no hypothesis + } + + if ( hyps.size() > 1 ) + { + aStatus = SMESH_Hypothesis::HYP_ALREADY_EXIST; + return false; + } + + const SMESHDS_Hypothesis *theHyp = hyps.front(); + + string hypName = theHyp->GetName(); + + if (hypName == _compatibleHypothesis.front()) + { + _sourceHyp = (StdMeshers_ImportSource1D *)theHyp; + aStatus = SMESH_Hypothesis::HYP_OK; + return true; + } + + aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE; + return true; +} + +//================================================================================ +namespace // INTERNAL STUFF +//================================================================================ +{ + int getSubmeshIDForCopiedMesh(const SMESHDS_Mesh* srcMeshDS, SMESH_Mesh* tgtMesh); + + enum _ListenerDataType + { + WAIT_HYP_MODIF=1, // data indicating awaiting for valid parameters of src hyp + LISTEN_SRC_MESH, // data storing submesh depending on source mesh state + SRC_HYP // data storing ImportSource hyp + }; + //================================================================================ + /*! + * \brief _ListenerData holding ImportSource hyp holding in its turn + * imported groups + */ + struct _ListenerData : public SMESH_subMeshEventListenerData + { + const StdMeshers_ImportSource1D* _srcHyp; + _ListenerData(const StdMeshers_ImportSource1D* h, _ListenerDataType type=SRC_HYP): + SMESH_subMeshEventListenerData(/*isDeletable=*/true), _srcHyp(h) + { + myType = type; + } + }; + //================================================================================ + /*! + * \brief Comparator of sub-meshes + */ + struct _SubLess + { + bool operator()(const SMESH_subMesh* sm1, const SMESH_subMesh* sm2 ) const + { + if ( sm1 == sm2 ) return false; + if ( !sm1 || !sm2 ) return sm1 < sm2; + const TopoDS_Shape& s1 = sm1->GetSubShape(); + const TopoDS_Shape& s2 = sm2->GetSubShape(); + TopAbs_ShapeEnum t1 = s1.IsNull() ? TopAbs_SHAPE : s1.ShapeType(); + TopAbs_ShapeEnum t2 = s2.IsNull() ? TopAbs_SHAPE : s2.ShapeType(); + if ( t1 == t2) + return (sm1 < sm2); + return t1 < t2; // to have: face < edge + } + }; + //================================================================================ + /*! + * \brief Container of data dedicated to one source mesh + */ + struct _ImportData + { + const SMESH_Mesh* _srcMesh; + StdMeshers_Import_1D::TNodeNodeMap _n2n; + StdMeshers_Import_1D::TElemElemMap _e2e; + + set< SMESH_subMesh*, _SubLess > _subM; // submeshes relating to this srcMesh + set< SMESH_subMesh*, _SubLess > _copyMeshSubM; // submeshes requesting mesh copying + set< SMESH_subMesh*, _SubLess > _copyGroupSubM; // submeshes requesting group copying + set< SMESH_subMesh*, _SubLess > _computedSubM; + + SMESHDS_SubMesh* _importMeshSubDS; // submesh storing a copy of _srcMesh + int _importMeshSubID; // id of _importMeshSubDS + + _ImportData(const SMESH_Mesh* srcMesh=0): + _srcMesh(srcMesh), _importMeshSubDS(0),_importMeshSubID(-1) {} + + void removeImportedMesh( SMESHDS_Mesh* meshDS ) + { + if ( !_importMeshSubDS ) return; + SMDS_ElemIteratorPtr eIt = _importMeshSubDS->GetElements(); + while ( eIt->more() ) + meshDS->RemoveFreeElement( eIt->next(), _importMeshSubDS, /*fromGroups=*/false ); + SMDS_NodeIteratorPtr nIt = _importMeshSubDS->GetNodes(); + while ( nIt->more() ) + meshDS->RemoveFreeNode( nIt->next(), _importMeshSubDS, /*fromGroups=*/false ); + _n2n.clear(); + _e2e.clear(); + } + void removeGroups( SMESH_subMesh* subM, const StdMeshers_ImportSource1D* srcHyp ) + { + if ( !srcHyp ) return; + SMESH_Mesh* tgtMesh = subM->GetFather(); + const SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS(); + const SMESHDS_Mesh* srcMeshDS = _srcMesh->GetMeshDS(); + vector* groups = + const_cast(srcHyp)->GetResultGroups(*srcMeshDS,*tgtMeshDS); + if ( groups ) + { + for ( unsigned i = 0; i < groups->size(); ++i ) + tgtMesh->RemoveGroup( groups->at(i)->GetGroupDS()->GetID() ); + groups->clear(); + } + } + void trackHypParams( SMESH_subMesh* sm, const StdMeshers_ImportSource1D* srcHyp ) + { + if ( !srcHyp ) return; + bool toCopyMesh, toCopyGroups; + srcHyp->GetCopySourceMesh(toCopyMesh, toCopyGroups); + + if ( toCopyMesh )_copyMeshSubM.insert( sm ); + else _copyMeshSubM.erase( sm ); + + if ( toCopyGroups ) _copyGroupSubM.insert( sm ); + else _copyGroupSubM.erase( sm ); + } + void addComputed( SMESH_subMesh* sm ) + { + SMESH_subMeshIteratorPtr smIt = sm->getDependsOnIterator(/*includeSelf=*/true, + /*complexShapeFirst=*/true); + while ( smIt->more() ) + { + sm = smIt->next(); + switch ( sm->GetSubShape().ShapeType() ) + { + case TopAbs_EDGE: + case TopAbs_FACE: + _subM.insert( sm ); + if ( !sm->IsEmpty() ) + _computedSubM.insert( sm ); + case TopAbs_VERTEX: + break; + default:; + } + } + } + }; + //================================================================================ + /*! + * Listener notified on events relating to imported submesh + */ + class _Listener : public SMESH_subMeshEventListener + { + typedef map< SMESH_Mesh*, list< _ImportData > > TMesh2ImpData; + TMesh2ImpData _tgtMesh2ImportData; + + _Listener():SMESH_subMeshEventListener(/*isDeletable=*/false, + "StdMeshers_Import_1D::_Listener") {} + + public: + // return poiter to a static listener + static _Listener* get() { static _Listener theListener; return &theListener; } + + static _ImportData* getImportData(const SMESH_Mesh* srcMesh, SMESH_Mesh* tgtMesh); + + static void storeImportSubmesh(SMESH_subMesh* importSub, + const SMESH_Mesh* srcMesh, + const StdMeshers_ImportSource1D* srcHyp); + + virtual void ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* subMesh, + SMESH_subMeshEventListenerData* data, + const SMESH_Hypothesis* hyp); + void removeSubmesh( SMESH_subMesh* sm, _ListenerData* data ); + void clearSubmesh ( SMESH_subMesh* sm, _ListenerData* data, bool clearAllSub ); + + // mark sm as missing src hyp with valid groups + static void waitHypModification(SMESH_subMesh* sm) + { + sm->SetEventListener + (get(), SMESH_subMeshEventListenerData::MakeData( sm, WAIT_HYP_MODIF ), sm); + } + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Find or create ImportData for given meshes + */ + _ImportData* _Listener::getImportData(const SMESH_Mesh* srcMesh, + SMESH_Mesh* tgtMesh) + { + list< _ImportData >& dList = get()->_tgtMesh2ImportData[tgtMesh]; + list< _ImportData >::iterator d = dList.begin(); + for ( ; d != dList.end(); ++d ) + if ( d->_srcMesh == srcMesh ) + return &*d; + dList.push_back(_ImportData(srcMesh)); + return &dList.back(); + } + + //-------------------------------------------------------------------------------- + /*! + * \brief Remember an imported sub-mesh and set needed even listeners + * \param importSub - submesh computed by Import algo + * \param srcMesh - source mesh + * \param srcHyp - ImportSource hypothesis + */ + void _Listener::storeImportSubmesh(SMESH_subMesh* importSub, + const SMESH_Mesh* srcMesh, + const StdMeshers_ImportSource1D* srcHyp) + { + // set listener to hear events of the submesh computed by "Import" algo + importSub->SetEventListener( get(), new _ListenerData(srcHyp), importSub ); + + // set listeners to hear events of the source mesh + SMESH_subMesh* smToNotify = importSub; + vector smToListen = srcHyp->GetSourceSubMeshes( srcMesh ); + for ( size_t i = 0; i < smToListen.size(); ++i ) + { + SMESH_subMeshEventListenerData* data = new _ListenerData(srcHyp, LISTEN_SRC_MESH); + data->mySubMeshes.push_back( smToNotify ); + importSub->SetEventListener( get(), data, smToListen[i] ); + } + // remember the submesh importSub and its sub-submeshes + _ImportData* iData = _Listener::getImportData( srcMesh, importSub->GetFather()); + iData->trackHypParams( importSub, srcHyp ); + iData->addComputed( importSub ); + if ( !iData->_copyMeshSubM.empty() && iData->_importMeshSubID < 1 ) + { + SMESH_Mesh* tgtMesh = importSub->GetFather(); + iData->_importMeshSubID = getSubmeshIDForCopiedMesh( srcMesh->GetMeshDS(),tgtMesh); + iData->_importMeshSubDS = tgtMesh->GetMeshDS()->NewSubMesh( iData->_importMeshSubID ); + } + } + //-------------------------------------------------------------------------------- + /*! + * \brief Remove imported mesh and/or groups if needed + * \param sm - submesh loosing Import algo + * \param data - data holding imported groups + */ + void _Listener::removeSubmesh( SMESH_subMesh* sm, _ListenerData* data ) + { + list< _ImportData > & dList = _tgtMesh2ImportData[ sm->GetFather() ]; + list< _ImportData >::iterator d = dList.begin(); + for ( ; d != dList.end(); ++d ) + if ( (*d)._subM.erase( sm )) + { + d->_computedSubM.erase( sm ); + bool rmMesh = d->_copyMeshSubM.erase( sm ) && d->_copyMeshSubM.empty(); + bool rmGroups = (d->_copyGroupSubM.erase( sm ) && d->_copyGroupSubM.empty()) || rmMesh; + if ( rmMesh ) + d->removeImportedMesh( sm->GetFather()->GetMeshDS() ); + if ( rmGroups && data ) + d->removeGroups( sm, data->_srcHyp ); + } + } + //-------------------------------------------------------------------------------- + /*! + * \brief Clear submeshes and remove imported mesh and/or groups if necessary + * \param sm - cleared submesh + * \param data - data holding imported groups + */ + void _Listener::clearSubmesh(SMESH_subMesh* sm, _ListenerData* data, bool clearAllSub) + { + list< _ImportData > & dList = _tgtMesh2ImportData[ sm->GetFather() ]; + list< _ImportData >::iterator d = dList.begin(); + for ( ; d != dList.end(); ++d ) + { + if ( !d->_subM.count( sm )) continue; + if ( (*d)._computedSubM.erase( sm ) ) + { + bool copyMesh = !d->_copyMeshSubM.empty(); + if ( copyMesh || clearAllSub ) + { + // remove imported mesh and groups + d->removeImportedMesh( sm->GetFather()->GetMeshDS() ); + + if ( data ) + d->removeGroups( sm, data->_srcHyp ); + + // clear the rest submeshes + if ( !d->_computedSubM.empty() ) + { + d->_computedSubM.clear(); + set< SMESH_subMesh*, _SubLess>::iterator sub = d->_subM.begin(); + for ( ; sub != d->_subM.end(); ++sub ) + { + SMESH_subMesh* subM = *sub; + _ListenerData* hypData = (_ListenerData*) subM->GetEventListenerData( get() ); + if ( hypData ) + d->removeGroups( sm, hypData->_srcHyp ); + + subM->ComputeStateEngine( SMESH_subMesh::CLEAN ); + if ( subM->GetSubShape().ShapeType() == TopAbs_FACE ) + subM->ComputeSubMeshStateEngine( SMESH_subMesh::CLEAN ); + } + } + } + sm->ComputeStateEngine( SMESH_subMesh::CLEAN ); + if ( sm->GetSubShape().ShapeType() == TopAbs_FACE ) + sm->ComputeSubMeshStateEngine( SMESH_subMesh::CLEAN ); + } + if ( data ) + d->trackHypParams( sm, data->_srcHyp ); + d->_n2n.clear(); + d->_e2e.clear(); + } + } + //-------------------------------------------------------------------------------- + /*! + * \brief Remove imported mesh and/or groups + */ + void _Listener::ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* subMesh, + SMESH_subMeshEventListenerData* data, + const SMESH_Hypothesis* /*hyp*/) + { + if ( data && data->myType == WAIT_HYP_MODIF ) + { + // event of Import submesh + if ( SMESH_subMesh::MODIF_HYP == event && + SMESH_subMesh::ALGO_EVENT == eventType ) + { + // re-call SetEventListener() to take into account valid parameters + // of ImportSource hypothesis + if ( SMESH_Algo* algo = subMesh->GetAlgo() ) + algo->SetEventListener( subMesh ); + } + } + else if ( data && data->myType == LISTEN_SRC_MESH ) + { + // event of source mesh + if ( SMESH_subMesh::COMPUTE_EVENT == eventType ) + { + switch ( event ) { + case SMESH_subMesh::CLEAN: + // source mesh cleaned -> clean target mesh + clearSubmesh( data->mySubMeshes.front(), (_ListenerData*) data, /*all=*/true ); + break; + case SMESH_subMesh::SUBMESH_COMPUTED: { + // source mesh computed -> reset FAILED state of Import submeshes to + // READY_TO_COMPUTE + SMESH_Mesh* srcMesh = subMesh->GetFather(); + if ( srcMesh->NbEdges() > 0 || srcMesh->NbFaces() > 0 ) + { + SMESH_Mesh* m = data->mySubMeshes.front()->GetFather(); + if ( SMESH_subMesh* sm1 = m->GetSubMeshContaining(1)) + { + sm1->ComputeStateEngine(SMESH_subMesh::SUBMESH_COMPUTED ); + sm1->ComputeSubMeshStateEngine( SMESH_subMesh::SUBMESH_COMPUTED ); + } + } + break; + } + default:; + } + } + } + else // event of Import submesh + { + // find out what happens: import hyp modified or removed + bool removeImport = false, modifHyp = false; + if ( SMESH_subMesh::ALGO_EVENT == eventType ) + modifHyp = true; + if ( subMesh->GetAlgoState() != SMESH_subMesh::HYP_OK ) + { + removeImport = true; + } + else if (( SMESH_subMesh::REMOVE_ALGO == event || + SMESH_subMesh::REMOVE_FATHER_ALGO == event ) && + SMESH_subMesh::ALGO_EVENT == eventType ) + { + SMESH_Algo* algo = subMesh->GetAlgo(); + removeImport = ( strncmp( "Import", algo->GetName(), 6 ) != 0 ); + } + + if ( removeImport ) + { + // treate removal of Import algo from subMesh + removeSubmesh( subMesh, (_ListenerData*) data ); + } + else if ( modifHyp || + ( SMESH_subMesh::CLEAN == event && + SMESH_subMesh::COMPUTE_EVENT == eventType)) + { + // treate modification of ImportSource hypothesis + clearSubmesh( subMesh, (_ListenerData*) data, /*all=*/false ); + } + else if ( SMESH_subMesh::CHECK_COMPUTE_STATE == event && + SMESH_subMesh::COMPUTE_EVENT == eventType ) + { + // check compute state of all submeshes impoting from same src mesh; + // this is to take into account 1D computed submeshes hidden by 2D import algo; + // else source mesh is not copied as _subM.size != _computedSubM.size() + list< _ImportData > & dList = _tgtMesh2ImportData[ subMesh->GetFather() ]; + list< _ImportData >::iterator d = dList.begin(); + for ( ; d != dList.end(); ++d ) + if ( d->_subM.count( subMesh )) + { + set::iterator smIt = d->_subM.begin(); + for( ; smIt != d->_subM.end(); ++smIt ) + if ( (*smIt)->IsMeshComputed() ) + d->_computedSubM.insert( *smIt); + } + } + } + } + + //================================================================================ + /*! + * \brief Return an ID of submesh to store nodes and elements of a copied mesh + */ + //================================================================================ + + int getSubmeshIDForCopiedMesh(const SMESHDS_Mesh* srcMeshDS, + SMESH_Mesh* tgtMesh) + { + // To get SMESH_subMesh corresponding to srcMeshDS we need to have a shape + // for which SMESHDS_Mesh::IsGroupOfSubShapes() returns true. + // And this shape must be different from sub-shapes of the main shape. + // So we create a compound containing + // 1) some sub-shapes of SMESH_Mesh::PseudoShape() corresponding to + // srcMeshDS->GetPersistentId() + // 2) the 1-st vertex of the main shape to assure + // SMESHDS_Mesh::IsGroupOfSubShapes(shape)==true + TopoDS_Shape shapeForSrcMesh; + TopTools_IndexedMapOfShape pseudoSubShapes; + TopExp::MapShapes( SMESH_Mesh::PseudoShape(), pseudoSubShapes ); + + // index of pseudoSubShapes corresponding to srcMeshDS + int subIndex = srcMeshDS->GetPersistentId() % pseudoSubShapes.Extent(); + int nbSubShapes = 1 + srcMeshDS->GetPersistentId() / pseudoSubShapes.Extent(); + + // try to find already present shapeForSrcMesh + SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS(); + for ( int i = tgtMeshDS->MaxShapeIndex(); i > 0 && shapeForSrcMesh.IsNull(); --i ) + { + const TopoDS_Shape& s = tgtMeshDS->IndexToShape(i); + if ( s.ShapeType() != TopAbs_COMPOUND ) break; + TopoDS_Iterator sSubIt( s ); + for ( int iSub = 0; iSub < nbSubShapes && sSubIt.More(); ++iSub, sSubIt.Next() ) + if ( pseudoSubShapes( subIndex+iSub ).IsSame( sSubIt.Value())) + if ( iSub+1 == nbSubShapes ) + { + shapeForSrcMesh = s; + break; + } + } + if ( shapeForSrcMesh.IsNull() ) + { + // make a new shapeForSrcMesh + BRep_Builder aBuilder; + TopoDS_Compound comp; + aBuilder.MakeCompound( comp ); + shapeForSrcMesh = comp; + for ( int iSub = 0; iSub < nbSubShapes; ++iSub ) + aBuilder.Add( comp, pseudoSubShapes( subIndex+iSub )); + TopExp_Explorer vExp( tgtMeshDS->ShapeToMesh(), TopAbs_VERTEX ); + aBuilder.Add( comp, vExp.Current() ); + } + SMESH_subMesh* sm = tgtMesh->GetSubMesh( shapeForSrcMesh ); + SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); + if ( !smDS ) + smDS = tgtMeshDS->NewSubMesh( sm->GetId() ); + + // make ordinary submesh from a complex one + if ( smDS->IsComplexSubmesh() ) + { + list< const SMESHDS_SubMesh* > subSM; + SMESHDS_SubMeshIteratorPtr smIt = smDS->GetSubMeshIterator(); + while ( smIt->more() ) subSM.push_back( smIt->next() ); + list< const SMESHDS_SubMesh* >::iterator sub = subSM.begin(); + for ( ; sub != subSM.end(); ++sub) + smDS->RemoveSubMesh( *sub ); + } + return sm->GetId(); + } + + //================================================================================ + /*! + * \brief Return a submesh to store nodes and elements of a copied mesh + * and set event listeners in order to clear + * imported mesh and groups as soon as submesh state requires it + */ + //================================================================================ + + SMESHDS_SubMesh* getSubmeshForCopiedMesh(const SMESH_Mesh* srcMesh, + SMESH_Mesh* tgtMesh, + const TopoDS_Shape& tgtShape, + StdMeshers_Import_1D::TNodeNodeMap*& n2n, + StdMeshers_Import_1D::TElemElemMap*& e2e, + bool & toCopyGroups) + { + StdMeshers_Import_1D::getMaps( srcMesh, tgtMesh, n2n,e2e ); + + _ImportData* iData = _Listener::getImportData(srcMesh,tgtMesh); + + SMESH_subMesh* importedSM = tgtMesh->GetSubMesh( tgtShape ); + iData->addComputed( importedSM ); + if ( iData->_computedSubM.size() != iData->_subM.size() ) + return 0; // not all submeshes computed yet + + toCopyGroups = !iData->_copyGroupSubM.empty(); + + if ( !iData->_copyMeshSubM.empty()) + { + // make submesh to store a copied mesh + int smID = getSubmeshIDForCopiedMesh( srcMesh->GetMeshDS(), tgtMesh ); + SMESHDS_SubMesh* subDS = tgtMesh->GetMeshDS()->NewSubMesh( smID ); + + iData->_importMeshSubID = smID; + iData->_importMeshSubDS = subDS; + return subDS; + } + return 0; + } + +} // namespace + + +//============================================================================= +/*! + * Import elements from the other mesh + */ +//============================================================================= + +bool StdMeshers_Import_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & theShape) +{ + if ( !_sourceHyp ) return false; + + const vector& srcGroups = _sourceHyp->GetGroups(); + if ( srcGroups.empty() ) + return error("Invalid source groups"); + + SMESH_MesherHelper helper(theMesh); + helper.SetSubShape(theShape); + SMESHDS_Mesh* tgtMesh = theMesh.GetMeshDS(); + + const TopoDS_Edge& geomEdge = TopoDS::Edge( theShape ); + const double edgeTol = BRep_Tool::Tolerance( geomEdge ); + const int shapeID = tgtMesh->ShapeToIndex( geomEdge ); + + set subShapeIDs; + subShapeIDs.insert( shapeID ); + + // get nodes on vertices + list < SMESH_TNodeXYZ > vertexNodes; + list < SMESH_TNodeXYZ >::iterator vNIt; + TopExp_Explorer vExp( theShape, TopAbs_VERTEX ); + for ( ; vExp.More(); vExp.Next() ) + { + const TopoDS_Vertex& v = TopoDS::Vertex( vExp.Current() ); + if ( !subShapeIDs.insert( tgtMesh->ShapeToIndex( v )).second ) + continue; // closed edge + const SMDS_MeshNode* n = SMESH_Algo::VertexNode( v, tgtMesh ); + if ( !n ) + { + _gen->Compute(theMesh,v,/*anUpward=*/true); + n = SMESH_Algo::VertexNode( v, tgtMesh ); + if ( !n ) return false; // very strange + } + vertexNodes.push_back( SMESH_TNodeXYZ( n )); + } + + // import edges from groups + TNodeNodeMap* n2n; + TElemElemMap* e2e; + for ( int iG = 0; iG < srcGroups.size(); ++iG ) + { + const SMESHDS_GroupBase* srcGroup = srcGroups[iG]->GetGroupDS(); + + const int meshID = srcGroup->GetMesh()->GetPersistentId(); + const SMESH_Mesh* srcMesh = GetMeshByPersistentID( meshID ); + if ( !srcMesh ) continue; + getMaps( srcMesh, &theMesh, n2n, e2e ); + + SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); + vector newNodes; + SMDS_MeshNode *tmpNode = helper.AddNode(0,0,0); + double u; + while ( srcElems->more() ) // loop on group contents + { + const SMDS_MeshElement* edge = srcElems->next(); + // find or create nodes of a new edge + newNodes.resize( edge->NbNodes() ); + newNodes.back() = 0; + SMDS_MeshElement::iterator node = edge->begin_nodes(); + for ( unsigned i = 0; i < newNodes.size(); ++i, ++node ) + { + TNodeNodeMap::iterator n2nIt = n2n->insert( make_pair( *node, (SMDS_MeshNode*)0 )).first; + if ( n2nIt->second ) + { + if ( !subShapeIDs.count( n2nIt->second->getshapeId() )) + break; + } + else + { + // find an existing vertex node + for ( vNIt = vertexNodes.begin(); vNIt != vertexNodes.end(); ++vNIt) + if ( vNIt->SquareDistance( *node ) < 10 * edgeTol * edgeTol) + { + (*n2nIt).second = vNIt->_node; + vertexNodes.erase( vNIt ); + break; + } + } + if ( !n2nIt->second ) + { + // find out if node lies on theShape + tmpNode->setXYZ( (*node)->X(), (*node)->Y(), (*node)->Z()); + if ( helper.CheckNodeU( geomEdge, tmpNode, u, 10 * edgeTol, /*force=*/true )) + { + SMDS_MeshNode* newNode = tgtMesh->AddNode( (*node)->X(), (*node)->Y(), (*node)->Z()); + n2nIt->second = newNode; + tgtMesh->SetNodeOnEdge( newNode, shapeID, u ); + } + } + if ( !(newNodes[i] = n2nIt->second )) + break; + } + if ( !newNodes.back() ) + continue; // not all nodes of edge lie on theShape + + // make a new edge + SMDS_MeshElement * newEdge; + if ( newNodes.size() == 3 ) + newEdge = tgtMesh->AddEdge( newNodes[0], newNodes[1], newNodes[2] ); + else + newEdge = tgtMesh->AddEdge( newNodes[0], newNodes[1]); + tgtMesh->SetMeshElementOnShape( newEdge, shapeID ); + e2e->insert( make_pair( edge, newEdge )); + } + helper.GetMeshDS()->RemoveNode(tmpNode); + } + if ( n2n->empty()) + return error("Empty source groups"); + + // check if the whole geom edge is covered by imported segments; + // the check consist in passing by segments from one vetrex node to another + bool isEdgeMeshed = false; + if ( SMESHDS_SubMesh* tgtSM = tgtMesh->MeshElements( theShape )) + { + const TopoDS_Vertex& v = ( vExp.ReInit(), TopoDS::Vertex( vExp.Current() )); + const SMDS_MeshNode* n = SMESH_Algo::VertexNode( v, tgtMesh ); + const SMDS_MeshElement* seg = 0; + SMDS_ElemIteratorPtr segIt = n->GetInverseElementIterator(SMDSAbs_Edge); + while ( segIt->more() && !seg ) + if ( !tgtSM->Contains( seg = segIt->next())) + seg = 0; + int nbPassedSegs = 0; + while ( seg ) + { + ++nbPassedSegs; + const SMDS_MeshNode* n2 = seg->GetNode(0); + n = ( n2 == n ? seg->GetNode(1) : n2 ); + if ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + break; + const SMDS_MeshElement* seg2 = 0; + segIt = n->GetInverseElementIterator(SMDSAbs_Edge); + while ( segIt->more() && !seg2 ) + if ( seg == ( seg2 = segIt->next())) + seg2 = 0; + seg = seg2; + } + if (nbPassedSegs > 0 && tgtSM->NbElements() > nbPassedSegs ) + return error( "Source elements overlap one another"); + + isEdgeMeshed = ( tgtSM->NbElements() == nbPassedSegs && + n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ); + } + if ( !isEdgeMeshed ) + return error( "Source elements don't cover totally the geometrical edge" ); + + // copy meshes + vector srcMeshes = _sourceHyp->GetSourceMeshes(); + for ( unsigned i = 0; i < srcMeshes.size(); ++i ) + importMesh( srcMeshes[i], theMesh, _sourceHyp, theShape ); + + return true; +} + +//================================================================================ +/*! + * \brief Copy mesh and groups + */ +//================================================================================ + +void StdMeshers_Import_1D::importMesh(const SMESH_Mesh* srcMesh, + SMESH_Mesh & tgtMesh, + StdMeshers_ImportSource1D* srcHyp, + const TopoDS_Shape& tgtShape) +{ + // get submesh to store the imported mesh + TNodeNodeMap* n2n; + TElemElemMap* e2e; + bool toCopyGroups; + SMESHDS_SubMesh* tgtSubMesh = + getSubmeshForCopiedMesh( srcMesh, &tgtMesh, tgtShape, n2n, e2e, toCopyGroups ); + if ( !tgtSubMesh || tgtSubMesh->NbNodes() + tgtSubMesh->NbElements() > 0 ) + return; // not to copy srcMeshDS twice + + SMESHDS_Mesh* tgtMeshDS = tgtMesh.GetMeshDS(); + SMESH_MeshEditor additor( &tgtMesh ); + + // 1. Copy mesh + + vector newNodes; + const SMESHDS_Mesh* srcMeshDS = srcMesh->GetMeshDS(); + SMDS_ElemIteratorPtr eIt = srcMeshDS->elementsIterator(); + while ( eIt->more() ) + { + const SMDS_MeshElement* elem = eIt->next(); + TElemElemMap::iterator e2eIt = e2e->insert( make_pair( elem, (SMDS_MeshElement*)0 )).first; + if ( e2eIt->second ) continue; // already copied by Compute() + newNodes.resize( elem->NbNodes() ); + SMDS_MeshElement::iterator node = elem->begin_nodes(); + for ( unsigned i = 0; i < newNodes.size(); ++i, ++node ) + { + TNodeNodeMap::iterator n2nIt = n2n->insert( make_pair( *node, (SMDS_MeshNode*)0 )).first; + if ( !n2nIt->second ) + { + (*n2nIt).second = tgtMeshDS->AddNode( (*node)->X(), (*node)->Y(), (*node)->Z()); + tgtSubMesh->AddNode( n2nIt->second ); + } + newNodes[i] = n2nIt->second; + } + const SMDS_MeshElement* newElem = + tgtMeshDS->FindElement( newNodes, elem->GetType(), /*noMedium=*/false ); + if ( !newElem ) + { + newElem = additor.AddElement( newNodes, elem->GetType(), elem->IsPoly()); + tgtSubMesh->AddElement( newElem ); + } + if ( toCopyGroups ) + (*e2eIt).second = newElem; + } + // copy free nodes + if ( srcMeshDS->NbNodes() > n2n->size() ) + { + SMDS_NodeIteratorPtr nIt = srcMeshDS->nodesIterator(); + while( nIt->more() ) + { + const SMDS_MeshNode* node = nIt->next(); + if ( node->NbInverseElements() == 0 ) + { + const SMDS_MeshNode* newNode = tgtMeshDS->AddNode( node->X(), node->Y(), node->Z()); + n2n->insert( make_pair( node, newNode )); + tgtSubMesh->AddNode( newNode ); + } + } + } + + // 2. Copy groups + + vector resultGroups; + if ( toCopyGroups ) + { + // collect names of existing groups to assure uniqueness of group names within a type + map< SMDSAbs_ElementType, set > namesByType; + SMESH_Mesh::GroupIteratorPtr groupIt = tgtMesh.GetGroups(); + while ( groupIt->more() ) + { + SMESH_Group* tgtGroup = groupIt->next(); + namesByType[ tgtGroup->GetGroupDS()->GetType() ].insert( tgtGroup->GetName() ); + } + if (srcMesh) + { + SMESH_Mesh::GroupIteratorPtr groupIt = srcMesh->GetGroups(); + while ( groupIt->more() ) + { + SMESH_Group* srcGroup = groupIt->next(); + SMESHDS_GroupBase* srcGroupDS = srcGroup->GetGroupDS(); + string name = srcGroup->GetName(); + int nb = 1; + while ( !namesByType[ srcGroupDS->GetType() ].insert( name ).second ) + name = SMESH_Comment(srcGroup->GetName()) << "_imported_" << nb++; + SMESH_Group* newGroup = tgtMesh.AddGroup( srcGroupDS->GetType(), name.c_str(), nb ); + SMESHDS_Group* newGroupDS = (SMESHDS_Group*)newGroup->GetGroupDS(); + resultGroups.push_back( newGroup ); + + eIt = srcGroupDS->GetElements(); + if ( srcGroupDS->GetType() == SMDSAbs_Node ) + while (eIt->more()) + { + TNodeNodeMap::iterator n2nIt = n2n->find((const SMDS_MeshNode*) eIt->next() ); + if ( n2nIt != n2n->end() && n2nIt->second ) + newGroupDS->SMDSGroup().Add((*n2nIt).second ); + } + else + while (eIt->more()) + { + TElemElemMap::iterator e2eIt = e2e->find( eIt->next() ); + if ( e2eIt != e2e->end() && e2eIt->second ) + newGroupDS->SMDSGroup().Add((*e2eIt).second ); + } + } + } + } + n2n->clear(); + e2e->clear(); + + // Remember created groups in order to remove them as soon as the srcHyp is + // modified or something other similar happens. This imformation must be persistent, + // for that store them in a hypothesis as it stores its values in the file anyway + srcHyp->StoreResultGroups( resultGroups, *srcMeshDS, *tgtMeshDS ); +} + +//============================================================================= +/*! + * \brief Set needed event listeners and create a submesh for a copied mesh + * + * This method is called only if a submesh has HYP_OK algo_state. + */ +//============================================================================= + +void StdMeshers_Import_1D::setEventListener(SMESH_subMesh* subMesh, + StdMeshers_ImportSource1D* sourceHyp) +{ + if ( sourceHyp ) + { + vector srcMeshes = sourceHyp->GetSourceMeshes(); + if ( srcMeshes.empty() ) + _Listener::waitHypModification( subMesh ); + for ( unsigned i = 0; i < srcMeshes.size(); ++i ) + // set a listener to remove the imported mesh and groups + _Listener::storeImportSubmesh( subMesh, srcMeshes[i], sourceHyp ); + } +} +void StdMeshers_Import_1D::SetEventListener(SMESH_subMesh* subMesh) +{ + if ( !_sourceHyp ) + { + const TopoDS_Shape& tgtShape = subMesh->GetSubShape(); + SMESH_Mesh* tgtMesh = subMesh->GetFather(); + Hypothesis_Status aStatus; + CheckHypothesis( *tgtMesh, tgtShape, aStatus ); + } + setEventListener( subMesh, _sourceHyp ); +} + +void StdMeshers_Import_1D::SubmeshRestored(SMESH_subMesh* subMesh) +{ + SetEventListener(subMesh); +} + +//============================================================================= +/*! + * Predict nb of mesh entities created by Compute() + */ +//============================================================================= + +bool StdMeshers_Import_1D::Evaluate(SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape, + MapShapeNbElems& aResMap) +{ + if ( !_sourceHyp ) return false; + + const vector& srcGroups = _sourceHyp->GetGroups(); + if ( srcGroups.empty() ) + return error("Invalid source groups"); + + vector aVec(SMDSEntity_Last,0); + + bool toCopyMesh, toCopyGroups; + _sourceHyp->GetCopySourceMesh(toCopyMesh, toCopyGroups); + if ( toCopyMesh ) // the whole mesh is copied + { + vector srcMeshes = _sourceHyp->GetSourceMeshes(); + for ( unsigned i = 0; i < srcMeshes.size(); ++i ) + { + SMESH_subMesh* sm = getSubMeshOfCopiedMesh( theMesh, *srcMeshes[i]); + if ( !sm || aResMap.count( sm )) continue; // already counted + aVec.assign( SMDSEntity_Last, 0); + const SMDS_MeshInfo& aMeshInfo = srcMeshes[i]->GetMeshDS()->GetMeshInfo(); + for (int i = 0; i < SMDSEntity_Last; i++) + aVec[i] = aMeshInfo.NbEntities((SMDSAbs_EntityType)i); + } + } + else + { + SMESH_MesherHelper helper(theMesh); + + const TopoDS_Edge& geomEdge = TopoDS::Edge( theShape ); + const double edgeTol = helper.MaxTolerance( geomEdge ); + + // take into account nodes on vertices + TopExp_Explorer vExp( theShape, TopAbs_VERTEX ); + for ( ; vExp.More(); vExp.Next() ) + theMesh.GetSubMesh( vExp.Current())->Evaluate( aResMap ); + + // count edges imported from groups + int nbEdges = 0, nbQuadEdges = 0; + for ( int iG = 0; iG < srcGroups.size(); ++iG ) + { + const SMESHDS_GroupBase* srcGroup = srcGroups[iG]->GetGroupDS(); + SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); + SMDS_MeshNode *tmpNode = helper.AddNode(0,0,0); + while ( srcElems->more() ) // loop on group contents + { + const SMDS_MeshElement* edge = srcElems->next(); + // find out if edge is located on geomEdge by projecting + // a middle of edge to geomEdge + SMESH_TNodeXYZ p1( edge->GetNode(0)); + SMESH_TNodeXYZ p2( edge->GetNode(1)); + gp_XYZ middle = ( p1 + p2 ) / 2.; + tmpNode->setXYZ( middle.X(), middle.Y(), middle.Z()); + double u = 0; + if ( helper.CheckNodeU( geomEdge, tmpNode, u, 10 * edgeTol, /*force=*/true )) + ++( edge->IsQuadratic() ? nbQuadEdges : nbEdges); + } + helper.GetMeshDS()->RemoveNode(tmpNode); + } + + int nbNodes = nbEdges + 2 * nbQuadEdges - 1; + + aVec[SMDSEntity_Node ] = nbNodes; + aVec[SMDSEntity_Edge ] = nbEdges; + aVec[SMDSEntity_Quad_Edge] = nbQuadEdges; + } + + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(make_pair(sm,aVec)); + + return true; +} + +//================================================================================ +/*! + * \brief Return node-node and element-element maps for import of geiven source mesh + */ +//================================================================================ + +void StdMeshers_Import_1D::getMaps(const SMESH_Mesh* srcMesh, + SMESH_Mesh* tgtMesh, + TNodeNodeMap*& n2n, + TElemElemMap*& e2e) +{ + _ImportData* iData = _Listener::getImportData(srcMesh,tgtMesh); + n2n = &iData->_n2n; + e2e = &iData->_e2e; + if ( iData->_copyMeshSubM.empty() ) + { + n2n->clear(); + e2e->clear(); + } +} + +//================================================================================ +/*! + * \brief Return submesh corresponding to the copied mesh + */ +//================================================================================ + +SMESH_subMesh* StdMeshers_Import_1D::getSubMeshOfCopiedMesh( SMESH_Mesh& tgtMesh, + SMESH_Mesh& srcMesh ) +{ + _ImportData* iData = _Listener::getImportData(&srcMesh,&tgtMesh); + if ( iData->_copyMeshSubM.empty() ) return 0; + SMESH_subMesh* sm = tgtMesh.GetSubMeshContaining( iData->_importMeshSubID ); + return sm; +} + diff --git a/src/StdMeshers/StdMeshers_Import_1D.hxx b/src/StdMeshers/StdMeshers_Import_1D.hxx new file mode 100644 index 000000000..b71b90564 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Import_1D.hxx @@ -0,0 +1,81 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// Module : SMESH +// +#ifndef _SMESH_Import_1D_HXX_ +#define _SMESH_Import_1D_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_1D_Algo.hxx" +#include "SMDS_MeshElement.hxx" + +class StdMeshers_ImportSource1D; + +/*! + * \brief Copy elements from other the mesh + */ +class STDMESHERS_EXPORT StdMeshers_Import_1D: public SMESH_1D_Algo +{ +public: + StdMeshers_Import_1D(int hypId, int studyId, SMESH_Gen* gen); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus); + + virtual bool Compute (SMESH_Mesh & aMesh, const TopoDS_Shape & aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + + virtual void SetEventListener(SMESH_subMesh* subMesh); + virtual void SubmeshRestored(SMESH_subMesh* subMesh); + + // internal utilities + + typedef std::map TNodeNodeMap; + typedef std::map TElemElemMap; + + static void getMaps(const SMESH_Mesh* srcMesh, + SMESH_Mesh* tgtMesh, + TNodeNodeMap*& n2n, + TElemElemMap*& e2e); + + static void importMesh(const SMESH_Mesh* srcMesh, + SMESH_Mesh & tgtMesh, + StdMeshers_ImportSource1D* srcHyp, + const TopoDS_Shape& tgtShape); + + static void setEventListener( SMESH_subMesh* subMesh, + StdMeshers_ImportSource1D* sourceHyp ); + + static SMESH_subMesh* getSubMeshOfCopiedMesh( SMESH_Mesh& tgtMesh, + SMESH_Mesh& srcMesh ); + + private: + + StdMeshers_ImportSource1D* _sourceHyp; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_Import_1D2D.cxx b/src/StdMeshers/StdMeshers_Import_1D2D.cxx new file mode 100644 index 000000000..8180f0a02 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Import_1D2D.cxx @@ -0,0 +1,778 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : StdMeshers_Import_1D2D.cxx +// Module : SMESH +// +#include "StdMeshers_Import_1D2D.hxx" + +#include "StdMeshers_Import_1D.hxx" +#include "StdMeshers_ImportSource.hxx" + +#include "SMDS_MeshElement.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMESHDS_Group.hxx" +#include "SMESHDS_Mesh.hxx" +#include "SMESH_Comment.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" + +#include "Utils_SALOME_Exception.hxx" +#include "utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace +{ + double getMinElemSize2( const SMESHDS_GroupBase* srcGroup ) + { + double minSize2 = 1e100; + SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); + while ( srcElems->more() ) // loop on group contents + { + const SMDS_MeshElement* face = srcElems->next(); + int nbN = face->NbCornerNodes(); + + SMESH_TNodeXYZ prevN( face->GetNode( nbN-1 )); + for ( int i = 0; i < nbN; ++i ) + { + SMESH_TNodeXYZ n( face->GetNode( i ) ); + double size2 = ( n - prevN ).SquareModulus(); + minSize2 = std::min( minSize2, size2 ); + prevN = n; + } + } + return minSize2; + } +} + +//============================================================================= +/*! + * Creates StdMeshers_Import_1D2D + */ +//============================================================================= + +StdMeshers_Import_1D2D::StdMeshers_Import_1D2D(int hypId, int studyId, SMESH_Gen * gen) + :SMESH_2D_Algo(hypId, studyId, gen), _sourceHyp(0) +{ + MESSAGE("StdMeshers_Import_1D2D::StdMeshers_Import_1D2D"); + _name = "Import_1D2D"; + _shapeType = (1 << TopAbs_FACE); + + _compatibleHypothesis.push_back("ImportSource2D"); + _requireDiscreteBoundary = false; +} + +//============================================================================= +/*! + * Check presence of a hypothesis + */ +//============================================================================= + +bool StdMeshers_Import_1D2D::CheckHypothesis + (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus) +{ + _sourceHyp = 0; + + const list &hyps = GetUsedHypothesis(aMesh, aShape); + if ( hyps.size() == 0 ) + { + aStatus = SMESH_Hypothesis::HYP_MISSING; + return false; // can't work with no hypothesis + } + + if ( hyps.size() > 1 ) + { + aStatus = SMESH_Hypothesis::HYP_ALREADY_EXIST; + return false; + } + + const SMESHDS_Hypothesis *theHyp = hyps.front(); + + string hypName = theHyp->GetName(); + + if (hypName == _compatibleHypothesis.front()) + { + _sourceHyp = (StdMeshers_ImportSource1D *)theHyp; + aStatus = SMESH_Hypothesis::HYP_OK; + return true; + } + + aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE; + return true; +} + +namespace +{ + /*! + * \brief OrientedLink additionally storing a medium node + */ + struct TLink : public SMESH_OrientedLink + { + const SMDS_MeshNode* _medium; + TLink( const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + const SMDS_MeshNode* medium=0) + : SMESH_OrientedLink( n1,n2 ), _medium( medium ) {} + }; +} + +//============================================================================= +/*! + * Import elements from the other mesh + */ +//============================================================================= + +bool StdMeshers_Import_1D2D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & theShape) +{ + if ( !_sourceHyp ) return false; + + const vector& srcGroups = _sourceHyp->GetGroups(); + if ( srcGroups.empty() ) + return error("Invalid source groups"); + + bool allGroupsEmpty = true; + for ( size_t iG = 0; iG < srcGroups.size() && allGroupsEmpty; ++iG ) + allGroupsEmpty = srcGroups[iG]->GetGroupDS()->IsEmpty(); + if ( allGroupsEmpty ) + return error("No faces in source groups"); + + SMESH_MesherHelper helper(theMesh); + helper.SetSubShape(theShape); + SMESHDS_Mesh* tgtMesh = theMesh.GetMeshDS(); + + const TopoDS_Face& geomFace = TopoDS::Face( theShape ); + const double faceTol = helper.MaxTolerance( geomFace ); + const int shapeID = tgtMesh->ShapeToIndex( geomFace ); + const bool toCheckOri = (helper.NbAncestors( geomFace, theMesh, TopAbs_SOLID ) == 1 ); + + Handle(Geom_Surface) surface = BRep_Tool::Surface( geomFace ); + const bool reverse = + ( helper.GetSubShapeOri( tgtMesh->ShapeToMesh(), geomFace) == TopAbs_REVERSED ); + gp_Pnt p; gp_Vec du, dv; + + set subShapeIDs; + subShapeIDs.insert( shapeID ); + + // get nodes on vertices + list < SMESH_TNodeXYZ > vertexNodes; + list < SMESH_TNodeXYZ >::iterator vNIt; + TopExp_Explorer exp( theShape, TopAbs_VERTEX ); + for ( ; exp.More(); exp.Next() ) + { + const TopoDS_Vertex& v = TopoDS::Vertex( exp.Current() ); + if ( !subShapeIDs.insert( tgtMesh->ShapeToIndex( v )).second ) + continue; + const SMDS_MeshNode* n = SMESH_Algo::VertexNode( v, tgtMesh ); + if ( !n ) + { + _gen->Compute(theMesh,v,/*anUpward=*/true); + n = SMESH_Algo::VertexNode( v, tgtMesh ); + if ( !n ) return false; // very strange + } + vertexNodes.push_back( SMESH_TNodeXYZ( n )); + } + + // to count now many times a link between nodes encounters + map linkCount; + map::iterator link2Nb; + double minGroupTol = Precision::Infinite(); + + // ========================= + // Import faces from groups + // ========================= + + StdMeshers_Import_1D::TNodeNodeMap* n2n; + StdMeshers_Import_1D::TElemElemMap* e2e; + vector newNodes; + for ( size_t iG = 0; iG < srcGroups.size(); ++iG ) + { + const SMESHDS_GroupBase* srcGroup = srcGroups[iG]->GetGroupDS(); + + const int meshID = srcGroup->GetMesh()->GetPersistentId(); + const SMESH_Mesh* srcMesh = GetMeshByPersistentID( meshID ); + if ( !srcMesh ) continue; + StdMeshers_Import_1D::getMaps( srcMesh, &theMesh, n2n, e2e ); + + const double groupTol = 0.5 * sqrt( getMinElemSize2( srcGroup )); + minGroupTol = std::min( groupTol, minGroupTol ); + + SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); + SMDS_MeshNode *tmpNode = helper.AddNode(0,0,0); + gp_XY uv( Precision::Infinite(), Precision::Infinite() ); + while ( srcElems->more() ) // loop on group contents + { + const SMDS_MeshElement* face = srcElems->next(); + // find or create nodes of a new face + newNodes.resize( face->NbNodes() ); + newNodes.back() = 0; + int nbCreatedNodes = 0; + SMDS_MeshElement::iterator node = face->begin_nodes(); + for ( size_t i = 0; i < newNodes.size(); ++i, ++node ) + { + StdMeshers_Import_1D::TNodeNodeMap::iterator n2nIt = n2n->insert( make_pair( *node, (SMDS_MeshNode*)0 )).first; + if ( n2nIt->second ) + { + if ( !subShapeIDs.count( n2nIt->second->getshapeId() )) + break; + } + else + { + // find an existing vertex node + for ( vNIt = vertexNodes.begin(); vNIt != vertexNodes.end(); ++vNIt) + if ( vNIt->SquareDistance( *node ) < groupTol * groupTol) + { + (*n2nIt).second = vNIt->_node; + vertexNodes.erase( vNIt ); + break; + } + } + if ( !n2nIt->second ) + { + // find out if node lies on theShape + tmpNode->setXYZ( (*node)->X(), (*node)->Y(), (*node)->Z()); + uv.SetCoord( Precision::Infinite(), Precision::Infinite() ); + if ( helper.CheckNodeUV( geomFace, tmpNode, uv, groupTol, /*force=*/true )) + { + SMDS_MeshNode* newNode = tgtMesh->AddNode( (*node)->X(), (*node)->Y(), (*node)->Z()); + n2nIt->second = newNode; + tgtMesh->SetNodeOnFace( newNode, shapeID, uv.X(), uv.Y() ); + nbCreatedNodes++; + } + } + if ( !(newNodes[i] = n2nIt->second )) + break; + } + if ( !newNodes.back() ) + continue; // not all nodes of the face lie on theShape + + // try to find already created face + SMDS_MeshElement * newFace = 0; + if ( nbCreatedNodes == 0 && + tgtMesh->FindElement(newNodes, SMDSAbs_Face, /*noMedium=*/false)) + continue; // repeated face in source groups already created + + // check future face orientation + if ( toCheckOri ) + { + int iNode = -1; + gp_Vec geomNorm; + do + { + uv = helper.GetNodeUV( geomFace, newNodes[++iNode] ); + surface->D1( uv.X(),uv.Y(), p, du,dv ); + geomNorm = reverse ? dv^du : du^dv; + } + while ( geomNorm.SquareMagnitude() < 1e-6 && iNode+1 < face->NbCornerNodes()); + + int iNext = helper.WrapIndex( iNode+1, face->NbCornerNodes() ); + int iPrev = helper.WrapIndex( iNode-1, face->NbCornerNodes() ); + + SMESH_TNodeXYZ prevNode( newNodes[iPrev] ); + SMESH_TNodeXYZ curNode ( newNodes[iNode] ); + SMESH_TNodeXYZ nextNode( newNodes[iNext] ); + gp_Vec n1n0( prevNode - curNode); + gp_Vec n1n2( nextNode - curNode ); + gp_Vec meshNorm = n1n2 ^ n1n0; + + if ( geomNorm * meshNorm < 0 ) + std::reverse( newNodes.begin(), newNodes.end() ); + } + + // make a new face + switch ( newNodes.size() ) + { + case 3: + newFace = tgtMesh->AddFace( newNodes[0], newNodes[1], newNodes[2] ); + break; + case 4: + newFace = tgtMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3] ); + break; + case 6: + newFace = tgtMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], + newNodes[3], newNodes[4], newNodes[5]); + break; + case 8: + newFace = tgtMesh->AddFace( newNodes[0], newNodes[1], newNodes[2], newNodes[3], + newNodes[4], newNodes[5], newNodes[6], newNodes[7]); + break; + default: continue; + } + tgtMesh->SetMeshElementOnShape( newFace, shapeID ); + e2e->insert( make_pair( face, newFace )); + + // collect links + int nbNodes = face->NbCornerNodes(); + const SMDS_MeshNode* medium = 0; + for ( int i = 0; i < nbNodes; ++i ) + { + const SMDS_MeshNode* n1 = newNodes[i]; + const SMDS_MeshNode* n2 = newNodes[ (i+1)%nbNodes ]; + if ( newFace->IsQuadratic() ) + medium = newNodes[i+nbNodes]; + link2Nb = linkCount.insert( make_pair( TLink( n1, n2, medium ), 0)).first; + ++link2Nb->second; + // if ( link2Nb->second == 1 ) + // { + // // measure link length + // double len2 = SMESH_TNodeXYZ( n1 ).SquareDistance( n2 ); + // if ( len2 < minGroupTol ) + // minGroupTol = len2; + // } + } + } + helper.GetMeshDS()->RemoveNode(tmpNode); + } + + // ========================================================== + // Put nodes on geom edges and create edges on them; + // check if the whole geom face is covered by imported faces + // ========================================================== + + vector< TopoDS_Edge > edges; + for ( exp.Init( theShape, TopAbs_EDGE ); exp.More(); exp.Next() ) + if ( subShapeIDs.insert( tgtMesh->ShapeToIndex( exp.Current() )).second ) + edges.push_back( TopoDS::Edge( exp.Current() )); + + // use large tolerance for projection of nodes to edges because of + // BLSURF mesher specifics (issue 0020918, Study2.hdf) + const double projTol = minGroupTol; + + bool isFaceMeshed = false; + SMESHDS_SubMesh* tgtFaceSM = tgtMesh->MeshElements( theShape ); + if ( tgtFaceSM ) + { + // the imported mesh is valid if all external links (encountered once) + // lie on geom edges + subShapeIDs.erase( shapeID ); // to contain edges and vertices only + double u, f, l; + for ( link2Nb = linkCount.begin(); link2Nb != linkCount.end(); ++link2Nb) + { + const TLink& link = (*link2Nb).first; + int nbFaces = link2Nb->second; + if ( nbFaces == 1 ) + { + // check if a not shared link lies on face boundary + bool nodesOnBoundary = true; + list< TopoDS_Shape > bndShapes; + for ( int is1stN = 0; is1stN < 2 && nodesOnBoundary; ++is1stN ) + { + const SMDS_MeshNode* n = is1stN ? link.node1() : link.node2(); + if ( !subShapeIDs.count( n->getshapeId() )) + { + for ( size_t iE = 0; iE < edges.size(); ++iE ) + if ( helper.CheckNodeU( edges[iE], n, u=0, projTol, /*force=*/true )) + { + BRep_Tool::Range(edges[iE],f,l); + if ( Abs(u-f) < 2 * faceTol || Abs(u-l) < 2 * faceTol ) + // duplicated node on vertex + return error("Source elements overlap one another"); + tgtFaceSM->RemoveNode( n, /*isNodeDeleted=*/false ); + tgtMesh->SetNodeOnEdge( (SMDS_MeshNode*)n, edges[iE], u ); + break; + } + nodesOnBoundary = subShapeIDs.count( n->getshapeId()); + } + if ( nodesOnBoundary ) + { + TopoDS_Shape s = helper.GetSubShapeByNode( n, tgtMesh ); + if ( s.ShapeType() == TopAbs_VERTEX ) + bndShapes.push_front( s ); // vertex first + else + bndShapes.push_back( s ); // edges last + } + } + if ( !nodesOnBoundary ) + { + error("free internal link"); // just for an easier debug + break; + } + if ( bndShapes.front().ShapeType() == TopAbs_EDGE && + bndShapes.front() != bndShapes.back() ) + // link nodes on different geom edges + return error(COMPERR_BAD_INPUT_MESH, "Source nodes mismatch target vertices"); + + // find geom edge the link is on + if ( bndShapes.back().ShapeType() != TopAbs_EDGE ) + { + // find geom edge by two vertices + TopoDS_Shape geomEdge = helper.GetCommonAncestor( bndShapes.back(), + bndShapes.front(), + theMesh, TopAbs_EDGE ); + if ( geomEdge.IsNull() ) + { + error("free internal link"); + break; // vertices belong to different edges + } + bndShapes.push_back( geomEdge ); + } + + // create an edge if not yet exists + newNodes.resize(2); + newNodes[0] = link.node1(), newNodes[1] = link.node2(); + const SMDS_MeshElement* edge = tgtMesh->FindElement( newNodes, SMDSAbs_Edge ); + if ( edge ) continue; + + if ( link._reversed ) std::swap( newNodes[0], newNodes[1] ); + if ( link._medium ) + { + newNodes.push_back( link._medium ); + edge = tgtMesh->AddEdge( newNodes[0], newNodes[1], newNodes[2] ); + + TopoDS_Edge geomEdge = TopoDS::Edge(bndShapes.back()); + helper.CheckNodeU( geomEdge, link._medium, u, projTol, /*force=*/true ); + tgtFaceSM->RemoveNode( link._medium, /*isNodeDeleted=*/false ); + tgtMesh->SetNodeOnEdge( (SMDS_MeshNode*)link._medium, geomEdge, u ); + } + else + { + edge = tgtMesh->AddEdge( newNodes[0], newNodes[1]); + } + if ( !edge ) + return false; + + tgtMesh->SetMeshElementOnShape( edge, bndShapes.back() ); + } + else if ( nbFaces > 2 ) + { + return error( COMPERR_BAD_INPUT_MESH, "Non-manifold source mesh"); + } + } + isFaceMeshed = ( link2Nb == linkCount.end() && !linkCount.empty()); + if ( isFaceMeshed ) + { + // check that source faces do not overlap: + // there must be only two edges sharing each vertex and bound to sub-edges of theShape + SMESH_MeshEditor editor( &theMesh ); + set::iterator subID = subShapeIDs.begin(); + for ( ; subID != subShapeIDs.end(); ++subID ) + { + const TopoDS_Shape& s = tgtMesh->IndexToShape( *subID ); + if ( s.ShapeType() != TopAbs_VERTEX ) continue; + const SMDS_MeshNode* n = SMESH_Algo::VertexNode( TopoDS::Vertex(s), tgtMesh ); + SMDS_ElemIteratorPtr eIt = n->GetInverseElementIterator(SMDSAbs_Edge); + int nbEdges = 0; + while ( eIt->more() ) + { + const SMDS_MeshElement* edge = eIt->next(); + int sId = editor.FindShape( edge ); + nbEdges += subShapeIDs.count( sId ); + } + if ( nbEdges < 2 ) + return false; // weird + if ( nbEdges > 2 ) + return error( COMPERR_BAD_INPUT_MESH, "Source elements overlap one another"); + } + } + } + if ( !isFaceMeshed ) + return error( COMPERR_BAD_INPUT_MESH, + "Source elements don't cover totally the geometrical face" ); + + if ( helper.HasSeam() ) + { + // links on seam edges are shared by two faces, so no edges were created on them + // by the previous detection of 2D mesh boundary + for ( size_t iE = 0; iE < edges.size(); ++iE ) + { + if ( !helper.IsRealSeam( edges[iE] )) continue; + const TopoDS_Edge& seamEdge = edges[iE]; + // to find nodes lying on the seamEdge we check nodes of mesh faces sharing a node on one + // of its vertices; after finding another node on seamEdge we continue the same way + // until finding all nodes. + TopoDS_Vertex seamVertex = helper.IthVertex( 0, seamEdge ); + const SMDS_MeshNode* vertNode = SMESH_Algo::VertexNode( seamVertex, tgtMesh ); + set< const SMDS_MeshNode* > checkedNodes; checkedNodes.insert( vertNode ); + set< const SMDS_MeshElement* > checkedFaces; + // as a face can have more than one node on the seamEdge, there is a difficulty in selecting + // one of those nodes to treat next; so we simply find all nodes on the seamEdge and + // then sort them by U on edge + typedef list< pair< double, const SMDS_MeshNode* > > TUNodeList; + TUNodeList nodesOnSeam; + double u = helper.GetNodeU( seamEdge, vertNode ); + nodesOnSeam.push_back( make_pair( u, vertNode )); + TUNodeList::iterator u2nIt = nodesOnSeam.begin(); + for ( ; u2nIt != nodesOnSeam.end(); ++u2nIt ) + { + const SMDS_MeshNode* startNode = (*u2nIt).second; + SMDS_ElemIteratorPtr faceIt = startNode->GetInverseElementIterator( SMDSAbs_Face ); + while ( faceIt->more() ) + { + const SMDS_MeshElement* face = faceIt->next(); + if ( !checkedFaces.insert( face ).second ) continue; + for ( int i = 0, nbNodes = face->NbCornerNodes(); i < nbNodes; ++i ) + { + const SMDS_MeshNode* n = face->GetNode( i ); + if ( n == startNode || !checkedNodes.insert( n ).second ) continue; + if ( helper.CheckNodeU( seamEdge, n, u=0, projTol, /*force=*/true )) + nodesOnSeam.push_back( make_pair( u, n )); + } + } + } + // sort the found nodes by U on the seamEdge; most probably they are in a good order, + // so we can use the hint to spead-up map filling + map< double, const SMDS_MeshNode* > u2nodeMap; + for ( u2nIt = nodesOnSeam.begin(); u2nIt != nodesOnSeam.end(); ++u2nIt ) + u2nodeMap.insert( u2nodeMap.end(), *u2nIt ); + + // create edges + { + SMESH_MesherHelper seamHelper( theMesh ); + seamHelper.SetSubShape( edges[ iE ]); + seamHelper.SetElementsOnShape( true ); + + if ( (*checkedFaces.begin())->IsQuadratic() ) + for ( set< const SMDS_MeshElement* >::iterator fIt = checkedFaces.begin(); + fIt != checkedFaces.end(); ++fIt ) + seamHelper.AddTLinks( static_cast( *fIt )); + + map< double, const SMDS_MeshNode* >::iterator n1, n2, u2nEnd = u2nodeMap.end(); + for ( n2 = u2nodeMap.begin(), n1 = n2++; n2 != u2nEnd; ++n1, ++n2 ) + { + const SMDS_MeshNode* node1 = n1->second; + const SMDS_MeshNode* node2 = n2->second; + seamHelper.AddEdge( node1, node2 ); + if ( node2->getshapeId() == helper.GetSubShapeID() ) + { + tgtFaceSM->RemoveNode( node2, /*isNodeDeleted=*/false ); + tgtMesh->SetNodeOnEdge( const_cast( node2 ), seamEdge, n2->first ); + } + } + } + } // loop on edges to find seam ones + } // if ( helper.HasSeam() ) + + // notify sub-meshes of edges on computation + for ( size_t iE = 0; iE < edges.size(); ++iE ) + { + SMESH_subMesh * sm = theMesh.GetSubMesh( edges[iE] ); + if ( BRep_Tool::Degenerated( edges[iE] )) + sm->SetIsAlwaysComputed( true ); + sm->ComputeStateEngine(SMESH_subMesh::CHECK_COMPUTE_STATE); + if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK ) + return error(SMESH_Comment("Failed to create segments on the edge ") + << tgtMesh->ShapeToIndex( edges[iE ])); + } + + // ============ + // Copy meshes + // ============ + + vector srcMeshes = _sourceHyp->GetSourceMeshes(); + for ( size_t i = 0; i < srcMeshes.size(); ++i ) + StdMeshers_Import_1D::importMesh( srcMeshes[i], theMesh, _sourceHyp, theShape ); + + return true; +} + +//============================================================================= +/*! + * \brief Set needed event listeners and create a submesh for a copied mesh + * + * This method is called only if a submesh has HYP_OK algo_state. + */ +//============================================================================= + +void StdMeshers_Import_1D2D::SetEventListener(SMESH_subMesh* subMesh) +{ + if ( !_sourceHyp ) + { + const TopoDS_Shape& tgtShape = subMesh->GetSubShape(); + SMESH_Mesh* tgtMesh = subMesh->GetFather(); + Hypothesis_Status aStatus; + CheckHypothesis( *tgtMesh, tgtShape, aStatus ); + } + StdMeshers_Import_1D::setEventListener( subMesh, _sourceHyp ); +} +void StdMeshers_Import_1D2D::SubmeshRestored(SMESH_subMesh* subMesh) +{ + SetEventListener(subMesh); +} + +//============================================================================= +/*! + * Predict nb of mesh entities created by Compute() + */ +//============================================================================= + +bool StdMeshers_Import_1D2D::Evaluate(SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape, + MapShapeNbElems& aResMap) +{ + if ( !_sourceHyp ) return false; + + const vector& srcGroups = _sourceHyp->GetGroups(); + if ( srcGroups.empty() ) + return error("Invalid source groups"); + + vector aVec(SMDSEntity_Last,0); + + bool toCopyMesh, toCopyGroups; + _sourceHyp->GetCopySourceMesh(toCopyMesh, toCopyGroups); + if ( toCopyMesh ) // the whole mesh is copied + { + vector srcMeshes = _sourceHyp->GetSourceMeshes(); + for ( unsigned i = 0; i < srcMeshes.size(); ++i ) + { + SMESH_subMesh* sm = StdMeshers_Import_1D::getSubMeshOfCopiedMesh( theMesh, *srcMeshes[i]); + if ( !sm || aResMap.count( sm )) continue; // already counted + const SMDS_MeshInfo& aMeshInfo = srcMeshes[i]->GetMeshDS()->GetMeshInfo(); + for (int i = 0; i < SMDSEntity_Last; i++) + aVec[i] = aMeshInfo.NbEntities((SMDSAbs_EntityType)i); + } + } + else + { + // std-like iterator used to get coordinates of nodes of mesh element + typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator; + + SMESH_MesherHelper helper(theMesh); + helper.SetSubShape(theShape); + + const TopoDS_Face& geomFace = TopoDS::Face( theShape ); + + // take into account nodes on vertices + TopExp_Explorer exp( theShape, TopAbs_VERTEX ); + for ( ; exp.More(); exp.Next() ) + theMesh.GetSubMesh( exp.Current())->Evaluate( aResMap ); + + // to count now many times a link between nodes encounters, + // negative nb additionally means that a link is quadratic + map linkCount; + map::iterator link2Nb; + + // count faces and nodes imported from groups + set allNodes; + gp_XY uv; + double minGroupTol = 1e100; + for ( int iG = 0; iG < srcGroups.size(); ++iG ) + { + const SMESHDS_GroupBase* srcGroup = srcGroups[iG]->GetGroupDS(); + const double groupTol = 0.5 * sqrt( getMinElemSize2( srcGroup )); + minGroupTol = std::min( groupTol, minGroupTol ); + SMDS_ElemIteratorPtr srcElems = srcGroup->GetElements(); + SMDS_MeshNode *tmpNode =helper.AddNode(0,0,0); + while ( srcElems->more() ) // loop on group contents + { + const SMDS_MeshElement* face = srcElems->next(); + // find out if face is located on geomEdge by projecting + // a gravity center of face to geomFace + gp_XYZ gc(0,0,0); + gc = accumulate( TXyzIterator(face->nodesIterator()), TXyzIterator(), gc)/face->NbNodes(); + tmpNode->setXYZ( gc.X(), gc.Y(), gc.Z()); + if ( helper.CheckNodeUV( geomFace, tmpNode, uv, groupTol, /*force=*/true )) + { + ++aVec[ face->GetEntityType() ]; + + // collect links + int nbConers = face->NbCornerNodes(); + for ( int i = 0; i < face->NbNodes(); ++i ) + { + const SMDS_MeshNode* n1 = face->GetNode(i); + allNodes.insert( n1 ); + if ( i < nbConers ) + { + const SMDS_MeshNode* n2 = face->GetNode( (i+1)%nbConers ); + link2Nb = linkCount.insert( make_pair( SMESH_TLink( n1, n2 ), 0)).first; + if ( (*link2Nb).second ) + link2Nb->second += (link2Nb->second < 0 ) ? -1 : 1; + else + link2Nb->second += ( face->IsQuadratic() ) ? -1 : 1; + } + } + } + } + helper.GetMeshDS()->RemoveNode(tmpNode); + } + + int nbNodes = allNodes.size(); + allNodes.clear(); + + // count nodes and edges on geom edges + + double u; + for ( exp.Init(theShape, TopAbs_EDGE); exp.More(); exp.Next() ) + { + TopoDS_Edge geomEdge = TopoDS::Edge( exp.Current() ); + SMESH_subMesh* sm = theMesh.GetSubMesh( geomEdge ); + vector& edgeVec = aResMap[sm]; + if ( edgeVec.empty() ) + { + edgeVec.resize(SMDSEntity_Last,0); + for ( link2Nb = linkCount.begin(); link2Nb != linkCount.end(); ) + { + const SMESH_TLink& link = (*link2Nb).first; + int nbFacesOfLink = Abs( link2Nb->second ); + bool eraseLink = ( nbFacesOfLink != 1 ); + if ( nbFacesOfLink == 1 ) + { + if ( helper.CheckNodeU( geomEdge, link.node1(), u, minGroupTol, /*force=*/true )&& + helper.CheckNodeU( geomEdge, link.node2(), u, minGroupTol, /*force=*/true )) + { + bool isQuadratic = ( link2Nb->second < 0 ); + ++edgeVec[ isQuadratic ? SMDSEntity_Quad_Edge : SMDSEntity_Edge ]; + ++edgeVec[ SMDSEntity_Node ]; + --nbNodes; + eraseLink = true; + } + } + if ( eraseLink ) + linkCount.erase(link2Nb++); + else + link2Nb++; + } + if ( edgeVec[ SMDSEntity_Node] > 0 ) + --edgeVec[ SMDSEntity_Node ]; // for one node on vertex + } + else if ( !helper.IsSeamShape( geomEdge ) || + geomEdge.Orientation() == TopAbs_FORWARD ) + { + nbNodes -= 1+edgeVec[ SMDSEntity_Node ]; + } + } + + aVec[SMDSEntity_Node] = nbNodes; + } + + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(make_pair(sm,aVec)); + + return true; +} diff --git a/src/StdMeshers/StdMeshers_Import_1D2D.hxx b/src/StdMeshers/StdMeshers_Import_1D2D.hxx new file mode 100644 index 000000000..fc3a89d85 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Import_1D2D.hxx @@ -0,0 +1,60 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// Module : SMESH +// +#ifndef _SMESH_Import_2D_HXX_ +#define _SMESH_Import_2D_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_2D_Algo.hxx" +#include "SMDS_MeshElement.hxx" + +class StdMeshers_ImportSource1D; + +/*! + * \brief Copy elements from other the mesh + */ +class STDMESHERS_EXPORT StdMeshers_Import_1D2D: public SMESH_2D_Algo +{ +public: + StdMeshers_Import_1D2D(int hypId, int studyId, SMESH_Gen* gen); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus); + + virtual bool Compute (SMESH_Mesh & aMesh, const TopoDS_Shape & aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + + virtual void SetEventListener(SMESH_subMesh* subMesh); + virtual void SubmeshRestored(SMESH_subMesh* subMesh); + + private: + + StdMeshers_ImportSource1D* _sourceHyp; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_LayerDistribution.cxx b/src/StdMeshers/StdMeshers_LayerDistribution.cxx index 8e5987e6b..06693566b 100644 --- a/src/StdMeshers/StdMeshers_LayerDistribution.cxx +++ b/src/StdMeshers/StdMeshers_LayerDistribution.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_LayerDistribution.cxx // Author : Edward AGAPOV diff --git a/src/StdMeshers/StdMeshers_LayerDistribution.hxx b/src/StdMeshers/StdMeshers_LayerDistribution.hxx index aca054d4f..921d06bd3 100644 --- a/src/StdMeshers/StdMeshers_LayerDistribution.hxx +++ b/src/StdMeshers/StdMeshers_LayerDistribution.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LayerDistribution.hxx // Author : Edward AGAPOV diff --git a/src/StdMeshers/StdMeshers_LayerDistribution2D.cxx b/src/StdMeshers/StdMeshers_LayerDistribution2D.cxx new file mode 100644 index 000000000..581e43613 --- /dev/null +++ b/src/StdMeshers/StdMeshers_LayerDistribution2D.cxx @@ -0,0 +1,59 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_LayerDistribution2D.cxx +// Author : Edward AGAPOV +// Module : SMESH +// +#include "StdMeshers_LayerDistribution2D.hxx" + +#include "utilities.h" + + +//============================================================================= +/*! + * StdMeshers_LayerDistribution2D::StdMeshers_LayerDistribution2D + * + * Constructor + */ +//============================================================================= + +StdMeshers_LayerDistribution2D::StdMeshers_LayerDistribution2D(int hypId, + int studyId, + SMESH_Gen * gen) + : StdMeshers_LayerDistribution(hypId, studyId, gen) +{ + _name = "LayerDistribution2D"; // used by RadialQuadrangle_1D2D + _param_algo_dim = 2; // 2D + myHyp = 0; +} + +//============================================================================= +/*! + * StdMeshers_LayerDistribution2D::~StdMeshers_LayerDistribution2D + * + * Destructor + */ +//============================================================================= + +StdMeshers_LayerDistribution2D::~StdMeshers_LayerDistribution2D() +{ + MESSAGE( "StdMeshers_LayerDistribution2D::~StdMeshers_LayerDistribution2D" ); +} diff --git a/src/StdMeshers/StdMeshers_LayerDistribution2D.hxx b/src/StdMeshers/StdMeshers_LayerDistribution2D.hxx new file mode 100644 index 000000000..f3ae9b290 --- /dev/null +++ b/src/StdMeshers/StdMeshers_LayerDistribution2D.hxx @@ -0,0 +1,53 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_LayerDistribution2D.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_LayerDistribution2D_HXX_ +#define _SMESH_LayerDistribution2D_HXX_ + +#include "StdMeshers_LayerDistribution.hxx" + + +// ========================================================= +// ========================================================= +/*! + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies 1D hypothesis defining distribution of segments + * between the internal and the external surfaces. + */ +// ========================================================= +// ========================================================= + +class STDMESHERS_EXPORT StdMeshers_LayerDistribution2D + :public StdMeshers_LayerDistribution +{ +public: + // Constructor + StdMeshers_LayerDistribution2D(int hypId, int studyId, SMESH_Gen* gen); + // Destructor + virtual ~StdMeshers_LayerDistribution2D(); + +}; + +#endif + diff --git a/src/StdMeshers/StdMeshers_LengthFromEdges.cxx b/src/StdMeshers/StdMeshers_LengthFromEdges.cxx index cad6e9186..317b43d05 100644 --- a/src/StdMeshers/StdMeshers_LengthFromEdges.cxx +++ b/src/StdMeshers/StdMeshers_LengthFromEdges.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_LengthFromEdges.cxx // Moved here from SMESH_LengthFromEdges.cxx diff --git a/src/StdMeshers/StdMeshers_LengthFromEdges.hxx b/src/StdMeshers/StdMeshers_LengthFromEdges.hxx index cb52638e5..a80a1e72c 100644 --- a/src/StdMeshers/StdMeshers_LengthFromEdges.hxx +++ b/src/StdMeshers/StdMeshers_LengthFromEdges.hxx @@ -1,31 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_LengthFromEdges.hxx // Moved here from SMESH_LengthFromEdges.hxx // Author : Paul RASCLE, EDF // Module : SMESH // - #ifndef _SMESH_LENGTHFROMEDGES_HXX_ #define _SMESH_LENGTHFROMEDGES_HXX_ diff --git a/src/StdMeshers/StdMeshers_LocalLength.cxx b/src/StdMeshers/StdMeshers_LocalLength.cxx index d74abe9fd..0706f9ef2 100644 --- a/src/StdMeshers/StdMeshers_LocalLength.cxx +++ b/src/StdMeshers/StdMeshers_LocalLength.cxx @@ -1,31 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_LocalLength.cxx // Moved here from SMESH_LocalLength.cxx // Author : Paul RASCLE, EDF // Module : SMESH // - #include "StdMeshers_LocalLength.hxx" #include "SMESH_Mesh.hxx" @@ -216,7 +216,7 @@ bool StdMeshers_LocalLength::SetParametersByMesh(const SMESH_Mesh* theMesh, { const TopoDS_Edge& edge = TopoDS::Edge( edgeMap( iE )); Handle(Geom_Curve) C = BRep_Tool::Curve( edge, L, UMin, UMax ); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); vector< double > params; SMESHDS_Mesh* aMeshDS = const_cast< SMESH_Mesh* >( theMesh )->GetMeshDS(); @@ -244,6 +244,6 @@ bool StdMeshers_LocalLength::SetParametersByMesh(const SMESH_Mesh* theMesh, bool StdMeshers_LocalLength::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*theMesh*/) { - return bool( _length = dflts._elemLength ); + return ( _length = dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_LocalLength.hxx b/src/StdMeshers/StdMeshers_LocalLength.hxx index 1c8d72b27..6232e127c 100644 --- a/src/StdMeshers/StdMeshers_LocalLength.hxx +++ b/src/StdMeshers/StdMeshers_LocalLength.hxx @@ -1,31 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_LocalLength.hxx // Moved here from SMESH_LocalLength.hxx // Author : Paul RASCLE, EDF // Module : SMESH // - #ifndef _SMESH_LOCALLENGTH_HXX_ #define _SMESH_LOCALLENGTH_HXX_ diff --git a/src/StdMeshers/StdMeshers_MEFISTO_2D.cxx b/src/StdMeshers/StdMeshers_MEFISTO_2D.cxx index 453398149..fe637c65a 100644 --- a/src/StdMeshers/StdMeshers_MEFISTO_2D.cxx +++ b/src/StdMeshers/StdMeshers_MEFISTO_2D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MEFISTO_2D.cxx // Moved here from SMESH_MEFISTO_2D.cxx @@ -50,8 +51,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -63,6 +66,10 @@ #include #include +#include +#include +#include + using namespace std; //============================================================================= @@ -230,6 +237,8 @@ bool StdMeshers_MEFISTO_2D::Compute(SMESH_Mesh & aMesh, const TopoDS_Shape & aSh Z nutysu = 1; // 1: il existe un fonction areteideale_() // Z nutysu=0; // 0: on utilise aretmx R aretmx = _edgeLength; // longueur max aretes future triangulation + if ( _hypMaxElementArea ) + aretmx *= 1.5; nblf = nbWires; @@ -282,6 +291,90 @@ bool StdMeshers_MEFISTO_2D::Compute(SMESH_Mesh & aMesh, const TopoDS_Shape & aSh return isOk; } + +//============================================================================= +/*! + * + */ +//============================================================================= + +bool StdMeshers_MEFISTO_2D::Evaluate(SMESH_Mesh & aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap) +{ + MESSAGE("StdMeshers_MEFISTO_2D::Evaluate"); + + TopoDS_Face F = TopoDS::Face(aShape.Oriented(TopAbs_FORWARD)); + + double aLen = 0.0; + int NbSeg = 0; + bool IsQuadratic = false; + bool IsFirst = true; + TopExp_Explorer exp(F,TopAbs_EDGE); + for(; exp.More(); exp.Next()) { + TopoDS_Edge E = TopoDS::Edge(exp.Current()); + MapShapeNbElemsItr anIt = aResMap.find( aMesh.GetSubMesh(E) ); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + int nbe = Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + NbSeg += nbe; + if(IsFirst) { + IsQuadratic = ( aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge] ); + IsFirst = false; + } + double a,b; + TopLoc_Location L; + Handle(Geom_Curve) C = BRep_Tool::Curve(E,L,a,b); + gp_Pnt P1; + C->D0(a,P1); + double dp = (b-a)/nbe; + for(int i=1; i<=nbe; i++) { + gp_Pnt P2; + C->D0(a+i*dp,P2); + aLen += P1.Distance(P2); + P1 = P2; + } + } + if(NbSeg<1) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED, + "Submesh can not be evaluated",this)); + return false; + } + aLen = aLen/NbSeg; // middle length + + _edgeLength = Precision::Infinite(); + double tmpLength = Min( _edgeLength, aLen ); + + GProp_GProps G; + BRepGProp::SurfaceProperties(aShape,G); + double anArea = G.Mass(); + + int nbFaces = Precision::IsInfinite( tmpLength ) ? 0 : + (int)( anArea/(tmpLength*tmpLength*sqrt(3.)/4) ); + int nbNodes = (int) ( nbFaces*3 - (NbSeg-1)*2 ) / 6; + + std::vector aVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; i(node->GetPosition().get()); + static_cast(node->GetPosition()); double u = epos->GetUParameter(); if ( u < umin ) umin = u; @@ -514,8 +607,31 @@ bool StdMeshers_MEFISTO_2D::LoadPoints(TWireVector & wires, // set UV uvslf[m].x = uvPt->u * scalex; uvslf[m].y = uvPt->v * scaley; - if ( uvPt->node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + switch ( uvPt->node->GetPosition()->GetTypeOfPosition()) + { + case SMDS_TOP_VERTEX: mOnVertex.push_back( m ); + break; + case SMDS_TOP_EDGE: + // In order to detect degenerated faces easily, we replace + // nodes on a degenerated edge by node on the vertex of that edge + if ( myTool->IsDegenShape( uvPt->node->getshapeId() )) + { + int edgeID = uvPt->node->getshapeId(); + SMESH_subMesh* edgeSM = myTool->GetMesh()->GetSubMeshContaining( edgeID ); + SMESH_subMeshIteratorPtr smIt = edgeSM->getDependsOnIterator( /*includeSelf=*/0, + /*complexShapeFirst=*/0); + if ( smIt->more() ) + { + SMESH_subMesh* vertexSM = smIt->next(); + SMDS_NodeIteratorPtr nIt = vertexSM->GetSubMeshDS()->GetNodes(); + if ( nIt->more() ) + mefistoToDS[m] = nIt->next(); + } + } + break; + default:; + } m++; } @@ -526,7 +642,7 @@ bool StdMeshers_MEFISTO_2D::LoadPoints(TWireVector & wires, int m = *mIt; if ( iW && !VWMap.IsEmpty()) { // except outer wire // avoid passing same uv point for a vertex common to 2 wires - int vID = mefistoToDS[m]->GetPosition()->GetShapeId(); + int vID = mefistoToDS[m]->getshapeId(); TopoDS_Vertex V = TopoDS::Vertex( myTool->GetMeshDS()->IndexToShape( vID )); if ( fixCommonVertexUV( uvslf[m], V, F, VWMap, *myTool->GetMesh(), scalex, scaley, _quadraticMesh )) { @@ -542,6 +658,9 @@ bool StdMeshers_MEFISTO_2D::LoadPoints(TWireVector & wires, fixOverlappedLinkUV (uvslf[ mB ], uvslf[ m ], uvslf[ mA ]); } } +// cout << "MEFISTO INPUT************" << endl; +// for ( int i =0; i < m; ++i ) +// cout << i << ": \t" << uvslf[i].x << ", " << uvslf[i].y << " Node " << mefistoToDS[i]->GetID()<< endl; return true; } @@ -688,13 +807,17 @@ void StdMeshers_MEFISTO_2D::StoreResult(Z nbst, R2 * uvst, Z nbt, Z * nust, const SMDS_MeshNode * n2 = mefistoToDS[ nust[m++] - 1 ]; const SMDS_MeshNode * n3 = mefistoToDS[ nust[m++] - 1 ]; - SMDS_MeshElement * elt; - if (triangleIsWellOriented) - elt = myTool->AddFace(n1, n2, n3); - else - elt = myTool->AddFace(n1, n3, n2); - - meshDS->SetMeshElementOnShape(elt, faceID); + // avoid creating degenetrated faces + bool isDegen = ( myTool->HasDegeneratedEdges() && ( n1 == n2 || n1 == n3 || n2 == n3 )); + if ( !isDegen ) + { + SMDS_MeshElement * elt; + if (triangleIsWellOriented) + elt = myTool->AddFace(n1, n2, n3); + else + elt = myTool->AddFace(n1, n3, n2); + meshDS->SetMeshElementOnShape(elt, faceID); + } m++; } diff --git a/src/StdMeshers/StdMeshers_MEFISTO_2D.hxx b/src/StdMeshers/StdMeshers_MEFISTO_2D.hxx index fba4b2d34..0dce86135 100644 --- a/src/StdMeshers/StdMeshers_MEFISTO_2D.hxx +++ b/src/StdMeshers/StdMeshers_MEFISTO_2D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MEFISTO_2D.hxx // Moved here from SMESH_MEFISTO_2D.hxx @@ -55,25 +56,28 @@ public: SMESH_Hypothesis::Hypothesis_Status& aStatus); virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape); + const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); typedef boost::shared_ptr< StdMeshers_FaceSide> StdMeshers_FaceSidePtr; typedef std::vector< StdMeshers_FaceSidePtr > TWireVector; bool LoadPoints(TWireVector & wires, - R2* uvslf, - std::vector< const SMDS_MeshNode*>& mefistoToDS, + R2* uvslf, + std::vector< const SMDS_MeshNode*>& mefistoToDS, double scalex, double scaley); void ComputeScaleOnFace(SMESH_Mesh& aMesh, - const TopoDS_Face& aFace, - double& scalex, - double& scaley); + const TopoDS_Face& aFace, + double& scalex, + double& scaley); void StoreResult (Z nbst, R2* uvst, Z nbt, Z* nust, - std::vector< const SMDS_MeshNode*>& mefistoToDS, + std::vector< const SMDS_MeshNode*>& mefistoToDS, double scalex, double scaley); - + protected: double _edgeLength; double _maxElementArea; diff --git a/src/StdMeshers/StdMeshers_MaxElementArea.cxx b/src/StdMeshers/StdMeshers_MaxElementArea.cxx index 36e40e172..e42dd783c 100644 --- a/src/StdMeshers/StdMeshers_MaxElementArea.cxx +++ b/src/StdMeshers/StdMeshers_MaxElementArea.cxx @@ -1,31 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxElementArea.cxx // Moved here from SMESH_MaxElementArea.cxx // Author : Paul RASCLE, EDF // Module : SMESH // - #include "StdMeshers_MaxElementArea.hxx" #include "SMESH_ControlsDef.hxx" @@ -195,6 +195,6 @@ bool StdMeshers_MaxElementArea::SetParametersByMesh(const SMESH_Mesh* theMesh, bool StdMeshers_MaxElementArea::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*theMesh*/) { - return bool( _maxArea = dflts._elemLength*dflts._elemLength ); + return ( _maxArea = dflts._elemLength*dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_MaxElementArea.hxx b/src/StdMeshers/StdMeshers_MaxElementArea.hxx index 095067363..f7ea305ad 100644 --- a/src/StdMeshers/StdMeshers_MaxElementArea.hxx +++ b/src/StdMeshers/StdMeshers_MaxElementArea.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxElementArea.hxx // Moved here from SMESH_MaxElementArea.hxx diff --git a/src/StdMeshers/StdMeshers_MaxElementVolume.cxx b/src/StdMeshers/StdMeshers_MaxElementVolume.cxx index 435213aad..b70bdfa52 100644 --- a/src/StdMeshers/StdMeshers_MaxElementVolume.cxx +++ b/src/StdMeshers/StdMeshers_MaxElementVolume.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxElementVolume.cxx // Moved here from SMESH_MaxElementVolume.cxx @@ -206,6 +207,6 @@ bool StdMeshers_MaxElementVolume::SetParametersByMesh(const SMESH_Mesh* theMes bool StdMeshers_MaxElementVolume::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*theMesh*/) { - return bool( _maxVolume = dflts._elemLength*dflts._elemLength*dflts._elemLength ); + return ( _maxVolume = dflts._elemLength*dflts._elemLength*dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_MaxElementVolume.hxx b/src/StdMeshers/StdMeshers_MaxElementVolume.hxx index 6395fd419..27d332b32 100644 --- a/src/StdMeshers/StdMeshers_MaxElementVolume.hxx +++ b/src/StdMeshers/StdMeshers_MaxElementVolume.hxx @@ -1,30 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxElementVolume.hxx // Moved here from SMESH_MaxElementVolume.hxx // Author : Paul RASCLE, EDF // Module : SMESH - +// #ifndef _SMESH_MAXELEMENTVOLUME_HXX_ #define _SMESH_MAXELEMENTVOLUME_HXX_ diff --git a/src/StdMeshers/StdMeshers_MaxLength.cxx b/src/StdMeshers/StdMeshers_MaxLength.cxx index 475e56ae5..9597001f5 100644 --- a/src/StdMeshers/StdMeshers_MaxLength.cxx +++ b/src/StdMeshers/StdMeshers_MaxLength.cxx @@ -1,27 +1,26 @@ -// SMESH SMESH : implementaion of SMESH idl descriptions +// Copyright (C) 2007-2012 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 // -// Copyright (C) 2003 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 // + +// SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxLength.cxx // Module : SMESH - +// #include "StdMeshers_MaxLength.hxx" #include "SMESH_Mesh.hxx" @@ -208,7 +207,7 @@ bool StdMeshers_MaxLength::SetParametersByMesh(const SMESH_Mesh* theMesh, { const TopoDS_Edge& edge = TopoDS::Edge( edgeMap( iE )); Handle(Geom_Curve) C = BRep_Tool::Curve( edge, L, UMin, UMax ); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); vector< double > params; SMESHDS_Mesh* aMeshDS = const_cast< SMESH_Mesh* >( theMesh )->GetMeshDS(); @@ -237,6 +236,6 @@ bool StdMeshers_MaxLength::SetParametersByDefaults(const TDefaults& dflts, //_preestimation = ( dflts._elemLength > 0.); if ( dflts._elemLength > 0. ) _preestimated = dflts._elemLength; - return bool( _length = dflts._elemLength ); + return ( _length = dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_MaxLength.hxx b/src/StdMeshers/StdMeshers_MaxLength.hxx index 23d94a9f2..7ea21e110 100644 --- a/src/StdMeshers/StdMeshers_MaxLength.hxx +++ b/src/StdMeshers/StdMeshers_MaxLength.hxx @@ -1,27 +1,26 @@ -// SMESH SMESH : implementaion of SMESH idl descriptions +// Copyright (C) 2007-2012 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 // -// Copyright (C) 2003 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 +// +// SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_MaxLength.hxx // Module : SMESH - +// #ifndef _SMESH_MaxLength_HXX_ #define _SMESH_MaxLength_HXX_ diff --git a/src/StdMeshers/StdMeshers_NotConformAllowed.cxx b/src/StdMeshers/StdMeshers_NotConformAllowed.cxx index 66c58f13c..54a986b06 100644 --- a/src/StdMeshers/StdMeshers_NotConformAllowed.cxx +++ b/src/StdMeshers/StdMeshers_NotConformAllowed.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_NotConformAllowed.cxx // Author : Paul RASCLE, EDF diff --git a/src/StdMeshers/StdMeshers_NotConformAllowed.hxx b/src/StdMeshers/StdMeshers_NotConformAllowed.hxx index ddcfcb445..d52574b59 100644 --- a/src/StdMeshers/StdMeshers_NotConformAllowed.hxx +++ b/src/StdMeshers/StdMeshers_NotConformAllowed.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_NotConformAllowed.hxx // Author : Paul RASCLE, EDF diff --git a/src/StdMeshers/StdMeshers_NumberOfLayers.cxx b/src/StdMeshers/StdMeshers_NumberOfLayers.cxx index 604ee185a..9026f2252 100644 --- a/src/StdMeshers/StdMeshers_NumberOfLayers.cxx +++ b/src/StdMeshers/StdMeshers_NumberOfLayers.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_NumberOfLayers.cxx // Author : Edward AGAPOV @@ -171,7 +172,7 @@ bool StdMeshers_NumberOfLayers::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh) { if ( dflts._elemLength ) - return bool( theMesh ? _nbLayers = int( theMesh->GetShapeDiagonalSize() / dflts._elemLength/ 2.) : 0); + return theMesh ? (_nbLayers = int( theMesh->GetShapeDiagonalSize() / dflts._elemLength/ 2.)) : 0; return false; } diff --git a/src/StdMeshers/StdMeshers_NumberOfLayers.hxx b/src/StdMeshers/StdMeshers_NumberOfLayers.hxx index ebaae84a9..dc7d60cde 100644 --- a/src/StdMeshers/StdMeshers_NumberOfLayers.hxx +++ b/src/StdMeshers/StdMeshers_NumberOfLayers.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_NumberOfLayers.hxx // Author : Edward AGAPOV diff --git a/src/StdMeshers/StdMeshers_NumberOfLayers2D.cxx b/src/StdMeshers/StdMeshers_NumberOfLayers2D.cxx new file mode 100644 index 000000000..776c42f7b --- /dev/null +++ b/src/StdMeshers/StdMeshers_NumberOfLayers2D.cxx @@ -0,0 +1,60 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_NumberOfLayers2D.cxx +// Author : Edward AGAPOV +// Module : SMESH +// +#include "StdMeshers_NumberOfLayers2D.hxx" + +#include "utilities.h" + + +//============================================================================= +/*! + * StdMeshers_NumberOfLayers2D::StdMeshers_NumberOfLayers2D + * + * Constructor + */ +//============================================================================= + +StdMeshers_NumberOfLayers2D::StdMeshers_NumberOfLayers2D(int hypId, + int studyId, + SMESH_Gen * gen) + : StdMeshers_NumberOfLayers(hypId, studyId, gen) +{ + _name = "NumberOfLayers2D"; // used by RadialQuadrangle_1D2D + _param_algo_dim = 2; // 2D + _nbLayers = 1; +} + +//============================================================================= +/*! + * StdMeshers_NumberOfLayers2D::~StdMeshers_NumberOfLayers2D + * + * Destructor + */ +//============================================================================= + +StdMeshers_NumberOfLayers2D::~StdMeshers_NumberOfLayers2D() +{ + MESSAGE( "StdMeshers_NumberOfLayers2D::~StdMeshers_NumberOfLayers2D" ); +} + diff --git a/src/StdMeshers/StdMeshers_NumberOfLayers2D.hxx b/src/StdMeshers/StdMeshers_NumberOfLayers2D.hxx new file mode 100644 index 000000000..eef7395eb --- /dev/null +++ b/src/StdMeshers/StdMeshers_NumberOfLayers2D.hxx @@ -0,0 +1,52 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_NumberOfLayers2D.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_NumberOfLayers2D_HXX_ +#define _SMESH_NumberOfLayers2D_HXX_ + +#include "StdMeshers_NumberOfLayers.hxx" + + +// ========================================================= +// ========================================================= +/*! + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies number of segments between the internal + * and the external surfaces. + */ +// ========================================================= +// ========================================================= + +class STDMESHERS_EXPORT StdMeshers_NumberOfLayers2D + : public StdMeshers_NumberOfLayers +{ +public: + // Constructor + StdMeshers_NumberOfLayers2D(int hypId, int studyId, SMESH_Gen* gen); + // Destructor + virtual ~StdMeshers_NumberOfLayers2D(); +}; + +#endif + diff --git a/src/StdMeshers/StdMeshers_NumberOfSegments.cxx b/src/StdMeshers/StdMeshers_NumberOfSegments.cxx index 90c50a0bb..d9d1362d0 100644 --- a/src/StdMeshers/StdMeshers_NumberOfSegments.cxx +++ b/src/StdMeshers/StdMeshers_NumberOfSegments.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_NumberOfSegments.cxx // Moved here from SMESH_NumberOfSegments.cxx @@ -30,6 +31,7 @@ #include "StdMeshers_Distribution.hxx" #include "SMESHDS_SubMesh.hxx" #include "SMESH_Mesh.hxx" +#include "SMESH_Comment.hxx" #include #include @@ -49,6 +51,8 @@ #include #endif +#include + using namespace std; const double PRECISION = 1e-7; @@ -233,11 +237,11 @@ void StdMeshers_NumberOfSegments::SetTableFunction(const vector& table) #ifdef NO_CAS_CATCH OCC_CATCH_SIGNALS; #endif - val = pow( 10.0, val ); + val = pow( 10.0, val ); } catch(Standard_Failure) { - Handle(Standard_Failure) aFail = Standard_Failure::Caught(); - throw SALOME_Exception( LOCALIZED( "invalid value")); - return; + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + throw SALOME_Exception( LOCALIZED( "invalid value")); + return; } } else if( _convMode==1 && val<0.0 ) @@ -303,7 +307,7 @@ bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr ) if( !name.IsNull() ) { if( name->GetName()!="t" ) - res = false; + res = false; } else res = isCorrectArg( sub ); @@ -317,10 +321,12 @@ bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr ) */ //================================================================================ bool process( const TCollection_AsciiString& str, int convMode, - bool& syntax, bool& args, - bool& non_neg, bool& non_zero, - bool& singulars, double& sing_point ) + bool& syntax, bool& args, + bool& non_neg, bool& non_zero, + bool& singulars, double& sing_point ) { + Kernel_Utils::Localizer loc; + bool parsed_ok = true; Handle( ExprIntrp_GenExp ) myExpr; try { @@ -359,19 +365,20 @@ bool process( const TCollection_AsciiString& str, int convMode, double t = double(i)/double(max), val; if( !f.value( t, val ) ) { - sing_point = t; - singulars = true; - break; + sing_point = t; + singulars = true; + break; } if( val<0 ) { - non_neg = false; - break; + non_neg = false; + break; } if( val>PRECISION ) - non_zero = true; + non_zero = true; } } + return res && non_neg && non_zero && ( !singulars ); } @@ -388,8 +395,27 @@ void StdMeshers_NumberOfSegments::SetExpressionFunction(const char* expr) _distrType = DT_ExprFunc; //throw SALOME_Exception(LOCALIZED("not an expression function distribution")); + string func = CheckExpressionFunction( expr, _convMode ); + if( _func != func ) + { + _func = func; + NotifySubMeshesHypothesisModification(); + } +} + +//======================================================================= +//function : CheckExpressionFunction +//purpose : Checks validity of the expression of the function f(t), e.g. "sin(t)". +// In case of validity returns a cleaned expression +//======================================================================= + +std::string +StdMeshers_NumberOfSegments::CheckExpressionFunction( const std::string& expr, + const int convMode) + throw (SALOME_Exception) +{ // remove white spaces - TCollection_AsciiString str((Standard_CString)expr); + TCollection_AsciiString str((Standard_CString)expr.c_str()); str.RemoveAll(' '); str.RemoveAll('\t'); str.RemoveAll('\r'); @@ -397,15 +423,15 @@ void StdMeshers_NumberOfSegments::SetExpressionFunction(const char* expr) bool syntax, args, non_neg, singulars, non_zero; double sing_point; - bool res = process( str, _convMode, syntax, args, non_neg, non_zero, singulars, sing_point ); + bool res = process( str, convMode, syntax, args, non_neg, non_zero, singulars, sing_point ); if( !res ) { if( !syntax ) - throw SALOME_Exception(LOCALIZED("invalid expression syntax")); + throw SALOME_Exception(SMESH_Comment("invalid expression syntax: ") << str ); if( !args ) throw SALOME_Exception(LOCALIZED("only 't' may be used as function argument")); if( !non_neg ) - throw SALOME_Exception(LOCALIZED("only non-negative function can be used as density")); + throw SALOME_Exception(LOCALIZED("only non-negative function can be used")); if( singulars ) { char buf[1024]; @@ -413,17 +439,10 @@ void StdMeshers_NumberOfSegments::SetExpressionFunction(const char* expr) throw SALOME_Exception( buf ); } if( !non_zero ) - throw SALOME_Exception(LOCALIZED("f(t)=0 cannot be used as density")); - - return; - } - - string func = expr; - if( _func != func ) - { - _func = func; - NotifySubMeshesHypothesisModification(); + throw SALOME_Exception(LOCALIZED("f(t)=0 cannot be used")); } + + return str.ToCString(); } //================================================================================ @@ -481,6 +500,7 @@ int StdMeshers_NumberOfSegments::ConversionMode() const ostream & StdMeshers_NumberOfSegments::SaveTo(ostream & save) { + int listSize = _edgeIDs.size(); save << _numberOfSegments << " " << (int)_distrType; switch (_distrType) { @@ -503,6 +523,13 @@ ostream & StdMeshers_NumberOfSegments::SaveTo(ostream & save) if (_distrType == DT_TabFunc || _distrType == DT_ExprFunc) save << " " << _convMode; + + if ( _distrType != DT_Regular && listSize > 0 ) { + save << " " << listSize; + for ( int i = 0; i < listSize; i++ ) + save << " " << _edgeIDs[i]; + save << " " << _objEntry; + } return save; } @@ -619,6 +646,18 @@ istream & StdMeshers_NumberOfSegments::LoadFrom(istream & load) load.clear(ios::badbit | load.rdstate()); } + // load reversed edges IDs + int intVal; + isOK = (load >> intVal); + if ( isOK && _distrType != DT_Regular && intVal > 0 ) { + _edgeIDs.reserve( intVal ); + for (int i = 0; i < _edgeIDs.capacity() && isOK; i++) { + isOK = (load >> intVal); + if ( isOK ) _edgeIDs.push_back( intVal ); + } + isOK = (load >> _objEntry); + } + return load; } @@ -692,6 +731,21 @@ bool StdMeshers_NumberOfSegments::SetParametersByMesh(const SMESH_Mesh* theMes bool StdMeshers_NumberOfSegments::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*theMesh*/) { - return bool(_numberOfSegments = dflts._nbSegments ); + return (_numberOfSegments = dflts._nbSegments ); +} + +//============================================================================= +/*! + * + */ +//============================================================================= + +void StdMeshers_NumberOfSegments::SetReversedEdges( std::vector& ids ) +{ + if ( ids != _edgeIDs ) { + _edgeIDs = ids; + + NotifySubMeshesHypothesisModification(); + } } diff --git a/src/StdMeshers/StdMeshers_NumberOfSegments.hxx b/src/StdMeshers/StdMeshers_NumberOfSegments.hxx index 1be5b9176..a232c5871 100644 --- a/src/StdMeshers/StdMeshers_NumberOfSegments.hxx +++ b/src/StdMeshers/StdMeshers_NumberOfSegments.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_NumberOfSegments.hxx // Moved here from SMESH_NumberOfSegments.hxx @@ -141,6 +142,15 @@ public: const char* GetExpressionFunction() const throw (SALOME_Exception); + /*! + * \brief Checks validity of the expression of the function f(t), e.g. "sin(t)". + * In case of validity returns a cleaned expression + * \param convMode - 0 for "Exponent mode", 1 for "Cut negative mode" + */ + static std::string CheckExpressionFunction( const std::string& expr, + const int convMode) + throw (SALOME_Exception); + /*! * \brief Set conversion mode. When it is 0, it means "exponent mode": * the function of distribution of density is used as an exponent of 10, i,e, 10^f(t). @@ -161,6 +171,13 @@ public: int ConversionMode() const throw (SALOME_Exception); + void SetReversedEdges( std::vector& ids); + + void SetObjectEntry( const char* entry ) { _objEntry = entry; } + + const char* GetObjectEntry() { return _objEntry.c_str(); } + + const std::vector& GetReversedEdges() const { return _edgeIDs; } /*! * \brief Initialize number of segments by the mesh built on the geometry @@ -188,6 +205,8 @@ protected: std::vector _table, _distr; //!< the table for DT_TabFunc, a sequence of pairs of numbers std::string _func; //!< the expression of the function for DT_ExprFunc int _convMode; //!< flag of conversion mode: 0=exponent, 1=cut negative + std::vector _edgeIDs; //!< list of reversed edges ids + std::string _objEntry; //!< Entry of the main object to reverse edges }; #endif diff --git a/src/StdMeshers/StdMeshers_Penta_3D.cxx b/src/StdMeshers/StdMeshers_Penta_3D.cxx index 93bb7fd5c..29c935a64 100644 --- a/src/StdMeshers/StdMeshers_Penta_3D.cxx +++ b/src/StdMeshers/StdMeshers_Penta_3D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_Penta_3D implementaion of SMESH idl descriptions // File : StdMeshers_Penta_3D.cxx // Module : SMESH @@ -34,7 +35,7 @@ #include "SMDS_VolumeTool.hxx" #include "SMESHDS_SubMesh.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_subMeshEventListener.hxx" #include "SMESH_Comment.hxx" @@ -46,6 +47,8 @@ #include #include #include +#include +#include #include #include #include @@ -67,7 +70,7 @@ enum { NB_WALL_FACES = 4 }; //purpose : //======================================================================= StdMeshers_Penta_3D::StdMeshers_Penta_3D() -: myErrorStatus(SMESH_ComputeError::New()) + : myErrorStatus(SMESH_ComputeError::New()) { myTol3D=0.1; myWallNodesMaps.resize( SMESH_Block::NbFaces() ); @@ -89,7 +92,7 @@ StdMeshers_Penta_3D::~StdMeshers_Penta_3D() //purpose : //======================================================================= bool StdMeshers_Penta_3D::Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape) + const TopoDS_Shape& aShape) { MESSAGE("StdMeshers_Penta_3D::Compute()"); // @@ -103,10 +106,6 @@ bool StdMeshers_Penta_3D::Compute(SMESH_Mesh& aMesh, return bOK; } - SMESH_MesherHelper helper(aMesh); - myTool = &helper; - myCreateQuadratic = myTool->IsQuadraticSubMesh(aShape); - // MakeBlock(); if (!myErrorStatus->IsOK()) { @@ -117,6 +116,12 @@ bool StdMeshers_Penta_3D::Compute(SMESH_Mesh& aMesh, if (!myErrorStatus->IsOK()) { return bOK; } + + // now unnecessary faces removed, we can load medium nodes + SMESH_MesherHelper helper(aMesh); + myTool = &helper; + myCreateQuadratic = myTool->IsQuadraticSubMesh(aShape); + // MakeNodes(); if (!myErrorStatus->IsOK()) { @@ -230,7 +235,7 @@ void StdMeshers_Penta_3D::MakeNodes() // if ( SMESH_Block::IsEdgeID (aSID)) { const SMDS_EdgePosition* epos = - static_cast(aNode->GetPosition().get()); + static_cast(aNode->GetPosition()); myBlock.ComputeParameters( epos->GetUParameter(), aS, aCoords ); } else { @@ -258,11 +263,11 @@ void StdMeshers_Penta_3D::MakeNodes() SMESH_Block::TShapeID wallFaceID[ NB_WALL_FACES ] = { SMESH_Block::ID_Fx0z, SMESH_Block::ID_Fx1z, SMESH_Block::ID_F0yz, SMESH_Block::ID_F1yz - }; + }; SMESH_Block::TShapeID baseEdgeID[ NB_WALL_FACES ] = { SMESH_Block::ID_Ex00, SMESH_Block::ID_Ex10, SMESH_Block::ID_E0y0, SMESH_Block::ID_E1y0 - }; + }; for ( i = 0; i < NB_WALL_FACES ; ++i ) { int fIndex = SMESH_Block::ShapeIndex( wallFaceID[ i ]); bool ok = LoadIJNodes (myWallNodesMaps[ fIndex ], @@ -420,7 +425,7 @@ void StdMeshers_Penta_3D::MakeNodes() ShapeSupportID(bIsUpperLayer, aBNSSID, aSSID); if (!myErrorStatus->IsOK()) { MESSAGE("StdMeshers_Penta_3D::MakeNodes() "); - return; + return; } // aTN.SetShapeSupportID(aSSID); @@ -428,13 +433,13 @@ void StdMeshers_Penta_3D::MakeNodes() aTN.SetBaseNodeID(iBNID); // if (aSSID!=SMESH_Block::ID_NONE){ - // try to find the node - const TopoDS_Shape& aS=myBlock.Shape((int)aSSID); - FindNodeOnShape(aS, aCoords, i, aTN); + // try to find the node + const TopoDS_Shape& aS=myBlock.Shape((int)aSSID); + FindNodeOnShape(aS, aCoords, i, aTN); } else{ - // create node and get it id - CreateNode (bIsUpperLayer, aCoords, aTN); + // create node and get its id + CreateNode (bIsUpperLayer, aCoords, aTN); // if ( bIsUpperLayer ) { const SMDS_MeshNode* n = aTN.Node(); @@ -467,39 +472,12 @@ void StdMeshers_Penta_3D::MakeNodes() } if (!myErrorStatus->IsOK()) { MESSAGE("StdMeshers_Penta_3D::MakeNodes() "); - return; + return; } // myTNodes[ij]=aTN; } } - //DEB - /* - { - int iSSID, iBNID, aID; - // - for (i=0; iGetID(); - aX=aNode->X(); - aY=aNode->Y(); - aZ=aNode->Z(); - printf("*** j:%d BNID#%d iSSID:%d ID:%d { %lf %lf %lf }, { %lf %lf %lf }\n", - j, iBNID, iSSID, aID, aXYZ.X(), aXYZ.Y(), aXYZ.Z(), aX, aY, aZ); - } - } - } - */ - //DEB t } @@ -509,9 +487,9 @@ void StdMeshers_Penta_3D::MakeNodes() //======================================================================= void StdMeshers_Penta_3D::FindNodeOnShape(const TopoDS_Shape& aS, - const gp_XYZ& aParams, + const gp_XYZ& aParams, const int z, - StdMeshers_TNode& aTN) + StdMeshers_TNode& aTN) { double aX, aY, aZ, aD, aTol2, minD; gp_Pnt aP1, aP2; @@ -630,16 +608,16 @@ double StdMeshers_Penta_3D::SetHorizEdgeXYZ(const gp_XYZ& aBase } else { // this variant is better for other cases -// SMESH_MesherHelper helper( *GetMesh() ); -// const TopoDS_Edge & edge = TopoDS::Edge( myBlock.Shape( edgeVec[ TOP ])); -// double u1 = helper.GetNodeU( edge, n1 ); -// double u2 = helper.GetNodeU( edge, n2 ); -// double u = ( 1. - r ) * u1 + r * u2; -// gp_XYZ topNodeParams; -// myBlock.Block().EdgeParameters( edgeVec[ TOP ], u, topNodeParams ); -// myBlock.Block().EdgePoint( edgeVec[ TOP ], -// topNodeParams, -// myShapeXYZ[ edgeVec[ TOP ]]); + // SMESH_MesherHelper helper( *GetMesh() ); + // const TopoDS_Edge & edge = TopoDS::Edge( myBlock.Shape( edgeVec[ TOP ])); + // double u1 = helper.GetNodeU( edge, n1 ); + // double u2 = helper.GetNodeU( edge, n2 ); + // double u = ( 1. - r ) * u1 + r * u2; + // gp_XYZ topNodeParams; + // myBlock.Block().EdgeParameters( edgeVec[ TOP ], u, topNodeParams ); + // myBlock.Block().EdgePoint( edgeVec[ TOP ], + // topNodeParams, + // myShapeXYZ[ edgeVec[ TOP ]]); } // base edge @@ -671,8 +649,8 @@ void StdMeshers_Penta_3D::MakeVolumeMesh() const StdMeshers_TNode& aTN = myTNodes[ij]; aSSID=aTN.ShapeSupportID(); if (aSSID==SMESH_Block::ID_NONE) { - SMDS_MeshNode* aNode = (SMDS_MeshNode*)aTN.Node(); - meshDS->SetNodeInVolume(aNode, shapeID); + SMDS_MeshNode* aNode = (SMDS_MeshNode*)aTN.Node(); + meshDS->SetNodeInVolume(aNode, shapeID); } } } @@ -698,22 +676,16 @@ void StdMeshers_Penta_3D::MakeVolumeMesh() if ( aN.size() < nbFaceNodes * 2 ) aN.resize( nbFaceNodes * 2 ); // - k=0; - aItNodes=pE0->nodesIterator(); - while (aItNodes->more()) { - //const SMDS_MeshElement* pNode = aItNodes->next(); - const SMDS_MeshNode* pNode = - static_cast (aItNodes->next()); - if(myTool->IsMedium(pNode)) - continue; + for ( k=0; kGetNode(k); +// if(myTool->IsMedium(pNode)) +// continue; aID0 = pNode->GetID(); aJ[k] = GetIndexOnLayer(aID0); if (!myErrorStatus->IsOK()) { MESSAGE("StdMeshers_Penta_3D::MakeVolumeMesh"); - return; + return; } - // - ++k; } // bool forward = true; @@ -721,15 +693,15 @@ void StdMeshers_Penta_3D::MakeVolumeMesh() i1=i; i2=i+1; for(j=0; jGetMeshDS(); @@ -820,7 +790,7 @@ void StdMeshers_Penta_3D::MakeMeshOnFxy1() itn = aSM0->GetNodes(); aNbNodes = aSM0->NbNodes(); //printf("** aNbNodes=%d\n", aNbNodes); - + myTool->SetSubShape( aFxy1 ); // to set medium nodes to aFxy1 // // set elements on aFxy1 vector aNodes1; @@ -849,7 +819,7 @@ void StdMeshers_Penta_3D::MakeMeshOnFxy1() aJ = GetIndexOnLayer(aID0); if (!myErrorStatus->IsOK()) { MESSAGE("StdMeshers_Penta_3D::MakeMeshOnFxy1() "); - return; + return; } // ij = aLevel*myJSize + aJ; @@ -861,16 +831,17 @@ void StdMeshers_Penta_3D::MakeMeshOnFxy1() SMDS_MeshFace * face = 0; switch ( aNbNodes ) { case 3: - face = faceHelper.AddFace(aNodes1[0], aNodes1[1], aNodes1[2]); + face = myTool->AddFace(aNodes1[0], aNodes1[1], aNodes1[2]); break; case 4: - face = faceHelper.AddFace(aNodes1[0], aNodes1[1], aNodes1[2], aNodes1[3]); + face = myTool->AddFace(aNodes1[0], aNodes1[1], aNodes1[2], aNodes1[3]); break; default: continue; } meshDS->SetMeshElementOnShape(face, aFxy1); } + myTool->SetSubShape( myShape ); // update compute state of top face submesh aSubMesh1->ComputeStateEngine( SMESH_subMesh::CHECK_COMPUTE_STATE ); @@ -878,7 +849,8 @@ void StdMeshers_Penta_3D::MakeMeshOnFxy1() // assure that mesh on the top face will be cleaned when it is cleaned // on the bottom face SMESH_subMesh* volSM = pMesh->GetSubMesh( myTool->GetSubShape() ); - volSM->SetEventListener( new SMESH_subMeshEventListener(true), + volSM->SetEventListener( new SMESH_subMeshEventListener(true, // deletable by SMESH_subMesh + "StdMeshers_Penta_3D"), SMESH_subMeshEventListenerData::MakeData( aSubMesh1 ), aSubMesh0 ); // translate CLEAN event of aSubMesh0 to aSubMesh1 } @@ -937,8 +909,8 @@ void StdMeshers_Penta_3D::MakeConnectingMap() //purpose : //======================================================================= void StdMeshers_Penta_3D::CreateNode(const bool bIsUpperLayer, - const gp_XYZ& aParams, - StdMeshers_TNode& aTN) + const gp_XYZ& aParams, + StdMeshers_TNode& aTN) { double aX, aY, aZ; // @@ -947,15 +919,15 @@ void StdMeshers_Penta_3D::CreateNode(const bool bIsUpperLayer, SMDS_MeshNode* pNode=NULL; aTN.SetNode(pNode); // -// if (bIsUpperLayer) { -// // point on face Fxy1 -// const TopoDS_Shape& aS=myBlock.Shape(SMESH_Block::ID_Fxy1); -// myBlock.Point(aParams, aS, aP); -// } -// else { -// // point inside solid -// myBlock.Point(aParams, aP); -// } + // if (bIsUpperLayer) { + // // point on face Fxy1 + // const TopoDS_Shape& aS=myBlock.Shape(SMESH_Block::ID_Fxy1); + // myBlock.Point(aParams, aS, aP); + // } + // else { + // // point inside solid + // myBlock.Point(aParams, aP); + // } if (bIsUpperLayer) { double u = aParams.X(), v = aParams.Y(); double u1 = ( 1. - u ), v1 = ( 1. - v ); @@ -973,11 +945,11 @@ void StdMeshers_Penta_3D::CreateNode(const bool bIsUpperLayer, SMESH_Block::ShellPoint( aParams, myShapeXYZ, aP.ChangeCoord() ); } // -// iErr=myBlock.ErrorStatus(); -// if (iErr) { -// myErrorStatus=12; // can not find the node point; -// return; -// } + // iErr=myBlock.ErrorStatus(); + // if (iErr) { + // myErrorStatus=12; // can not find the node point; + // return; + // } // aX=aP.X(); aY=aP.Y(); aZ=aP.Z(); // @@ -985,7 +957,7 @@ void StdMeshers_Penta_3D::CreateNode(const bool bIsUpperLayer, SMESHDS_Mesh* pMeshDS = pMesh->GetMeshDS(); // pNode = pMeshDS->AddNode(aX, aY, aZ); - + aTN.SetNode(pNode); } @@ -994,42 +966,42 @@ void StdMeshers_Penta_3D::CreateNode(const bool bIsUpperLayer, //purpose : //======================================================================= void StdMeshers_Penta_3D::ShapeSupportID(const bool bIsUpperLayer, - const SMESH_Block::TShapeID aBNSSID, - SMESH_Block::TShapeID& aSSID) + const SMESH_Block::TShapeID aBNSSID, + SMESH_Block::TShapeID& aSSID) { switch (aBNSSID) { - case SMESH_Block::ID_V000: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V001 : SMESH_Block::ID_E00z; - break; - case SMESH_Block::ID_V100: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V101 : SMESH_Block::ID_E10z; - break; - case SMESH_Block::ID_V110: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V111 : SMESH_Block::ID_E11z; - break; - case SMESH_Block::ID_V010: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V011 : SMESH_Block::ID_E01z; - break; - case SMESH_Block::ID_Ex00: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_Ex01 : SMESH_Block::ID_Fx0z; - break; - case SMESH_Block::ID_Ex10: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_Ex11 : SMESH_Block::ID_Fx1z; - break; - case SMESH_Block::ID_E0y0: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_E0y1 : SMESH_Block::ID_F0yz; - break; - case SMESH_Block::ID_E1y0: - aSSID=(bIsUpperLayer) ? SMESH_Block::ID_E1y1 : SMESH_Block::ID_F1yz; - break; - case SMESH_Block::ID_Fxy0: - aSSID=SMESH_Block::ID_NONE;//(bIsUpperLayer) ? Shape_ID_Fxy1 : Shape_ID_NONE; - break; - default: - aSSID=SMESH_Block::ID_NONE; - myErrorStatus->myName=10; // Can not find supporting shape ID - myErrorStatus->myComment = "Internal error of StdMeshers_Penta_3D"; - break; + case SMESH_Block::ID_V000: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V001 : SMESH_Block::ID_E00z; + break; + case SMESH_Block::ID_V100: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V101 : SMESH_Block::ID_E10z; + break; + case SMESH_Block::ID_V110: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V111 : SMESH_Block::ID_E11z; + break; + case SMESH_Block::ID_V010: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_V011 : SMESH_Block::ID_E01z; + break; + case SMESH_Block::ID_Ex00: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_Ex01 : SMESH_Block::ID_Fx0z; + break; + case SMESH_Block::ID_Ex10: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_Ex11 : SMESH_Block::ID_Fx1z; + break; + case SMESH_Block::ID_E0y0: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_E0y1 : SMESH_Block::ID_F0yz; + break; + case SMESH_Block::ID_E1y0: + aSSID=(bIsUpperLayer) ? SMESH_Block::ID_E1y1 : SMESH_Block::ID_F1yz; + break; + case SMESH_Block::ID_Fxy0: + aSSID=SMESH_Block::ID_NONE;//(bIsUpperLayer) ? Shape_ID_Fxy1 : Shape_ID_NONE; + break; + default: + aSSID=SMESH_Block::ID_NONE; + myErrorStatus->myName=10; // Can not find supporting shape ID + myErrorStatus->myComment = "Internal error of StdMeshers_Penta_3D"; + break; } return; } @@ -1066,25 +1038,25 @@ void StdMeshers_Penta_3D::MakeBlock() const SMDS_MeshElement * pElement = itf->next(); aElementType = pElement->GetType(); if (aElementType==SMDSAbs_Face) { - iNbNodes = pElement->NbNodes(); - if ( iNbNodes==3 || (myCreateQuadratic && iNbNodes==6) ) { - aFTr = aF; - ++iCnt; - if (iCnt>1) { - // \begin{E.A.} - // The current algorithm fails if there is more that one - // face wich contains triangles ... - // In that case, replace return by break to try another - // method (coded in "if (iCnt != 1) { ... }") - // + iNbNodes = pElement->NbNodes(); + if ( iNbNodes==3 || (pElement->IsQuadratic() && iNbNodes==6) ) { + aFTr = aF; + ++iCnt; + if (iCnt>1) { + // \begin{E.A.} + // The current algorithm fails if there is more that one + // face wich contains triangles ... + // In that case, replace return by break to try another + // method (coded in "if (iCnt != 1) { ... }") + // // MESSAGE("StdMeshers_Penta_3D::MakeBlock() "); - // myErrorStatus=5; // more than one face has triangulation - // return; - break; - // \end{E.A.} - } - break; // next face - } + // myErrorStatus=5; // more than one face has triangulation + // return; + break; + // \end{E.A.} + } + break; // next face + } } } } @@ -1153,85 +1125,85 @@ void StdMeshers_Penta_3D::MakeBlock() int has_only_quad_f6 = 1; // for (i=1; i<=iNbF; ++i) { - int ok = 1; - const TopoDS_Shape& aF = aM(i); - SMESH_subMesh *aSubMesh = pMesh->GetSubMeshContaining(aF); - SMESHDS_SubMesh *aSM = aSubMesh->GetSubMeshDS(); - SMDS_ElemIteratorPtr itf = aSM->GetElements(); - while(itf->more()) { - const SMDS_MeshElement * pElement = itf->next(); - aElementType = pElement->GetType(); - if (aElementType==SMDSAbs_Face) { - iNbNodes = pElement->NbNodes(); - if ( iNbNodes!=4 ) { - ok = 0; - break ; - } - } - } - if (i==1) has_only_quad_f1 = ok ; - if (i==2) has_only_quad_f2 = ok ; - if (i==3) has_only_quad_f3 = ok ; - if (i==4) has_only_quad_f4 = ok ; - if (i==5) has_only_quad_f5 = ok ; - if (i==6) has_only_quad_f6 = ok ; + int ok = 1; + const TopoDS_Shape& aF = aM(i); + SMESH_subMesh *aSubMesh = pMesh->GetSubMeshContaining(aF); + SMESHDS_SubMesh *aSM = aSubMesh->GetSubMeshDS(); + SMDS_ElemIteratorPtr itf = aSM->GetElements(); + while(itf->more()) { + const SMDS_MeshElement * pElement = itf->next(); + aElementType = pElement->GetType(); + if (aElementType==SMDSAbs_Face) { + iNbNodes = pElement->NbNodes(); + if ( iNbNodes!=4 ) { + ok = 0; + break ; + } + } + } + if (i==1) has_only_quad_f1 = ok ; + if (i==2) has_only_quad_f2 = ok ; + if (i==3) has_only_quad_f3 = ok ; + if (i==4) has_only_quad_f4 = ok ; + if (i==5) has_only_quad_f5 = ok ; + if (i==6) has_only_quad_f6 = ok ; } // TopTools_IndexedMapOfShape aE; TopExp::MapShapes(myShape, TopAbs_EDGE, aE); int iNbE = aE.Extent(); if (iNbE == 12) { - // - int nb_e01 = pMesh->GetSubMeshContaining(aE(1))->GetSubMeshDS()->NbElements(); - int nb_e02 = pMesh->GetSubMeshContaining(aE(2))->GetSubMeshDS()->NbElements(); - int nb_e03 = pMesh->GetSubMeshContaining(aE(3))->GetSubMeshDS()->NbElements(); - int nb_e04 = pMesh->GetSubMeshContaining(aE(4))->GetSubMeshDS()->NbElements(); - int nb_e05 = pMesh->GetSubMeshContaining(aE(5))->GetSubMeshDS()->NbElements(); - int nb_e06 = pMesh->GetSubMeshContaining(aE(6))->GetSubMeshDS()->NbElements(); - int nb_e07 = pMesh->GetSubMeshContaining(aE(7))->GetSubMeshDS()->NbElements(); - int nb_e08 = pMesh->GetSubMeshContaining(aE(8))->GetSubMeshDS()->NbElements(); - int nb_e09 = pMesh->GetSubMeshContaining(aE(9))->GetSubMeshDS()->NbElements(); - int nb_e10 = pMesh->GetSubMeshContaining(aE(10))->GetSubMeshDS()->NbElements(); - int nb_e11 = pMesh->GetSubMeshContaining(aE(11))->GetSubMeshDS()->NbElements(); - int nb_e12 = pMesh->GetSubMeshContaining(aE(12))->GetSubMeshDS()->NbElements(); - // - int nb_ok = 0 ; - // - if ( (nb_e01==nb_e03) && (nb_e03==nb_e05) && (nb_e05==nb_e07) ) { - if ( has_only_quad_f1 && has_only_quad_f2 && has_only_quad_f3 && has_only_quad_f4 ) { - if ( (nb_e09==nb_e10) && (nb_e08==nb_e06) && (nb_e11==nb_e12) && (nb_e04==nb_e02) ) { - if (nb_f5==nb_f6) { - nb_ok += 1; - aFTr = aM(5); - } - } - } - } - if ( (nb_e02==nb_e04) && (nb_e04==nb_e06) && (nb_e06==nb_e08) ) { - if ( has_only_quad_f1 && has_only_quad_f2 && has_only_quad_f5 && has_only_quad_f6 ) { - if ( (nb_e01==nb_e03) && (nb_e10==nb_e12) && (nb_e05==nb_e07) && (nb_e09==nb_e11) ) { - if (nb_f3==nb_f4) { - nb_ok += 1; - aFTr = aM(3); - } - } - } - } - if ( (nb_e09==nb_e10) && (nb_e10==nb_e11) && (nb_e11==nb_e12) ) { - if ( has_only_quad_f3 && has_only_quad_f4 && has_only_quad_f5 && has_only_quad_f6 ) { - if ( (nb_e01==nb_e05) && (nb_e02==nb_e06) && (nb_e03==nb_e07) && (nb_e04==nb_e08) ) { - if (nb_f1==nb_f2) { - nb_ok += 1; - aFTr = aM(1); - } - } - } - } - // - if ( nb_ok == 1 ) { - isOK = 1; - } - // + // + int nb_e01 = pMesh->GetSubMeshContaining(aE(1))->GetSubMeshDS()->NbElements(); + int nb_e02 = pMesh->GetSubMeshContaining(aE(2))->GetSubMeshDS()->NbElements(); + int nb_e03 = pMesh->GetSubMeshContaining(aE(3))->GetSubMeshDS()->NbElements(); + int nb_e04 = pMesh->GetSubMeshContaining(aE(4))->GetSubMeshDS()->NbElements(); + int nb_e05 = pMesh->GetSubMeshContaining(aE(5))->GetSubMeshDS()->NbElements(); + int nb_e06 = pMesh->GetSubMeshContaining(aE(6))->GetSubMeshDS()->NbElements(); + int nb_e07 = pMesh->GetSubMeshContaining(aE(7))->GetSubMeshDS()->NbElements(); + int nb_e08 = pMesh->GetSubMeshContaining(aE(8))->GetSubMeshDS()->NbElements(); + int nb_e09 = pMesh->GetSubMeshContaining(aE(9))->GetSubMeshDS()->NbElements(); + int nb_e10 = pMesh->GetSubMeshContaining(aE(10))->GetSubMeshDS()->NbElements(); + int nb_e11 = pMesh->GetSubMeshContaining(aE(11))->GetSubMeshDS()->NbElements(); + int nb_e12 = pMesh->GetSubMeshContaining(aE(12))->GetSubMeshDS()->NbElements(); + // + int nb_ok = 0 ; + // + if ( (nb_e01==nb_e03) && (nb_e03==nb_e05) && (nb_e05==nb_e07) ) { + if ( has_only_quad_f1 && has_only_quad_f2 && has_only_quad_f3 && has_only_quad_f4 ) { + if ( (nb_e09==nb_e10) && (nb_e08==nb_e06) && (nb_e11==nb_e12) && (nb_e04==nb_e02) ) { + if (nb_f5==nb_f6) { + nb_ok += 1; + aFTr = aM(5); + } + } + } + } + if ( (nb_e02==nb_e04) && (nb_e04==nb_e06) && (nb_e06==nb_e08) ) { + if ( has_only_quad_f1 && has_only_quad_f2 && has_only_quad_f5 && has_only_quad_f6 ) { + if ( (nb_e01==nb_e03) && (nb_e10==nb_e12) && (nb_e05==nb_e07) && (nb_e09==nb_e11) ) { + if (nb_f3==nb_f4) { + nb_ok += 1; + aFTr = aM(3); + } + } + } + } + if ( (nb_e09==nb_e10) && (nb_e10==nb_e11) && (nb_e11==nb_e12) ) { + if ( has_only_quad_f3 && has_only_quad_f4 && has_only_quad_f5 && has_only_quad_f6 ) { + if ( (nb_e01==nb_e05) && (nb_e02==nb_e06) && (nb_e03==nb_e07) && (nb_e04==nb_e08) ) { + if (nb_f1==nb_f2) { + nb_ok += 1; + aFTr = aM(1); + } + } + } + } + // + if ( nb_ok == 1 ) { + isOK = 1; + } + // } } if (!isOK) { @@ -1284,11 +1256,11 @@ void StdMeshers_Penta_3D::MakeBlock() const TopoDS_Edge& aE=TopoDS::Edge(aEx); TopExp::Vertices(aE, aV[0], aV[1]); for (i=0; i<2; ++i) { - if (!aV[i].IsSame(aV000)) { - aV001=aV[i]; - bFound=!bFound; - break; - } + if (!aV[i].IsSame(aV000)) { + aV001=aV[i]; + bFound=!bFound; + break; + } } } } @@ -1365,8 +1337,8 @@ void StdMeshers_Penta_3D::CheckData() iNb=aM.Extent(); if (iNb!=iNbEx[i]){ MESSAGE("StdMeshers_Penta_3D::CheckData() "); - myErrorStatus->myName=4; // number of subshape is not compatible - myErrorStatus->myComment="Wrong number of subshapes of a block"; + myErrorStatus->myName=4; // number of sub-shape is not compatible + myErrorStatus->myComment="Wrong number of sub-shapes of a block"; return; } } @@ -1432,7 +1404,7 @@ bool StdMeshers_Penta_3D::LoadIJNodes(StdMeshers_IJNodeMap & theIJNodes, SMESHDS_SubMesh* smVft = theMesh->MeshElements( vft ); if (!smFace || !smb || !smt || !sm1 || !sm2 || !smVfb || !smVlb || !smVft ) { MESSAGE( "NULL submesh " <NbNodes() != smt->NbNodes() || sm1->NbNodes() != sm2->NbNodes() ) { @@ -1453,13 +1425,13 @@ bool StdMeshers_Penta_3D::LoadIJNodes(StdMeshers_IJNodeMap & theIJNodes, int nf = sm1->NbNodes()*smb->NbNodes() - n3*n4; if( nf != smFace->NbNodes() ) { MESSAGE( "Wrong nb face nodes: " << - sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); + sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); return false; } } else { MESSAGE( "Wrong nb face nodes: " << - sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); + sm1->NbNodes()<<" "<NbNodes()<<" "<NbNodes()); return false; } } @@ -1494,7 +1466,7 @@ bool StdMeshers_Penta_3D::LoadIJNodes(StdMeshers_IJNodeMap & theIJNodes, if(myTool->IsMedium(node)) continue; const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); + dynamic_cast( node->GetPosition() ); if ( !pos ) { return false; } @@ -1517,7 +1489,7 @@ bool StdMeshers_Penta_3D::LoadIJNodes(StdMeshers_IJNodeMap & theIJNodes, if(myTool->IsMedium(node)) continue; const SMDS_EdgePosition* pos = - dynamic_cast( node->GetPosition().get() ); + dynamic_cast( node->GetPosition() ); if ( !pos ) { return false; } @@ -1594,7 +1566,7 @@ bool StdMeshers_Penta_3D::LoadIJNodes(StdMeshers_IJNodeMap & theIJNodes, } } else if ( (nbFaceNodes==3 || (myCreateQuadratic && nbFaceNodes==6) ) && - n3 == par_nVec_1->second[ row ] ) { + n3 == par_nVec_1->second[ row ] ) { n1 = n3; } else { @@ -1678,7 +1650,7 @@ SMESH_ComputeErrorPtr StdMeshers_SMESHBlock::GetError() const case 3: text = "Internal error of StdMeshers_Penta_3D"; break; case 4: text = "Can't compute normalized parameters of a point inside a block"; break; case 5: text = "Can't compute coordinates by normalized parameters inside a block"; break; - case 6: text = "Can't detect block subshapes. Not a block?"; break; + case 6: text = "Can't detect block sub-shapes. Not a block?"; break; } if (!text.empty()) err->myName = myErrorStatus; @@ -1701,8 +1673,8 @@ void StdMeshers_SMESHBlock::Load(const TopoDS_Shell& theShell) //purpose : //======================================================================= void StdMeshers_SMESHBlock::Load(const TopoDS_Shell& theShell, - const TopoDS_Vertex& theV000, - const TopoDS_Vertex& theV001) + const TopoDS_Vertex& theV000, + const TopoDS_Vertex& theV001) { myErrorStatus=0; // @@ -1723,7 +1695,7 @@ void StdMeshers_SMESHBlock::Load(const TopoDS_Shell& theShell, //purpose : //======================================================================= void StdMeshers_SMESHBlock::ComputeParameters(const gp_Pnt& thePnt, - gp_XYZ& theXYZ) + gp_XYZ& theXYZ) { ComputeParameters(thePnt, myShell, theXYZ); } @@ -1733,7 +1705,7 @@ void StdMeshers_SMESHBlock::ComputeParameters(const gp_Pnt& thePnt, //purpose : //======================================================================= void StdMeshers_SMESHBlock::ComputeParameters(const gp_Pnt& thePnt, - const TopoDS_Shape& theShape, + const TopoDS_Shape& theShape, gp_XYZ& theXYZ) { myErrorStatus=0; @@ -1771,7 +1743,7 @@ void StdMeshers_SMESHBlock::ComputeParameters(const double& theU, return; } if ( SMESH_Block::IsEdgeID( aID )) - bOk = myTBlock.EdgeParameters( aID, theU, theXYZ ); + bOk = myTBlock.EdgeParameters( aID, theU, theXYZ ); if (!bOk) { myErrorStatus=4; // problems with computation Parameters return; @@ -1782,8 +1754,7 @@ void StdMeshers_SMESHBlock::ComputeParameters(const double& theU, //function : Point //purpose : //======================================================================= - void StdMeshers_SMESHBlock::Point(const gp_XYZ& theParams, - gp_Pnt& aP3D) +void StdMeshers_SMESHBlock::Point(const gp_XYZ& theParams, gp_Pnt& aP3D) { TopoDS_Shape aS; // @@ -1794,9 +1765,9 @@ void StdMeshers_SMESHBlock::ComputeParameters(const double& theU, //function : Point //purpose : //======================================================================= - void StdMeshers_SMESHBlock::Point(const gp_XYZ& theParams, - const TopoDS_Shape& theShape, - gp_Pnt& aP3D) +void StdMeshers_SMESHBlock::Point(const gp_XYZ& theParams, + const TopoDS_Shape& theShape, + gp_Pnt& aP3D) { myErrorStatus = 0; // @@ -1882,3 +1853,112 @@ const TopoDS_Shape& StdMeshers_SMESHBlock::Shape(const int theID) } +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= +bool StdMeshers_Penta_3D::Evaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + MapShapeNbElems& aResMap) +{ + MESSAGE("StdMeshers_Penta_3D::Evaluate()"); + + // find face contains only triangles + vector < SMESH_subMesh * >meshFaces; + TopTools_SequenceOfShape aFaces; + int NumBase = 0, i = 0; + for (TopExp_Explorer exp(aShape, TopAbs_FACE); exp.More(); exp.Next()) { + i++; + aFaces.Append(exp.Current()); + SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current()); + meshFaces.push_back(aSubMesh); + MapShapeNbElemsItr anIt = aResMap.find(meshFaces[i]); + if( anIt == aResMap.end() ) { + NumBase = 0; + break; + } + std::vector aVec = (*anIt).second; + int nbtri = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + int nbqua = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + if( nbtri>0 && nbqua==0 ) { + NumBase = i; + } + } + + if(NumBase==0) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; imyName = COMPERR_ALGO_FAILED; + myErrorStatus->myComment = "Submesh can not be evaluated"; + return false; + } + + // find number of 1d elems for base face + int nb1d = 0; + TopTools_MapOfShape Edges1; + for (TopExp_Explorer exp(aFaces.Value(NumBase), TopAbs_EDGE); exp.More(); exp.Next()) { + Edges1.Add(exp.Current()); + SMESH_subMesh *sm = aMesh.GetSubMesh(exp.Current()); + if( sm ) { + MapShapeNbElemsItr anIt = aResMap.find(sm); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + } + } + // find face opposite to base face + int OppNum = 0; + for(i=1; i<=6; i++) { + if(i==NumBase) continue; + bool IsOpposite = true; + for(TopExp_Explorer exp(aFaces.Value(i), TopAbs_EDGE); exp.More(); exp.Next()) { + if( Edges1.Contains(exp.Current()) ) { + IsOpposite = false; + break; + } + } + if(IsOpposite) { + OppNum = i; + break; + } + } + // find number of 2d elems on side faces + int nb2d = 0; + for(i=1; i<=6; i++) { + if( i==OppNum || i==NumBase ) continue; + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[i-1] ); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb2d += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + } + + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[NumBase-1] ); + std::vector aVec = (*anIt).second; + int nb2d_face0 = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + int nb0d_face0 = aVec[SMDSEntity_Node]; + + anIt = aResMap.find( meshFaces[OppNum-1] ); + for(i=SMDSEntity_Node; i aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; i > StdMeshers_IJNodeMap; @@ -57,26 +59,26 @@ public: void Load (const TopoDS_Shell& theShell); void Load (const TopoDS_Shell& theShell, - const TopoDS_Vertex& theV000, - const TopoDS_Vertex& theV001); + const TopoDS_Vertex& theV000, + const TopoDS_Vertex& theV001); void ComputeParameters(const gp_Pnt& thePnt, - gp_XYZ& theXYZ); + gp_XYZ& theXYZ); void ComputeParameters(const gp_Pnt& thePnt, - const TopoDS_Shape& theShape, - gp_XYZ& theXYZ); + const TopoDS_Shape& theShape, + gp_XYZ& theXYZ); void ComputeParameters(const double& theU, - const TopoDS_Shape& theShape, - gp_XYZ& theXYZ); + const TopoDS_Shape& theShape, + gp_XYZ& theXYZ); void Point(const gp_XYZ& theParams, - gp_Pnt& thePnt); + gp_Pnt& thePnt); void Point(const gp_XYZ& theParams, - const TopoDS_Shape& theShape, - gp_Pnt& thePnt); + const TopoDS_Shape& theShape, + gp_Pnt& thePnt); int ShapeID(const TopoDS_Shape& theShape); @@ -204,6 +206,9 @@ class STDMESHERS_EXPORT StdMeshers_Penta_3D { // The key of theIJNodes map is a normalized parameter of each // 0-the node on theBaseEdge. + bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + protected: // methods @@ -219,17 +224,17 @@ class STDMESHERS_EXPORT StdMeshers_Penta_3D { std::vector*& aCol2); void ShapeSupportID(const bool theIsUpperLayer, - const SMESH_Block::TShapeID theBNSSID, - SMESH_Block::TShapeID& theSSID); + const SMESH_Block::TShapeID theBNSSID, + SMESH_Block::TShapeID& theSSID); void FindNodeOnShape(const TopoDS_Shape& aS, - const gp_XYZ& aParams, + const gp_XYZ& aParams, const int z, - StdMeshers_TNode& aTN); + StdMeshers_TNode& aTN); void CreateNode(const bool theIsUpperLayer, - const gp_XYZ& aParams, - StdMeshers_TNode& aTN); + const gp_XYZ& aParams, + StdMeshers_TNode& aTN); void ClearMeshOnFxy1(); diff --git a/src/StdMeshers/StdMeshers_Prism_3D.cxx b/src/StdMeshers/StdMeshers_Prism_3D.cxx index 2ab01d917..c4c421c80 100644 --- a/src/StdMeshers/StdMeshers_Prism_3D.cxx +++ b/src/StdMeshers/StdMeshers_Prism_3D.cxx @@ -1,25 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_Prism_3D.cxx // Module : SMESH // Created : Fri Oct 20 11:37:07 2006 @@ -28,7 +28,7 @@ #include "StdMeshers_Prism_3D.hxx" #include "StdMeshers_ProjectionUtils.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMDS_VolumeTool.hxx" #include "SMDS_VolumeOfNodes.hxx" #include "SMDS_EdgePosition.hxx" @@ -37,12 +37,18 @@ #include "utilities.h" #include +#include #include #include +#include #include #include #include +#include +#include #include +#include +#include using namespace std; @@ -149,6 +155,152 @@ namespace { } params.push_back( parLast ); // 1. } + + //================================================================================ + /*! + * \brief Return coordinate system for z-th layer of nodes + */ + //================================================================================ + + gp_Ax2 getLayerCoordSys(const int z, + const vector< const TNodeColumn* >& columns, + int& xColumn) + { + // gravity center of a layer + gp_XYZ O(0,0,0); + int vertexCol = -1; + for ( int i = 0; i < columns.size(); ++i ) + { + O += gpXYZ( (*columns[ i ])[ z ]); + if ( vertexCol < 0 && + columns[ i ]->front()->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX ) + vertexCol = i; + } + O /= columns.size(); + + // Z axis + gp_Vec Z(0,0,0); + int iPrev = columns.size()-1; + for ( int i = 0; i < columns.size(); ++i ) + { + gp_Vec v1( O, gpXYZ( (*columns[ iPrev ])[ z ])); + gp_Vec v2( O, gpXYZ( (*columns[ i ] )[ z ])); + Z += v1 ^ v2; + iPrev = i; + } + + if ( vertexCol >= 0 ) + { + O = gpXYZ( (*columns[ vertexCol ])[ z ]); + } + if ( xColumn < 0 || xColumn >= columns.size() ) + { + // select a column for X dir + double maxDist = 0; + for ( int i = 0; i < columns.size(); ++i ) + { + double dist = ( O - gpXYZ((*columns[ i ])[ z ])).SquareModulus(); + if ( dist > maxDist ) + { + xColumn = i; + maxDist = dist; + } + } + } + + // X axis + gp_Vec X( O, gpXYZ( (*columns[ xColumn ])[ z ])); + + return gp_Ax2( O, Z, X); + } + + //================================================================================ + /*! + * \brief Removes submeshes meshed with regular grid from given list + * \retval int - nb of removed submeshes + */ + //================================================================================ + + int removeQuasiQuads(list< SMESH_subMesh* >& notQuadSubMesh) + { + int oldNbSM = notQuadSubMesh.size(); + SMESHDS_Mesh* mesh = notQuadSubMesh.front()->GetFather()->GetMeshDS(); + list< SMESH_subMesh* >::iterator smIt = notQuadSubMesh.begin(); +#define __NEXT_SM { ++smIt; continue; } + while ( smIt != notQuadSubMesh.end() ) + { + SMESH_subMesh* faceSm = *smIt; + SMESHDS_SubMesh* faceSmDS = faceSm->GetSubMeshDS(); + int nbQuads = faceSmDS->NbElements(); + if ( nbQuads == 0 ) __NEXT_SM; + + // get oredered edges + list< TopoDS_Edge > orderedEdges; + list< int > nbEdgesInWires; + TopoDS_Vertex V000; + int nbWires = SMESH_Block::GetOrderedEdges( TopoDS::Face( faceSm->GetSubShape() ), + V000, orderedEdges, nbEdgesInWires ); + if ( nbWires != 1 || nbEdgesInWires.front() <= 4 ) + __NEXT_SM; + + // get nb of segements on edges + list nbSegOnEdge; + list< TopoDS_Edge >::iterator edge = orderedEdges.begin(); + for ( ; edge != orderedEdges.end(); ++edge ) + { + if ( SMESHDS_SubMesh* edgeSmDS = mesh->MeshElements( *edge )) + nbSegOnEdge.push_back( edgeSmDS->NbElements() ); + else + nbSegOnEdge.push_back(0); + } + + // unite nbSegOnEdge of continues edges + int nbEdges = nbEdgesInWires.front(); + list::iterator nbSegIt = nbSegOnEdge.begin(); + for ( edge = orderedEdges.begin(); edge != orderedEdges.end(); ) + { + const TopoDS_Edge& e1 = *edge++; + const TopoDS_Edge& e2 = ( edge == orderedEdges.end() ? orderedEdges.front() : *edge ); + if ( SMESH_Algo::IsContinuous( e1, e2 )) + { + // common vertex of continues edges must be shared by two 2D mesh elems of geom face + TopoDS_Vertex vCommon = TopExp::LastVertex( e1, true ); + const SMDS_MeshNode* vNode = SMESH_Algo::VertexNode( vCommon, mesh ); + int nbF = 0; + if ( vNode ) + { + SMDS_ElemIteratorPtr fIt = vNode->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) + nbF += faceSmDS->Contains( fIt->next() ); + } + list::iterator nbSegIt1 = nbSegIt++; + if ( !vNode || nbF == 2 ) // !vNode - two edges can be meshed as one + { + // unite + if ( nbSegIt == nbSegOnEdge.end() ) nbSegIt = nbSegOnEdge.begin(); + *nbSegIt += *nbSegIt1; + nbSegOnEdge.erase( nbSegIt1 ); + --nbEdges; + } + } + else + { + ++nbSegIt; + } + } + vector nbSegVec( nbSegOnEdge.begin(), nbSegOnEdge.end()); + if ( nbSegVec.size() == 4 && + nbSegVec[0] == nbSegVec[2] && + nbSegVec[1] == nbSegVec[3] && + nbSegVec[0] * nbSegVec[1] == nbQuads + ) + smIt = notQuadSubMesh.erase( smIt ); + else + __NEXT_SM; + } + + return oldNbSM - notQuadSubMesh.size(); + } } //======================================================================= @@ -160,7 +312,7 @@ StdMeshers_Prism_3D::StdMeshers_Prism_3D(int hypId, int studyId, SMESH_Gen* gen) :SMESH_3D_Algo(hypId, studyId, gen) { _name = "Prism_3D"; - _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit per shape type + _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit per shape type myProjectTriangles = false; } @@ -232,7 +384,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh myHelper->IsQuadraticSubMesh( theShape ); - // Analyse mesh and geomerty to find block subshapes and submeshes + // Analyse mesh and geomerty to find block sub-shapes and submeshes if ( !myBlock.Init( myHelper, theShape )) return error( myBlock.GetError()); @@ -255,75 +407,115 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh // Projections on the top and bottom faces are taken from nodes existing // on these faces; find correspondence between bottom and top nodes myBotToColumnMap.clear(); - if ( !assocOrProjBottom2Top() ) // it also fill myBotToColumnMap + if ( !assocOrProjBottom2Top() ) // it also fills myBotToColumnMap return false; // Create nodes inside the block - // loop on nodes inside the bottom face - TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin(); - for ( ; bot_column != myBotToColumnMap.end(); ++bot_column ) + // try to use transformation (issue 0020680) + vector trsf; + if ( myBlock.GetLayersTransformation(trsf)) { - const TNode& tBotNode = bot_column->first; // bottom TNode - if ( tBotNode.GetPositionType() != SMDS_TOP_FACE ) - continue; // node is not inside face - - // column nodes; middle part of the column are zero pointers - TNodeColumn& column = bot_column->second; - - // bottom node parameters and coords - myShapeXYZ[ ID_BOT_FACE ] = tBotNode.GetCoords(); - gp_XYZ botParams = tBotNode.GetParams(); - - // compute top node parameters - myShapeXYZ[ ID_TOP_FACE ] = gpXYZ( column.back() ); - gp_XYZ topParams = botParams; - topParams.SetZ( 1 ); - if ( column.size() > 2 ) { - gp_Pnt topCoords = myShapeXYZ[ ID_TOP_FACE ]; - if ( !myBlock.ComputeParameters( topCoords, topParams, ID_TOP_FACE, topParams )) - return error(TCom("Can't compute normalized parameters ") - << "for node " << column.back()->GetID() - << " on the face #"<< column.back()->GetPosition()->GetShapeId() ); - } + // loop on nodes inside the bottom face + TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin(); + for ( ; bot_column != myBotToColumnMap.end(); ++bot_column ) + { + const TNode& tBotNode = bot_column->first; // bottom TNode + if ( tBotNode.GetPositionType() != SMDS_TOP_FACE ) + continue; // node is not inside face + + // column nodes; middle part of the column are zero pointers + TNodeColumn& column = bot_column->second; + TNodeColumn::iterator columnNodes = column.begin(); + for ( int z = 0; columnNodes != column.end(); ++columnNodes, ++z) + { + const SMDS_MeshNode* & node = *columnNodes; + if ( node ) continue; // skip bottom or top node - // vertical loop - TNodeColumn::iterator columnNodes = column.begin(); - for ( int z = 0; columnNodes != column.end(); ++columnNodes, ++z) + gp_XYZ coords = tBotNode.GetCoords(); + trsf[z-1].Transforms( coords ); + node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() ); + meshDS->SetNodeInVolume( node, volumeID ); + } + } // loop on bottom nodes + } + else // use block approach + { + // loop on nodes inside the bottom face + TNode prevBNode; + TNode2ColumnMap::iterator bot_column = myBotToColumnMap.begin(); + for ( ; bot_column != myBotToColumnMap.end(); ++bot_column ) { - const SMDS_MeshNode* & node = *columnNodes; - if ( node ) continue; // skip bottom or top node - - // params of a node to create - double rz = (double) z / (double) ( column.size() - 1 ); - gp_XYZ params = botParams * ( 1 - rz ) + topParams * rz; - - // set coords on all faces and nodes - const int nbSideFaces = 4; - int sideFaceIDs[nbSideFaces] = { SMESH_Block::ID_Fx0z, - SMESH_Block::ID_Fx1z, - SMESH_Block::ID_F0yz, - SMESH_Block::ID_F1yz }; - for ( int iF = 0; iF < nbSideFaces; ++iF ) - if ( !setFaceAndEdgesXYZ( sideFaceIDs[ iF ], params, z )) - return false; - - // compute coords for a new node - gp_XYZ coords; - if ( !SMESH_Block::ShellPoint( params, myShapeXYZ, coords )) - return error("Can't compute coordinates by normalized parameters"); - - SHOWYXZ("TOPFacePoint ",myShapeXYZ[ ID_TOP_FACE]); - SHOWYXZ("BOT Node "<< tBotNode.myNode->GetID(),gpXYZ(tBotNode.myNode)); - SHOWYXZ("ShellPoint ",coords); - - // create a node - node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() ); - meshDS->SetNodeInVolume( node, volumeID ); - } - } // loop on bottom nodes + const TNode& tBotNode = bot_column->first; // bottom TNode + if ( tBotNode.GetPositionType() != SMDS_TOP_FACE ) + continue; // node is not inside face + + // column nodes; middle part of the column are zero pointers + TNodeColumn& column = bot_column->second; + + // compute bottom node parameters + gp_XYZ paramHint(-1,-1,-1); + if ( prevBNode.IsNeighbor( tBotNode )) + paramHint = prevBNode.GetParams(); + if ( !myBlock.ComputeParameters( tBotNode.GetCoords(), tBotNode.ChangeParams(), + ID_BOT_FACE, paramHint )) + return error(TCom("Can't compute normalized parameters for node ") + << tBotNode.myNode->GetID() << " on the face #" + << myBlock.SubMesh( ID_BOT_FACE )->GetId() ); + prevBNode = tBotNode; + + myShapeXYZ[ ID_BOT_FACE ] = tBotNode.GetCoords(); + gp_XYZ botParams = tBotNode.GetParams(); + + // compute top node parameters + myShapeXYZ[ ID_TOP_FACE ] = gpXYZ( column.back() ); + gp_XYZ topParams = botParams; + topParams.SetZ( 1 ); + if ( column.size() > 2 ) { + gp_Pnt topCoords = myShapeXYZ[ ID_TOP_FACE ]; + if ( !myBlock.ComputeParameters( topCoords, topParams, ID_TOP_FACE, topParams )) + return error(TCom("Can't compute normalized parameters ") + << "for node " << column.back()->GetID() + << " on the face #"<< column.back()->getshapeId() ); + } + // vertical loop + TNodeColumn::iterator columnNodes = column.begin(); + for ( int z = 0; columnNodes != column.end(); ++columnNodes, ++z) + { + const SMDS_MeshNode* & node = *columnNodes; + if ( node ) continue; // skip bottom or top node + + // params of a node to create + double rz = (double) z / (double) ( column.size() - 1 ); + gp_XYZ params = botParams * ( 1 - rz ) + topParams * rz; + + // set coords on all faces and nodes + const int nbSideFaces = 4; + int sideFaceIDs[nbSideFaces] = { SMESH_Block::ID_Fx0z, + SMESH_Block::ID_Fx1z, + SMESH_Block::ID_F0yz, + SMESH_Block::ID_F1yz }; + for ( int iF = 0; iF < nbSideFaces; ++iF ) + if ( !setFaceAndEdgesXYZ( sideFaceIDs[ iF ], params, z )) + return false; + + // compute coords for a new node + gp_XYZ coords; + if ( !SMESH_Block::ShellPoint( params, myShapeXYZ, coords )) + return error("Can't compute coordinates by normalized parameters"); + + SHOWYXZ("TOPFacePoint ",myShapeXYZ[ ID_TOP_FACE]); + SHOWYXZ("BOT Node "<< tBotNode.myNode->GetID(),gpXYZ(tBotNode.myNode)); + SHOWYXZ("ShellPoint ",coords); + + // create a node + node = meshDS->AddNode( coords.X(), coords.Y(), coords.Z() ); + meshDS->SetNodeInVolume( node, volumeID ); + } + } // loop on bottom nodes + } // Create volumes @@ -347,7 +539,7 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh { const SMDS_MeshNode* n = face->GetNode( i ); if ( n->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) { - bot_column = myBotToColumnMap.find( n ); + TNode2ColumnMap::iterator bot_column = myBotToColumnMap.find( n ); if ( bot_column == myBotToColumnMap.end() ) return error(TCom("No nodes found above node ") << n->GetID() ); columns[ i ] = & bot_column->second; @@ -362,10 +554,129 @@ bool StdMeshers_Prism_3D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theSh AddPrisms( columns, myHelper ); } // loop on bottom mesh faces + + // clear data + myBotToColumnMap.clear(); + myBlock.Clear(); return true; } + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_Prism_3D::Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& aResMap) +{ + // find face contains only triangles + vector < SMESH_subMesh * >meshFaces; + TopTools_SequenceOfShape aFaces; + int NumBase = 0, i = 0, NbQFs = 0; + for (TopExp_Explorer exp(theShape, TopAbs_FACE); exp.More(); exp.Next()) { + i++; + aFaces.Append(exp.Current()); + SMESH_subMesh *aSubMesh = theMesh.GetSubMesh(exp.Current()); + meshFaces.push_back(aSubMesh); + MapShapeNbElemsItr anIt = aResMap.find(meshFaces[i-1]); + if( anIt==aResMap.end() ) { + SMESH_ComputeErrorPtr& smError = aSubMesh->GetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + std::vector aVec = (*anIt).second; + int nbtri = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + int nbqua = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + if( nbtri==0 && nbqua>0 ) { + NbQFs++; + } + if( nbtri>0 ) { + NumBase = i; + } + } + + if(NbQFs<4) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + + if(NumBase==0) NumBase = 1; // only quads => set 1 faces as base + + // find number of 1d elems for base face + int nb1d = 0; + TopTools_MapOfShape Edges1; + for (TopExp_Explorer exp(aFaces.Value(NumBase), TopAbs_EDGE); exp.More(); exp.Next()) { + Edges1.Add(exp.Current()); + SMESH_subMesh *sm = theMesh.GetSubMesh(exp.Current()); + if( sm ) { + MapShapeNbElemsItr anIt = aResMap.find(sm); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb1d += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + } + } + // find face opposite to base face + int OppNum = 0; + for(i=1; i<=6; i++) { + if(i==NumBase) continue; + bool IsOpposite = true; + for(TopExp_Explorer exp(aFaces.Value(i), TopAbs_EDGE); exp.More(); exp.Next()) { + if( Edges1.Contains(exp.Current()) ) { + IsOpposite = false; + break; + } + } + if(IsOpposite) { + OppNum = i; + break; + } + } + // find number of 2d elems on side faces + int nb2d = 0; + for(i=1; i<=6; i++) { + if( i==OppNum || i==NumBase ) continue; + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[i-1] ); + if( anIt == aResMap.end() ) continue; + std::vector aVec = (*anIt).second; + nb2d += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + } + + MapShapeNbElemsItr anIt = aResMap.find( meshFaces[NumBase-1] ); + std::vector aVec = (*anIt).second; + bool IsQuadratic = (aVec[SMDSEntity_Quad_Triangle]>aVec[SMDSEntity_Triangle]) || + (aVec[SMDSEntity_Quad_Quadrangle]>aVec[SMDSEntity_Quadrangle]); + int nb2d_face0_3 = Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + int nb2d_face0_4 = Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + int nb0d_face0 = aVec[SMDSEntity_Node]; + int nb1d_face0_int = ( nb2d_face0_3*3 + nb2d_face0_4*4 - nb1d ) / 2; + + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; i & columns, SMESH_MesherHelper* helper) { - SMESHDS_Mesh * meshDS = helper->GetMeshDS(); - int shapeID = helper->GetSubShapeID(); - int nbNodes = columns.size(); int nbZ = columns[0]->size(); if ( nbZ < 2 ) return; @@ -390,87 +698,103 @@ void StdMeshers_Prism_3D::AddPrisms( vector & columns, int z = 1; switch ( nbNodes ) { case 3: { - const SMDS_MeshNode* botNodes[3] = { (*columns[0])[z-1], - (*columns[1])[z-1], - (*columns[2])[z-1] }; - const SMDS_MeshNode* topNodes[3] = { (*columns[0])[z], - (*columns[1])[z], - (*columns[2])[z] }; - SMDS_VolumeOfNodes tmpVol ( botNodes[0], botNodes[1], botNodes[2], - topNodes[0], topNodes[1], topNodes[2]); - vTool.Set( &tmpVol ); + SMDS_VolumeOfNodes tmpPenta ( (*columns[0])[z-1], // bottom + (*columns[1])[z-1], + (*columns[2])[z-1], + (*columns[0])[z], // top + (*columns[1])[z], + (*columns[2])[z] ); + vTool.Set( &tmpPenta ); isForward = vTool.IsForward(); break; } case 4: { - const SMDS_MeshNode* botNodes[4] = { (*columns[0])[z-1], (*columns[1])[z-1], - (*columns[2])[z-1], (*columns[3])[z-1] }; - const SMDS_MeshNode* topNodes[4] = { (*columns[0])[z], (*columns[1])[z], - (*columns[2])[z], (*columns[3])[z] }; - SMDS_VolumeOfNodes tmpVol ( botNodes[0], botNodes[1], botNodes[2], botNodes[3], - topNodes[0], topNodes[1], topNodes[2], topNodes[3]); - vTool.Set( &tmpVol ); + SMDS_VolumeOfNodes tmpHex( (*columns[0])[z-1], (*columns[1])[z-1], // bottom + (*columns[2])[z-1], (*columns[3])[z-1], + (*columns[0])[z], (*columns[1])[z], // top + (*columns[2])[z], (*columns[3])[z] ); + vTool.Set( &tmpHex ); isForward = vTool.IsForward(); break; } + default: + const int di = (nbNodes+1) / 3; + SMDS_VolumeOfNodes tmpVol ( (*columns[0] )[z-1], + (*columns[di] )[z-1], + (*columns[2*di])[z-1], + (*columns[0] )[z], + (*columns[di] )[z], + (*columns[2*di])[z] ); + vTool.Set( &tmpVol ); + isForward = vTool.IsForward(); } // vertical loop on columns - for ( z = 1; z < nbZ; ++z ) - { - SMDS_MeshElement* vol = 0; - switch ( nbNodes ) { - case 3: { - const SMDS_MeshNode* botNodes[3] = { (*columns[0])[z-1], - (*columns[1])[z-1], - (*columns[2])[z-1] }; - const SMDS_MeshNode* topNodes[3] = { (*columns[0])[z], - (*columns[1])[z], - (*columns[2])[z] }; - if ( isForward ) - vol = helper->AddVolume( botNodes[0], botNodes[1], botNodes[2], - topNodes[0], topNodes[1], topNodes[2]); - else - vol = helper->AddVolume( topNodes[0], topNodes[1], topNodes[2], - botNodes[0], botNodes[1], botNodes[2]); - break; - } - case 4: { - const SMDS_MeshNode* botNodes[4] = { (*columns[0])[z-1], (*columns[1])[z-1], - (*columns[2])[z-1], (*columns[3])[z-1] }; - const SMDS_MeshNode* topNodes[4] = { (*columns[0])[z], (*columns[1])[z], - (*columns[2])[z], (*columns[3])[z] }; - if ( isForward ) - vol = helper->AddVolume( botNodes[0], botNodes[1], botNodes[2], botNodes[3], - topNodes[0], topNodes[1], topNodes[2], topNodes[3]); - else - vol = helper->AddVolume( topNodes[0], topNodes[1], topNodes[2], topNodes[3], - botNodes[0], botNodes[1], botNodes[2], botNodes[3]); - break; - } - default: - // polyhedron - vector nodes( 2*nbNodes + 4*nbNodes); - vector quantities( 2 + nbNodes, 4 ); - quantities[0] = quantities[1] = nbNodes; - columns.resize( nbNodes + 1 ); - columns[ nbNodes ] = columns[ 0 ]; + helper->SetElementsOnShape( true ); + + switch ( nbNodes ) { + + case 3: { // ---------- pentahedra + const int i1 = isForward ? 1 : 2; + const int i2 = isForward ? 2 : 1; + for ( z = 1; z < nbZ; ++z ) + helper->AddVolume( (*columns[0 ])[z-1], // bottom + (*columns[i1])[z-1], + (*columns[i2])[z-1], + (*columns[0 ])[z], // top + (*columns[i1])[z], + (*columns[i2])[z] ); + break; + } + case 4: { // ---------- hexahedra + const int i1 = isForward ? 1 : 3; + const int i3 = isForward ? 3 : 1; + for ( z = 1; z < nbZ; ++z ) + helper->AddVolume( (*columns[0])[z-1], (*columns[i1])[z-1], // bottom + (*columns[2])[z-1], (*columns[i3])[z-1], + (*columns[0])[z], (*columns[i1])[z], // top + (*columns[2])[z], (*columns[i3])[z] ); + break; + } + case 6: { // ---------- octahedra + const int iBase1 = isForward ? -1 : 0; + const int iBase2 = isForward ? 0 :-1; + for ( z = 1; z < nbZ; ++z ) + helper->AddVolume( (*columns[0])[z+iBase1], (*columns[1])[z+iBase1], // bottom or top + (*columns[2])[z+iBase1], (*columns[3])[z+iBase1], + (*columns[4])[z+iBase1], (*columns[5])[z+iBase1], + (*columns[0])[z+iBase2], (*columns[1])[z+iBase2], // top or bottom + (*columns[2])[z+iBase2], (*columns[3])[z+iBase2], + (*columns[4])[z+iBase2], (*columns[5])[z+iBase2] ); + break; + } + default: // ---------- polyhedra + vector quantities( 2 + nbNodes, 4 ); + quantities[0] = quantities[1] = nbNodes; + columns.resize( nbNodes + 1 ); + columns[ nbNodes ] = columns[ 0 ]; + const int i1 = isForward ? 1 : 3; + const int i3 = isForward ? 3 : 1; + const int iBase1 = isForward ? -1 : 0; + const int iBase2 = isForward ? 0 :-1; + vector nodes( 2*nbNodes + 4*nbNodes); + for ( z = 1; z < nbZ; ++z ) + { for ( int i = 0; i < nbNodes; ++i ) { - nodes[ i ] = (*columns[ i ])[z-1]; // bottom - nodes[ i+nbNodes ] = (*columns[ i ])[z ]; // top + nodes[ i ] = (*columns[ i ])[z+iBase1]; // bottom or top + nodes[ 2*nbNodes-i-1 ] = (*columns[ i ])[z+iBase2]; // top or bottom // side - int di = 2*nbNodes + 4*i - 1; - nodes[ di ] = (*columns[i ])[z-1]; - nodes[ di+1 ] = (*columns[i+1])[z-1]; - nodes[ di+2 ] = (*columns[i+1])[z ]; - nodes[ di+3 ] = (*columns[i ])[z ]; + int di = 2*nbNodes + 4*i; + nodes[ di+0 ] = (*columns[i ])[z ]; + nodes[ di+i1] = (*columns[i+1])[z ]; + nodes[ di+2 ] = (*columns[i+1])[z-1]; + nodes[ di+i3] = (*columns[i ])[z-1]; } - vol = meshDS->AddPolyhedralVolume( nodes, quantities ); + helper->AddPolyhedralVolume( nodes, quantities ); } - if ( vol && shapeID > 0 ) - meshDS->SetMeshElementOnShape( vol, shapeID ); - } + + } // switch ( nbNodes ) } //================================================================================ @@ -498,6 +822,8 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top() botSMDS->NbElements() != topSMDS->NbElements() || botSMDS->NbNodes() != topSMDS->NbNodes()) { + MESSAGE("nb elem bot " << botSMDS->NbElements() << " top " << topSMDS->NbElements()); + MESSAGE("nb node bot " << botSMDS->NbNodes() << " top " << topSMDS->NbNodes()); if ( myBlock.HasNotQuadElemOnTop() ) return error(TCom("Mesh on faces #") << botSM->GetId() <<" and #"<< topSM->GetId() << " seems different" ); @@ -535,7 +861,7 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top() // Fill myBotToColumnMap int zSize = myBlock.VerticalSize(); - TNode prevTNode; + //TNode prevTNode; TNodeNodeMap::iterator bN_tN = n2nMap.begin(); for ( ; bN_tN != n2nMap.end(); ++bN_tN ) { @@ -543,19 +869,8 @@ bool StdMeshers_Prism_3D::assocOrProjBottom2Top() const SMDS_MeshNode* topNode = bN_tN->second; if ( botNode->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE ) continue; // wall columns are contained in myBlock - // compute bottom node params - TNode bN( botNode ); - if ( zSize > 2 ) { - gp_XYZ paramHint(-1,-1,-1); - if ( prevTNode.IsNeighbor( bN )) - paramHint = prevTNode.GetParams(); - if ( !myBlock.ComputeParameters( bN.GetCoords(), bN.ChangeParams(), - ID_BOT_FACE, paramHint )) - return error(TCom("Can't compute normalized parameters for node ") - << botNode->GetID() << " on the face #"<< botSM->GetId() ); - prevTNode = bN; - } // create node column + TNode bN( botNode ); TNode2ColumnMap::iterator bN_col = myBotToColumnMap.insert( make_pair ( bN, TNodeColumn() )).first; TNodeColumn & column = bN_col->second; @@ -681,7 +996,7 @@ bool StdMeshers_Prism_3D::projectBottomToTop() //================================================================================ /*! - * \brief Set projection coordinates of a node to a face and it's subshapes + * \brief Set projection coordinates of a node to a face and it's sub-shapes * \param faceID - the face given by in-block ID * \param params - node normalized parameters * \retval bool - is a success @@ -696,10 +1011,10 @@ bool StdMeshers_Prism_3D::setFaceAndEdgesXYZ( const int faceID, const gp_XYZ& pa SMESH_Block::GetFaceEdgesIDs( faceID, edgeVec ); myBlock.EdgePoint( edgeVec[ BASE ], params, myShapeXYZ[ edgeVec[ BASE ]]); - myBlock.EdgePoint( edgeVec[ TOP ], params, myShapeXYZ[ edgeVec[ TOP ]]); + myBlock.EdgePoint( edgeVec[ TOP ], params, myShapeXYZ[ edgeVec[ TOP ]]); SHOWYXZ("\nparams ", params); - SHOWYXZ("TOP is "< 0 && nbNotQuad != 2 ) - return error(COMPERR_BAD_SHAPE, - TCom("More than 2 not quadrilateral faces: ") - < 2 ) + { return error(COMPERR_BAD_INPUT_MESH, TCom("More than 2 faces with not quadrangle elements: ") < 0 && nbNotQuad != 2 ) + { + // Issue 0020843 - one of side faces is quasi-quadrilateral. + // Remove from notQuadGeomSubMesh faces meshed with regular grid + nbQuasiQuads = removeQuasiQuads( notQuadGeomSubMesh ); + nbNotQuad -= nbQuasiQuads; + if ( nbNotQuad > 0 && nbNotQuad != 2 ) + return error(COMPERR_BAD_SHAPE, + TCom("More than 2 not quadrilateral faces: ") + < 1 ); + MESSAGE("myNotQuadOnTop " << myNotQuadOnTop << " nbNotQuadMeshed " << nbNotQuadMeshed); // ---------------------------------------------------------- @@ -994,24 +1330,47 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, // Get ordered bottom edges list< TopoDS_Edge > orderedEdges; - list< int > nbVertexInWires; + list< int > nbEInW; SMESH_Block::GetOrderedEdges( TopoDS::Face( botSM->GetSubShape().Reversed() ), - V000, orderedEdges, nbVertexInWires ); -// if ( nbVertexInWires.size() != 1 ) + V000, orderedEdges, nbEInW ); +// if ( nbEInW.size() != 1 ) // RETURN_BAD_RESULT("Wrong prism geometry"); // Get Wall faces corresponding to the ordered bottom edges list< TopoDS_Face > wallFaces; - if ( !GetWallFaces( Mesh(), shape3D, botSM->GetSubShape(), orderedEdges, wallFaces)) + if ( !GetWallFaces( Mesh(), shape3D, botSM->GetSubShape(), orderedEdges, nbEInW, wallFaces)) return error(COMPERR_BAD_SHAPE, "Can't find side faces"); + // check that the found top and bottom faces are opposite + { + for (TopExp_Explorer edge(botSM->GetSubShape(), TopAbs_EDGE); edge.More(); edge.Next()) + if ( helper->IsSubShape( edge.Current(), topSM->GetSubShape() )) + return error(notQuadGeomSubMesh.empty() ? COMPERR_BAD_INPUT_MESH : COMPERR_BAD_SHAPE, + "Non-quadrilateral faces are not opposite"); + } + + // Protect from a distorted block (test 3D_mesh_HEXA3D/B7 on 32bit platform) + // check that all wall faces have an edge common with the top face + { + list< TopoDS_Face >::iterator faceIt = wallFaces.begin(); + for ( ; faceIt != wallFaces.end(); ++faceIt ) + { + bool hasCommon = false; + for (TopExp_Explorer edge(*faceIt, TopAbs_EDGE); !hasCommon && edge.More(); edge.Next()) + if ( helper->IsSubShape( edge.Current(), topSM->GetSubShape() )) + hasCommon = true; + if ( !hasCommon ) + return error(COMPERR_BAD_SHAPE); + } + } + // Find columns of wall nodes and calculate edges' lengths // -------------------------------------------------------- myParam2ColumnMaps.clear(); myParam2ColumnMaps.resize( orderedEdges.size() ); // total nb edges - int iE, nbEdges = nbVertexInWires.front(); // nb outer edges + int iE, nbEdges = nbEInW.front(); // nb outer edges vector< double > edgeLength( nbEdges ); map< double, int > len2edgeMap; @@ -1057,11 +1416,11 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, // columns for vertices // 1 const SMDS_MeshNode* n0 = faceColumns.begin()->second.front(); - id = n0->GetPosition()->GetShapeId(); + id = n0->getshapeId(); myShapeIndex2ColumnMap[ id ] = make_pair( & faceColumns, isForward ); // 2 const SMDS_MeshNode* n1 = faceColumns.rbegin()->second.front(); - id = n1->GetPosition()->GetShapeId(); + id = n1->getshapeId(); myShapeIndex2ColumnMap[ id ] = make_pair( & faceColumns, isForward ); // SHOWYXZ("\np1 F "<second.front() )); // SHOWYXZ("p2 F "<second.front() )); @@ -1113,9 +1472,12 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, int nbSplit = i_nb->second; vector< double > params; splitParams( nbSplit, &myParam2ColumnMaps[ iE ], params ); - bool isForward = ( edgeIt->Orientation() == TopAbs_FORWARD ); + const bool isForward = + StdMeshers_PrismAsBlock::IsForwardEdge( myHelper->GetMeshDS(), + myParam2ColumnMaps[iE], + *edgeIt, SMESH_Block::ID_Fx0z ); for ( int i = 0; i < nbSplit; ++i ) { - double f = ( isForward ? params[ i ] : params[ nbSplit - i-1 ]); + double f = ( isForward ? params[ i ] : params[ nbSplit - i-1 ]); double l = ( isForward ? params[ i+1 ] : params[ nbSplit - i ]); TSideFace* comp = new TSideFace( myHelper, wallFaceIds[ iSide ], *faceIt, *edgeIt, @@ -1236,6 +1598,7 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, break; } } + //sideFace->dumpNodes( 4 ); // debug } // horizontal faces geometry { @@ -1270,11 +1633,11 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, // columns for vertices const SMDS_MeshNode* n0 = cols->begin()->second.front(); - id = n0->GetPosition()->GetShapeId(); + id = n0->getshapeId(); myShapeIndex2ColumnMap[ id ] = make_pair( cols, isForward ); const SMDS_MeshNode* n1 = cols->rbegin()->second.front(); - id = n1->GetPosition()->GetShapeId(); + id = n1->getshapeId(); myShapeIndex2ColumnMap[ id ] = make_pair( cols, !isForward ); } } @@ -1301,7 +1664,7 @@ bool StdMeshers_PrismAsBlock::Init(SMESH_MesherHelper* helper, const TNodeColumn* StdMeshers_PrismAsBlock::GetNodeColumn(const SMDS_MeshNode* node) const { - int sID = node->GetPosition()->GetShapeId(); + int sID = node->getshapeId(); map >::const_iterator col_frw = myShapeIndex2ColumnMap.find( sID ); @@ -1315,6 +1678,89 @@ const TNodeColumn* StdMeshers_PrismAsBlock::GetNodeColumn(const SMDS_MeshNode* n return 0; } +//======================================================================= +//function : GetLayersTransformation +//purpose : Return transformations to get coordinates of nodes of each layer +// by nodes of the bottom. Layer is a set of nodes at a certain step +// from bottom to top. +//======================================================================= + +bool StdMeshers_PrismAsBlock::GetLayersTransformation(vector & trsf) const +{ + const int zSize = VerticalSize(); + if ( zSize < 3 ) return true; + trsf.resize( zSize - 2 ); + + // Select some node columns by which we will define coordinate system of layers + + vector< const TNodeColumn* > columns; + { + const TopoDS_Shape& baseFace = Shape(ID_BOT_FACE); + list< TopoDS_Edge > orderedEdges; + list< int > nbEdgesInWires; + GetOrderedEdges( TopoDS::Face( baseFace ), TopoDS_Vertex(), orderedEdges, nbEdgesInWires ); + bool isReverse; + list< TopoDS_Edge >::iterator edgeIt = orderedEdges.begin(); + for ( int iE = 0; iE < nbEdgesInWires.front(); ++iE, ++edgeIt ) + { + if ( BRep_Tool::Degenerated( *edgeIt )) continue; + const TParam2ColumnMap* u2colMap = + GetParam2ColumnMap( myHelper->GetMeshDS()->ShapeToIndex( *edgeIt ), isReverse ); + if ( !u2colMap ) return false; + isReverse = ( edgeIt->Orientation() == TopAbs_REVERSED ); + double f = u2colMap->begin()->first, l = u2colMap->rbegin()->first; + if ( isReverse ) swap ( f, l ); + const int nbCol = 5; + for ( int i = 0; i < nbCol; ++i ) + { + double u = f + i/double(nbCol) * ( l - f ); + const TNodeColumn* col = & getColumn( u2colMap, u )->second; + if ( columns.empty() || col != columns.back() ) + columns.push_back( col ); + } + } + } + + // Find tolerance to check transformations + + double tol2; + { + Bnd_B3d bndBox; + for ( int i = 0; i < columns.size(); ++i ) + bndBox.Add( gpXYZ( columns[i]->front() )); + tol2 = bndBox.SquareExtent() * 1e-5; + } + + // Compute transformations + + int xCol = -1; + gp_Trsf fromCsZ, toCs0; + gp_Ax3 cs0 = getLayerCoordSys(0, columns, xCol ); + //double dist0 = cs0.Location().Distance( gpXYZ( (*columns[0])[0])); + toCs0.SetTransformation( cs0 ); + for ( int z = 1; z < zSize-1; ++z ) + { + gp_Ax3 csZ = getLayerCoordSys(z, columns, xCol ); + //double distZ = csZ.Location().Distance( gpXYZ( (*columns[0])[z])); + fromCsZ.SetTransformation( csZ ); + fromCsZ.Invert(); + gp_Trsf& t = trsf[ z-1 ]; + t = fromCsZ * toCs0; + //t.SetScaleFactor( distZ/dist0 ); - it does not work properly, wrong base point + + // check a transformation + for ( int i = 0; i < columns.size(); ++i ) + { + gp_Pnt p0 = gpXYZ( (*columns[i])[0] ); + gp_Pnt pz = gpXYZ( (*columns[i])[z] ); + t.Transforms( p0.ChangeCoord() ); + if ( p0.SquareDistance( pz ) > tol2 ) + return false; + } + } + return true; +} + //================================================================================ /*! * \brief Check curve orientation of a bootom edge @@ -1322,7 +1768,7 @@ const TNodeColumn* StdMeshers_PrismAsBlock::GetNodeColumn(const SMDS_MeshNode* n * \param columnsMap - node columns map of side face * \param bottomEdge - the bootom edge * \param sideFaceID - side face in-block ID - * \retval bool - true if orientation coinside with in-block froward orientation + * \retval bool - true if orientation coinside with in-block forward orientation */ //================================================================================ @@ -1332,7 +1778,7 @@ bool StdMeshers_PrismAsBlock::IsForwardEdge(SMESHDS_Mesh* meshDS, const int sideFaceID) { bool isForward = false; - if ( TAssocTool::IsClosedEdge( bottomEdge )) + if ( SMESH_MesherHelper::IsClosedEdge( bottomEdge )) { isForward = ( bottomEdge.Orientation() == TopAbs_FORWARD ); } @@ -1350,41 +1796,59 @@ bool StdMeshers_PrismAsBlock::IsForwardEdge(SMESHDS_Mesh* meshDS, } //================================================================================ - /*! - * \brief Find wall faces by bottom edges - * \param mesh - the mesh - * \param mainShape - the prism - * \param bottomFace - the bottom face - * \param bottomEdges - edges bounding the bottom face - * \param wallFaces - faces list to fill in - */ +/*! + * \brief Find wall faces by bottom edges + * \param mesh - the mesh + * \param mainShape - the prism + * \param bottomFace - the bottom face + * \param bottomEdges - edges bounding the bottom face + * \param wallFaces - faces list to fill in + */ //================================================================================ -bool StdMeshers_PrismAsBlock::GetWallFaces( SMESH_Mesh* mesh, - const TopoDS_Shape & mainShape, - const TopoDS_Shape & bottomFace, - const std::list< TopoDS_Edge >& bottomEdges, - std::list< TopoDS_Face >& wallFaces) +bool StdMeshers_PrismAsBlock::GetWallFaces( SMESH_Mesh* mesh, + const TopoDS_Shape & mainShape, + const TopoDS_Shape & bottomFace, + std::list< TopoDS_Edge >& bottomEdges, + std::list< int > & nbEInW, + std::list< TopoDS_Face >& wallFaces) { wallFaces.clear(); TopTools_IndexedMapOfShape faceMap; TopExp::MapShapes( mainShape, TopAbs_FACE, faceMap ); - list< TopoDS_Edge >::const_iterator edge = bottomEdges.begin(); - for ( ; edge != bottomEdges.end(); ++edge ) + list< TopoDS_Edge >::iterator edge = bottomEdges.begin(); + std::list< int >::iterator nbE = nbEInW.begin(); + int iE = 0; + while ( edge != bottomEdges.end() ) { - TopTools_ListIteratorOfListOfShape ancestIt = mesh->GetAncestors( *edge ); - for ( ; ancestIt.More(); ancestIt.Next() ) + ++iE; + if ( BRep_Tool::Degenerated( *edge )) { - const TopoDS_Shape& ancestor = ancestIt.Value(); - if ( ancestor.ShapeType() == TopAbs_FACE && // face - !bottomFace.IsSame( ancestor ) && // not bottom - faceMap.FindIndex( ancestor )) // belongs to the prism + edge = bottomEdges.erase( edge ); + --iE; + --(*nbE); + } + else + { + PShapeIteratorPtr fIt = myHelper->GetAncestors( *edge, *mesh, TopAbs_FACE ); + while ( fIt->more() ) { - wallFaces.push_back( TopoDS::Face( ancestor )); - break; + const TopoDS_Shape* face = fIt->next(); + if ( !bottomFace.IsSame( *face ) && // not bottom + faceMap.FindIndex( *face )) // belongs to the prism + { + wallFaces.push_back( TopoDS::Face( *face )); + break; + } } + ++edge; + } + if ( iE == *nbE ) + { + iE = 0; + ++nbE; } } return ( wallFaces.size() == bottomEdges.size() ); @@ -1610,8 +2074,6 @@ double StdMeshers_PrismAsBlock::TSideFace::GetColumns(const double U, r = 0.5; } else { -// if ( !myIsForward ) -// std::swap( col1, col2 ); double uf = col1->first; double ul = col2->first; r = ( u - uf ) / ( ul - uf ); @@ -1631,8 +2093,8 @@ double StdMeshers_PrismAsBlock::TSideFace::GetColumns(const double U, gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U, const Standard_Real V) const { - double u; if ( !myComponents.empty() ) { + double u; TSideFace * comp = GetComponent(U,u); return comp->Value( u, V ); } @@ -1644,7 +2106,41 @@ gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U, const SMDS_MeshNode* n2 = 0; const SMDS_MeshNode* n3 = 0; const SMDS_MeshNode* n4 = 0; - gp_XYZ pnt; + + // BEGIN issue 0020680: EDF 1252 SMESH: Bad cell created by Radial prism in center of torus + // Workaround for a wrongly located point returned by mySurface.Value() for + // UV located near boundary of BSpline surface. + // To bypass the problem, we take point from 3D curve of edge. + // It solves pb of the bloc_fiss_new.py + const double tol = 1e-3; + if ( V < tol || V+tol >= 1. ) + { + n1 = V < tol ? u_col1->second.front() : u_col1->second.back(); + n3 = V < tol ? u_col2->second.front() : u_col2->second.back(); + TopoDS_Edge edge; + if ( V < tol ) + { + edge = myBaseEdge; + } + else + { + TopoDS_Shape s = myHelper->GetSubShapeByNode( n1, myHelper->GetMeshDS() ); + if ( s.ShapeType() != TopAbs_EDGE ) + s = myHelper->GetSubShapeByNode( n3, myHelper->GetMeshDS() ); + if ( s.ShapeType() == TopAbs_EDGE ) + edge = TopoDS::Edge( s ); + } + if ( !edge.IsNull() ) + { + double u1 = myHelper->GetNodeU( edge, n1 ); + double u3 = myHelper->GetNodeU( edge, n3 ); + double u = u1 * ( 1 - hR ) + u3 * hR; + TopLoc_Location loc; double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( edge,loc,f,l ); + return curve->Value( u ).Transformed( loc ); + } + } + // END issue 0020680: EDF 1252 SMESH: Bad cell created by Radial prism in center of torus vR = getRAndNodes( & u_col1->second, V, n1, n2 ); vR = getRAndNodes( & u_col2->second, V, n3, n4 ); @@ -1658,8 +2154,9 @@ gp_Pnt StdMeshers_PrismAsBlock::TSideFace::Value(const Standard_Real U, gp_XY uv34 = uv3 * ( 1 - vR ) + uv4 * vR; gp_XY uv = uv12 * ( 1 - hR ) + uv34 * hR; - - return mySurface.Value( uv.X(), uv.Y() ); + + gp_Pnt p = mySurface.Value( uv.X(), uv.Y() ); + return p; } @@ -1720,25 +2217,18 @@ TopoDS_Edge StdMeshers_PrismAsBlock::TSideFace::GetEdge(const int iEdge) const TopoDS_Shape V2 = myHelper->GetSubShapeByNode( node, meshDS ); if ( V2.ShapeType() == TopAbs_VERTEX && !V2.IsSame( V1 )) { - TopTools_ListIteratorOfListOfShape ancestIt = - myHelper->GetMesh()->GetAncestors( V1 ); - for ( ; ancestIt.More(); ancestIt.Next() ) - { - const TopoDS_Shape & ancestor = ancestIt.Value(); - if ( ancestor.ShapeType() == TopAbs_EDGE ) - for ( TopExp_Explorer e( ancestor, TopAbs_VERTEX ); e.More(); e.Next() ) - if ( V2.IsSame( e.Current() )) - return TopoDS::Edge( ancestor ); - } + TopoDS_Shape ancestor = myHelper->GetCommonAncestor( V1, V2, *myHelper->GetMesh(), TopAbs_EDGE); + if ( !ancestor.IsNull() ) + return TopoDS::Edge( ancestor ); } return TopoDS_Edge(); } //================================================================================ /*! - * \brief Fill block subshapes + * \brief Fill block sub-shapes * \param shapeMap - map to fill in - * \retval int - nb inserted subshapes + * \retval int - nb inserted sub-shapes */ //================================================================================ @@ -1837,6 +2327,28 @@ int StdMeshers_PrismAsBlock::TSideFace::InsertSubShapes(TBlockShapes& shapeMap) return nbInserted; } +//================================================================================ +/*! + * \brief Dump ids of nodes of sides + */ +//================================================================================ + +void StdMeshers_PrismAsBlock::TSideFace::dumpNodes(int nbNodes) const +{ +#ifdef _DEBUG_ + cout << endl << "NODES OF FACE "; SMESH_Block::DumpShapeID( myID, cout ) << endl; + THorizontalEdgeAdaptor* hSize0 = (THorizontalEdgeAdaptor*) HorizCurve(0); + cout << "Horiz side 0: "; hSize0->dumpNodes(nbNodes); cout << endl; + THorizontalEdgeAdaptor* hSize1 = (THorizontalEdgeAdaptor*) HorizCurve(1); + cout << "Horiz side 1: "; hSize1->dumpNodes(nbNodes); cout << endl; + TVerticalEdgeAdaptor* vSide0 = (TVerticalEdgeAdaptor*) VertiCurve(0); + cout << "Verti side 0: "; vSide0->dumpNodes(nbNodes); cout << endl; + TVerticalEdgeAdaptor* vSide1 = (TVerticalEdgeAdaptor*) VertiCurve(1); + cout << "Verti side 1: "; vSide1->dumpNodes(nbNodes); cout << endl; + delete hSize0; delete hSize1; delete vSide0; delete vSide1; +#endif +} + //================================================================================ /*! * \brief Creates TVerticalEdgeAdaptor @@ -1867,6 +2379,22 @@ gp_Pnt StdMeshers_PrismAsBlock::TVerticalEdgeAdaptor::Value(const Standard_Real return gpXYZ(n1) * ( 1 - r ) + gpXYZ(n2) * r; } +//================================================================================ +/*! + * \brief Dump ids of nodes + */ +//================================================================================ + +void StdMeshers_PrismAsBlock::TVerticalEdgeAdaptor::dumpNodes(int nbNodes) const +{ +#ifdef _DEBUG_ + for ( int i = 0; i < nbNodes && i < myNodeColumn->size(); ++i ) + cout << (*myNodeColumn)[i]->GetID() << " "; + if ( nbNodes < myNodeColumn->size() ) + cout << myNodeColumn->back()->GetID(); +#endif +} + //================================================================================ /*! * \brief Return coordinates for the given normalized parameter @@ -1880,6 +2408,50 @@ gp_Pnt StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::Value(const Standard_Rea return mySide->TSideFace::Value( U, myV ); } +//================================================================================ +/*! + * \brief Dump ids of first nodes and the last one + */ +//================================================================================ + +void StdMeshers_PrismAsBlock::THorizontalEdgeAdaptor::dumpNodes(int nbNodes) const +{ +#ifdef _DEBUG_ + // Not bedugged code. Last node is sometimes incorrect + const TSideFace* side = mySide; + double u = 0; + if ( mySide->IsComplex() ) + side = mySide->GetComponent(0,u); + + TParam2ColumnIt col, col2; + TParam2ColumnMap* u2cols = side->GetColumns(); + side->GetColumns( u , col, col2 ); + + int j, i = myV ? mySide->ColumnHeight()-1 : 0; + + const SMDS_MeshNode* n = 0; + const SMDS_MeshNode* lastN + = side->IsForward() ? u2cols->rbegin()->second[ i ] : u2cols->begin()->second[ i ]; + for ( j = 0; j < nbNodes && n != lastN; ++j ) + { + n = col->second[ i ]; + cout << n->GetID() << " "; + if ( side->IsForward() ) + ++col; + else + --col; + } + + // last node + u = 1; + if ( mySide->IsComplex() ) + side = mySide->GetComponent(1,u); + + side->GetColumns( u , col, col2 ); + if ( n != col->second[ i ] ) + cout << col->second[ i ]->GetID(); +#endif +} //================================================================================ /*! * \brief Return UV on pcurve for the given normalized parameter diff --git a/src/StdMeshers/StdMeshers_Prism_3D.hxx b/src/StdMeshers/StdMeshers_Prism_3D.hxx index 0eb4db1d0..8c1e6fa78 100644 --- a/src/StdMeshers/StdMeshers_Prism_3D.hxx +++ b/src/StdMeshers/StdMeshers_Prism_3D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Prism_3D.hxx // Module : SMESH @@ -46,6 +47,7 @@ #include #include #include +#include class SMESHDS_SubMesh; @@ -58,7 +60,7 @@ typedef std::vector TNodeColumn; // map of bottom nodes to the column of nodes above them // (the column includes the bottom nodes) -typedef std::map< TNode, TNodeColumn > TNode2ColumnMap; +typedef std::map< TNode, TNodeColumn > TNode2ColumnMap; typedef std::map< double, TNodeColumn > TParam2ColumnMap; typedef std::map< double, TNodeColumn >::const_iterator TParam2ColumnIt; @@ -73,18 +75,18 @@ typedef TopTools_IndexedMapOfOrientedShape TBlockShapes; struct TNode { const SMDS_MeshNode* myNode; - gp_XYZ myParams; + mutable gp_XYZ myParams; gp_XYZ GetCoords() const { return gp_XYZ( myNode->X(), myNode->Y(), myNode->Z() ); } gp_XYZ GetParams() const { return myParams; } - gp_XYZ& ChangeParams() { return myParams; } + gp_XYZ& ChangeParams() const { return myParams; } bool HasParams() const { return myParams.X() >= 0.0; } SMDS_TypeOfPosition GetPositionType() const { return myNode ? myNode->GetPosition()->GetTypeOfPosition() : SMDS_TOP_UNSPEC; } bool IsNeighbor( const TNode& other ) const; TNode(const SMDS_MeshNode* node = 0): myNode(node), myParams(-1,-1,-1) {} - bool operator < (const TNode& other) const { return myNode < other.myNode; } + bool operator < (const TNode& other) const { return myNode->GetID() < other.myNode->GetID(); } }; // =============================================================== @@ -92,7 +94,7 @@ struct TNode * \brief Tool analyzing and giving access to a prism geometry * treating it like a block, i.e. the four side faces are * emulated by division/uniting of missing/excess faces. - * It also manage associations between block subshapes and a mesh. + * It also manage associations between block sub-shapes and a mesh. */ // =============================================================== @@ -122,6 +124,11 @@ public: */ SMESH_ComputeErrorPtr GetError() const { return myError; } + /*! + * \brief Free allocated memory + */ + void Clear(); + /*! * \brief Return number of nodes on every vertical edge * \retval int - number of nodes including end nodes @@ -141,16 +148,26 @@ public: * \brief Return TParam2ColumnMap for a base edge * \param baseEdgeID - base edge SMESHDS Index * \param isReverse - columns in-block orientation - * \retval const TParam2ColumnMap& - map + * \retval const TParam2ColumnMap* - map */ - const TParam2ColumnMap& GetParam2ColumnMap(const int baseEdgeID, - bool & isReverse) + const TParam2ColumnMap* GetParam2ColumnMap(const int baseEdgeID, + bool & isReverse) const { - std::pair< TParam2ColumnMap*, bool > & col_frw = - myShapeIndex2ColumnMap[ baseEdgeID ]; + std::map< int, std::pair< TParam2ColumnMap*, bool > >::const_iterator i_mo = + myShapeIndex2ColumnMap.find( baseEdgeID ); + if ( i_mo == myShapeIndex2ColumnMap.end() ) return 0; + + const std::pair< TParam2ColumnMap*, bool >& col_frw = i_mo->second; isReverse = !col_frw.second; - return * col_frw.first; + return col_frw.first; } + + /*! + * \brief Return transformations to get coordinates of nodes of each internal layer + * by nodes of the bottom. Layer is a set of nodes at a certain step + * from bottom to top. + */ + bool GetLayersTransformation(std::vector & trsf) const; /*! * \brief Return pointer to mesh @@ -190,7 +207,7 @@ public: /*! * \brief Return in-block ID of a shape - * \param shape - block subshape + * \param shape - block sub-shape * \retval int - ID or zero if the shape has no ID */ int ShapeID(const TopoDS_Shape& shape) const @@ -216,11 +233,12 @@ public: * \param bottomEdges - edges bounding the bottom face * \param wallFaces - faces list to fill in */ - static bool GetWallFaces( SMESH_Mesh* mesh, - const TopoDS_Shape & mainShape, - const TopoDS_Shape & bottomFace, - const std::list< TopoDS_Edge >& bottomEdges, - std::list< TopoDS_Face >& wallFaces); + bool GetWallFaces( SMESH_Mesh* mesh, + const TopoDS_Shape & mainShape, + const TopoDS_Shape & bottomFace, + std::list< TopoDS_Edge >& bottomEdges, + std::list< int > & nbEInW, + std::list< TopoDS_Face >& wallFaces); private: @@ -284,6 +302,8 @@ private: int InsertSubShapes( TBlockShapes& shapeMap ) const; // redefine Adaptor methods gp_Pnt Value(const Standard_Real U,const Standard_Real V) const; + // debug + void dumpNodes(int nbNodes) const; }; // -------------------------------------------------------------------- @@ -299,6 +319,8 @@ private: gp_Pnt Value(const Standard_Real U) const; Standard_Real FirstParameter() const { return 0; } Standard_Real LastParameter() const { return 1; } + // debug + void dumpNodes(int nbNodes) const; }; // -------------------------------------------------------------------- @@ -316,6 +338,8 @@ private: gp_Pnt Value(const Standard_Real U) const; Standard_Real FirstParameter() const { return 0; } Standard_Real LastParameter() const { return 1; } + // debug + void dumpNodes(int nbNodes) const; }; // -------------------------------------------------------------------- @@ -337,20 +361,19 @@ private: Standard_Real FirstParameter() const { return 0; } Standard_Real LastParameter() const { return 1; } }; - // -------------------------------------------------------------------- - bool myNotQuadOnTop; - SMESH_MesherHelper* myHelper; - TBlockShapes myShapeIDMap; + bool myNotQuadOnTop; + SMESH_MesherHelper* myHelper; + TBlockShapes myShapeIDMap; + SMESH_ComputeErrorPtr myError; // container of 4 side faces - TSideFace* mySide; + TSideFace* mySide; // node columns for each base edge - std::vector< TParam2ColumnMap > myParam2ColumnMaps; + std::vector< TParam2ColumnMap > myParam2ColumnMaps; // to find a column for a node by edge SMESHDS Index std::map< int, std::pair< TParam2ColumnMap*, bool > > myShapeIndex2ColumnMap; - SMESH_ComputeErrorPtr myError; /*! * \brief store error and comment and then return ( error == COMPERR_OK ) */ @@ -358,7 +381,6 @@ private: myError = SMESH_ComputeError::New(error,comment); return myError->IsOK(); } - //std::vector< SMESH_subMesh* > mySubMeshesVec; // submesh by in-block id }; // ============================================= @@ -379,6 +401,9 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! * \brief Enable removal of quadrangles from the bottom face and * triangles creation there by projection from the top @@ -415,7 +440,7 @@ private: bool projectBottomToTop(); /*! - * \brief Set projection coordinates of a node to a face and it's subshapes + * \brief Set projection coordinates of a node to a face and it's sub-shapes * \param faceID - the face given by in-block ID * \param params - node normalized parameters * \retval bool - is a success @@ -429,12 +454,11 @@ private: StdMeshers_PrismAsBlock myBlock; SMESH_MesherHelper* myHelper; - std::vector myShapeXYZ; // point on each sub-shape + std::vector myShapeXYZ; // point on each sub-shape of the block // map of bottom nodes to the column of nodes above them // (the column includes the bottom node) - typedef std::map< TNode, TNodeColumn > TNode2ColumnMap; - TNode2ColumnMap myBotToColumnMap; + TNode2ColumnMap myBotToColumnMap; }; #endif diff --git a/src/StdMeshers/StdMeshers_ProjectionSource1D.cxx b/src/StdMeshers/StdMeshers_ProjectionSource1D.cxx index 8a2052071..cf1a41ee3 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource1D.cxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource1D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource1D.cxx // Author : Edward AGAPOV @@ -126,9 +127,10 @@ void StdMeshers_ProjectionSource1D::SetVertexAssociation(const TopoDS_Shape& sou void StdMeshers_ProjectionSource1D::SetSourceMesh(SMESH_Mesh* mesh) { - if ( _sourceMesh != mesh ) + if ( _sourceMesh != mesh ) { + _sourceMesh = mesh; NotifySubMeshesHypothesisModification(); - _sourceMesh = mesh; + } } //============================================================================= diff --git a/src/StdMeshers/StdMeshers_ProjectionSource1D.hxx b/src/StdMeshers/StdMeshers_ProjectionSource1D.hxx index 301dedf30..ddd82f673 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource1D.hxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource1D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource1D.hxx // Author : Edward AGAPOV diff --git a/src/StdMeshers/StdMeshers_ProjectionSource2D.cxx b/src/StdMeshers/StdMeshers_ProjectionSource2D.cxx index a1393fc79..479bdd6ec 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource2D.cxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource2D.cxx @@ -1,32 +1,34 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource2D.cxx // Author : Edward AGAPOV // Module : SMESH - +// #include "StdMeshers_ProjectionSource2D.hxx" #include "SMESH_Mesh.hxx" +#include "StdMeshers_ProjectionUtils.hxx" #include "utilities.h" @@ -102,17 +104,27 @@ void StdMeshers_ProjectionSource2D::SetVertexAssociation(const TopoDS_Shape& sou throw ( SALOME_Exception ) { if ( sourceVertex1.IsNull() != targetVertex1.IsNull() || - sourceVertex2.IsNull() != targetVertex2.IsNull() || - sourceVertex1.IsNull() != targetVertex2.IsNull() ) - throw SALOME_Exception(LOCALIZED("Two or none pairs of vertices must be provided")); + sourceVertex2.IsNull() != targetVertex2.IsNull() ) + throw SALOME_Exception(LOCALIZED("Vertices must be provided in couples")); + + if ( sourceVertex1.IsNull() != sourceVertex2.IsNull() ) + { + // possibly there is only 1 vertex in the face + if ( !_sourceFace.IsNull() && + StdMeshers_ProjectionUtils::Count( _sourceFace, TopAbs_VERTEX, /*ignoreSame=*/true) != 1 ) + throw SALOME_Exception(LOCALIZED("Two or none pairs of vertices must be provided")); + } - if ( !sourceVertex1.IsNull() ) { + if ( !sourceVertex1.IsNull() ) if ( sourceVertex1.ShapeType() != TopAbs_VERTEX || - sourceVertex2.ShapeType() != TopAbs_VERTEX || - targetVertex1.ShapeType() != TopAbs_VERTEX || + targetVertex1.ShapeType() != TopAbs_VERTEX ) + throw SALOME_Exception(LOCALIZED("Wrong shape type")); + + if ( !sourceVertex2.IsNull() ) + if ( sourceVertex2.ShapeType() != TopAbs_VERTEX || targetVertex2.ShapeType() != TopAbs_VERTEX ) throw SALOME_Exception(LOCALIZED("Wrong shape type")); - } + if ( !_sourceVertex1.IsSame( sourceVertex1 ) || !_sourceVertex2.IsSame( sourceVertex2 ) || @@ -136,9 +148,10 @@ void StdMeshers_ProjectionSource2D::SetVertexAssociation(const TopoDS_Shape& sou void StdMeshers_ProjectionSource2D::SetSourceMesh(SMESH_Mesh* mesh) { - if ( _sourceMesh != mesh ) + if ( _sourceMesh != mesh ) { + _sourceMesh = mesh; NotifySubMeshesHypothesisModification(); - _sourceMesh = mesh; + } } //============================================================================= diff --git a/src/StdMeshers/StdMeshers_ProjectionSource2D.hxx b/src/StdMeshers/StdMeshers_ProjectionSource2D.hxx index a23d4c494..978321147 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource2D.hxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource2D.hxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource2D.hxx // Author : Edward AGAPOV // Module : SMESH - +// #ifndef _SMESH_ProjectionSource2D_HXX_ #define _SMESH_ProjectionSource2D_HXX_ diff --git a/src/StdMeshers/StdMeshers_ProjectionSource3D.cxx b/src/StdMeshers/StdMeshers_ProjectionSource3D.cxx index b77808f36..f90b4eed3 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource3D.cxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource3D.cxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource3D.cxx // Author : Edward AGAPOV // Module : SMESH - +// #include "StdMeshers_ProjectionSource3D.hxx" #include "utilities.h" @@ -135,9 +136,10 @@ void StdMeshers_ProjectionSource3D::SetVertexAssociation(const TopoDS_Shape& sou void StdMeshers_ProjectionSource3D::SetSourceMesh(SMESH_Mesh* mesh) { - if ( _sourceMesh != mesh ) + if ( _sourceMesh != mesh ) { + _sourceMesh = mesh; NotifySubMeshesHypothesisModification(); - _sourceMesh = mesh; + } } //============================================================================= diff --git a/src/StdMeshers/StdMeshers_ProjectionSource3D.hxx b/src/StdMeshers/StdMeshers_ProjectionSource3D.hxx index fd6848aec..6098f1816 100644 --- a/src/StdMeshers/StdMeshers_ProjectionSource3D.hxx +++ b/src/StdMeshers/StdMeshers_ProjectionSource3D.hxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource3D.hxx // Author : Edward AGAPOV // Module : SMESH - +// #ifndef _SMESH_ProjectionSource3D_HXX_ #define _SMESH_ProjectionSource3D_HXX_ diff --git a/src/StdMeshers/StdMeshers_ProjectionUtils.cxx b/src/StdMeshers/StdMeshers_ProjectionUtils.cxx index 2bf6ac4be..3053be90d 100644 --- a/src/StdMeshers/StdMeshers_ProjectionUtils.cxx +++ b/src/StdMeshers/StdMeshers_ProjectionUtils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionUtils.cxx // Created : Fri Oct 27 10:24:28 2006 @@ -34,15 +35,15 @@ #include "SMESH_Block.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Hypothesis.hxx" -#include "SMESH_IndexedDataMapOfShapeIndexedMapOfShape.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_subMeshEventListener.hxx" #include "SMDS_EdgePosition.hxx" #include "utilities.h" +#include #include #include #include @@ -52,6 +53,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -60,20 +64,22 @@ #include #include #include -#include -#include + +#include using namespace std; #define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; } -#define SHOW_VERTEX(v,msg) // { \ -// if ( v.IsNull() ) cout << msg << " NULL SHAPE" << endl; \ -// else if (v.ShapeType() == TopAbs_VERTEX) {\ -// gp_Pnt p = BRep_Tool::Pnt( TopoDS::Vertex( v ));\ -// cout << msg << (v).TShape().operator->()<<" ( " <()<ShapeToIndex(S), theMeshDS[1]->ShapeToIndex(S) ); + return long(S.TShape().operator->()); + } + + //================================================================================ + /*! + * \brief Write shape for debug purposes + */ + //================================================================================ + + bool _StoreBadShape(const TopoDS_Shape& shape) + { +#ifdef _DEBUG_ + const char* type[] ={"COMPOUND","COMPSOLID","SOLID","SHELL","FACE","WIRE","EDGE","VERTEX"}; + BRepTools::Write( shape, SMESH_Comment("/tmp/") << type[shape.ShapeType()] << "_" + << shape.TShape().operator->() << ".brep"); +#endif + return false; + } + //================================================================================ /*! * \brief Reverse order of edges in a list and their orientation @@ -94,31 +127,24 @@ namespace { */ //================================================================================ - void Reverse( list< TopoDS_Edge > & edges, const int nbEdges ) + void Reverse( list< TopoDS_Edge > & edges, const int nbEdges, const int firstEdge=0) { SHOW_LIST("BEFORE REVERSE", edges); list< TopoDS_Edge >::iterator eIt = edges.begin(); - if ( edges.size() == nbEdges ) - { - edges.reverse(); - } - else // reverse only the given nb of edges + std::advance( eIt, firstEdge ); + list< TopoDS_Edge >::iterator eBackIt = eIt; + for ( int i = 0; i < nbEdges; ++i, ++eBackIt ) + eBackIt->Reverse(); // reverse edge + // reverse list + --eBackIt; + while ( eIt != eBackIt ) { - // look for the last edge to be reversed - list< TopoDS_Edge >::iterator eBackIt = edges.begin(); - for ( int i = 1; i < nbEdges; ++i ) - ++eBackIt; - // reverse - while ( eIt != eBackIt ) { - std::swap( *eIt, *eBackIt ); - SHOW_LIST("# AFTER SWAP", edges) + std::swap( *eIt, *eBackIt ); + SHOW_LIST("# AFTER SWAP", edges) if ( (++eIt) != eBackIt ) --eBackIt; - } } - for ( eIt = edges.begin(); eIt != edges.end(); ++eIt ) - eIt->Reverse(); SHOW_LIST("ATFER REVERSE", edges) } @@ -162,8 +188,7 @@ namespace { if ( nbEdges == 2 && IsPropagationPossible( theMesh1, theMesh2 ) ) { list< TopoDS_Edge >::iterator eIt2 = ++edges2.begin(); // 2nd edge of the 2nd face - TopoDS_Edge edge2 = - StdMeshers_ProjectionUtils::GetPropagationEdge( theMesh1, *eIt2, edges1.front() ).second; + TopoDS_Edge edge2 = HERE::GetPropagationEdge( theMesh1, *eIt2, edges1.front() ).second; if ( !edge2.IsNull() ) { // propagation found for the second edge Reverse( edges2, nbEdges ); return true; @@ -202,14 +227,200 @@ namespace { } return TopoDS_Shape(); } -} + + //================================================================================ + /*! + * \brief Find association of groups at top and bottom of prism + */ + //================================================================================ + + bool AssocGroupsByPropagation(const TopoDS_Shape& theGroup1, + const TopoDS_Shape& theGroup2, + SMESH_Mesh& theMesh, + HERE::TShapeShapeMap& theMap) + { + // If groups are on top and bottom of prism then we can associate + // them using "vertical" (or "side") edges and faces of prism since + // they connect corresponding vertices and edges of groups. + + TopTools_IndexedMapOfShape subshapes1, subshapes2; + TopExp::MapShapes( theGroup1, subshapes1 ); + TopExp::MapShapes( theGroup2, subshapes2 ); + TopTools_ListIteratorOfListOfShape ancestIt; + + // Iterate on vertices of group1 to find corresponding vertices in group2 + // and associate adjacent edges and faces + + TopTools_MapOfShape verticShapes; + TopExp_Explorer vExp1( theGroup1, TopAbs_VERTEX ); + for ( ; vExp1.More(); vExp1.Next() ) + { + const TopoDS_Vertex& v1 = TopoDS::Vertex( vExp1.Current() ); + if ( theMap.IsBound( v1 )) continue; // already processed + + // Find "vertical" edge ending in v1 and whose other vertex belongs to group2 + TopoDS_Shape verticEdge, v2; + ancestIt.Initialize( theMesh.GetAncestors( v1 )); + for ( ; verticEdge.IsNull() && ancestIt.More(); ancestIt.Next() ) + { + if ( ancestIt.Value().ShapeType() != TopAbs_EDGE ) continue; + v2 = HERE::GetNextVertex( TopoDS::Edge( ancestIt.Value() ), v1 ); + if ( subshapes2.Contains( v2 )) + verticEdge = ancestIt.Value(); + } + if ( verticEdge.IsNull() ) + return false; + + HERE::InsertAssociation( v1, v2, theMap); + + // Associate edges by vertical faces sharing the found vertical edge + ancestIt.Initialize( theMesh.GetAncestors( verticEdge ) ); + for ( ; ancestIt.More(); ancestIt.Next() ) + { + if ( ancestIt.Value().ShapeType() != TopAbs_FACE ) continue; + if ( !verticShapes.Add( ancestIt.Value() )) continue; + const TopoDS_Face& face = TopoDS::Face( ancestIt.Value() ); + + // get edges of the face + TopoDS_Edge edgeGr1, edgeGr2, verticEdge2; + list< TopoDS_Edge > edges; list< int > nbEdgesInWire; + SMESH_Block::GetOrderedEdges( face, v1, edges, nbEdgesInWire); + if ( nbEdgesInWire.front() != 4 ) + return _StoreBadShape( face ); + list< TopoDS_Edge >::iterator edge = edges.begin(); + if ( verticEdge.IsSame( *edge )) { + edgeGr2 = *(++edge); + verticEdge2 = *(++edge); + edgeGr1 = *(++edge); + } else { + edgeGr1 = *(edge++); + verticEdge2 = *(edge++); + edgeGr2 = *(edge++); + } + + HERE::InsertAssociation( edgeGr1, edgeGr2.Reversed(), theMap); + } + } + + // Associate faces + TopoDS_Iterator gr1It( theGroup1 ); + if ( gr1It.Value().ShapeType() == TopAbs_FACE ) + { + // find a boundary edge of group1 to start from + TopoDS_Shape bndEdge = StdMeshers_ProjectionUtils::GetBoundaryEdge( theGroup1, theMesh ); + if ( bndEdge.IsNull() ) + return false; + + list< TopoDS_Shape > edges(1, bndEdge); + list< TopoDS_Shape >::iterator edge1 = edges.begin(); + for ( ; edge1 != edges.end(); ++edge1 ) + { + // there must be one or zero not associated faces between ancestors of edge + // belonging to theGroup1 + TopoDS_Shape face1; + ancestIt.Initialize( theMesh.GetAncestors( *edge1 ) ); + for ( ; ancestIt.More() && face1.IsNull(); ancestIt.Next() ) { + if ( ancestIt.Value().ShapeType() == TopAbs_FACE && + !theMap.IsBound( ancestIt.Value() ) && + subshapes1.Contains( ancestIt.Value() )) + face1 = ancestIt.Value(); + + // add edges of face1 to start searching for adjacent faces from + for ( TopExp_Explorer e(face1, TopAbs_EDGE); e.More(); e.Next()) + if ( !edge1->IsSame( e.Current() )) + edges.push_back( e.Current() ); + } + if ( !face1.IsNull() ) { + // find the corresponding face of theGroup2 + TopoDS_Shape edge2 = theMap( *edge1 ); + TopoDS_Shape face2; + ancestIt.Initialize( theMesh.GetAncestors( edge2 ) ); + for ( ; ancestIt.More() && face2.IsNull(); ancestIt.Next() ) { + if ( ancestIt.Value().ShapeType() == TopAbs_FACE && + !theMap.IsBound( ancestIt.Value(), /*is2nd=*/true ) && + subshapes2.Contains( ancestIt.Value() )) + face2 = ancestIt.Value(); + } + if ( face2.IsNull() ) + return false; + + HERE::InsertAssociation( face1, face2, theMap); + } + } + } + return true; + } + + //================================================================================ + /*! + * \brief Return true if uv position of the vIndex-th vertex of edge on face is close + * enough to given uv + */ + //================================================================================ + + bool sameVertexUV( const TopoDS_Edge& edge, + const TopoDS_Face& face, + const int& vIndex, + const gp_Pnt2d& uv, + const double& tol2d ) + { + TopoDS_Vertex VV[2]; + TopExp::Vertices( edge, VV[0], VV[1], true); + gp_Pnt2d v1UV = BRep_Tool::Parameters( VV[vIndex], face); + double dist2d = v1UV.Distance( uv ); + return dist2d < tol2d; + } + + //================================================================================ + /*! + * \brief Returns an EDGE suitable for search of initial vertex association + */ + //================================================================================ + + TopoDS_Shape getOuterEdge( const TopoDS_Shape theShape1, SMESH_Mesh& mesh ) + { + TopoDS_Shape edge; + if ( theShape1.ShapeType() == TopAbs_COMPOUND ) + { + TopoDS_Iterator it( theShape1 ); + if ( it.Value().ShapeType() == TopAbs_FACE ) // group of FACEs + { + // look for a boundary EDGE of a group + edge = StdMeshers_ProjectionUtils::GetBoundaryEdge( theShape1, mesh ); + if ( !edge.IsNull() ) + return edge; + } + } + edge = theShape1; + TopExp_Explorer expF( theShape1, TopAbs_FACE ), expE; + if ( expF.More() ) { + for ( ; expF.More(); expF.Next() ) { + edge.Nullify(); + TopoDS_Shape wire = + StdMeshers_ProjectionUtils::OuterShape( TopoDS::Face( expF.Current() ), TopAbs_WIRE ); + for ( expE.Init( wire, TopAbs_EDGE ); edge.IsNull() && expE.More(); expE.Next() ) + if ( !SMESH_MesherHelper::IsClosedEdge( TopoDS::Edge( expE.Current() ))) + edge = expE.Current(); + if ( !edge.IsNull() ) + break; + } + } else if (edge.ShapeType() != TopAbs_EDGE) { // no faces + edge.Nullify(); + for ( expE.Init( theShape1, TopAbs_EDGE ); edge.IsNull() && expE.More(); expE.Next() ) + if ( !SMESH_MesherHelper::IsClosedEdge( TopoDS::Edge( expE.Current() ))) + edge = expE.Current(); + } + return edge; + } + +} // namespace //======================================================================= /*! - * \brief Looks for association of all subshapes of two shapes - * \param theShape1 - shape 1 + * \brief Looks for association of all sub-shapes of two shapes + * \param theShape1 - target shape * \param theMesh1 - mesh built on shape 1 - * \param theShape2 - shape 2 + * \param theShape2 - source shape * \param theMesh2 - mesh built on shape 2 * \param theAssociation - association map to be filled that may * contain association of one or two pairs of vertices @@ -223,8 +434,31 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the SMESH_Mesh* theMesh2, TShapeShapeMap & theMap) { + // Structure of this long function is following + // 1) Group->group projection: theShape1 is a group member, + // theShape2 is a group. We find a group theShape1 is in and recall self. + // 2) Accosiate same shapes with different location (partners). + // 3) If vertex association is given, perform accosiation according to shape type: + // switch ( ShapeType ) { + // case TopAbs_EDGE: + // case ...: + // } + // 4) else try to accosiate in different ways: + // a) accosiate shapes by propagation and other simple cases + // switch ( ShapeType ) { + // case TopAbs_EDGE: + // case ...: + // } + // b) find association of a couple of vertices and recall self. + // + + theMeshDS[0] = theMesh1->GetMeshDS(); // debug + theMeshDS[1] = theMesh2->GetMeshDS(); + + // ================================================================================= + // 1) Is it the case of associating a group member -> another group? (PAL16202, 16203) + // ================================================================================= if ( theShape1.ShapeType() != theShape2.ShapeType() ) { - // is it the case of a group member -> another group? (PAL16202, 16203) TopoDS_Shape group1, group2; if ( theShape1.ShapeType() == TopAbs_COMPOUND ) { group1 = theShape1; @@ -240,11 +474,34 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the return FindSubShapeAssociation(group1, theMesh1, group2, theMesh2, theMap ); } - bool bidirect = ( !theShape1.IsSame( theShape2 )); + // ============ + // 2) Is partner? + // ============ + bool partner = theShape1.IsPartner( theShape2 ); + TopTools_DataMapIteratorOfDataMapOfShapeShape vvIt( theMap._map1to2 ); + for ( ; partner && vvIt.More(); vvIt.Next() ) + partner = vvIt.Key().IsPartner( vvIt.Value() ); + + if ( partner ) // Same shape with different location + { + // recursively associate all sub-shapes of theShape1 and theShape2 + typedef list< pair< TopoDS_Shape, TopoDS_Shape > > TShapePairsList; + TShapePairsList shapesQueue( 1, make_pair( theShape1, theShape2 )); + TShapePairsList::iterator s1_s2 = shapesQueue.begin(); + for ( ; s1_s2 != shapesQueue.end(); ++s1_s2 ) + { + InsertAssociation( s1_s2->first, s1_s2->second, theMap ); + TopoDS_Iterator s1It( s1_s2->first), s2It( s1_s2->second ); + for ( ; s1It.More(); s1It.Next(), s2It.Next() ) + shapesQueue.push_back( make_pair( s1It.Value(), s2It.Value() )); + } + return true; + } + if ( !theMap.IsEmpty() ) { //====================================================================== - // HAS initial vertex association + // 3) HAS initial vertex association //====================================================================== switch ( theShape1.ShapeType() ) { // ---------------------------------------------------------------------- @@ -254,14 +511,16 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the RETURN_BAD_RESULT("Wrong map extent " << theMap.Extent() ); TopoDS_Edge edge1 = TopoDS::Edge( theShape1 ); TopoDS_Edge edge2 = TopoDS::Edge( theShape2 ); + if ( edge1.Orientation() >= TopAbs_INTERNAL ) edge1.Orientation( TopAbs_FORWARD ); + if ( edge2.Orientation() >= TopAbs_INTERNAL ) edge2.Orientation( TopAbs_FORWARD ); TopoDS_Vertex VV1[2], VV2[2]; TopExp::Vertices( edge1, VV1[0], VV1[1] ); TopExp::Vertices( edge2, VV2[0], VV2[1] ); int i1 = 0, i2 = 0; if ( theMap.IsBound( VV1[ i1 ] )) i1 = 1; if ( theMap.IsBound( VV2[ i2 ] )) i2 = 1; - InsertAssociation( VV1[ i1 ], VV2[ i2 ], theMap, bidirect); - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( VV1[ i1 ], VV2[ i2 ], theMap ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } // ---------------------------------------------------------------------- @@ -269,6 +528,8 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the // ---------------------------------------------------------------------- TopoDS_Face face1 = TopoDS::Face( theShape1 ); TopoDS_Face face2 = TopoDS::Face( theShape2 ); + if ( face1.Orientation() >= TopAbs_INTERNAL ) face1.Orientation( TopAbs_FORWARD ); + if ( face2.Orientation() >= TopAbs_INTERNAL ) face2.Orientation( TopAbs_FORWARD ); TopoDS_Vertex VV1[2], VV2[2]; // find a not closed edge of face1 both vertices of which are associated @@ -298,12 +559,12 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the list< TopoDS_Edge >::iterator eIt2 = edges2.begin(); for ( ; eIt1 != edges1.end(); ++eIt1, ++eIt2 ) { - InsertAssociation( *eIt1, *eIt2, theMap, bidirect); + InsertAssociation( *eIt1, *eIt2, theMap ); VV1[0] = TopExp::FirstVertex( *eIt1, true ); VV2[0] = TopExp::FirstVertex( *eIt2, true ); - InsertAssociation( VV1[0], VV2[0], theMap, bidirect); + InsertAssociation( VV1[0], VV2[0], theMap ); } - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } // ---------------------------------------------------------------------- @@ -311,11 +572,12 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the case TopAbs_SOLID: { // ---------------------------------------------------------------------- TopoDS_Vertex VV1[2], VV2[2]; - // find a not closed edge of shape1 both vertices of which are associated + // try to find a not closed edge of shape1 both vertices of which are associated TopoDS_Edge edge1; TopExp_Explorer exp ( theShape1, TopAbs_EDGE ); for ( ; VV2[ 1 ].IsNull() && exp.More(); exp.Next() ) { edge1 = TopoDS::Edge( exp.Current() ); + if ( edge1.Orientation() >= TopAbs_INTERNAL ) edge1.Orientation( TopAbs_FORWARD ); TopExp::Vertices( edge1 , VV1[0], VV1[1] ); if ( theMap.IsBound( VV1[0] )) { VV2[ 0 ] = TopoDS::Vertex( theMap( VV1[0] )); @@ -325,11 +587,12 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } if ( VV2[ 1 ].IsNull() ) // 2 bound vertices not found RETURN_BAD_RESULT("2 bound vertices not found" ); + // get an edge2 of theShape2 corresponding to edge1 TopoDS_Edge edge2 = GetEdgeByVertices( theMesh2, VV2[ 0 ], VV2[ 1 ]); if ( edge2.IsNull() ) RETURN_BAD_RESULT("GetEdgeByVertices() failed"); - // build map of edge to faces if shapes are not subshapes of main ones + // build map of edge to faces if shapes are not sub-shapes of main ones bool isSubOfMain = false; if ( SMESHDS_SubMesh * sm = theMesh1->GetMeshDS()->MeshElements( theShape1 )) isSubOfMain = !sm->IsComplexSubmesh(); @@ -382,7 +645,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the TopExp::Vertices( edge1, VV1[0], VV1[1], true ); TopExp::Vertices( edge2, VV2[0], VV2[1], true ); F2 = FF2[ 0 ]; // (F2 !) - if ( !VV1[ 0 ].IsSame( theMap( VV2[ 0 ]))) { + if ( !VV1[ 0 ].IsSame( theMap( VV2[ 0 ], /*is2=*/true))) { edge2.Reverse(); if ( FF2[ 1 ].IsNull() ) F2.Reverse(); @@ -392,7 +655,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the TopTools_MapOfShape boundEdges; - // association of face subshapes and neighbour faces + // association of face sub-shapes and neighbour faces list< pair < TopoDS_Face, TopoDS_Edge > > FE1, FE2; list< pair < TopoDS_Face, TopoDS_Edge > >::iterator fe1, fe2; FE1.push_back( make_pair( TopoDS::Face( F1 ), edge1 )); @@ -409,7 +672,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the list< TopoDS_Edge > edges1, edges2; int nbE = FindFaceAssociation( face1, VV1, face2, VV2, edges1, edges2 ); if ( !nbE ) RETURN_BAD_RESULT("FindFaceAssociation() failed"); - InsertAssociation( face1, face2, theMap, bidirect); // assoc faces + InsertAssociation( face1, face2, theMap ); // assoc faces MESSAGE("Assoc FACE " << theMesh1->GetMeshDS()->ShapeToIndex( face1 )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( face2 )); if ( nbE == 2 && (edge1.IsSame( edges1.front())) != (edge2.IsSame( edges2.front()))) @@ -421,12 +684,12 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the for ( ; eIt1 != edges1.end(); ++eIt1, ++eIt2 ) { if ( !boundEdges.Add( *eIt1 )) continue; // already associated - InsertAssociation( *eIt1, *eIt2, theMap, bidirect); // assoc edges + InsertAssociation( *eIt1, *eIt2, theMap ); // assoc edges MESSAGE("Assoc edge " << theMesh1->GetMeshDS()->ShapeToIndex( *eIt1 )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( *eIt2 )); VV1[0] = TopExp::FirstVertex( *eIt1, true ); VV2[0] = TopExp::FirstVertex( *eIt2, true ); - InsertAssociation( VV1[0], VV2[0], theMap, bidirect); // assoc vertices + InsertAssociation( VV1[0], VV2[0], theMap ); // assoc vertices MESSAGE("Assoc vertex " << theMesh1->GetMeshDS()->ShapeToIndex( VV1[0] )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( VV2[0] )); @@ -439,7 +702,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } } } - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } // ---------------------------------------------------------------------- @@ -478,7 +741,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the if ( !initAssocOK ) { // for shell association there must be an edge with both vertices bound TopoDS_Vertex v1, v2; - TopExp::Vertices( TopoDS::Edge( it1.Value()), v1, v2 ); + TopExp::Vertices( TopoDS::Edge( it1.Value().Oriented(TopAbs_FORWARD)), v1, v2 ); initAssocOK = ( theMap.IsBound( v1 ) && theMap.IsBound( v2 )); } } @@ -561,7 +824,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the TShapeShapeMap tmpMap; ok = FindSubShapeAssociation( comp[0], theMesh1, comp[1], theMesh2, tmpMap ); if ( ok ) { - TopTools_DataMapIteratorOfDataMapOfShapeShape mapIt( tmpMap ); + TopTools_DataMapIteratorOfDataMapOfShapeShape mapIt( tmpMap._map1to2 ); for ( ; mapIt.More(); mapIt.Next() ) theMap.Bind( mapIt.Key(), mapIt.Value()); } @@ -620,13 +883,13 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the TopoDS_Edge e1 = TopoDS::Edge( edges1.First() ); v2e[0].UnBind( V[0] ); v2e[1].UnBind( V[1] ); - InsertAssociation( e0, e1, theMap, bidirect ); + InsertAssociation( e0, e1, theMap ); MESSAGE("Assoc edge " << theMesh1->GetMeshDS()->ShapeToIndex( e0 )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( e1 )); V[0] = GetNextVertex( e0, V[0] ); V[1] = GetNextVertex( e1, V[1] ); if ( !V[0].IsNull() ) { - InsertAssociation( V[0], V[1], theMap, bidirect ); + InsertAssociation( V[0], V[1], theMap ); MESSAGE("Assoc vertex " << theMesh1->GetMeshDS()->ShapeToIndex( V[0] )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( V[1] )); } @@ -652,9 +915,9 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } else { v1n = v1e1; e1b = edges1.First(); e1n = edges1.Last(); } - InsertAssociation( e0b, e1b, theMap, bidirect ); - InsertAssociation( e0n, e1n, theMap, bidirect ); - InsertAssociation( v0n, v1n, theMap, bidirect ); + InsertAssociation( e0b, e1b, theMap ); + InsertAssociation( e0n, e1n, theMap ); + InsertAssociation( v0n, v1n, theMap ); MESSAGE("Assoc edge " << theMesh1->GetMeshDS()->ShapeToIndex( e0b )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( e1b )); MESSAGE("Assoc edge " << theMesh1->GetMeshDS()->ShapeToIndex( e0n )<< @@ -681,7 +944,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } // end case of available initial vertex association //====================================================================== - // NO INITIAL VERTEX ASSOCIATION + // 4) NO INITIAL VERTEX ASSOCIATION //====================================================================== switch ( theShape1.ShapeType() ) { @@ -698,24 +961,25 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the TopoDS_Vertex VV1[2], VV2[2]; TopExp::Vertices( edge1, VV1[0], VV1[1], true ); TopExp::Vertices( prpEdge, VV2[0], VV2[1], true ); - InsertAssociation( VV1[ 0 ], VV2[ 0 ], theMap, bidirect); - InsertAssociation( VV1[ 1 ], VV2[ 1 ], theMap, bidirect); + InsertAssociation( VV1[ 0 ], VV2[ 0 ], theMap ); + InsertAssociation( VV1[ 1 ], VV2[ 1 ], theMap ); if ( VV1[0].IsSame( VV1[1] ) || // one of edges is closed VV2[0].IsSame( VV2[1] ) ) { - InsertAssociation( edge1, prpEdge, theMap, bidirect); // insert with a proper orientation + InsertAssociation( edge1, prpEdge, theMap ); // insert with a proper orientation } - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; // done } } - if ( IsClosedEdge( edge1 ) && IsClosedEdge( edge2 )) + if ( SMESH_MesherHelper::IsClosedEdge( edge1 ) && + SMESH_MesherHelper::IsClosedEdge( edge2 )) { // TODO: find out a proper orientation (is it possible?) - InsertAssociation( edge1, edge2, theMap, bidirect); // insert with a proper orientation + InsertAssociation( edge1, edge2, theMap ); // insert with a proper orientation InsertAssociation( TopExp::FirstVertex(edge1), TopExp::FirstVertex(edge2), - theMap, bidirect); - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + theMap ); + InsertAssociation( theShape1, theShape2, theMap ); return true; // done } break; // try by vertex closeness @@ -727,6 +991,8 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the { TopoDS_Face face1 = TopoDS::Face(theShape1); TopoDS_Face face2 = TopoDS::Face(theShape2); + if ( face1.Orientation() >= TopAbs_INTERNAL ) face1.Orientation( TopAbs_FORWARD ); + if ( face2.Orientation() >= TopAbs_INTERNAL ) face2.Orientation( TopAbs_FORWARD ); TopoDS_Edge edge1, edge2; // get outer edge of theShape1 edge1 = TopoDS::Edge( OuterShape( face1, TopAbs_EDGE )); @@ -737,6 +1003,7 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the pair step_edge = GetPropagationEdge( theMesh1, edge2, edge1 ); if ( !step_edge.second.IsNull() ) { // propagation found propag_edges.insert( step_edge ); + if ( step_edge.first == 1 ) break; // most close found } } if ( !propag_edges.empty() ) // propagation found @@ -748,25 +1015,26 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the list< TopoDS_Edge > edges1, edges2; int nbE = FindFaceAssociation( face1, VV1, face2, VV2, edges1, edges2 ); if ( !nbE ) RETURN_BAD_RESULT("FindFaceAssociation() failed"); - if ( nbE == 2 ) // only 2 edges + // take care of proper association of propagated edges + bool same1 = edge1.IsSame( edges1.front() ); + bool same2 = edge2.IsSame( edges2.front() ); + if ( same1 != same2 ) { - // take care of proper association of propagated edges - bool same1 = edge1.IsSame( edges1.front() ); - bool same2 = edge2.IsSame( edges2.front() ); - if ( same1 != same2 ) - Reverse(edges2, nbE); + Reverse(edges2, nbE); + if ( nbE != 2 ) // 2 degen edges of 4 (issue 0021144) + edges2.splice( edges2.end(), edges2, edges2.begin()); } // store association list< TopoDS_Edge >::iterator eIt1 = edges1.begin(); list< TopoDS_Edge >::iterator eIt2 = edges2.begin(); for ( ; eIt1 != edges1.end(); ++eIt1, ++eIt2 ) { - InsertAssociation( *eIt1, *eIt2, theMap, bidirect); + InsertAssociation( *eIt1, *eIt2, theMap ); VV1[0] = TopExp::FirstVertex( *eIt1, true ); VV2[0] = TopExp::FirstVertex( *eIt2, true ); - InsertAssociation( VV1[0], VV2[0], theMap, bidirect); + InsertAssociation( VV1[0], VV2[0], theMap ); } - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } } @@ -775,25 +1043,16 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the case TopAbs_COMPOUND: { // ---------------------------------------------------------------------- if ( IsPropagationPossible( theMesh1, theMesh2 )) { - // find a boundary edge for theShape1 - TopoDS_Edge E; - for(TopExp_Explorer exp(theShape1, TopAbs_EDGE); exp.More(); exp.Next() ) { - E = TopoDS::Edge( exp.Current() ); - int NbFacesFromShape1 = 0; - const TopTools_ListOfShape& EAncestors = theMesh1->GetAncestors(E); - TopTools_ListIteratorOfListOfShape itea(EAncestors); - for(; itea.More(); itea.Next()) { - if( itea.Value().ShapeType() != TopAbs_FACE ) continue; - TopoDS_Face face = TopoDS::Face(itea.Value()); - for(TopExp_Explorer expf(theShape1, TopAbs_FACE); expf.More(); expf.Next() ) { - if(face.IsSame(expf.Current())) { - NbFacesFromShape1++; - break; - } - } - } - if(NbFacesFromShape1==1) break; - } + + // try to accosiate all using propagation + if ( AssocGroupsByPropagation( theShape1, theShape2, *theMesh1, theMap )) + return true; + + // find a boundary edge of theShape1 + TopoDS_Edge E = GetBoundaryEdge( theShape1, *theMesh1 ); + if ( E.IsNull() ) + break; // try by vertex closeness + // find association for vertices of edge E TopoDS_Vertex VV1[2], VV2[2]; for(TopExp_Explorer eexp(E, TopAbs_VERTEX); eexp.More(); eexp.Next()) { @@ -838,8 +1097,8 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } } if ( !VV1[1].IsNull() ) { - InsertAssociation( VV1[0], VV2[0], theMap, bidirect); - InsertAssociation( VV1[1], VV2[1], theMap, bidirect); + InsertAssociation( VV1[0], VV2[0], theMap ); + InsertAssociation( VV1[1], VV2[1], theMap ); return FindSubShapeAssociation( theShape1, theMesh1, theShape2, theMesh2, theMap); } } @@ -848,25 +1107,52 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the default:; } - // Find association by closeness of vertices - // ------------------------------------------ + // 4.b) Find association by closeness of vertices + // ---------------------------------------------- TopTools_IndexedMapOfShape vMap1, vMap2; TopExp::MapShapes( theShape1, TopAbs_VERTEX, vMap1 ); TopExp::MapShapes( theShape2, TopAbs_VERTEX, vMap2 ); + TopoDS_Vertex VV1[2], VV2[2]; if ( vMap1.Extent() != vMap2.Extent() ) RETURN_BAD_RESULT("Different nb of vertices"); if ( vMap1.Extent() == 1 ) { - InsertAssociation( vMap1(1), vMap2(1), theMap, bidirect); + InsertAssociation( vMap1(1), vMap2(1), theMap ); if ( theShape1.ShapeType() == TopAbs_EDGE ) { - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } return FindSubShapeAssociation( theShape1, theMesh1, theShape2, theMesh2, theMap); } + // Try to associate by common vertices of an edge + for ( int i = 1; i <= vMap1.Extent(); ++i ) + { + const TopoDS_Shape& v1 = vMap1(i); + if ( vMap2.Contains( v1 )) + { + // find an egde sharing v1 and sharing at the same time another common vertex + PShapeIteratorPtr edgeIt = SMESH_MesherHelper::GetAncestors( v1, *theMesh1, TopAbs_EDGE); + bool edgeFound = false; + while ( edgeIt->more() && !edgeFound ) + { + TopoDS_Edge edge = TopoDS::Edge( edgeIt->next()->Oriented(TopAbs_FORWARD)); + TopExp::Vertices(edge, VV1[0], VV1[1]); + if ( !VV1[0].IsSame( VV1[1] )) + edgeFound = ( vMap2.Contains( VV1[ v1.IsSame(VV1[0]) ? 1:0])); + } + if ( edgeFound ) + { + InsertAssociation( VV1[0], VV1[0], theMap ); + InsertAssociation( VV1[1], VV1[1], theMap ); + if (FindSubShapeAssociation( theShape1, theMesh1, theShape2, theMesh2, theMap )) + return true; + } + } + } + // Find transformation to make the shapes be of similar size at same location Bnd_Box box[2]; @@ -888,30 +1174,12 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the // Find 2 closest vertices - TopoDS_Vertex VV1[2], VV2[2]; // get 2 linked vertices of shape 1 not belonging to an inner wire of a face - TopoDS_Shape edge = theShape1; - TopExp_Explorer expF( theShape1, TopAbs_FACE ), expE; - if ( expF.More() ) { - for ( ; expF.More(); expF.Next() ) { - edge.Nullify(); - TopoDS_Shape wire = OuterShape( TopoDS::Face( expF.Current() ), TopAbs_WIRE ); - for ( expE.Init( wire, TopAbs_EDGE ); edge.IsNull() && expE.More(); expE.Next() ) - if ( !IsClosedEdge( TopoDS::Edge( expE.Current() ))) - edge = expE.Current(); - if ( !edge.IsNull() ) - break; - } - } else if (edge.ShapeType() != TopAbs_EDGE) { // no faces - edge.Nullify(); - for ( expE.Init( theShape1, TopAbs_EDGE ); edge.IsNull() && expE.More(); expE.Next() ) - if ( !IsClosedEdge( TopoDS::Edge( expE.Current() ))) - edge = expE.Current(); - } + TopoDS_Shape edge = getOuterEdge( theShape1, *theMesh1 ); if ( edge.IsNull() || edge.ShapeType() != TopAbs_EDGE ) RETURN_BAD_RESULT("Edge not found"); - TopExp::Vertices( TopoDS::Edge( edge ), VV1[0], VV1[1]); + TopExp::Vertices( TopoDS::Edge( edge.Oriented(TopAbs_FORWARD)), VV1[0], VV1[1]); if ( VV1[0].IsSame( VV1[1] )) RETURN_BAD_RESULT("Only closed edges"); @@ -933,14 +1201,14 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the } } - InsertAssociation( VV1[ 0 ], VV2[ 0 ], theMap, bidirect); - InsertAssociation( VV1[ 1 ], VV2[ 1 ], theMap, bidirect); + InsertAssociation( VV1[ 0 ], VV2[ 0 ], theMap ); + InsertAssociation( VV1[ 1 ], VV2[ 1 ], theMap ); MESSAGE("Initial assoc VERT " << theMesh1->GetMeshDS()->ShapeToIndex( VV1[ 0 ] )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( VV2[ 0 ] )<< "\nand VERT " << theMesh1->GetMeshDS()->ShapeToIndex( VV1[ 1 ] )<< " to " << theMesh2->GetMeshDS()->ShapeToIndex( VV2[ 1 ] )); if ( theShape1.ShapeType() == TopAbs_EDGE ) { - InsertAssociation( theShape1, theShape2, theMap, bidirect ); + InsertAssociation( theShape1, theShape2, theMap ); return true; } @@ -953,75 +1221,153 @@ bool StdMeshers_ProjectionUtils::FindSubShapeAssociation(const TopoDS_Shape& the * \param face1 - face 1 * \param VV1 - vertices of face 1 * \param face2 - face 2 - * \param VV2 - vertices of face 2 associated with oned of face 1 + * \param VV2 - vertices of face 2 associated with ones of face 1 * \param edges1 - out list of edges of face 1 * \param edges2 - out list of edges of face 2 * \retval int - nb of edges in an outer wire in a success case, else zero */ //================================================================================ -int StdMeshers_ProjectionUtils::FindFaceAssociation(const TopoDS_Face& face1, - TopoDS_Vertex VV1[2], - const TopoDS_Face& face2, - TopoDS_Vertex VV2[2], +int StdMeshers_ProjectionUtils::FindFaceAssociation(const TopoDS_Face& face1, + TopoDS_Vertex VV1[2], + const TopoDS_Face& face2, + TopoDS_Vertex VV2[2], list< TopoDS_Edge > & edges1, list< TopoDS_Edge > & edges2) { - edges1.clear(); - edges2.clear(); - - list< int > nbVInW1, nbVInW2; - if ( SMESH_Block::GetOrderedEdges( face1, VV1[0], edges1, nbVInW1) != - SMESH_Block::GetOrderedEdges( face2, VV2[0], edges2, nbVInW2) ) - RETURN_BAD_RESULT("Different number of wires in faces "); - - if ( nbVInW1.front() != nbVInW2.front() ) - RETURN_BAD_RESULT("Different number of edges in faces: " << - nbVInW1.front() << " != " << nbVInW2.front()); - - // Define if we need to reverse one of wires to make edges in lists match each other - - bool reverse = false; - - list< TopoDS_Edge >::iterator eBackIt; - if ( !VV1[1].IsSame( TopExp::LastVertex( edges1.front(), true ))) { - reverse = true; - eBackIt = --edges1.end(); - // check if the second vertex belongs to the first or last edge in the wire - if ( !VV1[1].IsSame( TopExp::FirstVertex( *eBackIt, true ))) { - bool KO = true; // belongs to none - if ( nbVInW1.size() > 1 ) { // several wires - eBackIt = edges1.begin(); - for ( int i = 1; i < nbVInW1.front(); ++i ) ++eBackIt; - KO = !VV1[1].IsSame( TopExp::FirstVertex( *eBackIt, true )); + bool OK = false; + list< int > nbEInW1, nbEInW2; + int i_ok_wire_algo = -1; + for ( int outer_wire_algo = 0; outer_wire_algo < 2 && !OK; ++outer_wire_algo ) + { + edges1.clear(); + edges2.clear(); + + if ( SMESH_Block::GetOrderedEdges( face1, VV1[0], edges1, nbEInW1, outer_wire_algo) != + SMESH_Block::GetOrderedEdges( face2, VV2[0], edges2, nbEInW2, outer_wire_algo) ) + CONT_BAD_RESULT("Different number of wires in faces "); + + if ( nbEInW1 != nbEInW2 ) + CONT_BAD_RESULT("Different number of edges in faces: " << + nbEInW1.front() << " != " << nbEInW2.front()); + + i_ok_wire_algo = outer_wire_algo; + + // Define if we need to reverse one of wires to make edges in lists match each other + + bool reverse = false; + + list< TopoDS_Edge >::iterator edgeIt; + if ( !VV1[1].IsSame( TopExp::LastVertex( edges1.front(), true ))) { + reverse = true; + edgeIt = --edges1.end(); + // check if the second vertex belongs to the first or last edge in the wire + if ( !VV1[1].IsSame( TopExp::FirstVertex( *edgeIt, true ))) { + bool KO = true; // belongs to none + if ( nbEInW1.size() > 1 ) { // several wires + edgeIt = edges1.begin(); + std::advance( edgeIt, nbEInW1.front()-1 ); + KO = !VV1[1].IsSame( TopExp::FirstVertex( *edgeIt, true )); + } + if ( KO ) + CONT_BAD_RESULT("GetOrderedEdges() failed"); } - if ( KO ) - RETURN_BAD_RESULT("GetOrderedEdges() failed"); } - } - eBackIt = --edges2.end(); - if ( !VV2[1].IsSame( TopExp::LastVertex( edges2.front(), true ))) { - reverse = !reverse; - // check if the second vertex belongs to the first or last edge in the wire - if ( !VV2[1].IsSame( TopExp::FirstVertex( *eBackIt, true ))) { - bool KO = true; // belongs to none - if ( nbVInW2.size() > 1 ) { // several wires - eBackIt = edges2.begin(); - for ( int i = 1; i < nbVInW2.front(); ++i ) ++eBackIt; - KO = !VV2[1].IsSame( TopExp::FirstVertex( *eBackIt, true )); + edgeIt = --edges2.end(); + if ( !VV2[1].IsSame( TopExp::LastVertex( edges2.front(), true ))) { + reverse = !reverse; + // check if the second vertex belongs to the first or last edge in the wire + if ( !VV2[1].IsSame( TopExp::FirstVertex( *edgeIt, true ))) { + bool KO = true; // belongs to none + if ( nbEInW2.size() > 1 ) { // several wires + edgeIt = edges2.begin(); + std::advance( edgeIt, nbEInW2.front()-1 ); + KO = !VV2[1].IsSame( TopExp::FirstVertex( *edgeIt, true )); + } + if ( KO ) + CONT_BAD_RESULT("GetOrderedEdges() failed"); } - if ( KO ) - RETURN_BAD_RESULT("GetOrderedEdges() failed"); } - } - if ( reverse ) + if ( reverse ) + { + Reverse( edges2 , nbEInW2.front()); + if (( VV1[1].IsSame( TopExp::LastVertex( edges1.front(), true ))) != + ( VV2[1].IsSame( TopExp::LastVertex( edges2.front(), true )))) + CONT_BAD_RESULT("GetOrderedEdges() failed"); + } + OK = true; + + } // loop algos getting an outer wire + + // Try to orient all (if !OK) or only internal wires (issue 0020996) by UV similarity + if (( !OK || nbEInW1.size() > 1 ) && i_ok_wire_algo > -1 ) { - Reverse( edges2 , nbVInW2.front()); - if (( VV1[1].IsSame( TopExp::LastVertex( edges1.front(), true ))) != - ( VV2[1].IsSame( TopExp::LastVertex( edges2.front(), true )))) - RETURN_BAD_RESULT("GetOrderedEdges() failed"); + // Check that Vec(VV1[0],VV1[1]) in 2D on face1 is the same + // as Vec(VV2[0],VV2[1]) on face2 + double vTol = BRep_Tool::Tolerance( VV1[0] ); + BRepAdaptor_Surface surface1( face1, false ); + double vTolUV = + surface1.UResolution( vTol ) + surface1.VResolution( vTol ); // let's be tolerant + gp_Pnt2d v0f1UV = BRep_Tool::Parameters( VV1[0], face1 ); + gp_Pnt2d v0f2UV = BRep_Tool::Parameters( VV2[0], face2 ); + gp_Pnt2d v1f1UV = BRep_Tool::Parameters( VV1[1], face1 ); + gp_Pnt2d v1f2UV = BRep_Tool::Parameters( VV2[1], face2 ); + gp_Vec2d v01f1Vec( v0f1UV, v1f1UV ); + gp_Vec2d v01f2Vec( v0f2UV, v1f2UV ); + if ( Abs( v01f1Vec.X()-v01f2Vec.X()) < vTolUV && + Abs( v01f1Vec.Y()-v01f2Vec.Y()) < vTolUV ) + { + if ( !OK /*i_ok_wire_algo != 1*/ ) + { + edges1.clear(); + edges2.clear(); + SMESH_Block::GetOrderedEdges( face1, VV1[0], edges1, nbEInW1, i_ok_wire_algo); + SMESH_Block::GetOrderedEdges( face2, VV2[0], edges2, nbEInW2, i_ok_wire_algo); + } + gp_XY dUV = v0f2UV.XY() - v0f1UV.XY(); // UV shift between 2 faces + // skip edges of the outer wire (if the outer wire is OK) + list< int >::iterator nbEInW = nbEInW1.begin(); + list< TopoDS_Edge >::iterator edge1Beg = edges1.begin(), edge2Beg = edges2.begin(); + if ( OK ) + { + for ( int i = 0; i < *nbEInW; ++i ) + ++edge1Beg, ++edge2Beg; + ++nbEInW; + } + for ( ; nbEInW != nbEInW1.end(); ++nbEInW ) // loop on wires + { + // reach an end of edges of a current wire + list< TopoDS_Edge >::iterator edge1End = edge1Beg, edge2End = edge2Beg; + for ( int i = 0; i < *nbEInW; ++i ) + ++edge1End, ++edge2End; + // rotate edges2 untill coincident with edges1 in 2D + v0f1UV = BRep_Tool::Parameters( TopExp::FirstVertex(*edge1Beg,true), face1 ); + v1f1UV = BRep_Tool::Parameters( TopExp::LastVertex (*edge1Beg,true), face1 ); + v0f1UV.ChangeCoord() += dUV; + v1f1UV.ChangeCoord() += dUV; + int i = *nbEInW; + while ( --i > 0 && !sameVertexUV( *edge2Beg, face2, 0, v0f1UV, vTolUV )) + edges2.splice( edge2End, edges2, edge2Beg++ ); // move edge2Beg to place before edge2End + if ( sameVertexUV( *edge2Beg, face2, 0, v0f1UV, vTolUV )) + { + if ( nbEInW == nbEInW1.begin() ) + OK = true; // OK is for the first wire + // reverse edges2 if needed + if ( !sameVertexUV( *edge2Beg, face2, 1, v1f1UV, vTolUV )) + { + Reverse( edges2 , *nbEInW, distance( edges2.begin(),edge2Beg )); + // set correct edge2End + edge2End = edges2.begin(); + std::advance( edge2End, std::accumulate( nbEInW1.begin(), nbEInW, *nbEInW)); + } + } + // prepare to the next wire loop + edge1Beg = edge1End, edge2Beg = edge2End; + } + } } - return nbVInW2.front(); + + return OK ? nbEInW1.front() : 0; } //======================================================================= @@ -1030,30 +1376,29 @@ int StdMeshers_ProjectionUtils::FindFaceAssociation(const TopoDS_Face& face1, //======================================================================= void StdMeshers_ProjectionUtils::InitVertexAssociation( const SMESH_Hypothesis* theHyp, - TShapeShapeMap & theAssociationMap, - const TopoDS_Shape& theTargetShape) + TShapeShapeMap & theAssociationMap) { string hypName = theHyp->GetName(); if ( hypName == "ProjectionSource1D" ) { const StdMeshers_ProjectionSource1D * hyp = static_cast( theHyp ); if ( hyp->HasVertexAssociation() ) - InsertAssociation( hyp->GetSourceVertex(),hyp->GetTargetVertex(),theAssociationMap); + InsertAssociation( hyp->GetTargetVertex(),hyp->GetSourceVertex(),theAssociationMap ); } else if ( hypName == "ProjectionSource2D" ) { const StdMeshers_ProjectionSource2D * hyp = static_cast( theHyp ); if ( hyp->HasVertexAssociation() ) { - InsertAssociation( hyp->GetSourceVertex(1),hyp->GetTargetVertex(1),theAssociationMap); - InsertAssociation( hyp->GetSourceVertex(2),hyp->GetTargetVertex(2),theAssociationMap); + InsertAssociation( hyp->GetTargetVertex(1),hyp->GetSourceVertex(1),theAssociationMap); + InsertAssociation( hyp->GetTargetVertex(2),hyp->GetSourceVertex(2),theAssociationMap); } } else if ( hypName == "ProjectionSource3D" ) { const StdMeshers_ProjectionSource3D * hyp = static_cast( theHyp ); if ( hyp->HasVertexAssociation() ) { - InsertAssociation( hyp->GetSourceVertex(1),hyp->GetTargetVertex(1),theAssociationMap); - InsertAssociation( hyp->GetSourceVertex(2),hyp->GetTargetVertex(2),theAssociationMap); + InsertAssociation( hyp->GetTargetVertex(1),hyp->GetSourceVertex(1),theAssociationMap); + InsertAssociation( hyp->GetTargetVertex(2),hyp->GetSourceVertex(2),theAssociationMap); } } } @@ -1061,24 +1406,21 @@ void StdMeshers_ProjectionUtils::InitVertexAssociation( const SMESH_Hypothesis* //======================================================================= /*! * \brief Inserts association theShape1 <-> theShape2 to TShapeShapeMap - * \param theShape1 - shape 1 - * \param theShape2 - shape 2 + * \param theShape1 - target shape + * \param theShape2 - source shape * \param theAssociationMap - association map * \retval bool - true if there was no association for these shapes before */ //======================================================================= -bool StdMeshers_ProjectionUtils::InsertAssociation( const TopoDS_Shape& theShape1, - const TopoDS_Shape& theShape2, - TShapeShapeMap & theAssociationMap, - const bool theBidirectional) +bool StdMeshers_ProjectionUtils::InsertAssociation( const TopoDS_Shape& theShape1, // tgt + const TopoDS_Shape& theShape2, // src + TShapeShapeMap & theAssociationMap) { if ( !theShape1.IsNull() && !theShape2.IsNull() ) { - SHOW_VERTEX(theShape1,"Assoc "); - SHOW_VERTEX(theShape2," to "); + SHOW_SHAPE(theShape1,"Assoc "); + SHOW_SHAPE(theShape2," to "); bool isNew = ( theAssociationMap.Bind( theShape1, theShape2 )); - if ( theBidirectional ) - theAssociationMap.Bind( theShape2, theShape1 ); return isNew; } else { @@ -1087,44 +1429,6 @@ bool StdMeshers_ProjectionUtils::InsertAssociation( const TopoDS_Shape& theShape return false; } -//======================================================================= -//function : IsSubShape -//purpose : -//======================================================================= - -bool StdMeshers_ProjectionUtils::IsSubShape( const TopoDS_Shape& shape, - SMESH_Mesh* aMesh ) -{ - if ( shape.IsNull() || !aMesh ) - return false; - return - aMesh->GetMeshDS()->ShapeToIndex( shape ) || - // PAL16202 - shape.ShapeType() == TopAbs_COMPOUND && aMesh->GetMeshDS()->IsGroupOfSubShapes( shape ); -} - -//======================================================================= -//function : IsSubShape -//purpose : -//======================================================================= - -bool StdMeshers_ProjectionUtils::IsSubShape( const TopoDS_Shape& shape, - const TopoDS_Shape& mainShape ) -{ - if ( !shape.IsNull() && !mainShape.IsNull() ) - { - for ( TopExp_Explorer exp( mainShape, shape.ShapeType()); - exp.More(); - exp.Next() ) - if ( shape.IsSame( exp.Current() )) - return true; - } - SCRUTE((shape.IsNull())); - SCRUTE((mainShape.IsNull())); - return false; -} - - //======================================================================= /*! * \brief Finds an edge by its vertices in a main shape of the mesh @@ -1210,7 +1514,7 @@ StdMeshers_ProjectionUtils::GetPropagationEdge( SMESH_Mesh* aMesh, const TopoDS_Edge& theEdge, const TopoDS_Edge& fromEdge) { - SMESH_IndexedMapOfShape aChain; + TopTools_IndexedMapOfShape aChain; int step = 0; // List of edges, added to chain on the previous cycle pass @@ -1286,7 +1590,7 @@ StdMeshers_ProjectionUtils::GetPropagationEdge( SMESH_Mesh* aMesh, * \param mesh1 - mesh containing elements on the first face * \param face2 - the second face * \param mesh2 - mesh containing elements on the second face - * \param assocMap - map associating subshapes of the faces + * \param assocMap - map associating sub-shapes of the faces * \param node1To2Map - map containing found matching nodes * \retval bool - is a success */ @@ -1330,24 +1634,25 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, // 1. Nodes of corresponding links: // get 2 matching edges, try to find not seam ones - TopoDS_Edge edge1, edge2, seam1, seam2; + TopoDS_Edge edge1, edge2, seam1, seam2, anyEdge1, anyEdge2; TopExp_Explorer eE( OuterShape( face2, TopAbs_WIRE ), TopAbs_EDGE ); do { // edge 2 TopoDS_Edge e2 = TopoDS::Edge( eE.Current() ); eE.Next(); // edge 1 - if ( !assocMap.IsBound( e2 )) + if ( !assocMap.IsBound( e2, /*is2nd=*/true )) RETURN_BAD_RESULT("Association not found for edge " << meshDS2->ShapeToIndex( e2 )); - TopoDS_Edge e1 = TopoDS::Edge( assocMap( e2 )); - if ( !IsSubShape( e1, face1 )) + TopoDS_Edge e1 = TopoDS::Edge( assocMap( e2, /*is2nd=*/true )); + if ( !helper1.IsSubShape( e1, face1 )) RETURN_BAD_RESULT("Wrong association, edge " << meshDS1->ShapeToIndex( e1 ) << - " isn't a subshape of face " << meshDS1->ShapeToIndex( face1 )); + " isn't a sub-shape of face " << meshDS1->ShapeToIndex( face1 )); // check that there are nodes on edges SMESHDS_SubMesh * eSM1 = meshDS1->MeshElements( e1 ); SMESHDS_SubMesh * eSM2 = meshDS2->MeshElements( e2 ); bool nodesOnEdges = ( eSM1 && eSM2 && eSM1->NbNodes() && eSM2->NbNodes() ); // check that the nodes on edges belong to faces + // (as NETGEN ignores nodes on the degenerated geom edge) bool nodesOfFaces = false; if ( nodesOnEdges ) { const SMDS_MeshNode* n1 = eSM1->GetNodes()->next(); @@ -1364,18 +1669,25 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, edge1 = e1; edge2 = e2; } } + else { + anyEdge1 = e1; anyEdge2 = e2; + } } while ( edge2.IsNull() && eE.More() ); // if ( edge2.IsNull() ) { edge1 = seam1; edge2 = seam2; } - if ( edge2.IsNull() ) RETURN_BAD_RESULT("No matching edges with nodes found"); + bool hasNodesOnEdge = (! edge2.IsNull() ); + if ( !hasNodesOnEdge ) { + // 0020338 - nb segments == 1 + edge1 = anyEdge1; edge2 = anyEdge2; + } // get 2 matching vertices TopoDS_Vertex V2 = TopExp::FirstVertex( TopoDS::Edge( edge2 )); - if ( !assocMap.IsBound( V2 )) + if ( !assocMap.IsBound( V2, /*is2nd=*/true )) RETURN_BAD_RESULT("Association not found for vertex " << meshDS2->ShapeToIndex( V2 )); - TopoDS_Vertex V1 = TopoDS::Vertex( assocMap( V2 )); + TopoDS_Vertex V1 = TopoDS::Vertex( assocMap( V2, /*is2nd=*/true )); // nodes on vertices const SMDS_MeshNode* vNode1 = SMESH_Algo::VertexNode( V1, meshDS1 ); @@ -1387,35 +1699,52 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, const SMDS_MeshNode* nullNode = 0; vector< const SMDS_MeshNode*> eNode1( 2, nullNode ); vector< const SMDS_MeshNode*> eNode2( 2, nullNode ); - int nbNodeToGet = 1; - if ( IsClosedEdge( edge1 ) || IsClosedEdge( edge2 ) ) - nbNodeToGet = 2; - for ( int is2 = 0; is2 < 2; ++is2 ) + if ( hasNodesOnEdge ) { - TopoDS_Edge & edge = is2 ? edge2 : edge1; - SMESHDS_Mesh * smDS = is2 ? meshDS2 : meshDS1; - SMESHDS_SubMesh* edgeSM = smDS->MeshElements( edge ); - // nodes linked with ones on vertices - const SMDS_MeshNode* vNode = is2 ? vNode2 : vNode1; - vector< const SMDS_MeshNode*>& eNode = is2 ? eNode2 : eNode1; - int nbGotNode = 0; - SMDS_ElemIteratorPtr vElem = vNode->GetInverseElementIterator(); - while ( vElem->more() && nbGotNode != nbNodeToGet ) { - const SMDS_MeshElement* elem = vElem->next(); - if ( elem->GetType() == SMDSAbs_Edge && edgeSM->Contains( elem )) - eNode[ nbGotNode++ ] = - ( elem->GetNode(0) == vNode ) ? elem->GetNode(1) : elem->GetNode(0); - } - if ( nbGotNode > 1 ) // sort found nodes by param on edge + int nbNodeToGet = 1; + if ( helper1.IsClosedEdge( edge1 ) || helper2.IsClosedEdge( edge2 ) ) + nbNodeToGet = 2; + for ( int is2 = 0; is2 < 2; ++is2 ) { - SMESH_MesherHelper* helper = is2 ? &helper2 : &helper1; - double u0 = helper->GetNodeU( edge, eNode[ 0 ]); - double u1 = helper->GetNodeU( edge, eNode[ 1 ]); - if ( u0 > u1 ) std::swap( eNode[ 0 ], eNode[ 1 ]); + TopoDS_Edge & edge = is2 ? edge2 : edge1; + SMESHDS_Mesh * smDS = is2 ? meshDS2 : meshDS1; + SMESHDS_SubMesh* edgeSM = smDS->MeshElements( edge ); + // nodes linked with ones on vertices + const SMDS_MeshNode* vNode = is2 ? vNode2 : vNode1; + vector< const SMDS_MeshNode*>& eNode = is2 ? eNode2 : eNode1; + int nbGotNode = 0; + SMDS_ElemIteratorPtr vElem = vNode->GetInverseElementIterator(SMDSAbs_Edge); + while ( vElem->more() && nbGotNode != nbNodeToGet ) { + const SMDS_MeshElement* elem = vElem->next(); + if ( edgeSM->Contains( elem )) + eNode[ nbGotNode++ ] = + ( elem->GetNode(0) == vNode ) ? elem->GetNode(1) : elem->GetNode(0); + } + if ( nbGotNode > 1 ) // sort found nodes by param on edge + { + SMESH_MesherHelper* helper = is2 ? &helper2 : &helper1; + double u0 = helper->GetNodeU( edge, eNode[ 0 ]); + double u1 = helper->GetNodeU( edge, eNode[ 1 ]); + if ( u0 > u1 ) std::swap( eNode[ 0 ], eNode[ 1 ]); + } + if ( nbGotNode == 0 ) + RETURN_BAD_RESULT("Found no nodes on edge " << smDS->ShapeToIndex( edge ) << + " linked to " << vNode ); } - if ( nbGotNode == 0 ) - RETURN_BAD_RESULT("Found no nodes on edge " << smDS->ShapeToIndex( edge ) << - " linked to " << vNode ); + } + else // 0020338 - nb segments == 1 + { + // get 2 other matching vertices + V2 = TopExp::LastVertex( TopoDS::Edge( edge2 )); + if ( !assocMap.IsBound( V2, /*is2nd=*/true )) + RETURN_BAD_RESULT("Association not found for vertex " << meshDS2->ShapeToIndex( V2 )); + V1 = TopoDS::Vertex( assocMap( V2, /*is2nd=*/true )); + + // nodes on vertices + eNode1[0] = SMESH_Algo::VertexNode( V1, meshDS1 ); + eNode2[0] = SMESH_Algo::VertexNode( V2, meshDS2 ); + if ( !eNode1[0] ) RETURN_BAD_RESULT("No node on vertex #" << meshDS1->ShapeToIndex( V1 )); + if ( !eNode2[0] ) RETURN_BAD_RESULT("No node on vertex #" << meshDS2->ShapeToIndex( V2 )); } // 2. face sets @@ -1461,7 +1790,7 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, const SMDS_MeshElement* f = ( iF ? f2 : f1 ); for ( int i = 0; !notSeamNode[ iF ] && i < f->NbNodes(); ++i ) { const SMDS_MeshNode* node = f->GetNode( i ); - if ( !helper->IsSeamShape( node->GetPosition()->GetShapeId() )) + if ( !helper->IsSeamShape( node->getshapeId() )) notSeamNode[ iF ] = node; } } @@ -1494,7 +1823,7 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, notInSet.insert( f2 ); for ( int i = 0; i < nbNodes; ++i ) { const SMDS_MeshNode* n1 = faceToKeep->GetNode( i ); - const SMDS_MeshNode* n2 = faceToKeep->GetNode( i+1 ); + const SMDS_MeshNode* n2 = faceToKeep->GetNode(( i+1 ) % nbNodes ); f1 = SMESH_MeshEditor::FindFaceInSet( n1, n2, inSet, notInSet ); if ( f1 ) elems.insert( f1 ); @@ -1529,7 +1858,7 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, while ( nIt->more() ) { const SMDS_MeshNode* node = nIt->next(); const SMDS_EdgePosition* pos = - static_cast(node->GetPosition().get()); + static_cast(node->GetPosition()); pos2nodes.insert( make_pair( pos->GetUParameter(), node )); } if ( pos2nodes.size() != edgeSM->NbNodes() ) @@ -1561,9 +1890,9 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, // associate matching nodes on the last vertices V2 = TopExp::LastVertex( TopoDS::Edge( edge2 )); - if ( !assocMap.IsBound( V2 )) + if ( !assocMap.IsBound( V2, /*is2nd=*/true )) RETURN_BAD_RESULT("Association not found for vertex " << meshDS2->ShapeToIndex( V2 )); - V1 = TopoDS::Vertex( assocMap( V2 )); + V1 = TopoDS::Vertex( assocMap( V2, /*is2nd=*/true )); vNode1 = SMESH_Algo::VertexNode( V1, meshDS1 ); vNode2 = SMESH_Algo::VertexNode( V2, meshDS2 ); if ( !vNode1 ) RETURN_BAD_RESULT("No node on vertex #" << meshDS1->ShapeToIndex( V1 )); @@ -1581,25 +1910,12 @@ FindMatchingNodesOnFaces( const TopoDS_Face& face1, return true; } -//================================================================================ -/*! - * \brief Check if the first and last vertices of an edge are the same - * \param anEdge - the edge to check - * \retval bool - true if same - */ -//================================================================================ - -bool StdMeshers_ProjectionUtils::IsClosedEdge( const TopoDS_Edge& anEdge ) -{ - return TopExp::FirstVertex( anEdge ).IsSame( TopExp::LastVertex( anEdge )); -} - //================================================================================ /*! - * \brief Return any subshape of a face belonging to the outer wire + * \brief Return any sub-shape of a face belonging to the outer wire * \param face - the face - * \param type - type of subshape to return - * \retval TopoDS_Shape - the found subshape + * \param type - type of sub-shape to return + * \retval TopoDS_Shape - the found sub-shape */ //================================================================================ @@ -1632,7 +1948,7 @@ bool StdMeshers_ProjectionUtils::MakeComputed(SMESH_subMesh * sm, const int iter SMESH_Mesh* mesh = sm->GetFather(); SMESH_Gen* gen = mesh->GetGen(); - SMESH_Algo* algo = gen->GetAlgo( *mesh, sm->GetSubShape() ); + SMESH_Algo* algo = sm->GetAlgo(); if ( !algo ) { if ( sm->GetSubShape().ShapeType() != TopAbs_COMPOUND ) @@ -1695,12 +2011,12 @@ bool StdMeshers_ProjectionUtils::MakeComputed(SMESH_subMesh * sm, const int iter } //================================================================================ - /*! - * \brief Count nb of subshapes - * \param shape - the shape - * \param type - the type of subshapes to count - * \retval int - the calculated number - */ +/*! + * \brief Count nb of sub-shapes + * \param shape - the shape + * \param type - the type of sub-shapes to count + * \retval int - the calculated number + */ //================================================================================ int StdMeshers_ProjectionUtils::Count(const TopoDS_Shape& shape, @@ -1720,7 +2036,37 @@ int StdMeshers_ProjectionUtils::Count(const TopoDS_Shape& shape, } } -namespace { +//================================================================================ +/*! + * \brief Return a boundary EDGE of edgeContainer + */ +//================================================================================ + +TopoDS_Edge StdMeshers_ProjectionUtils::GetBoundaryEdge(const TopoDS_Shape& edgeContainer, + const SMESH_Mesh& mesh) +{ + TopTools_IndexedMapOfShape facesOfEdgeContainer, facesNearEdge; + TopExp::MapShapes( edgeContainer, TopAbs_FACE, facesOfEdgeContainer ); + + if ( !facesOfEdgeContainer.IsEmpty() ) + for ( TopExp_Explorer exp(edgeContainer, TopAbs_EDGE); exp.More(); exp.Next() ) + { + const TopoDS_Edge& edge = TopoDS::Edge( exp.Current() ); + facesNearEdge.Clear(); + PShapeIteratorPtr faceIt = SMESH_MesherHelper::GetAncestors( edge, mesh, TopAbs_FACE ); + while ( const TopoDS_Shape* face = faceIt->next() ) + if ( facesOfEdgeContainer.Contains( *face )) + if ( facesNearEdge.Add( *face ) && facesNearEdge.Extent() > 1 ) + break; + if ( facesNearEdge.Extent() == 1 ) + return edge; + } + + return TopoDS_Edge(); +} + + +namespace { // Definition of event listeners SMESH_subMeshEventListener* GetSrcSubMeshListener(); @@ -1733,8 +2079,8 @@ namespace { struct HypModifWaiter: SMESH_subMeshEventListener { - HypModifWaiter():SMESH_subMeshEventListener(0){} // won't be deleted by submesh - + HypModifWaiter():SMESH_subMeshEventListener(false,// won't be deleted by submesh + "StdMeshers_ProjectionUtils::HypModifWaiter") {} void ProcessEvent(const int event, const int eventType, SMESH_subMesh* subMesh, EventListenerData*, const SMESH_Hypothesis*) { @@ -1744,9 +2090,7 @@ namespace { // delete current source listener subMesh->DeleteEventListener( GetSrcSubMeshListener() ); // let algo set a new one - SMESH_Gen* gen = subMesh->GetFather()->GetGen(); - if ( SMESH_Algo* algo = gen->GetAlgo( *subMesh->GetFather(), - subMesh->GetSubShape() )) + if ( SMESH_Algo* algo = subMesh->GetAlgo() ) algo->SetEventListener( subMesh ); } } @@ -1768,7 +2112,8 @@ namespace { //================================================================================ SMESH_subMeshEventListener* GetSrcSubMeshListener() { - static SMESH_subMeshEventListener srcListener(0); // won't be deleted by submesh + static SMESH_subMeshEventListener srcListener(false, // won't be deleted by submesh + "StdMeshers_ProjectionUtils::SrcSubMeshListener"); return &srcListener; } } @@ -1787,7 +2132,7 @@ void StdMeshers_ProjectionUtils::SetEventListener(SMESH_subMesh* subMesh, SMESH_Mesh* srcMesh) { // Set listener that resets an event listener on source submesh when - // "ProjectionSource*D" hypothesis is modified + // "ProjectionSource*D" hypothesis is modified since source shape can be changed subMesh->SetEventListener( GetHypModifWaiter(),0,subMesh); // Set an event listener to submesh of the source shape @@ -1802,18 +2147,21 @@ void StdMeshers_ProjectionUtils::SetEventListener(SMESH_subMesh* subMesh, if ( srcShapeSM->GetSubMeshDS() && srcShapeSM->GetSubMeshDS()->IsComplexSubmesh() ) { // source shape is a group - TopExp_Explorer it(srcShapeSM->GetSubShape(), // explore the group into subshapes... + TopExp_Explorer it(srcShapeSM->GetSubShape(), // explore the group into sub-shapes... subMesh->GetSubShape().ShapeType()); // ...of target shape type for (; it.More(); it.Next()) { SMESH_subMesh* srcSM = srcMesh->GetSubMesh( it.Current() ); - SMESH_subMeshEventListenerData* data = - srcSM->GetEventListenerData(GetSrcSubMeshListener()); - if ( data ) - data->mySubMeshes.push_back( subMesh ); - else - data = SMESH_subMeshEventListenerData::MakeData( subMesh ); - subMesh->SetEventListener ( GetSrcSubMeshListener(), data, srcSM ); + if ( srcSM != subMesh ) + { + SMESH_subMeshEventListenerData* data = + srcSM->GetEventListenerData(GetSrcSubMeshListener()); + if ( data ) + data->mySubMeshes.push_back( subMesh ); + else + data = SMESH_subMeshEventListenerData::MakeData( subMesh ); + subMesh->SetEventListener ( GetSrcSubMeshListener(), data, srcSM ); + } } } else diff --git a/src/StdMeshers/StdMeshers_ProjectionUtils.hxx b/src/StdMeshers/StdMeshers_ProjectionUtils.hxx index 6ca881c88..685536a4d 100644 --- a/src/StdMeshers/StdMeshers_ProjectionUtils.hxx +++ b/src/StdMeshers/StdMeshers_ProjectionUtils.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionUtils.hxx // Created : Thu Oct 26 15:37:24 2006 @@ -44,6 +45,29 @@ class SMESH_Hypothesis; class SMESH_subMesh; class TopTools_IndexedDataMapOfShapeListOfShape; +/*! + * \brief Struct used instead of a sole TopTools_DataMapOfShapeShape to avoid + * problems with bidirectional bindings + */ +struct StdMeshers_ShapeShapeBiDirectionMap +{ + TopTools_DataMapOfShapeShape _map1to2, _map2to1; + + // convension: s1 - target, s2 - source + bool Bind( const TopoDS_Shape& s1, const TopoDS_Shape& s2 ) + { _map1to2.Bind( s1, s2 ); return _map2to1.Bind( s2, s1 ); } + bool IsBound( const TopoDS_Shape& s, const bool isShape2=false ) const + { return (isShape2 ? _map2to1 : _map1to2).IsBound( s ); } + bool IsEmpty() const { return _map1to2.IsEmpty(); } + int Extent() const { return _map1to2.Extent(); } + void Clear() { _map1to2.Clear(); _map2to1.Clear(); } + const TopoDS_Shape& operator()( const TopoDS_Shape& s, const bool isShape2=false ) const + { // if we get a Standard_NoSuchObject here, it means that the calling code + // passes incorrect isShape2 + return (isShape2 ? _map2to1 : _map1to2)( s ); + } +}; + /*! * \brief Class encapsulating methods common to Projection algorithms */ @@ -51,12 +75,12 @@ class StdMeshers_ProjectionUtils { public: - typedef TopTools_DataMapOfShapeShape TShapeShapeMap; + typedef StdMeshers_ShapeShapeBiDirectionMap TShapeShapeMap; typedef TopTools_IndexedDataMapOfShapeListOfShape TAncestorMap; typedef std::map TNodeNodeMap; /*! - * \brief Looks for association of all subshapes of two shapes + * \brief Looks for association of all sub-shapes of two shapes * \param theShape1 - shape 1 * \param theMesh1 - mesh built on shape 1 * \param theShape2 - shape 2 @@ -81,10 +105,10 @@ class StdMeshers_ProjectionUtils * \param edges2 - out list of edges of face 2 * \retval int - nb of edges in an outer wire in a success case, else zero */ - static int FindFaceAssociation(const TopoDS_Face& face1, - TopoDS_Vertex VV1[2], - const TopoDS_Face& face2, - TopoDS_Vertex VV2[2], + static int FindFaceAssociation(const TopoDS_Face& face1, + TopoDS_Vertex VV1[2], + const TopoDS_Face& face2, + TopoDS_Vertex VV2[2], std::list< TopoDS_Edge > & edges1, std::list< TopoDS_Edge > & edges2); @@ -95,25 +119,19 @@ class StdMeshers_ProjectionUtils * \param theTargetShape - the shape theHyp assigned to */ static void InitVertexAssociation( const SMESH_Hypothesis* theHyp, - TShapeShapeMap & theAssociationMap, - const TopoDS_Shape& theTargetShape); + TShapeShapeMap & theAssociationMap); /*! * \brief Inserts association theShape1 <-> theShape2 to TShapeShapeMap - * \param theShape1 - shape 1 - * \param theShape2 - shape 2 + * \param theShape1 - target shape + * \param theShape2 - source shape * \param theAssociationMap - association map * \param theBidirectional - if false, inserts theShape1 -> theShape2 association * \retval bool - true if there was no association for these shapes before */ - static bool InsertAssociation( const TopoDS_Shape& theShape1, - const TopoDS_Shape& theShape2, - TShapeShapeMap & theAssociationMap, - const bool theBidirectional=true); - - static bool IsSubShape( const TopoDS_Shape& shape, SMESH_Mesh* aMesh ); - - static bool IsSubShape( const TopoDS_Shape& shape, const TopoDS_Shape& mainShape ); + static bool InsertAssociation( const TopoDS_Shape& theShape1, // target + const TopoDS_Shape& theShape2, // source + TShapeShapeMap & theAssociationMap); /*! * \brief Finds an edge by its vertices in a main shape of the mesh @@ -151,7 +169,7 @@ class StdMeshers_ProjectionUtils * \param mesh1 - mesh containing elements on the first face * \param face2 - the second face * \param mesh2 - mesh containing elements on the second face - * \param assocMap - map associating subshapes of the faces + * \param assocMap - map associating sub-shapes of the faces * \param nodeIn2OutMap - map containing found matching nodes * \retval bool - is a success */ @@ -162,17 +180,10 @@ class StdMeshers_ProjectionUtils const TShapeShapeMap & assocMap, TNodeNodeMap & nodeIn2OutMap); /*! - * \brief Check if the first and last vertices of an edge are the same - * \param anEdge - the edge to check - * \retval bool - true if same - */ - static bool IsClosedEdge( const TopoDS_Edge& anEdge ); - - /*! - * \brief Return any subshape of a face belonging to the outer wire + * \brief Return any sub-shape of a face belonging to the outer wire * \param face - the face - * \param type - type of subshape to return - * \retval TopoDS_Shape - the found subshape + * \param type - type of sub-shape to return + * \retval TopoDS_Shape - the found sub-shape */ static TopoDS_Shape OuterShape( const TopoDS_Face& face, TopAbs_ShapeEnum type); @@ -186,9 +197,9 @@ class StdMeshers_ProjectionUtils static bool MakeComputed(SMESH_subMesh * sm, const int iterationNb = 0); /*! - * \brief Count nb of subshapes + * \brief Count nb of sub-shapes * \param shape - the shape - * \param type - the type of subshapes to count + * \param type - the type of sub-shapes to count * \param ignoreSame - if true, use map not to count same shapes, esle use explorer * \retval int - the calculated number */ @@ -206,6 +217,11 @@ class StdMeshers_ProjectionUtils TopoDS_Shape srcShape, SMESH_Mesh* srcMesh); + /*! + * \brief Return a boundary EDGE of edgeContainer + */ + static TopoDS_Edge GetBoundaryEdge(const TopoDS_Shape& edgeContainer, + const SMESH_Mesh& mesh); }; #endif diff --git a/src/StdMeshers/StdMeshers_Projection_1D.cxx b/src/StdMeshers/StdMeshers_Projection_1D.cxx index 4fd7c5d8d..778b57c0e 100644 --- a/src/StdMeshers/StdMeshers_Projection_1D.cxx +++ b/src/StdMeshers/StdMeshers_Projection_1D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_1D.cxx // Module : SMESH @@ -67,7 +68,7 @@ StdMeshers_Projection_1D::StdMeshers_Projection_1D(int hypId, int studyId, SMESH :SMESH_1D_Algo(hypId, studyId, gen) { _name = "Projection_1D"; - _shapeType = (1 << TopAbs_EDGE); // 1 bit per shape type + _shapeType = (1 << TopAbs_EDGE); // 1 bit per shape type _compatibleHypothesis.push_back("ProjectionSource1D"); _sourceHypo = 0; @@ -128,25 +129,25 @@ bool StdMeshers_Projection_1D::CheckHypothesis(SMESH_Mesh& if ( _sourceHypo->HasVertexAssociation() ) { // source and target vertices - if ( !TAssocTool::IsSubShape( _sourceHypo->GetSourceVertex(), srcMesh ) || - !TAssocTool::IsSubShape( _sourceHypo->GetTargetVertex(), tgtMesh ) || - !TAssocTool::IsSubShape( _sourceHypo->GetSourceVertex(), - _sourceHypo->GetSourceEdge() )) + if ( !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceVertex(), srcMesh ) || + !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetTargetVertex(), tgtMesh ) || + !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceVertex(), + _sourceHypo->GetSourceEdge() )) { aStatus = HYP_BAD_PARAMETER; - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSourceVertex(), srcMesh ))); - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetTargetVertex(), tgtMesh ))); - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSourceVertex(), - _sourceHypo->GetSourceEdge() ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceVertex(), srcMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetTargetVertex(), tgtMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceVertex(), + _sourceHypo->GetSourceEdge() ))); } // PAL16202 - else + else { - bool isSub = TAssocTool::IsSubShape( _sourceHypo->GetTargetVertex(), aShape ); + bool isSub = SMESH_MesherHelper::IsSubShape( _sourceHypo->GetTargetVertex(), aShape ); if ( !_sourceHypo->IsCompoundSource() ) { if ( !isSub ) { aStatus = HYP_BAD_PARAMETER; - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetTargetVertex(), aShape))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetTargetVertex(), aShape))); } } else if ( isSub ) { @@ -159,7 +160,7 @@ bool StdMeshers_Projection_1D::CheckHypothesis(SMESH_Mesh& { const TopoDS_Shape& ancestor = ancestIt.Value(); if ( ancestor.ShapeType() == TopAbs_EDGE && - TAssocTool::IsSubShape( ancestor, _sourceHypo->GetSourceEdge() )) + SMESH_MesherHelper::IsSubShape( ancestor, _sourceHypo->GetSourceEdge() )) { if ( sharingEdge.IsNull() || ancestor.IsSame( sharingEdge )) sharingEdge = ancestor; @@ -175,11 +176,11 @@ bool StdMeshers_Projection_1D::CheckHypothesis(SMESH_Mesh& } } // check source edge - if ( !TAssocTool::IsSubShape( _sourceHypo->GetSourceEdge(), srcMesh ) || + if ( !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceEdge(), srcMesh ) || ( srcMesh == tgtMesh && aShape == _sourceHypo->GetSourceEdge() )) { aStatus = HYP_BAD_PARAMETER; - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSourceEdge(), srcMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceEdge(), srcMesh ))); SCRUTE((srcMesh == tgtMesh)); SCRUTE(( aShape == _sourceHypo->GetSourceEdge() )); } @@ -209,14 +210,14 @@ bool StdMeshers_Projection_1D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& SMESHDS_Mesh * meshDS = theMesh.GetMeshDS(); // --------------------------- - // Make subshapes association + // Make sub-shapes association // --------------------------- TopoDS_Edge srcEdge, tgtEdge = TopoDS::Edge( theShape.Oriented(TopAbs_FORWARD)); TopoDS_Shape srcShape = _sourceHypo->GetSourceEdge().Oriented(TopAbs_FORWARD); TAssocTool::TShapeShapeMap shape2ShapeMap; - TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap, tgtEdge ); + TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap ); if ( !TAssocTool::FindSubShapeAssociation( tgtEdge, tgtMesh, srcShape, srcMesh, shape2ShapeMap) || !shape2ShapeMap.IsBound( tgtEdge )) @@ -372,6 +373,97 @@ bool StdMeshers_Projection_1D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& return true; } + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_Projection_1D::Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& aResMap) +{ + if ( !_sourceHypo ) + return false; + + SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh(); + SMESH_Mesh * tgtMesh = & theMesh; + if ( !srcMesh ) + srcMesh = tgtMesh; + + //SMESHDS_Mesh * meshDS = theMesh.GetMeshDS(); + + // --------------------------- + // Make sub-shapes association + // --------------------------- + + TopoDS_Edge srcEdge, tgtEdge = TopoDS::Edge( theShape.Oriented(TopAbs_FORWARD)); + TopoDS_Shape srcShape = _sourceHypo->GetSourceEdge().Oriented(TopAbs_FORWARD); + + TAssocTool::TShapeShapeMap shape2ShapeMap; + TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap ); + if ( !TAssocTool::FindSubShapeAssociation( tgtEdge, tgtMesh, srcShape, srcMesh, + shape2ShapeMap) || + !shape2ShapeMap.IsBound( tgtEdge )) + return error("Vertices association failed" ); + + srcEdge = TopoDS::Edge( shape2ShapeMap( tgtEdge ).Oriented(TopAbs_FORWARD)); +// cout << " srcEdge #" << srcMesh->GetMeshDS()->ShapeToIndex( srcEdge ) +// << " tgtEdge #" << tgtMesh->GetMeshDS()->ShapeToIndex( tgtEdge ) << endl; + + TopoDS_Vertex tgtV[2], srcV[2]; + TopExp::Vertices( tgtEdge, tgtV[0], tgtV[1] ); + TopExp::Vertices( srcEdge, srcV[0], srcV[1] ); + + // ---------------------------------------------- + // Assure that mesh on a source edge is computed + // ---------------------------------------------- + + SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( srcEdge ); + //SMESH_subMesh* tgtSubMesh = tgtMesh->GetSubMesh( tgtEdge ); + + if ( tgtMesh == srcMesh ) { + if ( !TAssocTool::MakeComputed( srcSubMesh )) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed"); + } + else { + if ( !srcSubMesh->IsMeshComputed() ) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed"); + } + // ----------------------------------------------- + // Find out nodes distribution on the source edge + // ----------------------------------------------- + + //double srcLength = EdgeLength( srcEdge ); + //double tgtLength = EdgeLength( tgtEdge ); + + vector< double > params; // sorted parameters of nodes on the source edge + if ( !SMESH_Algo::GetNodeParamOnEdge( srcMesh->GetMeshDS(), srcEdge, params )) + return error(COMPERR_BAD_INPUT_MESH,"Bad node parameters on the source edge"); + + int nbNodes = params.size(); + + std::vector aVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetSubMeshDS()->GetElements(); + if ( elemIt->more() ) + quadratic = elemIt->next()->IsQuadratic(); + if(quadratic) + aVec[SMDSEntity_Quad_Edge] = (nbNodes-1)/2; + else + aVec[SMDSEntity_Edge] = nbNodes - 1; + + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(std::make_pair(sm,aVec)); + + return true; +} + + //============================================================================= /*! * \brief Sets a default event listener to submesh of the source edge diff --git a/src/StdMeshers/StdMeshers_Projection_1D.hxx b/src/StdMeshers/StdMeshers_Projection_1D.hxx index 52f76e170..5ef640170 100644 --- a/src/StdMeshers/StdMeshers_Projection_1D.hxx +++ b/src/StdMeshers/StdMeshers_Projection_1D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_1D.hxx // Module : SMESH @@ -46,6 +47,9 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! * \brief Sets a default event listener to submesh of the source edge * \param whenSetToSubMesh - submesh where algo is set diff --git a/src/StdMeshers/StdMeshers_Projection_1D2D.cxx b/src/StdMeshers/StdMeshers_Projection_1D2D.cxx new file mode 100644 index 000000000..efe6bf034 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Projection_1D2D.cxx @@ -0,0 +1,264 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_Projection_1D2D.cxx +// Module : SMESH +// Author : Edward AGAPOV (eap) +// +#include "StdMeshers_Projection_1D2D.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMeshEventListener.hxx" +#include "StdMeshers_FaceSide.hxx" +#include "StdMeshers_ProjectionSource2D.hxx" +#include "StdMeshers_ProjectionUtils.hxx" + +#include +#include + +using namespace std; + +namespace +{ + // -------------------------------------------------------------------------------- + /*! + * \brief an event listener updating submehses of EDGEs according to + * events on the target FACE submesh + */ + struct EventProparatorToEdges : public SMESH_subMeshEventListener + { + EventProparatorToEdges(): SMESH_subMeshEventListener(/*isDeletable=*/false, + "Projection_1D2D::EventProparatorToEdges") + {} + static EventProparatorToEdges* Instance() { static EventProparatorToEdges E; return &E; } + + static void Set(SMESH_subMesh* faceSubMesh) + { + SMESH_subMeshEventListenerData* edgeSubMeshes = + new SMESH_subMeshEventListenerData(/*isDeletable=*/true); + SMESH_Mesh* mesh = faceSubMesh->GetFather(); + TopExp_Explorer eExp( faceSubMesh->GetSubShape(), TopAbs_EDGE ); + for ( ; eExp.More(); eExp.Next() ) + edgeSubMeshes->mySubMeshes.push_back( mesh->GetSubMesh( eExp.Current() )); + + // set a listener + faceSubMesh->SetEventListener( Instance(), edgeSubMeshes, faceSubMesh ); + } + }; + // -------------------------------------------------------------------------------- + /*! + * \brief Structure used to temporary remove EventProparatorToEdges from faceSubMesh + * in order to prevent propagation of CLEAN event from FACE to EDGEs during + * StdMeshers_Projection_1D2D::Compute(). The CLEAN event is emmited by Pattern mapper + * and causes removal of faces generated on adjacent FACEs. + */ + struct UnsetterOfEventProparatorToEdges + { + SMESH_subMesh* _faceSubMesh; + UnsetterOfEventProparatorToEdges( SMESH_subMesh* faceSubMesh ):_faceSubMesh(faceSubMesh) + { + faceSubMesh->DeleteEventListener( EventProparatorToEdges::Instance() ); + } + ~UnsetterOfEventProparatorToEdges() + { + EventProparatorToEdges::Set(_faceSubMesh); + } + }; +} + +//======================================================================= +//function : StdMeshers_Projection_1D2D +//purpose : +//======================================================================= + +StdMeshers_Projection_1D2D::StdMeshers_Projection_1D2D(int hypId, int studyId, SMESH_Gen* gen) + :StdMeshers_Projection_2D(hypId, studyId, gen) +{ + _name = "Projection_1D2D"; + _requireDiscreteBoundary = false; + _supportSubmeshes = true; +} + +//======================================================================= +//function : Compute +//purpose : +//======================================================================= + +bool StdMeshers_Projection_1D2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape) +{ + UnsetterOfEventProparatorToEdges eventBarrier( theMesh.GetSubMesh( theShape )); + + if ( !StdMeshers_Projection_2D::Compute(theMesh, theShape)) + return false; + + SMESHDS_Mesh * meshDS = theMesh.GetMeshDS(); + + SMESHDS_SubMesh * faceSubMesh = meshDS->MeshElements( theShape ); + if ( !faceSubMesh || faceSubMesh->NbElements() == 0 ) return false; + _quadraticMesh = faceSubMesh->GetElements()->next()->IsQuadratic(); + + SMESH_MesherHelper helper( theMesh ); + helper.SetSubShape( theShape ); + + TopoDS_Face F = TopoDS::Face( theShape ); + TError err; + TSideVector wires = StdMeshers_FaceSide::GetFaceWires( F, theMesh, + /*ignoreMediumNodes=*/false, err); + if ( err && !err->IsOK() ) + return error( err ); + + for ( size_t iWire = 0; iWire < wires.size(); ++iWire ) + { + vector nodes = wires[ iWire ]->GetOrderedNodes(); + if ( nodes.empty() ) + return error("Wrong nodes on a wire"); + + // check that all nodes are shared by faces generated on F + for ( size_t i = 0; i < nodes.size(); ++i ) + { + SMDS_ElemIteratorPtr fIt = nodes[i]->GetInverseElementIterator(SMDSAbs_Face); + bool faceFound = false; + while ( !faceFound && fIt->more() ) + faceFound = ( helper.GetSubShapeID() == fIt->next()->getshapeId() ); + if ( !faceFound ) + return error("The existing 1D mesh mismatches the generated 2D mesh"); + } + + const bool checkExisting = ( wires[ iWire ]->NbSegments() || helper.HasSeam() ); + + if ( _quadraticMesh ) + { + for ( size_t i = 2; i < nodes.size(); i += 2 ) + { + if ( checkExisting && meshDS->FindEdge( nodes[i-2], nodes[i], nodes[i-1])) + continue; + SMDS_MeshElement* e = meshDS->AddEdge( nodes[i-2], nodes[i], nodes[i-1] ); + meshDS->SetMeshElementOnShape( e, nodes[i-1]->getshapeId() ); + } + } + else + { + int edgeID = meshDS->ShapeToIndex( wires[ iWire ]->Edge(0) ); + for ( size_t i = 1; i < nodes.size(); ++i ) + { + if ( checkExisting && meshDS->FindEdge( nodes[i-1], nodes[i])) + continue; + SMDS_MeshElement* e = meshDS->AddEdge( nodes[i-1], nodes[i] ); + if ( nodes[i-1]->getshapeId() != edgeID && + nodes[i ]->getshapeId() != edgeID ) + { + edgeID = helper.GetMediumPos( nodes[i-1], nodes[i] ).first; + if ( edgeID < 1 ) edgeID = helper.GetSubShapeID(); + } + meshDS->SetMeshElementOnShape( e, edgeID ); + } + } + } + + return true; +} + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_Projection_1D2D::Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& aResMap) +{ + if ( !StdMeshers_Projection_2D::Evaluate(theMesh,theShape,aResMap)) + return false; + + TopoDS_Shape srcFace = _sourceHypo->GetSourceFace(); + SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh(); + if ( !srcMesh ) srcMesh = & theMesh; + SMESH_subMesh* srcFaceSM = srcMesh->GetSubMesh( srcFace ); + + typedef StdMeshers_ProjectionUtils SPU; + SPU::TShapeShapeMap shape2ShapeMap; + SPU::InitVertexAssociation( _sourceHypo, shape2ShapeMap ); + if ( !SPU::FindSubShapeAssociation( theShape, &theMesh, srcFace, srcMesh, shape2ShapeMap)) + return error(COMPERR_BAD_SHAPE,"Topology of source and target faces seems different" ); + + MapShapeNbElems srcResMap; + if ( !srcFaceSM->IsMeshComputed() ) + _gen->Evaluate( *srcMesh, srcFace, srcResMap); + + SMESH_subMeshIteratorPtr smIt = srcFaceSM->getDependsOnIterator(/*includeSelf=*/false, + /*complexShapeFirst=*/true); + while ( smIt->more() ) + { + SMESH_subMesh* srcSM = smIt->next(); + TopAbs_ShapeEnum shapeType = srcSM->GetSubShape().ShapeType(); + if ( shapeType == TopAbs_EDGE ) + { + std::vector aVec; + SMESHDS_SubMesh* srcSubMeshDS = srcSM->GetSubMeshDS(); + if ( srcSubMeshDS && srcSubMeshDS->NbElements() ) + { + aVec.resize(SMDSEntity_Last, 0); + SMDS_ElemIteratorPtr eIt = srcSubMeshDS->GetElements(); + _quadraticMesh = ( eIt->more() && eIt->next()->IsQuadratic() ); + + aVec[SMDSEntity_Node] = srcSubMeshDS->NbNodes(); + aVec[_quadraticMesh ? SMDSEntity_Quad_Edge : SMDSEntity_Edge] = srcSubMeshDS->NbElements(); + } + else + { + if ( srcResMap.empty() ) + if ( !_gen->Evaluate( *srcMesh, srcSM->GetSubShape(), srcResMap )) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh not evaluatable"); + aVec = srcResMap[ srcSM ]; + if ( aVec.empty() ) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh is wrongly evaluated"); + } + TopoDS_Shape tgtEdge = shape2ShapeMap( srcSM->GetSubShape(), /*isSrc=*/true ); + SMESH_subMesh* tgtSM = theMesh.GetSubMesh( tgtEdge ); + aResMap.insert(std::make_pair(tgtSM,aVec)); + } + if ( shapeType == TopAbs_VERTEX ) break; + } + + return true; +} + +//======================================================================= +//function : SetEventListener +//purpose : Sets a default event listener to submesh of the source face. +// faceSubMesh - submesh where algo is set +// After being set, event listener is notified on each event of a submesh. +// This method is called when a submesh gets HYP_OK algo_state. +// Arranges that CLEAN event is translated from source submesh to +// the faceSubMesh submesh. +//======================================================================= + +void StdMeshers_Projection_1D2D::SetEventListener(SMESH_subMesh* faceSubMesh) +{ + // set a listener of events on a source submesh + StdMeshers_Projection_2D::SetEventListener(faceSubMesh); + + // set a listener to the target FACE submesh in order to update submehses + // of EDGEs according to events on the target FACE submesh + EventProparatorToEdges::Set( faceSubMesh ); +} + diff --git a/src/StdMeshers/StdMeshers_Projection_1D2D.hxx b/src/StdMeshers/StdMeshers_Projection_1D2D.hxx new file mode 100644 index 000000000..9e7715c82 --- /dev/null +++ b/src/StdMeshers/StdMeshers_Projection_1D2D.hxx @@ -0,0 +1,52 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_Projection_1D2D.hxx +// Module : SMESH +// +#ifndef _SMESH_Projection_1D2D_HXX_ +#define _SMESH_Projection_1D2D_HXX_ + +#include "StdMeshers_Projection_2D.hxx" + +class STDMESHERS_EXPORT StdMeshers_Projection_1D2D: public StdMeshers_Projection_2D +{ +public: + StdMeshers_Projection_1D2D(int hypId, int studyId, SMESH_Gen* gen); + + virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! + * \brief Sets a default event listener to submesh of the source face + * \param whenSetToSubMesh - submesh where algo is set + * + * After being set, event listener is notified on each event of a submesh. + * This method is called when a submesh gets HYP_OK algo_state. + * Arranges that CLEAN event is translated from source submesh to + * the whenSetToSubMesh submesh. + */ + virtual void SetEventListener(SMESH_subMesh* whenSetToSubMesh); +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_Projection_2D.cxx b/src/StdMeshers/StdMeshers_Projection_2D.cxx index ed6027268..b4734fa0c 100644 --- a/src/StdMeshers/StdMeshers_Projection_2D.cxx +++ b/src/StdMeshers/StdMeshers_Projection_2D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_2D.cxx // Module : SMESH @@ -29,18 +30,20 @@ #include "StdMeshers_ProjectionSource2D.hxx" #include "StdMeshers_ProjectionUtils.hxx" +#include "StdMeshers_FaceSide.hxx" +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FacePosition.hxx" #include "SMESHDS_Hypothesis.hxx" #include "SMESHDS_SubMesh.hxx" #include "SMESH_Block.hxx" +#include "SMESH_Comment.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_Pattern.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_subMeshEventListener.hxx" -#include "SMESH_Comment.hxx" -#include "SMDS_EdgePosition.hxx" #include "utilities.h" @@ -50,6 +53,8 @@ #include #include #include +#include +#include using namespace std; @@ -67,7 +72,7 @@ StdMeshers_Projection_2D::StdMeshers_Projection_2D(int hypId, int studyId, SMESH :SMESH_2D_Algo(hypId, studyId, gen) { _name = "Projection_2D"; - _shapeType = (1 << TopAbs_FACE); // 1 bit per shape type + _shapeType = (1 << TopAbs_FACE); // 1 bit per shape type _compatibleHypothesis.push_back("ProjectionSource2D"); _sourceHypo = 0; @@ -130,40 +135,40 @@ bool StdMeshers_Projection_2D::CheckHypothesis(SMESH_Mesh& TopoDS_Shape edge = TAssocTool::GetEdgeByVertices ( srcMesh, _sourceHypo->GetSourceVertex(1), _sourceHypo->GetSourceVertex(2) ); if ( edge.IsNull() || - !TAssocTool::IsSubShape( edge, srcMesh ) || - !TAssocTool::IsSubShape( edge, _sourceHypo->GetSourceFace() )) + !SMESH_MesherHelper::IsSubShape( edge, srcMesh ) || + !SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSourceFace() )) { theStatus = HYP_BAD_PARAMETER; SCRUTE((edge.IsNull())); - SCRUTE((TAssocTool::IsSubShape( edge, srcMesh ))); - SCRUTE((TAssocTool::IsSubShape( edge, _sourceHypo->GetSourceFace() ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, srcMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSourceFace() ))); } else { // target vertices edge = TAssocTool::GetEdgeByVertices ( tgtMesh, _sourceHypo->GetTargetVertex(1), _sourceHypo->GetTargetVertex(2) ); - if ( edge.IsNull() || !TAssocTool::IsSubShape( edge, tgtMesh )) + if ( edge.IsNull() || !SMESH_MesherHelper::IsSubShape( edge, tgtMesh )) { theStatus = HYP_BAD_PARAMETER; SCRUTE((edge.IsNull())); - SCRUTE((TAssocTool::IsSubShape( edge, tgtMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, tgtMesh ))); } // PAL16203 else if ( !_sourceHypo->IsCompoundSource() && - !TAssocTool::IsSubShape( edge, theShape )) + !SMESH_MesherHelper::IsSubShape( edge, theShape )) { theStatus = HYP_BAD_PARAMETER; - SCRUTE((TAssocTool::IsSubShape( edge, theShape ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, theShape ))); } } } // check a source face - if ( !TAssocTool::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ) || + if ( !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ) || ( srcMesh == tgtMesh && theShape == _sourceHypo->GetSourceFace() )) { theStatus = HYP_BAD_PARAMETER; - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSourceFace(), srcMesh ))); SCRUTE((srcMesh == tgtMesh)); SCRUTE(( theShape == _sourceHypo->GetSourceFace() )); } @@ -177,22 +182,29 @@ bool StdMeshers_Projection_2D::CheckHypothesis(SMESH_Mesh& namespace { - //================================================================================ /*! * \brief define if a node is new or old - * \param node - node to check - * \retval bool - true if the node existed before Compute() is called + * \param node - node to check + * \retval bool - true if the node existed before Compute() is called */ //================================================================================ - bool isOldNode( const SMDS_MeshNode* node ) + bool isOldNode( const SMDS_MeshNode* node/*, const bool is1DComputed*/ ) { // old nodes are shared by edges and new ones are shared // only by faces created by mapper - SMDS_ElemIteratorPtr invEdge = node->GetInverseElementIterator(SMDSAbs_Edge); - bool isOld = invEdge->more(); - return isOld; + //if ( is1DComputed ) + { + bool isOld = node->NbInverseElements(SMDSAbs_Edge) > 0; + return isOld; + } + // else + // { + // SMDS_ElemIteratorPtr invFace = node->GetInverseElementIterator(SMDSAbs_Face); + // bool isNew = invFace->more(); + // return !isNew; + // } } //================================================================================ @@ -211,7 +223,7 @@ namespace { void Release() { sm = 0; } // mesh will not be removed static void Clean( SMESH_subMesh* sm, bool withSub=true ) { - if ( !sm ) return; + if ( !sm || !sm->GetSubMeshDS() ) return; // PAL16567, 18920. Remove face nodes as well // switch ( sm->GetSubShape().ShapeType() ) { // case TopAbs_VERTEX: @@ -339,7 +351,7 @@ namespace { RETURN_BAD_RESULT("Bad node position type: node " << node->GetID() << " pos type " << node->GetPosition()->GetTypeOfPosition()); const SMDS_EdgePosition* pos = - static_cast(node->GetPosition().get()); + static_cast(node->GetPosition()); u2nodes.insert( make_pair( pos->GetUParameter(), node )); seamNodes.insert( node ); } @@ -354,8 +366,406 @@ namespace { } // bool getBoundaryNodes() + //================================================================================ + /*! + * \brief Preform projection in case if tgtFace.IsPartner( srcFace ) and in case + * if projection by transformation is possible + */ + //================================================================================ + + bool projectPartner(const TopoDS_Face& tgtFace, + const TopoDS_Face& srcFace, + SMESH_Mesh * tgtMesh, + SMESH_Mesh * srcMesh, + const TAssocTool::TShapeShapeMap& shape2ShapeMap) + { + MESSAGE("projectPartner"); + const double tol = 1.e-7*srcMesh->GetMeshDS()->getMaxDim(); + + gp_Trsf trsf; // transformation to get location of target nodes from source ones + if ( tgtFace.IsPartner( srcFace )) + { + gp_Trsf srcTrsf = srcFace.Location(); + gp_Trsf tgtTrsf = tgtFace.Location(); + trsf = srcTrsf.Inverted() * tgtTrsf; + } + else + { + // Try to find the transformation + + // make any local coord systems of src and tgt faces + vector srcPP, tgtPP; // 3 points on face boundaries to make axes of CS + SMESH_subMesh * srcSM = srcMesh->GetSubMesh( srcFace ); + SMESH_subMeshIteratorPtr smIt = srcSM->getDependsOnIterator(/*includeSelf=*/false,false); + srcSM = smIt->next(); // sm of a vertex + while ( smIt->more() && srcPP.size() < 3 ) + { + srcSM = smIt->next(); + SMESHDS_SubMesh* srcSmds = srcSM->GetSubMeshDS(); + if ( !srcSmds ) continue; + SMDS_NodeIteratorPtr nIt = srcSmds->GetNodes(); + while ( nIt->more() ) + { + SMESH_TNodeXYZ p ( nIt->next()); + bool pOK = false; + switch ( srcPP.size() ) + { + case 0: pOK = true; break; + + case 1: pOK = ( srcPP[0].SquareDistance( p ) > 10*tol ); break; + + case 2: + { + gp_Vec p0p1( srcPP[0], srcPP[1] ), p0p( srcPP[0], p ); + // pOK = !p0p1.IsParallel( p0p, tol ); + pOK = !p0p1.IsParallel( p0p, 3.14/20 ); // angle min 18 degrees + break; + } + } + if ( !pOK ) + continue; + + // find corresponding point on target shape + pOK = false; + gp_Pnt tgtP; + const TopoDS_Shape& tgtShape = shape2ShapeMap( srcSM->GetSubShape(), /*isSrc=*/true ); + if ( tgtShape.ShapeType() == TopAbs_VERTEX ) + { + tgtP = BRep_Tool::Pnt( TopoDS::Vertex( tgtShape )); + pOK = true; + //cout << "V - nS " << p._node->GetID() << " - nT " << SMESH_Algo::VertexNode(TopoDS::Vertex( tgtShape),tgtMesh->GetMeshDS())->GetID() << endl; + } + else if ( tgtPP.size() > 0 ) + { + if ( SMESHDS_SubMesh* tgtSmds = tgtMesh->GetMeshDS()->MeshElements( tgtShape )) + { + double srcDist = srcPP[0].Distance( p ); + double eTol = BRep_Tool::Tolerance( TopoDS::Edge( tgtShape )); + if (eTol < tol) eTol = tol; + SMDS_NodeIteratorPtr nItT = tgtSmds->GetNodes(); + while ( nItT->more() && !pOK ) + { + const SMDS_MeshNode* n = nItT->next(); + tgtP = SMESH_TNodeXYZ( n ); + pOK = ( fabs( srcDist - tgtPP[0].Distance( tgtP )) < 2*eTol ); + //cout << "E - nS " << p._node->GetID() << " - nT " << n->GetID()<< " OK - " << pOK<< " " << fabs( srcDist - tgtPP[0].Distance( tgtP ))<< " tol " << eTol<< endl; + } + } + } + if ( !pOK ) + continue; + + srcPP.push_back( p ); + tgtPP.push_back( tgtP ); + } + } + if ( srcPP.size() != 3 ) + return false; + + // make transformation + gp_Trsf fromTgtCS, toSrcCS; // from/to global CS + gp_Ax2 srcCS( srcPP[0], gp_Vec( srcPP[0], srcPP[1] ), gp_Vec( srcPP[0], srcPP[2])); + gp_Ax2 tgtCS( tgtPP[0], gp_Vec( tgtPP[0], tgtPP[1] ), gp_Vec( tgtPP[0], tgtPP[2])); + toSrcCS .SetTransformation( gp_Ax3( srcCS )); + fromTgtCS.SetTransformation( gp_Ax3( tgtCS )); + fromTgtCS.Invert(); + + trsf = fromTgtCS * toSrcCS; + } + + // Fill map of src to tgt nodes with nodes on edges + + map src2tgtNodes; + map::iterator srcN_tgtN; + + for ( TopExp_Explorer srcEdge( srcFace, TopAbs_EDGE); srcEdge.More(); srcEdge.Next() ) + { + const TopoDS_Shape& tgtEdge = shape2ShapeMap( srcEdge.Current(), /*isSrc=*/true ); + + map< double, const SMDS_MeshNode* > srcNodes, tgtNodes; + if ( !SMESH_Algo::GetSortedNodesOnEdge( srcMesh->GetMeshDS(), + TopoDS::Edge( srcEdge.Current() ), + /*ignoreMediumNodes = */true, + srcNodes ) + || + !SMESH_Algo::GetSortedNodesOnEdge( tgtMesh->GetMeshDS(), + TopoDS::Edge( tgtEdge ), + /*ignoreMediumNodes = */true, + tgtNodes ) + || + srcNodes.size() != tgtNodes.size()) + return false; + + if ( !tgtEdge.IsPartner( srcEdge.Current() )) + { + // check that transformation is OK by three nodes + gp_Pnt p0S = SMESH_TNodeXYZ( (srcNodes.begin()) ->second); + gp_Pnt p1S = SMESH_TNodeXYZ( (srcNodes.rbegin()) ->second); + gp_Pnt p2S = SMESH_TNodeXYZ( (++srcNodes.begin())->second); + + gp_Pnt p0T = SMESH_TNodeXYZ( (tgtNodes.begin()) ->second); + gp_Pnt p1T = SMESH_TNodeXYZ( (tgtNodes.rbegin()) ->second); + gp_Pnt p2T = SMESH_TNodeXYZ( (++tgtNodes.begin())->second); + + // transform source points, they must coincide with target ones + if ( p0T.SquareDistance( p0S.Transformed( trsf )) > tol || + p1T.SquareDistance( p1S.Transformed( trsf )) > tol || + p2T.SquareDistance( p2S.Transformed( trsf )) > tol ) + { + //cout << "KO trsf, 3 dist: " + //<< p0T.SquareDistance( p0S.Transformed( trsf ))<< ", " + //<< p1T.SquareDistance( p1S.Transformed( trsf ))<< ", " + //<< p2T.SquareDistance( p2S.Transformed( trsf ))<< ", "<::iterator u_tn = tgtNodes.begin(); + map< double, const SMDS_MeshNode* >::iterator u_sn = srcNodes.begin(); + for ( ; u_tn != tgtNodes.end(); ++u_tn, ++u_sn) + src2tgtNodes.insert( make_pair( u_sn->second, u_tn->second )); + } + + // Make new faces + + // prepare the helper to adding quadratic elements if necessary + SMESH_MesherHelper helper( *tgtMesh ); + helper.SetSubShape( tgtFace ); + helper.IsQuadraticSubMesh( tgtFace ); + helper.SetElementsOnShape( true ); + + SMESH_MesherHelper srcHelper( *srcMesh ); + srcHelper.SetSubShape( srcFace ); + + const SMDS_MeshNode* nullNode = 0; + + // indices of nodes to create properly oriented faces + int tri1 = 1, tri2 = 2, quad1 = 1, quad3 = 3; + if ( trsf.Form() != gp_Identity ) + std::swap( tri1, tri2 ), std::swap( quad1, quad3 ); + + SMESHDS_SubMesh* srcSubDS = srcMesh->GetMeshDS()->MeshElements( srcFace ); + SMDS_ElemIteratorPtr elemIt = srcSubDS->GetElements(); + vector< const SMDS_MeshNode* > tgtNodes; + while ( elemIt->more() ) // loop on all mesh faces on srcFace + { + const SMDS_MeshElement* elem = elemIt->next(); + const int nbN = elem->NbCornerNodes(); + tgtNodes.resize( nbN ); + for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element + { + const SMDS_MeshNode* srcNode = elem->GetNode(i); + srcN_tgtN = src2tgtNodes.insert( make_pair( srcNode, nullNode )).first; + if ( srcN_tgtN->second == nullNode ) + { + // create a new node + gp_Pnt tgtP = gp_Pnt(srcNode->X(),srcNode->Y(),srcNode->Z()).Transformed( trsf ); + SMDS_MeshNode* n = helper.AddNode( tgtP.X(), tgtP.Y(), tgtP.Z() ); + srcN_tgtN->second = n; + + gp_Pnt2d srcUV = srcHelper.GetNodeUV( srcFace, srcNode, + elem->GetNode( helper.WrapIndex(i+1,nbN))); + n->SetPosition( new SMDS_FacePosition( srcUV.X(), srcUV.Y() )); + } + tgtNodes[i] = srcN_tgtN->second; + } + // create a new face + switch ( nbN ) + { + case 3: helper.AddFace(tgtNodes[0], tgtNodes[tri1], tgtNodes[tri2]); break; + case 4: helper.AddFace(tgtNodes[0], tgtNodes[quad1], tgtNodes[2], tgtNodes[quad3]); break; + } + } + return true; + + } // bool projectPartner() + + //================================================================================ + /*! + * \brief Preform projection in case if the faces are similar in 2D space + */ + //================================================================================ + + bool projectBy2DSimilarity(const TopoDS_Face& tgtFace, + const TopoDS_Face& srcFace, + SMESH_Mesh * tgtMesh, + SMESH_Mesh * srcMesh, + const TAssocTool::TShapeShapeMap& shape2ShapeMap, + const bool is1DComputed) + { + // 1) Preparation + + // get ordered src EDGEs + TError err; + TSideVector srcWires = + StdMeshers_FaceSide::GetFaceWires( srcFace, *srcMesh,/*ignoreMediumNodes = */false, err); + if ( err && !err->IsOK() ) + return false; + + // make corresponding sequence of tgt EDGEs + TSideVector tgtWires( srcWires.size() ); + for ( unsigned iW = 0; iW < srcWires.size(); ++iW ) + { + list< TopoDS_Edge > tgtEdges; + StdMeshers_FaceSidePtr srcWire = srcWires[iW]; + TopTools_IndexedMapOfShape edgeMap; // to detect seam edges + for ( int iE = 0; iE < srcWire->NbEdges(); ++iE ) + { + tgtEdges.push_back( TopoDS::Edge( shape2ShapeMap( srcWire->Edge( iE ), /*isSrc=*/true))); + // reverse a seam edge encountered for the second time + const int oldExtent = edgeMap.Extent(); + edgeMap.Add( tgtEdges.back() ); + if ( oldExtent == edgeMap.Extent() ) + tgtEdges.back().Reverse(); + } + tgtWires[ iW ].reset( new StdMeshers_FaceSide( tgtFace, tgtEdges, tgtMesh, + /*theIsForward = */ true, + /*theIgnoreMediumNodes = */false)); + if ( is1DComputed && + srcWires[iW]->GetUVPtStruct().size() != + tgtWires[iW]->GetUVPtStruct().size()) + return false; + } + + // 2) Find transformation + + gp_Trsf2d trsf; + { + // get 2 pairs of corresponding UVs + gp_Pnt2d srcP0 = srcWires[0]->Value2d(0.0); + gp_Pnt2d srcP1 = srcWires[0]->Value2d(0.333); + gp_Pnt2d tgtP0 = tgtWires[0]->Value2d(0.0); + gp_Pnt2d tgtP1 = tgtWires[0]->Value2d(0.333); + + // make transformation + gp_Trsf2d fromTgtCS, toSrcCS; // from/to global CS + gp_Ax2d srcCS( srcP0, gp_Vec2d( srcP0, srcP1 )); + gp_Ax2d tgtCS( tgtP0, gp_Vec2d( tgtP0, tgtP1 )); + toSrcCS .SetTransformation( srcCS ); + fromTgtCS.SetTransformation( tgtCS ); + fromTgtCS.Invert(); + + trsf = fromTgtCS * toSrcCS; + + // check transformation + const double tol = 1e-5 * gp_Vec2d( srcP0, srcP1 ).Magnitude(); + for ( double u = 0.12; u < 1.; u += 0.1 ) + { + gp_Pnt2d srcUV = srcWires[0]->Value2d( u ); + gp_Pnt2d tgtUV = tgtWires[0]->Value2d( u ); + gp_Pnt2d tgtUV2 = srcUV.Transformed( trsf ); + if ( tgtUV.Distance( tgtUV2 ) > tol ) + return false; + } + } + + // 3) Projection + + typedef map TN2NMap; + TN2NMap src2tgtNodes; + TN2NMap::iterator srcN_tgtN; + + // fill src2tgtNodes in with nodes on EDGEs + for ( unsigned iW = 0; iW < srcWires.size(); ++iW ) + if ( is1DComputed ) + { + const vector& srcUVs = srcWires[iW]->GetUVPtStruct(); + const vector& tgtUVs = tgtWires[iW]->GetUVPtStruct(); + for ( unsigned i = 0; i < srcUVs.size(); ++i ) + src2tgtNodes.insert( make_pair( srcUVs[i].node, tgtUVs[i].node )); + } + else + { + for ( int iE = 0; iE < srcWires[iW]->NbEdges(); ++iE ) + { + TopoDS_Vertex srcV = srcWires[iW]->FirstVertex(iE); + TopoDS_Vertex tgtV = tgtWires[iW]->FirstVertex(iE); + const SMDS_MeshNode* srcNode = SMESH_Algo::VertexNode( srcV, srcMesh->GetMeshDS() ); + const SMDS_MeshNode* tgtNode = SMESH_Algo::VertexNode( tgtV, tgtMesh->GetMeshDS() ); + if ( tgtNode && srcNode ) + src2tgtNodes.insert( make_pair( srcNode, tgtNode )); + } + } + + // make elements + + SMESHDS_SubMesh* srcSubDS = srcMesh->GetMeshDS()->MeshElements( srcFace ); + + SMESH_MesherHelper helper( *tgtMesh ); + helper.SetSubShape( tgtFace ); + if ( is1DComputed ) + helper.IsQuadraticSubMesh( tgtFace ); + else + helper.SetIsQuadratic( srcSubDS->GetElements()->next()->IsQuadratic() ); + helper.SetElementsOnShape( true ); + Handle(Geom_Surface) tgtSurface = BRep_Tool::Surface( tgtFace ); + SMESHDS_Mesh* tgtMeshDS = tgtMesh->GetMeshDS(); + + SMESH_MesherHelper srcHelper( *srcMesh ); + srcHelper.SetSubShape( srcFace ); + + const SMDS_MeshNode* nullNode = 0; + + SMDS_ElemIteratorPtr elemIt = srcSubDS->GetElements(); + vector< const SMDS_MeshNode* > tgtNodes; + bool uvOK; + while ( elemIt->more() ) // loop on all mesh faces on srcFace + { + const SMDS_MeshElement* elem = elemIt->next(); + const int nbN = elem->NbCornerNodes(); + tgtNodes.resize( nbN ); + for ( int i = 0; i < nbN; ++i ) // loop on nodes of the source element + { + const SMDS_MeshNode* srcNode = elem->GetNode(i); + srcN_tgtN = src2tgtNodes.insert( make_pair( srcNode, nullNode )).first; + if ( srcN_tgtN->second == nullNode ) + { + // create a new node + gp_Pnt2d srcUV = srcHelper.GetNodeUV( srcFace, srcNode, + elem->GetNode( helper.WrapIndex(i+1,nbN)), &uvOK); + gp_Pnt2d tgtUV = srcUV.Transformed( trsf ); + gp_Pnt tgtP = tgtSurface->Value( tgtUV.X(), tgtUV.Y() ); + SMDS_MeshNode* n = tgtMeshDS->AddNode( tgtP.X(), tgtP.Y(), tgtP.Z() ); + switch ( srcNode->GetPosition()->GetTypeOfPosition() ) + { + case SMDS_TOP_FACE: { + tgtMeshDS->SetNodeOnFace( n, helper.GetSubShapeID(), tgtUV.X(), tgtUV.Y() ); + break; + } + case SMDS_TOP_EDGE: { + TopoDS_Shape srcEdge = srcHelper.GetSubShapeByNode( srcNode, srcHelper.GetMeshDS() ); + TopoDS_Edge tgtEdge = TopoDS::Edge( shape2ShapeMap( srcEdge, /*isSrc=*/true )); + tgtMeshDS->SetNodeOnEdge( n, TopoDS::Edge( tgtEdge )); + double U = srcHelper.GetNodeU( TopoDS::Edge( srcEdge ), srcNode ); + helper.CheckNodeU( tgtEdge, n, U, Precision::PConfusion()); + n->SetPosition(SMDS_PositionPtr(new SMDS_EdgePosition( U ))); + break; + } + case SMDS_TOP_VERTEX: { + TopoDS_Shape srcV = srcHelper.GetSubShapeByNode( srcNode, srcHelper.GetMeshDS() ); + TopoDS_Shape tgtV = shape2ShapeMap( srcV, /*isSrc=*/true ); + tgtMeshDS->SetNodeOnVertex( n, TopoDS::Vertex( tgtV )); + break; + } + } + srcN_tgtN->second = n; + } + tgtNodes[i] = srcN_tgtN->second; + } + // create a new face (with reversed orientation) + switch ( nbN ) + { + case 3: helper.AddFace(tgtNodes[0], tgtNodes[2], tgtNodes[1]); break; + case 4: helper.AddFace(tgtNodes[0], tgtNodes[3], tgtNodes[2], tgtNodes[1]); break; + } + } + return true; + + } // bool projectBy2DSimilarity(...) + } // namespace + //======================================================================= //function : Compute //purpose : @@ -363,6 +773,7 @@ namespace { bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& theShape) { + MESSAGE("Projection_2D Compute"); if ( !_sourceHypo ) return false; @@ -374,14 +785,14 @@ bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& SMESHDS_Mesh * meshDS = theMesh.GetMeshDS(); // --------------------------- - // Make subshapes association + // Make sub-shapes association // --------------------------- TopoDS_Face tgtFace = TopoDS::Face( theShape.Oriented(TopAbs_FORWARD)); TopoDS_Shape srcShape = _sourceHypo->GetSourceFace().Oriented(TopAbs_FORWARD); TAssocTool::TShapeShapeMap shape2ShapeMap; - TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap, tgtFace ); + TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap ); if ( !TAssocTool::FindSubShapeAssociation( tgtFace, tgtMesh, srcShape, srcMesh, shape2ShapeMap) || !shape2ShapeMap.IsBound( tgtFace )) @@ -405,197 +816,274 @@ bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed"); } - // -------------------- - // Prepare to mapping - // -------------------- - - SMESH_MesherHelper helper( theMesh ); - helper.SetSubShape( tgtFace ); - - // Check if node projection to a face is needed - Bnd_B2d uvBox; - SMDS_ElemIteratorPtr faceIt = srcSubMesh->GetSubMeshDS()->GetElements(); - for ( int nbN = 0; nbN < 3 && faceIt->more(); ) { - const SMDS_MeshElement* face = faceIt->next(); - SMDS_ElemIteratorPtr nodeIt = face->nodesIterator(); - while ( nodeIt->more() ) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) { - nbN++; - uvBox.Add( helper.GetNodeUV( srcFace, node )); - } - } - } - const bool toProjectNodes = ( uvBox.IsVoid() || uvBox.SquareExtent() < DBL_MIN ); - - // Load pattern from the source face - SMESH_Pattern mapper; - mapper.Load( srcMesh, srcFace, toProjectNodes ); - if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) - return error(COMPERR_BAD_INPUT_MESH,"Can't load mesh pattern from the source face"); - - // Find the first target vertex corresponding to first vertex of the - // and flag needed to call mapper.Apply() - - TopoDS_Vertex srcV1 = TopoDS::Vertex( mapper.GetSubShape( 1 )); - if ( srcV1.IsNull() ) - RETURN_BAD_RESULT("Mesh is not bound to the face"); - if ( !shape2ShapeMap.IsBound( srcV1 )) - RETURN_BAD_RESULT("Not associated vertices, srcV1 " << srcV1.TShape().operator->() ); - TopoDS_Vertex tgtV1 = TopoDS::Vertex( shape2ShapeMap( srcV1 )); - - if ( !TAssocTool::IsSubShape( srcV1, srcFace )) - RETURN_BAD_RESULT("Wrong srcV1 " << srcV1.TShape().operator->()); - if ( !TAssocTool::IsSubShape( tgtV1, tgtFace )) - RETURN_BAD_RESULT("Wrong tgtV1 " << tgtV1.TShape().operator->()); - - // try to find out orientation by order of edges - bool reverse = false; - list< TopoDS_Edge > tgtEdges, srcEdges; - list< int > nbEdgesInWires; - SMESH_Block::GetOrderedEdges( tgtFace, tgtV1, tgtEdges, nbEdgesInWires); - SMESH_Block::GetOrderedEdges( srcFace, srcV1, srcEdges, nbEdgesInWires); - if ( nbEdgesInWires.front() > 1 ) // possible to find out + // =========== + // Projection + // =========== + + // find out if EDGEs are meshed or not + bool is1DComputed = false; + SMESH_subMeshIteratorPtr smIt = tgtSubMesh->getDependsOnIterator(/*includeSelf=*/false, + /*complexShapeFirst=*/true); + while ( smIt->more() && !is1DComputed ) { - TopoDS_Edge srcE1 = srcEdges.front(), tgtE1 = tgtEdges.front(); - TopoDS_Shape srcE1bis = shape2ShapeMap( tgtE1 ); - reverse = ( ! srcE1.IsSame( srcE1bis )); + SMESH_subMesh* sm = smIt->next(); + if ( sm->GetSubShape().ShapeType() == TopAbs_EDGE ) + is1DComputed = sm->IsMeshComputed(); } - else if ( nbEdgesInWires.front() == 1 ) + + bool done = false; + + if ( !done ) { - // TODO::Compare orientation of curves in a sole edge - //RETURN_BAD_RESULT("Not implemented case"); + // try to project from the same face with different location + done = projectPartner( tgtFace, srcFace, tgtMesh, srcMesh, shape2ShapeMap ); } - else + if ( !done ) { - RETURN_BAD_RESULT("Bad result from SMESH_Block::GetOrderedEdges()"); + // projection in case if the faces are similar in 2D space + done = projectBy2DSimilarity( tgtFace, srcFace, tgtMesh, srcMesh, shape2ShapeMap, is1DComputed); } - // -------------------- - // Perform 2D mapping - // -------------------- - - // Compute mesh on a target face + if ( !done ) + { + // -------------------- + // Prepare to mapping + // -------------------- + + SMESH_MesherHelper helper( theMesh ); + helper.SetSubShape( tgtFace ); + + // Check if node projection to a face is needed + Bnd_B2d uvBox; + SMDS_ElemIteratorPtr faceIt = srcSubMesh->GetSubMeshDS()->GetElements(); + int nbFaceNodes = 0; + for ( ; nbFaceNodes < 3 && faceIt->more(); ) { + const SMDS_MeshElement* face = faceIt->next(); + SMDS_ElemIteratorPtr nodeIt = face->nodesIterator(); + while ( nodeIt->more() ) { + const SMDS_MeshNode* node = static_cast( nodeIt->next() ); + if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) { + nbFaceNodes++; + uvBox.Add( helper.GetNodeUV( srcFace, node )); + } + } + } + const bool toProjectNodes = + ( nbFaceNodes > 0 && ( uvBox.IsVoid() || uvBox.SquareExtent() < DBL_MIN )); + + // Load pattern from the source face + SMESH_Pattern mapper; + mapper.Load( srcMesh, srcFace, toProjectNodes ); + if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) + return error(COMPERR_BAD_INPUT_MESH,"Can't load mesh pattern from the source face"); + + // Find the first target vertex corresponding to first vertex of the + // and flag needed to call mapper.Apply() + + TopoDS_Vertex srcV1 = TopoDS::Vertex( mapper.GetSubShape( 1 )); + if ( srcV1.IsNull() ) + RETURN_BAD_RESULT("Mesh is not bound to the face"); + if ( !shape2ShapeMap.IsBound( srcV1, /*isSrc=*/true )) + RETURN_BAD_RESULT("Not associated vertices, srcV1 " << srcV1.TShape().operator->() ); + TopoDS_Vertex tgtV1 = TopoDS::Vertex( shape2ShapeMap( srcV1, /*isSrc=*/true )); + + if ( !SMESH_MesherHelper::IsSubShape( srcV1, srcFace )) + RETURN_BAD_RESULT("Wrong srcV1 " << srcV1.TShape().operator->()); + if ( !SMESH_MesherHelper::IsSubShape( tgtV1, tgtFace )) + RETURN_BAD_RESULT("Wrong tgtV1 " << tgtV1.TShape().operator->()); + + // try to find out orientation by order of edges + bool reverse = false; + list< TopoDS_Edge > tgtEdges, srcEdges; + list< int > nbEdgesInWires; + SMESH_Block::GetOrderedEdges( tgtFace, tgtV1, tgtEdges, nbEdgesInWires); + SMESH_Block::GetOrderedEdges( srcFace, srcV1, srcEdges, nbEdgesInWires); + if ( nbEdgesInWires.front() > 1 ) // possible to find out + { + TopoDS_Edge srcE1 = srcEdges.front(), tgtE1 = tgtEdges.front(); + TopoDS_Shape srcE1bis = shape2ShapeMap( tgtE1 ); + reverse = ( ! srcE1.IsSame( srcE1bis )); + } + else if ( nbEdgesInWires.front() == 1 ) + { + // TODO::Compare orientation of curves in a sole edge + //RETURN_BAD_RESULT("Not implemented case"); + } + else + { + RETURN_BAD_RESULT("Bad result from SMESH_Block::GetOrderedEdges()"); + } - mapper.Apply( tgtFace, tgtV1, reverse ); - if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) - return error("Can't apply source mesh pattern to the face"); + // -------------------- + // Perform 2D mapping + // -------------------- - // Create the mesh + // Compute mesh on a target face - const bool toCreatePolygons = false, toCreatePolyedrs = false; - mapper.MakeMesh( tgtMesh, toCreatePolygons, toCreatePolyedrs ); - if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) - return error("Can't make mesh by source mesh pattern"); + mapper.Apply( tgtFace, tgtV1, reverse ); + if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) + return error("Can't apply source mesh pattern to the face"); - // it will remove mesh built by pattern mapper on edges and vertices - // in failure case - MeshCleaner cleaner( tgtSubMesh ); + // Create the mesh - // ------------------------------------------------------------------------- - // mapper doesn't take care of nodes already existing on edges and vertices, - // so we must merge nodes created by it with existing ones - // ------------------------------------------------------------------------- + const bool toCreatePolygons = false, toCreatePolyedrs = false; + mapper.MakeMesh( tgtMesh, toCreatePolygons, toCreatePolyedrs ); + if ( mapper.GetErrorCode() != SMESH_Pattern::ERR_OK ) + return error("Can't make mesh by source mesh pattern"); - SMESH_MeshEditor editor( tgtMesh ); - SMESH_MeshEditor::TListOfListOfNodes groupsOfNodes; + // it will remove mesh built by pattern mapper on edges and vertices + // in failure case + MeshCleaner cleaner( tgtSubMesh ); - // Make groups of nodes to merge + // ------------------------------------------------------------------------- + // mapper doesn't take care of nodes already existing on edges and vertices, + // so we must merge nodes created by it with existing ones + // ------------------------------------------------------------------------- - // loop on edge and vertex submeshes of a target face - SMESH_subMeshIteratorPtr smIt = tgtSubMesh->getDependsOnIterator(false,false); - while ( smIt->more() ) - { - SMESH_subMesh* sm = smIt->next(); - SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); + SMESH_MeshEditor::TListOfListOfNodes groupsOfNodes; - // Sort new and old nodes of a submesh separately + // Make groups of nodes to merge - bool isSeam = helper.IsRealSeam( sm->GetId() ); + // loop on edge and vertex submeshes of a target face + smIt = tgtSubMesh->getDependsOnIterator(/*includeSelf=*/false,/*complexShapeFirst=*/false); + while ( smIt->more() ) + { + SMESH_subMesh* sm = smIt->next(); + SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); + if ( !smDS || smDS->NbNodes() == 0 ) + continue; + //if ( !is1DComputed && sm->GetSubShape().ShapeType() == TopAbs_EDGE ) + //break; - enum { NEW_NODES = 0, OLD_NODES }; - map< double, const SMDS_MeshNode* > u2nodesMaps[2], u2nodesOnSeam; - map< double, const SMDS_MeshNode* >::iterator u_oldNode, u_newNode, u_newOnSeam, newEnd; - set< const SMDS_MeshNode* > seamNodes; + // Sort new and old nodes of a submesh separately - // mapper puts on a seam edge nodes from 2 edges - if ( isSeam && ! getBoundaryNodes ( sm, tgtFace, u2nodesOnSeam, seamNodes )) - RETURN_BAD_RESULT("getBoundaryNodes() failed"); + bool isSeam = helper.IsRealSeam( sm->GetId() ); - SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); - while ( nIt->more() ) - { - const SMDS_MeshNode* node = nIt->next(); - bool isOld = isOldNode( node ); + enum { NEW_NODES = 0, OLD_NODES }; + map< double, const SMDS_MeshNode* > u2nodesMaps[2], u2nodesOnSeam; + map< double, const SMDS_MeshNode* >::iterator u_oldNode, u_newNode, u_newOnSeam, newEnd; + set< const SMDS_MeshNode* > seamNodes; - if ( !isOld && isSeam ) { // new node on a seam edge - if ( seamNodes.find( node ) != seamNodes.end()) - continue; // node is already in the map - } + // mapper puts on a seam edge nodes from 2 edges + if ( isSeam && ! getBoundaryNodes ( sm, tgtFace, u2nodesOnSeam, seamNodes )) + RETURN_BAD_RESULT("getBoundaryNodes() failed"); - // sort nodes on edges by their position - map< double, const SMDS_MeshNode* > & pos2nodes = u2nodesMaps[isOld ? OLD_NODES : NEW_NODES]; - switch ( node->GetPosition()->GetTypeOfPosition() ) + SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); + while ( nIt->more() ) { - case SMDS_TOP_VERTEX: { - pos2nodes.insert( make_pair( 0, node )); - break; - } - case SMDS_TOP_EDGE: { - const SMDS_EdgePosition* pos = - static_cast(node->GetPosition().get()); - pos2nodes.insert( make_pair( pos->GetUParameter(), node )); - break; + const SMDS_MeshNode* node = nIt->next(); + bool isOld = isOldNode( node ); + + if ( !isOld && isSeam ) { // new node on a seam edge + if ( seamNodes.count( node ) ) + continue; // node is already in the map + } + + // sort nodes on edges by their position + map< double, const SMDS_MeshNode* > & pos2nodes = u2nodesMaps[isOld ? OLD_NODES : NEW_NODES]; + switch ( node->GetPosition()->GetTypeOfPosition() ) + { + case SMDS_TOP_VERTEX: { + if ( !is1DComputed && !pos2nodes.empty() ) + u2nodesMaps[isOld ? NEW_NODES : OLD_NODES].insert( make_pair( 0, node )); + else + pos2nodes.insert( make_pair( 0, node )); + break; + } + case SMDS_TOP_EDGE: { + const SMDS_EdgePosition* pos = + static_cast(node->GetPosition()); + pos2nodes.insert( make_pair( pos->GetUParameter(), node )); + break; + } + default: + RETURN_BAD_RESULT("Wrong node position type: "<< + node->GetPosition()->GetTypeOfPosition()); + } } - default: - RETURN_BAD_RESULT("Wrong node position type: "<< - node->GetPosition()->GetTypeOfPosition()); + const bool mergeNewToOld = + ( u2nodesMaps[ NEW_NODES ].size() == u2nodesMaps[ OLD_NODES ].size() ); + const bool mergeSeamToNew = + ( u2nodesMaps[ NEW_NODES ].size() == u2nodesOnSeam.size() ); + + if ( !mergeNewToOld ) + if ( u2nodesMaps[ NEW_NODES ].size() > 0 && + u2nodesMaps[ OLD_NODES ].size() > 0 ) + { + u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); + newEnd = u2nodesMaps[ OLD_NODES ].end(); + for ( ; u_oldNode != newEnd; ++u_oldNode ) + _badInputElements.push_back( u_oldNode->second ); + return error( COMPERR_BAD_INPUT_MESH, + SMESH_Comment( "Existing mesh mismatches the projected 2D mesh on " ) + << ( sm->GetSubShape().ShapeType() == TopAbs_EDGE ? "edge" : "vertex" ) + << " #" << sm->GetId() ); + } + if ( isSeam && !mergeSeamToNew ) { + //RETURN_BAD_RESULT + MESSAGE("Different nb of old and seam nodes " << + u2nodesMaps[ OLD_NODES ].size() << " != " << u2nodesOnSeam.size()); } + // Make groups of nodes to merge + u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); + u_newNode = u2nodesMaps[ NEW_NODES ].begin(); + newEnd = u2nodesMaps[ NEW_NODES ].end(); + u_newOnSeam = u2nodesOnSeam.begin(); + if ( mergeNewToOld ) + for ( ; u_newNode != newEnd; ++u_newNode, ++u_oldNode ) + { + groupsOfNodes.push_back( list< const SMDS_MeshNode* >() ); + groupsOfNodes.back().push_back( u_oldNode->second ); + groupsOfNodes.back().push_back( u_newNode->second ); + if ( mergeSeamToNew ) + groupsOfNodes.back().push_back( (u_newOnSeam++)->second ); + } + else if ( mergeSeamToNew ) + for ( ; u_newNode != newEnd; ++u_newNode, ++u_newOnSeam ) + { + groupsOfNodes.push_back( list< const SMDS_MeshNode* >() ); + groupsOfNodes.back().push_back( u_newNode->second ); + groupsOfNodes.back().push_back( u_newOnSeam->second ); + } } - if ( u2nodesMaps[ NEW_NODES ].size() != u2nodesMaps[ OLD_NODES ].size() ) + + // Merge + + SMESH_MeshEditor editor( tgtMesh ); + int nbFaceBeforeMerge = tgtSubMesh->GetSubMeshDS()->NbElements(); + editor.MergeNodes( groupsOfNodes ); + int nbFaceAtferMerge = tgtSubMesh->GetSubMeshDS()->NbElements(); + if ( nbFaceBeforeMerge != nbFaceAtferMerge ) + return error(COMPERR_BAD_INPUT_MESH, "Probably invalid node parameters on geom faces"); + + // ---------------------------------------------------------------- + // The mapper can't create quadratic elements, so convert if needed + // ---------------------------------------------------------------- + + faceIt = srcSubMesh->GetSubMeshDS()->GetElements(); + bool srcIsQuad = faceIt->next()->IsQuadratic(); + faceIt = tgtSubMesh->GetSubMeshDS()->GetElements(); + bool tgtIsQuad = faceIt->next()->IsQuadratic(); + if ( srcIsQuad && !tgtIsQuad ) { - if ( u2nodesMaps[ NEW_NODES ].size() == 0 && - sm->GetSubShape().ShapeType() == TopAbs_EDGE && - helper.IsDegenShape( sm->GetId() ) ) - // NPAL15894 (tt88bis.py) - project mesh built by NETGEN_1d_2D that - // does not make segments/nodes on degenerated edges - continue; + TIDSortedElemSet tgtFaces; + faceIt = tgtSubMesh->GetSubMeshDS()->GetElements(); + while ( faceIt->more() ) + tgtFaces.insert( tgtFaces.end(), faceIt->next() ); - RETURN_BAD_RESULT("Different nb of old and new nodes on shape #"<< sm->GetId() <<" "<< - u2nodesMaps[ OLD_NODES ].size() << " != " << - u2nodesMaps[ NEW_NODES ].size()); + editor.ConvertToQuadratic(/*theForce3d=*/false, tgtFaces); } - if ( isSeam && u2nodesMaps[ OLD_NODES ].size() != u2nodesOnSeam.size() ) { - RETURN_BAD_RESULT("Different nb of old and seam nodes " << - u2nodesMaps[ OLD_NODES ].size() << " != " << u2nodesOnSeam.size()); - } - // Make groups of nodes to merge - u_oldNode = u2nodesMaps[ OLD_NODES ].begin(); - u_newNode = u2nodesMaps[ NEW_NODES ].begin(); - newEnd = u2nodesMaps[ NEW_NODES ].end(); - u_newOnSeam = u2nodesOnSeam.begin(); - for ( ; u_newNode != newEnd; ++u_newNode, ++u_oldNode ) { - groupsOfNodes.push_back( list< const SMDS_MeshNode* >() ); - groupsOfNodes.back().push_back( u_oldNode->second ); - groupsOfNodes.back().push_back( u_newNode->second ); - if ( isSeam ) - groupsOfNodes.back().push_back( (u_newOnSeam++)->second ); - } - } - // Merge + cleaner.Release(); // not to remove mesh + + } // end of projection using Pattern mapping - int nbFaceBeforeMerge = tgtSubMesh->GetSubMeshDS()->NbElements(); - editor.MergeNodes( groupsOfNodes ); - int nbFaceAtferMerge = tgtSubMesh->GetSubMeshDS()->NbElements(); - if ( nbFaceBeforeMerge != nbFaceAtferMerge ) - return error(COMPERR_BAD_INPUT_MESH, "Probably invalid node parameters on geom faces"); // --------------------------- // Check elements orientation // --------------------------- - TopoDS_Face face = tgtFace; + TopoDS_Face face = TopoDS::Face( theShape ); if ( !theMesh.IsMainShape( tgtFace )) { // find the main shape @@ -627,6 +1115,7 @@ bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& // Fix orientation if ( SMESH_Algo::IsReversedSubMesh( face, meshDS )) { + SMESH_MeshEditor editor( tgtMesh ); SMDS_ElemIteratorPtr eIt = meshDS->MeshElements( face )->GetElements(); while ( eIt->more() ) { const SMDS_MeshElement* e = eIt->next(); @@ -635,11 +1124,77 @@ bool StdMeshers_Projection_2D::Compute(SMESH_Mesh& theMesh, const TopoDS_Shape& } } - cleaner.Release(); // do not remove mesh + return true; +} + + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_Projection_2D::Evaluate(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + MapShapeNbElems& aResMap) +{ + if ( !_sourceHypo ) + return false; + + SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh(); + SMESH_Mesh * tgtMesh = & theMesh; + if ( !srcMesh ) + srcMesh = tgtMesh; + + // --------------------------- + // Make sub-shapes association + // --------------------------- + + TopoDS_Face tgtFace = TopoDS::Face( theShape.Oriented(TopAbs_FORWARD)); + TopoDS_Shape srcShape = _sourceHypo->GetSourceFace().Oriented(TopAbs_FORWARD); + + TAssocTool::TShapeShapeMap shape2ShapeMap; + TAssocTool::InitVertexAssociation( _sourceHypo, shape2ShapeMap ); + if ( !TAssocTool::FindSubShapeAssociation( tgtFace, tgtMesh, srcShape, srcMesh, + shape2ShapeMap) || + !shape2ShapeMap.IsBound( tgtFace )) + return error(COMPERR_BAD_SHAPE,"Topology of source and target faces seems different" ); + + TopoDS_Face srcFace = TopoDS::Face( shape2ShapeMap( tgtFace ).Oriented(TopAbs_FORWARD)); + + // ------------------------------------------------------- + // Assure that mesh on a source Face is computed/evaluated + // ------------------------------------------------------- + + std::vector aVec; + + SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( srcFace ); + if ( srcSubMesh->IsMeshComputed() ) + { + aVec.resize( SMDSEntity_Last, 0 ); + aVec[SMDSEntity_Node] = srcSubMesh->GetSubMeshDS()->NbNodes(); + + SMDS_ElemIteratorPtr elemIt = srcSubMesh->GetSubMeshDS()->GetElements(); + while ( elemIt->more() ) + aVec[ elemIt->next()->GetEntityType() ]++; + } + else + { + MapShapeNbElems tmpResMap; + MapShapeNbElems& srcResMap = (srcMesh == tgtMesh) ? aResMap : tmpResMap; + if ( !_gen->Evaluate( *srcMesh, srcShape, srcResMap )) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh not evaluatable"); + aVec = srcResMap[ srcSubMesh ]; + if ( aVec.empty() ) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh is wrongly evaluated"); + } + + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(std::make_pair(sm,aVec)); return true; } + //============================================================================= /*! * \brief Sets a default event listener to submesh of the source face diff --git a/src/StdMeshers/StdMeshers_Projection_2D.hxx b/src/StdMeshers/StdMeshers_Projection_2D.hxx index 006db8344..3d1bd1808 100644 --- a/src/StdMeshers/StdMeshers_Projection_2D.hxx +++ b/src/StdMeshers/StdMeshers_Projection_2D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_2D.hxx // Module : SMESH @@ -44,6 +45,9 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! * \brief Sets a default event listener to submesh of the source face * \param whenSetToSubMesh - submesh where algo is set diff --git a/src/StdMeshers/StdMeshers_Projection_3D.cxx b/src/StdMeshers/StdMeshers_Projection_3D.cxx index 7d657ac4e..76c29c822 100644 --- a/src/StdMeshers/StdMeshers_Projection_3D.cxx +++ b/src/StdMeshers/StdMeshers_Projection_3D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_3D.cxx // Module : SMESH @@ -30,19 +31,18 @@ #include "StdMeshers_ProjectionUtils.hxx" +#include "SMDS_PolyhedralVolumeOfNodes.hxx" +#include "SMDS_VolumeTool.hxx" #include "SMESHDS_Hypothesis.hxx" #include "SMESHDS_SubMesh.hxx" #include "SMESH_Block.hxx" +#include "SMESH_Comment.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" +#include "SMESH_MesherHelper.hxx" #include "SMESH_Pattern.hxx" #include "SMESH_subMesh.hxx" #include "SMESH_subMeshEventListener.hxx" -#include "SMESH_MesherHelper.hxx" -#include "SMESH_Comment.hxx" -#include "SMDS_VolumeTool.hxx" -#include "SMDS_PolyhedralVolumeOfNodes.hxx" #include "utilities.h" @@ -69,7 +69,7 @@ StdMeshers_Projection_3D::StdMeshers_Projection_3D(int hypId, int studyId, SMESH :SMESH_3D_Algo(hypId, studyId, gen) { _name = "Projection_3D"; - _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit per shape type + _shapeType = (1 << TopAbs_SHELL) | (1 << TopAbs_SOLID); // 1 bit per shape type _compatibleHypothesis.push_back("ProjectionSource3D"); _sourceHypo = 0; @@ -142,12 +142,12 @@ bool StdMeshers_Projection_3D::CheckHypothesis(SMESH_Mesh& TopoDS_Shape edge = TAssocTool::GetEdgeByVertices ( srcMesh, _sourceHypo->GetSourceVertex(1), _sourceHypo->GetSourceVertex(2) ); if ( edge.IsNull() || - !TAssocTool::IsSubShape( edge, srcMesh ) || - !TAssocTool::IsSubShape( edge, _sourceHypo->GetSource3DShape() )) + !SMESH_MesherHelper::IsSubShape( edge, srcMesh ) || + !SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSource3DShape() )) { SCRUTE((edge.IsNull())); - SCRUTE((TAssocTool::IsSubShape( edge, srcMesh ))); - SCRUTE((TAssocTool::IsSubShape( edge, _sourceHypo->GetSource3DShape() ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, srcMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, _sourceHypo->GetSource3DShape() ))); aStatus = SMESH_Hypothesis::HYP_BAD_PARAMETER; } else @@ -156,21 +156,21 @@ bool StdMeshers_Projection_3D::CheckHypothesis(SMESH_Mesh& edge = TAssocTool::GetEdgeByVertices ( tgtMesh, _sourceHypo->GetTargetVertex(1), _sourceHypo->GetTargetVertex(2) ); if ( edge.IsNull() || - !TAssocTool::IsSubShape( edge, tgtMesh ) || - !TAssocTool::IsSubShape( edge, aShape )) + !SMESH_MesherHelper::IsSubShape( edge, tgtMesh ) || + !SMESH_MesherHelper::IsSubShape( edge, aShape )) { SCRUTE((edge.IsNull())); - SCRUTE((TAssocTool::IsSubShape( edge, tgtMesh ))); - SCRUTE((TAssocTool::IsSubShape( edge, aShape ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, tgtMesh ))); + SCRUTE((SMESH_MesherHelper::IsSubShape( edge, aShape ))); aStatus = SMESH_Hypothesis::HYP_BAD_PARAMETER; } } } // check a source shape - if ( !TAssocTool::IsSubShape( _sourceHypo->GetSource3DShape(), srcMesh ) || + if ( !SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSource3DShape(), srcMesh ) || ( srcMesh == tgtMesh && aShape == _sourceHypo->GetSource3DShape())) { - SCRUTE((TAssocTool::IsSubShape( _sourceHypo->GetSource3DShape(), srcMesh))); + SCRUTE((SMESH_MesherHelper::IsSubShape( _sourceHypo->GetSource3DShape(), srcMesh))); SCRUTE((srcMesh == tgtMesh)); SCRUTE((aShape == _sourceHypo->GetSource3DShape())); aStatus = SMESH_Hypothesis::HYP_BAD_PARAMETER; @@ -264,12 +264,12 @@ bool StdMeshers_Projection_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aS TopExp::Vertices( TopoDS::Edge( exp.Current() ), tgtV000, tgtV100 ); if ( !shape2ShapeMap.IsBound( tgtV000 ) || !shape2ShapeMap.IsBound( tgtV100 )) - return error("Association of subshapes failed" ); + return error("Association of sub-shapes failed" ); srcV000 = TopoDS::Vertex( shape2ShapeMap( tgtV000 )); srcV100 = TopoDS::Vertex( shape2ShapeMap( tgtV100 )); - if ( !TAssocTool::IsSubShape( srcV000, srcShell ) || - !TAssocTool::IsSubShape( srcV100, srcShell )) - return error("Incorrect association of subshapes" ); + if ( !SMESH_MesherHelper::IsSubShape( srcV000, srcShell ) || + !SMESH_MesherHelper::IsSubShape( srcV100, srcShell )) + return error("Incorrect association of sub-shapes" ); } // Load 2 SMESH_Block's with src and tgt shells @@ -277,20 +277,20 @@ bool StdMeshers_Projection_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aS SMESH_Block srcBlock, tgtBlock; TopTools_IndexedMapOfOrientedShape scrShapes, tgtShapes; if ( !tgtBlock.LoadBlockShapes( tgtShell, tgtV000, tgtV100, tgtShapes )) - return error(COMPERR_BAD_SHAPE, "Can't detect block subshapes. Not a block?"); + return error(COMPERR_BAD_SHAPE, "Can't detect block sub-shapes. Not a block?"); if ( !srcBlock.LoadBlockShapes( srcShell, srcV000, srcV100, scrShapes )) - return error(COMPERR_BAD_SHAPE, "Can't detect block subshapes. Not a block?"); + return error(COMPERR_BAD_SHAPE, "Can't detect block sub-shapes. Not a block?"); // Find matching nodes of src and tgt shells TNodeNodeMap src2tgtNodeMap; for ( int fId = SMESH_Block::ID_FirstF; fId < SMESH_Block::ID_Shell; ++fId ) { - // Corresponding subshapes + // Corresponding sub-shapes TopoDS_Face srcFace = TopoDS::Face( scrShapes( fId )); TopoDS_Face tgtFace = TopoDS::Face( tgtShapes( fId )); - if ( _sourceHypo->HasVertexAssociation() ) { // associate face subshapes + if ( _sourceHypo->HasVertexAssociation() ) { // associate face sub-shapes shape2ShapeMap.Clear(); vector< int > edgeIdVec; SMESH_Block::GetFaceEdgesIDs( fId, edgeIdVec ); @@ -309,9 +309,9 @@ bool StdMeshers_Projection_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aS TNodeNodeMap faceMatchingNodes; if ( ! TAssocTool::FindMatchingNodesOnFaces( srcFace, srcMesh, tgtFace, tgtMesh, shape2ShapeMap, faceMatchingNodes )) - return error(COMPERR_BAD_INPUT_MESH,SMESH_Comment("Mesh on faces #") - << srcMeshDS->ShapeToIndex( srcFace ) << " and " - << tgtMeshDS->ShapeToIndex( tgtFace ) << " seems different" ); + return error(COMPERR_BAD_INPUT_MESH,SMESH_Comment("Mesh on faces #") + << srcMeshDS->ShapeToIndex( srcFace ) << " and " + << tgtMeshDS->ShapeToIndex( tgtFace ) << " seems different" ); // put found matching nodes of 2 faces to the global map src2tgtNodeMap.insert( faceMatchingNodes.begin(), faceMatchingNodes.end() ); @@ -407,11 +407,13 @@ bool StdMeshers_Projection_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aS nodes[6], nodes[7], id, force3d); break; default: // polyhedron - const SMDS_PolyhedralVolumeOfNodes * poly = - dynamic_cast( srcVol ); + const SMDS_VtkVolume * poly = + dynamic_cast( srcVol ); if ( !poly ) RETURN_BAD_RESULT("Unexpected volume type"); - tgtVol = tgtMeshDS->AddPolyhedralVolume( nodes, poly->GetQuanities() ); + if ( !poly->IsPoly()) + RETURN_BAD_RESULT("Unexpected volume type"); + tgtVol = tgtMeshDS->AddPolyhedralVolume( nodes, poly->GetQuantities() ); } if ( tgtVol ) { tgtMeshDS->SetMeshElementOnShape( tgtVol, helper.GetSubShapeID() ); @@ -421,6 +423,104 @@ bool StdMeshers_Projection_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aS return true; } + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_Projection_3D::Evaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + MapShapeNbElems& aResMap) +{ + if ( !_sourceHypo ) + return false; + + SMESH_Mesh * srcMesh = _sourceHypo->GetSourceMesh(); + SMESH_Mesh * tgtMesh = & aMesh; + if ( !srcMesh ) + srcMesh = tgtMesh; + + // get shell from shape3D + TopoDS_Shell srcShell, tgtShell; + TopExp_Explorer exp( _sourceHypo->GetSource3DShape(), TopAbs_SHELL ); + int nbShell; + for ( nbShell = 0; exp.More(); exp.Next(), ++nbShell ) + srcShell = TopoDS::Shell( exp.Current() ); + if ( nbShell != 1 ) + return error(COMPERR_BAD_SHAPE, + SMESH_Comment("Source shape must have 1 shell but not ") << nbShell); + + exp.Init( aShape, TopAbs_SHELL ); + for ( nbShell = 0; exp.More(); exp.Next(), ++nbShell ) + tgtShell = TopoDS::Shell( exp.Current() ); + if ( nbShell != 1 ) + return error(COMPERR_BAD_SHAPE, + SMESH_Comment("Target shape must have 1 shell but not ") << nbShell); + + // Check that shapes are blocks + if ( TAssocTool::Count( tgtShell, TopAbs_FACE , 1 ) != 6 || + TAssocTool::Count( tgtShell, TopAbs_EDGE , 1 ) != 12 || + TAssocTool::Count( tgtShell, TopAbs_WIRE , 1 ) != 6 ) + return error(COMPERR_BAD_SHAPE, "Target shape is not a block"); + if ( TAssocTool::Count( srcShell, TopAbs_FACE , 1 ) != 6 || + TAssocTool::Count( srcShell, TopAbs_EDGE , 1 ) != 12 || + TAssocTool::Count( srcShell, TopAbs_WIRE , 1 ) != 6 ) + return error(COMPERR_BAD_SHAPE, "Source shape is not a block"); + + // Assure that mesh on a source shape is computed + + SMESH_subMesh* srcSubMesh = srcMesh->GetSubMesh( _sourceHypo->GetSource3DShape() ); + + if ( !srcSubMesh->IsMeshComputed() ) + return error(COMPERR_BAD_INPUT_MESH,"Source mesh not computed"); + + + std::vector aVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetSubMeshDS()->NbNodes(); + + //bool quadratic = false; + SMDS_ElemIteratorPtr elemIt = srcSubMesh->GetSubMeshDS()->GetElements(); + while ( elemIt->more() ) { + const SMDS_MeshElement* E = elemIt->next(); + if( E->NbNodes()==4 ) { + aVec[SMDSEntity_Tetra]++; + } + else if( E->NbNodes()==5 ) { + aVec[SMDSEntity_Pyramid]++; + } + else if( E->NbNodes()==6 ) { + aVec[SMDSEntity_Penta]++; + } + else if( E->NbNodes()==8 ) { + aVec[SMDSEntity_Hexa]++; + } + else if( E->NbNodes()==10 && E->IsQuadratic() ) { + aVec[SMDSEntity_Quad_Tetra]++; + } + else if( E->NbNodes()==13 && E->IsQuadratic() ) { + aVec[SMDSEntity_Quad_Pyramid]++; + } + else if( E->NbNodes()==15 && E->IsQuadratic() ) { + aVec[SMDSEntity_Quad_Penta]++; + } + else if( E->NbNodes()==20 && E->IsQuadratic() ) { + aVec[SMDSEntity_Quad_Hexa]++; + } + else { + aVec[SMDSEntity_Polyhedra]++; + } + } + + SMESH_subMesh * sm = aMesh.GetSubMesh(aShape); + aResMap.insert(std::make_pair(sm,aVec)); + + return true; +} + + //============================================================================= /*! * \brief Sets a default event listener to submesh of the source shape diff --git a/src/StdMeshers/StdMeshers_Projection_3D.hxx b/src/StdMeshers/StdMeshers_Projection_3D.hxx index 7f4200ef4..03e8c4838 100644 --- a/src/StdMeshers/StdMeshers_Projection_3D.hxx +++ b/src/StdMeshers/StdMeshers_Projection_3D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Projection_3D.hxx // Module : SMESH @@ -44,6 +45,9 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! * \brief Sets a default event listener to submesh of the source shape * \param whenSetToSubMesh - submesh where algo is set diff --git a/src/StdMeshers/StdMeshers_Propagation.cxx b/src/StdMeshers/StdMeshers_Propagation.cxx index c6b9b015f..0550c86a7 100644 --- a/src/StdMeshers/StdMeshers_Propagation.cxx +++ b/src/StdMeshers/StdMeshers_Propagation.cxx @@ -1,34 +1,36 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Propagation.cxx // Module : SMESH - +// #include "StdMeshers_Propagation.hxx" #include "utilities.h" #include "SMDS_SetIterator.hxx" #include "SMESH_Algo.hxx" +#include "SMESH_Gen.hxx" #include "SMESH_HypoFilter.hxx" #include "SMESH_Mesh.hxx" #include "SMESH_subMesh.hxx" @@ -39,7 +41,7 @@ #include #define DBGMSG(txt) \ -// cout << txt << endl; + // cout << txt << endl; using namespace std; @@ -266,7 +268,7 @@ namespace { for (; itA.More(); itA.Next()) { // there are objects of different type among the ancestors of edge - if ( itA.Value().ShapeType() != TopAbs_WIRE || !checkedShapes.Add( itA.Value() )) + if ( itA.Value().ShapeType() != TopAbs_WIRE /*|| !checkedShapes.Add( itA.Value() )*/) continue; // Get ordered edges and find index of anE in a sequence @@ -288,8 +290,7 @@ namespace { continue; // too few edges } else if ( edges.size() == 4 ) { - int oppIndex = edgeIndex + 2; - if ( oppIndex > 3 ) oppIndex -= 4; + int oppIndex = ( edgeIndex + 2 ) % 4; anOppE = edges[ oppIndex ]; } else { @@ -346,6 +347,10 @@ namespace { oppSM->ComputeStateEngine( SMESH_subMesh::CLEAN ); oppData->SetState( IN_CHAIN ); DBGMSG( "set IN_CHAIN on " << oppSM->GetId() ); + if ( oppSM->GetAlgoState() != SMESH_subMesh::HYP_OK ) + // make oppSM check algo state + if ( SMESH_Algo* algo = mesh->GetGen()->GetAlgo( *mesh, anOppE )) + oppSM->AlgoStateEngine(SMESH_subMesh::ADD_FATHER_ALGO,algo); } else { oppData->SetState( LAST_IN_CHAIN ); @@ -444,7 +449,8 @@ namespace { //================================================================================ PropagationMgr::PropagationMgr() - : SMESH_subMeshEventListener( false ) // won't be deleted by submesh + : SMESH_subMeshEventListener( false, // won't be deleted by submesh + "StdMeshers_Propagation::PropagationMgr") {} //================================================================================ /*! @@ -454,6 +460,7 @@ namespace { void PropagationMgr::Set(SMESH_subMesh * submesh) { + if ( findData( submesh )) return; DBGMSG( "PropagationMgr::Set() on " << submesh->GetId() ); EventListenerData* data = new PropagationMgrData(); submesh->SetEventListener( getListener(), data, submesh ); diff --git a/src/StdMeshers/StdMeshers_Propagation.hxx b/src/StdMeshers/StdMeshers_Propagation.hxx index b02f9aebe..5a72d72a5 100644 --- a/src/StdMeshers/StdMeshers_Propagation.hxx +++ b/src/StdMeshers/StdMeshers_Propagation.hxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Propagation.hxx // Module : SMESH - +// #ifndef _SMESH_PROPAGATION_HXX_ #define _SMESH_PROPAGATION_HXX_ diff --git a/src/StdMeshers/StdMeshers_QuadToTriaAdaptor.cxx b/src/StdMeshers/StdMeshers_QuadToTriaAdaptor.cxx index 620bad81c..5764cc6ea 100644 --- a/src/StdMeshers/StdMeshers_QuadToTriaAdaptor.cxx +++ b/src/StdMeshers/StdMeshers_QuadToTriaAdaptor.cxx @@ -1,59 +1,370 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 // -// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_QuadToTriaAdaptor.cxx // Module : SMESH // Created : Wen May 07 16:37:07 2008 // Author : Sergey KUUL (skl) -// + #include "StdMeshers_QuadToTriaAdaptor.hxx" -//#include -//#include +#include "SMDS_SetIterator.hxx" + +#include "SMESH_Algo.hxx" +#include "SMESH_MesherHelper.hxx" + +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -typedef NCollection_Array1 StdMeshers_Array1OfSequenceOfInteger; +#include +#include +using namespace std; -//======================================================================= -//function : StdMeshers_QuadToTriaAdaptor -//purpose : -//======================================================================= +enum EQuadNature { NOT_QUAD, QUAD, DEGEN_QUAD, PYRAM_APEX = 4, TRIA_APEX = 0 }; -StdMeshers_QuadToTriaAdaptor::StdMeshers_QuadToTriaAdaptor() +// std-like iterator used to get coordinates of nodes of mesh element +typedef SMDS_StdIterator< SMESH_TNodeXYZ, SMDS_ElemIteratorPtr > TXyzIterator; + +namespace { + //================================================================================ + /*! + * \brief Return true if two nodes of triangles are equal + */ + //================================================================================ + + bool EqualTriangles(const SMDS_MeshElement* F1,const SMDS_MeshElement* F2) + { + return + ( F1->GetNode(1)==F2->GetNode(2) && F1->GetNode(2)==F2->GetNode(1) ) || + ( F1->GetNode(1)==F2->GetNode(1) && F1->GetNode(2)==F2->GetNode(2) ); + } + //================================================================================ + /*! + * \brief Return true if two adjacent pyramids are too close one to another + * so that a tetrahedron to built between them would have too poor quality + */ + //================================================================================ + + bool TooCloseAdjacent( const SMDS_MeshElement* PrmI, + const SMDS_MeshElement* PrmJ, + const bool hasShape) + { + const SMDS_MeshNode* nApexI = PrmI->GetNode(4); + const SMDS_MeshNode* nApexJ = PrmJ->GetNode(4); + if ( nApexI == nApexJ || + nApexI->getshapeId() != nApexJ->getshapeId() ) + return false; + + // Find two common base nodes and their indices within PrmI and PrmJ + const SMDS_MeshNode* baseNodes[2] = { 0,0 }; + int baseNodesIndI[2], baseNodesIndJ[2]; + for ( int i = 0; i < 4 ; ++i ) + { + int j = PrmJ->GetNodeIndex( PrmI->GetNode(i)); + if ( j >= 0 ) + { + int ind = baseNodes[0] ? 1:0; + if ( baseNodes[ ind ]) + return false; // pyramids with a common base face + baseNodes [ ind ] = PrmI->GetNode(i); + baseNodesIndI[ ind ] = i; + baseNodesIndJ[ ind ] = j; + } + } + if ( !baseNodes[1] ) return false; // not adjacent + + // Get normals of triangles sharing baseNodes + gp_XYZ apexI = SMESH_TNodeXYZ( nApexI ); + gp_XYZ apexJ = SMESH_TNodeXYZ( nApexJ ); + gp_XYZ base1 = SMESH_TNodeXYZ( baseNodes[0]); + gp_XYZ base2 = SMESH_TNodeXYZ( baseNodes[1]); + gp_Vec baseVec( base1, base2 ); + gp_Vec baI( base1, apexI ); + gp_Vec baJ( base1, apexJ ); + gp_Vec nI = baseVec.Crossed( baI ); + gp_Vec nJ = baseVec.Crossed( baJ ); + + // Check angle between normals + double angle = nI.Angle( nJ ); + bool tooClose = ( angle < 15. * M_PI / 180. ); + + // Check if pyramids collide + if ( !tooClose && baI * baJ > 0 ) + { + // find out if nI points outside of PrmI or inside + int dInd = baseNodesIndI[1] - baseNodesIndI[0]; + bool isOutI = ( abs(dInd)==1 ) ? dInd < 0 : dInd > 0; + + // find out sign of projection of nJ to baI + double proj = baI * nJ; + + tooClose = isOutI ? proj > 0 : proj < 0; + } + + // Check if PrmI and PrmJ are in same domain + if ( tooClose && !hasShape ) + { + // check order of baseNodes within pyramids, it must be opposite + int dInd; + dInd = baseNodesIndI[1] - baseNodesIndI[0]; + bool isOutI = ( abs(dInd)==1 ) ? dInd < 0 : dInd > 0; + dInd = baseNodesIndJ[1] - baseNodesIndJ[0]; + bool isOutJ = ( abs(dInd)==1 ) ? dInd < 0 : dInd > 0; + if ( isOutJ == isOutI ) + return false; // other domain + + // direct both normals outside pyramid + ( isOutI ? nJ : nI ).Reverse(); + + // check absence of a face separating domains between pyramids + TIDSortedElemSet emptySet, avoidSet; + int i1, i2; + while ( const SMDS_MeshElement* f = + SMESH_MeshEditor::FindFaceInSet( baseNodes[0], baseNodes[1], + emptySet, avoidSet, &i1, &i2 )) + { + avoidSet.insert( f ); + + // face node other than baseNodes + int otherNodeInd = 0; + while ( otherNodeInd == i1 || otherNodeInd == i2 ) otherNodeInd++; + const SMDS_MeshNode* otherFaceNode = f->GetNode( otherNodeInd ); + + if ( otherFaceNode == nApexI || otherFaceNode == nApexJ ) + continue; // f is a temporary triangle + + // check if f is a base face of either of pyramids + if ( f->NbCornerNodes() == 4 && + ( PrmI->GetNodeIndex( otherFaceNode ) >= 0 || + PrmJ->GetNodeIndex( otherFaceNode ) >= 0 )) + continue; // f is a base quadrangle + + // check projections of face direction (baOFN) to triange normals (nI and nJ) + gp_Vec baOFN( base1, SMESH_TNodeXYZ( otherFaceNode )); + if ( nI * baOFN > 0 && nJ * baOFN > 0 ) + { + tooClose = false; // f is between pyramids + break; + } + } + } + + return tooClose; + } + + //================================================================================ + /*! + * \brief Move medium nodes of merged quadratic pyramids + */ + //================================================================================ + + void UpdateQuadraticPyramids(const set& commonApex, + SMESHDS_Mesh* meshDS) + { + typedef SMDS_StdIterator< const SMDS_MeshElement*, SMDS_ElemIteratorPtr > TStdElemIterator; + TStdElemIterator itEnd; + + // shift of node index to get medium nodes between the 4 base nodes and the apex + const int base2MediumShift = 9; + + set::const_iterator nIt = commonApex.begin(); + for ( ; nIt != commonApex.end(); ++nIt ) + { + SMESH_TNodeXYZ apex( *nIt ); + + vector< const SMDS_MeshElement* > pyrams // pyramids sharing the apex node + ( TStdElemIterator( apex._node->GetInverseElementIterator( SMDSAbs_Volume )), itEnd ); + + // Select medium nodes to keep and medium nodes to remove + + typedef map < const SMDS_MeshNode*, const SMDS_MeshNode*, TIDCompare > TN2NMap; + TN2NMap base2medium; // to keep + vector< const SMDS_MeshNode* > nodesToRemove; + + for ( unsigned i = 0; i < pyrams.size(); ++i ) + for ( int baseIndex = 0; baseIndex < PYRAM_APEX; ++baseIndex ) + { + SMESH_TNodeXYZ base = pyrams[i]->GetNode( baseIndex ); + const SMDS_MeshNode* medium = pyrams[i]->GetNode( baseIndex + base2MediumShift ); + TN2NMap::iterator b2m = base2medium.insert( make_pair( base._node, medium )).first; + if ( b2m->second != medium ) + { + nodesToRemove.push_back( medium ); + } + else + { + // move the kept medium node + gp_XYZ newXYZ = 0.5 * ( apex + base ); + meshDS->MoveNode( medium, newXYZ.X(), newXYZ.Y(), newXYZ.Z() ); + } + } + + // Within pyramids, replace nodes to remove by nodes to keep + + for ( unsigned i = 0; i < pyrams.size(); ++i ) + { + vector< const SMDS_MeshNode* > nodes( pyrams[i]->begin_nodes(), + pyrams[i]->end_nodes() ); + for ( int baseIndex = 0; baseIndex < PYRAM_APEX; ++baseIndex ) + { + const SMDS_MeshNode* base = pyrams[i]->GetNode( baseIndex ); + nodes[ baseIndex + base2MediumShift ] = base2medium[ base ]; + } + meshDS->ChangeElementNodes( pyrams[i], &nodes[0], nodes.size()); + } + + // Remove the replaced nodes + + if ( !nodesToRemove.empty() ) + { + SMESHDS_SubMesh * sm = meshDS->MeshElements( nodesToRemove[0]->getshapeId() ); + for ( unsigned i = 0; i < nodesToRemove.size(); ++i ) + meshDS->RemoveFreeNode( nodesToRemove[i], sm, /*fromGroups=*/false); + } + } + } + } +//================================================================================ +/*! + * \brief Merge the two pyramids (i.e. fuse their apex) and others already merged with them + */ +//================================================================================ + +void StdMeshers_QuadToTriaAdaptor::MergePiramids( const SMDS_MeshElement* PrmI, + const SMDS_MeshElement* PrmJ, + set & nodesToMove) +{ + const SMDS_MeshNode* Nrem = PrmJ->GetNode(4); // node to remove + //int nbJ = Nrem->NbInverseElements( SMDSAbs_Volume ); + SMESH_TNodeXYZ Pj( Nrem ); + + // an apex node to make common to all merged pyramids + SMDS_MeshNode* CommonNode = const_cast(PrmI->GetNode(4)); + if ( CommonNode == Nrem ) return; // already merged + //int nbI = CommonNode->NbInverseElements( SMDSAbs_Volume ); + SMESH_TNodeXYZ Pi( CommonNode ); + gp_XYZ Pnew = /*( nbI*Pi + nbJ*Pj ) / (nbI+nbJ);*/ 0.5 * ( Pi + Pj ); + CommonNode->setXYZ( Pnew.X(), Pnew.Y(), Pnew.Z() ); + + nodesToMove.insert( CommonNode ); + nodesToMove.erase ( Nrem ); + + typedef SMDS_StdIterator< const SMDS_MeshElement*, SMDS_ElemIteratorPtr > TStdElemIterator; + TStdElemIterator itEnd; + + // find and remove coincided faces of merged pyramids + vector< const SMDS_MeshElement* > inverseElems + // copy inverse elements to avoid iteration on changing container + ( TStdElemIterator( CommonNode->GetInverseElementIterator(SMDSAbs_Face)), itEnd); + for ( unsigned i = 0; i < inverseElems.size(); ++i ) + { + const SMDS_MeshElement* FI = inverseElems[i]; + const SMDS_MeshElement* FJEqual = 0; + SMDS_ElemIteratorPtr triItJ = Nrem->GetInverseElementIterator(SMDSAbs_Face); + while ( !FJEqual && triItJ->more() ) + { + const SMDS_MeshElement* FJ = triItJ->next(); + if ( EqualTriangles( FJ, FI )) + FJEqual = FJ; + } + if ( FJEqual ) + { + removeTmpElement( FI ); + removeTmpElement( FJEqual ); + myRemovedTrias.insert( FI ); + myRemovedTrias.insert( FJEqual ); + } + } + + // set the common apex node to pyramids and triangles merged with J + inverseElems.assign( TStdElemIterator( Nrem->GetInverseElementIterator()), itEnd ); + for ( unsigned i = 0; i < inverseElems.size(); ++i ) + { + const SMDS_MeshElement* elem = inverseElems[i]; + vector< const SMDS_MeshNode* > nodes( elem->begin_nodes(), elem->end_nodes() ); + nodes[ elem->GetType() == SMDSAbs_Volume ? PYRAM_APEX : TRIA_APEX ] = CommonNode; + GetMeshDS()->ChangeElementNodes( elem, &nodes[0], nodes.size()); + } + ASSERT( Nrem->NbInverseElements() == 0 ); + GetMeshDS()->RemoveFreeNode( Nrem, + GetMeshDS()->MeshElements( Nrem->getshapeId()), + /*fromGroups=*/false); +} + +//================================================================================ +/*! + * \brief Merges adjacent pyramids + */ +//================================================================================ + +void StdMeshers_QuadToTriaAdaptor::MergeAdjacent(const SMDS_MeshElement* PrmI, + set& nodesToMove) +{ + TIDSortedElemSet adjacentPyrams; + bool mergedPyrams = false; + for(int k=0; k<4; k++) // loop on 4 base nodes of PrmI + { + const SMDS_MeshNode* n = PrmI->GetNode(k); + SMDS_ElemIteratorPtr vIt = n->GetInverseElementIterator( SMDSAbs_Volume ); + while ( vIt->more() ) + { + const SMDS_MeshElement* PrmJ = vIt->next(); + if ( PrmJ->NbCornerNodes() != 5 || !adjacentPyrams.insert( PrmJ ).second ) + continue; + if ( PrmI != PrmJ && TooCloseAdjacent( PrmI, PrmJ, GetMesh()->HasShapeToMesh() )) + { + MergePiramids( PrmI, PrmJ, nodesToMove ); + mergedPyrams = true; + // container of inverse elements can change + vIt = n->GetInverseElementIterator( SMDSAbs_Volume ); + } + } + } + if ( mergedPyrams ) + { + TIDSortedElemSet::iterator prm; + for (prm = adjacentPyrams.begin(); prm != adjacentPyrams.end(); ++prm) + MergeAdjacent( *prm, nodesToMove ); + } +} + +//================================================================================ +/*! + * \brief Constructor + */ +//================================================================================ + +StdMeshers_QuadToTriaAdaptor::StdMeshers_QuadToTriaAdaptor(): + myElemSearcher(0) +{ +} //================================================================================ /*! @@ -62,39 +373,47 @@ StdMeshers_QuadToTriaAdaptor::StdMeshers_QuadToTriaAdaptor() //================================================================================ StdMeshers_QuadToTriaAdaptor::~StdMeshers_QuadToTriaAdaptor() -{} - +{ + // temporary faces are deleted by ~SMESH_ProxyMesh() + if ( myElemSearcher ) delete myElemSearcher; + myElemSearcher=0; +} //======================================================================= //function : FindBestPoint -//purpose : Auxilare for Compute() +//purpose : Return a point P laying on the line (PC,V) so that triangle +// (P, P1, P2) to be equilateral as much as possible // V - normal to (P1,P2,PC) //======================================================================= + static gp_Pnt FindBestPoint(const gp_Pnt& P1, const gp_Pnt& P2, const gp_Pnt& PC, const gp_Vec& V) { - double a = P1.Distance(P2); - double b = P1.Distance(PC); - double c = P2.Distance(PC); + gp_Pnt Pbest = PC; + const double a = P1.Distance(P2); + const double b = P1.Distance(PC); + const double c = P2.Distance(PC); if( a < (b+c)/2 ) - return PC; - else { - // find shift along V in order to a became equal to (b+c)/2 - double shift = sqrt( a*a + (b*b-c*c)*(b*b-c*c)/16/a/a - (b*b+c*c)/2 ); - gp_Dir aDir(V); - gp_Pnt Pbest( PC.X() + aDir.X()*shift, PC.Y() + aDir.Y()*shift, - PC.Z() + aDir.Z()*shift ); return Pbest; + else { + // find shift along V in order a to became equal to (b+c)/2 + const double Vsize = V.Magnitude(); + if ( fabs( Vsize ) > std::numeric_limits::min() ) + { + const double shift = sqrt( a*a + (b*b-c*c)*(b*b-c*c)/16/a/a - (b*b+c*c)/2 ); + Pbest.ChangeCoord() += shift * V.XYZ() / Vsize; + } } + return Pbest; } - //======================================================================= //function : HasIntersection3 //purpose : Auxilare for HasIntersection() // find intersection point between triangle (P1,P2,P3) // and segment [PC,P] //======================================================================= + static bool HasIntersection3(const gp_Pnt& P, const gp_Pnt& PC, gp_Pnt& Pint, const gp_Pnt& P1, const gp_Pnt& P2, const gp_Pnt& P3) { @@ -113,7 +432,7 @@ static bool HasIntersection3(const gp_Pnt& P, const gp_Pnt& PC, gp_Pnt& Pint, return false; if( IAICQ.NbPoints() == 1 ) { gp_Pnt PIn = IAICQ.Point(1); - double preci = 1.e-6; + const double preci = 1.e-10 * P.Distance(PC); // check if this point is internal for segment [PC,P] bool IsExternal = ( (PC.X()-PIn.X())*(P.X()-PIn.X()) > preci ) || @@ -126,32 +445,34 @@ static bool HasIntersection3(const gp_Pnt& P, const gp_Pnt& PC, gp_Pnt& Pint, gp_Vec V1(PIn,P1); gp_Vec V2(PIn,P2); gp_Vec V3(PIn,P3); - if( V1.Magnitude()(myElemSearcher); + + //SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); //cout<<" CheckIntersection: meshDS->NbFaces() = "<NbFaces()<MeshElements(aShapeFace); - if ( aSubMeshDSFace ) { - SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements(); - while ( iteratorElem->more() ) { // loop on elements on a face - const SMDS_MeshElement* face = iteratorElem->next(); - Handle(TColgp_HSequenceOfPnt) aContour = new TColgp_HSequenceOfPnt; - SMDS_ElemIteratorPtr nodeIt = face->nodesIterator(); - if( !face->IsQuadratic() ) { - while ( nodeIt->more() ) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - } - } - else { - int nn = 0; - while ( nodeIt->more() ) { - nn++; - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - if(nn==face->NbNodes()/2) break; - } - } - if( HasIntersection(P, PC, Pres, aContour) ) { - res = true; - double tmp = PC.Distance(Pres); - if(tmp suspectElems; + searcher->GetElementsNearLine( line, SMDSAbs_Face, suspectElems); + + for ( int i = 0; i < suspectElems.size(); ++i ) + { + const SMDS_MeshElement* face = suspectElems[i]; + if ( face == NotCheckedFace ) continue; + Handle(TColgp_HSequenceOfPnt) aContour = new TColgp_HSequenceOfPnt; + for ( int i = 0; i < face->NbCornerNodes(); ++i ) + aContour->Append( SMESH_TNodeXYZ( face->GetNode(i) )); + if( HasIntersection(P, PC, Pres, aContour) ) { + res = true; + double tmp = PC.Distance(Pres); + if(tmpnodesIterator(); - const SMDS_MeshNode* Ns1[3]; - int k = 0; - while( nIt->more() ) { - Ns1[k] = static_cast( nIt->next() ); - k++; - } - nIt = F2->nodesIterator(); - const SMDS_MeshNode* Ns2[3]; - k = 0; - while( nIt->more() ) { - Ns2[k] = static_cast( nIt->next() ); - k++; - } - if( ( Ns1[1]==Ns2[1] && Ns1[2]==Ns2[2] ) || - ( Ns1[1]==Ns2[2] && Ns1[2]==Ns2[1] ) ) - return true; - return false; -} - - -//======================================================================= -//function : IsDegenarate -//purpose : Auxilare for Preparation() -//======================================================================= -static int IsDegenarate(const Handle(TColgp_HArray1OfPnt)& PN) +int StdMeshers_QuadToTriaAdaptor::Preparation(const SMDS_MeshElement* face, + Handle(TColgp_HArray1OfPnt)& PN, + Handle(TColgp_HArray1OfVec)& VN, + vector& FNodes, + gp_Pnt& PC, + gp_Vec& VNorm, + const SMDS_MeshElement** volumes) { - int i = 1; - for(; i<4; i++) { - int j = i+1; - for(; j<=4; j++) { - if( PN->Value(i).Distance(PN->Value(j)) < 1.e-6 ) - return j; - } + if( face->NbCornerNodes() != 4 ) + { + return NOT_QUAD; } - return 0; -} - -//======================================================================= -//function : Preparation -//purpose : Auxilare for Compute() -// : Return 0 if given face is not quad, -// 1 if given face is quad, -// 2 if given face is degenerate quad (two nodes are coincided) -//======================================================================= -int StdMeshers_QuadToTriaAdaptor::Preparation(const SMDS_MeshElement* face, - Handle(TColgp_HArray1OfPnt) PN, - Handle(TColgp_HArray1OfVec) VN, - std::vector& FNodes, - gp_Pnt& PC, gp_Vec& VNorm) -{ int i = 0; - double xc=0., yc=0., zc=0.; - SMDS_ElemIteratorPtr nodeIt = face->nodesIterator(); - if( !face->IsQuadratic() ) { - if( face->NbNodes() != 4 ) - return 0; - while ( nodeIt->more() ) { - i++; - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - FNodes[i-1] = node; - PN->SetValue( i, gp_Pnt(node->X(), node->Y(), node->Z()) ); - xc += node->X(); - yc += node->Y(); - zc += node->Z(); - } - } - else { - if( face->NbNodes() != 8) - return 0; - while ( nodeIt->more() ) { - i++; - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - FNodes[i-1] = node; - PN->SetValue( i, gp_Pnt(node->X(), node->Y(), node->Z()) ); - xc += node->X(); - yc += node->Y(); - zc += node->Z(); - if(i==4) break; - } + gp_XYZ xyzC(0., 0., 0.); + for ( i = 0; i < 4; ++i ) + { + gp_XYZ p = SMESH_TNodeXYZ( FNodes[i] = face->GetNode(i) ); + PN->SetValue( i+1, p ); + xyzC += p; } + PC = xyzC/4; int nbp = 4; @@ -362,7 +627,7 @@ int StdMeshers_QuadToTriaAdaptor::Preparation(const SMDS_MeshElement* face, hasdeg = true; gp_Pnt Pdeg = PN->Value(i); - std::list< const SMDS_MeshNode* >::iterator itdg = myDegNodes.begin(); + list< const SMDS_MeshNode* >::iterator itdg = myDegNodes.begin(); const SMDS_MeshNode* DegNode = 0; for(; itdg!=myDegNodes.end(); itdg++) { const SMDS_MeshNode* N = (*itdg); @@ -385,24 +650,15 @@ int StdMeshers_QuadToTriaAdaptor::Preparation(const SMDS_MeshElement* face, FNodes[i-1] = FNodes[i]; } nbp = 3; - //PC = gp_Pnt( PN->Value(1).X() + PN.Value } - PC = gp_Pnt(xc/4., yc/4., zc/4.); - //cout<<" PC("<SetValue(5,PN->Value(1)); PN->SetValue(nbp+1,PN->Value(1)); - //FNodes[4] = FNodes[0]; FNodes[nbp] = FNodes[0]; // find normal direction - //gp_Vec V1(PC,PN->Value(4)); gp_Vec V1(PC,PN->Value(nbp)); gp_Vec V2(PC,PN->Value(1)); VNorm = V1.Crossed(V2); - //VN->SetValue(4,VNorm); VN->SetValue(nbp,VNorm); - //for(i=1; i<4; i++) { for(i=1; iValue(i)); V2 = gp_Vec(PC,PN->Value(i+1)); @@ -410,9 +666,37 @@ int StdMeshers_QuadToTriaAdaptor::Preparation(const SMDS_MeshElement* face, VN->SetValue(i,Vtmp); VNorm += Vtmp; } + + // find volumes sharing the face + if ( volumes ) + { + volumes[0] = volumes[1] = 0; + SMDS_ElemIteratorPtr vIt = FNodes[0]->GetInverseElementIterator( SMDSAbs_Volume ); + while ( vIt->more() ) + { + const SMDS_MeshElement* vol = vIt->next(); + bool volSharesAllNodes = true; + for ( int i = 1; i < face->NbNodes() && volSharesAllNodes; ++i ) + volSharesAllNodes = ( vol->GetNodeIndex( FNodes[i] ) >= 0 ); + if ( volSharesAllNodes ) + volumes[ volumes[0] ? 1 : 0 ] = vol; + // we could additionally check that vol has all FNodes in its one face using SMDS_VolumeTool + } + // define volume position relating to the face normal + if ( volumes[0] ) + { + // get volume gc + SMDS_ElemIteratorPtr nodeIt = volumes[0]->nodesIterator(); + gp_XYZ volGC(0,0,0); + volGC = accumulate( TXyzIterator(nodeIt), TXyzIterator(), volGC ) / volumes[0]->NbNodes(); + + if ( VNorm * gp_Vec( PC, volGC ) < 0 ) + swap( volumes[0], volumes[1] ); + } + } + //cout<<" VNorm("< myPyramids; + + SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + SMESH_MesherHelper helper(aMesh); + helper.IsQuadraticSubMesh(aShape); + helper.SetElementsOnShape( true ); + + if ( myElemSearcher ) delete myElemSearcher; + if ( aProxyMesh ) + myElemSearcher = SMESH_MeshEditor(&aMesh).GetElementSearcher( aProxyMesh->GetFaces(aShape)); + else + myElemSearcher = SMESH_MeshEditor(&aMesh).GetElementSearcher(); + + const SMESHDS_SubMesh * aSubMeshDSFace; + Handle(TColgp_HArray1OfPnt) PN = new TColgp_HArray1OfPnt(1,5); + Handle(TColgp_HArray1OfVec) VN = new TColgp_HArray1OfVec(1,4); + vector FNodes(5); + gp_Pnt PC; + gp_Vec VNorm; + + for (TopExp_Explorer exp(aShape,TopAbs_FACE);exp.More();exp.Next()) + { const TopoDS_Shape& aShapeFace = exp.Current(); - const SMESHDS_SubMesh * aSubMeshDSFace = meshDS->MeshElements( aShapeFace ); - if ( aSubMeshDSFace ) { - bool isRev = SMESH_Algo::IsReversedSubMesh( TopoDS::Face(aShapeFace), meshDS ); + if ( aProxyMesh ) + aSubMeshDSFace = aProxyMesh->GetSubMesh( aShapeFace ); + else + aSubMeshDSFace = meshDS->MeshElements( aShapeFace ); + + vector trias, quads; + bool hasNewTrias = false; + + if ( aSubMeshDSFace ) + { + bool isRev = false; + if ( helper.NbAncestors( aShapeFace, aMesh, aShape.ShapeType() ) > 1 ) + isRev = SMESH_Algo::IsReversedSubMesh( TopoDS::Face(aShapeFace), meshDS ); SMDS_ElemIteratorPtr iteratorElem = aSubMeshDSFace->GetElements(); - while ( iteratorElem->more() ) { // loop on elements on a face + while ( iteratorElem->more() ) // loop on elements on a geometrical face + { const SMDS_MeshElement* face = iteratorElem->next(); - //cout<GetID() = "<GetID()< FNodes(5); - gp_Pnt PC; - gp_Vec VNorm; - int stat = Preparation(face, PN, VN, FNodes, PC, VNorm); - if(stat==0) - continue; + // preparation step to get face info + int stat = Preparation(face, PN, VN, FNodes, PC, VNorm); + switch ( stat ) + { + case NOT_QUAD: - if(stat==2) { - // degenerate face - // add triangles to result map - std::list aList; - SMDS_FaceOfNodes* NewFace; - if(!isRev) - NewFace = new SMDS_FaceOfNodes( FNodes[0], FNodes[1], FNodes[2] ); - else - NewFace = new SMDS_FaceOfNodes( FNodes[0], FNodes[2], FNodes[1] ); - aList.push_back(NewFace); - myResMap.insert(make_pair(face,aList)); - continue; - } + trias.push_back( face ); + break; - if(!isRev) VNorm.Reverse(); - double xc = 0., yc = 0., zc = 0.; - int i = 1; - for(; i<=4; i++) { - gp_Pnt Pbest; - if(!isRev) - Pbest = FindBestPoint(PN->Value(i), PN->Value(i+1), PC, VN->Value(i).Reversed()); - else - Pbest = FindBestPoint(PN->Value(i), PN->Value(i+1), PC, VN->Value(i)); - xc += Pbest.X(); - yc += Pbest.Y(); - zc += Pbest.Z(); - } - gp_Pnt PCbest(xc/4., yc/4., zc/4.); - - // check PCbest - double height = PCbest.Distance(PC); - if(height<1.e-6) { - // create new PCbest using a bit shift along VNorm - PCbest = gp_Pnt( PC.X() + VNorm.X()*0.001, - PC.Y() + VNorm.Y()*0.001, - PC.Z() + VNorm.Z()*0.001); - } - else { - // check possible intersection with other faces - gp_Pnt Pint; - bool check = CheckIntersection(PCbest, PC, Pint, aMesh, aShape, aShapeFace); - if(check) { - //cout<<"--PC("<AddFace( FNodes[0], FNodes[1], FNodes[2] ); + else + NewFace = meshDS->AddFace( FNodes[0], FNodes[2], FNodes[1] ); + storeTmpElement( NewFace ); + trias.push_back ( NewFace ); + quads.push_back( face ); + hasNewTrias = true; + break; } - else { - gp_Vec VB(PC,PCbest); - gp_Pnt PCbestTmp(PC.X()+VB.X()*3, PC.X()+VB.X()*3, PC.X()+VB.X()*3); - bool check = CheckIntersection(PCbestTmp, PC, Pint, aMesh, aShape, aShapeFace); - if(check) { - double dist = PC.Distance(Pint)/3.; - if(distValue(i), PN->Value(i+1), PC, VN->Value(i).Reversed()); + else + Pbest = FindBestPoint(PN->Value(i), PN->Value(i+1), PC, VN->Value(i)); + xc += Pbest.X(); + yc += Pbest.Y(); + zc += Pbest.Z(); + } + gp_Pnt PCbest(xc/4., yc/4., zc/4.); + + // check PCbest + double height = PCbest.Distance(PC); + if(height<1.e-6) { + // create new PCbest using a bit shift along VNorm + PCbest = PC.XYZ() + VNorm.XYZ() * 0.001; + } + else { + // check possible intersection with other faces + gp_Pnt Pint; + bool check = CheckIntersection(PCbest, PC, Pint, aMesh, aShape, face); + if(check) { + //cout<<"--PC("<AddNode( PCbest.X(), PCbest.Y(), PCbest.Z() ); - // add triangles to result map - std::list aList; - for(i=0; i<4; i++) { - SMDS_FaceOfNodes* NewFace = new SMDS_FaceOfNodes( NewNode, FNodes[i], FNodes[i+1] ); - aList.push_back(NewFace); + // create node for PCbest + SMDS_MeshNode* NewNode = helper.AddNode( PCbest.X(), PCbest.Y(), PCbest.Z() ); + + // add triangles to result map + for(i=0; i<4; i++) + { + trias.push_back ( meshDS->AddFace( NewNode, FNodes[i], FNodes[i+1] )); + storeTmpElement( trias.back() ); + } + // create a pyramid + if ( isRev ) swap( FNodes[1], FNodes[3]); + SMDS_MeshVolume* aPyram = + helper.AddVolume( FNodes[0], FNodes[1], FNodes[2], FNodes[3], NewNode ); + myPyramids.push_back(aPyram); + + quads.push_back( face ); + hasNewTrias = true; + break; + + } // case QUAD: + + } // switch ( stat ) + } // end loop on elements on a face submesh + + bool sourceSubMeshIsProxy = false; + if ( aProxyMesh ) + { + // move proxy sub-mesh from other proxy mesh to this + sourceSubMeshIsProxy = takeProxySubMesh( aShapeFace, aProxyMesh ); + // move also tmp elements added in mesh + takeTmpElemsInMesh( aProxyMesh ); + } + if ( hasNewTrias ) + { + SMESH_ProxyMesh::SubMesh* prxSubMesh = getProxySubMesh( aShapeFace ); + prxSubMesh->ChangeElements( trias.begin(), trias.end() ); + + // delete tmp quadrangles removed from aProxyMesh + if ( sourceSubMeshIsProxy ) + { + for ( unsigned i = 0; i < quads.size(); ++i ) + removeTmpElement( quads[i] ); + + delete myElemSearcher; + myElemSearcher = + SMESH_MeshEditor(&aMesh).GetElementSearcher( aProxyMesh->GetFaces(aShape)); } - myResMap.insert(make_pair(face,aList)); - // create pyramid - SMDS_MeshVolume* aPyram = - meshDS->AddVolume( FNodes[0], FNodes[1], FNodes[2], FNodes[3], NewNode ); - myMapFPyram.insert(make_pair(face,aPyram)); - } // end loop on elements on a face + } } } // end for(TopExp_Explorer exp(aShape,TopAbs_FACE);exp.More();exp.Next()) { - return Compute2ndPart(aMesh); + return Compute2ndPart(aMesh, myPyramids); } - -//======================================================================= -//function : Compute -//purpose : -//======================================================================= +//================================================================================ +/*! + * \brief Computes pyramids in mesh with no shape + */ +//================================================================================ bool StdMeshers_QuadToTriaAdaptor::Compute(SMESH_Mesh& aMesh) { - myResMap.clear(); - myMapFPyram.clear(); + SMESH_ProxyMesh::setMesh( aMesh ); + SMESH_ProxyMesh::_allowedTypes.push_back( SMDSEntity_Triangle ); + SMESH_ProxyMesh::_allowedTypes.push_back( SMDSEntity_Quad_Triangle ); + if ( aMesh.NbQuadrangles() < 1 ) + return false; - SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + vector myPyramids; + SMESH_MesherHelper helper(aMesh); + helper.IsQuadraticSubMesh(aMesh.GetShapeToMesh()); + helper.SetElementsOnShape( true ); - SMDS_FaceIteratorPtr itFace = meshDS->facesIterator(); + if ( !myElemSearcher ) + myElemSearcher = SMESH_MeshEditor(&aMesh).GetElementSearcher(); + SMESH_ElementSearcher* searcher = const_cast(myElemSearcher); - while(itFace->more()) { - const SMDS_MeshElement* face = itFace->next(); + SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + SMESH_ProxyMesh::SubMesh* prxSubMesh = getProxySubMesh(); + + SMDS_FaceIteratorPtr fIt = meshDS->facesIterator(/*idInceasingOrder=*/true); + while( fIt->more()) + { + const SMDS_MeshElement* face = fIt->next(); if ( !face ) continue; - //cout<GetID() = "<GetID()< FNodes(5); + vector FNodes(5); gp_Pnt PC; gp_Vec VNorm; - - int stat = Preparation(face, PN, VN, FNodes, PC, VNorm); - if(stat==0) + const SMDS_MeshElement* volumes[2]; + int what = Preparation(face, PN, VN, FNodes, PC, VNorm, volumes); + if ( what == NOT_QUAD ) continue; + if ( volumes[0] && volumes[1] ) + continue; // face is shared by two volumes - no space for a pyramid - if(stat==2) { + if ( what == DEGEN_QUAD ) + { // degenerate face - // add triangles to result map - std::list aList; - SMDS_FaceOfNodes* NewFace; - // check orientation + // add a triangle to the proxy mesh + SMDS_MeshFace* NewFace; - double tmp = PN->Value(1).Distance(PN->Value(2)) + - PN->Value(2).Distance(PN->Value(3)); - gp_Dir tmpDir(VNorm); - gp_Pnt Ptmp1( PC.X() + tmpDir.X()*tmp*1.e6, - PC.Y() + tmpDir.Y()*tmp*1.e6, - PC.Z() + tmpDir.Z()*tmp*1.e6 ); - gp_Pnt Ptmp2( PC.X() + tmpDir.Reversed().X()*tmp*1.e6, - PC.Y() + tmpDir.Reversed().Y()*tmp*1.e6, - PC.Z() + tmpDir.Reversed().Z()*tmp*1.e6 ); + // check orientation + double tmp = PN->Value(1).Distance(PN->Value(2)) + PN->Value(2).Distance(PN->Value(3)); + // far points in VNorm direction + gp_Pnt Ptmp1 = PC.XYZ() + VNorm.XYZ() * tmp * 1.e6; + gp_Pnt Ptmp2 = PC.XYZ() - VNorm.XYZ() * tmp * 1.e6; // check intersection for Ptmp1 and Ptmp2 bool IsRev = false; bool IsOK1 = false; @@ -586,29 +946,19 @@ bool StdMeshers_QuadToTriaAdaptor::Compute(SMESH_Mesh& aMesh) double dist1 = RealLast(); double dist2 = RealLast(); gp_Pnt Pres1,Pres2; - SMDS_FaceIteratorPtr itf = meshDS->facesIterator(); - while(itf->more()) { - const SMDS_MeshElement* F = itf->next(); + + gp_Ax1 line( PC, VNorm ); + vector< const SMDS_MeshElement* > suspectElems; + searcher->GetElementsNearLine( line, SMDSAbs_Face, suspectElems); + + for ( int iF = 0; iF < suspectElems.size(); ++iF ) { + const SMDS_MeshElement* F = suspectElems[iF]; if(F==face) continue; Handle(TColgp_HSequenceOfPnt) aContour = new TColgp_HSequenceOfPnt; - SMDS_ElemIteratorPtr nodeIt = F->nodesIterator(); - if( !F->IsQuadratic() ) { - while ( nodeIt->more() ) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - } - } - else { - int nn = 0; - while ( nodeIt->more() ) { - nn++; - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - if(nn==face->NbNodes()/2) break; - } - } + for ( int i = 0; i < 4; ++i ) + aContour->Append( SMESH_TNodeXYZ( F->GetNode(i) )); gp_Pnt PPP; - if( HasIntersection(Ptmp1, PC, PPP, aContour) ) { + if( !volumes[0] && HasIntersection(Ptmp1, PC, PPP, aContour) ) { IsOK1 = true; double tmp = PC.Distance(PPP); if(tmpAddFace( FNodes[0], FNodes[1], FNodes[2] ); else - NewFace = new SMDS_FaceOfNodes( FNodes[0], FNodes[2], FNodes[1] ); - aList.push_back(NewFace); - myResMap.insert(make_pair(face,aList)); + NewFace = meshDS->AddFace( FNodes[0], FNodes[2], FNodes[1] ); + storeTmpElement( NewFace ); + prxSubMesh->AddElement( NewFace ); continue; } - - double xc = 0., yc = 0., zc = 0.; + + // Case of non-degenerated quadrangle + + // Find pyramid peak + + gp_XYZ PCbest(0., 0., 0.); // pyramid peak int i = 1; for(; i<=4; i++) { gp_Pnt Pbest = FindBestPoint(PN->Value(i), PN->Value(i+1), PC, VN->Value(i)); - xc += Pbest.X(); - yc += Pbest.Y(); - zc += Pbest.Z(); + PCbest += Pbest.XYZ(); } - gp_Pnt PCbest(xc/4., yc/4., zc/4.); - double height = PCbest.Distance(PC); - if(height<1.e-6) { + PCbest /= 4; + + double height = PC.Distance(PCbest); // pyramid height to precise + if ( height < 1.e-6 ) { // create new PCbest using a bit shift along VNorm - PCbest = gp_Pnt( PC.X() + VNorm.X()*0.001, - PC.Y() + VNorm.Y()*0.001, - PC.Z() + VNorm.Z()*0.001); - height = PCbest.Distance(PC); + PCbest = PC.XYZ() + VNorm.XYZ() * 0.001; + height = PC.Distance(PCbest); + if ( height < std::numeric_limits::min() ) + return false; // batterfly element } - //cout<<" PCbest("<Value(1).Distance(PN->Value(3)) + - PN->Value(2).Distance(PN->Value(4)); - gp_Dir tmpDir(V1); - gp_Pnt Ptmp1( PC.X() + tmpDir.X()*tmp*1.e6, - PC.Y() + tmpDir.Y()*tmp*1.e6, - PC.Z() + tmpDir.Z()*tmp*1.e6 ); - gp_Pnt Ptmp2( PC.X() + tmpDir.Reversed().X()*tmp*1.e6, - PC.Y() + tmpDir.Reversed().Y()*tmp*1.e6, - PC.Z() + tmpDir.Reversed().Z()*tmp*1.e6 ); - // check intersection for Ptmp1 and Ptmp2 - bool IsRev = false; - bool IsOK1 = false; - bool IsOK2 = false; - double dist1 = RealLast(); - double dist2 = RealLast(); - gp_Pnt Pres1,Pres2; - SMDS_FaceIteratorPtr itf = meshDS->facesIterator(); - while(itf->more()) { - const SMDS_MeshElement* F = itf->next(); + + // Restrict pyramid height by intersection with other faces + gp_Vec tmpDir(PC,PCbest); tmpDir.Normalize(); + double tmp = PN->Value(1).Distance(PN->Value(3)) + PN->Value(2).Distance(PN->Value(4)); + // far points: in (PC, PCbest) direction and vice-versa + gp_Pnt farPnt[2] = { PC.XYZ() + tmpDir.XYZ() * tmp * 1.e6, + PC.XYZ() - tmpDir.XYZ() * tmp * 1.e6 }; + // check intersection for farPnt1 and farPnt2 + bool intersected[2] = { false, false }; + double dist [2] = { RealLast(), RealLast() }; + gp_Pnt intPnt[2]; + + gp_Ax1 line( PC, tmpDir ); + vector< const SMDS_MeshElement* > suspectElems; + searcher->GetElementsNearLine( line, SMDSAbs_Face, suspectElems); + + for ( int iF = 0; iF < suspectElems.size(); ++iF ) + { + const SMDS_MeshElement* F = suspectElems[iF]; if(F==face) continue; Handle(TColgp_HSequenceOfPnt) aContour = new TColgp_HSequenceOfPnt; - SMDS_ElemIteratorPtr nodeIt = F->nodesIterator(); - if( !F->IsQuadratic() ) { - while ( nodeIt->more() ) { - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - } - } - else { - int nn = 0; - while ( nodeIt->more() ) { - nn++; - const SMDS_MeshNode* node = static_cast( nodeIt->next() ); - aContour->Append(gp_Pnt(node->X(), node->Y(), node->Z())); - if(nn==face->NbNodes()/2) break; - } - } - gp_Pnt PPP; - if( HasIntersection(Ptmp1, PC, PPP, aContour) ) { - IsOK1 = true; - double tmp = PC.Distance(PPP); - if(tmpNbNodes() / ( F->IsQuadratic() ? 2 : 1 ); + for ( i = 0; i < nbN; ++i ) + aContour->Append( SMESH_TNodeXYZ( F->GetNode(i) )); + gp_Pnt intP; + for ( int isRev = 0; isRev < 2; ++isRev ) + { + if( !volumes[isRev] && HasIntersection(farPnt[isRev], PC, intP, aContour) ) { + intersected[isRev] = true; + double d = PC.Distance( intP ); + if( d < dist[isRev] ) + { + intPnt[isRev] = intP; + dist [isRev] = d; + } } } } - if( IsOK1 && !IsOK2 ) { - // using existed direction - double tmp = PC.Distance(Pres1)/3.; - if( height > tmp ) { - height = tmp; - PCbest = gp_Pnt( PC.X() + tmpDir.X()*height, - PC.Y() + tmpDir.Y()*height, - PC.Z() + tmpDir.Z()*height ); - } - } - else if( !IsOK1 && IsOK2 ) { - // using opposite direction - IsRev = true; - double tmp = PC.Distance(Pres2)/3.; - if( height > tmp ) height = tmp; - PCbest = gp_Pnt( PC.X() + tmpDir.Reversed().X()*height, - PC.Y() + tmpDir.Reversed().Y()*height, - PC.Z() + tmpDir.Reversed().Z()*height ); - } - else { // IsOK1 && IsOK2 - double tmp1 = PC.Distance(Pres1)/3.; - double tmp2 = PC.Distance(Pres2)/3.; - if(tmp1 tmp1 ) { - height = tmp1; - PCbest = gp_Pnt( PC.X() + tmpDir.X()*height, - PC.Y() + tmpDir.Y()*height, - PC.Z() + tmpDir.Z()*height ); - } - } - else { - // using opposite direction - IsRev = true; - if( height > tmp2 ) height = tmp2; - PCbest = gp_Pnt( PC.X() + tmpDir.Reversed().X()*height, - PC.Y() + tmpDir.Reversed().Y()*height, - PC.Z() + tmpDir.Reversed().Z()*height ); - } - } + // Create one or two pyramids + + for ( int isRev = 0; isRev < 2; ++isRev ) + { + if( !intersected[isRev] ) continue; + double pyramidH = Min( height, PC.Distance(intPnt[isRev])/3.); + PCbest = PC.XYZ() + tmpDir.XYZ() * (isRev ? -pyramidH : pyramidH); - // create node for PCbest - SMDS_MeshNode* NewNode = meshDS->AddNode( PCbest.X(), PCbest.Y(), PCbest.Z() ); - // add triangles to result map - std::list aList; - for(i=0; i<4; i++) { - SMDS_FaceOfNodes* NewFace; - if(IsRev) - NewFace = new SMDS_FaceOfNodes( NewNode, FNodes[i], FNodes[i+1] ); + // create node for PCbest + SMDS_MeshNode* NewNode = helper.AddNode( PCbest.X(), PCbest.Y(), PCbest.Z() ); + + // add triangles to result map + for(i=0; i<4; i++) { + SMDS_MeshFace* NewFace; + if(isRev) + NewFace = meshDS->AddFace( NewNode, FNodes[i], FNodes[i+1] ); + else + NewFace = meshDS->AddFace( NewNode, FNodes[i+1], FNodes[i] ); + storeTmpElement( NewFace ); + prxSubMesh->AddElement( NewFace ); + } + // create a pyramid + SMDS_MeshVolume* aPyram; + if(isRev) + aPyram = helper.AddVolume( FNodes[0], FNodes[1], FNodes[2], FNodes[3], NewNode ); else - NewFace = new SMDS_FaceOfNodes( NewNode, FNodes[i+1], FNodes[i] ); - aList.push_back(NewFace); + aPyram = helper.AddVolume( FNodes[0], FNodes[3], FNodes[2], FNodes[1], NewNode ); + myPyramids.push_back(aPyram); } - myResMap.insert(make_pair(face,aList)); - // create pyramid - SMDS_MeshVolume* aPyram; - if(IsRev) - aPyram = meshDS->AddVolume( FNodes[0], FNodes[1], FNodes[2], FNodes[3], NewNode ); - else - aPyram = meshDS->AddVolume( FNodes[0], FNodes[3], FNodes[2], FNodes[1], NewNode ); - myMapFPyram.insert(make_pair(face,aPyram)); - } // end loop on elements on a face + } // end loop on all faces - return Compute2ndPart(aMesh); + return Compute2ndPart(aMesh, myPyramids); } +//================================================================================ +/*! + * \brief Update created pyramids and faces to avoid their intersection + */ +//================================================================================ -//======================================================================= -//function : Compute2ndPart -//purpose : -//======================================================================= - -bool StdMeshers_QuadToTriaAdaptor::Compute2ndPart(SMESH_Mesh& aMesh) +bool StdMeshers_QuadToTriaAdaptor::Compute2ndPart(SMESH_Mesh& aMesh, + const vector& myPyramids) { + if(myPyramids.empty()) + return true; + SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + int i, j, k, myShapeID = myPyramids[0]->GetNode(4)->getshapeId(); - // check intersections between created pyramids - int NbPyram = myMapFPyram.size(); - //cout<<"NbPyram = "<(myElemSearcher); - std::vector< const SMDS_MeshElement* > Pyrams(NbPyram); - std::vector< const SMDS_MeshElement* > Faces(NbPyram); - std::map< const SMDS_MeshElement*, - const SMDS_MeshElement* >::iterator itp = myMapFPyram.begin(); - int i = 0; - for(; itp!=myMapFPyram.end(); itp++, i++) { - Faces[i] = (*itp).first; - Pyrams[i] = (*itp).second; - } - StdMeshers_Array1OfSequenceOfInteger MergesInfo(0,NbPyram-1); - for(i=0; i nodesToMove; + + // check adjacent pyramids + + for ( i = 0; i < myPyramids.size(); ++i ) + { + const SMDS_MeshElement* PrmI = myPyramids[i]; + MergeAdjacent( PrmI, nodesToMove ); } - for(i=0; inodesIterator(); - std::vector Ps1(5); - const SMDS_MeshNode* Ns1[5]; - int k = 0; - while( nIt->more() ) { - const SMDS_MeshNode* node = static_cast( nIt->next() ); - Ns1[k] = node; - Ps1[k] = gp_Pnt(node->X(), node->Y(), node->Z()); - k++; - } - bool NeedMove = false; - for(int j=i+1; jChangeElementNodes(Prm2, Ns2, 5); - // update pyramids for J - for(k=2; k<=nbJ; k++) { - const SMDS_MeshElement* tmpPrm = Pyrams[aMergesJ.Value(k)]; - SMDS_ElemIteratorPtr tmpIt = tmpPrm->nodesIterator(); - const SMDS_MeshNode* Ns[5]; - int m = 0; - while( tmpIt->more() ) { - Ns[m] = static_cast( tmpIt->next() ); - m++; - } - Ns[4] = CommonNode; - meshDS->ChangeElementNodes(tmpPrm, Ns, 5); - } + if ( hasInt ) + { + // count common nodes of base faces of two pyramids + int nbc = 0; + for (k=0; k<4; k++) + nbc += int ( PrmI->GetNodeIndex( PrmJ->GetNode(k) ) >= 0 ); - // update MergesInfo - for(k=1; k<=nbI; k++) { - int num = aMergesI.Value(k); - const TColStd_SequenceOfInteger& aSeq = MergesInfo.Value(num); - TColStd_SequenceOfInteger tmpSeq; - int m = 1; - for(; m<=aSeq.Length(); m++) { - tmpSeq.Append(aSeq.Value(m)); - } - for(m=1; m<=nbJ; m++) { - tmpSeq.Append(aMergesJ.Value(m)); - } - MergesInfo.SetValue(num,tmpSeq); - } - for(k=1; k<=nbJ; k++) { - int num = aMergesJ.Value(k); - const TColStd_SequenceOfInteger& aSeq = MergesInfo.Value(num); - TColStd_SequenceOfInteger tmpSeq; - int m = 1; - for(; m<=aSeq.Length(); m++) { - tmpSeq.Append(aSeq.Value(m)); - } - for(m=1; m<=nbI; m++) { - tmpSeq.Append(aMergesI.Value(m)); - } - MergesInfo.SetValue(num,tmpSeq); - } + if ( nbc == 4 ) + continue; // pyrams have a common base face - // update triangles for aMergesJ - for(k=1; k<=nbJ; k++) { - std::list< std::list< const SMDS_MeshNode* > > aFNodes; - std::list< const SMDS_MeshElement* > aFFaces; - int num = aMergesJ.Value(k); - std::map< const SMDS_MeshElement*, - std::list >::iterator itrm = myResMap.find(Faces[num]); - std::list trias = (*itrm).second; - std::list::iterator itt = trias.begin(); - for(; itt!=trias.end(); itt++) { - int nn = -1; - SMDS_ElemIteratorPtr nodeIt = (*itt)->nodesIterator(); - const SMDS_MeshNode* NF[3]; - while ( nodeIt->more() ) { - nn++; - NF[nn] = static_cast( nodeIt->next() ); - } - NF[0] = CommonNode; - SMDS_FaceOfNodes* Ftria = const_cast< SMDS_FaceOfNodes*>( (*itt) ); - Ftria->ChangeNodes(NF, 3); - } + if(nbc>0) + { + // Merge the two pyramids and others already merged with them + MergePiramids( PrmI, PrmJ, nodesToMove ); } + else { // nbc==0 - // check and remove coincided faces - TColStd_SequenceOfInteger IdRemovedTrias; - int i1 = 1; - for(; i1<=nbI; i1++) { - int numI = aMergesI.Value(i1); - std::map< const SMDS_MeshElement*, - std::list >::iterator itrmI = myResMap.find(Faces[numI]); - std::list triasI = (*itrmI).second; - std::list::iterator ittI = triasI.begin(); - int nbfI = triasI.size(); - std::vector FsI(nbfI); - k = 0; - for(; ittI!=triasI.end(); ittI++) { - FsI[k] = (*ittI); - k++; + // decrease height of pyramids + gp_XYZ PCi(0,0,0), PCj(0,0,0); + for(k=0; k<4; k++) { + PCi += PsI[k].XYZ(); + PCj += PsJ[k].XYZ(); } - int i2 = 0; - for(; i2 >::iterator itrmJ = myResMap.find(Faces[numJ]); - std::list triasJ = (*itrmJ).second; - std::list::iterator ittJ = triasJ.begin(); - int nbfJ = triasJ.size(); - std::vector FsJ(nbfJ); - k = 0; - for(; ittJ!=triasJ.end(); ittJ++) { - FsJ[k] = (*ittJ); - k++; - } - int j2 = 0; - for(; j2GetID() ); - IdRemovedTrias.Append( FJ->GetID() ); - FsI[i2] = 0; - FsJ[j2] = 0; - std::list new_triasI; - for(k=0; k new_triasJ; - for(k=0; kRemoveNode(Nrem); - } - else { // nbc==0 - //cout<<"decrease height of pyramids"<(PrmI->GetNode(4)); + aNode1->setXYZ( PCi.X()+VN1.X(), PCi.Y()+VN1.Y(), PCi.Z()+VN1.Z() ); + SMDS_MeshNode* aNode2 = const_cast(PrmJ->GetNode(4)); + aNode2->setXYZ( PCj.X()+VN2.X(), PCj.Y()+VN2.Y(), PCj.Z()+VN2.Z() ); + nodesToMove.insert( aNode1 ); + nodesToMove.insert( aNode2 ); } - gp_Pnt PC1(xc1/4.,yc1/4.,zc1/4.); - gp_Pnt PC2(xc2/4.,yc2/4.,zc2/4.); - gp_Vec VN1(PC1,Ps1[4]); - gp_Vec VI1(PC1,Pint); - gp_Vec VN2(PC2,Ps2[4]); - gp_Vec VI2(PC2,Pint); - double ang1 = fabs(VN1.Angle(VI1)); - double ang2 = fabs(VN2.Angle(VI2)); - double h1,h2; - if(ang1>PI/3.) - h1 = VI1.Magnitude()/2; - else - h1 = VI1.Magnitude()*cos(ang1); - if(ang2>PI/3.) - h2 = VI2.Magnitude()/2; + // fix intersections that could appear after apex movement + MergeAdjacent( PrmI, nodesToMove ); + MergeAdjacent( PrmJ, nodesToMove ); + + } // end if(hasInt) + } // loop on suspectPyrams + } // loop on 4 base nodes of PrmI + + } // loop on all pyramids + + if( !nodesToMove.empty() && !meshDS->IsEmbeddedMode() ) + { + set::iterator n = nodesToMove.begin(); + for ( ; n != nodesToMove.end(); ++n ) + meshDS->MoveNode( *n, (*n)->X(), (*n)->Y(), (*n)->Z() ); + } + + // move medium nodes of merged quadratic pyramids + if ( myPyramids[0]->IsQuadratic() ) + UpdateQuadraticPyramids( nodesToMove, GetMeshDS() ); + + // erase removed triangles from the proxy mesh + if ( !myRemovedTrias.empty() ) + { + for ( int i = 0; i <= meshDS->MaxShapeIndex(); ++i ) + if ( SMESH_ProxyMesh::SubMesh* sm = findProxySubMesh(i)) + { + vector faces; + faces.reserve( sm->NbElements() ); + SMDS_ElemIteratorPtr fIt = sm->GetElements(); + while ( fIt->more() ) + { + const SMDS_MeshElement* tria = fIt->next(); + set::iterator rmTria = myRemovedTrias.find( tria ); + if ( rmTria != myRemovedTrias.end() ) + myRemovedTrias.erase( rmTria ); else - h2 = VI2.Magnitude()*cos(ang2); - double coef1 = 0.5; - if(ang1(Ns1[4]); - VN1.Scale(coef1); - aNode1->setXYZ( PC1.X()+VN1.X(), PC1.Y()+VN1.Y(), PC1.Z()+VN1.Z() ); - SMDS_MeshNode* aNode2 = const_cast(Ns2[4]); - VN2.Scale(coef2); - aNode2->setXYZ( PC2.X()+VN2.X(), PC2.Y()+VN2.Y(), PC2.Z()+VN2.Z() ); - NeedMove = true; + faces.push_back( tria ); } - } // end if(hasInt) - else { - //cout<<" no intersec for i="< + +#include #include #include -class STDMESHERS_EXPORT StdMeshers_QuadToTriaAdaptor +#include + +/*! + * \brief "Transforms" quadrilateral faces into triangular ones by creation of pyramids + */ +class STDMESHERS_EXPORT StdMeshers_QuadToTriaAdaptor : public SMESH_ProxyMesh { public: - StdMeshers_QuadToTriaAdaptor(); ~StdMeshers_QuadToTriaAdaptor(); - bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + bool Compute(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_ProxyMesh* aProxyMesh=0); bool Compute(SMESH_Mesh& aMesh); - std::list GetTriangles(const SMDS_MeshElement* aFace); + const TopoDS_Shape& GetShape() const { return myShape; } protected: - //bool CheckDegenerate(const SMDS_MeshElement* aFace); - int Preparation(const SMDS_MeshElement* face, - Handle(TColgp_HArray1OfPnt) PN, - Handle(TColgp_HArray1OfVec) VN, + Handle_TColgp_HArray1OfPnt& PN, + Handle_TColgp_HArray1OfVec& VN, std::vector& FNodes, - gp_Pnt& PC, gp_Vec& VNorm); + gp_Pnt& PC, gp_Vec& VNorm, + const SMDS_MeshElement** volumes=0); bool CheckIntersection(const gp_Pnt& P, const gp_Pnt& PC, gp_Pnt& Pint, SMESH_Mesh& aMesh, const TopoDS_Shape& aShape, - const TopoDS_Shape& NotCheckedFace); + const SMDS_MeshElement* NotCheckedFace); - bool Compute2ndPart(SMESH_Mesh& aMesh); + bool Compute2ndPart(SMESH_Mesh& aMesh, + const std::vector& pyramids); - std::map< const SMDS_MeshElement*, std::list > myResMap; - std::map< const SMDS_MeshElement*, const SMDS_MeshElement* > myMapFPyram; - std::list< const SMDS_MeshNode* > myDegNodes; + void MergePiramids( const SMDS_MeshElement* PrmI, + const SMDS_MeshElement* PrmJ, + std::set & nodesToMove); + + void MergeAdjacent(const SMDS_MeshElement* PrmI, + std::set& nodesToMove); + + + TopoDS_Shape myShape; + std::set myRemovedTrias; + std::list< const SMDS_MeshNode* > myDegNodes; + const SMESH_ElementSearcher* myElemSearcher; }; #endif diff --git a/src/StdMeshers/StdMeshers_QuadrangleParams.cxx b/src/StdMeshers/StdMeshers_QuadrangleParams.cxx new file mode 100644 index 000000000..13a7f48ff --- /dev/null +++ b/src/StdMeshers/StdMeshers_QuadrangleParams.cxx @@ -0,0 +1,175 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_QuadrangleParams.cxx +// Author : Sergey KUUL, OCC +// Module : SMESH + +#include "StdMeshers_QuadrangleParams.hxx" + +#include "SMESH_Algo.hxx" +#include "SMESH_Mesh.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +//============================================================================= +/*! + * + */ +//============================================================================= +StdMeshers_QuadrangleParams::StdMeshers_QuadrangleParams(int hypId, int studyId, + SMESH_Gen * gen) + : SMESH_Hypothesis(hypId, studyId, gen) +{ + _name = "QuadrangleParams"; + _param_algo_dim = 2; + _triaVertexID = -1; + _quadType = QUAD_STANDARD; +} + +//============================================================================= +/*! + * + */ +//============================================================================= +StdMeshers_QuadrangleParams::~StdMeshers_QuadrangleParams() +{ +} + +//============================================================================= +/*! + * + */ +//============================================================================= +void StdMeshers_QuadrangleParams::SetTriaVertex (int id) +{ + if (id != _triaVertexID) { + _triaVertexID = id; + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= +void StdMeshers_QuadrangleParams::SetQuadType (StdMeshers_QuadType type) +{ + if (type != _quadType) { + _quadType = type; + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= +ostream & StdMeshers_QuadrangleParams::SaveTo(ostream & save) +{ + if (_objEntry.size() == 0) + save << _triaVertexID << " UNDEFINED " << int(_quadType); + else + save << _triaVertexID << " " << _objEntry << " " << int(_quadType); + return save; +} + +//============================================================================= +/*! + * + */ +//============================================================================= +istream & StdMeshers_QuadrangleParams::LoadFrom(istream & load) +{ + bool isOK = true; + isOK = (load >> _triaVertexID); + if (!isOK) + load.clear(ios::badbit | load.rdstate()); + + isOK = (load >> _objEntry); + if (!isOK) + load.clear(ios::badbit | load.rdstate()); + + int type; + isOK = (load >> type); + if (isOK) + _quadType = StdMeshers_QuadType(type); + + return load; +} + +//============================================================================= +/*! + * + */ +//============================================================================= +ostream & operator <<(ostream & save, StdMeshers_QuadrangleParams & hyp) +{ + return hyp.SaveTo( save ); +} + +//============================================================================= +/*! + * + */ +//============================================================================= +istream & operator >>(istream & load, StdMeshers_QuadrangleParams & hyp) +{ + return hyp.LoadFrom( load ); +} + +//================================================================================ +/*! + * \brief Redifined method + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ +bool StdMeshers_QuadrangleParams::SetParametersByMesh(const SMESH_Mesh* theMesh, + const TopoDS_Shape& theShape) +{ + if ( !theMesh || theShape.IsNull() ) + return false; + + return true; +} + +//================================================================================ +/*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ +//================================================================================ +bool StdMeshers_QuadrangleParams::SetParametersByDefaults(const TDefaults& dflts, + const SMESH_Mesh* /*mesh*/) +{ + return true; +} diff --git a/src/StdMeshers/StdMeshers_QuadrangleParams.hxx b/src/StdMeshers/StdMeshers_QuadrangleParams.hxx new file mode 100644 index 000000000..6bf9c2a22 --- /dev/null +++ b/src/StdMeshers/StdMeshers_QuadrangleParams.hxx @@ -0,0 +1,86 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_QuadrangleParams.hxx +// Author : Sergey KUUL, OCC +// Module : SMESH + +#ifndef _SMESH_QUADRANGLEPARAMS_HXX_ +#define _SMESH_QUADRANGLEPARAMS_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_Hypothesis.hxx" +#include "Utils_SALOME_Exception.hxx" + +enum StdMeshers_QuadType + { + QUAD_STANDARD, + QUAD_TRIANGLE_PREF, + QUAD_QUADRANGLE_PREF, + QUAD_QUADRANGLE_PREF_REVERSED, + QUAD_REDUCED, + QUAD_NB_TYPES + }; + +class STDMESHERS_EXPORT StdMeshers_QuadrangleParams: + public SMESH_Hypothesis +{ +public: + StdMeshers_QuadrangleParams(int hypId, int studyId, SMESH_Gen* gen); + virtual ~StdMeshers_QuadrangleParams(); + + void SetTriaVertex (int id); + int GetTriaVertex() const { return _triaVertexID; } + + void SetObjectEntry (const char* entry) { _objEntry = entry; } + const char* GetObjectEntry() { return _objEntry.c_str(); } + + void SetQuadType (StdMeshers_QuadType type); + StdMeshers_QuadType GetQuadType() const { return _quadType; } + + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + friend std::ostream& operator << (std::ostream & save, + StdMeshers_QuadrangleParams & hyp); + friend std::istream& operator >> (std::istream & load, + StdMeshers_QuadrangleParams & hyp); + + /*! + * \brief Initialize start and end length by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + */ + virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, + const TopoDS_Shape& theShape); + + /*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ + virtual bool SetParametersByDefaults(const TDefaults& dflts, + const SMESH_Mesh* theMesh=0); + +protected: + int _triaVertexID; + std::string _objEntry; + StdMeshers_QuadType _quadType; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_QuadranglePreference.cxx b/src/StdMeshers/StdMeshers_QuadranglePreference.cxx index cf3986e38..8ecb551a5 100644 --- a/src/StdMeshers/StdMeshers_QuadranglePreference.cxx +++ b/src/StdMeshers/StdMeshers_QuadranglePreference.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_QuadranglePreference : implementaion of SMESH idl descriptions // File : StdMeshers_QuadranglePreference.cxx // Module : SMESH - +// #include "StdMeshers_QuadranglePreference.hxx" #include "utilities.h" @@ -40,7 +41,7 @@ StdMeshers_QuadranglePreference::StdMeshers_QuadranglePreference(int hyp :SMESH_Hypothesis(hypId, studyId, gen) { _name = "QuadranglePreference"; - _param_algo_dim = -2; // auxiliary used by StdMeshers_Quadrangle_2D + _param_algo_dim = -2; // auxiliary used by NETGEN 2D } //============================================================================= @@ -75,27 +76,6 @@ istream & StdMeshers_QuadranglePreference::LoadFrom(istream & load) return load; } -//============================================================================= -/*! - * - */ -//============================================================================= - -ostream & operator <<(ostream & save, StdMeshers_QuadranglePreference & hyp) -{ - return hyp.SaveTo( save ); -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -istream & operator >>(istream & load, StdMeshers_QuadranglePreference & hyp) -{ - return hyp.LoadFrom( load ); -} //================================================================================ /*! * \brief Initialize my parameter values by the mesh built on the geometry diff --git a/src/StdMeshers/StdMeshers_QuadranglePreference.hxx b/src/StdMeshers/StdMeshers_QuadranglePreference.hxx index ec641d15a..184072509 100644 --- a/src/StdMeshers/StdMeshers_QuadranglePreference.hxx +++ b/src/StdMeshers/StdMeshers_QuadranglePreference.hxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_QuadranglePreference.hxx // Module : SMESH - +// #ifndef _StdMeshers_QuadranglePreference_HXX_ #define _StdMeshers_QuadranglePreference_HXX_ @@ -45,8 +46,6 @@ class STDMESHERS_EXPORT StdMeshers_QuadranglePreference:public SMESH_Hypothesis virtual std::ostream & SaveTo(std::ostream & save); virtual std::istream & LoadFrom(std::istream & load); - friend std::ostream & operator <<(std::ostream & save, StdMeshers_QuadranglePreference & hyp); - friend std::istream & operator >>(std::istream & load, StdMeshers_QuadranglePreference & hyp); /*! * \brief Initialize my parameter values by the mesh built on the geometry diff --git a/src/StdMeshers/StdMeshers_Quadrangle_2D.cxx b/src/StdMeshers/StdMeshers_Quadrangle_2D.cxx index 3a137b109..3842b4733 100644 --- a/src/StdMeshers/StdMeshers_Quadrangle_2D.cxx +++ b/src/StdMeshers/StdMeshers_Quadrangle_2D.cxx @@ -1,35 +1,35 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_Quadrangle_2D.cxx -// Moved here from SMESH_Quadrangle_2D.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ -// + #include "StdMeshers_Quadrangle_2D.hxx" #include "StdMeshers_FaceSide.hxx" +#include "StdMeshers_QuadrangleParams.hxx" + #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" #include "SMESH_subMesh.hxx" @@ -42,15 +42,17 @@ #include "SMDS_EdgePosition.hxx" #include "SMDS_FacePosition.hxx" -#include -#include #include #include #include #include #include +#include #include #include +#include +#include +#include #include #include "utilities.h" @@ -75,15 +77,17 @@ typedef SMESH_Comment TComm; */ //============================================================================= -StdMeshers_Quadrangle_2D::StdMeshers_Quadrangle_2D (int hypId, int studyId, SMESH_Gen* gen) +StdMeshers_Quadrangle_2D::StdMeshers_Quadrangle_2D (int hypId, int studyId, + SMESH_Gen* gen) : SMESH_2D_Algo(hypId, studyId, gen) { MESSAGE("StdMeshers_Quadrangle_2D::StdMeshers_Quadrangle_2D"); _name = "Quadrangle_2D"; _shapeType = (1 << TopAbs_FACE); + _compatibleHypothesis.push_back("QuadrangleParams"); _compatibleHypothesis.push_back("QuadranglePreference"); _compatibleHypothesis.push_back("TrianglePreference"); - myTool = 0; + myHelper = 0; } //============================================================================= @@ -111,25 +115,75 @@ bool StdMeshers_Quadrangle_2D::CheckHypothesis bool isOk = true; aStatus = SMESH_Hypothesis::HYP_OK; - - const list &hyps = GetUsedHypothesis(aMesh, aShape, false); - const SMESHDS_Hypothesis *theHyp = 0; - - if(hyps.size() > 0){ - theHyp = *hyps.begin(); - if(strcmp("QuadranglePreference", theHyp->GetName()) == 0) { - myQuadranglePreference= true; - myTrianglePreference= false; + const list & hyps = + GetUsedHypothesis(aMesh, aShape, false); + const SMESHDS_Hypothesis * aHyp = 0; + + myTriaVertexID = -1; + myQuadType = QUAD_STANDARD; + myQuadranglePreference = false; + myTrianglePreference = false; + + bool isFirstParams = true; + + // First assigned hypothesis (if any) is processed now + if (hyps.size() > 0) { + aHyp = hyps.front(); + if (strcmp("QuadrangleParams", aHyp->GetName()) == 0) { + const StdMeshers_QuadrangleParams* aHyp1 = + (const StdMeshers_QuadrangleParams*)aHyp; + myTriaVertexID = aHyp1->GetTriaVertex(); + myQuadType = aHyp1->GetQuadType(); + if (myQuadType == QUAD_QUADRANGLE_PREF || + myQuadType == QUAD_QUADRANGLE_PREF_REVERSED) + myQuadranglePreference = true; + else if (myQuadType == QUAD_TRIANGLE_PREF) + myTrianglePreference = true; + } + else if (strcmp("QuadranglePreference", aHyp->GetName()) == 0) { + isFirstParams = false; + myQuadranglePreference = true; + } + else if (strcmp("TrianglePreference", aHyp->GetName()) == 0){ + isFirstParams = false; + myTrianglePreference = true; } - else if(strcmp("TrianglePreference", theHyp->GetName()) == 0){ - myQuadranglePreference= false; - myTrianglePreference= true; + else { + isFirstParams = false; } } - else { - myQuadranglePreference = false; - myTrianglePreference = false; + + // Second(last) assigned hypothesis (if any) is processed now + if (hyps.size() > 1) { + aHyp = hyps.back(); + if (isFirstParams) { + if (strcmp("QuadranglePreference", aHyp->GetName()) == 0) { + myQuadranglePreference = true; + myTrianglePreference = false; + myQuadType = QUAD_STANDARD; + } + else if (strcmp("TrianglePreference", aHyp->GetName()) == 0){ + myQuadranglePreference = false; + myTrianglePreference = true; + myQuadType = QUAD_STANDARD; + } + } + else { + const StdMeshers_QuadrangleParams* aHyp2 = + (const StdMeshers_QuadrangleParams*)aHyp; + myTriaVertexID = aHyp2->GetTriaVertex(); + + if (!myQuadranglePreference && !myTrianglePreference) { // priority of hypos + myQuadType = aHyp2->GetQuadType(); + if (myQuadType == QUAD_QUADRANGLE_PREF || + myQuadType == QUAD_QUADRANGLE_PREF_REVERSED) + myQuadranglePreference = true; + else if (myQuadType == QUAD_TRIANGLE_PREF) + myTrianglePreference = true; + } + } } + return isOk; } @@ -148,17 +202,18 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); aMesh.GetSubMesh(aShape); - SMESH_MesherHelper helper(aMesh); - myTool = &helper; + SMESH_MesherHelper helper (aMesh); + myHelper = &helper; - _quadraticMesh = myTool->IsQuadraticSubMesh(aShape); + _quadraticMesh = myHelper->IsQuadraticSubMesh(aShape); + myNeedSmooth = false; - FaceQuadStruct *quad = CheckNbEdges( aMesh, aShape ); - std::auto_ptr quadDeleter( quad ); // to delete quad at exit from Compute() + FaceQuadStruct *quad = CheckNbEdges(aMesh, aShape); + std::auto_ptr quadDeleter (quad); // to delete quad at exit from Compute() if (!quad) return false; - if(myQuadranglePreference) { + if (myQuadranglePreference) { int n1 = quad->side[0]->NbPoints(); int n2 = quad->side[1]->NbPoints(); int n3 = quad->side[2]->NbPoints(); @@ -166,9 +221,28 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, int nfull = n1+n2+n3+n4; int ntmp = nfull/2; ntmp = ntmp*2; - if( nfull==ntmp && ( (n1!=n3) || (n2!=n4) ) ) { + if (nfull == ntmp && ((n1 != n3) || (n2 != n4))) { // special path for using only quandrangle faces bool ok = ComputeQuadPref(aMesh, aShape, quad); + if ( ok && myNeedSmooth ) + Smooth( quad ); + return ok; + } + } + else if (myQuadType == QUAD_REDUCED) { + int n1 = quad->side[0]->NbPoints(); + int n2 = quad->side[1]->NbPoints(); + int n3 = quad->side[2]->NbPoints(); + int n4 = quad->side[3]->NbPoints(); + int n13 = n1 - n3; + int n24 = n2 - n4; + int n13tmp = n13/2; n13tmp = n13tmp*2; + int n24tmp = n24/2; n24tmp = n24tmp*2; + if ((n1 == n3 && n2 != n4 && n24tmp == n24) || + (n2 == n4 && n1 != n3 && n13tmp == n13)) { + bool ok = ComputeReduced(aMesh, aShape, quad); + if ( ok && myNeedSmooth ) + Smooth( quad ); return ok; } } @@ -193,7 +267,7 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, Handle(Geom_Surface) S = BRep_Tool::Surface(F); // internal mesh nodes - int i, j, geomFaceID = meshDS->ShapeToIndex( F ); + int i, j, geomFaceID = meshDS->ShapeToIndex(F); for (i = 1; i < nbhoriz - 1; i++) { for (j = 1; j < nbvertic - 1; j++) { int ij = j * nbhoriz + i; @@ -237,18 +311,20 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, b = quad->uv_grid[j * nbhoriz + i + 1].node; c = quad->uv_grid[(j + 1) * nbhoriz + i + 1].node; d = quad->uv_grid[(j + 1) * nbhoriz + i].node; - SMDS_MeshFace* face = myTool->AddFace(a, b, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d); + if (face) { + meshDS->SetMeshElementOnShape(face, geomFaceID); + } } } - const vector& uv_e0 = quad->side[0]->GetUVPtStruct(true,0 ); + const vector& uv_e0 = quad->side[0]->GetUVPtStruct(true,0); const vector& uv_e1 = quad->side[1]->GetUVPtStruct(false,1); - const vector& uv_e2 = quad->side[2]->GetUVPtStruct(true,1 ); + const vector& uv_e2 = quad->side[2]->GetUVPtStruct(true,1); const vector& uv_e3 = quad->side[3]->GetUVPtStruct(false,0); - if ( uv_e0.empty() || uv_e1.empty() || uv_e2.empty() || uv_e3.empty() ) - return error( COMPERR_BAD_INPUT_MESH ); + if (uv_e0.empty() || uv_e1.empty() || uv_e2.empty() || uv_e3.empty()) + return error(COMPERR_BAD_INPUT_MESH); double eps = Precision::Confusion(); @@ -312,9 +388,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, } if (near == g) { // make triangle - //SMDS_MeshFace* face = meshDS->AddFace(a, b, c); - SMDS_MeshFace* face = myTool->AddFace(a, b, c); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, b, c); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { // make quadrangle if (near - 1 < ilow) @@ -323,9 +398,9 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = quad->uv_grid[nbhoriz + near - 1].node; //SMDS_MeshFace* face = meshDS->AddFace(a, b, c, d); - if(!myTrianglePreference){ - SMDS_MeshFace* face = myTool->AddFace(a, b, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + if (!myTrianglePreference){ + SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { SplitQuad(meshDS, geomFaceID, a, b, c, d); @@ -339,9 +414,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = uv_e3[1].node; else d = quad->uv_grid[nbhoriz + k - 1].node; - //SMDS_MeshFace* face = meshDS->AddFace(a, c, d); - SMDS_MeshFace* face = myTool->AddFace(a, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } } g = near; @@ -402,9 +476,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, } if (near == g) { // make triangle - //SMDS_MeshFace* face = meshDS->AddFace(a, b, c); - SMDS_MeshFace* face = myTool->AddFace(a, b, c); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, b, c); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { // make quadrangle if (near + 1 > iup) @@ -412,9 +485,9 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, else d = quad->uv_grid[nbhoriz*(nbvertic - 2) + near + 1].node; //SMDS_MeshFace* face = meshDS->AddFace(a, b, c, d); - if(!myTrianglePreference){ - SMDS_MeshFace* face = myTool->AddFace(a, b, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + if (!myTrianglePreference){ + SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { SplitQuad(meshDS, geomFaceID, a, b, c, d); @@ -427,9 +500,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = uv_e1[nbright - 2].node; else d = quad->uv_grid[nbhoriz*(nbvertic - 2) + k + 1].node; - //SMDS_MeshFace* face = meshDS->AddFace(a, c, d); - SMDS_MeshFace* face = myTool->AddFace(a, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } } g = near; @@ -476,9 +548,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, } if (near == g) { // make triangle - //SMDS_MeshFace* face = meshDS->AddFace(a, b, c); - SMDS_MeshFace* face = myTool->AddFace(a, b, c); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, b, c); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { // make quadrangle if (near - 1 < jlow) @@ -487,9 +558,9 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = quad->uv_grid[nbhoriz*near - 2].node; //SMDS_MeshFace* face = meshDS->AddFace(a, b, c, d); - if(!myTrianglePreference){ - SMDS_MeshFace* face = myTool->AddFace(a, b, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + if (!myTrianglePreference){ + SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { SplitQuad(meshDS, geomFaceID, a, b, c, d); @@ -502,9 +573,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = uv_e0[nbdown - 2].node; else d = quad->uv_grid[nbhoriz*k - 2].node; - //SMDS_MeshFace* face = meshDS->AddFace(a, c, d); - SMDS_MeshFace* face = myTool->AddFace(a, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } } g = near; @@ -548,9 +618,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, } if (near == g) { // make triangle - //SMDS_MeshFace* face = meshDS->AddFace(a, b, c); - SMDS_MeshFace* face = myTool->AddFace(a, b, c); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, b, c); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { // make quadrangle if (near + 1 > jup) @@ -558,9 +627,9 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, else d = quad->uv_grid[nbhoriz*(near + 1) + 1].node; //SMDS_MeshFace* face = meshDS->AddFace(a, b, c, d); - if(!myTrianglePreference){ - SMDS_MeshFace* face = myTool->AddFace(a, b, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + if (!myTrianglePreference){ + SMDS_MeshFace* face = myHelper->AddFace(a, b, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } else { SplitQuad(meshDS, geomFaceID, a, b, c, d); @@ -573,9 +642,8 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, d = uv_e2[1].node; else d = quad->uv_grid[nbhoriz*(k + 1) + 1].node; - //SMDS_MeshFace* face = meshDS->AddFace(a, c, d); - SMDS_MeshFace* face = myTool->AddFace(a, c, d); - meshDS->SetMeshElementOnShape(face, geomFaceID); + SMDS_MeshFace* face = myHelper->AddFace(a, c, d); + if (face) meshDS->SetMeshElementOnShape(face, geomFaceID); } } g = near; @@ -584,10 +652,129 @@ bool StdMeshers_Quadrangle_2D::Compute (SMESH_Mesh& aMesh, } } + if ( myNeedSmooth ) + Smooth( quad ); + bool isOk = true; return isOk; } + +//============================================================================= +/*! + * Evaluate + */ +//============================================================================= + +bool StdMeshers_Quadrangle_2D::Evaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + MapShapeNbElems& aResMap) + +{ + aMesh.GetSubMesh(aShape); + + std::vector aNbNodes(4); + bool IsQuadratic = false; + if (!CheckNbEdgesForEvaluate(aMesh, aShape, aResMap, aNbNodes, IsQuadratic)) { + std::vector aResVec(SMDSEntity_Last); + for (int i=SMDSEntity_Node; iGetComputeError(); + smError.reset(new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + + if (myQuadranglePreference) { + int n1 = aNbNodes[0]; + int n2 = aNbNodes[1]; + int n3 = aNbNodes[2]; + int n4 = aNbNodes[3]; + int nfull = n1+n2+n3+n4; + int ntmp = nfull/2; + ntmp = ntmp*2; + if (nfull==ntmp && ((n1!=n3) || (n2!=n4))) { + // special path for using only quandrangle faces + return EvaluateQuadPref(aMesh, aShape, aNbNodes, aResMap, IsQuadratic); + //return true; + } + } + + int nbdown = aNbNodes[0]; + int nbup = aNbNodes[2]; + + int nbright = aNbNodes[1]; + int nbleft = aNbNodes[3]; + + int nbhoriz = Min(nbdown, nbup); + int nbvertic = Min(nbright, nbleft); + + int dh = Max(nbdown, nbup) - nbhoriz; + int dv = Max(nbright, nbleft) - nbvertic; + + //int kdh = 0; + //if (dh>0) kdh = 1; + //int kdv = 0; + //if (dv>0) kdv = 1; + + int nbNodes = (nbhoriz-2)*(nbvertic-2); + //int nbFaces3 = dh + dv + kdh*(nbvertic-1)*2 + kdv*(nbhoriz-1)*2; + int nbFaces3 = dh + dv; + //if (kdh==1 && kdv==1) nbFaces3 -= 2; + //if (dh>0 && dv>0) nbFaces3 -= 2; + //int nbFaces4 = (nbhoriz-1-kdh)*(nbvertic-1-kdv); + int nbFaces4 = (nbhoriz-1)*(nbvertic-1); + + std::vector aVec(SMDSEntity_Last); + for (int i=SMDSEntity_Node; i= TopAbs_INTERNAL ) F.Orientation( TopAbs_FORWARD ); const bool ignoreMediumNodes = _quadraticMesh; // verify 1 wire only, with 4 edges @@ -613,50 +801,157 @@ FaceQuadStruct* StdMeshers_Quadrangle_2D::CheckNbEdges(SMESH_Mesh & aMes FaceQuadStruct* quad = new FaceQuadStruct; quad->uv_grid = 0; quad->side.reserve(nbEdgesInWire.front()); + quad->face = F; int nbSides = 0; list< TopoDS_Edge >::iterator edgeIt = edges.begin(); - if ( nbEdgesInWire.front() == 4 ) { // exactly 4 edges - for ( ; edgeIt != edges.end(); ++edgeIt, nbSides++ ) - quad->side.push_back( new StdMeshers_FaceSide(F, *edgeIt, &aMesh, + if (nbEdgesInWire.front() == 3) // exactly 3 edges + { + SMESH_Comment comment; + SMESHDS_Mesh* meshDS = aMesh.GetMeshDS(); + if (myTriaVertexID == -1) + { + comment << "No Base vertex parameter provided for a trilateral geometrical face"; + } + else + { + TopoDS_Vertex V = TopoDS::Vertex(meshDS->IndexToShape(myTriaVertexID)); + if (!V.IsNull()) { + TopoDS_Edge E1,E2,E3; + for (; edgeIt != edges.end(); ++edgeIt) { + TopoDS_Edge E = *edgeIt; + TopoDS_Vertex VF, VL; + TopExp::Vertices(E, VF, VL, true); + if (VF.IsSame(V)) + E1 = E; + else if (VL.IsSame(V)) + E3 = E; + else + E2 = E; + } + if (!E1.IsNull() && !E2.IsNull() && !E3.IsNull()) + { + quad->side.push_back(new StdMeshers_FaceSide(F, E1, &aMesh, true, ignoreMediumNodes)); + quad->side.push_back(new StdMeshers_FaceSide(F, E2, &aMesh, true, ignoreMediumNodes)); + quad->side.push_back(new StdMeshers_FaceSide(F, E3, &aMesh, false,ignoreMediumNodes)); + const vector& UVPSleft = quad->side[0]->GetUVPtStruct(true,0); + /* vector& UVPStop = */quad->side[1]->GetUVPtStruct(false,1); + /* vector& UVPSright = */quad->side[2]->GetUVPtStruct(true,1); + const SMDS_MeshNode* aNode = UVPSleft[0].node; + gp_Pnt2d aPnt2d(UVPSleft[0].u, UVPSleft[0].v); + quad->side.push_back(new StdMeshers_FaceSide(aNode, aPnt2d, quad->side[1])); + return quad; + } + } + comment << "Invalid Base vertex parameter: " << myTriaVertexID << " is not among ["; + TopTools_MapOfShape vMap; + for (TopExp_Explorer v(aShape, TopAbs_VERTEX); v.More(); v.Next()) + if (vMap.Add(v.Current())) + comment << meshDS->ShapeToIndex(v.Current()) << (vMap.Extent()==3 ? "]" : ", "); + } + error(comment); + delete quad; + return quad = 0; + } + else if (nbEdgesInWire.front() == 4) // exactly 4 edges + { + for (; edgeIt != edges.end(); ++edgeIt, nbSides++) + quad->side.push_back(new StdMeshers_FaceSide(F, *edgeIt, &aMesh, nbSides 4 ) { // more than 4 edges - try to unite some + else if (nbEdgesInWire.front() > 4) // more than 4 edges - try to unite some + { list< TopoDS_Edge > sideEdges; - while ( !edges.empty()) { + vector< int > degenSides; + while (!edges.empty()) { sideEdges.clear(); - sideEdges.splice( sideEdges.end(), edges, edges.begin()); // edges.front() -> sideEdges.end() + sideEdges.splice(sideEdges.end(), edges, edges.begin()); // edges.front() -> sideEdges.end() bool sameSide = true; - while ( !edges.empty() && sameSide ) { - sameSide = SMESH_Algo::IsContinuous( sideEdges.back(), edges.front() ); - if ( sameSide ) - sideEdges.splice( sideEdges.end(), edges, edges.begin()); + while (!edges.empty() && sameSide) { + sameSide = SMESH_Algo::IsContinuous(sideEdges.back(), edges.front()); + if (sameSide) + sideEdges.splice(sideEdges.end(), edges, edges.begin()); } - if ( nbSides == 0 ) { // go backward from the first edge + if (nbSides == 0) { // go backward from the first edge sameSide = true; - while ( !edges.empty() && sameSide ) { - sameSide = SMESH_Algo::IsContinuous( sideEdges.front(), edges.back() ); - if ( sameSide ) - sideEdges.splice( sideEdges.begin(), edges, --edges.end()); + while (!edges.empty() && sameSide) { + sameSide = SMESH_Algo::IsContinuous(sideEdges.front(), edges.back()); + if (sameSide) + sideEdges.splice(sideEdges.begin(), edges, --edges.end()); } } - quad->side.push_back( new StdMeshers_FaceSide(F, sideEdges, &aMesh, + if ( sideEdges.size() == 1 && BRep_Tool::Degenerated( sideEdges.front() )) + degenSides.push_back( nbSides ); + + quad->side.push_back(new StdMeshers_FaceSide(F, sideEdges, &aMesh, nbSidesside.size(); ++i ) + quad->side[i]->Reverse(); + + for ( int i = degenSides.size()-1; i > -1; --i ) + { + StdMeshers_FaceSide* degenSide = quad->side[ degenSides[ i ]]; + delete degenSide; + quad->side.erase( quad->side.begin() + degenSides[ i ] ); + } + for ( unsigned i = TOP_SIDE; i < quad->side.size(); ++i ) + quad->side[i]->Reverse(); + + nbSides -= degenSides.size(); + } + // issue 20222. Try to unite only edges shared by two same faces + if (nbSides < 4) { + // delete found sides + { FaceQuadStruct cleaner(*quad); } + quad->side.clear(); + quad->side.reserve(nbEdgesInWire.front()); + nbSides = 0; + + SMESH_Block::GetOrderedEdges (F, V, edges, nbEdgesInWire); + while (!edges.empty()) { + sideEdges.clear(); + sideEdges.splice(sideEdges.end(), edges, edges.begin()); + bool sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = + SMESH_Algo::IsContinuous(sideEdges.back(), edges.front()) && + twoEdgesMeatAtVertex(sideEdges.back(), edges.front(), aMesh); + if (sameSide) + sideEdges.splice(sideEdges.end(), edges, edges.begin()); + } + if (nbSides == 0) { // go backward from the first edge + sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = + SMESH_Algo::IsContinuous(sideEdges.front(), edges.back()) && + twoEdgesMeatAtVertex(sideEdges.front(), edges.back(), aMesh); + if (sameSide) + sideEdges.splice(sideEdges.begin(), edges, --edges.end()); + } + } + quad->side.push_back(new StdMeshers_FaceSide(F, sideEdges, &aMesh, + nbSidesside[i]->NbEdges(); ++e ) - MESSAGE ( myTool->GetMeshDS()->ShapeToIndex( quad->side[i]->Edge( e )) << " " ); - MESSAGE ( ")\n" ); + MESSAGE ("StdMeshers_Quadrangle_2D. Edge IDs of " << nbSides << " sides:\n"); + for (int i = 0; i < nbSides; ++i) { + MESSAGE (" ("); + for (int e = 0; e < quad->side[i]->NbEdges(); ++e) + MESSAGE (myHelper->GetMeshDS()->ShapeToIndex(quad->side[i]->Edge(e)) << " "); + MESSAGE (")\n"); } //cout << endl; #endif - if ( !nbSides ) + if (!nbSides) nbSides = nbEdgesInWire.front(); error(COMPERR_BAD_SHAPE, TComm("Face must have 4 sides but not ") << nbSides); delete quad; @@ -666,6 +961,193 @@ FaceQuadStruct* StdMeshers_Quadrangle_2D::CheckNbEdges(SMESH_Mesh & aMes return quad; } + +//============================================================================= +/*! + * + */ +//============================================================================= + +bool StdMeshers_Quadrangle_2D::CheckNbEdgesForEvaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap, + std::vector& aNbNodes, + bool& IsQuadratic) + +{ + const TopoDS_Face & F = TopoDS::Face(aShape); + + // verify 1 wire only, with 4 edges + TopoDS_Vertex V; + list< TopoDS_Edge > edges; + list< int > nbEdgesInWire; + int nbWire = SMESH_Block::GetOrderedEdges (F, V, edges, nbEdgesInWire); + if (nbWire != 1) { + return false; + } + + aNbNodes.resize(4); + + int nbSides = 0; + list< TopoDS_Edge >::iterator edgeIt = edges.begin(); + SMESH_subMesh * sm = aMesh.GetSubMesh(*edgeIt); + MapShapeNbElemsItr anIt = aResMap.find(sm); + if (anIt==aResMap.end()) { + return false; + } + std::vector aVec = (*anIt).second; + IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]); + if (nbEdgesInWire.front() == 3) { // exactly 3 edges + if (myTriaVertexID>0) { + SMESHDS_Mesh* meshDS = aMesh.GetMeshDS(); + TopoDS_Vertex V = TopoDS::Vertex(meshDS->IndexToShape(myTriaVertexID)); + if (!V.IsNull()) { + TopoDS_Edge E1,E2,E3; + for (; edgeIt != edges.end(); ++edgeIt) { + TopoDS_Edge E = TopoDS::Edge(*edgeIt); + TopoDS_Vertex VF, VL; + TopExp::Vertices(E, VF, VL, true); + if (VF.IsSame(V)) + E1 = E; + else if (VL.IsSame(V)) + E3 = E; + else + E2 = E; + } + SMESH_subMesh * sm = aMesh.GetSubMesh(E1); + MapShapeNbElemsItr anIt = aResMap.find(sm); + if (anIt==aResMap.end()) return false; + std::vector aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[0] = (aVec[SMDSEntity_Node]-1)/2 + 2; + else + aNbNodes[0] = aVec[SMDSEntity_Node] + 2; + sm = aMesh.GetSubMesh(E2); + anIt = aResMap.find(sm); + if (anIt==aResMap.end()) return false; + aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[1] = (aVec[SMDSEntity_Node]-1)/2 + 2; + else + aNbNodes[1] = aVec[SMDSEntity_Node] + 2; + sm = aMesh.GetSubMesh(E3); + anIt = aResMap.find(sm); + if (anIt==aResMap.end()) return false; + aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[2] = (aVec[SMDSEntity_Node]-1)/2 + 2; + else + aNbNodes[2] = aVec[SMDSEntity_Node] + 2; + aNbNodes[3] = aNbNodes[1]; + aNbNodes.resize(5); + nbSides = 4; + } + } + } + if (nbEdgesInWire.front() == 4) { // exactly 4 edges + for (; edgeIt != edges.end(); edgeIt++) { + SMESH_subMesh * sm = aMesh.GetSubMesh(*edgeIt); + MapShapeNbElemsItr anIt = aResMap.find(sm); + if (anIt==aResMap.end()) { + return false; + } + std::vector aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[nbSides] = (aVec[SMDSEntity_Node]-1)/2 + 2; + else + aNbNodes[nbSides] = aVec[SMDSEntity_Node] + 2; + nbSides++; + } + } + else if (nbEdgesInWire.front() > 4) { // more than 4 edges - try to unite some + list< TopoDS_Edge > sideEdges; + while (!edges.empty()) { + sideEdges.clear(); + sideEdges.splice(sideEdges.end(), edges, edges.begin()); // edges.front() -> sideEdges.end() + bool sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = SMESH_Algo::IsContinuous(sideEdges.back(), edges.front()); + if (sameSide) + sideEdges.splice(sideEdges.end(), edges, edges.begin()); + } + if (nbSides == 0) { // go backward from the first edge + sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = SMESH_Algo::IsContinuous(sideEdges.front(), edges.back()); + if (sameSide) + sideEdges.splice(sideEdges.begin(), edges, --edges.end()); + } + } + list::iterator ite = sideEdges.begin(); + aNbNodes[nbSides] = 1; + for (; ite!=sideEdges.end(); ite++) { + SMESH_subMesh * sm = aMesh.GetSubMesh(*ite); + MapShapeNbElemsItr anIt = aResMap.find(sm); + if (anIt==aResMap.end()) { + return false; + } + std::vector aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[nbSides] += (aVec[SMDSEntity_Node]-1)/2 + 1; + else + aNbNodes[nbSides] += aVec[SMDSEntity_Node] + 1; + } + ++nbSides; + } + // issue 20222. Try to unite only edges shared by two same faces + if (nbSides < 4) { + nbSides = 0; + SMESH_Block::GetOrderedEdges (F, V, edges, nbEdgesInWire); + while (!edges.empty()) { + sideEdges.clear(); + sideEdges.splice(sideEdges.end(), edges, edges.begin()); + bool sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = + SMESH_Algo::IsContinuous(sideEdges.back(), edges.front()) && + twoEdgesMeatAtVertex(sideEdges.back(), edges.front(), aMesh); + if (sameSide) + sideEdges.splice(sideEdges.end(), edges, edges.begin()); + } + if (nbSides == 0) { // go backward from the first edge + sameSide = true; + while (!edges.empty() && sameSide) { + sameSide = + SMESH_Algo::IsContinuous(sideEdges.front(), edges.back()) && + twoEdgesMeatAtVertex(sideEdges.front(), edges.back(), aMesh); + if (sameSide) + sideEdges.splice(sideEdges.begin(), edges, --edges.end()); + } + } + list::iterator ite = sideEdges.begin(); + aNbNodes[nbSides] = 1; + for (; ite!=sideEdges.end(); ite++) { + SMESH_subMesh * sm = aMesh.GetSubMesh(*ite); + MapShapeNbElemsItr anIt = aResMap.find(sm); + if (anIt==aResMap.end()) { + return false; + } + std::vector aVec = (*anIt).second; + if (IsQuadratic) + aNbNodes[nbSides] += (aVec[SMDSEntity_Node]-1)/2 + 1; + else + aNbNodes[nbSides] += aVec[SMDSEntity_Node] + 1; + } + ++nbSides; + } + } + } + if (nbSides != 4) { + if (!nbSides) + nbSides = nbEdgesInWire.front(); + error(COMPERR_BAD_SHAPE, TComm("Face must have 4 sides but not ") << nbSides); + return false; + } + + return true; +} + + //============================================================================= /*! * CheckAnd2Dcompute @@ -681,13 +1163,12 @@ FaceQuadStruct *StdMeshers_Quadrangle_2D::CheckAnd2Dcompute FaceQuadStruct *quad = CheckNbEdges(aMesh, aShape); - if(!quad) return 0; + if (!quad) return 0; // set normalized grid on unit square in parametric domain bool stat = SetNormalizedGrid(aMesh, aShape, quad); - if(!stat) { - if(!quad) - delete quad; + if (!stat) { + if (quad) delete quad; quad = 0; } @@ -711,13 +1192,21 @@ faceQuadStruct::~faceQuadStruct() namespace { inline const vector& GetUVPtStructIn(FaceQuadStruct* quad, int i, int nbSeg) { - bool isXConst = ( i == BOTTOM_SIDE || i == TOP_SIDE ); - double constValue = ( i == BOTTOM_SIDE || i == LEFT_SIDE ) ? 0 : 1; + bool isXConst = (i == BOTTOM_SIDE || i == TOP_SIDE); + double constValue = (i == BOTTOM_SIDE || i == LEFT_SIDE) ? 0 : 1; return quad->isEdgeOut[i] ? quad->side[i]->SimulateUVPtStruct(nbSeg,isXConst,constValue) : quad->side[i]->GetUVPtStruct(isXConst,constValue); } + inline gp_UV CalcUV(double x, double y, + const gp_UV& a0,const gp_UV& a1,const gp_UV& a2,const gp_UV& a3, + const gp_UV& p0,const gp_UV& p1,const gp_UV& p2,const gp_UV& p3) + { + return + ((1 - y) * p0 + x * p1 + y * p2 + (1 - x) * p3 ) - + ((1 - x) * (1 - y) * a0 + x * (1 - y) * a1 + x * y * a2 + (1 - x) * y * a3); + } } //============================================================================= @@ -730,10 +1219,10 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, const TopoDS_Shape& aShape, FaceQuadStruct* & quad) //throw (SALOME_Exception) { - // Algorithme décrit dans "Génération automatique de maillages" - // P.L. GEORGE, MASSON, § 6.4.1 p. 84-85 - // traitement dans le domaine paramétrique 2d u,v - // transport - projection sur le carré unité + // Algorithme décrit dans "Génération automatique de maillages" + // P.L. GEORGE, MASSON, § 6.4.1 p. 84-85 + // traitement dans le domaine paramétrique 2d u,v + // transport - projection sur le carré unité // MESSAGE("StdMeshers_Quadrangle_2D::SetNormalizedGrid"); // const TopoDS_Face& F = TopoDS::Face(aShape); @@ -764,14 +1253,17 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, UVPtStruct *uv_grid = quad->uv_grid = new UVPtStruct[nbvertic * nbhoriz]; - const vector& uv_e0 = GetUVPtStructIn( quad, 0, nbhoriz - 1 ); - const vector& uv_e1 = GetUVPtStructIn( quad, 1, nbvertic - 1 ); - const vector& uv_e2 = GetUVPtStructIn( quad, 2, nbhoriz - 1 ); - const vector& uv_e3 = GetUVPtStructIn( quad, 3, nbvertic - 1 ); + const vector& uv_e0 = GetUVPtStructIn(quad, 0, nbhoriz - 1); + const vector& uv_e1 = GetUVPtStructIn(quad, 1, nbvertic - 1); + const vector& uv_e2 = GetUVPtStructIn(quad, 2, nbhoriz - 1); + const vector& uv_e3 = GetUVPtStructIn(quad, 3, nbvertic - 1); - if ( uv_e0.empty() || uv_e1.empty() || uv_e2.empty() || uv_e3.empty() ) - //return error( "Can't find nodes on sides"); - return error( COMPERR_BAD_INPUT_MESH ); + if (uv_e0.empty() || uv_e1.empty() || uv_e2.empty() || uv_e3.empty()) + //return error("Can't find nodes on sides"); + return error(COMPERR_BAD_INPUT_MESH); + + if ( myNeedSmooth ) + UpdateDegenUV( quad ); // nodes Id on "in" edges if (! quad->isEdgeOut[0]) { @@ -804,17 +1296,15 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, } // normalized 2d values on grid - for (int i = 0; i < nbhoriz; i++) - { - for (int j = 0; j < nbvertic; j++) - { + for (int i = 0; i < nbhoriz; i++) { + for (int j = 0; j < nbvertic; j++) { int ij = j * nbhoriz + i; // --- droite i cste : x = x0 + y(x1-x0) - double x0 = uv_e0[i].normParam; // bas - sud - double x1 = uv_e2[i].normParam; // haut - nord + double x0 = uv_e0[i].normParam; // bas - sud + double x1 = uv_e2[i].normParam; // haut - nord // --- droite j cste : y = y0 + x(y1-y0) - double y0 = uv_e3[j].normParam; // gauche-ouest - double y1 = uv_e1[j].normParam; // droite - est + double y0 = uv_e3[j].normParam; // gauche-ouest + double y1 = uv_e1[j].normParam; // droite - est // --- intersection : x=x0+(y0+x(y1-y0))(x1-x0) double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0)); double y = y0 + x * (y1 - y0); @@ -826,15 +1316,13 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, } // 4 --- projection on 2d domain (u,v) - gp_UV a0( uv_e0.front().u, uv_e0.front().v ); - gp_UV a1( uv_e0.back().u, uv_e0.back().v ); - gp_UV a2( uv_e2.back().u, uv_e2.back().v ); - gp_UV a3( uv_e2.front().u, uv_e2.front().v ); + gp_UV a0(uv_e0.front().u, uv_e0.front().v); + gp_UV a1(uv_e0.back().u, uv_e0.back().v); + gp_UV a2(uv_e2.back().u, uv_e2.back().v); + gp_UV a3(uv_e2.front().u, uv_e2.front().v); - for (int i = 0; i < nbhoriz; i++) - { - for (int j = 0; j < nbvertic; j++) - { + for (int i = 0; i < nbhoriz; i++) { + for (int j = 0; j < nbvertic; j++) { int ij = j * nbhoriz + i; double x = uv_grid[ij].x; double y = uv_grid[ij].y; @@ -849,8 +1337,7 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, gp_UV p2 = quad->side[2]->Value2d(param_2).XY(); gp_UV p3 = quad->side[3]->Value2d(param_3).XY(); - gp_UV uv = (1 - y) * p0 + x * p1 + y * p2 + (1 - x) * p3; - uv -= (1 - x) * (1 - y) * a0 + x * (1 - y) * a1 + x * y * a2 + (1 - x) * y * a3; + gp_UV uv = CalcUV(x,y, a0,a1,a2,a3, p0,p1,p2,p3); uv_grid[ij].u = uv.X(); uv_grid[ij].v = uv.Y(); @@ -867,11 +1354,11 @@ bool StdMeshers_Quadrangle_2D::SetNormalizedGrid (SMESH_Mesh & aMesh, static void ShiftQuad(FaceQuadStruct* quad, const int num, bool) { StdMeshers_FaceSide* side[4] = { quad->side[0], quad->side[1], quad->side[2], quad->side[3] }; - for (int i = BOTTOM_SIDE; i < NB_SIDES; ++i ) { - int id = ( i + num ) % NB_SIDES; - bool wasForward = ( i < TOP_SIDE ); - bool newForward = ( id < TOP_SIDE ); - if ( wasForward != newForward ) + for (int i = BOTTOM_SIDE; i < NB_SIDES; ++i) { + int id = (i + num) % NB_SIDES; + bool wasForward = (i < TOP_SIDE); + bool newForward = (id < TOP_SIDE); + if (wasForward != newForward) side[ i ]->Reverse(); quad->side[ id ] = side[ i ]; } @@ -887,9 +1374,9 @@ static gp_UV CalcUV(double x0, double x1, double y0, double y1, const gp_UV& a0, const gp_UV& a1, const gp_UV& a2, const gp_UV& a3) { - const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0 ); + const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0); const vector& uv_er = quad->side[1]->GetUVPtStruct(false,1); - const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1 ); + const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1); const vector& uv_el = quad->side[3]->GetUVPtStruct(false,0); double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0)); @@ -905,9 +1392,7 @@ static gp_UV CalcUV(double x0, double x1, double y0, double y1, gp_UV p2 = quad->side[TOP_SIDE ]->Value2d(param_t).XY(); gp_UV p3 = quad->side[LEFT_SIDE ]->Value2d(param_l).XY(); - gp_UV uv = p0 * (1 - y) + p1 * x + p2 * y + p3 * (1 - x); - - uv -= (1 - x) * (1 - y) * a0 + x * (1 - y) * a1 + x * y * a2 + (1 - x) * y * a3; + gp_UV uv = CalcUV(x,y, a0,a1,a2,a3, p0,p1,p2,p3); return uv; } @@ -922,27 +1407,12 @@ static gp_UV CalcUV2(double x, double y, const gp_UV& a0, const gp_UV& a1, const gp_UV& a2, const gp_UV& a3) { - const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0 ); - const vector& uv_er = quad->side[1]->GetUVPtStruct(false,1); - const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1 ); - const vector& uv_el = quad->side[3]->GetUVPtStruct(false,0); + gp_UV p0 = quad->side[BOTTOM_SIDE]->Value2d(x).XY(); + gp_UV p1 = quad->side[RIGHT_SIDE ]->Value2d(y).XY(); + gp_UV p2 = quad->side[TOP_SIDE ]->Value2d(x).XY(); + gp_UV p3 = quad->side[LEFT_SIDE ]->Value2d(y).XY(); - //double x = (x0 + y0 * (x1 - x0)) / (1 - (y1 - y0) * (x1 - x0)); - //double y = y0 + x * (y1 - y0); - - double param_b = uv_eb[0].normParam + x * (uv_eb.back().normParam - uv_eb[0].normParam); - double param_t = uv_et[0].normParam + x * (uv_et.back().normParam - uv_et[0].normParam); - double param_r = uv_er[0].normParam + y * (uv_er.back().normParam - uv_er[0].normParam); - double param_l = uv_el[0].normParam + y * (uv_el.back().normParam - uv_el[0].normParam); - - gp_UV p0 = quad->side[BOTTOM_SIDE]->Value2d(param_b).XY(); - gp_UV p1 = quad->side[RIGHT_SIDE ]->Value2d(param_r).XY(); - gp_UV p2 = quad->side[TOP_SIDE ]->Value2d(param_t).XY(); - gp_UV p3 = quad->side[LEFT_SIDE ]->Value2d(param_l).XY(); - - gp_UV uv = p0 * (1 - y) + p1 * x + p2 * y + p3 * (1 - x); - - uv -= (1 - x) * (1 - y) * a0 + x * (1 - y) * a1 + x * y * a2 + (1 - x) * y * a3; + gp_UV uv = CalcUV(x,y, a0,a1,a2,a3, p0,p1,p2,p3); return uv; } @@ -962,20 +1432,14 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, // of meshing after implementation new variant // for bug 0016220 from Mantis. bool OldVersion = false; + if (myQuadType == QUAD_QUADRANGLE_PREF_REVERSED) + OldVersion = true; SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); const TopoDS_Face& F = TopoDS::Face(aShape); Handle(Geom_Surface) S = BRep_Tool::Surface(F); -// const TopoDS_Wire& W = BRepTools::OuterWire(F); bool WisF = true; -// if(W.Orientation()==TopAbs_FORWARD) -// WisF = true; - //if(WisF) cout<<"W is FORWARD"<ShapeToIndex( F ); + int i,j,geomFaceID = meshDS->ShapeToIndex(F); int nb = quad->side[0]->NbPoints(); int nr = quad->side[1]->NbPoints(); @@ -984,8 +1448,8 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, int dh = abs(nb-nt); int dv = abs(nr-nl); - if( dh>=dv ) { - if( nt>nb ) { + if (dh>=dv) { + if (nt>nb) { // it is a base case => not shift quad but me be replacement is need ShiftQuad(quad,0,WisF); } @@ -995,7 +1459,7 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, } } else { - if( nr>nl ) { + if (nr>nl) { // we have to shift quad on 1 ShiftQuad(quad,1,WisF); } @@ -1044,7 +1508,7 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, // 0------------0 // 0 bottom 1 - if(dh>dv) { + if (dh>dv) { addv = (dh-dv)/2; nbv = nbv + addv; } @@ -1053,79 +1517,84 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, nbh = nbh + addh; } - const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0 ); + const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0); const vector& uv_er = quad->side[1]->GetUVPtStruct(false,1); - const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1 ); + const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1); const vector& uv_el = quad->side[3]->GetUVPtStruct(false,0); + if (uv_eb.size() != nb || uv_er.size() != nr || uv_et.size() != nt || uv_el.size() != nl) + return error(COMPERR_BAD_INPUT_MESH); + + if ( myNeedSmooth ) + UpdateDegenUV( quad ); + // arrays for normalized params //cout<<"Dump B:"<X()<<","<Y()<<","<Z()<<")"<0) { + if (dl>0) { // add top nodes - for(i=1; i<=dl; i++) + for (i=1; i<=dl; i++) NodesL.SetValue(i+1,nl,uv_et[i].node); // create and add needed nodes TColgp_SequenceOfXY UVtmp; - for(i=1; i<=dl; i++) { + for (i=1; i<=dl; i++) { double x0 = npt.Value(i+1); double x1 = x0; // diagonal node @@ -1136,9 +1605,9 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, SMDS_MeshNode * N = meshDS->AddNode(P.X(), P.Y(), P.Z()); meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); NodesL.SetValue(i+1,1,N); - if(UVL.Length()AddNode(P.X(), P.Y(), P.Z()); meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); NodesL.SetValue(i+1,j,N); - if( i==dl ) UVtmp.Append(UV); + if (i==dl) UVtmp.Append(UV); } } - for(i=1; i<=UVtmp.Length() && UVL.Length()X()<<","<Y()<<","<Z()<<")"; // } // cout<AddFace(NodesL.Value(i,j), NodesL.Value(i+1,j), + myHelper->AddFace(NodesL.Value(i,j), NodesL.Value(i+1,j), NodesL.Value(i+1,j+1), NodesL.Value(i,j+1)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesL.Value(i,j), NodesL.Value(i,j+1), + myHelper->AddFace(NodesL.Value(i,j), NodesL.Value(i,j+1), NodesL.Value(i+1,j+1), NodesL.Value(i+1,j)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } } } else { // fill UVL using c2d - for(i=1; i0) { + if (dr>0) { // add top nodes - for(i=1; i<=dr; i++) + for (i=1; i<=dr; i++) NodesR.SetValue(i+1,1,uv_et[nt-1-i].node); // create and add needed nodes TColgp_SequenceOfXY UVtmp; - for(i=1; i<=dr; i++) { + for (i=1; i<=dr; i++) { double x0 = npt.Value(nt-i); double x1 = x0; // diagonal node @@ -1207,9 +1676,9 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, SMDS_MeshNode * N = meshDS->AddNode(P.X(), P.Y(), P.Z()); meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); NodesR.SetValue(i+1,nr,N); - if(UVR.Length()AddNode(P.X(), P.Y(), P.Z()); meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); NodesR.SetValue(i+1,j,N); - if( i==dr ) UVtmp.Prepend(UV); + if (i==dr) UVtmp.Prepend(UV); } } - for(i=1; i<=UVtmp.Length() && UVR.Length()AddFace(NodesR.Value(i,j), NodesR.Value(i+1,j), + myHelper->AddFace(NodesR.Value(i,j), NodesR.Value(i+1,j), NodesR.Value(i+1,j+1), NodesR.Value(i,j+1)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesR.Value(i,j), NodesR.Value(i,j+1), + myHelper->AddFace(NodesR.Value(i,j), NodesR.Value(i,j+1), NodesR.Value(i+1,j+1), NodesR.Value(i+1,j)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } } } else { // fill UVR using c2d - for(i=1; iAddNode(P.X(), P.Y(), P.Z()); meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); NodesC.SetValue(i,nbv-nnn+j,N); + if ( j==1 ) + UVT.Append( UV ); } } // add diagonal layers //cout<<"UVL.Length()="<Value(u,v); + gp_UV A2 = UVR.Value(nbv-nnn); + gp_UV A3 = UVL.Value(nbv-nnn); + for (i=1; iValue(UV.X(),UV.Y()); SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnFace(N, geomFaceID, u, v); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(),UV.Y()); NodesC.SetValue(j,i+1,N); } } // create faces - for(i=1; iAddFace(NodesC.Value(i,j), NodesC.Value(i+1,j), + myHelper->AddFace(NodesC.Value(i,j), NodesC.Value(i+1,j), NodesC.Value(i+1,j+1), NodesC.Value(i,j+1)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesC.Value(i,j), NodesC.Value(i,j+1), + myHelper->AddFace(NodesC.Value(i,j), NodesC.Value(i,j+1), NodesC.Value(i+1,j+1), NodesC.Value(i+1,j)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } } @@ -1324,58 +1800,55 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, // step1: create faces for bottom rectangle domain StdMeshers_Array2OfNode NodesBRD(1,nb,1,nnn-1); // fill UVL and UVR using c2d - for(j=0; jValue(u,v); + for (j=2; jValue(UV.X(),UV.Y()); SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); - meshDS->SetNodeOnFace(N, geomFaceID, u, v); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(),UV.Y()); NodesBRD.SetValue(j,i+1,N); - } } - for(j=1; jAddFace(NodesBRD.Value(i,j), NodesBRD.Value(i+1,j), + myHelper->AddFace(NodesBRD.Value(i,j), NodesBRD.Value(i+1,j), NodesBRD.Value(i+1,j+1), NodesBRD.Value(i,j+1)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesBRD.Value(i,j), NodesBRD.Value(i,j+1), + myHelper->AddFace(NodesBRD.Value(i,j), NodesBRD.Value(i,j+1), NodesBRD.Value(i+1,j+1), NodesBRD.Value(i+1,j)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } } - int drl = abs(nr-nl); // create faces for region C StdMeshers_Array2OfNode NodesC(1,nb,1,drl+1+addv); // add nodes from previous region - for(j=1; j<=nb; j++) { + for (j=1; j<=nb; j++) { NodesC.SetValue(j,1,NodesBRD.Value(j,nnn-1)); } - if( (drl+addv) > 0 ) { + if ((drl+addv) > 0) { int n1,n2; - if(nr>nl) { + if (nr>nl) { n1 = 1; n2 = drl + 1; TColgp_SequenceOfXY UVtmp; double drparam = npr.Value(nr) - npr.Value(nnn-1); double dlparam = npl.Value(nnn) - npl.Value(nnn-1); double y0,y1; - for(i=1; i<=drl; i++) { + for (i=1; i<=drl; i++) { // add existed nodes from right edge NodesC.SetValue(nb,i+1,uv_er[nnn+i-2].node); //double dtparam = npt.Value(i+1); @@ -1383,7 +1856,7 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, double dpar = (y1 - npr.Value(nnn-1))/drparam; y0 = npl.Value(nnn-1) + dpar*dlparam; // param on left edge double dy = y1 - y0; - for(j=1; jValue(UV.X(),UV.Y()); @@ -1419,14 +1892,14 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, double drparam = npr.Value(nnn) - npr.Value(nnn-1); double y0 = npl.Value(nnn-1); double y1 = npr.Value(nnn-1); - for(i=1; i<=drl; i++) { + for (i=1; i<=drl; i++) { // add existed nodes from right edge NodesC.SetValue(1,i+1,uv_el[nnn+i-2].node); y0 = npl.Value(nnn+i-1); // param on left edge double dpar = (y0 - npl.Value(nnn-1))/dlparam; y1 = npr.Value(nnn-1) + dpar*drparam; // param on right edge double dy = y1 - y0; - for(j=2; j<=nb; j++) { + for (j=2; j<=nb; j++) { double x = npb.Value(j)*npt.Value(nt-i); double y = y0 + dy*x; gp_UV UV = CalcUV2(x, y, quad, a0, a1, a2, a3); @@ -1438,13 +1911,13 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, } double dy0 = (1-y0)/(addv+1); double dy1 = (1-y1)/(addv+1); - for(i=1; i<=addv; i++) { + for (i=1; i<=addv; i++) { double yy0 = y0 + dy0*i; double yy1 = y1 + dy1*i; double dyy = yy1 - yy0; - for(j=1; j<=nb; j++) { + for (j=1; j<=nb; j++) { double x = npt.Value(i+1) + - npb.Value(j) * ( npt.Value(nt-i-drl) - npt.Value(i+1) ); + npb.Value(j) * (npt.Value(nt-i-drl) - npt.Value(i+1)); double y = yy0 + dyy*x; gp_UV UV = CalcUV2(x, y, quad, a0, a1, a2, a3); gp_Pnt P = S->Value(UV.X(),UV.Y()); @@ -1455,55 +1928,55 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, } } // create faces - for(j=1; j<=drl+addv; j++) { - for(i=1; iAddFace(NodesC.Value(i,j), NodesC.Value(i+1,j), + myHelper->AddFace(NodesC.Value(i,j), NodesC.Value(i+1,j), NodesC.Value(i+1,j+1), NodesC.Value(i,j+1)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesC.Value(i,j), NodesC.Value(i,j+1), + myHelper->AddFace(NodesC.Value(i,j), NodesC.Value(i,j+1), NodesC.Value(i+1,j+1), NodesC.Value(i+1,j)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } } // end nr=n2; i--) { + for (i=drl+addv; i>=n2; i--) { nnn++; NodesLast.SetValue(nnn,1,NodesC.Value(nb,i)); } - for(i=1; iAddFace(NodesLast.Value(i,1), NodesLast.Value(i+1,1), + myHelper->AddFace(NodesLast.Value(i,1), NodesLast.Value(i+1,1), NodesLast.Value(i+1,2), NodesLast.Value(i,2)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } else { SMDS_MeshFace* F = - myTool->AddFace(NodesLast.Value(i,1), NodesLast.Value(i,2), + myHelper->AddFace(NodesLast.Value(i,1), NodesLast.Value(i,2), NodesLast.Value(i+1,2), NodesLast.Value(i+1,2)); - meshDS->SetMeshElementOnShape(F, geomFaceID); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); } } - } // if( (drl+addv) > 0 ) + } // if ((drl+addv) > 0) } // end new version implementation @@ -1511,34 +1984,1470 @@ bool StdMeshers_Quadrangle_2D::ComputeQuadPref (SMESH_Mesh & aMesh, return isOk; } -//============================================================================= -/*! Split quadrangle in to 2 triangles by smallest diagonal - * + +//======================================================================= +/*! + * Evaluate only quandrangle faces */ -//============================================================================= -void StdMeshers_Quadrangle_2D::SplitQuad(SMESHDS_Mesh *theMeshDS, - int theFaceID, - const SMDS_MeshNode* theNode1, - const SMDS_MeshNode* theNode2, - const SMDS_MeshNode* theNode3, - const SMDS_MeshNode* theNode4) +//======================================================================= + +bool StdMeshers_Quadrangle_2D::EvaluateQuadPref(SMESH_Mesh & aMesh, + const TopoDS_Shape& aShape, + std::vector& aNbNodes, + MapShapeNbElems& aResMap, + bool IsQuadratic) { - gp_Pnt a(theNode1->X(),theNode1->Y(),theNode1->Z()); - gp_Pnt b(theNode2->X(),theNode2->Y(),theNode2->Z()); - gp_Pnt c(theNode3->X(),theNode3->Y(),theNode3->Z()); - gp_Pnt d(theNode4->X(),theNode4->Y(),theNode4->Z()); - SMDS_MeshFace* face; - if(a.Distance(c) > b.Distance(d)){ - face = myTool->AddFace(theNode2, theNode4 , theNode1); - theMeshDS->SetMeshElementOnShape(face, theFaceID ); - face = myTool->AddFace(theNode2, theNode3, theNode4); - theMeshDS->SetMeshElementOnShape(face, theFaceID ); + // Auxilary key in order to keep old variant + // of meshing after implementation new variant + // for bug 0016220 from Mantis. + bool OldVersion = false; + if (myQuadType == QUAD_QUADRANGLE_PREF_REVERSED) + OldVersion = true; + + const TopoDS_Face& F = TopoDS::Face(aShape); + Handle(Geom_Surface) S = BRep_Tool::Surface(F); + + int nb = aNbNodes[0]; + int nr = aNbNodes[1]; + int nt = aNbNodes[2]; + int nl = aNbNodes[3]; + int dh = abs(nb-nt); + int dv = abs(nr-nl); + if (dh>=dv) { + if (nt>nb) { + // it is a base case => not shift + } + else { + // we have to shift on 2 + nb = aNbNodes[2]; + nr = aNbNodes[3]; + nt = aNbNodes[0]; + nl = aNbNodes[1]; + } } - else{ - face = myTool->AddFace(theNode1, theNode2 ,theNode3); - theMeshDS->SetMeshElementOnShape(face, theFaceID ); - face = myTool->AddFace(theNode1, theNode3, theNode4); - theMeshDS->SetMeshElementOnShape(face, theFaceID ); + else { + if (nr>nl) { + // we have to shift quad on 1 + nb = aNbNodes[3]; + nr = aNbNodes[0]; + nt = aNbNodes[1]; + nl = aNbNodes[2]; + } + else { + // we have to shift quad on 3 + nb = aNbNodes[1]; + nr = aNbNodes[2]; + nt = aNbNodes[3]; + nl = aNbNodes[0]; + } + } + + dh = abs(nb-nt); + dv = abs(nr-nl); + int nbh = Max(nb,nt); + int nbv = Max(nr,nl); + int addh = 0; + int addv = 0; + + if (dh>dv) { + addv = (dh-dv)/2; + nbv = nbv + addv; + } + else { // dv>=dh + addh = (dv-dh)/2; + nbh = nbh + addh; + } + + int dl,dr; + if (OldVersion) { + // add some params to right and left after the first param + // insert to right + dr = nbv - nr; + // insert to left + dl = nbv - nl; + } + + int nnn = Min(nr,nl); + + int nbNodes = 0; + int nbFaces = 0; + if (OldVersion) { + // step1: create faces for left domain + if (dl>0) { + nbNodes += dl*(nl-1); + nbFaces += dl*(nl-1); + } + // step2: create faces for right domain + if (dr>0) { + nbNodes += dr*(nr-1); + nbFaces += dr*(nr-1); + } + // step3: create faces for central domain + nbNodes += (nb-2)*(nnn-1) + (nbv-nnn-1)*(nb-2); + nbFaces += (nb-1)*(nbv-1); + } + else { // New version (!OldVersion) + nbNodes += (nnn-2)*(nb-2); + nbFaces += (nnn-2)*(nb-1); + int drl = abs(nr-nl); + nbNodes += drl*(nb-1) + addv*nb; + nbFaces += (drl+addv)*(nb-1) + (nt-1); + } // end new version implementation + + std::vector aVec(SMDSEntity_Last); + for (int i=SMDSEntity_Node; iX(),theNode1->Y(),theNode1->Z()); + gp_Pnt b(theNode2->X(),theNode2->Y(),theNode2->Z()); + gp_Pnt c(theNode3->X(),theNode3->Y(),theNode3->Z()); + gp_Pnt d(theNode4->X(),theNode4->Y(),theNode4->Z()); + SMDS_MeshFace* face; + if (a.Distance(c) > b.Distance(d)){ + face = myHelper->AddFace(theNode2, theNode4 , theNode1); + if (face) theMeshDS->SetMeshElementOnShape(face, theFaceID); + face = myHelper->AddFace(theNode2, theNode3, theNode4); + if (face) theMeshDS->SetMeshElementOnShape(face, theFaceID); + + } + else{ + face = myHelper->AddFace(theNode1, theNode2 ,theNode3); + if (face) theMeshDS->SetMeshElementOnShape(face, theFaceID); + face = myHelper->AddFace(theNode1, theNode3, theNode4); + if (face) theMeshDS->SetMeshElementOnShape(face, theFaceID); + } +} + +namespace +{ + enum uvPos { UV_A0, UV_A1, UV_A2, UV_A3, UV_B, UV_R, UV_T, UV_L, UV_SIZE }; + + inline SMDS_MeshNode* makeNode( UVPtStruct & uvPt, + const double y, + FaceQuadStruct* quad, + const gp_UV* UVs, + SMESH_MesherHelper* helper, + Handle(Geom_Surface) S) + { + const vector& uv_eb = quad->side[BOTTOM_SIDE]->GetUVPtStruct(); + const vector& uv_et = quad->side[TOP_SIDE ]->GetUVPtStruct(); + double rBot = ( uv_eb.size() - 1 ) * uvPt.normParam; + double rTop = ( uv_et.size() - 1 ) * uvPt.normParam; + int iBot = int( rBot ); + int iTop = int( rTop ); + double xBot = uv_eb[ iBot ].normParam + ( rBot - iBot ) * ( uv_eb[ iBot+1 ].normParam - uv_eb[ iBot ].normParam ); + double xTop = uv_et[ iTop ].normParam + ( rTop - iTop ) * ( uv_et[ iTop+1 ].normParam - uv_et[ iTop ].normParam ); + double x = xBot + y * ( xTop - xBot ); + + gp_UV uv = CalcUV(/*x,y=*/x, y, + /*a0,...=*/UVs[UV_A0], UVs[UV_A1], UVs[UV_A2], UVs[UV_A3], + /*p0=*/quad->side[BOTTOM_SIDE]->Value2d( x ).XY(), + /*p1=*/UVs[ UV_R ], + /*p2=*/quad->side[TOP_SIDE ]->Value2d( x ).XY(), + /*p3=*/UVs[ UV_L ]); + gp_Pnt P = S->Value( uv.X(), uv.Y() ); + uvPt.u = uv.X(); + uvPt.v = uv.Y(); + return helper->AddNode(P.X(), P.Y(), P.Z(), 0, uv.X(), uv.Y() ); + } + + void reduce42( const vector& curr_base, + vector& next_base, + const int j, + int & next_base_len, + FaceQuadStruct* quad, + gp_UV* UVs, + const double y, + SMESH_MesherHelper* helper, + Handle(Geom_Surface)& S) + { + // add one "HH": nodes a,b,c,d,e and faces 1,2,3,4,5,6 + // + // .-----a-----b i + 1 + // |\ 5 | 6 /| + // | \ | / | + // | c--d--e | + // |1 |2 |3 |4 | + // | | | | | + // .--.--.--.--. i + // + // j j+2 j+4 + + // a (i + 1, j + 2) + const SMDS_MeshNode*& Na = next_base[ ++next_base_len ].node; + if ( !Na ) + Na = makeNode( next_base[ next_base_len ], y, quad, UVs, helper, S ); + + // b (i + 1, j + 4) + const SMDS_MeshNode*& Nb = next_base[ ++next_base_len ].node; + if ( !Nb ) + Nb = makeNode( next_base[ next_base_len ], y, quad, UVs, helper, S ); + + // c + double u = (curr_base[j + 2].u + next_base[next_base_len - 2].u) / 2.0; + double v = (curr_base[j + 2].v + next_base[next_base_len - 2].v) / 2.0; + gp_Pnt P = S->Value(u,v); + SMDS_MeshNode* Nc = helper->AddNode(P.X(), P.Y(), P.Z(), 0, u, v); + + // d + u = (curr_base[j + 2].u + next_base[next_base_len - 1].u) / 2.0; + v = (curr_base[j + 2].v + next_base[next_base_len - 1].v) / 2.0; + P = S->Value(u,v); + SMDS_MeshNode* Nd = helper->AddNode(P.X(), P.Y(), P.Z(), 0, u, v); + + // e + u = (curr_base[j + 2].u + next_base[next_base_len].u) / 2.0; + v = (curr_base[j + 2].v + next_base[next_base_len].v) / 2.0; + P = S->Value(u,v); + SMDS_MeshNode* Ne = helper->AddNode(P.X(), P.Y(), P.Z(), 0, u, v); + + // Faces + helper->AddFace(curr_base[j + 0].node, + curr_base[j + 1].node, Nc, + next_base[next_base_len - 2].node); + + helper->AddFace(curr_base[j + 1].node, + curr_base[j + 2].node, Nd, Nc); + + helper->AddFace(curr_base[j + 2].node, + curr_base[j + 3].node, Ne, Nd); + + helper->AddFace(curr_base[j + 3].node, + curr_base[j + 4].node, Nb, Ne); + + helper->AddFace(Nc, Nd, Na, next_base[next_base_len - 2].node); + + helper->AddFace(Nd, Ne, Nb, Na); + } + + void reduce31( const vector& curr_base, + vector& next_base, + const int j, + int & next_base_len, + FaceQuadStruct* quad, + gp_UV* UVs, + const double y, + SMESH_MesherHelper* helper, + Handle(Geom_Surface)& S) + { + // add one "H": nodes b,c,e and faces 1,2,4,5 + // + // .---------b i + 1 + // |\ 5 /| + // | \ / | + // | c---e | + // |1 |2 |4 | + // | | | | + // .--.---.--. i + // + // j j+1 j+2 j+3 + + // b (i + 1, j + 3) + const SMDS_MeshNode*& Nb = next_base[ ++next_base_len ].node; + if ( !Nb ) + Nb = makeNode( next_base[ next_base_len ], y, quad, UVs, helper, S ); + + // c and e + double u1 = (curr_base[ j ].u + next_base[ next_base_len-1 ].u ) / 2.0; + double u2 = (curr_base[ j+3 ].u + next_base[ next_base_len ].u ) / 2.0; + double u3 = (u2 - u1) / 3.0; + // + double v1 = (curr_base[ j ].v + next_base[ next_base_len-1 ].v ) / 2.0; + double v2 = (curr_base[ j+3 ].v + next_base[ next_base_len ].v ) / 2.0; + double v3 = (v2 - v1) / 3.0; + // c + double u = u1 + u3; + double v = v1 + v3; + gp_Pnt P = S->Value(u,v); + SMDS_MeshNode* Nc = helper->AddNode( P.X(), P.Y(), P.Z(), 0, u, v ); + // e + u = u1 + u3 + u3; + v = v1 + v3 + v3; + P = S->Value(u,v); + SMDS_MeshNode* Ne = helper->AddNode( P.X(), P.Y(), P.Z(), 0, u, v ); + + // Faces + // 1 + helper->AddFace( curr_base[ j + 0 ].node, + curr_base[ j + 1 ].node, + Nc, + next_base[ next_base_len - 1 ].node); + // 2 + helper->AddFace( curr_base[ j + 1 ].node, + curr_base[ j + 2 ].node, Ne, Nc); + // 4 + helper->AddFace( curr_base[ j + 2 ].node, + curr_base[ j + 3 ].node, Nb, Ne); + // 5 + helper->AddFace(Nc, Ne, Nb, + next_base[ next_base_len - 1 ].node); + } + + typedef void (* PReduceFunction) ( const vector& curr_base, + vector& next_base, + const int j, + int & next_base_len, + FaceQuadStruct* quad, + gp_UV* UVs, + const double y, + SMESH_MesherHelper* helper, + Handle(Geom_Surface)& S); + +} // namespace + +//======================================================================= +/*! + * Implementation of Reduced algorithm (meshing with quadrangles only) + */ +//======================================================================= +bool StdMeshers_Quadrangle_2D::ComputeReduced (SMESH_Mesh & aMesh, + const TopoDS_Shape& aShape, + FaceQuadStruct* quad) +{ + SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + const TopoDS_Face& F = TopoDS::Face(aShape); + Handle(Geom_Surface) S = BRep_Tool::Surface(F); + int i,j,geomFaceID = meshDS->ShapeToIndex(F); + + int nb = quad->side[0]->NbPoints(); + int nr = quad->side[1]->NbPoints(); + int nt = quad->side[2]->NbPoints(); + int nl = quad->side[3]->NbPoints(); + + // Simple Reduce 10->8->6->4 (3 steps) Multiple Reduce 10->4 (1 step) + // + // .-----.-----.-----.-----. .-----.-----.-----.-----. + // | / \ | / \ | | / \ | / \ | + // | / .--.--. \ | | / \ | / \ | + // | / / | \ \ | | / .----.----. \ | + // .---.---.---.---.---.---. | / / \ | / \ \ | + // | / / \ | / \ \ | | / / \ | / \ \ | + // | / / .-.-. \ \ | | / / .---.---. \ \ | + // | / / / | \ \ \ | | / / / \ | / \ \ \ | + // .--.--.--.--.--.--.--.--. | / / / \ | / \ \ \ | + // | / / / \ | / \ \ \ | | / / / .-.-. \ \ \ | + // | / / / .-.-. \ \ \ | | / / / / | \ \ \ \ | + // | / / / / | \ \ \ \ | | / / / / | \ \ \ \ | + // .-.-.-.--.--.--.--.-.-.-. .-.-.-.--.--.--.--.-.-.-. + + bool MultipleReduce = false; + { + int nb1 = nb; + int nr1 = nr; + int nt1 = nt; + + if (nr == nl) { + if (nb < nt) { + nt1 = nb; + nb1 = nt; + } + } + else if (nb == nt) { + nr1 = nb; // and == nt + if (nl < nr) { + nt1 = nl; + nb1 = nr; + } + else { + nt1 = nr; + nb1 = nl; + } + } + else { + return false; + } + + // number of rows and columns + int nrows = nr1 - 1; + int ncol_top = nt1 - 1; + int ncol_bot = nb1 - 1; + // number of rows needed to reduce ncol_bot to ncol_top using simple 3->1 "tree" (see below) + int nrows_tree31 = int( log( (double)(ncol_bot / ncol_top) ) / log((double) 3 )); // = log x base 3 + if ( nrows < nrows_tree31 ) + MultipleReduce = true; + } + + if (MultipleReduce) { // == ComputeQuadPref QUAD_QUADRANGLE_PREF_REVERSED + //================================================== + int dh = abs(nb-nt); + int dv = abs(nr-nl); + + if (dh >= dv) { + if (nt > nb) { + // it is a base case => not shift quad but may be replacement is need + ShiftQuad(quad,0,true); + } + else { + // we have to shift quad on 2 + ShiftQuad(quad,2,true); + } + } + else { + if (nr > nl) { + // we have to shift quad on 1 + ShiftQuad(quad,1,true); + } + else { + // we have to shift quad on 3 + ShiftQuad(quad,3,true); + } + } + + nb = quad->side[0]->NbPoints(); + nr = quad->side[1]->NbPoints(); + nt = quad->side[2]->NbPoints(); + nl = quad->side[3]->NbPoints(); + dh = abs(nb-nt); + dv = abs(nr-nl); + int nbh = Max(nb,nt); + int nbv = Max(nr,nl); + int addh = 0; + int addv = 0; + + if (dh>dv) { + addv = (dh-dv)/2; + nbv = nbv + addv; + } + else { // dv>=dh + addh = (dv-dh)/2; + nbh = nbh + addh; + } + + const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0); + const vector& uv_er = quad->side[1]->GetUVPtStruct(false,1); + const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1); + const vector& uv_el = quad->side[3]->GetUVPtStruct(false,0); + + if (uv_eb.size() != nb || uv_er.size() != nr || uv_et.size() != nt || uv_el.size() != nl) + return error(COMPERR_BAD_INPUT_MESH); + + if ( myNeedSmooth ) + UpdateDegenUV( quad ); + + // arrays for normalized params + TColStd_SequenceOfReal npb, npr, npt, npl; + for (j = 0; j < nb; j++) { + npb.Append(uv_eb[j].normParam); + } + for (i = 0; i < nr; i++) { + npr.Append(uv_er[i].normParam); + } + for (j = 0; j < nt; j++) { + npt.Append(uv_et[j].normParam); + } + for (i = 0; i < nl; i++) { + npl.Append(uv_el[i].normParam); + } + + int dl,dr; + // orientation of face and 3 main domain for future faces + // 0 top 1 + // 1------------1 + // | | | | + // | | | | + // | L | | R | + // left | | | | rigth + // | / \ | + // | / C \ | + // |/ \| + // 0------------0 + // 0 bottom 1 + + // add some params to right and left after the first param + // insert to right + dr = nbv - nr; + double dpr = (npr.Value(2) - npr.Value(1))/(dr+1); + for (i=1; i<=dr; i++) { + npr.InsertAfter(1,npr.Value(2)-dpr); + } + // insert to left + dl = nbv - nl; + dpr = (npl.Value(2) - npl.Value(1))/(dl+1); + for (i=1; i<=dl; i++) { + npl.InsertAfter(1,npl.Value(2)-dpr); + } + + gp_XY a0 (uv_eb.front().u, uv_eb.front().v); + gp_XY a1 (uv_eb.back().u, uv_eb.back().v); + gp_XY a2 (uv_et.back().u, uv_et.back().v); + gp_XY a3 (uv_et.front().u, uv_et.front().v); + + int nnn = Min(nr,nl); + // auxilary sequence of XY for creation of nodes + // in the bottom part of central domain + // it's length must be == nbv-nnn-1 + TColgp_SequenceOfXY UVL; + TColgp_SequenceOfXY UVR; + //================================================== + + // step1: create faces for left domain + StdMeshers_Array2OfNode NodesL(1,dl+1,1,nl); + // add left nodes + for (j=1; j<=nl; j++) + NodesL.SetValue(1,j,uv_el[j-1].node); + if (dl>0) { + // add top nodes + for (i=1; i<=dl; i++) + NodesL.SetValue(i+1,nl,uv_et[i].node); + // create and add needed nodes + TColgp_SequenceOfXY UVtmp; + for (i=1; i<=dl; i++) { + double x0 = npt.Value(i+1); + double x1 = x0; + // diagonal node + double y0 = npl.Value(i+1); + double y1 = npr.Value(i+1); + gp_UV UV = CalcUV(x0, x1, y0, y1, quad, a0, a1, a2, a3); + gp_Pnt P = S->Value(UV.X(),UV.Y()); + SMDS_MeshNode * N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); + NodesL.SetValue(i+1,1,N); + if (UVL.Length()Value(UV.X(),UV.Y()); + SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); + NodesL.SetValue(i+1,j,N); + if (i==dl) UVtmp.Append(UV); + } + } + for (i=1; i<=UVtmp.Length() && UVL.Length()AddFace(NodesL.Value(i,j), NodesL.Value(i+1,j), + NodesL.Value(i+1,j+1), NodesL.Value(i,j+1)); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); + } + } + } + else { + // fill UVL using c2d + for (i=1; i0) { + // add top nodes + for (i=1; i<=dr; i++) + NodesR.SetValue(i+1,1,uv_et[nt-1-i].node); + // create and add needed nodes + TColgp_SequenceOfXY UVtmp; + for (i=1; i<=dr; i++) { + double x0 = npt.Value(nt-i); + double x1 = x0; + // diagonal node + double y0 = npl.Value(i+1); + double y1 = npr.Value(i+1); + gp_UV UV = CalcUV(x0, x1, y0, y1, quad, a0, a1, a2, a3); + gp_Pnt P = S->Value(UV.X(),UV.Y()); + SMDS_MeshNode * N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); + NodesR.SetValue(i+1,nr,N); + if (UVR.Length()Value(UV.X(),UV.Y()); + SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); + NodesR.SetValue(i+1,j,N); + if (i==dr) UVtmp.Prepend(UV); + } + } + for (i=1; i<=UVtmp.Length() && UVR.Length()AddFace(NodesR.Value(i,j), NodesR.Value(i+1,j), + NodesR.Value(i+1,j+1), NodesR.Value(i,j+1)); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); + } + } + } + else { + // fill UVR using c2d + for (i=1; iValue(UV.X(),UV.Y()); + SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, UV.X(), UV.Y()); + NodesC.SetValue(i,nbv-nnn+j,N); + } + } + // add diagonal layers + for (i=1; iValue(u,v); + SMDS_MeshNode* N = meshDS->AddNode(P.X(), P.Y(), P.Z()); + meshDS->SetNodeOnFace(N, geomFaceID, u, v); + NodesC.SetValue(j,i+1,N); + } + } + // create faces + for (i=1; iAddFace(NodesC.Value(i,j), NodesC.Value(i+1,j), + NodesC.Value(i+1,j+1), NodesC.Value(i,j+1)); + if (F) meshDS->SetMeshElementOnShape(F, geomFaceID); + } + } + // TODO ??? + } // end Multiple Reduce implementation + else { // Simple Reduce (!MultipleReduce) + //========================================================= + if (nr == nl) { + if (nt < nb) { + // it is a base case => not shift quad + //ShiftQuad(quad,0,true); + } + else { + // we have to shift quad on 2 + ShiftQuad(quad,2,true); + } + } + else { + if (nl > nr) { + // we have to shift quad on 1 + ShiftQuad(quad,1,true); + } + else { + // we have to shift quad on 3 + ShiftQuad(quad,3,true); + } + } + + nb = quad->side[0]->NbPoints(); + nr = quad->side[1]->NbPoints(); + nt = quad->side[2]->NbPoints(); + nl = quad->side[3]->NbPoints(); + + // number of rows and columns + int nrows = nr - 1; // and also == nl - 1 + int ncol_top = nt - 1; + int ncol_bot = nb - 1; + int npair_top = ncol_top / 2; + // maximum number of bottom elements for "linear" simple reduce 4->2 + int max_lin42 = ncol_top + npair_top * 2 * nrows; + // maximum number of bottom elements for "linear" simple reduce 3->1 + int max_lin31 = ncol_top + ncol_top * 2 * nrows; + // maximum number of bottom elements for "tree" simple reduce 4->2 + int max_tree42 = 0; + // number of rows needed to reduce ncol_bot to ncol_top using simple 4->2 "tree" + int nrows_tree42 = int( log( (double)(ncol_bot / ncol_top) )/log((double)2) ); // needed to avoid overflow at pow(2) while computing max_tree42 + if (nrows_tree42 < nrows) { + max_tree42 = npair_top * pow(2.0, nrows + 1); + if ( ncol_top > npair_top * 2 ) { + int delta = ncol_bot - max_tree42; + for (int irow = 1; irow < nrows; irow++) { + int nfour = delta / 4; + delta -= nfour * 2; + } + if (delta <= (ncol_top - npair_top * 2)) + max_tree42 = ncol_bot; + } + } + // maximum number of bottom elements for "tree" simple reduce 3->1 + //int max_tree31 = ncol_top * pow(3.0, nrows); + bool is_lin_31 = false; + bool is_lin_42 = false; + bool is_tree_31 = false; + bool is_tree_42 = false; + int max_lin = max_lin42; + if (ncol_bot > max_lin42) { + if (ncol_bot <= max_lin31) { + is_lin_31 = true; + max_lin = max_lin31; + } + } + else { + // if ncol_bot is a 3*n or not 2*n + if ((ncol_bot/3)*3 == ncol_bot || (ncol_bot/2)*2 != ncol_bot) { + is_lin_31 = true; + max_lin = max_lin31; + } + else { + is_lin_42 = true; + } + } + if (ncol_bot > max_lin) { // not "linear" + is_tree_31 = (ncol_bot > max_tree42); + if (ncol_bot <= max_tree42) { + if ((ncol_bot/3)*3 == ncol_bot || (ncol_bot/2)*2 != ncol_bot) { + is_tree_31 = true; + } + else { + is_tree_42 = true; + } + } + } + + const vector& uv_eb = quad->side[0]->GetUVPtStruct(true,0); + const vector& uv_er = quad->side[1]->GetUVPtStruct(false,1); + const vector& uv_et = quad->side[2]->GetUVPtStruct(true,1); + const vector& uv_el = quad->side[3]->GetUVPtStruct(false,0); + + if (uv_eb.size() != nb || uv_er.size() != nr || uv_et.size() != nt || uv_el.size() != nl) + return error(COMPERR_BAD_INPUT_MESH); + + myHelper->SetElementsOnShape( true ); + + gp_UV uv[ UV_SIZE ]; + uv[ UV_A0 ].SetCoord( uv_eb.front().u, uv_eb.front().v); + uv[ UV_A1 ].SetCoord( uv_eb.back().u, uv_eb.back().v ); + uv[ UV_A2 ].SetCoord( uv_et.back().u, uv_et.back().v ); + uv[ UV_A3 ].SetCoord( uv_et.front().u, uv_et.front().v); + + vector curr_base = uv_eb, next_base; + + UVPtStruct nullUVPtStruct; nullUVPtStruct.node = 0; + + int curr_base_len = nb; + int next_base_len = 0; + + if ( true ) + { // ------------------------------------------------------------------ + // New algorithm implemented by request of IPAL22856 + // "2D quadrangle mesher of reduced type works wrong" + // http://bugtracker.opencascade.com/show_bug.cgi?id=22856 + + // the algorithm is following: all reduces are centred in horizontal + // direction and are distributed among all rows + + if (ncol_bot > max_tree42) { + is_lin_31 = true; + } + else { + if ((ncol_top/3)*3 == ncol_top ) { + is_lin_31 = true; + } + else { + is_lin_42 = true; + } + } + + const int col_top_size = is_lin_42 ? 2 : 1; + const int col_base_size = is_lin_42 ? 4 : 3; + + // Compute nb of "columns" (like in "linear" simple reducing) in all rows + + vector nb_col_by_row; + + int delta_all = nb - nt; + int delta_one_col = nrows * 2; + int nb_col = delta_all / delta_one_col; + int remainder = delta_all - nb_col * delta_one_col; + if (remainder > 0) { + nb_col++; + } + if ( nb_col * col_top_size >= nt ) // == "tree" reducing situation + { + // top row is full (all elements reduced), add "columns" one by one + // in rows below until all bottom elements are reduced + nb_col = ( nt - 1 ) / col_top_size; + nb_col_by_row.resize( nrows, nb_col ); + int nbrows_not_full = nrows - 1; + int cur_top_size = nt - 1; + remainder = delta_all - nb_col * delta_one_col; + while ( remainder > 0 ) + { + delta_one_col = nbrows_not_full * 2; + int nb_col_add = remainder / delta_one_col; + cur_top_size += 2 * nb_col_by_row[ nbrows_not_full ]; + int nb_col_free = cur_top_size / col_top_size - nb_col_by_row[ nbrows_not_full-1 ]; + if ( nb_col_add > nb_col_free ) + nb_col_add = nb_col_free; + for ( int irow = 0; irow < nbrows_not_full; ++irow ) + nb_col_by_row[ irow ] += nb_col_add; + nbrows_not_full --; + remainder -= nb_col_add * delta_one_col; + } + } + else // == "linear" reducing situation + { + nb_col_by_row.resize( nrows, nb_col ); + if (remainder > 0) + for ( int irow = remainder / 2; irow < nrows; ++irow ) + nb_col_by_row[ irow ]--; + } + + // Make elements + + PReduceFunction reduceFunction = & ( is_lin_42 ? reduce42 : reduce31 ); + + const int reduce_grp_size = is_lin_42 ? 4 : 3; + + for (i = 1; i < nr; i++) // layer by layer + { + nb_col = nb_col_by_row[ i-1 ]; + int nb_next = curr_base_len - nb_col * 2; + if (nb_next < nt) nb_next = nt; + + const double y = uv_el[ i ].normParam; + + if ( i + 1 == nr ) // top + { + next_base = uv_et; + } + else + { + next_base.clear(); + next_base.resize( nb_next, nullUVPtStruct ); + next_base.front() = uv_el[i]; + next_base.back() = uv_er[i]; + + // compute normalized param u + double du = 1. / ( nb_next - 1 ); + next_base[0].normParam = 0.; + for ( j = 1; j < nb_next; ++j ) + next_base[j].normParam = next_base[j-1].normParam + du; + } + uv[ UV_L ].SetCoord( next_base.front().u, next_base.front().v ); + uv[ UV_R ].SetCoord( next_base.back().u, next_base.back().v ); + + int free_left = ( curr_base_len - 1 - nb_col * col_base_size ) / 2; + int free_middle = curr_base_len - 1 - nb_col * col_base_size - 2 * free_left; + + // not reduced left elements + for (j = 0; j < free_left; j++) + { + // f (i + 1, j + 1) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j ].node, + curr_base[ j+1 ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + + for (int icol = 1; icol <= nb_col; icol++) + { + // add "H" + reduceFunction( curr_base, next_base, j, next_base_len, quad, uv, y, myHelper, S ); + + j += reduce_grp_size; + + // elements in the middle of "columns" added for symmetry + if ( free_middle > 0 && ( nb_col % 2 == 0 ) && icol == nb_col / 2 ) + { + for (int imiddle = 1; imiddle <= free_middle; imiddle++) { + // f (i + 1, j + imiddle) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j-1+imiddle ].node, + curr_base[ j +imiddle ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + j += free_middle; + } + } + + // not reduced right elements + for (; j < curr_base_len-1; j++) { + // f (i + 1, j + 1) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j ].node, + curr_base[ j+1 ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + + curr_base_len = next_base_len + 1; + next_base_len = 0; + curr_base.swap( next_base ); + } + + } + else if ( is_tree_42 || is_tree_31 ) + { + // "tree" simple reduce "42": 2->4->8->16->32->... + // + // .-------------------------------.-------------------------------. nr + // | \ | / | + // | \ .---------------.---------------. / | + // | | | | | + // .---------------.---------------.---------------.---------------. + // | \ | / | \ | / | + // | \ .-------.-------. / | \ .-------.-------. / | + // | | | | | | | | | + // .-------.-------.-------.-------.-------.-------.-------.-------. i + // |\ | /|\ | /|\ | /|\ | /| + // | \.---.---./ | \.---.---./ | \.---.---./ | \.---.---./ | + // | | | | | | | | | | | | | | | | | + // .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---. + // |\ | /|\ | /|\ | /|\ | /|\ | /|\ | /|\ | /|\ | /| + // | .-.-. | .-.-. | .-.-. | .-.-. | .-.-. | .-.-. | .-.-. | .-.-. | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + // .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. 1 + // 1 j nb + + // "tree" simple reduce "31": 1->3->9->27->... + // + // .-----------------------------------------------------. nr + // | \ / | + // | .-----------------. | + // | | | | + // .-----------------.-----------------.-----------------. + // | \ / | \ / | \ / | + // | .-----. | .-----. | .-----. | i + // | | | | | | | | | | + // .-----.-----.-----.-----.-----.-----.-----.-----.-----. + // |\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /|\ /| + // | .-. | .-. | .-. | .-. | .-. | .-. | .-. | .-. | .-. | + // | | | | | | | | | | | | | | | | | | | | | | | | | | | | + // .-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. 1 + // 1 j nb + + PReduceFunction reduceFunction = & ( is_tree_42 ? reduce42 : reduce31 ); + + const int reduce_grp_size = is_tree_42 ? 4 : 3; + + for (i = 1; i < nr; i++) // layer by layer + { + // to stop reducing, if number of nodes reaches nt + int delta = curr_base_len - nt; + + // to calculate normalized parameter, we must know number of points in next layer + int nb_reduce_groups = (curr_base_len - 1) / reduce_grp_size; + int nb_next = nb_reduce_groups * (reduce_grp_size-2) + (curr_base_len - nb_reduce_groups*reduce_grp_size); + if (nb_next < nt) nb_next = nt; + + const double y = uv_el[ i ].normParam; + + if ( i + 1 == nr ) // top + { + next_base = uv_et; + } + else + { + next_base.clear(); + next_base.resize( nb_next, nullUVPtStruct ); + next_base.front() = uv_el[i]; + next_base.back() = uv_er[i]; + + // compute normalized param u + double du = 1. / ( nb_next - 1 ); + next_base[0].normParam = 0.; + for ( j = 1; j < nb_next; ++j ) + next_base[j].normParam = next_base[j-1].normParam + du; + } + uv[ UV_L ].SetCoord( next_base.front().u, next_base.front().v ); + uv[ UV_R ].SetCoord( next_base.back().u, next_base.back().v ); + + for (j = 0; j+reduce_grp_size < curr_base_len && delta > 0; j+=reduce_grp_size, delta-=2) + { + reduceFunction( curr_base, next_base, j, next_base_len, quad, uv, y, myHelper, S ); + } + + // not reduced side elements (if any) + for (; j < curr_base_len-1; j++) + { + // f (i + 1, j + 1) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j ].node, + curr_base[ j+1 ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + curr_base_len = next_base_len + 1; + next_base_len = 0; + curr_base.swap( next_base ); + } + } // end "tree" simple reduce + + else if ( is_lin_42 || is_lin_31 ) { + // "linear" simple reduce "31": 2->6->10->14 + // + // .-----------------------------.-----------------------------. nr + // | \ / | \ / | + // | .---------. | .---------. | + // | | | | | | | + // .---------.---------.---------.---------.---------.---------. + // | / \ / \ | / \ / \ | + // | / .-----. \ | / .-----. \ | i + // | / | | \ | / | | \ | + // .-----.-----.-----.-----.-----.-----.-----.-----.-----.-----. + // | / / \ / \ \ | / / \ / \ \ | + // | / / .-. \ \ | / / .-. \ \ | + // | / / / \ \ \ | / / / \ \ \ | + // .--.----.---.-----.---.-----.-.--.----.---.-----.---.-----.-. 1 + // 1 j nb + + // "linear" simple reduce "42": 4->8->12->16 + // + // .---------------.---------------.---------------.---------------. nr + // | \ | / | \ | / | + // | \ .-------.-------. / | \ .-------.-------. / | + // | | | | | | | | | + // .-------.-------.-------.-------.-------.-------.-------.-------. + // | / \ | / \ | / \ | / \ | + // | / \.----.----./ \ | / \.----.----./ \ | i + // | / | | | \ | / | | | \ | + // .-----.----.----.----.----.-----.-----.----.----.----.----.-----. + // | / / \ | / \ \ | / / \ | / \ \ | + // | / / .-.-. \ \ | / / .-.-. \ \ | + // | / / / | \ \ \ | / / / | \ \ \ | + // .---.---.---.---.---.---.---.---.---.---.---.---.---.---.---.---. 1 + // 1 j nb + + // nt = 5, nb = 7, nr = 4 + //int delta_all = 2; + //int delta_one_col = 6; + //int nb_col = 0; + //int remainder = 2; + //if (remainder > 0) nb_col++; + //nb_col = 1; + //int free_left = 1; + //free_left += 2; + //int free_middle = 4; + + int delta_all = nb - nt; + int delta_one_col = (nr - 1) * 2; + int nb_col = delta_all / delta_one_col; + int remainder = delta_all - nb_col * delta_one_col; + if (remainder > 0) { + nb_col++; + } + const int col_top_size = is_lin_42 ? 2 : 1; + int free_left = ((nt - 1) - nb_col * col_top_size) / 2; + free_left += nr - 2; + int free_middle = (nr - 2) * 2; + if (remainder > 0 && nb_col == 1) { + int nb_rows_short_col = remainder / 2; + int nb_rows_thrown = (nr - 1) - nb_rows_short_col; + free_left -= nb_rows_thrown; + } + + // nt = 5, nb = 17, nr = 4 + //int delta_all = 12; + //int delta_one_col = 6; + //int nb_col = 2; + //int remainder = 0; + //int free_left = 2; + //int free_middle = 4; + + PReduceFunction reduceFunction = & ( is_lin_42 ? reduce42 : reduce31 ); + + const int reduce_grp_size = is_lin_42 ? 4 : 3; + + for (i = 1; i < nr; i++, free_middle -= 2, free_left -= 1) // layer by layer + { + // to calculate normalized parameter, we must know number of points in next layer + int nb_next = curr_base_len - nb_col * 2; + if (remainder > 0 && i > remainder / 2) + // take into account short "column" + nb_next += 2; + if (nb_next < nt) nb_next = nt; + + const double y = uv_el[ i ].normParam; + + if ( i + 1 == nr ) // top + { + next_base = uv_et; + } + else + { + next_base.clear(); + next_base.resize( nb_next, nullUVPtStruct ); + next_base.front() = uv_el[i]; + next_base.back() = uv_er[i]; + + // compute normalized param u + double du = 1. / ( nb_next - 1 ); + next_base[0].normParam = 0.; + for ( j = 1; j < nb_next; ++j ) + next_base[j].normParam = next_base[j-1].normParam + du; + } + uv[ UV_L ].SetCoord( next_base.front().u, next_base.front().v ); + uv[ UV_R ].SetCoord( next_base.back().u, next_base.back().v ); + + // not reduced left elements + for (j = 0; j < free_left; j++) + { + // f (i + 1, j + 1) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j ].node, + curr_base[ j+1 ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + + for (int icol = 1; icol <= nb_col; icol++) { + + if (remainder > 0 && icol == nb_col && i > remainder / 2) + // stop short "column" + break; + + // add "H" + reduceFunction( curr_base, next_base, j, next_base_len, quad, uv, y, myHelper, S ); + + j += reduce_grp_size; + + // not reduced middle elements + if (icol < nb_col) { + if (remainder > 0 && icol == nb_col - 1 && i > remainder / 2) + // pass middle elements before stopped short "column" + break; + + int free_add = free_middle; + if (remainder > 0 && icol == nb_col - 1) + // next "column" is short + free_add -= (nr - 1) - (remainder / 2); + + for (int imiddle = 1; imiddle <= free_add; imiddle++) { + // f (i + 1, j + imiddle) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j-1+imiddle ].node, + curr_base[ j +imiddle ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + j += free_add; + } + } + + // not reduced right elements + for (; j < curr_base_len-1; j++) { + // f (i + 1, j + 1) + const SMDS_MeshNode*& Nf = next_base[++next_base_len].node; + if ( !Nf ) + Nf = makeNode( next_base[ next_base_len ], y, quad, uv, myHelper, S ); + + myHelper->AddFace(curr_base[ j ].node, + curr_base[ j+1 ].node, + Nf, + next_base[ next_base_len-1 ].node); + } + + curr_base_len = next_base_len + 1; + next_base_len = 0; + curr_base.swap( next_base ); + } + + } // end "linear" simple reduce + + else { + return false; + } + } // end Simple Reduce implementation + + bool isOk = true; + return isOk; +} + +//================================================================================ +namespace // data for smoothing +{ + struct TSmoothNode; + // -------------------------------------------------------------------------------- + /*! + * \brief Structure used to check validity of node position after smoothing. + * It holds two nodes connected to a smoothed node and belonging to + * one mesh face + */ + struct TTriangle + { + TSmoothNode* _n1; + TSmoothNode* _n2; + TTriangle( TSmoothNode* n1=0, TSmoothNode* n2=0 ): _n1(n1), _n2(n2) {} + + inline bool IsForward( gp_UV uv ) const; + }; + // -------------------------------------------------------------------------------- + /*! + * \brief Data of a smoothed node + */ + struct TSmoothNode + { + gp_XY _uv; + vector< TTriangle > _triangles; // if empty, then node is not movable + }; + // -------------------------------------------------------------------------------- + inline bool TTriangle::IsForward( gp_UV uv ) const + { + gp_Vec2d v1( uv, _n1->_uv ), v2( uv, _n2->_uv ); + double d = v1 ^ v2; + return d > 1e-100; + } +} + +//================================================================================ +/*! + * \brief Set UV of nodes on degenerated VERTEXes in the middle of degenerated EDGE + * + * WARNING: this method must be called AFTER retrieving UVPtStruct's from quad + */ +//================================================================================ + +void StdMeshers_Quadrangle_2D::UpdateDegenUV(FaceQuadStruct* quad) +{ + for ( unsigned i = 0; i < quad->side.size(); ++i ) + { + StdMeshers_FaceSide* side = quad->side[i]; + const vector& uvVec = side->GetUVPtStruct(); + + // find which end of the side is on degenerated shape + int degenInd = -1; + if ( myHelper->IsDegenShape( uvVec[0].node->getshapeId() )) + degenInd = 0; + else if ( myHelper->IsDegenShape( uvVec.back().node->getshapeId() )) + degenInd = uvVec.size() - 1; + else + continue; + + // find another side sharing the degenerated shape + bool isPrev = ( degenInd == 0 ); + if ( i >= TOP_SIDE ) + isPrev = !isPrev; + int i2 = ( isPrev ? ( i + 3 ) : ( i + 1 )) % 4; + StdMeshers_FaceSide* side2 = quad->side[ i2 ]; + const vector& uvVec2 = side2->GetUVPtStruct(); + int degenInd2 = -1; + if ( uvVec[ degenInd ].node == uvVec2[0].node ) + degenInd2 = 0; + else if ( uvVec[ degenInd ].node == uvVec2.back().node ) + degenInd2 = uvVec2.size() - 1; + else + throw SALOME_Exception( LOCALIZED( "Logical error" )); + + // move UV in the middle + uvPtStruct& uv1 = const_cast( uvVec [ degenInd ]); + uvPtStruct& uv2 = const_cast( uvVec2[ degenInd2 ]); + uv1.u = uv2.u = 0.5 * ( uv1.u + uv2.u ); + uv1.v = uv2.v = 0.5 * ( uv1.v + uv2.v ); + } +} + +//================================================================================ +/*! + * \brief Perform smoothing of 2D elements on a FACE with ignored degenerated EDGE + */ +//================================================================================ + +void StdMeshers_Quadrangle_2D::Smooth (FaceQuadStruct* quad) +{ + if ( !myNeedSmooth ) return; + + // Get nodes to smooth + + typedef map< const SMDS_MeshNode*, TSmoothNode, TIDCompare > TNo2SmooNoMap; + TNo2SmooNoMap smooNoMap; + + const TopoDS_Face& geomFace = TopoDS::Face( myHelper->GetSubShape() ); + SMESHDS_Mesh* meshDS = myHelper->GetMeshDS(); + SMESHDS_SubMesh* fSubMesh = meshDS->MeshElements( geomFace ); + SMDS_NodeIteratorPtr nIt = fSubMesh->GetNodes(); + while ( nIt->more() ) // loop on nodes bound to a FACE + { + const SMDS_MeshNode* node = nIt->next(); + TSmoothNode & sNode = smooNoMap[ node ]; + sNode._uv = myHelper->GetNodeUV( geomFace, node ); + + // set sNode._triangles + SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator( SMDSAbs_Face ); + while ( fIt->more() ) + { + const SMDS_MeshElement* face = fIt->next(); + const int nbN = face->NbCornerNodes(); + const int nInd = face->GetNodeIndex( node ); + const int prevInd = myHelper->WrapIndex( nInd - 1, nbN ); + const int nextInd = myHelper->WrapIndex( nInd + 1, nbN ); + const SMDS_MeshNode* prevNode = face->GetNode( prevInd ); + const SMDS_MeshNode* nextNode = face->GetNode( nextInd ); + sNode._triangles.push_back( TTriangle( & smooNoMap[ prevNode ], + & smooNoMap[ nextNode ])); + } + } + // set _uv of smooth nodes on FACE boundary + for ( unsigned i = 0; i < quad->side.size(); ++i ) + { + const vector& uvVec = quad->side[i]->GetUVPtStruct(); + for ( unsigned j = 0; j < uvVec.size(); ++j ) + { + TSmoothNode & sNode = smooNoMap[ uvVec[j].node ]; + sNode._uv.SetCoord( uvVec[j].u, uvVec[j].v ); + } + } + + // define refernce orientation in 2D + TNo2SmooNoMap::iterator n2sn = smooNoMap.begin(); + for ( ; n2sn != smooNoMap.end(); ++n2sn ) + if ( !n2sn->second._triangles.empty() ) + break; + if ( n2sn == smooNoMap.end() ) return; + const TSmoothNode & sampleNode = n2sn->second; + const bool refForward = ( sampleNode._triangles[0].IsForward( sampleNode._uv )); + + // Smoothing + + for ( int iLoop = 0; iLoop < 5; ++iLoop ) + { + for ( n2sn = smooNoMap.begin(); n2sn != smooNoMap.end(); ++n2sn ) + { + TSmoothNode& sNode = n2sn->second; + if ( sNode._triangles.empty() ) + continue; // not movable node + + // compute a new UV + gp_XY newUV (0,0); + for ( unsigned i = 0; i < sNode._triangles.size(); ++i ) + newUV += sNode._triangles[i]._n1->_uv; + newUV /= sNode._triangles.size(); + + // check validity of the newUV + bool isValid = true; + for ( unsigned i = 0; i < sNode._triangles.size() && isValid; ++i ) + isValid = ( sNode._triangles[i].IsForward( newUV ) == refForward ); + + if ( isValid ) + sNode._uv = newUV; + } + } + + // Set new XYZ to the smoothed nodes + + Handle(Geom_Surface) surface = BRep_Tool::Surface( geomFace ); + + for ( n2sn = smooNoMap.begin(); n2sn != smooNoMap.end(); ++n2sn ) + { + TSmoothNode& sNode = n2sn->second; + if ( sNode._triangles.empty() ) + continue; // not movable node + + SMDS_MeshNode* node = const_cast< SMDS_MeshNode*>( n2sn->first ); + gp_Pnt xyz = surface->Value( sNode._uv.X(), sNode._uv.Y() ); + meshDS->MoveNode( node, xyz.X(), xyz.Y(), xyz.Z() ); + + // store the new UV + node->SetPosition( SMDS_PositionPtr( new SMDS_FacePosition( sNode._uv.X(), sNode._uv.Y() ))); + } + + // Move medium nodes in quadratic mesh + if ( _quadraticMesh ) + { + const TLinkNodeMap& links = myHelper->GetTLinkNodeMap(); + TLinkNodeMap::const_iterator linkIt = links.begin(); + for ( ; linkIt != links.end(); ++linkIt ) + { + const SMESH_TLink& link = linkIt->first; + SMDS_MeshNode* node = const_cast< SMDS_MeshNode*>( linkIt->second ); + + if ( node->getshapeId() != myHelper->GetSubShapeID() ) + continue; // medium node is on EDGE or VERTEX + + gp_XY uv1 = myHelper->GetNodeUV( geomFace, link.node1(), node ); + gp_XY uv2 = myHelper->GetNodeUV( geomFace, link.node2(), node ); + + gp_XY uv = myHelper->GetMiddleUV( surface, uv1, uv2 ); + node->SetPosition( SMDS_PositionPtr( new SMDS_FacePosition( uv.X(), uv.Y() ))); + + gp_Pnt xyz = surface->Value( uv.X(), uv.Y() ); + meshDS->MoveNode( node, xyz.X(), xyz.Y(), xyz.Z() ); + } } } diff --git a/src/StdMeshers/StdMeshers_Quadrangle_2D.hxx b/src/StdMeshers/StdMeshers_Quadrangle_2D.hxx index 18c1daeca..3f9202e86 100644 --- a/src/StdMeshers/StdMeshers_Quadrangle_2D.hxx +++ b/src/StdMeshers/StdMeshers_Quadrangle_2D.hxx @@ -1,39 +1,41 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Quadrangle_2D.hxx // Moved here from SMESH_Quadrangle_2D.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ -// + #ifndef _SMESH_QUADRANGLE_2D_HXX_ #define _SMESH_QUADRANGLE_2D_HXX_ #include "SMESH_StdMeshers.hxx" +#include "StdMeshers_QuadrangleParams.hxx" + #include "SMESH_2D_Algo.hxx" #include "Utils_SALOME_Exception.hxx" +#include + class SMESH_Mesh; class SMESH_MesherHelper; class StdMeshers_FaceSide; @@ -49,6 +51,7 @@ typedef struct faceQuadStruct std::vector< StdMeshers_FaceSide*> side; bool isEdgeOut[4]; // true, if an edge has more nodes, than the opposite UVPtStruct* uv_grid; + TopoDS_Face face; ~faceQuadStruct(); } FaceQuadStruct; @@ -63,20 +66,29 @@ public: SMESH_Hypothesis::Hypothesis_Status& aStatus); virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape); + const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); FaceQuadStruct* CheckAnd2Dcompute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, + const TopoDS_Shape& aShape, const bool CreateQuadratic); -protected: - FaceQuadStruct* CheckNbEdges(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); +protected: + + bool CheckNbEdgesForEvaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap, + std::vector& aNbNodes, + bool& IsQuadratic); + bool SetNormalizedGrid(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, - FaceQuadStruct*& quad); + const TopoDS_Shape& aShape, + FaceQuadStruct*& quad); void SplitQuad(SMESHDS_Mesh *theMeshDS, const int theFaceID, @@ -85,34 +97,40 @@ protected: const SMDS_MeshNode* theNode3, const SMDS_MeshNode* theNode4); - /** - * Special function for creation only quandrangle faces - */ bool ComputeQuadPref(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape, FaceQuadStruct* quad); - UVPtStruct* LoadEdgePoints2(SMESH_Mesh& aMesh, - const TopoDS_Face& F, const TopoDS_Edge& E, - bool IsReverse); + bool EvaluateQuadPref(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + std::vector& aNbNodes, + MapShapeNbElems& aResMap, + bool IsQuadratic); + + bool ComputeReduced (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + FaceQuadStruct* quad); + + void UpdateDegenUV(FaceQuadStruct* quad); - UVPtStruct* LoadEdgePoints(SMESH_Mesh& aMesh, - const TopoDS_Face& F, const TopoDS_Edge& E, - double first, double last); + void Smooth (FaceQuadStruct* quad); - UVPtStruct* MakeEdgePoints(SMESH_Mesh& aMesh, - const TopoDS_Face& F, const TopoDS_Edge& E, - double first, double last, int nb_segm); // true if QuadranglePreference hypothesis is assigned that forces // construction of quadrangles if the number of nodes on opposite edges - // is not the same in the case where the global number of nodes on edges is even + // is not the same in the case where the global number of nodes on edges + // is even bool myQuadranglePreference; bool myTrianglePreference; - SMESH_MesherHelper* myTool; // tool for working with quadratic elements + int myTriaVertexID; + + StdMeshers_QuadType myQuadType; + + SMESH_MesherHelper* myHelper; // tool for working with quadratic elements + + bool myNeedSmooth; }; #endif diff --git a/src/StdMeshers/StdMeshers_QuadraticMesh.cxx b/src/StdMeshers/StdMeshers_QuadraticMesh.cxx index 4764356f2..b58504b1f 100644 --- a/src/StdMeshers/StdMeshers_QuadraticMesh.cxx +++ b/src/StdMeshers/StdMeshers_QuadraticMesh.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_QuadraticMesh : implementaion of SMESH idl descriptions // File : StdMeshers_QuadraticMesh.cxx // Module : SMESH - +// #include "StdMeshers_QuadraticMesh.hxx" #include "utilities.h" diff --git a/src/StdMeshers/StdMeshers_QuadraticMesh.hxx b/src/StdMeshers/StdMeshers_QuadraticMesh.hxx index 9972469c0..7d870408b 100644 --- a/src/StdMeshers/StdMeshers_QuadraticMesh.hxx +++ b/src/StdMeshers/StdMeshers_QuadraticMesh.hxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_QuadraticMesh.hxx // Module : SMESH - +// #ifndef _StdMeshers_QuadraticMesh_HXX_ #define _StdMeshers_QuadraticMesh_HXX_ diff --git a/src/StdMeshers/StdMeshers_RadialPrism_3D.cxx b/src/StdMeshers/StdMeshers_RadialPrism_3D.cxx index b19f79aee..6ae4ac787 100644 --- a/src/StdMeshers/StdMeshers_RadialPrism_3D.cxx +++ b/src/StdMeshers/StdMeshers_RadialPrism_3D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_RadialPrism_3D.cxx // Module : SMESH @@ -37,7 +38,6 @@ #include "SMESHDS_SubMesh.hxx" #include "SMESH_Gen.hxx" #include "SMESH_Mesh.hxx" -#include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" #include "SMESH_subMesh.hxx" @@ -46,10 +46,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -70,7 +72,7 @@ StdMeshers_RadialPrism_3D::StdMeshers_RadialPrism_3D(int hypId, int studyId, SME :SMESH_3D_Algo(hypId, studyId, gen) { _name = "RadialPrism_3D"; - _shapeType = (1 << TopAbs_SOLID); // 1 bit per shape type + _shapeType = (1 << TopAbs_SOLID); // 1 bit per shape type _compatibleHypothesis.push_back("LayerDistribution"); _compatibleHypothesis.push_back("NumberOfLayers"); @@ -170,12 +172,12 @@ bool StdMeshers_RadialPrism_3D::Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& a return error(COMPERR_BAD_SHAPE, SMESH_Comment("Must be 2 shells but not ")<ShapeToIndex( outFace )); } else { - inFace = TopoDS::Face( shape2ShapeMap( outFace )); + inFace = TopoDS::Face( shape2ShapeMap( outFace, /*isOut=*/true )); } // Find matching nodes of in and out faces @@ -387,3 +389,171 @@ bool StdMeshers_RadialPrism_3D::computeLayerPositions(const gp_Pnt& pIn, } RETURN_BAD_RESULT("Bad hypothesis"); } + + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_RadialPrism_3D::Evaluate(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + MapShapeNbElems& aResMap) +{ + // get 2 shells + TopoDS_Solid solid = TopoDS::Solid( aShape ); + TopoDS_Shell outerShell = BRepTools::OuterShell( solid ); + TopoDS_Shape innerShell; + int nbShells = 0; + for ( TopoDS_Iterator It (solid); It.More(); It.Next(), ++nbShells ) + if ( !outerShell.IsSame( It.Value() )) + innerShell = It.Value(); + if ( nbShells != 2 ) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + + // Associate sub-shapes of the shells + TAssocTool::TShapeShapeMap shape2ShapeMap; + if ( !TAssocTool::FindSubShapeAssociation( outerShell, &aMesh, + innerShell, &aMesh, + shape2ShapeMap) ) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + + // get info for outer shell + int nb0d_Out=0, nb2d_3_Out=0, nb2d_4_Out=0; + //TopTools_SequenceOfShape FacesOut; + for (TopExp_Explorer exp(outerShell, TopAbs_FACE); exp.More(); exp.Next()) { + //FacesOut.Append(exp.Current()); + SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current()); + MapShapeNbElemsItr anIt = aResMap.find(aSubMesh); + std::vector aVec = (*anIt).second; + nb0d_Out += aVec[SMDSEntity_Node]; + nb2d_3_Out += Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + nb2d_4_Out += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + } + int nb1d_Out = 0; + TopTools_MapOfShape tmpMap; + for (TopExp_Explorer exp(outerShell, TopAbs_EDGE); exp.More(); exp.Next()) { + if( tmpMap.Contains( exp.Current() ) ) + continue; + tmpMap.Add( exp.Current() ); + SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current()); + MapShapeNbElemsItr anIt = aResMap.find(aSubMesh); + std::vector aVec = (*anIt).second; + nb0d_Out += aVec[SMDSEntity_Node]; + nb1d_Out += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + } + tmpMap.Clear(); + for (TopExp_Explorer exp(outerShell, TopAbs_VERTEX); exp.More(); exp.Next()) { + if( tmpMap.Contains( exp.Current() ) ) + continue; + tmpMap.Add( exp.Current() ); + nb0d_Out++; + } + + // get info for inner shell + int nb0d_In=0, nb2d_3_In=0, nb2d_4_In=0; + //TopTools_SequenceOfShape FacesIn; + for (TopExp_Explorer exp(innerShell, TopAbs_FACE); exp.More(); exp.Next()) { + //FacesIn.Append(exp.Current()); + SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current()); + MapShapeNbElemsItr anIt = aResMap.find(aSubMesh); + std::vector aVec = (*anIt).second; + nb0d_In += aVec[SMDSEntity_Node]; + nb2d_3_In += Max(aVec[SMDSEntity_Triangle],aVec[SMDSEntity_Quad_Triangle]); + nb2d_4_In += Max(aVec[SMDSEntity_Quadrangle],aVec[SMDSEntity_Quad_Quadrangle]); + } + int nb1d_In = 0; + tmpMap.Clear(); + bool IsQuadratic = false; + bool IsFirst = true; + for (TopExp_Explorer exp(innerShell, TopAbs_EDGE); exp.More(); exp.Next()) { + if( tmpMap.Contains( exp.Current() ) ) + continue; + tmpMap.Add( exp.Current() ); + SMESH_subMesh *aSubMesh = aMesh.GetSubMesh(exp.Current()); + MapShapeNbElemsItr anIt = aResMap.find(aSubMesh); + std::vector aVec = (*anIt).second; + nb0d_In += aVec[SMDSEntity_Node]; + nb1d_In += Max(aVec[SMDSEntity_Edge],aVec[SMDSEntity_Quad_Edge]); + if(IsFirst) { + IsQuadratic = (aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]); + IsFirst = false; + } + } + tmpMap.Clear(); + for (TopExp_Explorer exp(innerShell, TopAbs_VERTEX); exp.More(); exp.Next()) { + if( tmpMap.Contains( exp.Current() ) ) + continue; + tmpMap.Add( exp.Current() ); + nb0d_In++; + } + + bool IsOK = (nb0d_Out==nb0d_In) && (nb1d_Out==nb1d_In) && + (nb2d_3_Out==nb2d_3_In) && (nb2d_4_Out==nb2d_4_In); + if(!IsOK) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + + int nbLayers = 0; + if( myNbLayerHypo ) { + nbLayers = myNbLayerHypo->GetNumberOfLayers(); + } + if ( myDistributionHypo ) { + if ( !myDistributionHypo->GetLayerDistribution() ) { + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; iGetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + TopExp_Explorer exp(outerShell, TopAbs_VERTEX); + TopoDS_Vertex Vout = TopoDS::Vertex(exp.Current()); + TopoDS_Vertex Vin = TopoDS::Vertex( shape2ShapeMap(Vout) ); + if ( myLayerPositions.empty() ) { + gp_Pnt pIn = BRep_Tool::Pnt(Vin); + gp_Pnt pOut = BRep_Tool::Pnt(Vout); + computeLayerPositions( pIn, pOut ); + } + nbLayers = myLayerPositions.size() + 1; + } + + std::vector aResVec(SMDSEntity_Last); + for(int i=SMDSEntity_Node; i TNodeColumn; diff --git a/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx new file mode 100644 index 000000000..d93c64eb6 --- /dev/null +++ b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.cxx @@ -0,0 +1,1285 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : StdMeshers_RadialQuadrangle_1D2D.cxx +// Module : SMESH +// Created : Fri Oct 20 11:37:07 2006 +// Author : Edward AGAPOV (eap) + +#include "StdMeshers_RadialQuadrangle_1D2D.hxx" + +#include "StdMeshers_NumberOfLayers.hxx" +#include "StdMeshers_LayerDistribution.hxx" +#include "StdMeshers_Regular_1D.hxx" +#include "StdMeshers_NumberOfSegments.hxx" + +#include "SMDS_MeshNode.hxx" +#include "SMESHDS_SubMesh.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_HypoFilter.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_subMeshEventListener.hxx" + +#include "utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + +#define RETURN_BAD_RESULT(msg) { MESSAGE(")-: Error: " << msg); return false; } +#define gpXYZ(n) gp_XYZ(n->X(),n->Y(),n->Z()) + + +//======================================================================= +//function : StdMeshers_RadialQuadrangle_1D2D +//purpose : +//======================================================================= + +StdMeshers_RadialQuadrangle_1D2D::StdMeshers_RadialQuadrangle_1D2D(int hypId, + int studyId, + SMESH_Gen* gen) + :SMESH_2D_Algo(hypId, studyId, gen) +{ + _name = "RadialQuadrangle_1D2D"; + _shapeType = (1 << TopAbs_FACE); // 1 bit per shape type + + _compatibleHypothesis.push_back("LayerDistribution2D"); + _compatibleHypothesis.push_back("NumberOfLayers2D"); + myNbLayerHypo = 0; + myDistributionHypo = 0; + _requireDiscreteBoundary = false; + _supportSubmeshes = true; +} + + +//================================================================================ +/*! + * \brief Destructor + */ +//================================================================================ + +StdMeshers_RadialQuadrangle_1D2D::~StdMeshers_RadialQuadrangle_1D2D() +{} + + +//======================================================================= +//function : CheckHypothesis +//purpose : +//======================================================================= + +bool StdMeshers_RadialQuadrangle_1D2D::CheckHypothesis + (SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus) +{ + // check aShape + myNbLayerHypo = 0; + myDistributionHypo = 0; + + list ::const_iterator itl; + + const list &hyps = GetUsedHypothesis(aMesh, aShape); + if ( hyps.size() == 0 ) { + aStatus = SMESH_Hypothesis::HYP_OK; + return true; // can work with no hypothesis + } + + if ( hyps.size() > 1 ) { + aStatus = SMESH_Hypothesis::HYP_ALREADY_EXIST; + return false; + } + + const SMESHDS_Hypothesis *theHyp = hyps.front(); + + string hypName = theHyp->GetName(); + + if (hypName == "NumberOfLayers2D") { + myNbLayerHypo = static_cast(theHyp); + aStatus = SMESH_Hypothesis::HYP_OK; + return true; + } + if (hypName == "LayerDistribution2D") { + myDistributionHypo = static_cast(theHyp); + aStatus = SMESH_Hypothesis::HYP_OK; + return true; + } + aStatus = SMESH_Hypothesis::HYP_INCOMPATIBLE; + return true; +} + +namespace +{ + // ------------------------------------------------------------------------------ + /*! + * \brief Listener used to mark edges meshed by StdMeshers_RadialQuadrangle_1D2D + */ + class TEdgeMarker : public SMESH_subMeshEventListener + { + TEdgeMarker(): SMESH_subMeshEventListener(/*isDeletable=*/false, + "StdMeshers_RadialQuadrangle_1D2D::TEdgeMarker") {} + public: + //!< Return static listener + static SMESH_subMeshEventListener* getListener() + { + static TEdgeMarker theEdgeMarker; + return &theEdgeMarker; + } + //! Clear face sumbesh if something happens on edges + void ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* edgeSubMesh, + EventListenerData* data, + const SMESH_Hypothesis* /*hyp*/) + { + if ( data && !data->mySubMeshes.empty() && eventType == SMESH_subMesh::ALGO_EVENT) + { + ASSERT( data->mySubMeshes.front() != edgeSubMesh ); + SMESH_subMesh* faceSubMesh = data->mySubMeshes.front(); + faceSubMesh->ComputeStateEngine( SMESH_subMesh::CLEAN ); + } + } + }; + + // ------------------------------------------------------------------------------ + /*! + * \brief Mark an edge as computed by StdMeshers_RadialQuadrangle_1D2D + */ + void markEdgeAsComputedByMe(const TopoDS_Edge& edge, SMESH_subMesh* faceSubMesh) + { + if ( SMESH_subMesh* edgeSM = faceSubMesh->GetFather()->GetSubMeshContaining( edge )) + { + if ( !edgeSM->GetEventListenerData( TEdgeMarker::getListener() )) + faceSubMesh->SetEventListener( TEdgeMarker::getListener(), + SMESH_subMeshEventListenerData::MakeData(faceSubMesh), + edgeSM); + } + } + // ------------------------------------------------------------------------------ + /*! + * \brief Return true if a radial edge was meshed with StdMeshers_RadialQuadrangle_1D2D with + * the same radial distribution + */ +// bool isEdgeCompatiballyMeshed(const TopoDS_Edge& edge, SMESH_subMesh* faceSubMesh) +// { +// if ( SMESH_subMesh* edgeSM = faceSubMesh->GetFather()->GetSubMeshContaining( edge )) +// { +// if ( SMESH_subMeshEventListenerData* otherFaceData = +// edgeSM->GetEventListenerData( TEdgeMarker::getListener() )) +// { +// // compare hypothesis aplied to two disk faces sharing radial edges +// SMESH_Mesh& mesh = *faceSubMesh->GetFather(); +// SMESH_Algo* radialQuadAlgo = mesh.GetGen()->GetAlgo(mesh, faceSubMesh->GetSubShape() ); +// SMESH_subMesh* otherFaceSubMesh = otherFaceData->mySubMeshes.front(); +// list hyps1 = +// radialQuadAlgo->GetUsedHypothesis( mesh, faceSubMesh->GetSubShape()); +// list hyps2 = +// radialQuadAlgo->GetUsedHypothesis( mesh, otherFaceSubMesh->GetSubShape()); +// if( hyps1.empty() && hyps2.empty() ) +// return true; // defaul hyps +// if ( hyps1.size() != hyps2.size() ) +// return false; +// return *hyps1.front() == *hyps2.front(); +// } +// } +// return false; +// } + + //================================================================================ + /*! + * \brief Return base curve of the edge and extremum parameters + */ + //================================================================================ + + Handle(Geom_Curve) getCurve(const TopoDS_Edge& edge, double* f=0, double* l=0) + { + Handle(Geom_Curve) C; + if ( !edge.IsNull() ) + { + double first = 0., last = 0.; + C = BRep_Tool::Curve(edge, first, last); + if ( !C.IsNull() ) + { + Handle(Geom_TrimmedCurve) tc = Handle(Geom_TrimmedCurve)::DownCast(C); + while( !tc.IsNull() ) { + C = tc->BasisCurve(); + tc = Handle(Geom_TrimmedCurve)::DownCast(C); + } + if ( f ) *f = first; + if ( l ) *l = last; + } + } + return C; + } + + //================================================================================ + /*! + * \brief Return edges of the face + * \retval int - nb of edges + */ + //================================================================================ + + int analyseFace(const TopoDS_Shape& face, + TopoDS_Edge& CircEdge, + TopoDS_Edge& LinEdge1, + TopoDS_Edge& LinEdge2) + { + CircEdge.Nullify(); LinEdge1.Nullify(); LinEdge2.Nullify(); + int nbe = 0; + + for ( TopExp_Explorer exp( face, TopAbs_EDGE ); exp.More(); exp.Next(), ++nbe ) + { + const TopoDS_Edge& E = TopoDS::Edge( exp.Current() ); + double f,l; + Handle(Geom_Curve) C = getCurve(E,&f,&l); + if ( !C.IsNull() ) + { + if ( C->IsKind( STANDARD_TYPE(Geom_Circle))) + { + if ( CircEdge.IsNull() ) + CircEdge = E; + else + return 0; + } + else if ( LinEdge1.IsNull() ) + LinEdge1 = E; + else + LinEdge2 = E; + } + } + return nbe; + } + +//================================================================================ +//================================================================================ +/*! + * \brief Class computing layers distribution using data of + * StdMeshers_LayerDistribution hypothesis + */ +//================================================================================ +//================================================================================ + +class TNodeDistributor: public StdMeshers_Regular_1D +{ + list myUsedHyps; +public: + // ----------------------------------------------------------------------------- + static TNodeDistributor* GetDistributor(SMESH_Mesh& aMesh) + { + const int myID = -1000; + map < int, SMESH_1D_Algo * > & algoMap = aMesh.GetGen()->_map1D_Algo; + map < int, SMESH_1D_Algo * >::iterator id_algo = algoMap.find( myID ); + if ( id_algo == algoMap.end() ) + return new TNodeDistributor( myID, 0, aMesh.GetGen() ); + return static_cast< TNodeDistributor* >( id_algo->second ); + } + // ----------------------------------------------------------------------------- + //! Computes distribution of nodes on a straight line ending at pIn and pOut + bool Compute( vector< double > & positions, + gp_Pnt pIn, + gp_Pnt pOut, + SMESH_Mesh& aMesh, + const SMESH_Hypothesis* hyp1d) + { + if ( !hyp1d ) return error( "Invalid LayerDistribution hypothesis"); + + double len = pIn.Distance( pOut ); + if ( len <= DBL_MIN ) return error("Too close points of inner and outer shells"); + + myUsedHyps.clear(); + myUsedHyps.push_back( hyp1d ); + + TopoDS_Edge edge = BRepBuilderAPI_MakeEdge( pIn, pOut ); + SMESH_Hypothesis::Hypothesis_Status aStatus; + if ( !StdMeshers_Regular_1D::CheckHypothesis( aMesh, edge, aStatus )) + return error( "StdMeshers_Regular_1D::CheckHypothesis() failed " + "with LayerDistribution hypothesis"); + + BRepAdaptor_Curve C3D(edge); + double f = C3D.FirstParameter(), l = C3D.LastParameter(); + list< double > params; + if ( !StdMeshers_Regular_1D::computeInternalParameters( aMesh, C3D, len, f, l, params, false )) + return error("StdMeshers_Regular_1D failed to compute layers distribution"); + + positions.clear(); + positions.reserve( params.size() ); + for (list::iterator itU = params.begin(); itU != params.end(); itU++) + positions.push_back( *itU / len ); + return true; + } + // ----------------------------------------------------------------------------- + //! Make mesh on an adge using assigned 1d hyp or defaut nb of segments + bool ComputeCircularEdge(SMESH_Mesh& aMesh, + const TopoDS_Edge& anEdge) + { + _gen->Compute( aMesh, anEdge); + SMESH_subMesh *sm = aMesh.GetSubMesh(anEdge); + if ( sm->GetComputeState() != SMESH_subMesh::COMPUTE_OK) + { + // find any 1d hyp assigned (there can be a hyp w/o algo) + myUsedHyps = SMESH_Algo::GetUsedHypothesis(aMesh, anEdge, /*ignoreAux=*/true); + Hypothesis_Status aStatus; + if ( !StdMeshers_Regular_1D::CheckHypothesis( aMesh, anEdge, aStatus )) + { + // no valid 1d hyp assigned, use default nb of segments + _hypType = NB_SEGMENTS; + _ivalue[ DISTR_TYPE_IND ] = StdMeshers_NumberOfSegments::DT_Regular; + _ivalue[ NB_SEGMENTS_IND ] = _gen->GetDefaultNbSegments(); + } + return StdMeshers_Regular_1D::Compute( aMesh, anEdge ); + } + return true; + } + // ----------------------------------------------------------------------------- + //! Make mesh on an adge using assigned 1d hyp or defaut nb of segments + bool EvaluateCircularEdge(SMESH_Mesh& aMesh, + const TopoDS_Edge& anEdge, + MapShapeNbElems& aResMap) + { + _gen->Evaluate( aMesh, anEdge, aResMap ); + if ( aResMap.count( aMesh.GetSubMesh( anEdge ))) + return true; + + // find any 1d hyp assigned + myUsedHyps = SMESH_Algo::GetUsedHypothesis(aMesh, anEdge, /*ignoreAux=*/true); + Hypothesis_Status aStatus; + if ( !StdMeshers_Regular_1D::CheckHypothesis( aMesh, anEdge, aStatus )) + { + // no valid 1d hyp assigned, use default nb of segments + _hypType = NB_SEGMENTS; + _ivalue[ DISTR_TYPE_IND ] = StdMeshers_NumberOfSegments::DT_Regular; + _ivalue[ NB_SEGMENTS_IND ] = _gen->GetDefaultNbSegments(); + } + return StdMeshers_Regular_1D::Evaluate( aMesh, anEdge, aResMap ); + } +protected: + // ----------------------------------------------------------------------------- + TNodeDistributor( int hypId, int studyId, SMESH_Gen* gen) + : StdMeshers_Regular_1D( hypId, studyId, gen) + { + } + // ----------------------------------------------------------------------------- + virtual const list & + GetUsedHypothesis(SMESH_Mesh &, const TopoDS_Shape &, const bool) + { + return myUsedHyps; + } + // ----------------------------------------------------------------------------- +}; +} + +//======================================================================= +/*! + * \brief Allow algo to do something after persistent restoration + * \param subMesh - restored submesh + * + * call markEdgeAsComputedByMe() + */ +//======================================================================= + +void StdMeshers_RadialQuadrangle_1D2D::SubmeshRestored(SMESH_subMesh* faceSubMesh) +{ + if ( !faceSubMesh->IsEmpty() ) + { + TopoDS_Edge CircEdge, LinEdge1, LinEdge2; + analyseFace( faceSubMesh->GetSubShape(), CircEdge, LinEdge1, LinEdge2 ); + if ( !CircEdge.IsNull() ) markEdgeAsComputedByMe( CircEdge, faceSubMesh ); + if ( !LinEdge1.IsNull() ) markEdgeAsComputedByMe( LinEdge1, faceSubMesh ); + if ( !LinEdge2.IsNull() ) markEdgeAsComputedByMe( LinEdge2, faceSubMesh ); + } +} + +//======================================================================= +//function : Compute +//purpose : +//======================================================================= + +bool StdMeshers_RadialQuadrangle_1D2D::Compute(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape) +{ + SMESHDS_Mesh * meshDS = aMesh.GetMeshDS(); + + myHelper = new SMESH_MesherHelper( aMesh ); + myHelper->IsQuadraticSubMesh( aShape ); + // to delete helper at exit from Compute() + auto_ptr helperDeleter( myHelper ); + + TNodeDistributor* algo1d = TNodeDistributor::GetDistributor(aMesh); + + TopoDS_Edge CircEdge, LinEdge1, LinEdge2; + int nbe = analyseFace( aShape, CircEdge, LinEdge1, LinEdge2 ); + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge )); + if( nbe>3 || nbe < 1 || aCirc.IsNull() ) + return error("The face must be a full circle or a part of circle (i.e. the number of edges is less or equal to 3 and one of them is a circle curve)"); + + gp_Pnt P0, P1; + // points for rotation + TColgp_SequenceOfPnt Points; + // angles for rotation + TColStd_SequenceOfReal Angles; + // Nodes1 and Nodes2 - nodes along radiuses + // CNodes - nodes on circle edge + vector< const SMDS_MeshNode* > Nodes1, Nodes2, CNodes; + SMDS_MeshNode * NC; + // parameters edge nodes on face + TColgp_SequenceOfPnt2d Pnts2d1; + gp_Pnt2d PC; + + int faceID = meshDS->ShapeToIndex(aShape); + TopoDS_Face F = TopoDS::Face(aShape); + Handle(Geom_Surface) S = BRep_Tool::Surface(F); + + + if(nbe==1) + { + if (!algo1d->ComputeCircularEdge( aMesh, CircEdge )) + return error( algo1d->GetComputeError() ); + map< double, const SMDS_MeshNode* > theNodes; + if ( !GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes)) + return error("Circular edge is incorrectly meshed"); + + CNodes.clear(); + map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin(); + const SMDS_MeshNode* NF = (*itn).second; + CNodes.push_back( (*itn).second ); + double fang = (*itn).first; + if ( itn != theNodes.end() ) { + itn++; + for(; itn != theNodes.end(); itn++ ) { + CNodes.push_back( (*itn).second ); + double ang = (*itn).first - fang; + if( ang>M_PI ) ang = ang - 2.*M_PI; + if( ang<-M_PI ) ang = ang + 2.*M_PI; + Angles.Append( ang ); + } + } + P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() ); + P0 = aCirc->Location(); + + if ( !computeLayerPositions(P0,P1)) + return false; + + TopoDS_Vertex V1 = myHelper->IthVertex(0, CircEdge ); + gp_Pnt2d p2dV = BRep_Tool::Parameters( V1, TopoDS::Face(aShape) ); + + NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z()); + GeomAPI_ProjectPointOnSurf PPS(P0,S); + double U0,V0; + PPS.Parameters(1,U0,V0); + meshDS->SetNodeOnFace(NC, faceID, U0, V0); + PC = gp_Pnt2d(U0,V0); + + gp_Vec aVec(P0,P1); + gp_Vec2d aVec2d(PC,p2dV); + Nodes1.resize( myLayerPositions.size()+1 ); + Nodes2.resize( myLayerPositions.size()+1 ); + int i = 0; + for(; iAddNode(P.X(), P.Y(), P.Z()); + Nodes1[i] = node; + Nodes2[i] = node; + double U = PC.X() + aVec2d.X()*myLayerPositions[i]; + double V = PC.Y() + aVec2d.Y()*myLayerPositions[i]; + meshDS->SetNodeOnFace( node, faceID, U, V ); + Pnts2d1.Append(gp_Pnt2d(U,V)); + } + Nodes1[Nodes1.size()-1] = NF; + Nodes2[Nodes1.size()-1] = NF; + } + else if(nbe==2 && LinEdge1.Orientation() != TopAbs_INTERNAL ) + { + // one curve must be a half of circle and other curve must be + // a segment of line + double fp, lp; + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge, &fp, &lp )); + if( fabs(fabs(lp-fp)-M_PI) > Precision::Confusion() ) { + // not half of circle + return error(COMPERR_BAD_SHAPE); + } + Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 )); + if( aLine.IsNull() ) { + // other curve not line + return error(COMPERR_BAD_SHAPE); + } + + if ( !algo1d->ComputeCircularEdge( aMesh, CircEdge )) + return error( algo1d->GetComputeError() ); + map< double, const SMDS_MeshNode* > theNodes; + if ( !GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes) ) + return error("Circular edge is incorrectly meshed"); + + map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin(); + CNodes.clear(); + CNodes.push_back( itn->second ); + double fang = (*itn).first; + itn++; + for(; itn != theNodes.end(); itn++ ) { + CNodes.push_back( (*itn).second ); + double ang = (*itn).first - fang; + if( ang>M_PI ) ang = ang - 2.*M_PI; + if( ang<-M_PI ) ang = ang + 2.*M_PI; + Angles.Append( ang ); + } + const SMDS_MeshNode* NF = theNodes.begin()->second; + const SMDS_MeshNode* NL = theNodes.rbegin()->second; + P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() ); + gp_Pnt P2( NL->X(), NL->Y(), NL->Z() ); + P0 = aCirc->Location(); + + bool linEdgeComputed; + if ( !computeLayerPositions(P0,P1,LinEdge1,&linEdgeComputed)) + return false; + + if ( linEdgeComputed ) + { + if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge1,true,theNodes)) + return error("Invalid mesh on a straight edge"); + + Nodes1.resize( myLayerPositions.size()+1 ); + Nodes2.resize( myLayerPositions.size()+1 ); + vector< const SMDS_MeshNode* > *pNodes1 = &Nodes1, *pNodes2 = &Nodes2; + bool nodesFromP0ToP1 = ( theNodes.rbegin()->second == NF ); + if ( !nodesFromP0ToP1 ) std::swap( pNodes1, pNodes2 ); + + map< double, const SMDS_MeshNode* >::reverse_iterator ritn = theNodes.rbegin(); + itn = theNodes.begin(); + for ( int i = Nodes1.size()-1; i > -1; ++itn, ++ritn, --i ) + { + (*pNodes1)[i] = ritn->second; + (*pNodes2)[i] = itn->second; + Points.Prepend( gpXYZ( Nodes1[i])); + Pnts2d1.Prepend( myHelper->GetNodeUV( F, Nodes1[i])); + } + NC = const_cast( itn->second ); + Points.Remove( Nodes1.size() ); + } + else + { + gp_Vec aVec(P0,P1); + int edgeID = meshDS->ShapeToIndex(LinEdge1); + // check orientation + Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge1,fp,lp); + gp_Pnt Ptmp; + Crv->D0(fp,Ptmp); + bool ori = true; + if( P1.Distance(Ptmp) > Precision::Confusion() ) + ori = false; + // get UV points for edge + gp_Pnt2d PF,PL; + BRep_Tool::UVPoints( LinEdge1, TopoDS::Face(aShape), PF, PL ); + PC = gp_Pnt2d( (PF.X()+PL.X())/2, (PF.Y()+PL.Y())/2 ); + gp_Vec2d V2d; + if(ori) V2d = gp_Vec2d(PC,PF); + else V2d = gp_Vec2d(PC,PL); + // add nodes on edge + double cp = (fp+lp)/2; + double dp2 = (lp-fp)/2; + NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z()); + meshDS->SetNodeOnEdge(NC, edgeID, cp); + Nodes1.resize( myLayerPositions.size()+1 ); + Nodes2.resize( myLayerPositions.size()+1 ); + int i = 0; + for(; iAddNode(P.X(), P.Y(), P.Z()); + Nodes1[i] = node; + double param; + if(ori) + param = fp + dp2*(1-myLayerPositions[i]); + else + param = cp + dp2*myLayerPositions[i]; + meshDS->SetNodeOnEdge(node, edgeID, param); + P = gp_Pnt( P0.X() - aVec.X()*myLayerPositions[i], + P0.Y() - aVec.Y()*myLayerPositions[i], + P0.Z() - aVec.Z()*myLayerPositions[i] ); + node = meshDS->AddNode(P.X(), P.Y(), P.Z()); + Nodes2[i] = node; + if(!ori) + param = fp + dp2*(1-myLayerPositions[i]); + else + param = cp + dp2*myLayerPositions[i]; + meshDS->SetNodeOnEdge(node, edgeID, param); + // parameters on face + gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i], + PC.Y() + V2d.Y()*myLayerPositions[i] ); + Pnts2d1.Append(P2d); + } + Nodes1[ myLayerPositions.size() ] = NF; + Nodes2[ myLayerPositions.size() ] = NL; + // create 1D elements on edge + vector< const SMDS_MeshNode* > tmpNodes; + tmpNodes.resize(2*Nodes1.size()+1); + for(i=0; iAddEdge( tmpNodes[i-1], tmpNodes[i] ); + if(ME) meshDS->SetMeshElementOnShape(ME, edgeID); + } + markEdgeAsComputedByMe( LinEdge1, aMesh.GetSubMesh( F )); + } + } + else // nbe==3 or ( nbe==2 && linEdge is INTERNAL ) + { + if (nbe==2 && LinEdge1.Orientation() == TopAbs_INTERNAL ) + LinEdge2 = LinEdge1; + + // one curve must be a part of circle and other curves must be + // segments of line + double fp, lp; + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge )); + Handle(Geom_Line) aLine1 = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 )); + Handle(Geom_Line) aLine2 = Handle(Geom_Line)::DownCast( getCurve( LinEdge2 )); + if( aCirc.IsNull() || aLine1.IsNull() || aLine2.IsNull() ) + return error(COMPERR_BAD_SHAPE); + + if ( !algo1d->ComputeCircularEdge( aMesh, CircEdge )) + return error( algo1d->GetComputeError() ); + map< double, const SMDS_MeshNode* > theNodes; + if ( !GetSortedNodesOnEdge(aMesh.GetMeshDS(),CircEdge,true,theNodes)) + return error("Circular edge is incorrectly meshed"); + + const SMDS_MeshNode* NF = theNodes.begin()->second; + const SMDS_MeshNode* NL = theNodes.rbegin()->second; + CNodes.clear(); + CNodes.push_back( NF ); + map< double, const SMDS_MeshNode* >::iterator itn = theNodes.begin(); + double fang = (*itn).first; + itn++; + for(; itn != theNodes.end(); itn++ ) { + CNodes.push_back( (*itn).second ); + double ang = (*itn).first - fang; + if( ang>M_PI ) ang = ang - 2.*M_PI; + if( ang<-M_PI ) ang = ang + 2.*M_PI; + Angles.Append( ang ); + } + P1 = gp_Pnt( NF->X(), NF->Y(), NF->Z() ); + gp_Pnt P2( NL->X(), NL->Y(), NL->Z() ); + P0 = aCirc->Location(); + + // make P1 belong to LinEdge1 + TopoDS_Vertex V1 = myHelper->IthVertex( 0, LinEdge1 ); + TopoDS_Vertex V2 = myHelper->IthVertex( 1, LinEdge1 ); + gp_Pnt PE1 = BRep_Tool::Pnt(V1); + gp_Pnt PE2 = BRep_Tool::Pnt(V2); + if( ( P1.Distance(PE1) > Precision::Confusion() ) && + ( P1.Distance(PE2) > Precision::Confusion() ) ) + std::swap( LinEdge1, LinEdge2 ); + + bool linEdge1Computed, linEdge2Computed; + if ( !computeLayerPositions(P0,P1,LinEdge1,&linEdge1Computed)) + return false; + + Nodes1.resize( myLayerPositions.size()+1 ); + Nodes2.resize( myLayerPositions.size()+1 ); + + // check that both linear edges have same hypotheses + if ( !computeLayerPositions(P0,P2,LinEdge2, &linEdge2Computed)) + return false; + if ( Nodes1.size() != myLayerPositions.size()+1 ) + return error("Different hypotheses apply to radial edges"); + + // find the central vertex + TopoDS_Vertex VC = V2; + if( ( P1.Distance(PE1) > Precision::Confusion() ) && + ( P2.Distance(PE1) > Precision::Confusion() ) ) + VC = V1; + int vertID = meshDS->ShapeToIndex(VC); + + // LinEdge1 + if ( linEdge1Computed ) + { + if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge1,true,theNodes)) + return error("Invalid mesh on a straight edge"); + + bool nodesFromP0ToP1 = ( theNodes.rbegin()->second == NF ); + NC = const_cast + ( nodesFromP0ToP1 ? theNodes.begin()->second : theNodes.rbegin()->second ); + int i = 0, ir = Nodes1.size()-1; + int * pi = nodesFromP0ToP1 ? &i : &ir; + itn = theNodes.begin(); + if ( nodesFromP0ToP1 ) ++itn; + for ( ; i < Nodes1.size(); ++i, --ir, ++itn ) + { + Nodes1[*pi] = itn->second; + } + for ( i = 0; i < Nodes1.size()-1; ++i ) + { + Points.Append( gpXYZ( Nodes1[i])); + Pnts2d1.Append( myHelper->GetNodeUV( F, Nodes1[i])); + } + } + else + { + int edgeID = meshDS->ShapeToIndex(LinEdge1); + gp_Vec aVec(P0,P1); + // check orientation + Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge1,fp,lp); + gp_Pnt Ptmp = Crv->Value(fp); + bool ori = false; + if( P1.Distance(Ptmp) > Precision::Confusion() ) + ori = true; + // get UV points for edge + gp_Pnt2d PF,PL; + BRep_Tool::UVPoints( LinEdge1, TopoDS::Face(aShape), PF, PL ); + gp_Vec2d V2d; + if(ori) { + V2d = gp_Vec2d(PF,PL); + PC = PF; + } + else { + V2d = gp_Vec2d(PL,PF); + PC = PL; + } + NC = const_cast( VertexNode( VC, meshDS )); + if ( !NC ) + { + NC = meshDS->AddNode(P0.X(), P0.Y(), P0.Z()); + meshDS->SetNodeOnVertex(NC, vertID); + } + double dp = lp-fp; + int i = 0; + for(; iAddNode(P.X(), P.Y(), P.Z()); + Nodes1[i] = node; + double param; + if(!ori) + param = fp + dp*(1-myLayerPositions[i]); + else + param = fp + dp*myLayerPositions[i]; + meshDS->SetNodeOnEdge(node, edgeID, param); + // parameters on face + gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i], + PC.Y() + V2d.Y()*myLayerPositions[i] ); + Pnts2d1.Append(P2d); + } + Nodes1[ myLayerPositions.size() ] = NF; + // create 1D elements on edge + SMDS_MeshEdge* ME = myHelper->AddEdge( NC, Nodes1[0] ); + if(ME) meshDS->SetMeshElementOnShape(ME, edgeID); + for(i=1; iAddEdge( Nodes1[i-1], Nodes1[i] ); + if(ME) meshDS->SetMeshElementOnShape(ME, edgeID); + } + if (nbe==2 && LinEdge1.Orientation() == TopAbs_INTERNAL ) + Nodes2 = Nodes1; + } + markEdgeAsComputedByMe( LinEdge1, aMesh.GetSubMesh( F )); + + // LinEdge2 + if ( linEdge2Computed ) + { + if (!GetSortedNodesOnEdge(aMesh.GetMeshDS(),LinEdge2,true,theNodes)) + return error("Invalid mesh on a straight edge"); + + bool nodesFromP0ToP2 = ( theNodes.rbegin()->second == NL ); + int i = 0, ir = Nodes1.size()-1; + int * pi = nodesFromP0ToP2 ? &i : &ir; + itn = theNodes.begin(); + if ( nodesFromP0ToP2 ) ++itn; + for ( ; i < Nodes2.size(); ++i, --ir, ++itn ) + Nodes2[*pi] = itn->second; + } + else + { + int edgeID = meshDS->ShapeToIndex(LinEdge2); + gp_Vec aVec = gp_Vec(P0,P2); + // check orientation + Handle(Geom_Curve) Crv = BRep_Tool::Curve(LinEdge2,fp,lp); + gp_Pnt Ptmp = Crv->Value(fp); + bool ori = false; + if( P2.Distance(Ptmp) > Precision::Confusion() ) + ori = true; + // get UV points for edge + gp_Pnt2d PF,PL; + BRep_Tool::UVPoints( LinEdge2, TopoDS::Face(aShape), PF, PL ); + gp_Vec2d V2d; + if(ori) { + V2d = gp_Vec2d(PF,PL); + PC = PF; + } + else { + V2d = gp_Vec2d(PL,PF); + PC = PL; + } + double dp = lp-fp; + for(int i=0; iAddNode(P.X(), P.Y(), P.Z()); + Nodes2[i] = node; + double param; + if(!ori) + param = fp + dp*(1-myLayerPositions[i]); + else + param = fp + dp*myLayerPositions[i]; + meshDS->SetNodeOnEdge(node, edgeID, param); + // parameters on face + gp_Pnt2d P2d( PC.X() + V2d.X()*myLayerPositions[i], + PC.Y() + V2d.Y()*myLayerPositions[i] ); + } + Nodes2[ myLayerPositions.size() ] = NL; + // create 1D elements on edge + SMDS_MeshEdge* ME = myHelper->AddEdge( NC, Nodes2[0] ); + if(ME) meshDS->SetMeshElementOnShape(ME, edgeID); + for(int i=1; iAddEdge( Nodes2[i-1], Nodes2[i] ); + if(ME) meshDS->SetMeshElementOnShape(ME, edgeID); + } + } + markEdgeAsComputedByMe( LinEdge2, aMesh.GetSubMesh( F )); + } + markEdgeAsComputedByMe( CircEdge, aMesh.GetSubMesh( F )); + + // orientation + bool IsForward = ( CircEdge.Orientation()==TopAbs_FORWARD ); + + // create nodes and mesh elements on face + // find axis of rotation + gp_Pnt P2 = gp_Pnt( CNodes[1]->X(), CNodes[1]->Y(), CNodes[1]->Z() ); + gp_Vec Vec1(P0,P1); + gp_Vec Vec2(P0,P2); + gp_Vec Axis = Vec1.Crossed(Vec2); + // create elements + int i = 1; + //cout<<"Angles.Length() = "<& aResVec = + aResMap.insert( make_pair(sm, vector(SMDSEntity_Last,0))).first->second; + + myHelper = new SMESH_MesherHelper( aMesh ); + myHelper->SetSubShape( aShape ); + auto_ptr helperDeleter( myHelper ); + + TNodeDistributor* algo1d = TNodeDistributor::GetDistributor(aMesh); + + TopoDS_Edge CircEdge, LinEdge1, LinEdge2; + int nbe = analyseFace( aShape, CircEdge, LinEdge1, LinEdge2 ); + if( nbe>3 || nbe < 1 || CircEdge.IsNull() ) + return false; + + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge )); + if( aCirc.IsNull() ) + return error(COMPERR_BAD_SHAPE); + + gp_Pnt P0 = aCirc->Location(); + gp_Pnt P1 = aCirc->Value(0.); + computeLayerPositions( P0, P1, LinEdge1 ); + + int nb0d=0, nb2d_tria=0, nb2d_quad=0; + bool isQuadratic = false, ok = true; + if(nbe==1) + { + // C1 must be a circle + ok = algo1d->EvaluateCircularEdge( aMesh, CircEdge, aResMap ); + if(ok) { + const vector& aVec = aResMap[aMesh.GetSubMesh(CircEdge)]; + isQuadratic = aVec[SMDSEntity_Quad_Edge]>aVec[SMDSEntity_Edge]; + if(isQuadratic) { + // main nodes + nb0d = (aVec[SMDSEntity_Node]+1) * myLayerPositions.size(); + // radial medium nodes + nb0d += (aVec[SMDSEntity_Node]+1) * (myLayerPositions.size()+1); + // other medium nodes + nb0d += (aVec[SMDSEntity_Node]+1) * myLayerPositions.size(); + } + else { + nb0d = (aVec[SMDSEntity_Node]+1) * myLayerPositions.size(); + } + nb2d_tria = aVec[SMDSEntity_Node] + 1; + nb2d_quad = nb0d; + } + } + else if(nbe==2 && LinEdge1.Orientation() != TopAbs_INTERNAL) + { + // one curve must be a half of circle and other curve must be + // a segment of line + double fp, lp; + Handle(Geom_Circle) aCirc = Handle(Geom_Circle)::DownCast( getCurve( CircEdge, &fp, &lp )); + if( fabs(fabs(lp-fp)-M_PI) > Precision::Confusion() ) { + // not half of circle + return error(COMPERR_BAD_SHAPE); + } + Handle(Geom_Line) aLine = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 )); + if( aLine.IsNull() ) { + // other curve not line + return error(COMPERR_BAD_SHAPE); + } + ok = !aResMap.count( aMesh.GetSubMesh(LinEdge1) ); + if ( !ok ) { + const vector& aVec = aResMap[ aMesh.GetSubMesh(LinEdge1) ]; + ok = ( aVec[SMDSEntity_Node] == myLayerPositions.size() ); + } + if(ok) { + ok = algo1d->EvaluateCircularEdge( aMesh, CircEdge, aResMap ); + } + if(ok) { + const vector& aVec = aResMap[ aMesh.GetSubMesh(CircEdge) ]; + isQuadratic = aVec[SMDSEntity_Quad_Edge] > aVec[SMDSEntity_Edge]; + if(isQuadratic) { + // main nodes + nb0d = aVec[SMDSEntity_Node] * myLayerPositions.size(); + // radial medium nodes + nb0d += aVec[SMDSEntity_Node] * (myLayerPositions.size()+1); + // other medium nodes + nb0d += (aVec[SMDSEntity_Node]+1) * myLayerPositions.size(); + } + else { + nb0d = aVec[SMDSEntity_Node] * myLayerPositions.size(); + } + nb2d_tria = aVec[SMDSEntity_Node] + 1; + nb2d_quad = nb2d_tria * myLayerPositions.size(); + // add evaluation for edges + vector aResVec(SMDSEntity_Last,0); + if(isQuadratic) { + aResVec[SMDSEntity_Node] = 4*myLayerPositions.size() + 3; + aResVec[SMDSEntity_Quad_Edge] = 2*myLayerPositions.size() + 2; + } + else { + aResVec[SMDSEntity_Node] = 2*myLayerPositions.size() + 1; + aResVec[SMDSEntity_Edge] = 2*myLayerPositions.size() + 2; + } + aResMap[ aMesh.GetSubMesh(LinEdge1) ] = aResVec; + } + } + else // nbe==3 or ( nbe==2 && linEdge is INTERNAL ) + { + if (nbe==2 && LinEdge1.Orientation() == TopAbs_INTERNAL ) + LinEdge2 = LinEdge1; + + // one curve must be a part of circle and other curves must be + // segments of line + Handle(Geom_Line) aLine1 = Handle(Geom_Line)::DownCast( getCurve( LinEdge1 )); + Handle(Geom_Line) aLine2 = Handle(Geom_Line)::DownCast( getCurve( LinEdge2 )); + if( aLine1.IsNull() || aLine2.IsNull() ) { + // other curve not line + return error(COMPERR_BAD_SHAPE); + } + int nbLayers = myLayerPositions.size(); + computeLayerPositions( P0, P1, LinEdge2 ); + if ( nbLayers != myLayerPositions.size() ) + return error("Different hypotheses apply to radial edges"); + + bool ok = !aResMap.count( aMesh.GetSubMesh(LinEdge1)); + if ( !ok ) { + if ( myDistributionHypo || myNbLayerHypo ) + ok = true; // override other 1d hyps + else { + const vector& aVec = aResMap[ aMesh.GetSubMesh(LinEdge1) ]; + ok = ( aVec[SMDSEntity_Node] == myLayerPositions.size() ); + } + } + if( ok && aResMap.count( aMesh.GetSubMesh(LinEdge2) )) { + if ( myDistributionHypo || myNbLayerHypo ) + ok = true; // override other 1d hyps + else { + const vector& aVec = aResMap[ aMesh.GetSubMesh(LinEdge2) ]; + ok = ( aVec[SMDSEntity_Node] == myLayerPositions.size() ); + } + } + if(ok) { + ok = algo1d->EvaluateCircularEdge( aMesh, CircEdge, aResMap ); + } + if(ok) { + const vector& aVec = aResMap[ aMesh.GetSubMesh(CircEdge) ]; + isQuadratic = aVec[SMDSEntity_Quad_Edge]>aVec[SMDSEntity_Edge]; + if(isQuadratic) { + // main nodes + nb0d = aVec[SMDSEntity_Node] * myLayerPositions.size(); + // radial medium nodes + nb0d += aVec[SMDSEntity_Node] * (myLayerPositions.size()+1); + // other medium nodes + nb0d += (aVec[SMDSEntity_Node]+1) * myLayerPositions.size(); + } + else { + nb0d = aVec[SMDSEntity_Node] * myLayerPositions.size(); + } + nb2d_tria = aVec[SMDSEntity_Node] + 1; + nb2d_quad = nb2d_tria * myLayerPositions.size(); + // add evaluation for edges + vector aResVec(SMDSEntity_Last, 0); + if(isQuadratic) { + aResVec[SMDSEntity_Node] = 2*myLayerPositions.size() + 1; + aResVec[SMDSEntity_Quad_Edge] = myLayerPositions.size() + 1; + } + else { + aResVec[SMDSEntity_Node] = myLayerPositions.size(); + aResVec[SMDSEntity_Edge] = myLayerPositions.size() + 1; + } + sm = aMesh.GetSubMesh(LinEdge1); + aResMap[sm] = aResVec; + sm = aMesh.GetSubMesh(LinEdge2); + aResMap[sm] = aResVec; + } + } + + if(nb0d>0) { + aResVec[0] = nb0d; + if(isQuadratic) { + aResVec[SMDSEntity_Quad_Triangle] = nb2d_tria; + aResVec[SMDSEntity_Quad_Quadrangle] = nb2d_quad; + } + else { + aResVec[SMDSEntity_Triangle] = nb2d_tria; + aResVec[SMDSEntity_Quadrangle] = nb2d_quad; + } + return true; + } + + // invalid case + sm = aMesh.GetSubMesh(aShape); + SMESH_ComputeErrorPtr& smError = sm->GetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED, + "Submesh can not be evaluated",this)); + return false; + +} diff --git a/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.hxx b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.hxx new file mode 100644 index 000000000..b68ebe930 --- /dev/null +++ b/src/StdMeshers/StdMeshers_RadialQuadrangle_1D2D.hxx @@ -0,0 +1,76 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH : implementaion of SMESH idl descriptions +// File : StdMeshers_RadialQuadrangle_1D2D.hxx +// Module : SMESH +// +#ifndef _SMESH_RadialQuadrangle_1D2D_HXX_ +#define _SMESH_RadialQuadrangle_1D2D_HXX_ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_2D_Algo.hxx" + +#include + +#include + +class StdMeshers_NumberOfLayers; +class StdMeshers_LayerDistribution; +class SMESH_MesherHelper; +class gp_Pnt; + +class STDMESHERS_EXPORT StdMeshers_RadialQuadrangle_1D2D: public SMESH_2D_Algo +{ +public: + StdMeshers_RadialQuadrangle_1D2D(int hypId, int studyId, SMESH_Gen* gen); + virtual ~StdMeshers_RadialQuadrangle_1D2D(); + + virtual bool CheckHypothesis(SMESH_Mesh& aMesh, + const TopoDS_Shape& aShape, + SMESH_Hypothesis::Hypothesis_Status& aStatus); + + virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); + /*! + * \brief Allow algo to do something after persistent restoration + * \param subMesh - restored submesh + * + * This method is called only if a submesh has HYP_OK algo_state. + */ + virtual void SubmeshRestored(SMESH_subMesh* subMesh); + +protected: + + bool computeLayerPositions(const gp_Pnt& p1, + const gp_Pnt& p2, + const TopoDS_Edge& linEdge=TopoDS_Edge(), + bool* linEdgeComputed = 0); + + + const StdMeshers_NumberOfLayers* myNbLayerHypo; + const StdMeshers_LayerDistribution* myDistributionHypo; + SMESH_MesherHelper* myHelper; + std::vector< double > myLayerPositions; +}; + +#endif diff --git a/src/StdMeshers/StdMeshers_Regular_1D.cxx b/src/StdMeshers/StdMeshers_Regular_1D.cxx index ee4aa86e7..8d900955d 100644 --- a/src/StdMeshers/StdMeshers_Regular_1D.cxx +++ b/src/StdMeshers/StdMeshers_Regular_1D.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH : implementaion of SMESH idl descriptions + // File : StdMeshers_Regular_1D.cxx // Moved here from SMESH_Regular_1D.cxx // Author : Paul RASCLE, EDF // Module : SMESH - +// #include "StdMeshers_Regular_1D.hxx" #include "StdMeshers_Distribution.hxx" @@ -61,8 +61,10 @@ #include #include #include +#include #include +#include using namespace std; @@ -73,22 +75,24 @@ using namespace std; //============================================================================= StdMeshers_Regular_1D::StdMeshers_Regular_1D(int hypId, int studyId, - SMESH_Gen * gen):SMESH_1D_Algo(hypId, studyId, gen) + SMESH_Gen * gen):SMESH_1D_Algo(hypId, studyId, gen) { - MESSAGE("StdMeshers_Regular_1D::StdMeshers_Regular_1D"); - _name = "Regular_1D"; - _shapeType = (1 << TopAbs_EDGE); - - _compatibleHypothesis.push_back("LocalLength"); - _compatibleHypothesis.push_back("MaxLength"); - _compatibleHypothesis.push_back("NumberOfSegments"); - _compatibleHypothesis.push_back("StartEndLength"); - _compatibleHypothesis.push_back("Deflection1D"); - _compatibleHypothesis.push_back("Arithmetic1D"); - _compatibleHypothesis.push_back("AutomaticLength"); - - _compatibleHypothesis.push_back("QuadraticMesh"); // auxiliary !!! - _compatibleHypothesis.push_back("Propagation"); // auxiliary !!! + MESSAGE("StdMeshers_Regular_1D::StdMeshers_Regular_1D"); + _name = "Regular_1D"; + _shapeType = (1 << TopAbs_EDGE); + _fpHyp = 0; + + _compatibleHypothesis.push_back("LocalLength"); + _compatibleHypothesis.push_back("MaxLength"); + _compatibleHypothesis.push_back("NumberOfSegments"); + _compatibleHypothesis.push_back("StartEndLength"); + _compatibleHypothesis.push_back("Deflection1D"); + _compatibleHypothesis.push_back("Arithmetic1D"); + _compatibleHypothesis.push_back("FixedPoints1D"); + _compatibleHypothesis.push_back("AutomaticLength"); + + _compatibleHypothesis.push_back("QuadraticMesh"); // auxiliary !!! + _compatibleHypothesis.push_back("Propagation"); // auxiliary !!! } //============================================================================= @@ -115,9 +119,8 @@ bool StdMeshers_Regular_1D::CheckHypothesis _hypType = NONE; _quadraticMesh = false; - const bool ignoreAuxiliaryHyps = false; const list & hyps = - GetUsedHypothesis(aMesh, aShape, ignoreAuxiliaryHyps); + GetUsedHypothesis(aMesh, aShape, /*ignoreAuxiliaryHyps=*/false); // find non-auxiliary hypothesis const SMESHDS_Hypothesis *theHyp = 0; @@ -180,12 +183,15 @@ bool StdMeshers_Regular_1D::CheckHypothesis { case StdMeshers_NumberOfSegments::DT_Scale: _value[ SCALE_FACTOR_IND ] = hyp->GetScaleFactor(); + _revEdgesIDs = hyp->GetReversedEdges(); break; case StdMeshers_NumberOfSegments::DT_TabFunc: _vvalue[ TAB_FUNC_IND ] = hyp->GetTableFunction(); + _revEdgesIDs = hyp->GetReversedEdges(); break; case StdMeshers_NumberOfSegments::DT_ExprFunc: _svalue[ EXPR_FUNC_IND ] = hyp->GetExpressionFunction(); + _revEdgesIDs = hyp->GetReversedEdges(); break; case StdMeshers_NumberOfSegments::DT_Regular: break; @@ -209,6 +215,19 @@ bool StdMeshers_Regular_1D::CheckHypothesis _value[ END_LENGTH_IND ] = hyp->GetLength( false ); ASSERT( _value[ BEG_LENGTH_IND ] > 0 && _value[ END_LENGTH_IND ] > 0 ); _hypType = ARITHMETIC_1D; + + _revEdgesIDs = hyp->GetReversedEdges(); + + aStatus = SMESH_Hypothesis::HYP_OK; + } + + else if (hypName == "FixedPoints1D") { + _fpHyp = dynamic_cast (theHyp); + ASSERT(_fpHyp); + _hypType = FIXED_POINTS_1D; + + _revEdgesIDs = _fpHyp->GetReversedEdges(); + aStatus = SMESH_Hypothesis::HYP_OK; } @@ -221,6 +240,9 @@ bool StdMeshers_Regular_1D::CheckHypothesis _value[ END_LENGTH_IND ] = hyp->GetLength( false ); ASSERT( _value[ BEG_LENGTH_IND ] > 0 && _value[ END_LENGTH_IND ] > 0 ); _hypType = BEG_END_LENGTH; + + _revEdgesIDs = hyp->GetReversedEdges(); + aStatus = SMESH_Hypothesis::HYP_OK; } @@ -303,6 +325,8 @@ static bool computeParamByFunc(Adaptor3d_Curve& C3d, double first, double last, return false; prevU = U; } + if ( theReverse ) + theParams.reverse(); return true; } @@ -352,10 +376,14 @@ static void compensateError(double a1, double an, } double q = dUn / ( nPar - 1 ); - if ( !adjustNeighbors2an ) { - for ( itU = theParams.rbegin(), i = 1; i < nPar; itU++, i++ ) { + if ( !adjustNeighbors2an ) + { + q = dUn / ( Utgt - Un ); // (signed) factor of segment length change + for ( itU = theParams.rbegin(), i = 1; i < nPar; i++ ) { + double prevU = *itU; (*itU) += dUn; - dUn -= q; + ++itU; + dUn = q * (*itU - prevU) * (prevU-U1)/(Un-U1); } } else { @@ -602,7 +630,7 @@ bool StdMeshers_Regular_1D::computeInternalParameters(SMESH_Mesh & theMesh, bool computed = sm->IsMeshComputed(); if (!computed) { if (sm->GetComputeState() == SMESH_subMesh::READY_TO_COMPUTE) { - sm->ComputeStateEngine(SMESH_subMesh::COMPUTE); + _gen->Compute( theMesh, _mainEdge, /*anUpward=*/true); computed = sm->IsMeshComputed(); } } @@ -659,6 +687,14 @@ bool StdMeshers_Regular_1D::computeInternalParameters(SMESH_Mesh & theMesh, theParams.push_back( param ); } } + const double lenFactor = theLength/(l-f); + list::iterator u = theParams.begin(), uEnd = theParams.end(); + for ( ; u != uEnd; ++u ) + { + GCPnts_AbscissaPoint Discret( theC3d, ((*u)-f) * lenFactor, f ); + if ( Discret.IsDone() ) + *u = Discret.Parameter(); + } return true; } break; @@ -706,6 +742,9 @@ bool StdMeshers_Regular_1D::computeInternalParameters(SMESH_Mesh & theMesh, double a1 = _value[ BEG_LENGTH_IND ]; double an = _value[ END_LENGTH_IND ]; double q = ( theLength - a1 ) / ( theLength - an ); + if ( q < theLength/1e6 || 1.01*theLength < a1 + an) + return error ( SMESH_Comment("Invalid segment lengths (")< numeric_limits::min() ? ( 1+( an-a1 )/q ) : ( 1+theLength/a1 )); double U1 = theReverse ? l : f; double Un = theReverse ? f : l; @@ -764,6 +806,107 @@ bool StdMeshers_Regular_1D::computeInternalParameters(SMESH_Mesh & theMesh, return true; } + case FIXED_POINTS_1D: { + const std::vector& aPnts = _fpHyp->GetPoints(); + const std::vector& nbsegs = _fpHyp->GetNbSegments(); + int i = 0; + TColStd_SequenceOfReal Params; + for(; i0.9999 ) continue; + int j=1; + bool IsExist = false; + for(; j<=Params.Length(); j++) { + if( fabs(aPnts[i]-Params.Value(j)) < 1e-4 ) { + IsExist = true; + break; + } + if( aPnts[i] nbsegs.size()-1 ) ? nbsegs[0] : nbsegs[i]; + segmentSize = Params.Value(i+1)*theLength - currAbscissa; + currAbscissa += segmentSize; + GCPnts_AbscissaPoint APnt(theC3d, sign*segmentSize, par1); + if( !APnt.IsDone() ) + return error( "GCPnts_AbscissaPoint failed"); + par2 = APnt.Parameter(); + eltSize = segmentSize/nbseg; + GCPnts_UniformAbscissa Discret(theC3d, eltSize, par1, par2); + if(theReverse) + Discret.Initialize(theC3d, eltSize, par2, par1); + else + Discret.Initialize(theC3d, eltSize, par1, par2); + if ( !Discret.IsDone() ) + return error( "GCPnts_UniformAbscissa failed"); + int NbPoints = Discret.NbPoints(); + list tmpParams; + for(int i=2; i::iterator itP = tmpParams.begin(); + for(; itP != tmpParams.end(); itP++) { + theParams.push_back( *(itP) ); + } + theParams.push_back( par2 ); + + par1 = par2; + } + // add for last + int nbseg = ( nbsegs.size() > Params.Length() ) ? nbsegs[Params.Length()] : nbsegs[0]; + segmentSize = theLength - currAbscissa; + eltSize = segmentSize/nbseg; + GCPnts_UniformAbscissa Discret; + if(theReverse) + Discret.Initialize(theC3d, eltSize, par1, lp); + else + Discret.Initialize(theC3d, eltSize, lp, par1); + if ( !Discret.IsDone() ) + return error( "GCPnts_UniformAbscissa failed"); + int NbPoints = Discret.NbPoints(); + list tmpParams; + for(int i=2; i::iterator itP = tmpParams.begin(); + for(; itP != tmpParams.end(); itP++) { + theParams.push_back( *(itP) ); + } + + if (theReverse) { + theParams.reverse(); // NPAL18025 + } + return true; + } + case DEFLECTION: { GCPnts_UniformDeflection Discret(theC3d, _value[ DEFLECTION_IND ], f, l, true); @@ -815,12 +958,43 @@ bool StdMeshers_Regular_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & t if (!idFirst || !idLast) return error( COMPERR_BAD_INPUT_MESH, "No node on vertex"); + // remove elements created by e.g. patern mapping (PAL21999) + // CLEAN event is incorrectly ptopagated seemingly due to Propagation hyp + // so TEMPORARY solution is to clean the submesh manually + //theMesh.GetSubMesh(theShape)->ComputeStateEngine( SMESH_subMesh::CLEAN ); + if (SMESHDS_SubMesh * subMeshDS = meshDS->MeshElements(theShape)) + { + SMDS_ElemIteratorPtr ite = subMeshDS->GetElements(); + while (ite->more()) + meshDS->RemoveFreeElement(ite->next(), subMeshDS); + SMDS_NodeIteratorPtr itn = subMeshDS->GetNodes(); + while (itn->more()) { + const SMDS_MeshNode * node = itn->next(); + if ( node->NbInverseElements() == 0 ) + meshDS->RemoveFreeNode(node, subMeshDS); + else + meshDS->RemoveNode(node); + } + } + if (!Curve.IsNull()) { list< double > params; bool reversed = false; - if ( !_mainEdge.IsNull() ) + if ( theMesh.GetShapeToMesh().ShapeType() >= TopAbs_WIRE ) { + // if the shape to mesh is WIRE or EDGE + reversed = ( EE.Orientation() == TopAbs_REVERSED ); + } + if ( !_mainEdge.IsNull() ) { + // take into account reversing the edge the hypothesis is propagated from reversed = ( _mainEdge.Orientation() == TopAbs_REVERSED ); + int mainID = meshDS->ShapeToIndex(_mainEdge); + if ( std::find( _revEdgesIDs.begin(), _revEdgesIDs.end(), mainID) != _revEdgesIDs.end()) + reversed = !reversed; + } + // take into account this edge reversing + if ( std::find( _revEdgesIDs.begin(), _revEdgesIDs.end(), shapeID) != _revEdgesIDs.end()) + reversed = !reversed; BRepAdaptor_Curve C3d( E ); double length = EdgeLength( E ); @@ -845,7 +1019,6 @@ bool StdMeshers_Regular_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & t parLast = f; } */ - for (list::iterator itU = params.begin(); itU != params.end(); itU++) { double param = *itU; gp_Pnt P = Curve->Value(param); @@ -930,6 +1103,81 @@ bool StdMeshers_Regular_1D::Compute(SMESH_Mesh & theMesh, const TopoDS_Shape & t return true; } + +//============================================================================= +/*! + * + */ +//============================================================================= + +bool StdMeshers_Regular_1D::Evaluate(SMESH_Mesh & theMesh, + const TopoDS_Shape & theShape, + MapShapeNbElems& aResMap) +{ + if ( _hypType == NONE ) + return false; + + //SMESHDS_Mesh * meshDS = theMesh.GetMeshDS(); + + const TopoDS_Edge & EE = TopoDS::Edge(theShape); + TopoDS_Edge E = TopoDS::Edge(EE.Oriented(TopAbs_FORWARD)); + // int shapeID = meshDS->ShapeToIndex( E ); + + double f, l; + Handle(Geom_Curve) Curve = BRep_Tool::Curve(E, f, l); + + TopoDS_Vertex VFirst, VLast; + TopExp::Vertices(E, VFirst, VLast); // Vfirst corresponds to f and Vlast to l + + ASSERT(!VFirst.IsNull()); + ASSERT(!VLast.IsNull()); + + std::vector aVec(SMDSEntity_Last,0); + + if (!Curve.IsNull()) { + list< double > params; + + BRepAdaptor_Curve C3d( E ); + double length = EdgeLength( E ); + if ( ! computeInternalParameters( theMesh, C3d, length, f, l, params, false, true )) { + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(std::make_pair(sm,aVec)); + SMESH_ComputeErrorPtr& smError = sm->GetComputeError(); + smError.reset( new SMESH_ComputeError(COMPERR_ALGO_FAILED,"Submesh can not be evaluated",this)); + return false; + } + redistributeNearVertices( theMesh, C3d, length, params, VFirst, VLast ); + + if(_quadraticMesh) { + aVec[SMDSEntity_Node] = 2*params.size() + 1; + aVec[SMDSEntity_Quad_Edge] = params.size() + 1; + } + else { + aVec[SMDSEntity_Node] = params.size(); + aVec[SMDSEntity_Edge] = params.size() + 1; + } + + } + else { + //MESSAGE("************* Degenerated edge! *****************"); + // Edge is a degenerated Edge : We put n = 5 points on the edge. + if(_quadraticMesh) { + aVec[SMDSEntity_Node] = 11; + aVec[SMDSEntity_Quad_Edge] = 6; + } + else { + aVec[SMDSEntity_Node] = 5; + aVec[SMDSEntity_Edge] = 6; + } + } + + SMESH_subMesh * sm = theMesh.GetSubMesh(theShape); + aResMap.insert(std::make_pair(sm,aVec)); + + return true; +} + + //============================================================================= /*! * See comments in SMESH_Algo.cxx @@ -946,10 +1194,9 @@ StdMeshers_Regular_1D::GetUsedHypothesis(SMESH_Mesh & aMesh, SMESH_HypoFilter auxiliaryFilter, compatibleFilter; auxiliaryFilter.Init( SMESH_HypoFilter::IsAuxiliary() ); - const bool ignoreAux = true; - InitCompatibleHypoFilter( compatibleFilter, ignoreAux ); + InitCompatibleHypoFilter( compatibleFilter, /*ignoreAux=*/true ); - // get non-auxiliary assigned to aShape + // get non-auxiliary assigned directly to aShape int nbHyp = aMesh.GetHypotheses( aShape, compatibleFilter, _usedHypList, false ); if (nbHyp == 0 && aShape.ShapeType() == TopAbs_EDGE) diff --git a/src/StdMeshers/StdMeshers_Regular_1D.hxx b/src/StdMeshers/StdMeshers_Regular_1D.hxx index 7df8e4533..aed1064ab 100644 --- a/src/StdMeshers/StdMeshers_Regular_1D.hxx +++ b/src/StdMeshers/StdMeshers_Regular_1D.hxx @@ -1,30 +1,31 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_Regular_1D.hxx // Moved here from SMESH_Regular_1D.hxx // Author : Paul RASCLE, EDF // Module : SMESH - +// #ifndef _SMESH_REGULAR_1D_HXX_ #define _SMESH_REGULAR_1D_HXX_ @@ -32,6 +33,8 @@ #include "SMESH_1D_Algo.hxx" +#include "StdMeshers_FixedPoints1D.hxx" + class Adaptor3d_Curve; class TopoDS_Vertex; class StdMeshers_SegmentLengthAroundVertex; @@ -47,7 +50,10 @@ public: SMESH_Hypothesis::Hypothesis_Status& aStatus); virtual bool Compute(SMESH_Mesh& aMesh, - const TopoDS_Shape& aShape); + const TopoDS_Shape& aShape); + + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); virtual const std::list & GetUsedHypothesis(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, const bool=true); @@ -94,7 +100,7 @@ protected: StdMeshers_SegmentLengthAroundVertex* getVertexHyp(SMESH_Mesh & theMesh, const TopoDS_Vertex & theV); - enum HypothesisType { LOCAL_LENGTH, MAX_LENGTH, NB_SEGMENTS, BEG_END_LENGTH, DEFLECTION, ARITHMETIC_1D, NONE }; + enum HypothesisType { LOCAL_LENGTH, MAX_LENGTH, NB_SEGMENTS, BEG_END_LENGTH, DEFLECTION, ARITHMETIC_1D, FIXED_POINTS_1D, NONE }; enum ValueIndex { SCALE_FACTOR_IND = 0, @@ -120,10 +126,13 @@ protected: HypothesisType _hypType; + const StdMeshers_FixedPoints1D* _fpHyp; + double _value[2]; int _ivalue[3]; std::vector _vvalue[1]; std::string _svalue[1]; + std::vector _revEdgesIDs; // a source of propagated hypothesis, is set by CheckHypothesis() // always called before Compute() diff --git a/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.cxx b/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.cxx index 60ba2518d..1a6f14e18 100644 --- a/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.cxx +++ b/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_SegmentAroundVertex_0D.cxx // Module : SMESH @@ -38,7 +39,7 @@ StdMeshers_SegmentAroundVertex_0D::StdMeshers_SegmentAroundVertex_0D { _name = "SegmentAroundVertex_0D"; // it is assigned to vertices but influence a state of EDGE submeshes - _shapeType = (1 << TopAbs_VERTEX); // 1 bit per shape type + _shapeType = (1 << TopAbs_VERTEX); // 1 bit per shape type _compatibleHypothesis.push_back("SegmentLengthAroundVertex"); } @@ -92,3 +93,18 @@ bool StdMeshers_SegmentAroundVertex_0D::Compute(SMESH_Mesh&, const TopoDS_Shape& // StdMeshers_SegmentLengthAroundVertex hypothesis return true; } + + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_SegmentAroundVertex_0D::Evaluate(SMESH_Mesh&, + const TopoDS_Shape&, + MapShapeNbElems&) +{ + // This algorithm exists in order just to enable assignation of + // StdMeshers_SegmentLengthAroundVertex hypothesis + return false; +} diff --git a/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.hxx b/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.hxx index 4054a715d..abb8ce2c6 100644 --- a/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.hxx +++ b/src/StdMeshers/StdMeshers_SegmentAroundVertex_0D.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_SegmentAroundVertex_0D.hxx // Module : SMESH @@ -46,6 +47,8 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); }; #endif diff --git a/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.cxx b/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.cxx index f5540a88c..27f280f43 100644 --- a/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.cxx +++ b/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_SegmentLengthAroundVertex.cxx // Module : SMESH - +// #include "StdMeshers_SegmentLengthAroundVertex.hxx" #include "SMESH_Mesh.hxx" @@ -30,7 +31,6 @@ #include "SMDS_MeshNode.hxx" #include "SMESHDS_Mesh.hxx" #include "SMESHDS_SubMesh.hxx" -#include "SMESH_MeshEditor.hxx" #include "SMESH_MesherHelper.hxx" #include "utilities.h" diff --git a/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.hxx b/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.hxx index c7c54bbf6..ed196455a 100644 --- a/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.hxx +++ b/src/StdMeshers/StdMeshers_SegmentLengthAroundVertex.hxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_SegmentLengthAroundVertex.hxx // Author : Paul RASCLE, EDF // Module : SMESH - +// #ifndef _SMESH_SegmentLengthAroundVertex_HXX_ #define _SMESH_SegmentLengthAroundVertex_HXX_ diff --git a/src/StdMeshers/StdMeshers_StartEndLength.cxx b/src/StdMeshers/StdMeshers_StartEndLength.cxx index 0beb6349a..46cee3f22 100644 --- a/src/StdMeshers/StdMeshers_StartEndLength.cxx +++ b/src/StdMeshers/StdMeshers_StartEndLength.cxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_StartEndLength : implementaion of SMESH idl descriptions // File : StdMeshers_StartEndLength.cxx // Module : SMESH - +// #include "StdMeshers_StartEndLength.hxx" #include "SMESH_Algo.hxx" @@ -105,9 +106,33 @@ double StdMeshers_StartEndLength::GetLength(bool isStartLength) const */ //============================================================================= +void StdMeshers_StartEndLength::SetReversedEdges( std::vector& ids ) +{ + if ( ids != _edgeIDs ) { + _edgeIDs = ids; + + NotifySubMeshesHypothesisModification(); + } +} + +//============================================================================= +/*! + * + */ +//============================================================================= + ostream & StdMeshers_StartEndLength::SaveTo(ostream & save) { - save << _begLength << " " <<_endLength; + int listSize = _edgeIDs.size(); + save << _begLength << " " << _endLength << " " << listSize; + + if ( listSize > 0 ) { + for ( int i = 0; i < listSize; i++) { + save << " " << _edgeIDs[i]; + } + save << " " << _objEntry; + } + return save; } @@ -120,12 +145,25 @@ ostream & StdMeshers_StartEndLength::SaveTo(ostream & save) istream & StdMeshers_StartEndLength::LoadFrom(istream & load) { bool isOK = true; + int intVal; isOK = (load >> _begLength); if (!isOK) load.clear(ios::badbit | load.rdstate()); isOK = (load >> _endLength); + if (!isOK) load.clear(ios::badbit | load.rdstate()); + + isOK = (load >> intVal); + if (isOK && intVal > 0) { + _edgeIDs.reserve( intVal ); + for (int i = 0; i < _edgeIDs.capacity() && isOK; i++) { + isOK = (load >> intVal); + if ( isOK ) _edgeIDs.push_back( intVal ); + } + isOK = (load >> _objEntry); + } + return load; } @@ -178,7 +216,7 @@ bool StdMeshers_StartEndLength::SetParametersByMesh(const SMESH_Mesh* theMesh, { const TopoDS_Edge& edge = TopoDS::Edge( edgeMap( i )); Handle(Geom_Curve) C = BRep_Tool::Curve(edge, L, UMin, UMax); - GeomAdaptor_Curve AdaptCurve(C); + GeomAdaptor_Curve AdaptCurve(C, UMin, UMax); vector< double > params; SMESHDS_Mesh* aMeshDS = const_cast< SMESH_Mesh* >( theMesh )->GetMeshDS(); @@ -207,6 +245,6 @@ bool StdMeshers_StartEndLength::SetParametersByMesh(const SMESH_Mesh* theMesh, bool StdMeshers_StartEndLength::SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* /*theMesh*/) { - return bool(_begLength = _endLength = dflts._elemLength ); + return (_begLength = _endLength = dflts._elemLength ); } diff --git a/src/StdMeshers/StdMeshers_StartEndLength.hxx b/src/StdMeshers/StdMeshers_StartEndLength.hxx index 00f228771..dc8781a7f 100644 --- a/src/StdMeshers/StdMeshers_StartEndLength.hxx +++ b/src/StdMeshers/StdMeshers_StartEndLength.hxx @@ -1,28 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_StartEndLength.hxx // Module : SMESH - +// #ifndef _STDMESHERS_STARTENDLENGTH_HXX_ #define _STDMESHERS_STARTENDLENGTH_HXX_ @@ -31,6 +32,8 @@ #include "SMESH_Hypothesis.hxx" #include "Utils_SALOME_Exception.hxx" +#include + class STDMESHERS_EXPORT StdMeshers_StartEndLength:public SMESH_Hypothesis { public: @@ -40,6 +43,14 @@ class STDMESHERS_EXPORT StdMeshers_StartEndLength:public SMESH_Hypothesis void SetLength(double length, bool isStartLength) throw(SALOME_Exception); double GetLength(bool isStartLength) const; + + void SetReversedEdges( std::vector& ids); + + const std::vector& GetReversedEdges() const { return _edgeIDs; } + + void SetObjectEntry( const char* entry ) { _objEntry = entry; } + + const char* GetObjectEntry() { return _objEntry.c_str(); } virtual std::ostream & SaveTo(std::ostream & save); virtual std::istream & LoadFrom(std::istream & load); @@ -63,6 +74,8 @@ class STDMESHERS_EXPORT StdMeshers_StartEndLength:public SMESH_Hypothesis protected: double _begLength, _endLength; + std::vector _edgeIDs; + std::string _objEntry; }; #endif diff --git a/src/StdMeshers/StdMeshers_TrianglePreference.cxx b/src/StdMeshers/StdMeshers_TrianglePreference.cxx deleted file mode 100644 index 331cd0d1c..000000000 --- a/src/StdMeshers/StdMeshers_TrianglePreference.cxx +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH StdMeshers_TrianglePreference -// File : StdMeshers_TrianglePreference.cxx -// Module : SMESH - -#include "StdMeshers_TrianglePreference.hxx" -#include "utilities.h" - -using namespace std; - -//============================================================================= -/*! - * - */ -//============================================================================= - -StdMeshers_TrianglePreference::StdMeshers_TrianglePreference(int hypId, - int studyId, - SMESH_Gen * gen) - :SMESH_Hypothesis(hypId, studyId, gen) -{ - _name = "TrianglePreference"; - _param_algo_dim = -2; // auxiliary used by StdMeshers_Quadrangle_2D -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -StdMeshers_TrianglePreference::~StdMeshers_TrianglePreference() -{ -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -ostream & StdMeshers_TrianglePreference::SaveTo(ostream & save) -{ - return save; -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -istream & StdMeshers_TrianglePreference::LoadFrom(istream & load) -{ - return load; -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -ostream & operator <<(ostream & save, StdMeshers_TrianglePreference & hyp) -{ - return hyp.SaveTo( save ); -} - -//============================================================================= -/*! - * - */ -//============================================================================= - -istream & operator >>(istream & load, StdMeshers_TrianglePreference & hyp) -{ - return hyp.LoadFrom( load ); -} -//================================================================================ -/*! - * \brief Initialize my parameter values by the mesh built on the geometry - * \param theMesh - the built mesh - * \param theShape - the geometry of interest - * \retval bool - true if parameter values have been successfully defined - * - * Just return false as this hypothesis does not have parameters values - */ -//================================================================================ - -bool StdMeshers_TrianglePreference::SetParametersByMesh(const SMESH_Mesh* /*theMesh*/, - const TopoDS_Shape& /*theShape*/) -{ - return false; -} - -//================================================================================ -/*! - * \brief Initialize my parameter values by default parameters. - * \retval bool - true if parameter values have been successfully defined - */ -//================================================================================ - -bool StdMeshers_TrianglePreference::SetParametersByDefaults(const TDefaults& /*dflts*/, - const SMESH_Mesh* /*theMesh*/) -{ - return false; -} - diff --git a/src/StdMeshers/StdMeshers_TrianglePreference.hxx b/src/StdMeshers/StdMeshers_TrianglePreference.hxx deleted file mode 100644 index 34ec364a1..000000000 --- a/src/StdMeshers/StdMeshers_TrianglePreference.hxx +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// SMESH StdMeshers : implementaion of SMESH idl descriptions -// File : StdMeshers_TrianglePreference.hxx -// Module : SMESH - -#ifndef _StdMeshers_TrianglePreference_HXX_ -#define _StdMeshers_TrianglePreference_HXX_ - -#include "SMESH_StdMeshers.hxx" - -#include "SMESH_Hypothesis.hxx" -#include "Utils_SALOME_Exception.hxx" - -/*! - * \brief Hypothesis for StdMeshers_Quadrangle_2D, forcing construction - * of triangles in the in a refinement area if the number of nodes - * on opposite edges is not the same. See Issue 16186. - */ -class STDMESHERS_EXPORT StdMeshers_TrianglePreference:public SMESH_Hypothesis -{ - public: - StdMeshers_TrianglePreference(int hypId, int studyId, SMESH_Gen * gen); - virtual ~ StdMeshers_TrianglePreference(); - - virtual std::ostream & SaveTo(std::ostream & save); - virtual std::istream & LoadFrom(std::istream & load); - friend std::ostream & operator <<(std::ostream & save, StdMeshers_TrianglePreference & hyp); - friend std::istream & operator >>(std::istream & load, StdMeshers_TrianglePreference & hyp); - - /*! - * \brief Initialize my parameter values by the mesh built on the geometry - * \param theMesh - the built mesh - * \param theShape - the geometry of interest - * \retval bool - true if parameter values have been successfully defined - * - * Just return false as this hypothesis does not have parameters values - */ - virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape); - - /*! - * \brief Initialize my parameter values by default parameters. - * \retval bool - true if parameter values have been successfully defined - */ - virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0); - -}; - -#endif diff --git a/src/StdMeshers/StdMeshers_UseExisting_1D2D.cxx b/src/StdMeshers/StdMeshers_UseExisting_1D2D.cxx index e44345af4..e93dfffaf 100644 --- a/src/StdMeshers/StdMeshers_UseExisting_1D2D.cxx +++ b/src/StdMeshers/StdMeshers_UseExisting_1D2D.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_UseExisting_1D2D.cxx // Module : SMESH @@ -60,10 +58,27 @@ bool StdMeshers_UseExisting_1D::CheckHypothesis(SMESH_Mesh& , bool StdMeshers_UseExisting_1D::Compute(SMESH_Mesh&, const TopoDS_Shape&) { - // This algorithm exists to allow mesh generation by mesh edition functions in TUI mode + // This algorithm exists to allow mesh generation by mesh + // edition functions in TUI mode return true; } + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_UseExisting_1D::Evaluate(SMESH_Mesh&, + const TopoDS_Shape&, + MapShapeNbElems&) +{ + // This algorithm exists to allow mesh generation by mesh + // edition functions in TUI mode + return false; +} + + //======================================================================= //function : StdMeshers_UseExisting_2D //purpose : @@ -97,6 +112,22 @@ bool StdMeshers_UseExisting_2D::CheckHypothesis(SMESH_Mesh& , bool StdMeshers_UseExisting_2D::Compute(SMESH_Mesh&, const TopoDS_Shape&) { - // This algorithm exists to allow mesh generation by mesh edition functions in TUI mode + // This algorithm exists to allow mesh generation by mesh edition + // functions in TUI mode return true; } + + +//======================================================================= +//function : Evaluate +//purpose : +//======================================================================= + +bool StdMeshers_UseExisting_2D::Evaluate(SMESH_Mesh&, + const TopoDS_Shape&, + MapShapeNbElems&) +{ + // This algorithm exists to allow mesh generation by mesh edition + // functions in TUI mode + return false; +} diff --git a/src/StdMeshers/StdMeshers_UseExisting_1D2D.hxx b/src/StdMeshers/StdMeshers_UseExisting_1D2D.hxx index 6d5a5e495..23c64b5b9 100644 --- a/src/StdMeshers/StdMeshers_UseExisting_1D2D.hxx +++ b/src/StdMeshers/StdMeshers_UseExisting_1D2D.hxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_UseExisting_1D2D.hxx // Module : SMESH @@ -46,6 +44,8 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); }; class STDMESHERS_EXPORT StdMeshers_UseExisting_1D: public SMESH_1D_Algo @@ -59,6 +59,8 @@ public: virtual bool Compute(SMESH_Mesh& aMesh, const TopoDS_Shape& aShape); + virtual bool Evaluate(SMESH_Mesh & aMesh, const TopoDS_Shape & aShape, + MapShapeNbElems& aResMap); }; #endif diff --git a/src/StdMeshers/StdMeshers_ViscousLayers.cxx b/src/StdMeshers/StdMeshers_ViscousLayers.cxx new file mode 100644 index 000000000..ddae235f7 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ViscousLayers.cxx @@ -0,0 +1,4483 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_ViscousLayers.cxx +// Created : Wed Dec 1 15:15:34 2010 +// Author : Edward AGAPOV (eap) + +#include "StdMeshers_ViscousLayers.hxx" + +#include "SMDS_EdgePosition.hxx" +#include "SMDS_FaceOfNodes.hxx" +#include "SMDS_FacePosition.hxx" +#include "SMDS_MeshNode.hxx" +#include "SMDS_SetIterator.hxx" +#include "SMESHDS_Group.hxx" +#include "SMESHDS_Hypothesis.hxx" +#include "SMESH_Algo.hxx" +#include "SMESH_ComputeError.hxx" +#include "SMESH_ControlsDef.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_Group.hxx" +#include "SMESH_Mesh.hxx" +#include "SMESH_MesherHelper.hxx" +#include "SMESH_ProxyMesh.hxx" +#include "SMESH_subMesh.hxx" +#include "SMESH_subMeshEventListener.hxx" + +#include "utilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +//#define __myDEBUG + +using namespace std; + +//================================================================================ +namespace VISCOUS +{ + typedef int TGeomID; + + enum UIndex { U_TGT = 1, U_SRC, LEN_TGT }; + + /*! + * \brief SMESH_ProxyMesh computed by _ViscousBuilder for a SOLID. + * It is stored in a SMESH_subMesh of the SOLID as SMESH_subMeshEventListenerData + */ + struct _MeshOfSolid : public SMESH_ProxyMesh, + public SMESH_subMeshEventListenerData + { + bool _n2nMapComputed; + + _MeshOfSolid( SMESH_Mesh* mesh) + :SMESH_subMeshEventListenerData( /*isDeletable=*/true),_n2nMapComputed(false) + { + SMESH_ProxyMesh::setMesh( *mesh ); + } + + // returns submesh for a geom face + SMESH_ProxyMesh::SubMesh* getFaceSubM(const TopoDS_Face& F, bool create=false) + { + TGeomID i = SMESH_ProxyMesh::shapeIndex(F); + return create ? SMESH_ProxyMesh::getProxySubMesh(i) : findProxySubMesh(i); + } + void setNode2Node(const SMDS_MeshNode* srcNode, + const SMDS_MeshNode* proxyNode, + const SMESH_ProxyMesh::SubMesh* subMesh) + { + SMESH_ProxyMesh::setNode2Node( srcNode,proxyNode,subMesh); + } + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Listener of events of 3D sub-meshes computed with viscous layers. + * It is used to clear an inferior dim sub-meshes modified by viscous layers + */ + class _SrinkShapeListener : SMESH_subMeshEventListener + { + _SrinkShapeListener() + : SMESH_subMeshEventListener(/*isDeletable=*/false, + "StdMeshers_ViscousLayers::_SrinkShapeListener") {} + static SMESH_subMeshEventListener* Get() { static _SrinkShapeListener l; return &l; } + public: + virtual void ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* solidSM, + SMESH_subMeshEventListenerData* data, + const SMESH_Hypothesis* hyp) + { + if ( SMESH_subMesh::COMPUTE_EVENT == eventType && solidSM->IsEmpty() && data ) + { + SMESH_subMeshEventListener::ProcessEvent(event,eventType,solidSM,data,hyp); + } + } + static void ToClearSubMeshWithSolid( SMESH_subMesh* sm, + const TopoDS_Shape& solid) + { + SMESH_subMesh* solidSM = sm->GetFather()->GetSubMesh( solid ); + SMESH_subMeshEventListenerData* data = solidSM->GetEventListenerData( Get()); + if ( data ) + { + if ( find( data->mySubMeshes.begin(), data->mySubMeshes.end(), sm ) == + data->mySubMeshes.end()) + data->mySubMeshes.push_back( sm ); + } + else + { + data = SMESH_subMeshEventListenerData::MakeData( /*dependent=*/sm ); + sm->SetEventListener( Get(), data, /*whereToListenTo=*/solidSM ); + } + } + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Listener of events of 3D sub-meshes computed with viscous layers. + * It is used to store data computed by _ViscousBuilder for a sub-mesh and to + * delete the data as soon as it has been used + */ + class _ViscousListener : SMESH_subMeshEventListener + { + _ViscousListener(): + SMESH_subMeshEventListener(/*isDeletable=*/false, + "StdMeshers_ViscousLayers::_ViscousListener") {} + static SMESH_subMeshEventListener* Get() { static _ViscousListener l; return &l; } + public: + virtual void ProcessEvent(const int event, + const int eventType, + SMESH_subMesh* subMesh, + SMESH_subMeshEventListenerData* data, + const SMESH_Hypothesis* hyp) + { + if ( SMESH_subMesh::COMPUTE_EVENT == eventType ) + { + // delete SMESH_ProxyMesh containing temporary faces + subMesh->DeleteEventListener( this ); + } + } + // Finds or creates proxy mesh of the solid + static _MeshOfSolid* GetSolidMesh(SMESH_Mesh* mesh, + const TopoDS_Shape& solid, + bool toCreate=false) + { + if ( !mesh ) return 0; + SMESH_subMesh* sm = mesh->GetSubMesh(solid); + _MeshOfSolid* data = (_MeshOfSolid*) sm->GetEventListenerData( Get() ); + if ( !data && toCreate ) + { + data = new _MeshOfSolid(mesh); + data->mySubMeshes.push_back( sm ); // to find SOLID by _MeshOfSolid + sm->SetEventListener( Get(), data, sm ); + } + return data; + } + // Removes proxy mesh of the solid + static void RemoveSolidMesh(SMESH_Mesh* mesh, const TopoDS_Shape& solid) + { + mesh->GetSubMesh(solid)->DeleteEventListener( _ViscousListener::Get() ); + } + }; + + //-------------------------------------------------------------------------------- + /*! + * \brief Simplex (triangle or tetrahedron) based on 1 (tria) or 2 (tet) nodes of + * _LayerEdge and 2 nodes of the mesh surface beening smoothed. + * The class is used to check validity of face or volumes around a smoothed node; + * it stores only 2 nodes as the other nodes are stored by _LayerEdge. + */ + struct _Simplex + { + const SMDS_MeshNode *_nPrev, *_nNext; // nodes on a smoothed mesh surface + _Simplex(const SMDS_MeshNode* nPrev=0, const SMDS_MeshNode* nNext=0) + : _nPrev(nPrev), _nNext(nNext) {} + bool IsForward(const SMDS_MeshNode* nSrc, const gp_XYZ* pntTgt) const + { + const double M[3][3] = + {{ _nNext->X() - nSrc->X(), _nNext->Y() - nSrc->Y(), _nNext->Z() - nSrc->Z() }, + { pntTgt->X() - nSrc->X(), pntTgt->Y() - nSrc->Y(), pntTgt->Z() - nSrc->Z() }, + { _nPrev->X() - nSrc->X(), _nPrev->Y() - nSrc->Y(), _nPrev->Z() - nSrc->Z() }}; + double determinant = ( + M[0][0]*M[1][1]*M[2][2] + + M[0][1]*M[1][2]*M[2][0] + + M[0][2]*M[1][0]*M[2][1] + - M[0][0]*M[1][2]*M[2][1] + - M[0][1]*M[1][0]*M[2][2] + - M[0][2]*M[1][1]*M[2][0]); + return determinant > 1e-100; + } + bool IsForward(const gp_XY& tgtUV, + const SMDS_MeshNode* smoothedNode, + const TopoDS_Face& face, + SMESH_MesherHelper& helper, + const double refSign) const + { + gp_XY prevUV = helper.GetNodeUV( face, _nPrev, smoothedNode ); + gp_XY nextUV = helper.GetNodeUV( face, _nNext, smoothedNode ); + gp_Vec2d v1( tgtUV, prevUV ), v2( tgtUV, nextUV ); + double d = v1 ^ v2; + return d*refSign > 1e-100; + } + bool IsNeighbour(const _Simplex& other) const + { + return _nPrev == other._nNext || _nNext == other._nPrev; + } + }; + //-------------------------------------------------------------------------------- + /*! + * Structure used to take into account surface curvature while smoothing + */ + struct _Curvature + { + double _r; // radius + double _k; // factor to correct node smoothed position + public: + static _Curvature* New( double avgNormProj, double avgDist ) + { + _Curvature* c = 0; + if ( fabs( avgNormProj / avgDist ) > 1./200 ) + { + c = new _Curvature; + c->_r = avgDist * avgDist / avgNormProj; + c->_k = avgDist * avgDist / c->_r / c->_r; + c->_k *= ( c->_r < 0 ? 1/1.1 : 1.1 ); // not to be too restrictive + } + return c; + } + double lenDelta(double len) const { return _k * ( _r + len ); } + }; + struct _LayerEdge; + //-------------------------------------------------------------------------------- + /*! + * Structure used to smooth a _LayerEdge (master) based on an EDGE. + */ + struct _2NearEdges + { + // target nodes of 2 neighbour _LayerEdge's based on the same EDGE + const SMDS_MeshNode* _nodes[2]; + // vectors from source nodes of 2 _LayerEdge's to the source node of master _LayerEdge + //gp_XYZ _vec[2]; + double _wgt[2]; // weights of _nodes + _LayerEdge* _edges[2]; + + // normal to plane passing through _LayerEdge._normal and tangent of EDGE + gp_XYZ* _plnNorm; + + _2NearEdges() { _nodes[0]=_nodes[1]=0; _plnNorm = 0; } + void reverse() { + std::swap( _nodes[0], _nodes[1] ); + std::swap( _wgt[0], _wgt[1] ); + } + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Edge normal to surface, connecting a node on solid surface (_nodes[0]) + * and a node of the most internal layer (_nodes.back()) + */ + struct _LayerEdge + { + vector< const SMDS_MeshNode*> _nodes; + + gp_XYZ _normal; // to solid surface + vector _pos; // points computed during inflation + double _len; // length achived with the last step + double _cosin; // of angle (_normal ^ surface) + double _lenFactor; // to compute _len taking _cosin into account + + // face or edge w/o layer along or near which _LayerEdge is inflated + TopoDS_Shape _sWOL; + // simplices connected to the source node (_nodes[0]); + // used for smoothing and quality check of _LayerEdge's based on the FACE + vector<_Simplex> _simplices; + // data for smoothing of _LayerEdge's based on the EDGE + _2NearEdges* _2neibors; + + _Curvature* _curvature; + // TODO:: detele _Curvature, _plnNorm + + void SetNewLength( double len, SMESH_MesherHelper& helper ); + bool SetNewLength2d( Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper ); + void SetDataByNeighbors( const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + SMESH_MesherHelper& helper); + void InvalidateStep( int curStep ); + bool Smooth(int& badNb); + bool SmoothOnEdge(Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper); + bool FindIntersection( SMESH_ElementSearcher& searcher, + double & distance, + const double& epsilon, + const SMDS_MeshElement** face = 0); + bool SegTriaInter( const gp_Ax1& lastSegment, + const SMDS_MeshNode* n0, + const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + double& dist, + const double& epsilon) const; + gp_Ax1 LastSegment(double& segLen) const; + bool IsOnEdge() const { return _2neibors; } + void Copy( _LayerEdge& other, SMESH_MesherHelper& helper ); + void SetCosin( double cosin ); + }; + struct _LayerEdgeCmp + { + bool operator () (const _LayerEdge* e1, const _LayerEdge* e2) const + { + const bool cmpNodes = ( e1 && e2 && e1->_nodes.size() && e2->_nodes.size() ); + return cmpNodes ? ( e1->_nodes[0]->GetID() < e2->_nodes[0]->GetID()) : ( e1 < e2 ); + } + }; + //-------------------------------------------------------------------------------- + + typedef map< const SMDS_MeshNode*, _LayerEdge*, TIDCompare > TNode2Edge; + + //-------------------------------------------------------------------------------- + /*! + * \brief Data of a SOLID + */ + struct _SolidData + { + TopoDS_Shape _solid; + const StdMeshers_ViscousLayers* _hyp; + _MeshOfSolid* _proxyMesh; + set _reversedFaceIds; + + double _stepSize, _stepSizeCoeff; + const SMDS_MeshNode* _stepSizeNodes[2]; + + TNode2Edge _n2eMap; + // edges of _n2eMap. We keep same data in two containers because + // iteration over the map is 5 time longer than over the vector + vector< _LayerEdge* > _edges; + + // key: an id of shape (EDGE or VERTEX) shared by a FACE with + // layers and a FACE w/o layers + // value: the shape (FACE or EDGE) to shrink mesh on. + // _LayerEdge's basing on nodes on key shape are inflated along the value shape + map< TGeomID, TopoDS_Shape > _shrinkShape2Shape; + + // FACE's WOL, srink on which is forbiden due to algo on the adjacent SOLID + set< TGeomID > _noShrinkFaces; + + // to + map< TGeomID,Handle(Geom_Curve)> _edge2curve; + + // end indices in _edges of _LayerEdge on one shape to smooth + vector< int > _endEdgeToSmooth; + + double _epsilon; // precision for SegTriaInter() + + int _index; // for debug + + _SolidData(const TopoDS_Shape& s=TopoDS_Shape(), + const StdMeshers_ViscousLayers* h=0, + _MeshOfSolid* m=0) :_solid(s), _hyp(h), _proxyMesh(m) {} + ~_SolidData(); + + Handle(Geom_Curve) CurveForSmooth( const TopoDS_Edge& E, + const int iFrom, + const int iTo, + Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper); + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Data of node on a shrinked FACE + */ + struct _SmoothNode + { + const SMDS_MeshNode* _node; + //vector _nodesAround; + vector<_Simplex> _simplices; // for quality check + + bool Smooth(int& badNb, + Handle(Geom_Surface)& surface, + SMESH_MesherHelper& helper, + const double refSign, + bool isCentroidal, + bool set3D); + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Builder of viscous layers + */ + class _ViscousBuilder + { + public: + _ViscousBuilder(); + // does it's job + SMESH_ComputeErrorPtr Compute(SMESH_Mesh& mesh, + const TopoDS_Shape& shape); + + // restore event listeners used to clear an inferior dim sub-mesh modified by viscous layers + void RestoreListeners(); + + // computes SMESH_ProxyMesh::SubMesh::_n2n; + bool MakeN2NMap( _MeshOfSolid* pm ); + + private: + + bool findSolidsWithLayers(); + bool findFacesWithLayers(); + bool makeLayer(_SolidData& data); + bool setEdgeData(_LayerEdge& edge, const set& subIds, + SMESH_MesherHelper& helper, _SolidData& data); + bool findNeiborsOnEdge(const _LayerEdge* edge, + const SMDS_MeshNode*& n1, + const SMDS_MeshNode*& n2, + _SolidData& data); + void getSimplices( const SMDS_MeshNode* node, vector<_Simplex>& simplices, + const set& ingnoreShapes, + const _SolidData* dataToCheckOri = 0, + const bool toSort = false); + bool sortEdges( _SolidData& data, + vector< vector<_LayerEdge*> >& edgesByGeom); + void limitStepSize( _SolidData& data, + const SMDS_MeshElement* face, + const double cosin); + void limitStepSize( _SolidData& data, const double minSize); + bool inflate(_SolidData& data); + bool smoothAndCheck(_SolidData& data, const int nbSteps, double & distToIntersection); + bool smoothAnalyticEdge( _SolidData& data, + const int iFrom, + const int iTo, + Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper); + bool updateNormals( _SolidData& data, SMESH_MesherHelper& helper ); + bool refine(_SolidData& data); + bool shrink(); + bool prepareEdgeToShrink( _LayerEdge& edge, const TopoDS_Face& F, + SMESH_MesherHelper& helper, + const SMESHDS_SubMesh* faceSubMesh ); + void fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& helper); + bool addBoundaryElements(); + + bool error( const string& text, int solidID=-1 ); + SMESHDS_Mesh* getMeshDS() { return _mesh->GetMeshDS(); } + + // debug + void makeGroupOfLE(); + + SMESH_Mesh* _mesh; + SMESH_ComputeErrorPtr _error; + + vector< _SolidData > _sdVec; + set _ignoreShapeIds; + int _tmpFaceID; + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Shrinker of nodes on the EDGE + */ + class _Shrinker1D + { + vector _initU; + vector _normPar; + vector _nodes; + const _LayerEdge* _edges[2]; + bool _done; + public: + void AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper ); + void Compute(bool set3D, SMESH_MesherHelper& helper); + void RestoreParams(); + void SwapSrcTgtNodes(SMESHDS_Mesh* mesh); + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Class of temporary mesh face. + * We can't use SMDS_FaceOfNodes since it's impossible to set it's ID which is + * needed because SMESH_ElementSearcher internaly uses set of elements sorted by ID + */ + struct TmpMeshFace : public SMDS_MeshElement + { + vector _nn; + TmpMeshFace( const vector& nodes, int id): + SMDS_MeshElement(id), _nn(nodes) {} + virtual const SMDS_MeshNode* GetNode(const int ind) const { return _nn[ind]; } + virtual SMDSAbs_ElementType GetType() const { return SMDSAbs_Face; } + virtual vtkIdType GetVtkType() const { return -1; } + virtual SMDSAbs_EntityType GetEntityType() const { return SMDSEntity_Last; } + virtual SMDSAbs_GeometryType GetGeomType() const { return SMDSGeom_TRIANGLE; } +virtual SMDS_ElemIteratorPtr elementsIterator(SMDSAbs_ElementType type) const + { return SMDS_ElemIteratorPtr( new SMDS_NodeVectorElemIterator( _nn.begin(), _nn.end()));} + }; + //-------------------------------------------------------------------------------- + /*! + * \brief Class of temporary mesh face storing _LayerEdge it's based on + */ + struct TmpMeshFaceOnEdge : public TmpMeshFace + { + _LayerEdge *_le1, *_le2; + TmpMeshFaceOnEdge( _LayerEdge* le1, _LayerEdge* le2, int ID ): + TmpMeshFace( vector(4), ID ), _le1(le1), _le2(le2) + { + _nn[0]=_le1->_nodes[0]; + _nn[1]=_le1->_nodes.back(); + _nn[2]=_le2->_nodes.back(); + _nn[3]=_le2->_nodes[0]; + } + }; +} // namespace VISCOUS + +//================================================================================ +// StdMeshers_ViscousLayers hypothesis +// +StdMeshers_ViscousLayers::StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen) + :SMESH_Hypothesis(hypId, studyId, gen), + _nbLayers(1), _thickness(1), _stretchFactor(1) +{ + _name = StdMeshers_ViscousLayers::GetHypType(); + _param_algo_dim = -3; // auxiliary hyp used by 3D algos +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayers::SetIgnoreFaces(const std::vector& faceIds) +{ + if ( faceIds != _ignoreFaceIds ) + _ignoreFaceIds = faceIds, NotifySubMeshesHypothesisModification(); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayers::SetTotalThickness(double thickness) +{ + if ( thickness != _thickness ) + _thickness = thickness, NotifySubMeshesHypothesisModification(); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayers::SetNumberLayers(int nb) +{ + if ( _nbLayers != nb ) + _nbLayers = nb, NotifySubMeshesHypothesisModification(); +} // -------------------------------------------------------------------------------- +void StdMeshers_ViscousLayers::SetStretchFactor(double factor) +{ + if ( _stretchFactor != factor ) + _stretchFactor = factor, NotifySubMeshesHypothesisModification(); +} // -------------------------------------------------------------------------------- +SMESH_ProxyMesh::Ptr +StdMeshers_ViscousLayers::Compute(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + const bool toMakeN2NMap) const +{ + using namespace VISCOUS; + _ViscousBuilder bulder; + SMESH_ComputeErrorPtr err = bulder.Compute( theMesh, theShape ); + if ( err && !err->IsOK() ) + return SMESH_ProxyMesh::Ptr(); + + vector components; + TopExp_Explorer exp( theShape, TopAbs_SOLID ); + for ( ; exp.More(); exp.Next() ) + { + if ( _MeshOfSolid* pm = + _ViscousListener::GetSolidMesh( &theMesh, exp.Current(), /*toCreate=*/false)) + { + if ( toMakeN2NMap && !pm->_n2nMapComputed ) + if ( !bulder.MakeN2NMap( pm )) + return SMESH_ProxyMesh::Ptr(); + components.push_back( SMESH_ProxyMesh::Ptr( pm )); + pm->myIsDeletable = false; // it will de deleted by boost::shared_ptr + } + _ViscousListener::RemoveSolidMesh ( &theMesh, exp.Current() ); + } + switch ( components.size() ) + { + case 0: break; + + case 1: return components[0]; + + default: return SMESH_ProxyMesh::Ptr( new SMESH_ProxyMesh( components )); + } + return SMESH_ProxyMesh::Ptr(); +} // -------------------------------------------------------------------------------- +std::ostream & StdMeshers_ViscousLayers::SaveTo(std::ostream & save) +{ + save << " " << _nbLayers + << " " << _thickness + << " " << _stretchFactor + << " " << _ignoreFaceIds.size(); + for ( unsigned i = 0; i < _ignoreFaceIds.size(); ++i ) + save << " " << _ignoreFaceIds[i]; + return save; +} // -------------------------------------------------------------------------------- +std::istream & StdMeshers_ViscousLayers::LoadFrom(std::istream & load) +{ + int nbFaces, faceID; + load >> _nbLayers >> _thickness >> _stretchFactor >> nbFaces; + while ( _ignoreFaceIds.size() < nbFaces && load >> faceID ) + _ignoreFaceIds.push_back( faceID ); + return load; +} // -------------------------------------------------------------------------------- +bool StdMeshers_ViscousLayers::SetParametersByMesh(const SMESH_Mesh* theMesh, + const TopoDS_Shape& theShape) +{ + // TODO + return false; +} +// END StdMeshers_ViscousLayers hypothesis +//================================================================================ + +namespace +{ + gp_XYZ getEdgeDir( const TopoDS_Edge& E, const TopoDS_Vertex& fromV ) + { + gp_Vec dir; + double f,l; + Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l ); + gp_Pnt p = BRep_Tool::Pnt( fromV ); + double distF = p.SquareDistance( c->Value( f )); + double distL = p.SquareDistance( c->Value( l )); + c->D1(( distF < distL ? f : l), p, dir ); + if ( distL < distF ) dir.Reverse(); + return dir.XYZ(); + } + //-------------------------------------------------------------------------------- + gp_XYZ getEdgeDir( const TopoDS_Edge& E, const SMDS_MeshNode* atNode, + SMESH_MesherHelper& helper) + { + gp_Vec dir; + double f,l; gp_Pnt p; + Handle(Geom_Curve) c = BRep_Tool::Curve( E, f, l ); + double u = helper.GetNodeU( E, atNode ); + c->D1( u, p, dir ); + return dir.XYZ(); + } + //-------------------------------------------------------------------------------- + gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Edge& fromE, + const SMDS_MeshNode* node, SMESH_MesherHelper& helper, bool& ok) + { + gp_XY uv = helper.GetNodeUV( F, node, 0, &ok ); + Handle(Geom_Surface) surface = BRep_Tool::Surface( F ); + gp_Pnt p; gp_Vec du, dv, norm; + surface->D1( uv.X(),uv.Y(), p, du,dv ); + norm = du ^ dv; + + double f,l; + Handle(Geom_Curve) c = BRep_Tool::Curve( fromE, f, l ); + double u = helper.GetNodeU( fromE, node, 0, &ok ); + c->D1( u, p, du ); + TopAbs_Orientation o = helper.GetSubShapeOri( F.Oriented(TopAbs_FORWARD), fromE); + if ( o == TopAbs_REVERSED ) + du.Reverse(); + + gp_Vec dir = norm ^ du; + + if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_VERTEX && + helper.IsClosedEdge( fromE )) + { + if ( fabs(u-f) < fabs(u-l )) c->D1( l, p, dv ); + else c->D1( f, p, dv ); + if ( o == TopAbs_REVERSED ) + dv.Reverse(); + gp_Vec dir2 = norm ^ dv; + dir = dir.Normalized() + dir2.Normalized(); + } + return dir.XYZ(); + } + //-------------------------------------------------------------------------------- + gp_XYZ getFaceDir( const TopoDS_Face& F, const TopoDS_Vertex& fromV, + const SMDS_MeshNode* node, SMESH_MesherHelper& helper, + bool& ok, double* cosin=0) + { + double f,l; TopLoc_Location loc; + vector< TopoDS_Edge > edges; // sharing a vertex + PShapeIteratorPtr eIt = helper.GetAncestors( fromV, *helper.GetMesh(), TopAbs_EDGE); + while ( eIt->more()) + { + const TopoDS_Edge* e = static_cast( eIt->next() ); + if ( helper.IsSubShape( *e, F ) && !BRep_Tool::Curve( *e, loc,f,l).IsNull() ) + edges.push_back( *e ); + } + gp_XYZ dir(0,0,0); + if ( !( ok = ( edges.size() > 0 ))) return dir; + // get average dir of edges going fromV + gp_Vec edgeDir; + for ( unsigned i = 0; i < edges.size(); ++i ) + { + edgeDir = getEdgeDir( edges[i], fromV ); + double size2 = edgeDir.SquareMagnitude(); + if ( size2 > numeric_limits::min() ) + edgeDir /= sqrt( size2 ); + else + ok = false; + dir += edgeDir.XYZ(); + } + gp_XYZ fromEdgeDir = getFaceDir( F, edges[0], node, helper, ok ); + if ( edges.size() == 1 || dir.SquareModulus() < 1e-10) + dir = fromEdgeDir; + else if ( dir * fromEdgeDir < 0 ) + dir *= -1; + if ( ok ) + { + //dir /= edges.size(); + if ( cosin ) { + double angle = edgeDir.Angle( dir ); + *cosin = cos( angle ); + } + } + return dir; + } + //================================================================================ + /*! + * \brief Returns true if a FACE is bound by a concave EDGE + */ + //================================================================================ + + bool isConcave( const TopoDS_Face& F, SMESH_MesherHelper& helper ) + { + gp_Vec2d drv1, drv2; + gp_Pnt2d p; + TopExp_Explorer eExp( F.Oriented( TopAbs_FORWARD ), TopAbs_EDGE ); + for ( ; eExp.More(); eExp.Next() ) + { + const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() ); + if ( BRep_Tool::Degenerated( E )) continue; + // check if 2D curve is concave + BRepAdaptor_Curve2d curve( E, F ); + const int nbIntervals = curve.NbIntervals( GeomAbs_C2 ); + TColStd_Array1OfReal intervals(1, nbIntervals + 1 ); + curve.Intervals( intervals, GeomAbs_C2 ); + bool isConvex = true; + for ( int i = 1; i <= nbIntervals && isConvex; ++i ) + { + double u1 = intervals( i ); + double u2 = intervals( i+1 ); + curve.D2( 0.5*( u1+u2 ), p, drv1, drv2 ); + double cross = drv2 ^ drv1; + if ( E.Orientation() == TopAbs_REVERSED ) + cross = -cross; + isConvex = ( cross < 1e-9 ); + } + // check if concavity is strong enough to care about it + //const double maxAngle = 5 * Standard_PI180; + if ( !isConvex ) + { + //cout << "Concave FACE " << helper.GetMeshDS()->ShapeToIndex( F ) << endl; + return true; + // map< double, const SMDS_MeshNode* > u2nodes; + // if ( !SMESH_Algo::GetSortedNodesOnEdge( helper.GetMeshDS(), E, + // /*ignoreMedium=*/true, u2nodes)) + // continue; + // map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin(); + // gp_Pnt2d uvPrev = helper.GetNodeUV( F, u2n->second ); + // double uPrev = u2n->first; + // for ( ++u2n; u2n != u2nodes.end(); ++u2n ) + // { + // gp_Pnt2d uv = helper.GetNodeUV( F, u2n->second ); + // gp_Vec2d segmentDir( uvPrev, uv ); + // curve.D1( uPrev, p, drv1 ); + // try { + // if ( fabs( segmentDir.Angle( drv1 )) > maxAngle ) + // return true; + // } + // catch ( ... ) {} + // uvPrev = uv; + // uPrev = u2n->first; + // } + } + } + return false; + } + //-------------------------------------------------------------------------------- + // DEBUG. Dump intermediate node positions into a python script +#ifdef __myDEBUG + ofstream* py; + struct PyDump { + PyDump() { + const char* fname = "/tmp/viscous.py"; + cout << "execfile('"<GetShapeToMesh(), TopAbs_SOLID, allSolids ); + _sdVec.reserve( allSolids.Extent()); + + SMESH_Gen* gen = _mesh->GetGen(); + for ( int i = 1; i <= allSolids.Extent(); ++i ) + { + // find StdMeshers_ViscousLayers hyp assigned to the i-th solid + SMESH_Algo* algo = gen->GetAlgo( *_mesh, allSolids(i) ); + if ( !algo ) continue; + // TODO: check if algo is hidden + const list & allHyps = + algo->GetUsedHypothesis(*_mesh, allSolids(i), /*ignoreAuxiliary=*/false); + list< const SMESHDS_Hypothesis *>::const_iterator hyp = allHyps.begin(); + const StdMeshers_ViscousLayers* viscHyp = 0; + for ( ; hyp != allHyps.end() && !viscHyp; ++hyp ) + viscHyp = dynamic_cast( *hyp ); + if ( viscHyp ) + { + _MeshOfSolid* proxyMesh = _ViscousListener::GetSolidMesh( _mesh, + allSolids(i), + /*toCreate=*/true); + _sdVec.push_back( _SolidData( allSolids(i), viscHyp, proxyMesh )); + _sdVec.back()._index = getMeshDS()->ShapeToIndex( allSolids(i)); + } + } + if ( _sdVec.empty() ) + return error + ( SMESH_Comment(StdMeshers_ViscousLayers::GetHypType()) << " hypothesis not found",0); + + return true; +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +bool _ViscousBuilder::findFacesWithLayers() +{ + // collect all faces to ignore defined by hyp + vector ignoreFaces; + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + vector ids = _sdVec[i]._hyp->GetIgnoreFaces(); + for ( unsigned i = 0; i < ids.size(); ++i ) + { + const TopoDS_Shape& s = getMeshDS()->IndexToShape( ids[i] ); + if ( !s.IsNull() && s.ShapeType() == TopAbs_FACE ) + { + _ignoreShapeIds.insert( ids[i] ); + ignoreFaces.push_back( s ); + } + } + } + + // ignore internal faces + SMESH_MesherHelper helper( *_mesh ); + TopExp_Explorer exp; + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + exp.Init( _sdVec[i]._solid.Oriented( TopAbs_FORWARD ), TopAbs_FACE ); + for ( ; exp.More(); exp.Next() ) + { + TGeomID faceInd = getMeshDS()->ShapeToIndex( exp.Current() ); + if ( helper.NbAncestors( exp.Current(), *_mesh, TopAbs_SOLID ) > 1 ) + { + _ignoreShapeIds.insert( faceInd ); + ignoreFaces.push_back( exp.Current() ); + if ( SMESH_Algo::IsReversedSubMesh( TopoDS::Face( exp.Current() ), getMeshDS())) + _sdVec[i]._reversedFaceIds.insert( faceInd ); + } + } + } + + // Find faces to shrink mesh on (solution 2 in issue 0020832); + TopTools_IndexedMapOfShape shapes; + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + shapes.Clear(); + TopExp::MapShapes(_sdVec[i]._solid, TopAbs_EDGE, shapes); + for ( int iE = 1; iE <= shapes.Extent(); ++iE ) + { + const TopoDS_Shape& edge = shapes(iE); + // find 2 faces sharing an edge + TopoDS_Shape FF[2]; + PShapeIteratorPtr fIt = helper.GetAncestors(edge, *_mesh, TopAbs_FACE); + while ( fIt->more()) + { + const TopoDS_Shape* f = fIt->next(); + if ( helper.IsSubShape( *f, _sdVec[i]._solid)) + FF[ int( !FF[0].IsNull()) ] = *f; + } + if( FF[1].IsNull() ) continue; // seam edge can be shared by 1 FACE only + // check presence of layers on them + int ignore[2]; + for ( int j = 0; j < 2; ++j ) + ignore[j] = _ignoreShapeIds.count ( getMeshDS()->ShapeToIndex( FF[j] )); + if ( ignore[0] == ignore[1] ) continue; // nothing interesting + TopoDS_Shape fWOL = FF[ ignore[0] ? 0 : 1 ]; + // add edge to maps + TGeomID edgeInd = getMeshDS()->ShapeToIndex( edge ); + _sdVec[i]._shrinkShape2Shape.insert( make_pair( edgeInd, fWOL )); + } + } + // Exclude from _shrinkShape2Shape FACE's that can't be shrinked since + // the algo of the SOLID sharing the FACE does not support it + set< string > notSupportAlgos; notSupportAlgos.insert("Hexa_3D"); + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + TopTools_MapOfShape noShrinkVertices; + map< TGeomID, TopoDS_Shape >::iterator e2f = _sdVec[i]._shrinkShape2Shape.begin(); + for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ++e2f ) + { + const TopoDS_Shape& fWOL = e2f->second; + TGeomID edgeID = e2f->first; + bool notShrinkFace = false; + PShapeIteratorPtr soIt = helper.GetAncestors(fWOL, *_mesh, TopAbs_SOLID); + while ( soIt->more()) + { + const TopoDS_Shape* solid = soIt->next(); + if ( _sdVec[i]._solid.IsSame( *solid )) continue; + SMESH_Algo* algo = _mesh->GetGen()->GetAlgo( *_mesh, *solid ); + if ( !algo || !notSupportAlgos.count( algo->GetName() )) continue; + notShrinkFace = true; + for ( unsigned j = 0; j < _sdVec.size(); ++j ) + { + if ( _sdVec[j]._solid.IsSame( *solid ) ) + if ( _sdVec[j]._shrinkShape2Shape.count( edgeID )) + notShrinkFace = false; + } + } + if ( notShrinkFace ) + { + _sdVec[i]._noShrinkFaces.insert( getMeshDS()->ShapeToIndex( fWOL )); + for ( TopExp_Explorer vExp( fWOL, TopAbs_VERTEX ); vExp.More(); vExp.Next() ) + noShrinkVertices.Add( vExp.Current() ); + } + } + // erase from _shrinkShape2Shape all srink EDGE's of a SOLID connected + // to the found not shrinked fWOL's + e2f = _sdVec[i]._shrinkShape2Shape.begin(); + for ( ; e2f != _sdVec[i]._shrinkShape2Shape.end(); ) + { + TGeomID edgeID = e2f->first; + TopoDS_Vertex VV[2]; + TopExp::Vertices( TopoDS::Edge( getMeshDS()->IndexToShape( edgeID )),VV[0],VV[1]); + if ( noShrinkVertices.Contains( VV[0] ) || noShrinkVertices.Contains( VV[1] )) + { + _sdVec[i]._noShrinkFaces.insert( getMeshDS()->ShapeToIndex( e2f->second )); + _sdVec[i]._shrinkShape2Shape.erase( e2f++ ); + } + else + { + e2f++; + } + } + } + + // Find the SHAPE along which to inflate _LayerEdge based on VERTEX + + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + shapes.Clear(); + TopExp::MapShapes(_sdVec[i]._solid, TopAbs_VERTEX, shapes); + for ( int iV = 1; iV <= shapes.Extent(); ++iV ) + { + const TopoDS_Shape& vertex = shapes(iV); + // find faces WOL sharing the vertex + vector< TopoDS_Shape > facesWOL; + int totalNbFaces = 0; + PShapeIteratorPtr fIt = helper.GetAncestors(vertex, *_mesh, TopAbs_FACE); + while ( fIt->more()) + { + const TopoDS_Shape* f = fIt->next(); + const int fID = getMeshDS()->ShapeToIndex( *f ); + if ( helper.IsSubShape( *f, _sdVec[i]._solid ) ) + { + totalNbFaces++; + if ( _ignoreShapeIds.count ( fID ) && ! _sdVec[i]._noShrinkFaces.count( fID )) + facesWOL.push_back( *f ); + } + } + if ( facesWOL.size() == totalNbFaces || facesWOL.empty() ) + continue; // no layers at this vertex or no WOL + TGeomID vInd = getMeshDS()->ShapeToIndex( vertex ); + switch ( facesWOL.size() ) + { + case 1: + { + helper.SetSubShape( facesWOL[0] ); + if ( helper.IsRealSeam( vInd )) // inflate along a seam edge? + { + TopoDS_Shape seamEdge; + PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE); + while ( eIt->more() && seamEdge.IsNull() ) + { + const TopoDS_Shape* e = eIt->next(); + if ( helper.IsRealSeam( *e ) ) + seamEdge = *e; + } + if ( !seamEdge.IsNull() ) + { + _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, seamEdge )); + break; + } + } + _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, facesWOL[0] )); + break; + } + case 2: + { + // find an edge shared by 2 faces + PShapeIteratorPtr eIt = helper.GetAncestors(vertex, *_mesh, TopAbs_EDGE); + while ( eIt->more()) + { + const TopoDS_Shape* e = eIt->next(); + if ( helper.IsSubShape( *e, facesWOL[0]) && + helper.IsSubShape( *e, facesWOL[1])) + { + _sdVec[i]._shrinkShape2Shape.insert( make_pair( vInd, *e )); break; + } + } + break; + } + default: + return error("Not yet supported case", _sdVec[i]._index); + } + } + } + + return true; +} + +//================================================================================ +/*! + * \brief Create the inner surface of the viscous layer and prepare data for infation + */ +//================================================================================ + +bool _ViscousBuilder::makeLayer(_SolidData& data) +{ + // get all sub-shapes to make layers on + set subIds, faceIds; + subIds = data._noShrinkFaces; + TopExp_Explorer exp( data._solid, TopAbs_FACE ); + for ( ; exp.More(); exp.Next() ) + if ( ! _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() ))) + { + SMESH_subMesh* fSubM = _mesh->GetSubMesh( exp.Current() ); + faceIds.insert( fSubM->GetId() ); + SMESH_subMeshIteratorPtr subIt = + fSubM->getDependsOnIterator(/*includeSelf=*/true, /*complexShapeFirst=*/false); + while ( subIt->more() ) + subIds.insert( subIt->next()->GetId() ); + } + + // make a map to find new nodes on sub-shapes shared with other SOLID + map< TGeomID, TNode2Edge* > s2neMap; + map< TGeomID, TNode2Edge* >::iterator s2ne; + map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin(); + for (; s2s != data._shrinkShape2Shape.end(); ++s2s ) + { + TGeomID shapeInd = s2s->first; + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + if ( _sdVec[i]._index == data._index ) continue; + map< TGeomID, TopoDS_Shape >::iterator s2s2 = _sdVec[i]._shrinkShape2Shape.find( shapeInd ); + if ( s2s2 != _sdVec[i]._shrinkShape2Shape.end() && + *s2s == *s2s2 && !_sdVec[i]._n2eMap.empty() ) + { + s2neMap.insert( make_pair( shapeInd, &_sdVec[i]._n2eMap )); + break; + } + } + } + + // Create temporary faces and _LayerEdge's + + dumpFunction(SMESH_Comment("makeLayers_")< newNodes; // of a mesh face + TNode2Edge::iterator n2e2; + + // collect _LayerEdge's of shapes they are based on + const int nbShapes = getMeshDS()->MaxShapeIndex(); + vector< vector<_LayerEdge*> > edgesByGeom( nbShapes+1 ); + + for ( set::iterator id = faceIds.begin(); id != faceIds.end(); ++id ) + { + SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( *id ); + if ( !smDS ) return error(SMESH_Comment("Not meshed face ") << *id, data._index ); + + const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( *id )); + SMESH_ProxyMesh::SubMesh* proxySub = + data._proxyMesh->getFaceSubM( F, /*create=*/true); + + SMDS_ElemIteratorPtr eIt = smDS->GetElements(); + while ( eIt->more() ) + { + const SMDS_MeshElement* face = eIt->next(); + newNodes.resize( face->NbCornerNodes() ); + double faceMaxCosin = -1; + for ( int i = 0 ; i < face->NbCornerNodes(); ++i ) + { + const SMDS_MeshNode* n = face->GetNode(i); + TNode2Edge::iterator n2e = data._n2eMap.insert( make_pair( n, (_LayerEdge*)0 )).first; + if ( !(*n2e).second ) + { + // add a _LayerEdge + _LayerEdge* edge = new _LayerEdge(); + n2e->second = edge; + edge->_nodes.push_back( n ); + const int shapeID = n->getshapeId(); + edgesByGeom[ shapeID ].push_back( edge ); + + // set edge data or find already refined _LayerEdge and get data from it + if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE && + ( s2ne = s2neMap.find( shapeID )) != s2neMap.end() && + ( n2e2 = (*s2ne).second->find( n )) != s2ne->second->end()) + { + _LayerEdge* foundEdge = (*n2e2).second; + edge->Copy( *foundEdge, helper ); + // location of the last node is modified but we can restore + // it by node position on _sWOL stored by the node + const_cast< SMDS_MeshNode* > + ( edge->_nodes.back() )->setXYZ( n->X(), n->Y(), n->Z() ); + } + else + { + edge->_nodes.push_back( helper.AddNode( n->X(), n->Y(), n->Z() )); + if ( !setEdgeData( *edge, subIds, helper, data )) + return false; + } + dumpMove(edge->_nodes.back()); + if ( edge->_cosin > 0.01 ) + { + if ( edge->_cosin > faceMaxCosin ) + faceMaxCosin = edge->_cosin; + } + } + newNodes[ i ] = n2e->second->_nodes.back(); + } + // create a temporary face + const SMDS_MeshElement* newFace = new TmpMeshFace( newNodes, --_tmpFaceID ); + proxySub->AddElement( newFace ); + + // compute inflation step size by min size of element on a convex surface + if ( faceMaxCosin > 0.1 ) + limitStepSize( data, face, faceMaxCosin ); + } // loop on 2D elements on a FACE + } // loop on FACEs of a SOLID + + data._epsilon = 1e-7; + if ( data._stepSize < 1. ) + data._epsilon *= data._stepSize; + + // Put _LayerEdge's into a vector + + if ( !sortEdges( data, edgesByGeom )) + return false; + + // Set target nodes into _Simplex and _2NearEdges + TNode2Edge::iterator n2e; + for ( unsigned i = 0; i < data._edges.size(); ++i ) + { + if ( data._edges[i]->IsOnEdge()) + for ( int j = 0; j < 2; ++j ) + { + if ( data._edges[i]->_nodes.back()->NbInverseElements(SMDSAbs_Volume) > 0 ) + break; // _LayerEdge is shared by two _SolidData's + const SMDS_MeshNode* & n = data._edges[i]->_2neibors->_nodes[j]; + if (( n2e = data._n2eMap.find( n )) == data._n2eMap.end() ) + return error("_LayerEdge not found by src node", data._index); + n = (*n2e).second->_nodes.back(); + data._edges[i]->_2neibors->_edges[j] = n2e->second; + } + else + for ( unsigned j = 0; j < data._edges[i]->_simplices.size(); ++j ) + { + _Simplex& s = data._edges[i]->_simplices[j]; + s._nNext = data._n2eMap[ s._nNext ]->_nodes.back(); + s._nPrev = data._n2eMap[ s._nPrev ]->_nodes.back(); + } + } + + dumpFunctionEnd(); + return true; +} + +//================================================================================ +/*! + * \brief Compute inflation step size by min size of element on a convex surface + */ +//================================================================================ + +void _ViscousBuilder::limitStepSize( _SolidData& data, + const SMDS_MeshElement* face, + const double cosin) +{ + int iN = 0; + double minSize = 10 * data._stepSize; + const int nbNodes = face->NbCornerNodes(); + for ( int i = 0; i < nbNodes; ++i ) + { + const SMDS_MeshNode* nextN = face->GetNode( SMESH_MesherHelper::WrapIndex( i+1, nbNodes )); + const SMDS_MeshNode* curN = face->GetNode( i ); + if ( nextN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE || + curN->GetPosition()->GetTypeOfPosition() == SMDS_TOP_FACE ) + { + double dist = SMESH_TNodeXYZ( face->GetNode(i)).Distance( nextN ); + if ( dist < minSize ) + minSize = dist, iN = i; + } + } + double newStep = 0.8 * minSize / cosin; + if ( newStep < data._stepSize ) + { + data._stepSize = newStep; + data._stepSizeCoeff = 0.8 / cosin; + data._stepSizeNodes[0] = face->GetNode( iN ); + data._stepSizeNodes[1] = face->GetNode( SMESH_MesherHelper::WrapIndex( iN+1, nbNodes )); + } +} + +//================================================================================ +/*! + * \brief Compute inflation step size by min size of element on a convex surface + */ +//================================================================================ + +void _ViscousBuilder::limitStepSize( _SolidData& data, const double minSize) +{ + if ( minSize < data._stepSize ) + { + data._stepSize = minSize; + if ( data._stepSizeNodes[0] ) + { + double dist = + SMESH_TNodeXYZ(data._stepSizeNodes[0]).Distance(data._stepSizeNodes[1]); + data._stepSizeCoeff = data._stepSize / dist; + } + } +} + +//================================================================================ +/*! + * \brief Separate shapes (and _LayerEdge's on them) to smooth from the rest ones + */ +//================================================================================ + +bool _ViscousBuilder::sortEdges( _SolidData& data, + vector< vector<_LayerEdge*> >& edgesByGeom) +{ + // Find shapes needing smoothing; such a shape has _LayerEdge._normal on it's + // boundry inclined at a sharp angle to the shape + + list< TGeomID > shapesToSmooth; + + SMESH_MesherHelper helper( *_mesh ); + bool ok = true; + + for ( unsigned iS = 0; iS < edgesByGeom.size(); ++iS ) + { + vector<_LayerEdge*>& eS = edgesByGeom[iS]; + if ( eS.empty() ) continue; + TopoDS_Shape S = getMeshDS()->IndexToShape( iS ); + bool needSmooth = false; + switch ( S.ShapeType() ) + { + case TopAbs_EDGE: { + + bool isShrinkEdge = !eS[0]->_sWOL.IsNull(); + for ( TopoDS_Iterator vIt( S ); vIt.More() && !needSmooth; vIt.Next() ) + { + TGeomID iV = getMeshDS()->ShapeToIndex( vIt.Value() ); + vector<_LayerEdge*>& eV = edgesByGeom[ iV ]; + if ( eV.empty() ) continue; + double cosin = eV[0]->_cosin; + bool badCosin = + ( !eV[0]->_sWOL.IsNull() && ( eV[0]->_sWOL.ShapeType() == TopAbs_EDGE || !isShrinkEdge)); + if ( badCosin ) + { + gp_Vec dir1, dir2; + if ( eV[0]->_sWOL.ShapeType() == TopAbs_EDGE ) + dir1 = getEdgeDir( TopoDS::Edge( eV[0]->_sWOL ), TopoDS::Vertex( vIt.Value() )); + else + dir1 = getFaceDir( TopoDS::Face( eV[0]->_sWOL ), TopoDS::Vertex( vIt.Value() ), + eV[0]->_nodes[0], helper, ok); + dir2 = getEdgeDir( TopoDS::Edge( S ), TopoDS::Vertex( vIt.Value() )); + double angle = dir1.Angle( dir2 ); + cosin = cos( angle ); + } + needSmooth = ( cosin > 0.1 ); + } + break; + } + case TopAbs_FACE: { + + for ( TopExp_Explorer eExp( S, TopAbs_EDGE ); eExp.More() && !needSmooth; eExp.Next() ) + { + TGeomID iE = getMeshDS()->ShapeToIndex( eExp.Current() ); + vector<_LayerEdge*>& eE = edgesByGeom[ iE ]; + if ( eE.empty() ) continue; + if ( eE[0]->_sWOL.IsNull() ) + { + for ( unsigned i = 0; i < eE.size() && !needSmooth; ++i ) + needSmooth = ( eE[i]->_cosin > 0.1 ); + } + else + { + const TopoDS_Face& F1 = TopoDS::Face( S ); + const TopoDS_Face& F2 = TopoDS::Face( eE[0]->_sWOL ); + const TopoDS_Edge& E = TopoDS::Edge( eExp.Current() ); + for ( unsigned i = 0; i < eE.size() && !needSmooth; ++i ) + { + gp_Vec dir1 = getFaceDir( F1, E, eE[i]->_nodes[0], helper, ok ); + gp_Vec dir2 = getFaceDir( F2, E, eE[i]->_nodes[0], helper, ok ); + double angle = dir1.Angle( dir2 ); + double cosin = cos( angle ); + needSmooth = ( cosin > 0.1 ); + } + } + } + break; + } + case TopAbs_VERTEX: + continue; + default:; + } + if ( needSmooth ) + { + if ( S.ShapeType() == TopAbs_EDGE ) shapesToSmooth.push_front( iS ); + else shapesToSmooth.push_back ( iS ); + } + + } // loop on edgesByGeom + + data._edges.reserve( data._n2eMap.size() ); + data._endEdgeToSmooth.clear(); + + // first we put _LayerEdge's on shapes to smooth + list< TGeomID >::iterator gIt = shapesToSmooth.begin(); + for ( ; gIt != shapesToSmooth.end(); ++gIt ) + { + vector<_LayerEdge*>& eVec = edgesByGeom[ *gIt ]; + if ( eVec.empty() ) continue; + data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() ); + data._endEdgeToSmooth.push_back( data._edges.size() ); + eVec.clear(); + } + + // then the rest _LayerEdge's + for ( unsigned iS = 0; iS < edgesByGeom.size(); ++iS ) + { + vector<_LayerEdge*>& eVec = edgesByGeom[iS]; + data._edges.insert( data._edges.end(), eVec.begin(), eVec.end() ); + eVec.clear(); + } + + return ok; +} + +//================================================================================ +/*! + * \brief Set data of _LayerEdge needed for smoothing + * \param subIds - ids of sub-shapes of a SOLID to take into account faces from + */ +//================================================================================ + +bool _ViscousBuilder::setEdgeData(_LayerEdge& edge, + const set& subIds, + SMESH_MesherHelper& helper, + _SolidData& data) +{ + SMESH_MeshEditor editor(_mesh); + + const SMDS_MeshNode* node = edge._nodes[0]; // source node + SMDS_TypeOfPosition posType = node->GetPosition()->GetTypeOfPosition(); + + edge._len = 0; + edge._2neibors = 0; + edge._curvature = 0; + + // -------------------------- + // Compute _normal and _cosin + // -------------------------- + + edge._cosin = 0; + edge._normal.SetCoord(0,0,0); + + int totalNbFaces = 0; + gp_Pnt p; + gp_Vec du, dv, geomNorm; + bool normOK = true; + + TGeomID shapeInd = node->getshapeId(); + map< TGeomID, TopoDS_Shape >::const_iterator s2s = data._shrinkShape2Shape.find( shapeInd ); + bool onShrinkShape ( s2s != data._shrinkShape2Shape.end() ); + TopoDS_Shape vertEdge; + + if ( onShrinkShape ) // one of faces the node is on has no layers + { + vertEdge = getMeshDS()->IndexToShape( s2s->first ); // vertex or edge + if ( s2s->second.ShapeType() == TopAbs_EDGE ) + { + // inflate from VERTEX along EDGE + edge._normal = getEdgeDir( TopoDS::Edge( s2s->second ), TopoDS::Vertex( vertEdge )); + } + else if ( vertEdge.ShapeType() == TopAbs_VERTEX ) + { + // inflate from VERTEX along FACE + edge._normal = getFaceDir( TopoDS::Face( s2s->second ), TopoDS::Vertex( vertEdge ), + node, helper, normOK, &edge._cosin); + } + else + { + // inflate from EDGE along FACE + edge._normal = getFaceDir( TopoDS::Face( s2s->second ), TopoDS::Edge( vertEdge ), + node, helper, normOK); + } + } + else // layers are on all faces of SOLID the node is on + { + // find indices of geom faces the node lies on + set faceIds; + if ( posType == SMDS_TOP_FACE ) + { + faceIds.insert( node->getshapeId() ); + } + else + { + SMDS_ElemIteratorPtr fIt = node->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) + faceIds.insert( editor.FindShape(fIt->next())); + } + + set::iterator id = faceIds.begin(); + TopoDS_Face F; + for ( ; id != faceIds.end(); ++id ) + { + const TopoDS_Shape& s = getMeshDS()->IndexToShape( *id ); + if ( s.IsNull() || s.ShapeType() != TopAbs_FACE || !subIds.count( *id )) + continue; + totalNbFaces++; + //nbLayerFaces += subIds.count( *id ); + F = TopoDS::Face( s ); + + gp_XY uv = helper.GetNodeUV( F, node, 0, &normOK ); + Handle(Geom_Surface) surface = BRep_Tool::Surface( F ); + surface->D1( uv.X(),uv.Y(), p, du,dv ); + geomNorm = du ^ dv; + double size2 = geomNorm.SquareMagnitude(); + if ( size2 > numeric_limits::min() ) + geomNorm /= sqrt( size2 ); + else + normOK = false; + if ( helper.GetSubShapeOri( data._solid, F ) != TopAbs_REVERSED ) + geomNorm.Reverse(); + edge._normal += geomNorm.XYZ(); + } + if ( totalNbFaces == 0 ) + return error(SMESH_Comment("Can't get normal to node ") << node->GetID(), data._index); + + edge._normal /= totalNbFaces; + + switch ( posType ) + { + case SMDS_TOP_FACE: + edge._cosin = 0; break; + + case SMDS_TOP_EDGE: { + TopoDS_Edge E = TopoDS::Edge( helper.GetSubShapeByNode( node, getMeshDS())); + gp_Vec inFaceDir = getFaceDir( F, E, node, helper, normOK); + double angle = inFaceDir.Angle( edge._normal ); // [0,PI] + edge._cosin = cos( angle ); + //cout << "Cosin on EDGE " << edge._cosin << " node " << node->GetID() << endl; + break; + } + case SMDS_TOP_VERTEX: { + TopoDS_Vertex V = TopoDS::Vertex( helper.GetSubShapeByNode( node, getMeshDS())); + gp_Vec inFaceDir = getFaceDir( F, V, node, helper, normOK); + double angle = inFaceDir.Angle( edge._normal ); // [0,PI] + edge._cosin = cos( angle ); + //cout << "Cosin on VERTEX " << edge._cosin << " node " << node->GetID() << endl; + break; + } + default: + return error(SMESH_Comment("Invalid shape position of node ")<::min() ) + return error(SMESH_Comment("Bad normal at node ")<< node->GetID(), data._index ); + + edge._normal /= sqrt( normSize ); + + // TODO: if ( !normOK ) then get normal by mesh faces + + // Set the rest data + // -------------------- + if ( onShrinkShape ) + { + edge._sWOL = (*s2s).second; + + SMDS_MeshNode* tgtNode = const_cast( edge._nodes.back() ); + if ( SMESHDS_SubMesh* sm = getMeshDS()->MeshElements( data._solid )) + sm->RemoveNode( tgtNode , /*isNodeDeleted=*/false ); + + // set initial position which is parameters on _sWOL in this case + if ( edge._sWOL.ShapeType() == TopAbs_EDGE ) + { + double u = helper.GetNodeU( TopoDS::Edge( edge._sWOL ), node, 0, &normOK ); + edge._pos.push_back( gp_XYZ( u, 0, 0)); + getMeshDS()->SetNodeOnEdge( tgtNode, TopoDS::Edge( edge._sWOL ), u ); + } + else // TopAbs_FACE + { + gp_XY uv = helper.GetNodeUV( TopoDS::Face( edge._sWOL ), node, 0, &normOK ); + edge._pos.push_back( gp_XYZ( uv.X(), uv.Y(), 0)); + getMeshDS()->SetNodeOnFace( tgtNode, TopoDS::Face( edge._sWOL ), uv.X(), uv.Y() ); + } + } + else + { + edge._pos.push_back( SMESH_TNodeXYZ( node )); + + if ( posType == SMDS_TOP_FACE ) + { + getSimplices( node, edge._simplices, _ignoreShapeIds, &data ); + double avgNormProj = 0, avgLen = 0; + for ( unsigned i = 0; i < edge._simplices.size(); ++i ) + { + gp_XYZ vec = edge._pos.back() - SMESH_TNodeXYZ( edge._simplices[i]._nPrev ); + avgNormProj += edge._normal * vec; + avgLen += vec.Modulus(); + } + avgNormProj /= edge._simplices.size(); + avgLen /= edge._simplices.size(); + edge._curvature = _Curvature::New( avgNormProj, avgLen ); + } + } + + // Set neighbour nodes for a _LayerEdge based on EDGE + + if ( posType == SMDS_TOP_EDGE /*|| + ( onShrinkShape && posType == SMDS_TOP_VERTEX && fabs( edge._cosin ) < 1e-10 )*/) + { + edge._2neibors = new _2NearEdges; + // target node instead of source ones will be set later + if ( ! findNeiborsOnEdge( &edge, + edge._2neibors->_nodes[0], + edge._2neibors->_nodes[1], + data)) + return false; + edge.SetDataByNeighbors( edge._2neibors->_nodes[0], + edge._2neibors->_nodes[1], + helper); + } + + edge.SetCosin( edge._cosin ); // to update edge._lenFactor + + return true; +} + +//================================================================================ +/*! + * \brief Find 2 neigbor nodes of a node on EDGE + */ +//================================================================================ + +bool _ViscousBuilder::findNeiborsOnEdge(const _LayerEdge* edge, + const SMDS_MeshNode*& n1, + const SMDS_MeshNode*& n2, + _SolidData& data) +{ + const SMDS_MeshNode* node = edge->_nodes[0]; + const int shapeInd = node->getshapeId(); + SMESHDS_SubMesh* edgeSM = 0; + if ( node->GetPosition()->GetTypeOfPosition() == SMDS_TOP_EDGE ) + { + + edgeSM = getMeshDS()->MeshElements( shapeInd ); + if ( !edgeSM || edgeSM->NbElements() == 0 ) + return error(SMESH_Comment("Not meshed EDGE ") << shapeInd, data._index); + } + int iN = 0; + n2 = 0; + SMDS_ElemIteratorPtr eIt = node->GetInverseElementIterator(SMDSAbs_Edge); + while ( eIt->more() && !n2 ) + { + const SMDS_MeshElement* e = eIt->next(); + const SMDS_MeshNode* nNeibor = e->GetNode( 0 ); + if ( nNeibor == node ) nNeibor = e->GetNode( 1 ); + if ( edgeSM ) + { + if (!edgeSM->Contains(e)) continue; + } + else + { + TopoDS_Shape s = SMESH_MesherHelper::GetSubShapeByNode(nNeibor, getMeshDS() ); + if ( !SMESH_MesherHelper::IsSubShape( s, edge->_sWOL )) continue; + } + ( iN++ ? n2 : n1 ) = nNeibor; + } + if ( !n2 ) + return error(SMESH_Comment("Wrongly meshed EDGE ") << shapeInd, data._index); + return true; +} + +//================================================================================ +/*! + * \brief Set _curvature and _2neibors->_plnNorm by 2 neigbor nodes residing the same EDGE + */ +//================================================================================ + +void _LayerEdge::SetDataByNeighbors( const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + SMESH_MesherHelper& helper) +{ + if ( _nodes[0]->GetPosition()->GetTypeOfPosition() != SMDS_TOP_EDGE ) + return; + + gp_XYZ pos = SMESH_TNodeXYZ( _nodes[0] ); + gp_XYZ vec1 = pos - SMESH_TNodeXYZ( n1 ); + gp_XYZ vec2 = pos - SMESH_TNodeXYZ( n2 ); + + // Set _curvature + + double sumLen = vec1.Modulus() + vec2.Modulus(); + _2neibors->_wgt[0] = 1 - vec1.Modulus() / sumLen; + _2neibors->_wgt[1] = 1 - vec2.Modulus() / sumLen; + double avgNormProj = 0.5 * ( _normal * vec1 + _normal * vec2 ); + double avgLen = 0.5 * ( vec1.Modulus() + vec2.Modulus() ); + if ( _curvature ) delete _curvature; + _curvature = _Curvature::New( avgNormProj, avgLen ); +#ifdef __myDEBUG +// if ( _curvature ) +// cout << _nodes[0]->GetID() +// << " CURV r,k: " << _curvature->_r<<","<<_curvature->_k +// << " proj = "<AddGroup(SMDSAbs_Edge, name.c_str(), id ); +// SMESHDS_Group* gDS = (SMESHDS_Group*)g->GetGroupDS(); +// SMESHDS_Mesh* mDS = _mesh->GetMeshDS(); + + dumpFunction( SMESH_Comment("make_LayerEdge_") << i ); + for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j ) + { + _LayerEdge* le = _sdVec[i]._edges[j]; + for ( unsigned iN = 1; iN < le->_nodes.size(); ++iN ) + dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <_nodes[iN-1]->GetID() + << ", " << le->_nodes[iN]->GetID() <<"])"); + //gDS->SMDSGroup().Add( mDS->AddEdge( le->_nodes[iN-1], le->_nodes[iN])); + } + dumpFunctionEnd(); + + dumpFunction( SMESH_Comment("makeNormals") << i ); + for ( unsigned j = 0 ; j < _sdVec[i]._edges.size(); ++j ) + { + _LayerEdge& edge = *_sdVec[i]._edges[j]; + SMESH_TNodeXYZ nXYZ( edge._nodes[0] ); + nXYZ += edge._normal * _sdVec[i]._stepSize; + dumpCmd(SMESH_Comment("mesh.AddEdge([ ") <GetID() + << ", mesh.AddNode( " << nXYZ.X()<<","<< nXYZ.Y()<<","<< nXYZ.Z()<<")])"); + } + dumpFunctionEnd(); + +// name = SMESH_Comment("tmp_faces ") << i; +// g = _mesh->AddGroup(SMDSAbs_Face, name.c_str(), id ); +// gDS = (SMESHDS_Group*)g->GetGroupDS(); +// SMESH_MeshEditor editor( _mesh ); + dumpFunction( SMESH_Comment("makeTmpFaces_") << i ); + TopExp_Explorer fExp( _sdVec[i]._solid, TopAbs_FACE ); + for ( ; fExp.More(); fExp.Next() ) + { + if (const SMESHDS_SubMesh* sm = _sdVec[i]._proxyMesh->GetProxySubMesh( fExp.Current())) + { + SMDS_ElemIteratorPtr fIt = sm->GetElements(); + while ( fIt->more()) + { + const SMDS_MeshElement* e = fIt->next(); + SMESH_Comment cmd("mesh.AddFace(["); + for ( int j=0; j < e->NbCornerNodes(); ++j ) + cmd << e->GetNode(j)->GetID() << (j+1NbCornerNodes() ? ",": "])"); + dumpCmd( cmd ); + //vector nodes( e->begin_nodes(), e->end_nodes() ); + //gDS->SMDSGroup().Add( editor.AddElement( nodes, e->GetType(), e->IsPoly())); + } + } + } + dumpFunctionEnd(); + } +#endif +} + +//================================================================================ +/*! + * \brief Increase length of _LayerEdge's to reach the required thickness of layers + */ +//================================================================================ + +bool _ViscousBuilder::inflate(_SolidData& data) +{ + SMESH_MesherHelper helper( *_mesh ); + + // Limit inflation step size by geometry size found by itersecting + // normals of _LayerEdge's with mesh faces + double geomSize = Precision::Infinite(), intersecDist; + SMESH_MeshEditor editor( _mesh ); + auto_ptr searcher + ( editor.GetElementSearcher( data._proxyMesh->GetFaces( data._solid )) ); + for ( unsigned i = 0; i < data._edges.size(); ++i ) + { + if ( data._edges[i]->IsOnEdge() ) continue; + data._edges[i]->FindIntersection( *searcher, intersecDist, data._epsilon ); + if ( geomSize > intersecDist ) + geomSize = intersecDist; + } + if ( data._stepSize > 0.3 * geomSize ) + limitStepSize( data, 0.3 * geomSize ); + + const double tgtThick = data._hyp->GetTotalThickness(); + if ( data._stepSize > tgtThick ) + limitStepSize( data, tgtThick ); + + if ( data._stepSize < 1. ) + data._epsilon = data._stepSize * 1e-7; + +#ifdef __myDEBUG + cout << "-- geomSize = " << geomSize << ", stepSize = " << data._stepSize << endl; +#endif + + double avgThick = 0, curThick = 0, distToIntersection = Precision::Infinite(); + int nbSteps = 0, nbRepeats = 0; + while ( 1.01 * avgThick < tgtThick ) + { + // new target length + curThick += data._stepSize; + if ( curThick > tgtThick ) + { + curThick = tgtThick + ( tgtThick-avgThick ) * nbRepeats; + nbRepeats++; + } + + // Elongate _LayerEdge's + dumpFunction(SMESH_Comment("inflate")<SetNewLength( curThick, helper ); + } + dumpFunctionEnd(); + + if ( !nbSteps ) + if ( !updateNormals( data, helper ) ) + return false; + + // Improve and check quality + if ( !smoothAndCheck( data, nbSteps, distToIntersection )) + { + if ( nbSteps > 0 ) + { + dumpFunction(SMESH_Comment("invalidate")<InvalidateStep( nbSteps+1 ); + } + dumpFunctionEnd(); + } + break; // no more inflating possible + } + nbSteps++; + + // Evaluate achieved thickness + avgThick = 0; + for ( unsigned i = 0; i < data._edges.size(); ++i ) + avgThick += data._edges[i]->_len; + avgThick /= data._edges.size(); +#ifdef __myDEBUG + cout << "-- Thickness " << avgThick << " reached" << endl; +#endif + + if ( distToIntersection < avgThick*1.5 ) + { +#ifdef __myDEBUG + cout << "-- Stop inflation since distToIntersection( "<_sWOL.IsNull() && + data._edges[ iBeg ]->_sWOL.ShapeType() == TopAbs_FACE ) + { + if ( !F.IsSame( data._edges[ iBeg ]->_sWOL )) { + F = TopoDS::Face( data._edges[ iBeg ]->_sWOL ); + helper.SetSubShape( F ); + surface = BRep_Tool::Surface( F ); + } + } + else + { + F.Nullify(); surface.Nullify(); + } + TGeomID sInd = data._edges[ iBeg ]->_nodes[0]->getshapeId(); + + if ( data._edges[ iBeg ]->IsOnEdge() ) + { + dumpFunction(SMESH_Comment("smooth")<SmoothOnEdge(surface, F, helper); + } + dumpCmd( SMESH_Comment("# end step ")<Smooth(badNb); + improved = ( badNb < oldBadNb ); + + dumpFunctionEnd(); + } + if ( badNb > 0 ) + { +#ifdef __myDEBUG + for ( int i = iBeg; i < iEnd; ++i ) + { + _LayerEdge* edge = data._edges[i]; + SMESH_TNodeXYZ tgtXYZ( edge->_nodes.back() ); + for ( unsigned j = 0; j < edge->_simplices.size(); ++j ) + if ( !edge->_simplices[j].IsForward( edge->_nodes[0], &tgtXYZ )) + { + cout << "Bad simplex ( " << edge->_nodes[0]->GetID()<< " "<< tgtXYZ._node->GetID() + << " "<< edge->_simplices[j]._nPrev->GetID() + << " "<< edge->_simplices[j]._nNext->GetID() << " )" << endl; + return false; + } + } +#endif + return false; + } + } + } // loop on shapes to smooth + + // Check if the last segments of _LayerEdge intersects 2D elements; + // checked elements are either temporary faces or faces on surfaces w/o the layers + + SMESH_MeshEditor editor( _mesh ); + auto_ptr searcher + ( editor.GetElementSearcher( data._proxyMesh->GetFaces( data._solid )) ); + + distToIntersection = Precision::Infinite(); + double dist; + const SMDS_MeshElement* intFace = 0; +#ifdef __myDEBUG + const SMDS_MeshElement* closestFace = 0; + int iLE = 0; +#endif + for ( unsigned i = 0; i < data._edges.size(); ++i ) + { + if ( data._edges[i]->FindIntersection( *searcher, dist, data._epsilon, &intFace )) + return false; + if ( distToIntersection > dist ) + { + distToIntersection = dist; +#ifdef __myDEBUG + iLE = i; + closestFace = intFace; +#endif + } + } +#ifdef __myDEBUG + if ( closestFace ) + { + SMDS_MeshElement::iterator nIt = closestFace->begin_nodes(); + cout << "Shortest distance: _LayerEdge nodes: tgt " << data._edges[iLE]->_nodes.back()->GetID() + << " src " << data._edges[iLE]->_nodes[0]->GetID()<< ", intersection with face (" + << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID() + << ") distance = " << distToIntersection<< endl; + } +#endif + + return true; +} + +//================================================================================ +/*! + * \brief Return a curve of the EDGE to be used for smoothing and arrange + * _LayerEdge's to be in a consequent order + */ +//================================================================================ + +Handle(Geom_Curve) _SolidData::CurveForSmooth( const TopoDS_Edge& E, + const int iFrom, + const int iTo, + Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper) +{ + TGeomID eIndex = helper.GetMeshDS()->ShapeToIndex( E ); + + map< TGeomID, Handle(Geom_Curve)>::iterator i2curve = _edge2curve.find( eIndex ); + + if ( i2curve == _edge2curve.end() ) + { + // sort _LayerEdge's by position on the EDGE + { + map< double, _LayerEdge* > u2edge; + for ( int i = iFrom; i < iTo; ++i ) + u2edge.insert( make_pair( helper.GetNodeU( E, _edges[i]->_nodes[0] ), _edges[i] )); + + ASSERT( u2edge.size() == iTo - iFrom ); + map< double, _LayerEdge* >::iterator u2e = u2edge.begin(); + for ( int i = iFrom; i < iTo; ++i, ++u2e ) + _edges[i] = u2e->second; + + // set _2neibors according to the new order + for ( int i = iFrom; i < iTo-1; ++i ) + if ( _edges[i]->_2neibors->_nodes[1] != _edges[i+1]->_nodes.back() ) + _edges[i]->_2neibors->reverse(); + if ( u2edge.size() > 1 && + _edges[iTo-1]->_2neibors->_nodes[0] != _edges[iTo-2]->_nodes.back() ) + _edges[iTo-1]->_2neibors->reverse(); + } + + SMESHDS_SubMesh* smDS = helper.GetMeshDS()->MeshElements( eIndex ); + + TopLoc_Location loc; double f,l; + + Handle(Geom_Line) line; + Handle(Geom_Circle) circle; + bool isLine, isCirc; + if ( F.IsNull() ) // 3D case + { + // check if the EDGE is a line + Handle(Geom_Curve) curve = BRep_Tool::Curve( E, loc, f, l); + if ( curve->IsKind( STANDARD_TYPE( Geom_TrimmedCurve ))) + curve = Handle(Geom_TrimmedCurve)::DownCast( curve )->BasisCurve(); + + line = Handle(Geom_Line)::DownCast( curve ); + circle = Handle(Geom_Circle)::DownCast( curve ); + isLine = (!line.IsNull()); + isCirc = (!circle.IsNull()); + + if ( !isLine && !isCirc ) // Check if the EDGE is close to a line + { + Bnd_B3d bndBox; + SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); + while ( nIt->more() ) + bndBox.Add( SMESH_TNodeXYZ( nIt->next() )); + gp_XYZ size = bndBox.CornerMax() - bndBox.CornerMin(); + + SMESH_TNodeXYZ p0( _edges[iFrom]->_2neibors->_nodes[0] ); + SMESH_TNodeXYZ p1( _edges[iFrom]->_2neibors->_nodes[1] ); + const double lineTol = 1e-2 * ( p0 - p1 ).Modulus(); + for ( int i = 0; i < 3 && !isLine; ++i ) + isLine = ( size.Coord( i+1 ) <= lineTol ); + } + if ( !isLine && !isCirc && iTo-iFrom > 2) // Check if the EDGE is close to a circle + { + // TODO + } + } + else // 2D case + { + // check if the EDGE is a line + Handle(Geom2d_Curve) curve = BRep_Tool::CurveOnSurface( E, F, f, l); + if ( curve->IsKind( STANDARD_TYPE( Geom2d_TrimmedCurve ))) + curve = Handle(Geom2d_TrimmedCurve)::DownCast( curve )->BasisCurve(); + + Handle(Geom2d_Line) line2d = Handle(Geom2d_Line)::DownCast( curve ); + Handle(Geom2d_Circle) circle2d = Handle(Geom2d_Circle)::DownCast( curve ); + isLine = (!line2d.IsNull()); + isCirc = (!circle2d.IsNull()); + + if ( !isLine && !isCirc) // Check if the EDGE is close to a line + { + Bnd_B2d bndBox; + SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); + while ( nIt->more() ) + bndBox.Add( helper.GetNodeUV( F, nIt->next() )); + gp_XY size = bndBox.CornerMax() - bndBox.CornerMin(); + + const double lineTol = 1e-2 * sqrt( bndBox.SquareExtent() ); + for ( int i = 0; i < 2 && !isLine; ++i ) + isLine = ( size.Coord( i+1 ) <= lineTol ); + } + if ( !isLine && !isCirc && iTo-iFrom > 2) // Check if the EDGE is close to a circle + { + // TODO + } + if ( isLine ) + { + line = new Geom_Line( gp::OX() ); // only type does matter + } + else if ( isCirc ) + { + gp_Pnt2d p = circle2d->Location(); + gp_Ax2 ax( gp_Pnt( p.X(), p.Y(), 0), gp::DX()); + circle = new Geom_Circle( ax, 1.); // only center position does matter + } + } + + Handle(Geom_Curve)& res = _edge2curve[ eIndex ]; + if ( isLine ) + res = line; + else if ( isCirc ) + res = circle; + + return res; + } + return i2curve->second; +} + +//================================================================================ +/*! + * \brief smooth _LayerEdge's on a staight EDGE or circular EDGE + */ +//================================================================================ + +bool _ViscousBuilder::smoothAnalyticEdge( _SolidData& data, + const int iFrom, + const int iTo, + Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper) +{ + TopoDS_Shape S = helper.GetSubShapeByNode( data._edges[ iFrom ]->_nodes[0], + helper.GetMeshDS()); + TopoDS_Edge E = TopoDS::Edge( S ); + + Handle(Geom_Curve) curve = data.CurveForSmooth( E, iFrom, iTo, surface, F, helper ); + if ( curve.IsNull() ) return false; + + // compute a relative length of segments + vector< double > len( iTo-iFrom+1 ); + { + double curLen, prevLen = len[0] = 1.0; + for ( int i = iFrom; i < iTo; ++i ) + { + curLen = prevLen * data._edges[i]->_2neibors->_wgt[0] / data._edges[i]->_2neibors->_wgt[1]; + len[i-iFrom+1] = len[i-iFrom] + curLen; + prevLen = curLen; + } + } + + if ( curve->IsKind( STANDARD_TYPE( Geom_Line ))) + { + if ( F.IsNull() ) // 3D + { + SMESH_TNodeXYZ p0( data._edges[iFrom]->_2neibors->_nodes[0]); + SMESH_TNodeXYZ p1( data._edges[iTo-1]->_2neibors->_nodes[1]); + for ( int i = iFrom; i < iTo; ++i ) + { + double r = len[i-iFrom] / len.back(); + gp_XYZ newPos = p0 * ( 1. - r ) + p1 * r; + data._edges[i]->_pos.back() = newPos; + SMDS_MeshNode* tgtNode = const_cast( data._edges[i]->_nodes.back() ); + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + dumpMove( tgtNode ); + } + } + else + { + gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->_nodes[0]); + gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->_nodes[1]); + if ( data._edges[iFrom]->_2neibors->_nodes[0] == + data._edges[iTo-1]->_2neibors->_nodes[1] ) // closed edge + { + int iPeriodic = helper.GetPeriodicIndex(); + if ( iPeriodic == 1 || iPeriodic == 2 ) + { + uv1.SetCoord( iPeriodic, helper.GetOtherParam( uv1.Coord( iPeriodic ))); + if ( uv0.Coord( iPeriodic ) > uv1.Coord( iPeriodic )) + std::swap( uv0, uv1 ); + } + } + const gp_XY rangeUV = uv1 - uv0; + for ( int i = iFrom; i < iTo; ++i ) + { + double r = len[i-iFrom] / len.back(); + gp_XY newUV = uv0 + r * rangeUV; + data._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 ); + + gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() ); + SMDS_MeshNode* tgtNode = const_cast( data._edges[i]->_nodes.back() ); + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + dumpMove( tgtNode ); + + SMDS_FacePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( newUV.X() ); + pos->SetVParameter( newUV.Y() ); + } + } + return true; + } + + if ( curve->IsKind( STANDARD_TYPE( Geom_Circle ))) + { + Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast( curve ); + gp_Pnt center3D = circle->Location(); + + if ( F.IsNull() ) // 3D + { + return false; // TODO ??? + } + else // 2D + { + const gp_XY center( center3D.X(), center3D.Y() ); + + gp_XY uv0 = helper.GetNodeUV( F, data._edges[iFrom]->_2neibors->_nodes[0]); + gp_XY uvM = helper.GetNodeUV( F, data._edges[iFrom]->_nodes.back()); + gp_XY uv1 = helper.GetNodeUV( F, data._edges[iTo-1]->_2neibors->_nodes[1]); + gp_Vec2d vec0( center, uv0 ); + gp_Vec2d vecM( center, uvM); + gp_Vec2d vec1( center, uv1 ); + double uLast = vec0.Angle( vec1 ); // -PI - +PI + double uMidl = vec0.Angle( vecM ); + if ( uLast < 0 ) uLast += 2.*M_PI; // 0.0 - 2*PI + if ( uMidl < 0 ) uMidl += 2.*M_PI; + const bool sense = ( uMidl < uLast ); + const double radius = 0.5 * ( vec0.Magnitude() + vec1.Magnitude() ); + + gp_Ax2d axis( center, vec0 ); + gp_Circ2d circ ( axis, radius, sense ); + for ( int i = iFrom; i < iTo; ++i ) + { + double newU = uLast * len[i-iFrom] / len.back(); + gp_Pnt2d newUV = ElCLib::Value( newU, circ ); + data._edges[i]->_pos.back().SetCoord( newUV.X(), newUV.Y(), 0 ); + + gp_Pnt newPos = surface->Value( newUV.X(), newUV.Y() ); + SMDS_MeshNode* tgtNode = const_cast( data._edges[i]->_nodes.back() ); + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + dumpMove( tgtNode ); + + SMDS_FacePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( newUV.X() ); + pos->SetVParameter( newUV.Y() ); + } + } + return true; + } + + return false; +} + +//================================================================================ +/*! + * \brief Modify normals of _LayerEdge's on EDGE's to avoid intersection with + * _LayerEdge's on neighbor EDGE's + */ +//================================================================================ + +bool _ViscousBuilder::updateNormals( _SolidData& data, + SMESH_MesherHelper& helper ) +{ + // make temporary quadrangles got by extrusion of + // mesh edges along _LayerEdge._normal's + + vector< const SMDS_MeshElement* > tmpFaces; + { + set< SMESH_TLink > extrudedLinks; // contains target nodes + vector< const SMDS_MeshNode*> nodes(4); // of a tmp mesh face + + dumpFunction(SMESH_Comment("makeTmpFacesOnEdges")<IsOnEdge() || !edge->_sWOL.IsNull() ) continue; + const SMDS_MeshNode* tgt1 = edge->_nodes.back(); + for ( int j = 0; j < 2; ++j ) // loop on _2NearEdges + { + const SMDS_MeshNode* tgt2 = edge->_2neibors->_nodes[j]; + pair< set< SMESH_TLink >::iterator, bool > link_isnew = + extrudedLinks.insert( SMESH_TLink( tgt1, tgt2 )); + if ( !link_isnew.second ) + { + extrudedLinks.erase( link_isnew.first ); + continue; // already extruded and will no more encounter + } + // look for a _LayerEdge containg tgt2 +// _LayerEdge* neiborEdge = 0; +// unsigned di = 0; // check _edges[i+di] and _edges[i-di] +// while ( !neiborEdge && ++di <= data._edges.size() ) +// { +// if ( i+di < data._edges.size() && data._edges[i+di]->_nodes.back() == tgt2 ) +// neiborEdge = data._edges[i+di]; +// else if ( di <= i && data._edges[i-di]->_nodes.back() == tgt2 ) +// neiborEdge = data._edges[i-di]; +// } +// if ( !neiborEdge ) +// return error("updateNormals(): neighbor _LayerEdge not found", data._index); + _LayerEdge* neiborEdge = edge->_2neibors->_edges[j]; + + TmpMeshFaceOnEdge* f = new TmpMeshFaceOnEdge( edge, neiborEdge, --_tmpFaceID ); + tmpFaces.push_back( f ); + + dumpCmd(SMESH_Comment("mesh.AddFace([ ") + <_nn[0]->GetID()<<", "<_nn[1]->GetID()<<", " + <_nn[2]->GetID()<<", "<_nn[3]->GetID()<<" ])"); + } + } + dumpFunctionEnd(); + } + // Check if _LayerEdge's based on EDGE's intersects tmpFaces. + // Perform two loops on _LayerEdge on EDGE's: + // 1) to find and fix intersection + // 2) to check that no new intersection appears as result of 1) + + SMESH_MeshEditor editor( _mesh ); + SMDS_ElemIteratorPtr fIt( new SMDS_ElementVectorIterator( tmpFaces.begin(), + tmpFaces.end())); + auto_ptr searcher ( editor.GetElementSearcher( fIt )); + + // 1) Find intersections + double dist; + const SMDS_MeshElement* face; + typedef map< _LayerEdge*, set< _LayerEdge*, _LayerEdgeCmp >, _LayerEdgeCmp > TLEdge2LEdgeSet; + TLEdge2LEdgeSet edge2CloseEdge; + + const double eps = data._epsilon * data._epsilon; + for ( unsigned i = 0; i < data._edges.size(); ++i ) + { + _LayerEdge* edge = data._edges[i]; + if ( !edge->IsOnEdge() || !edge->_sWOL.IsNull() ) continue; + if ( edge->FindIntersection( *searcher, dist, eps, &face )) + { + const TmpMeshFaceOnEdge* f = (const TmpMeshFaceOnEdge*) face; + set< _LayerEdge*, _LayerEdgeCmp > & ee = edge2CloseEdge[ edge ]; + ee.insert( f->_le1 ); + ee.insert( f->_le2 ); + if ( f->_le1->IsOnEdge() && f->_le1->_sWOL.IsNull() ) + edge2CloseEdge[ f->_le1 ].insert( edge ); + if ( f->_le2->IsOnEdge() && f->_le2->_sWOL.IsNull() ) + edge2CloseEdge[ f->_le2 ].insert( edge ); + } + } + + // Set _LayerEdge._normal + + if ( !edge2CloseEdge.empty() ) + { + dumpFunction(SMESH_Comment("updateNormals")<first; + _LayerEdge* edge2 = 0; + set< _LayerEdge*, _LayerEdgeCmp >& ee = e2ee->second; + + // find EDGEs the edges reside + TopoDS_Edge E1, E2; + TopoDS_Shape S = helper.GetSubShapeByNode( edge1->_nodes[0], getMeshDS() ); + if ( S.ShapeType() != TopAbs_EDGE ) + continue; // TODO: find EDGE by VERTEX + E1 = TopoDS::Edge( S ); + set< _LayerEdge*, _LayerEdgeCmp >::iterator eIt = ee.begin(); + while ( E2.IsNull() && eIt != ee.end()) + { + _LayerEdge* e2 = *eIt++; + TopoDS_Shape S = helper.GetSubShapeByNode( e2->_nodes[0], getMeshDS() ); + if ( S.ShapeType() == TopAbs_EDGE ) + E2 = TopoDS::Edge( S ), edge2 = e2; + } + if ( E2.IsNull() ) continue; // TODO: find EDGE by VERTEX + + // find 3 FACEs sharing 2 EDGEs + + TopoDS_Face FF1[2], FF2[2]; + PShapeIteratorPtr fIt = helper.GetAncestors(E1, *_mesh, TopAbs_FACE); + while ( fIt->more() && FF1[1].IsNull()) + { + const TopoDS_Face *F = (const TopoDS_Face*) fIt->next(); + if ( helper.IsSubShape( *F, data._solid)) + FF1[ FF1[0].IsNull() ? 0 : 1 ] = *F; + } + fIt = helper.GetAncestors(E2, *_mesh, TopAbs_FACE); + while ( fIt->more() && FF2[1].IsNull()) + { + const TopoDS_Face *F = (const TopoDS_Face*) fIt->next(); + if ( helper.IsSubShape( *F, data._solid)) + FF2[ FF2[0].IsNull() ? 0 : 1 ] = *F; + } + // exclude a FACE common to E1 and E2 (put it at [1] in FF* ) + if ( FF1[0].IsSame( FF2[0]) || FF1[0].IsSame( FF2[1])) + std::swap( FF1[0], FF1[1] ); + if ( FF2[0].IsSame( FF1[0]) ) + std::swap( FF2[0], FF2[1] ); + if ( FF1[0].IsNull() || FF2[0].IsNull() ) + continue; + +// // get a new normal for edge1 + bool ok; + gp_Vec dir1 = edge1->_normal, dir2 = edge2->_normal; + if ( edge1->_cosin < 0 ) + dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ).Normalized(); + if ( edge2->_cosin < 0 ) + dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok ).Normalized(); + // gp_Vec dir1 = getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ); +// gp_Vec dir2 = getFaceDir( FF2[0], E2, edge2->_nodes[0], helper, ok2 ); +// double wgt1 = ( edge1->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 ); +// double wgt2 = ( edge2->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 ); +// gp_Vec newNorm = wgt1 * dir1 + wgt2 * dir2; +// newNorm.Normalize(); + + double wgt1 = ( edge1->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 ); + double wgt2 = ( edge2->_cosin + 1 ) / ( edge1->_cosin + edge2->_cosin + 2 ); + gp_Vec newNorm = wgt1 * dir1 + wgt2 * dir2; + newNorm.Normalize(); + + edge1->_normal = newNorm.XYZ(); + + // update data of edge1 depending on _normal + const SMDS_MeshNode *n1, *n2; + n1 = edge1->_2neibors->_edges[0]->_nodes[0]; + n2 = edge1->_2neibors->_edges[1]->_nodes[0]; + //if ( !findNeiborsOnEdge( edge1, n1, n2, data )) + //continue; + edge1->SetDataByNeighbors( n1, n2, helper ); + gp_Vec dirInFace; + if ( edge1->_cosin < 0 ) + dirInFace = dir1; + else + getFaceDir( FF1[0], E1, edge1->_nodes[0], helper, ok ); + double angle = dir1.Angle( edge1->_normal ); // [0,PI] + edge1->SetCosin( cos( angle )); + + // limit data._stepSize + if ( edge1->_cosin > 0.1 ) + { + SMDS_ElemIteratorPtr fIt = edge1->_nodes[0]->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) + limitStepSize( data, fIt->next(), edge1->_cosin ); + } + // set new XYZ of target node + edge1->InvalidateStep( 1 ); + edge1->_len = 0; + edge1->SetNewLength( data._stepSize, helper ); + } + + // Update normals and other dependent data of not intersecting _LayerEdge's + // neighboring the intersecting ones + + for ( e2ee = edge2CloseEdge.begin(); e2ee != edge2CloseEdge.end(); ++e2ee ) + { + _LayerEdge* edge1 = e2ee->first; + if ( !edge1->_2neibors ) + continue; + for ( int j = 0; j < 2; ++j ) // loop on 2 neighbors + { + _LayerEdge* neighbor = edge1->_2neibors->_edges[j]; + if ( edge2CloseEdge.count ( neighbor )) + continue; // j-th neighbor is also intersected + _LayerEdge* prevEdge = edge1; + const int nbSteps = 6; + for ( int step = nbSteps; step; --step ) // step from edge1 in j-th direction + { + if ( !neighbor->_2neibors ) + break; // neighbor is on VERTEX + int iNext = 0; + _LayerEdge* nextEdge = neighbor->_2neibors->_edges[iNext]; + if ( nextEdge == prevEdge ) + nextEdge = neighbor->_2neibors->_edges[ ++iNext ]; +// const double& wgtPrev = neighbor->_2neibors->_wgt[1-iNext]; +// const double& wgtNext = neighbor->_2neibors->_wgt[iNext]; + double r = double(step-1)/nbSteps; + if ( !nextEdge->_2neibors ) + r = 0.5; + + gp_XYZ newNorm = prevEdge->_normal * r + nextEdge->_normal * (1-r); + newNorm.Normalize(); + + neighbor->_normal = newNorm; + neighbor->SetCosin( prevEdge->_cosin * r + nextEdge->_cosin * (1-r) ); + neighbor->SetDataByNeighbors( prevEdge->_nodes[0], nextEdge->_nodes[0], helper ); + + neighbor->InvalidateStep( 1 ); + neighbor->_len = 0; + neighbor->SetNewLength( data._stepSize, helper ); + + // goto the next neighbor + prevEdge = neighbor; + neighbor = nextEdge; + } + } + } + dumpFunctionEnd(); + } + // 2) Check absence of intersections + // TODO? + + for ( unsigned i = 0 ; i < tmpFaces.size(); ++i ) + delete tmpFaces[i]; + + return true; +} + +//================================================================================ +/*! + * \brief Looks for intersection of it's last segment with faces + * \param distance - returns shortest distance from the last node to intersection + */ +//================================================================================ + +bool _LayerEdge::FindIntersection( SMESH_ElementSearcher& searcher, + double & distance, + const double& epsilon, + const SMDS_MeshElement** face) +{ + vector< const SMDS_MeshElement* > suspectFaces; + double segLen; + gp_Ax1 lastSegment = LastSegment(segLen); + searcher.GetElementsNearLine( lastSegment, SMDSAbs_Face, suspectFaces ); + + bool segmentIntersected = false; + distance = Precision::Infinite(); + int iFace = -1; // intersected face + for ( unsigned j = 0 ; j < suspectFaces.size() && !segmentIntersected; ++j ) + { + const SMDS_MeshElement* face = suspectFaces[j]; + if ( face->GetNodeIndex( _nodes.back() ) >= 0 || + face->GetNodeIndex( _nodes[0] ) >= 0 ) + continue; // face sharing _LayerEdge node + const int nbNodes = face->NbCornerNodes(); + bool intFound = false; + double dist; + SMDS_MeshElement::iterator nIt = face->begin_nodes(); + if ( nbNodes == 3 ) + { + intFound = SegTriaInter( lastSegment, *nIt++, *nIt++, *nIt++, dist, epsilon ); + } + else + { + const SMDS_MeshNode* tria[3]; + tria[0] = *nIt++; + tria[1] = *nIt++;; + for ( int n2 = 2; n2 < nbNodes && !intFound; ++n2 ) + { + tria[2] = *nIt++; + intFound = SegTriaInter(lastSegment, tria[0], tria[1], tria[2], dist, epsilon ); + tria[1] = tria[2]; + } + } + if ( intFound ) + { + if ( dist < segLen*(1.01)) + segmentIntersected = true; + if ( distance > dist ) + distance = dist, iFace = j; + } + } + if ( iFace != -1 && face ) *face = suspectFaces[iFace]; +// if ( distance && iFace > -1 ) +// { +// // distance is used to limit size of inflation step which depends on +// // whether the intersected face bears viscous layers or not +// bool faceHasVL = suspectFaces[iFace]->GetID() < 1; +// if ( faceHasVL ) +// *distance /= 2; +// } + if ( segmentIntersected ) + { +#ifdef __myDEBUG + SMDS_MeshElement::iterator nIt = suspectFaces[iFace]->begin_nodes(); + gp_XYZ intP( lastSegment.Location().XYZ() + lastSegment.Direction().XYZ() * distance ); + cout << "nodes: tgt " << _nodes.back()->GetID() << " src " << _nodes[0]->GetID() + << ", intersection with face (" + << (*nIt++)->GetID()<<" "<< (*nIt++)->GetID()<<" "<< (*nIt++)->GetID() + << ") at point (" << intP.X() << ", " << intP.Y() << ", " << intP.Z() + << ") distance = " << distance - segLen<< endl; +#endif + } + + distance -= segLen; + + return segmentIntersected; +} + +//================================================================================ +/*! + * \brief Returns size and direction of the last segment + */ +//================================================================================ + +gp_Ax1 _LayerEdge::LastSegment(double& segLen) const +{ + // find two non-coincident positions + gp_XYZ orig = _pos.back(); + gp_XYZ dir; + int iPrev = _pos.size() - 2; + while ( iPrev >= 0 ) + { + dir = orig - _pos[iPrev]; + if ( dir.SquareModulus() > 1e-100 ) + break; + else + iPrev--; + } + + // make gp_Ax1 + gp_Ax1 segDir; + if ( iPrev < 0 ) + { + segDir.SetLocation( SMESH_TNodeXYZ( _nodes[0] )); + segDir.SetDirection( _normal ); + segLen = 0; + } + else + { + gp_Pnt pPrev = _pos[ iPrev ]; + if ( !_sWOL.IsNull() ) + { + TopLoc_Location loc; + if ( _sWOL.ShapeType() == TopAbs_EDGE ) + { + double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l); + pPrev = curve->Value( pPrev.X() ).Transformed( loc ); + } + else + { + Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc ); + pPrev = surface->Value( pPrev.X(), pPrev.Y() ).Transformed( loc ); + } + dir = SMESH_TNodeXYZ( _nodes.back() ) - pPrev.XYZ(); + } + segDir.SetLocation( pPrev ); + segDir.SetDirection( dir ); + segLen = dir.Modulus(); + } + + return segDir; +} + +//================================================================================ +/*! + * \brief Test intersection of the last segment with a given triangle + * using Moller-Trumbore algorithm + * Intersection is detected if distance to intersection is less than _LayerEdge._len + */ +//================================================================================ + +bool _LayerEdge::SegTriaInter( const gp_Ax1& lastSegment, + const SMDS_MeshNode* n0, + const SMDS_MeshNode* n1, + const SMDS_MeshNode* n2, + double& t, + const double& EPSILON) const +{ + //const double EPSILON = 1e-6; + + gp_XYZ orig = lastSegment.Location().XYZ(); + gp_XYZ dir = lastSegment.Direction().XYZ(); + + SMESH_TNodeXYZ vert0( n0 ); + SMESH_TNodeXYZ vert1( n1 ); + SMESH_TNodeXYZ vert2( n2 ); + + /* calculate distance from vert0 to ray origin */ + gp_XYZ tvec = orig - vert0; + + if ( tvec * dir > EPSILON ) + // intersected face is at back side of the temporary face this _LayerEdge belongs to + return false; + + gp_XYZ edge1 = vert1 - vert0; + gp_XYZ edge2 = vert2 - vert0; + + /* begin calculating determinant - also used to calculate U parameter */ + gp_XYZ pvec = dir ^ edge2; + + /* if determinant is near zero, ray lies in plane of triangle */ + double det = edge1 * pvec; + + if (det > -EPSILON && det < EPSILON) + return 0; + double inv_det = 1.0 / det; + + /* calculate U parameter and test bounds */ + double u = ( tvec * pvec ) * inv_det; + if (u < 0.0 || u > 1.0) + return 0; + + /* prepare to test V parameter */ + gp_XYZ qvec = tvec ^ edge1; + + /* calculate V parameter and test bounds */ + double v = (dir * qvec) * inv_det; + if ( v < 0.0 || u + v > 1.0 ) + return 0; + + /* calculate t, ray intersects triangle */ + t = (edge2 * qvec) * inv_det; + + // if (det < EPSILON) + // return false; + + // /* calculate distance from vert0 to ray origin */ + // gp_XYZ tvec = orig - vert0; + + // /* calculate U parameter and test bounds */ + // double u = tvec * pvec; + // if (u < 0.0 || u > det) +// return 0; + +// /* prepare to test V parameter */ +// gp_XYZ qvec = tvec ^ edge1; + +// /* calculate V parameter and test bounds */ +// double v = dir * qvec; +// if (v < 0.0 || u + v > det) +// return 0; + +// /* calculate t, scale parameters, ray intersects triangle */ +// double t = edge2 * qvec; +// double inv_det = 1.0 / det; +// t *= inv_det; +// //u *= inv_det; +// //v *= inv_det; + + return true; +} + +//================================================================================ +/*! + * \brief Perform smooth of _LayerEdge's based on EDGE's + * \retval bool - true if node has been moved + */ +//================================================================================ + +bool _LayerEdge::SmoothOnEdge(Handle(Geom_Surface)& surface, + const TopoDS_Face& F, + SMESH_MesherHelper& helper) +{ + ASSERT( IsOnEdge() ); + + SMDS_MeshNode* tgtNode = const_cast( _nodes.back() ); + SMESH_TNodeXYZ oldPos( tgtNode ); + double dist01, distNewOld; + + SMESH_TNodeXYZ p0( _2neibors->_nodes[0]); + SMESH_TNodeXYZ p1( _2neibors->_nodes[1]); + dist01 = p0.Distance( _2neibors->_nodes[1] ); + + gp_Pnt newPos = p0 * _2neibors->_wgt[0] + p1 * _2neibors->_wgt[1]; + double lenDelta = 0; + if ( _curvature ) + { + lenDelta = _curvature->lenDelta( _len ); + newPos.ChangeCoord() += _normal * lenDelta; + } + + distNewOld = newPos.Distance( oldPos ); + + if ( F.IsNull() ) + { + if ( _2neibors->_plnNorm ) + { + // put newPos on the plane defined by source node and _plnNorm + gp_XYZ new2src = SMESH_TNodeXYZ( _nodes[0] ) - newPos.XYZ(); + double new2srcProj = (*_2neibors->_plnNorm) * new2src; + newPos.ChangeCoord() += (*_2neibors->_plnNorm) * new2srcProj; + } + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + _pos.back() = newPos.XYZ(); + } + else + { + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + gp_XY uv( Precision::Infinite(), 0 ); + helper.CheckNodeUV( F, tgtNode, uv, 1e-10, /*force=*/true ); + _pos.back().SetCoord( uv.X(), uv.Y(), 0 ); + + newPos = surface->Value( uv.X(), uv.Y() ); + tgtNode->setXYZ( newPos.X(), newPos.Y(), newPos.Z() ); + } + + if ( _curvature && lenDelta < 0 ) + { + gp_Pnt prevPos( _pos[ _pos.size()-2 ]); + _len -= prevPos.Distance( oldPos ); + _len += prevPos.Distance( newPos ); + } + bool moved = distNewOld > dist01/50; + //if ( moved ) + dumpMove( tgtNode ); // debug + + return moved; +} + +//================================================================================ +/*! + * \brief Perform laplacian smooth in 3D of nodes inflated from FACE + * \retval bool - true if _tgtNode has been moved + */ +//================================================================================ + +bool _LayerEdge::Smooth(int& badNb) +{ + if ( _simplices.size() < 2 ) + return false; // _LayerEdge inflated along EDGE or FACE + + // compute new position for the last _pos + gp_XYZ newPos (0,0,0); + for ( unsigned i = 0; i < _simplices.size(); ++i ) + newPos += SMESH_TNodeXYZ( _simplices[i]._nPrev ); + newPos /= _simplices.size(); + + if ( _curvature ) + newPos += _normal * _curvature->lenDelta( _len ); + + gp_Pnt prevPos( _pos[ _pos.size()-2 ]); +// if ( _cosin < -0.1) +// { +// // Avoid decreasing length of edge on concave surface +// //gp_Vec oldMove( _pos[ _pos.size()-2 ], _pos.back() ); +// gp_Vec newMove( prevPos, newPos ); +// newPos = _pos.back() + newMove.XYZ(); +// } +// else if ( _cosin > 0.3 ) +// { +// // Avoid increasing length of edge too much + +// } + // count quality metrics (orientation) of tetras around _tgtNode + int nbOkBefore = 0; + SMESH_TNodeXYZ tgtXYZ( _nodes.back() ); + for ( unsigned i = 0; i < _simplices.size(); ++i ) + nbOkBefore += _simplices[i].IsForward( _nodes[0], &tgtXYZ ); + + int nbOkAfter = 0; + for ( unsigned i = 0; i < _simplices.size(); ++i ) + nbOkAfter += _simplices[i].IsForward( _nodes[0], &newPos ); + + if ( nbOkAfter < nbOkBefore ) + return false; + + SMDS_MeshNode* n = const_cast< SMDS_MeshNode* >( _nodes.back() ); + + _len -= prevPos.Distance(SMESH_TNodeXYZ( n )); + _len += prevPos.Distance(newPos); + + n->setXYZ( newPos.X(), newPos.Y(), newPos.Z()); + _pos.back() = newPos; + + badNb += _simplices.size() - nbOkAfter; + + dumpMove( n ); + + return true; +} + +//================================================================================ +/*! + * \brief Add a new segment to _LayerEdge during inflation + */ +//================================================================================ + +void _LayerEdge::SetNewLength( double len, SMESH_MesherHelper& helper ) +{ + if ( _len - len > -1e-6 ) + { + _pos.push_back( _pos.back() ); + return; + } + + SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() ); + SMESH_TNodeXYZ oldXYZ( n ); + gp_XYZ nXYZ = oldXYZ + _normal * ( len - _len ) * _lenFactor; + n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() ); + + _pos.push_back( nXYZ ); + _len = len; + if ( !_sWOL.IsNull() ) + { + double distXYZ[4]; + if ( _sWOL.ShapeType() == TopAbs_EDGE ) + { + double u = Precision::Infinite(); // to force projection w/o distance check + helper.CheckNodeU( TopoDS::Edge( _sWOL ), n, u, 1e-10, /*force=*/true, distXYZ ); + _pos.back().SetCoord( u, 0, 0 ); + SMDS_EdgePosition* pos = static_cast( n->GetPosition() ); + pos->SetUParameter( u ); + } + else // TopAbs_FACE + { + gp_XY uv( Precision::Infinite(), 0 ); + helper.CheckNodeUV( TopoDS::Face( _sWOL ), n, uv, 1e-10, /*force=*/true, distXYZ ); + _pos.back().SetCoord( uv.X(), uv.Y(), 0 ); + SMDS_FacePosition* pos = static_cast( n->GetPosition() ); + pos->SetUParameter( uv.X() ); + pos->SetVParameter( uv.Y() ); + } + n->setXYZ( distXYZ[1], distXYZ[2], distXYZ[3]); + } + dumpMove( n ); //debug +} + +//================================================================================ +/*! + * \brief Remove last inflation step + */ +//================================================================================ + +void _LayerEdge::InvalidateStep( int curStep ) +{ + if ( _pos.size() > curStep ) + { + _pos.resize( curStep ); + gp_Pnt nXYZ = _pos.back(); + SMDS_MeshNode* n = const_cast< SMDS_MeshNode*>( _nodes.back() ); + if ( !_sWOL.IsNull() ) + { + TopLoc_Location loc; + if ( _sWOL.ShapeType() == TopAbs_EDGE ) + { + SMDS_EdgePosition* pos = static_cast( n->GetPosition() ); + pos->SetUParameter( nXYZ.X() ); + double f,l; + Handle(Geom_Curve) curve = BRep_Tool::Curve( TopoDS::Edge( _sWOL ), loc, f,l); + nXYZ = curve->Value( nXYZ.X() ).Transformed( loc ); + } + else + { + SMDS_FacePosition* pos = static_cast( n->GetPosition() ); + pos->SetUParameter( nXYZ.X() ); + pos->SetVParameter( nXYZ.Y() ); + Handle(Geom_Surface) surface = BRep_Tool::Surface( TopoDS::Face(_sWOL), loc ); + nXYZ = surface->Value( nXYZ.X(), nXYZ.Y() ).Transformed( loc ); + } + } + n->setXYZ( nXYZ.X(), nXYZ.Y(), nXYZ.Z() ); + dumpMove( n ); + } +} + +//================================================================================ +/*! + * \brief Create layers of prisms + */ +//================================================================================ + +bool _ViscousBuilder::refine(_SolidData& data) +{ + SMESH_MesherHelper helper( *_mesh ); + helper.SetSubShape( data._solid ); + helper.SetElementsOnShape(false); + + Handle(Geom_Curve) curve; + Handle(Geom_Surface) surface; + TopoDS_Edge geomEdge; + TopoDS_Face geomFace; + TopLoc_Location loc; + double f,l, u/*, distXYZ[4]*/; + gp_XY uv; + bool isOnEdge; + + for ( unsigned i = 0; i < data._edges.size(); ++i ) + { + _LayerEdge& edge = *data._edges[i]; + + // get accumulated length of segments + vector< double > segLen( edge._pos.size() ); + segLen[0] = 0.0; + for ( unsigned j = 1; j < edge._pos.size(); ++j ) + segLen[j] = segLen[j-1] + (edge._pos[j-1] - edge._pos[j] ).Modulus(); + + // allocate memory for new nodes if it is not yet refined + const SMDS_MeshNode* tgtNode = edge._nodes.back(); + if ( edge._nodes.size() == 2 ) + { + edge._nodes.resize( data._hyp->GetNumberLayers() + 1, 0 ); + edge._nodes[1] = 0; + edge._nodes.back() = tgtNode; + } + if ( !edge._sWOL.IsNull() ) + { + isOnEdge = ( edge._sWOL.ShapeType() == TopAbs_EDGE ); + // restore position of the last node +// gp_Pnt p; + if ( isOnEdge ) + { + geomEdge = TopoDS::Edge( edge._sWOL ); + curve = BRep_Tool::Curve( geomEdge, loc, f,l); +// double u = helper.GetNodeU( tgtNode ); +// p = curve->Value( u ); + } + else + { + geomFace = TopoDS::Face( edge._sWOL ); + surface = BRep_Tool::Surface( geomFace, loc ); +// gp_XY uv = helper.GetNodeUV( tgtNode ); +// p = surface->Value( uv.X(), uv.Y() ); + } +// p.Transform( loc ); +// const_cast< SMDS_MeshNode* >( tgtNode )->setXYZ( p.X(), p.Y(), p.Z() ); + } + // calculate height of the first layer + double h0; + const double T = segLen.back(); //data._hyp.GetTotalThickness(); + const double f = data._hyp->GetStretchFactor(); + const int N = data._hyp->GetNumberLayers(); + const double fPowN = pow( f, N ); + if ( fPowN - 1 <= numeric_limits::min() ) + h0 = T / N; + else + h0 = T * ( f - 1 )/( fPowN - 1 ); + + const double zeroLen = std::numeric_limits::min(); + + // create intermediate nodes + double hSum = 0, hi = h0/f; + unsigned iSeg = 1; + for ( unsigned iStep = 1; iStep < edge._nodes.size(); ++iStep ) + { + // compute an intermediate position + hi *= f; + hSum += hi; + while ( hSum > segLen[iSeg] && iSeg < segLen.size()-1) + ++iSeg; + int iPrevSeg = iSeg-1; + while ( fabs( segLen[iPrevSeg] - segLen[iSeg]) <= zeroLen && iPrevSeg > 0 ) + --iPrevSeg; + double r = ( segLen[iSeg] - hSum ) / ( segLen[iSeg] - segLen[iPrevSeg] ); + gp_Pnt pos = r * edge._pos[iPrevSeg] + (1-r) * edge._pos[iSeg]; + + SMDS_MeshNode*& node = const_cast< SMDS_MeshNode*& >(edge._nodes[ iStep ]); + if ( !edge._sWOL.IsNull() ) + { + // compute XYZ by parameters + if ( isOnEdge ) + { + u = pos.X(); + pos = curve->Value( u ).Transformed(loc); + } + else + { + uv.SetCoord( pos.X(), pos.Y() ); + pos = surface->Value( pos.X(), pos.Y() ).Transformed(loc); + } + } + // create or update the node + if ( !node ) + { + node = helper.AddNode( pos.X(), pos.Y(), pos.Z()); + if ( !edge._sWOL.IsNull() ) + { + if ( isOnEdge ) + getMeshDS()->SetNodeOnEdge( node, geomEdge, u ); + else + getMeshDS()->SetNodeOnFace( node, geomFace, uv.X(), uv.Y() ); + } + else + { + getMeshDS()->SetNodeInVolume( node, helper.GetSubShapeID() ); + } + } + else + { + if ( !edge._sWOL.IsNull() ) + { + // make average pos from new and current parameters + if ( isOnEdge ) + { + u = 0.5 * ( u + helper.GetNodeU( geomEdge, node )); + pos = curve->Value( u ).Transformed(loc); + } + else + { + uv = 0.5 * ( uv + helper.GetNodeUV( geomFace, node )); + pos = surface->Value( uv.X(), uv.Y()).Transformed(loc); + } + } + node->setXYZ( pos.X(), pos.Y(), pos.Z() ); + } + } + } + + // TODO: make quadratic prisms and polyhedrons(?) + + helper.SetElementsOnShape(true); + + TopExp_Explorer exp( data._solid, TopAbs_FACE ); + for ( ; exp.More(); exp.Next() ) + { + if ( _ignoreShapeIds.count( getMeshDS()->ShapeToIndex( exp.Current() ))) + continue; + SMESHDS_SubMesh* fSubM = getMeshDS()->MeshElements( exp.Current() ); + SMDS_ElemIteratorPtr fIt = fSubM->GetElements(); + vector< vector* > nnVec; + while ( fIt->more() ) + { + const SMDS_MeshElement* face = fIt->next(); + int nbNodes = face->NbCornerNodes(); + nnVec.resize( nbNodes ); + SMDS_ElemIteratorPtr nIt = face->nodesIterator(); + for ( int iN = 0; iN < nbNodes; ++iN ) + { + const SMDS_MeshNode* n = static_cast( nIt->next() ); + nnVec[ iN ] = & data._n2eMap[ n ]->_nodes; + } + + int nbZ = nnVec[0]->size(); + switch ( nbNodes ) + { + case 3: + for ( int iZ = 1; iZ < nbZ; ++iZ ) + helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], (*nnVec[2])[iZ-1], + (*nnVec[0])[iZ], (*nnVec[1])[iZ], (*nnVec[2])[iZ]); + break; + case 4: + for ( int iZ = 1; iZ < nbZ; ++iZ ) + helper.AddVolume( (*nnVec[0])[iZ-1], (*nnVec[1])[iZ-1], + (*nnVec[2])[iZ-1], (*nnVec[3])[iZ-1], + (*nnVec[0])[iZ], (*nnVec[1])[iZ], + (*nnVec[2])[iZ], (*nnVec[3])[iZ]); + break; + default: + return error("Not supported type of element", data._index); + } + } + } + return true; +} + +//================================================================================ +/*! + * \brief Shrink 2D mesh on faces to let space for inflated layers + */ +//================================================================================ + +bool _ViscousBuilder::shrink() +{ + // make map of (ids of FACEs to shrink mesh on) to (_SolidData containing _LayerEdge's + // inflated along FACE or EDGE) + map< TGeomID, _SolidData* > f2sdMap; + for ( unsigned i = 0 ; i < _sdVec.size(); ++i ) + { + _SolidData& data = _sdVec[i]; + TopTools_MapOfShape FFMap; + map< TGeomID, TopoDS_Shape >::iterator s2s = data._shrinkShape2Shape.begin(); + for (; s2s != data._shrinkShape2Shape.end(); ++s2s ) + if ( s2s->second.ShapeType() == TopAbs_FACE ) + { + f2sdMap.insert( make_pair( getMeshDS()->ShapeToIndex( s2s->second ), &data )); + + if ( FFMap.Add( (*s2s).second )) + // Put mesh faces on the shrinked FACE to the proxy sub-mesh to avoid + // usage of mesh faces made in addBoundaryElements() by the 3D algo or + // by StdMeshers_QuadToTriaAdaptor + if ( SMESHDS_SubMesh* smDS = getMeshDS()->MeshElements( s2s->second )) + { + SMESH_ProxyMesh::SubMesh* proxySub = + data._proxyMesh->getFaceSubM( TopoDS::Face( s2s->second ), /*create=*/true); + SMDS_ElemIteratorPtr fIt = smDS->GetElements(); + while ( fIt->more() ) + proxySub->AddElement( fIt->next() ); + // as a result 3D algo will use elements from proxySub and not from smDS + } + } + } + + SMESH_MesherHelper helper( *_mesh ); + helper.ToFixNodeParameters( true ); + + // EDGE's to shrink + map< int, _Shrinker1D > e2shrMap; + + // loop on FACES to srink mesh on + map< TGeomID, _SolidData* >::iterator f2sd = f2sdMap.begin(); + for ( ; f2sd != f2sdMap.end(); ++f2sd ) + { + _SolidData& data = *f2sd->second; + TNode2Edge& n2eMap = data._n2eMap; + const TopoDS_Face& F = TopoDS::Face( getMeshDS()->IndexToShape( f2sd->first )); + + Handle(Geom_Surface) surface = BRep_Tool::Surface(F); + + SMESH_subMesh* sm = _mesh->GetSubMesh( F ); + SMESHDS_SubMesh* smDS = sm->GetSubMeshDS(); + + helper.SetSubShape(F); + + // =========================== + // Prepare data for shrinking + // =========================== + + // Collect nodes to smooth, as src nodes are not yet replaced by tgt ones + // and thus all nodes on a FACE connected to 2d elements are to be smoothed + vector < const SMDS_MeshNode* > smoothNodes; + { + SMDS_NodeIteratorPtr nIt = smDS->GetNodes(); + while ( nIt->more() ) + { + const SMDS_MeshNode* n = nIt->next(); + if ( n->NbInverseElements( SMDSAbs_Face ) > 0 ) + smoothNodes.push_back( n ); + } + } + // Find out face orientation + double refSign = 1; + const set ignoreShapes; + bool isOkUV; + if ( !smoothNodes.empty() ) + { + vector<_Simplex> simplices; + getSimplices( smoothNodes[0], simplices, ignoreShapes ); + helper.GetNodeUV( F, simplices[0]._nPrev, 0, &isOkUV ); // fix UV of silpmex nodes + helper.GetNodeUV( F, simplices[0]._nNext, 0, &isOkUV ); + gp_XY uv = helper.GetNodeUV( F, smoothNodes[0], 0, &isOkUV ); + if ( !simplices[0].IsForward(uv, smoothNodes[0], F, helper,refSign) ) + refSign = -1; + } + + // Find _LayerEdge's inflated along F + vector< _LayerEdge* > lEdges; + { + SMESH_subMeshIteratorPtr subIt = + sm->getDependsOnIterator(/*includeSelf=*/false, /*complexShapeFirst=*/false); + while ( subIt->more() ) + { + SMESH_subMesh* sub = subIt->next(); + SMESHDS_SubMesh* subDS = sub->GetSubMeshDS(); + if ( subDS->NbNodes() == 0 || !n2eMap.count( subDS->GetNodes()->next() )) + continue; + SMDS_NodeIteratorPtr nIt = subDS->GetNodes(); + while ( nIt->more() ) + { + _LayerEdge* edge = n2eMap[ nIt->next() ]; + lEdges.push_back( edge ); + prepareEdgeToShrink( *edge, F, helper, smDS ); + } + } + } + + // Replace source nodes by target nodes in mesh faces to shrink + const SMDS_MeshNode* nodes[20]; + for ( unsigned i = 0; i < lEdges.size(); ++i ) + { + _LayerEdge& edge = *lEdges[i]; + const SMDS_MeshNode* srcNode = edge._nodes[0]; + const SMDS_MeshNode* tgtNode = edge._nodes.back(); + SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + if ( !smDS->Contains( f )) + continue; + SMDS_ElemIteratorPtr nIt = f->nodesIterator(); + for ( int iN = 0; iN < f->NbNodes(); ++iN ) + { + const SMDS_MeshNode* n = static_cast( nIt->next() ); + nodes[iN] = ( n == srcNode ? tgtNode : n ); + } + helper.GetMeshDS()->ChangeElementNodes( f, nodes, f->NbNodes() ); + } + } + + // find out if a FACE is concave + const bool isConcaveFace = isConcave( F, helper ); + + // Create _SmoothNode's on face F + vector< _SmoothNode > nodesToSmooth( smoothNodes.size() ); + { + dumpFunction(SMESH_Comment("beforeShrinkFace")<first); // debug + for ( unsigned i = 0; i < smoothNodes.size(); ++i ) + { + const SMDS_MeshNode* n = smoothNodes[i]; + nodesToSmooth[ i ]._node = n; + // src nodes must be replaced by tgt nodes to have tgt nodes in _simplices + getSimplices( n, nodesToSmooth[ i ]._simplices, ignoreShapes, NULL, isConcaveFace ); + // fix up incorrect uv of nodes on the FACE + helper.GetNodeUV( F, n, 0, &isOkUV); + dumpMove( n ); + } + dumpFunctionEnd(); + } + //if ( nodesToSmooth.empty() ) continue; + + // Find EDGE's to shrink + set< _Shrinker1D* > eShri1D; + { + for ( unsigned i = 0; i < lEdges.size(); ++i ) + { + _LayerEdge* edge = lEdges[i]; + if ( edge->_sWOL.ShapeType() == TopAbs_EDGE ) + { + TGeomID edgeIndex = getMeshDS()->ShapeToIndex( edge->_sWOL ); + _Shrinker1D& srinker = e2shrMap[ edgeIndex ]; + eShri1D.insert( & srinker ); + srinker.AddEdge( edge, helper ); + // restore params of nodes on EGDE if the EDGE has been already + // srinked while srinking another FACE + srinker.RestoreParams(); + } + } + } + + // ================== + // Perform shrinking + // ================== + + bool shrinked = true; + int badNb, shriStep=0, smooStep=0; + while ( shrinked ) + { + // Move boundary nodes (actually just set new UV) + // ----------------------------------------------- + dumpFunction(SMESH_Comment("moveBoundaryOnF")<first<<"_st"<SetNewLength2d( surface,F,helper ); + } + dumpFunctionEnd(); + + // Move nodes on EDGE's + set< _Shrinker1D* >::iterator shr = eShri1D.begin(); + for ( ; shr != eShri1D.end(); ++shr ) + (*shr)->Compute( /*set3D=*/false, helper ); + + // Smoothing in 2D + // ----------------- + int nbNoImpSteps = 0; + bool moved = true; + badNb = 1; + while (( nbNoImpSteps < 5 && badNb > 0) && moved) + { + dumpFunction(SMESH_Comment("shrinkFace")<first<<"_st"<<++smooStep); // debug + + int oldBadNb = badNb; + badNb = 0; + moved = false; + for ( unsigned i = 0; i < nodesToSmooth.size(); ++i ) + { + moved |= nodesToSmooth[i].Smooth( badNb,surface,helper,refSign, + /*isCentroidal=*/isConcaveFace,/*set3D=*/false ); + } + if ( badNb < oldBadNb ) + nbNoImpSteps = 0; + else + nbNoImpSteps++; + + dumpFunctionEnd(); + } + if ( badNb > 0 ) + return error(SMESH_Comment("Can't shrink 2D mesh on face ") << f2sd->first ); + } + // No wrongly shaped faces remain; final smooth. Set node XYZ. + // First, find out a needed quality of smoothing (high for quadrangles only) + bool highQuality; + { + const bool hasTria = _mesh->NbTriangles(), hasQuad = _mesh->NbQuadrangles(); + if ( hasTria != hasQuad ) + { + highQuality = hasQuad; + } + else + { + set nbNodesSet; + SMDS_ElemIteratorPtr fIt = smDS->GetElements(); + while ( fIt->more() && nbNodesSet.size() < 2 ) + nbNodesSet.insert( fIt->next()->NbCornerNodes() ); + highQuality = ( *nbNodesSet.begin() == 4 ); + } + } + if ( !highQuality && isConcaveFace ) + fixBadFaces( F, helper ); // fix narrow faces by swaping diagonals + for ( int st = highQuality ? 10 : 3; st; --st ) + { + dumpFunction(SMESH_Comment("shrinkFace")<first<<"_st"<<++smooStep); // debug + for ( unsigned i = 0; i < nodesToSmooth.size(); ++i ) + nodesToSmooth[i].Smooth( badNb,surface,helper,refSign, + /*isCentroidal=*/isConcaveFace,/*set3D=*/st==1 ); + dumpFunctionEnd(); + } + // Set an event listener to clear FACE sub-mesh together with SOLID sub-mesh + _SrinkShapeListener::ToClearSubMeshWithSolid( sm, data._solid ); + + } // loop on FACES to srink mesh on + + + // Replace source nodes by target nodes in shrinked mesh edges + + map< int, _Shrinker1D >::iterator e2shr = e2shrMap.begin(); + for ( ; e2shr != e2shrMap.end(); ++e2shr ) + e2shr->second.SwapSrcTgtNodes( getMeshDS() ); + + return true; +} + +//================================================================================ +/*! + * \brief Computes 2d shrink direction and finds nodes limiting shrinking + */ +//================================================================================ + +bool _ViscousBuilder::prepareEdgeToShrink( _LayerEdge& edge, + const TopoDS_Face& F, + SMESH_MesherHelper& helper, + const SMESHDS_SubMesh* faceSubMesh) +{ + const SMDS_MeshNode* srcNode = edge._nodes[0]; + const SMDS_MeshNode* tgtNode = edge._nodes.back(); + + edge._pos.clear(); + + if ( edge._sWOL.ShapeType() == TopAbs_FACE ) + { + gp_XY srcUV = helper.GetNodeUV( F, srcNode ); + gp_XY tgtUV = helper.GetNodeUV( F, tgtNode ); + gp_Vec2d uvDir( srcUV, tgtUV ); + double uvLen = uvDir.Magnitude(); + uvDir /= uvLen; + edge._normal.SetCoord( uvDir.X(),uvDir.Y(), 0); + + // IMPORTANT to have src nodes NOT yet REPLACED by tgt nodes in shrinked faces + vector faces; + multimap< double, const SMDS_MeshNode* > proj2node; + SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face); + while ( fIt->more() ) + { + const SMDS_MeshElement* f = fIt->next(); + if ( faceSubMesh->Contains( f )) + faces.push_back( f ); + } + for ( unsigned i = 0; i < faces.size(); ++i ) + { + const int nbNodes = faces[i]->NbCornerNodes(); + for ( int j = 0; j < nbNodes; ++j ) + { + const SMDS_MeshNode* n = faces[i]->GetNode(j); + if ( n == srcNode ) continue; + if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE && + ( faces.size() > 1 || nbNodes > 3 )) + continue; + gp_Pnt2d uv = helper.GetNodeUV( F, n ); + gp_Vec2d uvDirN( srcUV, uv ); + double proj = uvDirN * uvDir; + proj2node.insert( make_pair( proj, n )); + } + } + + multimap< double, const SMDS_MeshNode* >::iterator p2n = proj2node.begin(), p2nEnd; + const double minProj = p2n->first; + const double projThreshold = 1.1 * uvLen; + if ( minProj > projThreshold ) + { + // tgtNode is located so that it does not make faces with wrong orientation + return true; + } + edge._pos.resize(1); + edge._pos[0].SetCoord( tgtUV.X(), tgtUV.Y(), 0 ); + + // store most risky nodes in _simplices + p2nEnd = proj2node.lower_bound( projThreshold ); + int nbSimpl = ( std::distance( p2n, p2nEnd ) + 1) / 2; + edge._simplices.resize( nbSimpl ); + for ( int i = 0; i < nbSimpl; ++i ) + { + edge._simplices[i]._nPrev = p2n->second; + if ( ++p2n != p2nEnd ) + edge._simplices[i]._nNext = p2n->second; + } + // set UV of source node to target node + SMDS_FacePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( srcUV.X() ); + pos->SetVParameter( srcUV.Y() ); + } + else // _sWOL is TopAbs_EDGE + { + TopoDS_Edge E = TopoDS::Edge( edge._sWOL); + SMESHDS_SubMesh* edgeSM = getMeshDS()->MeshElements( E ); + if ( !edgeSM || edgeSM->NbElements() == 0 ) + return error(SMESH_Comment("Not meshed EDGE ") << getMeshDS()->ShapeToIndex( E )); + + const SMDS_MeshNode* n2 = 0; + SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge); + while ( eIt->more() && !n2 ) + { + const SMDS_MeshElement* e = eIt->next(); + if ( !edgeSM->Contains(e)) continue; + n2 = e->GetNode( 0 ); + if ( n2 == srcNode ) n2 = e->GetNode( 1 ); + } + if ( !n2 ) + return error(SMESH_Comment("Wrongly meshed EDGE ") << getMeshDS()->ShapeToIndex( E )); + + double uSrc = helper.GetNodeU( E, srcNode, n2 ); + double uTgt = helper.GetNodeU( E, tgtNode, srcNode ); + double u2 = helper.GetNodeU( E, n2, srcNode ); + + if ( fabs( uSrc-uTgt ) < 0.99 * fabs( uSrc-u2 )) + { + // tgtNode is located so that it does not make faces with wrong orientation + return true; + } + edge._pos.resize(1); + edge._pos[0].SetCoord( U_TGT, uTgt ); + edge._pos[0].SetCoord( U_SRC, uSrc ); + edge._pos[0].SetCoord( LEN_TGT, fabs( uSrc-uTgt )); + + edge._simplices.resize( 1 ); + edge._simplices[0]._nPrev = n2; + + // set UV of source node to target node + SMDS_EdgePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( uSrc ); + } + return true; + + //================================================================================ + /*! + * \brief Compute positions (UV) to set to a node on edge moved during shrinking + */ + //================================================================================ + + // Compute UV to follow during shrinking + +// const SMDS_MeshNode* srcNode = edge._nodes[0]; +// const SMDS_MeshNode* tgtNode = edge._nodes.back(); + +// gp_XY srcUV = helper.GetNodeUV( F, srcNode ); +// gp_XY tgtUV = helper.GetNodeUV( F, tgtNode ); +// gp_Vec2d uvDir( srcUV, tgtUV ); +// double uvLen = uvDir.Magnitude(); +// uvDir /= uvLen; + +// // Select shrinking step such that not to make faces with wrong orientation. +// // IMPORTANT to have src nodes NOT yet REPLACED by tgt nodes in shrinked faces +// const double minStepSize = uvLen / 20; +// double stepSize = uvLen; +// SMDS_ElemIteratorPtr fIt = srcNode->GetInverseElementIterator(SMDSAbs_Face); +// while ( fIt->more() ) +// { +// const SMDS_MeshElement* f = fIt->next(); +// if ( !faceSubMesh->Contains( f )) continue; +// const int nbNodes = f->NbCornerNodes(); +// for ( int i = 0; i < nbNodes; ++i ) +// { +// const SMDS_MeshNode* n = f->GetNode(i); +// if ( n->GetPosition()->GetTypeOfPosition() != SMDS_TOP_FACE || n == srcNode) +// continue; +// gp_XY uv = helper.GetNodeUV( F, n ); +// gp_Vec2d uvDirN( srcUV, uv ); +// double proj = uvDirN * uvDir; +// if ( proj < stepSize && proj > minStepSize ) +// stepSize = proj; +// } +// } +// stepSize *= 0.8; + +// const int nbSteps = ceil( uvLen / stepSize ); +// gp_XYZ srcUV0( srcUV.X(), srcUV.Y(), 0 ); +// gp_XYZ tgtUV0( tgtUV.X(), tgtUV.Y(), 0 ); +// edge._pos.resize( nbSteps ); +// edge._pos[0] = tgtUV0; +// for ( int i = 1; i < nbSteps; ++i ) +// { +// double r = i / double( nbSteps ); +// edge._pos[i] = (1-r) * tgtUV0 + r * srcUV0; +// } +// return true; +} + +//================================================================================ +/*! + * \brief Try to fix triangles with high aspect ratio by swaping diagonals + */ +//================================================================================ + +void _ViscousBuilder::fixBadFaces(const TopoDS_Face& F, SMESH_MesherHelper& helper) +{ + SMESH::Controls::AspectRatio qualifier; + SMESH::Controls::TSequenceOfXYZ points(3), points1(3), points2(3); + const double maxAspectRatio = 4.; + + // find bad triangles + + vector< const SMDS_MeshElement* > badTrias; + vector< double > badAspects; + SMESHDS_SubMesh* sm = helper.GetMeshDS()->MeshElements( F ); + SMDS_ElemIteratorPtr fIt = sm->GetElements(); + while ( fIt->more() ) + { + const SMDS_MeshElement * f = fIt->next(); + if ( f->NbCornerNodes() != 3 ) continue; + for ( int iP = 0; iP < 3; ++iP ) points(iP+1) = SMESH_TNodeXYZ( f->GetNode(iP)); + double aspect = qualifier.GetValue( points ); + if ( aspect > maxAspectRatio ) + { + badTrias.push_back( f ); + badAspects.push_back( aspect ); + } + } + if ( badTrias.empty() ) + return; + + // find couples of faces to swap diagonal + + typedef pair < const SMDS_MeshElement* , const SMDS_MeshElement* > T2Trias; + vector< T2Trias > triaCouples; + + TIDSortedElemSet involvedFaces, emptySet; + for ( size_t iTia = 0; iTia < badTrias.size(); ++iTia ) + { + T2Trias trias [3]; + double aspRatio [3]; + int i1, i2, i3; + + involvedFaces.insert( badTrias[iTia] ); + for ( int iP = 0; iP < 3; ++iP ) + points(iP+1) = SMESH_TNodeXYZ( badTrias[iTia]->GetNode(iP)); + + // find triangles adjacent to badTrias[iTia] with better aspect ratio after diag-swaping + int bestCouple = -1; + for ( int iSide = 0; iSide < 3; ++iSide ) + { + const SMDS_MeshNode* n1 = badTrias[iTia]->GetNode( iSide ); + const SMDS_MeshNode* n2 = badTrias[iTia]->GetNode(( iSide+1 ) % 3 ); + trias [iSide].first = badTrias[iTia]; + trias [iSide].second = SMESH_MeshEditor::FindFaceInSet( n1, n2, emptySet, involvedFaces, + & i1, & i2 ); + if ( ! trias[iSide].second || trias[iSide].second->NbCornerNodes() != 3 ) + continue; + + // aspect ratio of an adjacent tria + for ( int iP = 0; iP < 3; ++iP ) + points2(iP+1) = SMESH_TNodeXYZ( trias[iSide].second->GetNode(iP)); + double aspectInit = qualifier.GetValue( points2 ); + + // arrange nodes as after diag-swaping + if ( helper.WrapIndex( i1+1, 3 ) == i2 ) + i3 = helper.WrapIndex( i1-1, 3 ); + else + i3 = helper.WrapIndex( i1+1, 3 ); + points1 = points; + points1( 1+ iSide ) = points2( 1+ i3 ); + points2( 1+ i2 ) = points1( 1+ ( iSide+2 ) % 3 ); + + // aspect ratio after diag-swaping + aspRatio[ iSide ] = qualifier.GetValue( points1 ) + qualifier.GetValue( points2 ); + if ( aspRatio[ iSide ] > aspectInit + badAspects[ iTia ] ) + continue; + + if ( bestCouple < 0 || aspRatio[ bestCouple ] > aspRatio[ iSide ] ) + bestCouple = iSide; + } + + if ( bestCouple >= 0 ) + { + triaCouples.push_back( trias[bestCouple] ); + involvedFaces.insert ( trias[bestCouple].second ); + } + else + { + involvedFaces.erase( badTrias[iTia] ); + } + } + if ( triaCouples.empty() ) + return; + + // swap diagonals + + SMESH_MeshEditor editor( helper.GetMesh() ); + dumpFunction(SMESH_Comment("beforeSwapDiagonals_F")<( _nodes.back() ); + + if ( _sWOL.ShapeType() == TopAbs_FACE ) + { + gp_XY curUV = helper.GetNodeUV( F, tgtNode ); + gp_Pnt2d tgtUV( _pos[0].X(), _pos[0].Y()); + gp_Vec2d uvDir( _normal.X(), _normal.Y() ); + const double uvLen = tgtUV.Distance( curUV ); + + // Select shrinking step such that not to make faces with wrong orientation. + const double kSafe = 0.8; + const double minStepSize = uvLen / 10; + double stepSize = uvLen; + for ( unsigned i = 0; i < _simplices.size(); ++i ) + { + const SMDS_MeshNode* nn[2] = { _simplices[i]._nPrev, _simplices[i]._nNext }; + for ( int j = 0; j < 2; ++j ) + if ( const SMDS_MeshNode* n = nn[j] ) + { + gp_XY uv = helper.GetNodeUV( F, n ); + gp_Vec2d uvDirN( curUV, uv ); + double proj = uvDirN * uvDir * kSafe; + if ( proj < stepSize && proj > minStepSize ) + stepSize = proj; + } + } + + gp_Pnt2d newUV; + if ( stepSize == uvLen ) + { + newUV = tgtUV; + _pos.clear(); + } + else + { + newUV = curUV + uvDir.XY() * stepSize; + } + + SMDS_FacePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( newUV.X() ); + pos->SetVParameter( newUV.Y() ); + +#ifdef __myDEBUG + gp_Pnt p = surface->Value( newUV.X(), newUV.Y() ); + tgtNode->setXYZ( p.X(), p.Y(), p.Z() ); + dumpMove( tgtNode ); +#endif + } + else // _sWOL is TopAbs_EDGE + { + TopoDS_Edge E = TopoDS::Edge( _sWOL ); + const SMDS_MeshNode* n2 = _simplices[0]._nPrev; + + const double u2 = helper.GetNodeU( E, n2, tgtNode ); + const double uSrc = _pos[0].Coord( U_SRC ); + const double lenTgt = _pos[0].Coord( LEN_TGT ); + + double newU = _pos[0].Coord( U_TGT ); + if ( lenTgt < 0.99 * fabs( uSrc-u2 )) + { + _pos.clear(); + } + else + { + newU = 0.1 * uSrc + 0.9 * u2; + } + SMDS_EdgePosition* pos = static_cast( tgtNode->GetPosition() ); + pos->SetUParameter( newU ); +#ifdef __myDEBUG + gp_XY newUV = helper.GetNodeUV( F, tgtNode, _nodes[0]); + gp_Pnt p = surface->Value( newUV.X(), newUV.Y() ); + tgtNode->setXYZ( p.X(), p.Y(), p.Z() ); + dumpMove( tgtNode ); +#endif + } + return true; +} + +//================================================================================ +/*! + * \brief Perform smooth on the FACE + * \retval bool - true if the node has been moved + */ +//================================================================================ + +bool _SmoothNode::Smooth(int& badNb, + Handle(Geom_Surface)& surface, + SMESH_MesherHelper& helper, + const double refSign, + bool isCentroidal, + bool set3D) +{ + const TopoDS_Face& face = TopoDS::Face( helper.GetSubShape() ); + + // get uv of surrounding nodes + vector uv( _simplices.size() ); + for ( size_t i = 0; i < _simplices.size(); ++i ) + uv[i] = helper.GetNodeUV( face, _simplices[i]._nPrev, _node ); + + // compute new UV for the node + gp_XY newPos (0,0); + if ( isCentroidal && _simplices.size() > 3 ) + { + // average centers of diagonals wieghted with their reciprocal lengths + if ( _simplices.size() == 4 ) + { + double w1 = 1. / ( uv[2]-uv[0] ).SquareModulus(); + double w2 = 1. / ( uv[3]-uv[1] ).SquareModulus(); + newPos = ( w1 * ( uv[2]+uv[0] ) + w2 * ( uv[3]+uv[1] )) / ( w1+w2 ) / 2; + } + else + { + double sumWeight = 0; + int nb = _simplices.size() == 4 ? 2 : _simplices.size(); + for ( int i = 0; i < nb; ++i ) + { + int iFrom = i + 2; + int iTo = i + _simplices.size() - 1; + for ( int j = iFrom; j < iTo; ++j ) + { + int i2 = SMESH_MesherHelper::WrapIndex( j, _simplices.size() ); + double w = 1. / ( uv[i]-uv[i2] ).SquareModulus(); + sumWeight += w; + newPos += w * ( uv[i]+uv[i2] ); + } + } + newPos /= 2 * sumWeight; + } + } + else + { + // Laplacian smooth + isCentroidal = false; + for ( size_t i = 0; i < _simplices.size(); ++i ) + newPos += uv[i]; + newPos /= _simplices.size(); + } + + // count quality metrics (orientation) of triangles around the node + int nbOkBefore = 0; + gp_XY tgtUV = helper.GetNodeUV( face, _node ); + for ( unsigned i = 0; i < _simplices.size(); ++i ) + nbOkBefore += _simplices[i].IsForward( tgtUV, _node, face, helper, refSign ); + + int nbOkAfter = 0; + for ( unsigned i = 0; i < _simplices.size(); ++i ) + nbOkAfter += _simplices[i].IsForward( newPos, _node, face, helper, refSign ); + + if ( nbOkAfter < nbOkBefore ) + { + // if ( isCentroidal ) + // return Smooth( badNb, surface, helper, refSign, !isCentroidal, set3D ); + badNb += _simplices.size() - nbOkBefore; + return false; + } + + SMDS_FacePosition* pos = static_cast( _node->GetPosition() ); + pos->SetUParameter( newPos.X() ); + pos->SetVParameter( newPos.Y() ); + +#ifdef __myDEBUG + set3D = true; +#endif + if ( set3D ) + { + gp_Pnt p = surface->Value( newPos.X(), newPos.Y() ); + const_cast< SMDS_MeshNode* >( _node )->setXYZ( p.X(), p.Y(), p.Z() ); + dumpMove( _node ); + } + + badNb += _simplices.size() - nbOkAfter; + return ( (tgtUV-newPos).SquareModulus() > 1e-10 ); +} + +//================================================================================ +/*! + * \brief Delete _SolidData + */ +//================================================================================ + +_SolidData::~_SolidData() +{ + for ( unsigned i = 0; i < _edges.size(); ++i ) + { + if ( _edges[i] && _edges[i]->_2neibors ) + delete _edges[i]->_2neibors; + delete _edges[i]; + } + _edges.clear(); +} +//================================================================================ +/*! + * \brief Add a _LayerEdge inflated along the EDGE + */ +//================================================================================ + +void _Shrinker1D::AddEdge( const _LayerEdge* e, SMESH_MesherHelper& helper ) +{ + // init + if ( _nodes.empty() ) + { + _edges[0] = _edges[1] = 0; + _done = false; + } + // check _LayerEdge + if ( e == _edges[0] || e == _edges[1] ) + return; + if ( e->_sWOL.IsNull() || e->_sWOL.ShapeType() != TopAbs_EDGE ) + throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added")); + if ( _edges[0] && _edges[0]->_sWOL != e->_sWOL ) + throw SALOME_Exception(LOCALIZED("Wrong _LayerEdge is added")); + + // store _LayerEdge + const TopoDS_Edge& E = TopoDS::Edge( e->_sWOL ); + double f,l; + BRep_Tool::Range( E, f,l ); + double u = helper.GetNodeU( E, e->_nodes[0], e->_nodes.back()); + _edges[ u < 0.5*(f+l) ? 0 : 1 ] = e; + + // Update _nodes + + const SMDS_MeshNode* tgtNode0 = _edges[0] ? _edges[0]->_nodes.back() : 0; + const SMDS_MeshNode* tgtNode1 = _edges[1] ? _edges[1]->_nodes.back() : 0; + + if ( _nodes.empty() ) + { + SMESHDS_SubMesh * eSubMesh = helper.GetMeshDS()->MeshElements( E ); + if ( !eSubMesh || eSubMesh->NbNodes() < 1 ) + return; + TopLoc_Location loc; + Handle(Geom_Curve) C = BRep_Tool::Curve(E, loc, f,l); + GeomAdaptor_Curve aCurve(C, f,l); + const double totLen = GCPnts_AbscissaPoint::Length(aCurve, f, l); + + int nbExpectNodes = eSubMesh->NbNodes() - e->_nodes.size(); + _initU .reserve( nbExpectNodes ); + _normPar.reserve( nbExpectNodes ); + _nodes .reserve( nbExpectNodes ); + SMDS_NodeIteratorPtr nIt = eSubMesh->GetNodes(); + while ( nIt->more() ) + { + const SMDS_MeshNode* node = nIt->next(); + if ( node->NbInverseElements(SMDSAbs_Edge) == 0 || + node == tgtNode0 || node == tgtNode1 ) + continue; // refinement nodes + _nodes.push_back( node ); + _initU.push_back( helper.GetNodeU( E, node )); + double len = GCPnts_AbscissaPoint::Length(aCurve, f, _initU.back()); + _normPar.push_back( len / totLen ); + } + } + else + { + // remove target node of the _LayerEdge from _nodes + int nbFound = 0; + for ( unsigned i = 0; i < _nodes.size(); ++i ) + if ( !_nodes[i] || _nodes[i] == tgtNode0 || _nodes[i] == tgtNode1 ) + _nodes[i] = 0, nbFound++; + if ( nbFound == _nodes.size() ) + _nodes.clear(); + } +} + +//================================================================================ +/*! + * \brief Move nodes on EDGE from ends where _LayerEdge's are inflated + */ +//================================================================================ + +void _Shrinker1D::Compute(bool set3D, SMESH_MesherHelper& helper) +{ + if ( _done || _nodes.empty()) + return; + const _LayerEdge* e = _edges[0]; + if ( !e ) e = _edges[1]; + if ( !e ) return; + + _done = (( !_edges[0] || _edges[0]->_pos.empty() ) && + ( !_edges[1] || _edges[1]->_pos.empty() )); + + const TopoDS_Edge& E = TopoDS::Edge( e->_sWOL ); + double f,l; + if ( set3D || _done ) + { + Handle(Geom_Curve) C = BRep_Tool::Curve(E, f,l); + GeomAdaptor_Curve aCurve(C, f,l); + + if ( _edges[0] ) + f = helper.GetNodeU( E, _edges[0]->_nodes.back(), _nodes[0] ); + if ( _edges[1] ) + l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() ); + double totLen = GCPnts_AbscissaPoint::Length( aCurve, f, l ); + + for ( unsigned i = 0; i < _nodes.size(); ++i ) + { + if ( !_nodes[i] ) continue; + double len = totLen * _normPar[i]; + GCPnts_AbscissaPoint discret( aCurve, len, f ); + if ( !discret.IsDone() ) + return throw SALOME_Exception(LOCALIZED("GCPnts_AbscissaPoint failed")); + double u = discret.Parameter(); + SMDS_EdgePosition* pos = static_cast( _nodes[i]->GetPosition() ); + pos->SetUParameter( u ); + gp_Pnt p = C->Value( u ); + const_cast< SMDS_MeshNode*>( _nodes[i] )->setXYZ( p.X(), p.Y(), p.Z() ); + } + } + else + { + BRep_Tool::Range( E, f,l ); + if ( _edges[0] ) + f = helper.GetNodeU( E, _edges[0]->_nodes.back(), _nodes[0] ); + if ( _edges[1] ) + l = helper.GetNodeU( E, _edges[1]->_nodes.back(), _nodes.back() ); + + for ( unsigned i = 0; i < _nodes.size(); ++i ) + { + if ( !_nodes[i] ) continue; + double u = f * ( 1-_normPar[i] ) + l * _normPar[i]; + SMDS_EdgePosition* pos = static_cast( _nodes[i]->GetPosition() ); + pos->SetUParameter( u ); + } + } +} + +//================================================================================ +/*! + * \brief Restore initial parameters of nodes on EDGE + */ +//================================================================================ + +void _Shrinker1D::RestoreParams() +{ + if ( _done ) + for ( unsigned i = 0; i < _nodes.size(); ++i ) + { + if ( !_nodes[i] ) continue; + SMDS_EdgePosition* pos = static_cast( _nodes[i]->GetPosition() ); + pos->SetUParameter( _initU[i] ); + } + _done = false; +} + +//================================================================================ +/*! + * \brief Replace source nodes by target nodes in shrinked mesh edges + */ +//================================================================================ + +void _Shrinker1D::SwapSrcTgtNodes( SMESHDS_Mesh* mesh ) +{ + const SMDS_MeshNode* nodes[3]; + for ( int i = 0; i < 2; ++i ) + { + if ( !_edges[i] ) continue; + + SMESHDS_SubMesh * eSubMesh = mesh->MeshElements( _edges[i]->_sWOL ); + if ( !eSubMesh ) return; + const SMDS_MeshNode* srcNode = _edges[i]->_nodes[0]; + const SMDS_MeshNode* tgtNode = _edges[i]->_nodes.back(); + SMDS_ElemIteratorPtr eIt = srcNode->GetInverseElementIterator(SMDSAbs_Edge); + while ( eIt->more() ) + { + const SMDS_MeshElement* e = eIt->next(); + if ( !eSubMesh->Contains( e )) + continue; + SMDS_ElemIteratorPtr nIt = e->nodesIterator(); + for ( int iN = 0; iN < e->NbNodes(); ++iN ) + { + const SMDS_MeshNode* n = static_cast( nIt->next() ); + nodes[iN] = ( n == srcNode ? tgtNode : n ); + } + mesh->ChangeElementNodes( e, nodes, e->NbNodes() ); + } + } +} + +//================================================================================ +/*! + * \brief Creates 2D and 1D elements on boundaries of new prisms + */ +//================================================================================ + +bool _ViscousBuilder::addBoundaryElements() +{ + SMESH_MesherHelper helper( *_mesh ); + + for ( unsigned i = 0; i < _sdVec.size(); ++i ) + { + _SolidData& data = _sdVec[i]; + TopTools_IndexedMapOfShape geomEdges; + TopExp::MapShapes( data._solid, TopAbs_EDGE, geomEdges ); + for ( int iE = 1; iE <= geomEdges.Extent(); ++iE ) + { + const TopoDS_Edge& E = TopoDS::Edge( geomEdges(iE)); + + // Get _LayerEdge's based on E + + map< double, const SMDS_MeshNode* > u2nodes; + if ( !SMESH_Algo::GetSortedNodesOnEdge( getMeshDS(), E, /*ignoreMedium=*/false, u2nodes)) + continue; + + vector< _LayerEdge* > ledges; ledges.reserve( u2nodes.size() ); + TNode2Edge & n2eMap = data._n2eMap; + map< double, const SMDS_MeshNode* >::iterator u2n = u2nodes.begin(); + { + //check if 2D elements are needed on E + TNode2Edge::iterator n2e = n2eMap.find( u2n->second ); + if ( n2e == n2eMap.end() ) continue; // no layers on vertex + ledges.push_back( n2e->second ); + u2n++; + if (( n2e = n2eMap.find( u2n->second )) == n2eMap.end() ) + continue; // no layers on E + ledges.push_back( n2eMap[ u2n->second ]); + + const SMDS_MeshNode* tgtN0 = ledges[0]->_nodes.back(); + const SMDS_MeshNode* tgtN1 = ledges[1]->_nodes.back(); + int nbSharedPyram = 0; + SMDS_ElemIteratorPtr vIt = tgtN0->GetInverseElementIterator(SMDSAbs_Volume); + while ( vIt->more() ) + { + const SMDS_MeshElement* v = vIt->next(); + nbSharedPyram += int( v->GetNodeIndex( tgtN1 ) >= 0 ); + } + if ( nbSharedPyram > 1 ) + continue; // not free border of the pyramid + + if ( getMeshDS()->FindFace( ledges[0]->_nodes[0], ledges[0]->_nodes[1], + ledges[1]->_nodes[0], ledges[1]->_nodes[1])) + continue; // faces already created + } + for ( ++u2n; u2n != u2nodes.end(); ++u2n ) + ledges.push_back( n2eMap[ u2n->second ]); + + // Find out orientation and type of face to create + + bool reverse = false, isOnFace; + + map< TGeomID, TopoDS_Shape >::iterator e2f = + data._shrinkShape2Shape.find( getMeshDS()->ShapeToIndex( E )); + TopoDS_Shape F; + if (( isOnFace = ( e2f != data._shrinkShape2Shape.end() ))) + { + F = e2f->second.Oriented( TopAbs_FORWARD ); + reverse = ( helper.GetSubShapeOri( F, E ) == TopAbs_REVERSED ); + if ( helper.GetSubShapeOri( data._solid, F ) == TopAbs_REVERSED ) + reverse = !reverse; + } + else + { + // find FACE with layers sharing E + PShapeIteratorPtr fIt = helper.GetAncestors( E, *_mesh, TopAbs_FACE ); + while ( fIt->more() && F.IsNull() ) + { + const TopoDS_Shape* pF = fIt->next(); + if ( helper.IsSubShape( *pF, data._solid) && + !_ignoreShapeIds.count( e2f->first )) + F = *pF; + } + } + // Find the sub-mesh to add new faces + SMESHDS_SubMesh* sm = 0; + if ( isOnFace ) + sm = getMeshDS()->MeshElements( F ); + else + sm = data._proxyMesh->getFaceSubM( TopoDS::Face(F), /*create=*/true ); + if ( !sm ) + return error("error in addBoundaryElements()", data._index); + + // Make faces + const int dj1 = reverse ? 0 : 1; + const int dj2 = reverse ? 1 : 0; + for ( unsigned j = 1; j < ledges.size(); ++j ) + { + vector< const SMDS_MeshNode*>& nn1 = ledges[j-dj1]->_nodes; + vector< const SMDS_MeshNode*>& nn2 = ledges[j-dj2]->_nodes; + if ( isOnFace ) + for ( unsigned z = 1; z < nn1.size(); ++z ) + sm->AddElement( getMeshDS()->AddFace( nn1[z-1], nn2[z-1], nn2[z], nn1[z] )); + else + for ( unsigned z = 1; z < nn1.size(); ++z ) + sm->AddElement( new SMDS_FaceOfNodes( nn1[z-1], nn2[z-1], nn2[z], nn1[z])); + } + } + } + + return true; +} diff --git a/src/StdMeshers/StdMeshers_ViscousLayers.hxx b/src/StdMeshers/StdMeshers_ViscousLayers.hxx new file mode 100644 index 000000000..42c187130 --- /dev/null +++ b/src/StdMeshers/StdMeshers_ViscousLayers.hxx @@ -0,0 +1,94 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshers_ViscousLayers.hxx +// Created : Wed Dec 1 15:15:34 2010 +// Author : Edward AGAPOV (eap) + +#ifndef __StdMeshers_ViscousLayers_HXX__ +#define __StdMeshers_ViscousLayers_HXX__ + +#include "SMESH_StdMeshers.hxx" + +#include "SMESH_Hypothesis.hxx" +#include "SMESH_ProxyMesh.hxx" + +#include + +/*! + * \brief Hypothesis defining parameters of viscous layers + */ +class STDMESHERS_EXPORT StdMeshers_ViscousLayers : public SMESH_Hypothesis +{ +public: + StdMeshers_ViscousLayers(int hypId, int studyId, SMESH_Gen* gen); + + // Set faces to exclude from treatment + void SetIgnoreFaces(const std::vector& faceIds); + std::vector GetIgnoreFaces() const { return _ignoreFaceIds; } + + // Set total thickness of layers of prisms + void SetTotalThickness(double thickness); + double GetTotalThickness() const { return _thickness; } + + // Set number of layers of prisms + void SetNumberLayers(int nb); + int GetNumberLayers() const { return _nbLayers; } + + // Set factor (>1.0) of growth of layer thickness towards inside of mesh + void SetStretchFactor(double factor); + double GetStretchFactor() const { return _stretchFactor; } + + // Computes temporary 2D mesh to be used by 3D algorithm. + // Return SMESH_ProxyMesh for each SOLID in theShape + SMESH_ProxyMesh::Ptr Compute(SMESH_Mesh& theMesh, + const TopoDS_Shape& theShape, + const bool toMakeN2NMap=false) const; + + virtual std::ostream & SaveTo(std::ostream & save); + virtual std::istream & LoadFrom(std::istream & load); + + /*! + * \brief Initialize my parameter values by the mesh built on the geometry + * \param theMesh - the built mesh + * \param theShape - the geometry of interest + * \retval bool - true if parameter values have been successfully defined + * + * Just return false as this hypothesis does not have parameters values + */ + virtual bool SetParametersByMesh(const SMESH_Mesh* theMesh, const TopoDS_Shape& theShape); + + /*! + * \brief Initialize my parameter values by default parameters. + * \retval bool - true if parameter values have been successfully defined + */ + virtual bool SetParametersByDefaults(const TDefaults& dflts, const SMESH_Mesh* theMesh=0) + { return false; } + + static const char* GetHypType() { return "ViscousLayers"; } + + private: + + std::vector _ignoreFaceIds; + int _nbLayers; + double _thickness; + double _stretchFactor; +}; + +#endif diff --git a/src/StdMeshersGUI/Makefile.am b/src/StdMeshersGUI/Makefile.am index 38425ec0c..3f7a1d8ff 100644 --- a/src/StdMeshersGUI/Makefile.am +++ b/src/StdMeshersGUI/Makefile.am @@ -1,30 +1,26 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 # -# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -# -# SMESH StdMeshersGUI : GUI for StdMeshers plugin # File : Makefile.in # Author : Julia DOROVSKIKH # Modified by : Alexander BORODIN (OCN) - autotools usage # Module : SMESH -# + include $(top_srcdir)/adm_local/unix/make_common_starter.am # header files @@ -35,10 +31,15 @@ salomeinclude_HEADERS = \ StdMeshersGUI_DistrTable.h \ StdMeshersGUI_NbSegmentsCreator.h \ StdMeshersGUI_ObjectReferenceParamWdg.h \ - StdMeshersGUI_LayerDistributionParamWdg.h + StdMeshersGUI_QuadrangleParamWdg.h \ + StdMeshersGUI_LayerDistributionParamWdg.h \ + StdMeshersGUI_FixedPointsParamWdg.h \ + StdMeshersGUI_SubShapeSelectorWdg.h \ + StdMeshersGUI_CartesianParamCreator.h # Libraries targets lib_LTLIBRARIES = libStdMeshersGUI.la + dist_libStdMeshersGUI_la_SOURCES = \ StdMeshersGUI.cxx \ StdMeshersGUI_StdHypothesisCreator.cxx \ @@ -46,7 +47,11 @@ dist_libStdMeshersGUI_la_SOURCES = \ StdMeshersGUI_DistrTable.cxx \ StdMeshersGUI_NbSegmentsCreator.cxx \ StdMeshersGUI_ObjectReferenceParamWdg.cxx \ - StdMeshersGUI_LayerDistributionParamWdg.cxx + StdMeshersGUI_QuadrangleParamWdg.cxx \ + StdMeshersGUI_LayerDistributionParamWdg.cxx \ + StdMeshersGUI_FixedPointsParamWdg.cxx \ + StdMeshersGUI_SubShapeSelectorWdg.cxx \ + StdMeshersGUI_CartesianParamCreator.cxx MOC_FILES = \ StdMeshersGUI_StdHypothesisCreator_moc.cxx \ @@ -54,7 +59,11 @@ MOC_FILES = \ StdMeshersGUI_DistrTable_moc.cxx \ StdMeshersGUI_NbSegmentsCreator_moc.cxx \ StdMeshersGUI_ObjectReferenceParamWdg_moc.cxx \ - StdMeshersGUI_LayerDistributionParamWdg_moc.cxx + StdMeshersGUI_QuadrangleParamWdg_moc.cxx \ + StdMeshersGUI_LayerDistributionParamWdg_moc.cxx \ + StdMeshersGUI_FixedPointsParamWdg_moc.cxx \ + StdMeshersGUI_SubShapeSelectorWdg_moc.cxx \ + StdMeshersGUI_CartesianParamCreator_moc.cxx nodist_libStdMeshersGUI_la_SOURCES= \ $(MOC_FILES) @@ -74,6 +83,7 @@ libStdMeshersGUI_la_CPPFLAGS = \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ -I$(srcdir)/../SMESH \ + -I$(srcdir)/../SMESHUtils \ -I$(srcdir)/../SMESH_I \ -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../SMDS \ @@ -82,9 +92,7 @@ libStdMeshersGUI_la_CPPFLAGS = \ -I$(srcdir)/../StdMeshers \ -I$(srcdir)/../OBJECT \ -I$(srcdir)/../SMESHFiltersSelection \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix - + -I$(top_builddir)/idl libStdMeshersGUI_la_LDFLAGS = \ ../../idl/libSalomeIDLSMESH.la \ @@ -98,4 +106,5 @@ libStdMeshersGUI_la_LDFLAGS = \ # resources files nodist_salomeres_DATA= \ StdMeshers_images.qm \ - StdMeshers_msg_en.qm + StdMeshers_msg_en.qm \ + StdMeshers_msg_fr.qm diff --git a/src/StdMeshersGUI/SMESH_StdMeshersGUI.hxx b/src/StdMeshersGUI/SMESH_StdMeshersGUI.hxx index 76ce2a2a2..946bd61a6 100755 --- a/src/StdMeshersGUI/SMESH_StdMeshersGUI.hxx +++ b/src/StdMeshersGUI/SMESH_StdMeshersGUI.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_StdMeshersGUI.hxx // Author : Alexander BORODIN, Open CASCADE S.A.S. // @@ -26,7 +27,7 @@ #define SMESH_STDMESHERSGUI_HXX #ifdef WNT - #if defined STDMESHERSGUI_EXPORTS + #if defined STDMESHERSGUI_EXPORTS || defined StdMeshersGUI_EXPORTS #define STDMESHERSGUI_EXPORT __declspec( dllexport ) #else #define STDMESHERSGUI_EXPORT __declspec( dllimport ) diff --git a/src/StdMeshersGUI/StdMeshersGUI.cxx b/src/StdMeshersGUI/StdMeshersGUI.cxx index f3e400e82..8b86c5d27 100644 --- a/src/StdMeshersGUI/StdMeshersGUI.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI.cxx @@ -1,30 +1,32 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI.cxx // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // SMESH includes // #include "StdMeshersGUI_StdHypothesisCreator.h" #include "StdMeshersGUI_NbSegmentsCreator.h" +#include "StdMeshersGUI_CartesianParamCreator.h" //============================================================================= /*! GetHypothesisCreator @@ -38,6 +40,8 @@ extern "C" { if( aHypType=="NumberOfSegments" ) return new StdMeshersGUI_NbSegmentsCreator(); + else if ( aHypType=="CartesianParameters3D" ) + return new StdMeshersGUI_CartesianParamCreator( aHypType ); else return new StdMeshersGUI_StdHypothesisCreator( aHypType ); } diff --git a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx new file mode 100644 index 000000000..48eec0eb4 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.cxx @@ -0,0 +1,698 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshersGUI_CartesianParamCreator.cxx +// Author : Open CASCADE S.A.S. + +// SMESH includes +#include "StdMeshersGUI_CartesianParamCreator.h" + +#include +#include +#include +#include + +// IDL includes +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +// SALOME GUI includes +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPACING 6 +#define MARGIN 11 + +namespace StdMeshersGUI +{ + enum { COORD_BUT = 0, SPACING_BUT }; + + //================================================================================ + /*! + * \brief get spacing definition from a tree item + */ + //================================================================================ + + void getFromItem(QTreeWidgetItem * item, double& t0, double& t1, QString& fun ) + { + if ( item ) + { + t0 = item->text( 0 ).split(' ')[0].toDouble(); + t1 = item->data( 0, Qt::UserRole ).toDouble(); + fun = item->text( 1 ); + } + } + + //================================================================================ + /*! + * \brief set spacing definition to a tree item + */ + //================================================================================ + + QTreeWidgetItem* setToItem(double t0, double t1, const QString& fun, QTreeWidgetItem * item) + { + if ( !item ) item = new QTreeWidgetItem; + item->setText( 0, QString( "%1 - %2" ).arg( t0 ).arg( t1 )); + item->setData( 0, Qt::UserRole, t1 ); + item->setText( 1, fun ); + item->setFlags( item->flags() | Qt::ItemIsEditable ); + return item; + } + + //================================================================================ + /*! + * \brief Retrieves coordinate value from a list item + */ + //================================================================================ + + double coordFromItem( QListWidgetItem * item ) + { + return item ? item->data( Qt::UserRole ).toDouble() : 0; + } + + //================================================================================ + /*! + * \brief Sets coordinate value to a list item + */ + //================================================================================ + + QListWidgetItem* coordToItem( double coord, QListWidgetItem * item ) + { + if ( !item ) item = new QListWidgetItem; + item->setText( QString::number( coord )); + item->setData( Qt::UserRole, coord ); + item->setFlags( item->flags() | Qt::ItemIsEditable ); + return item; + } + + //================================================================================ + /*! + * \brief Constructor + * \param theParent - Parent widget for this tab + * + * Makes tab's look and feel + */ + //================================================================================ + + GridAxisTab::GridAxisTab( QWidget* theParent,const int axisIndex ): + QFrame( theParent ), myAxisIndex( axisIndex ) + { + // 1) Grid definition mode + myModeGroup = new QButtonGroup( this ); + QGroupBox* modeBox = new QGroupBox( tr( "GRID_DEF_MODE" ), this ); + QHBoxLayout* modeLay = new QHBoxLayout( modeBox ); + modeLay->setMargin( MARGIN ); + modeLay->setSpacing( SPACING ); + + QRadioButton* coordModeBtn = new QRadioButton( tr( "SMESH_COORDINATES" ), modeBox ); + QRadioButton* spacModeBtn = new QRadioButton( tr( "SPACING" ), modeBox ); + + modeLay->addWidget( coordModeBtn ); + modeLay->addWidget( spacModeBtn ); + myModeGroup->addButton( coordModeBtn, COORD_BUT ); + myModeGroup->addButton( spacModeBtn, SPACING_BUT ); + + // 2) Buttons + Step + myInsertBtn = new QPushButton( tr("INSERT"), this); + myDeleteBtn = new QPushButton( tr("SMESH_BUT_DELETE"), this); + + myStepLabel = new QLabel( tr("COORD_STEP")); + myStepSpin = new SMESHGUI_SpinBox( this ); + myStepSpin->setAcceptNames( false ); // No Notebook variables allowed + myStepSpin->RangeStepAndValidator(); + myStepSpin->SetStep( 1. ); + myStepSpin->SetValue( myStep = 1. ); + + // 3) Coodrinates/Spacing group + QFrame* csFrame = new QFrame( this ); + QVBoxLayout* scLay = new QVBoxLayout( csFrame ); + scLay->setMargin( 0 ); + scLay->setSpacing( SPACING ); + + // 3.1) Spacing + mySpacingTreeWdg = new QTreeWidget( csFrame ); + mySpacingTreeWdg->setColumnCount(2); + mySpacingTreeWdg->setHeaderLabels( QStringList() << tr( "SMESH_RANGE" ) << QString( "f(t)" )); + mySpacingTreeWdg->setColumnWidth( 1, 40 ); + mySpacingTreeWdg->setColumnWidth( 2, 30 ); + mySpacingTreeWdg->setItemDelegate( new LineDelegate( mySpacingTreeWdg )); + scLay->addWidget( mySpacingTreeWdg ); + + // 3.2) Coordinates + myCoordList = new QListWidget( csFrame ); + myCoordList->setItemDelegate( new LineDelegate( myCoordList )); + scLay->addWidget( myCoordList ); + + // layouting + + QGridLayout* axisTabLayout = new QGridLayout( this ); + axisTabLayout->setMargin( MARGIN ); + axisTabLayout->setSpacing( SPACING ); + + axisTabLayout->addWidget( modeBox , 0, 0, 1, 3 ); + axisTabLayout->addWidget( myInsertBtn , 1, 0, 1, 2 ); + axisTabLayout->addWidget( myDeleteBtn , 2, 0, 1, 2 ); + axisTabLayout->addWidget( myStepLabel, 3, 0 ); + axisTabLayout->addWidget( myStepSpin , 3, 1 ); + axisTabLayout->addWidget( csFrame , 1, 2, 4, 1 ); + + axisTabLayout->setRowStretch( 4, 1 ); + + // signals + connect( myInsertBtn, SIGNAL( clicked() ), SLOT( onInsert() )); + connect( myDeleteBtn, SIGNAL( clicked() ), SLOT( onDelete() )); + connect( myModeGroup, SIGNAL( buttonClicked ( int )), SLOT( onMode(int))); + connect( mySpacingTreeWdg, SIGNAL( itemSelectionChanged()), SLOT( updateButtons() )); + connect( myCoordList, SIGNAL( itemSelectionChanged()), SLOT( updateButtons() )); + connect( myStepSpin, SIGNAL( valueChanged(double)), SLOT( onStepChange() )); + } + + void GridAxisTab::onInsert() + { + if ( isGridBySpacing() ) + { + QTreeWidgetItem * item = mySpacingTreeWdg->currentItem(); + if ( !item ) item = mySpacingTreeWdg->topLevelItem( 0 ); + int i = mySpacingTreeWdg->indexOfTopLevelItem( item ); + + double t0, t1; QString fun; + getFromItem( item, t0, t1, fun ); + double t = 0.5 * ( t0 + t1 ); + setToItem( t0, t, fun, item ); + + item = setToItem( t, t1, fun ); + if ( i == mySpacingTreeWdg->topLevelItemCount()-1 ) + mySpacingTreeWdg->addTopLevelItem( item ); + else + mySpacingTreeWdg->insertTopLevelItem( i+1, item ); + mySpacingTreeWdg->setCurrentItem( item ); + } + else + { + if ( myCoordList->count() == 0 ) + { + myCoordList->addItem( coordToItem( 0 )); + } + else + { + double coord = coordFromItem( myCoordList->currentItem() ) + myStep; + int i = myCoordList->currentRow(); + while ( i > 0 && coordFromItem( myCoordList->item( i-1 )) > coord ) + --i; + while ( i < myCoordList->count() && coordFromItem( myCoordList->item( i )) < coord ) + ++i; + const double tol = 1e-6; + const bool isSame = + ( i < myCoordList->count() && coordFromItem( myCoordList->item( i )) - coord < tol ) || + ( i > 0 && coord - coordFromItem( myCoordList->item( i-1 )) < tol ); + if ( !isSame ) + myCoordList->insertItem( i, coordToItem( coord )); + else if ( myStep < 0 ) + --i; + myCoordList->setCurrentRow( i ); + } + } + updateButtons(); + } + + void GridAxisTab::onDelete() + { + if ( isGridBySpacing() ) + { + QList selItems = mySpacingTreeWdg->selectedItems(); + QTreeWidgetItem * item; + foreach ( item, selItems ) + { + int i = mySpacingTreeWdg->indexOfTopLevelItem( item ); + if ( i == 0 ) continue; + QTreeWidgetItem* prevItem = mySpacingTreeWdg->topLevelItem( i-1 ); + + double t0, t1, t2; QString fun; + getFromItem( item, t1, t2, fun ); + getFromItem( prevItem, t0, t1, fun ); + delete item; + + setToItem( t0, t2, fun, prevItem ); + } + } + else + { + if ( myCoordList->count() > 2 ) + if ( QListWidgetItem * item = myCoordList->currentItem() ) + delete item; + } + updateButtons(); + } + + void GridAxisTab::onMode(int isSpacing) + { + mySpacingTreeWdg->setShown( isSpacing ); + myCoordList->setShown( !isSpacing ); + myStepSpin->setShown( !isSpacing ); + myStepLabel->setShown( !isSpacing ); + if ( isSpacing ) + { + if ( mySpacingTreeWdg->topLevelItemCount() == 0 ) + { + QString spacing( "1" ); + if ( myCoordList->count() > 1 ) + { + double c1 = coordFromItem( myCoordList->item( 1 )); + double c0 = coordFromItem( myCoordList->item( 0 )); + spacing = QString::number( c1 - c0 ); + } + mySpacingTreeWdg->addTopLevelItem( setToItem( 0., 1., spacing ) ); + } + //myCoordList->clear(); + } + else + { + //mySpacingTreeWdg->clear(); + if ( myCoordList->count() == 0 ) + myCoordList->addItem( coordToItem( 0 )); + } + updateButtons(); + } + + void GridAxisTab::onStepChange() + { + if ( fabs( myStepSpin->GetValue() ) < 1e-100 ) + { + double delta = myStepSpin->singleStep() * ( myStep > myStepSpin->GetValue() ? -1 : +1 ); + myStepSpin->SetValue( myStepSpin->GetValue() + delta ); + } + myStep = myStepSpin->GetValue(); + } + + void GridAxisTab::updateButtons() + { + bool insertEnable = false, deleteEnable = false; + if ( isGridBySpacing() ) + { + insertEnable = true; + const int nbSelected = mySpacingTreeWdg->selectedItems().count(); + if ( nbSelected > 0 ) + { + // we delete a current range by uniting it with the previous + int i = mySpacingTreeWdg->indexOfTopLevelItem( mySpacingTreeWdg->currentItem() ); + deleteEnable = ( i > 0 ); + } + } + else + { + const int nbSelected = myCoordList->selectedItems().count(); + insertEnable = ( nbSelected || myCoordList->count() < 2 ); + deleteEnable = ( nbSelected && myCoordList->count() > 2 ); + } + myInsertBtn->setEnabled( insertEnable ); + myDeleteBtn->setEnabled( deleteEnable ); + } + + void GridAxisTab::setCoordinates( SMESH::double_array_var coords ) + { + myCoordList->clear(); + for ( size_t i = 0; i < coords->length(); ++i ) + myCoordList->addItem( coordToItem( coords[i] )); + + myModeGroup->button( COORD_BUT )->setChecked( true ); + onMode( COORD_BUT ); + } + + void GridAxisTab::setSpacing( SMESH::string_array_var funs, SMESH::double_array_var points ) + { + mySpacingTreeWdg->clear(); + if ( funs->length() == points->length() - 1 ) + { + for ( size_t i = 1; i < points->length(); ++i ) + mySpacingTreeWdg->addTopLevelItem + ( setToItem( points[i-1], points[i], (const char*) funs[i-1] )); + } + myModeGroup->button( SPACING_BUT )->setChecked( true ); + onMode( SPACING_BUT ); + } + + bool GridAxisTab::isGridBySpacing() const + { + return ( myModeGroup->checkedId() == SPACING_BUT ); + } + + SMESH::double_array* GridAxisTab::getCoordinates() + { + SMESH::double_array_var coords = new SMESH::double_array; + coords->length( myCoordList->count() ); + for ( size_t i = 0; i < coords->length(); ++i ) + coords[i] = coordFromItem( myCoordList->item( i ) ); + + return coords._retn(); + } + + void GridAxisTab::getSpacing(SMESH::string_array_out funs, + SMESH::double_array_out points) const + { + funs = new SMESH::string_array(); + points = new SMESH::double_array(); + funs->length( mySpacingTreeWdg->topLevelItemCount() ); + points->length( mySpacingTreeWdg->topLevelItemCount() + 1 ); + double t0, t1; QString fun; + for ( size_t i = 0; i < funs->length(); ++i ) + { + QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( i ); + getFromItem( item, t0, t1, fun ); + points[i] = t0; + funs[i] = fun.toLatin1().constData(); + } + points[ points->length()-1 ] = 1.0; + } + + + bool GridAxisTab::checkParams(QString& msg, SMESH::SMESH_Hypothesis_var& hyp) const + { + if ( isGridBySpacing() ) + { + if ( mySpacingTreeWdg->topLevelItemCount() == 0 ) + return false; // how could it be? + StdMeshers::StdMeshers_CartesianParameters3D_var h = + StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hyp ); + SMESH::string_array_var funs; + SMESH::double_array_var points; + getSpacing( funs.out(), points.out() ); + try { + const char* axisName[3] = { "X", "Y", "Z" }; + SMESH::double_array_var coords = + h->ComputeCoordinates(0.,1., funs, points, axisName[ myAxisIndex ]); + } + catch ( const SALOME::SALOME_Exception& ex ) { + msg = (const char*) ex.details.text; + return false; + } + } + else + { + return myCoordList->count() > 1; + } + return true; + } + + LineDelegate::LineDelegate( QWidget* parent ): + QItemDelegate( parent ), + mySpacingTreeWdg( qobject_cast( parent )), + myCoordList( qobject_cast( parent )) + { + } + + QWidget* LineDelegate::createEditor( QWidget* parent, + const QStyleOptionViewItem& opt, + const QModelIndex& index) const + { + QWidget* w = 0; + if ( mySpacingTreeWdg ) + { + if ( index.column() == 0 && + index.row() != mySpacingTreeWdg->topLevelItemCount()-1 ) + { + SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent ); + sb->setAcceptNames( false ); // No Notebook variables allowed + sb->setFrame( false ); + w = sb; + } + if ( index.column() == 1 ) { + w = new QLineEdit( parent ); + } + } + else + { + SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent ); + sb->setAcceptNames( false ); // No Notebook variables allowed + sb->setFrame( false ); + const double tol = 1e-5; + double from = index.row() ? coordFromItem( myCoordList->item( index.row()-1 ))+tol : -1e+6; + double to = index.row() == myCoordList->count()-1 ? 1e+6 : coordFromItem( myCoordList->item( index.row()+1 ))-tol; + sb->RangeStepAndValidator( from, to, 0.01 ); + w = sb; + } + return w; + } + + void LineDelegate::setEditorData ( QWidget * editor, const QModelIndex & index ) const + { + if ( mySpacingTreeWdg && index.column() == 0 ) + { + double t0, t1, t2=1.0; QString fun; + QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( index.row() ); + getFromItem( item, t0, t1, fun ); + if ( index.row() != mySpacingTreeWdg->topLevelItemCount()-1 ) + { + item = mySpacingTreeWdg->topLevelItem( index.row()+1 ); + getFromItem( item, t1, t2, fun ); + } + const double tol = 1e-3; + SMESHGUI_SpinBox* sb = qobject_cast( editor ); + sb->RangeStepAndValidator( t0 + tol, t2 - tol, 0.01 ); + sb->SetValue( t1 ); + } + else + { + QItemDelegate::setEditorData( editor, index ); + } + } + void LineDelegate::setModelData( QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index ) const + { + if ( mySpacingTreeWdg ) + { + if ( index.column() == 0 ) + { + if ( index.row() != mySpacingTreeWdg->topLevelItemCount()-1 ) + { + SMESHGUI_SpinBox* sb = qobject_cast( editor ); + double t0, t1, t = sb->GetValue(); QString fun; + + QTreeWidgetItem* item = mySpacingTreeWdg->topLevelItem( index.row() ); + getFromItem( item, t0, t1, fun ); + setToItem( t0, t, fun, item ); + + item = mySpacingTreeWdg->topLevelItem( index.row() + 1 ); + getFromItem( item, t0, t1, fun ); + setToItem( t, t1, fun, item ); + } + } + else if ( !qobject_cast(editor)->text().trimmed().isEmpty() ) + { + QItemDelegate::setModelData( editor, model, index ); + } + } + else + { + SMESHGUI_SpinBox* sb = qobject_cast( editor ); + coordToItem( sb->GetValue(), myCoordList->item( index.row() )); + } + } + +} // namespace StdMeshersGUI + + +StdMeshersGUI_CartesianParamCreator::StdMeshersGUI_CartesianParamCreator(const QString& aHypType) + : StdMeshersGUI_StdHypothesisCreator( aHypType ), + myThreshold( 0 ) +{ + myAxisTabs[0] = 0; + myAxisTabs[1] = 0; + myAxisTabs[2] = 0; +} + +StdMeshersGUI_CartesianParamCreator::~StdMeshersGUI_CartesianParamCreator() +{ + if ( myAxisTabs[0] ) delete myAxisTabs[0]; + if ( myAxisTabs[1] ) delete myAxisTabs[1]; + if ( myAxisTabs[2] ) delete myAxisTabs[2]; + myAxisTabs[0] = 0; + myAxisTabs[1] = 0; + myAxisTabs[2] = 0; +} + +bool StdMeshersGUI_CartesianParamCreator::checkParams( QString& msg ) const +{ + if( !SMESHGUI_GenericHypothesisCreator::checkParams( msg ) ) + return false; + + if ( myName && myName->text().trimmed().isEmpty() ) + { + msg = tr("SMESH_WRN_EMPTY_NAME"); + return false; + } + if ( ! myThreshold->isValid( msg, true )) + return false; + + SMESH::SMESH_Hypothesis_var hyp = hypothesis(); + if ( !myAxisTabs[0]->checkParams( msg, hyp )) return false; + if ( !myAxisTabs[1]->checkParams( msg, hyp )) return false; + if ( !myAxisTabs[2]->checkParams( msg, hyp )) return false; + + return true; +} + +QFrame* StdMeshersGUI_CartesianParamCreator::buildFrame() +{ + QFrame* fr = new QFrame(); + //fr->setMinimumWidth(460); + + QVBoxLayout* lay = new QVBoxLayout( fr ); + lay->setMargin( 0 ); + lay->setSpacing( SPACING ); + + QGroupBox* GroupC1 = new QGroupBox( tr( "SMESH_ARGUMENTS" ), fr ); + lay->addWidget( GroupC1 ); + + StdMeshers::StdMeshers_NumberOfSegments_var h = + StdMeshers::StdMeshers_NumberOfSegments::_narrow( hypothesis() ); + + QGridLayout* argGroupLayout = new QGridLayout( GroupC1 ); + argGroupLayout->setSpacing( SPACING ); + argGroupLayout->setMargin( MARGIN ); + argGroupLayout->setColumnStretch( 0, 0 ); + argGroupLayout->setColumnStretch( 1, 1 ); + + int row = 0; + // 0) name + myName = 0; + if( isCreation() ) + { + myName = new QLineEdit( GroupC1 ); + argGroupLayout->addWidget( new QLabel( tr( "SMESH_NAME" ), GroupC1 ), row, 0 ); + argGroupLayout->addWidget( myName, row, 1 ); + row++; + } + + // 1) threshold + argGroupLayout->addWidget( new QLabel( tr( "THRESHOLD" ), GroupC1 ), row, 0 ); + myThreshold = new SMESHGUI_SpinBox( GroupC1 ); + myThreshold->setAcceptNames( false ); // No Notebook variables allowed + myThreshold->RangeStepAndValidator( 1.1, 1e+10, 1., "length_precision" ); + argGroupLayout->addWidget( myThreshold, row, 1 ); + row++; + + // 2) Grid definition + QTabWidget* tabWdg = new QTabWidget( fr ); + myAxisTabs[ 0 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 0 ); + myAxisTabs[ 1 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 1 ); + myAxisTabs[ 2 ] = new StdMeshersGUI::GridAxisTab( tabWdg, 2 ); + tabWdg->addTab( myAxisTabs[ 0 ], tr( "AXIS_X" ) ); + tabWdg->addTab( myAxisTabs[ 1 ], tr( "AXIS_Y" ) ); + tabWdg->addTab( myAxisTabs[ 2 ], tr( "AXIS_Z" ) ); + argGroupLayout->addWidget( tabWdg, row, 0, 1, 2 ); + + return fr; +} + +void StdMeshersGUI_CartesianParamCreator::retrieveParams() const +{ + StdMeshers::StdMeshers_CartesianParameters3D_var h = + StdMeshers::StdMeshers_CartesianParameters3D::_narrow( initParamsHypothesis() ); + + if( myName ) + myName->setText( hypName() ); + + QString varName = getVariableName( "SetSizeThreshold" ); + if ( varName.isEmpty() ) + myThreshold->setValue( h->GetSizeThreshold() ); + else + myThreshold->setText( varName ); + + for ( int ax = 0; ax < 3; ++ax ) + { + if ( h->IsGridBySpacing( ax )) + { + SMESH::string_array_var funs; + SMESH::double_array_var intPoints; + h->GetGridSpacing( funs.out(), intPoints.out(), ax ); + myAxisTabs[ax]->setSpacing( funs, intPoints ); + } + else + { + SMESH::double_array_var coords = h->GetGrid( ax ); + myAxisTabs[ax]->setCoordinates( coords ); + } + } + if ( dlg() ) + dlg()->setMinimumSize( dlg()->minimumSizeHint().width(), dlg()->minimumSizeHint().height() ); +} + +QString StdMeshersGUI_CartesianParamCreator::storeParams() const +{ + StdMeshers::StdMeshers_CartesianParameters3D_var h = + StdMeshers::StdMeshers_CartesianParameters3D::_narrow( hypothesis() ); + + try + { + if( isCreation() ) + SMESH::SetName( SMESH::FindSObject( h ), myName->text().toLatin1().constData() ); + + h->SetVarParameter( myThreshold->text().toLatin1().constData(), "SetSizeThreshold" ); + h->SetSizeThreshold( myThreshold->text().toDouble() ); + + for ( int ax = 0; ax < 3; ++ax ) + { + if ( myAxisTabs[ax]->isGridBySpacing()) + { + SMESH::double_array_var intPoints; + SMESH::string_array_var funs; + myAxisTabs[ax]->getSpacing( funs.out(), intPoints.out() ); + h->SetGridSpacing( funs, intPoints, ax ); + } + else + { + SMESH::double_array_var coords = myAxisTabs[ax]->getCoordinates(); + h->SetGrid( coords, ax ); + } + } + } + catch(const SALOME::SALOME_Exception& ex) + { + SalomeApp_Tools::QtCatchCorbaException(ex); + } + return ""; +} + +QString StdMeshersGUI_CartesianParamCreator::helpPage() const +{ + return "cartesian_algo_page.html#cartesian_hyp_anchor"; +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h new file mode 100644 index 000000000..d8cddb6a8 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_CartesianParamCreator.h @@ -0,0 +1,142 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshersGUI_CartesianParamCreator.h +// Author : Open CASCADE S.A.S. +// +#ifndef STDMESHERSGUI_CartesianParamCreator_H +#define STDMESHERSGUI_CartesianParamCreator_H + +// SMESH includes +#include "SMESH_StdMeshersGUI.hxx" + +#include "StdMeshersGUI_StdHypothesisCreator.h" + +// IDL includes +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_Hypothesis) + +#include +#include + +class SMESHGUI_SpinBox; +class QLineEdit; +class QButtonGroup; +class QTreeWidgetItem; +class QString; +class QWidget; +class QTreeWidget; +class QListWidget; +class QStyleOptionViewItem; +class QModelIndex; +class QAbstractItemModel; +class QListWidgetItem; + +namespace StdMeshersGUI +{ + void getFromItem(QTreeWidgetItem * item, double& t0, double& t1, QString& fun ); + QTreeWidgetItem* setToItem (double t0, double t1, const QString& fun, QTreeWidgetItem* item=0); + + double coordFromItem( QListWidgetItem * ); + QListWidgetItem* coordToItem( double coord, QListWidgetItem * item=0); + + /*! + * \brief Widget defining the grid in one direction + */ + class GridAxisTab : public QFrame + { + Q_OBJECT + public: + GridAxisTab( QWidget* parent, const int axisIndex ); + ~GridAxisTab() {} + + void setCoordinates( SMESH::double_array_var coords ); + void setSpacing( SMESH::string_array_var funs, SMESH::double_array_var points ); + + bool checkParams(QString& msg, SMESH::SMESH_Hypothesis_var& hyp) const; + bool isGridBySpacing() const; + SMESH::double_array* getCoordinates(); + void getSpacing(SMESH::string_array_out funs, SMESH::double_array_out points) const; + + private slots: + void onInsert(); + void onDelete(); + void onMode(int); + void onStepChange(); + void updateButtons(); + + private: + + int myAxisIndex; + QButtonGroup* myModeGroup; + QTreeWidget* mySpacingTreeWdg; + QListWidget* myCoordList; + QPushButton* myInsertBtn; + QPushButton* myDeleteBtn; + SMESHGUI_SpinBox* myStepSpin; + QLabel* myStepLabel; + double myStep; + }; + /* + * \brief : Custom item delegate + */ + class LineDelegate : public QItemDelegate + { + Q_OBJECT + public: + LineDelegate( QWidget* parent ); + ~LineDelegate() {} + + QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const; + void setEditorData ( QWidget * editor, const QModelIndex & index ) const; + void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const; + + private: + QTreeWidget* mySpacingTreeWdg; + QListWidget* myCoordList; + }; +} + +class STDMESHERSGUI_EXPORT StdMeshersGUI_CartesianParamCreator : public StdMeshersGUI_StdHypothesisCreator +{ + Q_OBJECT + +public: + StdMeshersGUI_CartesianParamCreator( const QString& aHypType ); + virtual ~StdMeshersGUI_CartesianParamCreator(); + + virtual bool checkParams( QString& ) const; + virtual QString helpPage() const; + +protected: + virtual QFrame* buildFrame(); + virtual void retrieveParams() const; + virtual QString storeParams() const; + +private: + QLineEdit* myName; + SMESHGUI_SpinBox* myThreshold; + StdMeshersGUI::GridAxisTab* myAxisTabs[3]; +}; + +#endif // STDMESHERSGUI_CartesianParamCreator_H diff --git a/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.cxx b/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.cxx index ead8d1c3d..4d9bd5111 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_DistrPreview.cxx // Author : Open CASCADE S.A.S. // SMESH includes @@ -49,6 +50,10 @@ #ifdef WIN32 # include #endif +#include +#include + +#include StdMeshersGUI_DistrPreview::StdMeshersGUI_DistrPreview( QWidget* p, StdMeshers::StdMeshers_NumberOfSegments_ptr h ) : QwtPlot( p ), @@ -60,6 +65,7 @@ StdMeshersGUI_DistrPreview::StdMeshersGUI_DistrPreview( QWidget* p, StdMeshers:: myIsDone( true ), myNbSeg( 1 ) { + Kernel_Utils::Localizer loc; myHypo = StdMeshers::StdMeshers_NumberOfSegments::_duplicate( h ); myVars.ChangeValue( 1 ) = new Expr_NamedUnknown( "t" ); myDensity = new QwtPlotCurve( QString() ); @@ -72,7 +78,7 @@ StdMeshersGUI_DistrPreview::StdMeshersGUI_DistrPreview( QWidget* p, StdMeshers:: QwtText mt = myMsg->label(); mt.setBackgroundPen( QPen( Qt::red, 1 ) ); QFont f = mt.font(); - f.setPointSize( 14 ); f.setBold( true ); + f.setPointSize( 14 ); //f.setBold( true ); mt.setFont( f ); myMsg->setLabel( mt ); myDensity->setPen( QPen( Qt::red, 1 ) ); @@ -88,6 +94,17 @@ StdMeshersGUI_DistrPreview::StdMeshersGUI_DistrPreview( QWidget* p, StdMeshers:: } insertLegend( l, QwtPlot::BottomLegend ); + enableAxis(QwtPlot::yLeft, false); + enableAxis(QwtPlot::yRight, true); + + QFont axisFont; + axisFont.setPointSize( 8 ); + setAxisFont(QwtPlot::yRight, axisFont); + setAxisFont(QwtPlot::xBottom, axisFont); + + myDensity->setYAxis(QwtPlot::yRight); + myDistr->setYAxis(QwtPlot::yRight); + myMsg->setYAxis(QwtPlot::yRight); myDensity->setTitle( tr( "SMESH_DENSITY_FUNC" ) ); myDistr->setTitle( tr( "SMESH_DISTR" ) ); @@ -200,6 +217,7 @@ bool StdMeshersGUI_DistrPreview::createTable( SMESH::double_array& func ) void StdMeshersGUI_DistrPreview::update() { + Kernel_Utils::Localizer loc; SMESH::double_array graph, distr; if( isTableFunc() ) { @@ -218,13 +236,13 @@ void StdMeshersGUI_DistrPreview::update() { SMESH::double_array* arr = 0; if( isTableFunc() ) - arr = h->BuildDistributionTab( myTableFunc, myNbSeg, ( int )myConv ); + arr = h->BuildDistributionTab( myTableFunc, myNbSeg, ( int )myConv ); else - arr = h->BuildDistributionExpr( myFunction.toLatin1().data(), myNbSeg, ( int )myConv ); + arr = h->BuildDistributionExpr( myFunction.toLatin1().data(), myNbSeg, ( int )myConv ); if( arr ) { - distr = *arr; - delete arr; + distr = *arr; + delete arr; } } } @@ -257,6 +275,15 @@ void StdMeshersGUI_DistrPreview::update() showError(); return; } +#ifdef WIN32 + if ( std::fabs(y[i]) >= HUGE_VAL) + y[i] = HUGE_VAL/100.; +#else + if ( isinf(y[i])) + y[i] = std::numeric_limits::max()/100.; +#endif +// if ( y[i] > 1e3 ) +// y[i] = 1e3; if( i==0 || y[i]max_y ) @@ -333,7 +360,7 @@ bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr ) if( !name.IsNull() ) { if( name->GetName()!="t" ) - res = false; + res = false; } else res = isCorrectArg( sub ); @@ -343,6 +370,7 @@ bool isCorrectArg( const Handle( Expr_GeneralExpression )& expr ) bool StdMeshersGUI_DistrPreview::init( const QString& str ) { + Kernel_Utils::Localizer loc; bool parsed_ok = true; try { #ifdef NO_CAS_CATCH @@ -416,15 +444,15 @@ bool StdMeshersGUI_DistrPreview::convert( double& v ) const #ifdef NO_CAS_CATCH OCC_CATCH_SIGNALS; #endif - // in StdMeshers_NumberOfSegments.cc - // const double PRECISION = 1e-7; - // - if(v < -7) v = -7.0; - v = pow( 10.0, v ); + // in StdMeshers_NumberOfSegments.cc + // const double PRECISION = 1e-7; + // + if(v < -7) v = -7.0; + v = pow( 10.0, v ); } catch(Standard_Failure) { - Handle(Standard_Failure) aFail = Standard_Failure::Caught(); - v = 0.0; - ok = false; + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + v = 0.0; + ok = false; } } break; diff --git a/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.h b/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.h index 87a9eb86e..8abcc3e26 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.h +++ b/src/StdMeshersGUI/StdMeshersGUI_DistrPreview.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_DistrPreview.h // Author : Open CASCADE S.A.S. // diff --git a/src/StdMeshersGUI/StdMeshersGUI_DistrTable.cxx b/src/StdMeshersGUI/StdMeshersGUI_DistrTable.cxx index cc718ed66..dbd9a4d8b 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_DistrTable.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_DistrTable.cxx @@ -1,34 +1,37 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_DistrTable.cxx // Author : Open CASCADE S.A.S. // SMESH includes // #include "StdMeshersGUI_DistrTable.h" +#include + // Qt incldues #include #include -#include +#include #include #include #include @@ -84,14 +87,14 @@ public: ~SpinBoxDelegate(); QWidget* createEditor( QWidget*, - const QStyleOptionViewItem&, - const QModelIndex& ) const; + const QStyleOptionViewItem&, + const QModelIndex& ) const; void setEditorData( QWidget*, const QModelIndex&) const; void setModelData( QWidget*, QAbstractItemModel*, - const QModelIndex& ) const; + const QModelIndex& ) const; void updateEditorGeometry( QWidget*, - const QStyleOptionViewItem&, - const QModelIndex& ) const; + const QStyleOptionViewItem&, + const QModelIndex& ) const; private: StdMeshersGUI_DistrTableFrame::Table* myTable; @@ -109,7 +112,7 @@ private: struct EditorData { int r, c; - QDoubleSpinBox* sb; + SMESHGUI_SpinBox* sb; EditorData() { reset(); } void reset() { r = -1; c = -1; sb = 0; } }; @@ -138,7 +141,7 @@ public: void addRow(); void deleteRow(); - void setEditor( int, int, QDoubleSpinBox* ); + void setEditor( int, int, SMESHGUI_SpinBox* ); protected: void closeEditor( QWidget*, QAbstractItemDelegate::EndEditHint ); @@ -173,21 +176,25 @@ StdMeshersGUI_DistrTableFrame::SpinBoxDelegate:: QWidget* StdMeshersGUI_DistrTableFrame::SpinBoxDelegate:: createEditor( QWidget* parent, - const QStyleOptionViewItem& /*option*/, - const QModelIndex& index ) const + const QStyleOptionViewItem& /*option*/, + const QModelIndex& index ) const { - QDoubleSpinBox* sb = new QDoubleSpinBox( parent ); + SMESHGUI_SpinBox* sb = new SMESHGUI_SpinBox( parent ); + + sb->setAcceptNames(false); // No Notebook variables allowed + double aMin = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? + myTable->argMinimum( index.row() ) : + myTable->funcMinimum( index.row() ); + double aMax = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? + myTable->argMaximum( index.row() ) : + myTable->funcMaximum( index.row() ); + double aStep = index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? + myTable->argStep( index.row() ) : + myTable->funcStep( index.row() ); + sb->RangeStepAndValidator( aMin, aMax, aStep, "parametric_precision" ); sb->setFrame(false); - sb->setMinimum( index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? - myTable->argMinimum( index.row() ) : - myTable->funcMinimum( index.row() ) ); - sb->setMaximum( index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? - myTable->argMaximum( index.row() ) : - myTable->funcMaximum( index.row() ) ); - sb->setSingleStep( index.column() == StdMeshersGUI_DistrTableFrame::ArgColumn ? - myTable->argStep( index.row() ) : - myTable->funcStep( index.row() ) ); - myTable->setEditor( index.row(), index.column(), sb ); + + myTable->setEditor( index.row(), index.column(), sb ); return sb; } @@ -196,7 +203,7 @@ StdMeshersGUI_DistrTableFrame::SpinBoxDelegate:: setEditorData( QWidget* editor, const QModelIndex& index ) const { QString value = index.model()->data(index, Qt::DisplayRole).toString(); - QDoubleSpinBox* sb = static_cast(editor); + SMESHGUI_SpinBox* sb = static_cast(editor); bool bOk = false; double v = value.toDouble( &bOk ); @@ -208,17 +215,17 @@ setEditorData( QWidget* editor, const QModelIndex& index ) const void StdMeshersGUI_DistrTableFrame::SpinBoxDelegate:: setModelData( QWidget* editor, QAbstractItemModel* model, - const QModelIndex& index ) const + const QModelIndex& index ) const { - QDoubleSpinBox* sb = static_cast(editor); + SMESHGUI_SpinBox* sb = static_cast(editor); model->setData( index, QString::number( sb->value() ), Qt::DisplayRole ); } void StdMeshersGUI_DistrTableFrame::SpinBoxDelegate:: updateEditorGeometry( QWidget* editor, - const QStyleOptionViewItem& option, - const QModelIndex& /*index*/ ) const + const QStyleOptionViewItem& option, + const QModelIndex& /*index*/ ) const { editor->setGeometry( option.rect ); } @@ -238,6 +245,8 @@ Table( QWidget* parent, int rows ) QStringList labs; labs << "t" << "f(t)"; setHorizontalHeaderLabels( labs ); + this->horizontalHeader()->setStretchLastSection(true); + this->horizontalHeader()->setDefaultSectionSize(60); while( rows-- ) addRow(); @@ -247,7 +256,7 @@ Table( QWidget* parent, int rows ) void StdMeshersGUI_DistrTableFrame::Table:: -setEditor( int r, int c, QDoubleSpinBox* sb ) +setEditor( int r, int c, SMESHGUI_SpinBox* sb ) { myEditorData.r = r; myEditorData.c = c; @@ -405,15 +414,15 @@ sizeHint() const { if( cachedSizeHint().isValid() ) return cachedSizeHint(); - - QSize sh = QTableWidget::sizeHint(); - if( sh.width() < 400 ) - sh.setWidth( 400 ); - if( sh.height() < 200 ) - sh.setHeight( 200 ); - - setCachedSizeHint( sh ); - return sh; + return QTableWidget::sizeHint(); +// QSize sh = QTableWidget::sizeHint(); +// if( sh.width() < 400 ) +// sh.setWidth( 400 ); +// if( sh.height() < 200 ) +// sh.setHeight( 200 ); +// +// setCachedSizeHint( sh ); +// return sh; } void @@ -501,38 +510,32 @@ StdMeshersGUI_DistrTableFrame:: StdMeshersGUI_DistrTableFrame( QWidget* parent ) : QWidget( parent ) { - QVBoxLayout* main = new QVBoxLayout( this ); + QGridLayout* main = new QGridLayout( this ); main->setMargin( 0 ); main->setSpacing( 0 ); // --- myTable = new Table( this ); connect( myTable, SIGNAL( valueChanged( int, int ) ), this, SIGNAL( valueChanged( int, int ) ) ); - - // --- - QWidget* aButFrame = new QWidget( this ); - QHBoxLayout* butLay = new QHBoxLayout( aButFrame ); - butLay->setContentsMargins( 0, SPACING, 0, SPACING ); - butLay->setSpacing( SPACING ); - myButtons[ InsertRowBtn ] = new QPushButton( tr( "SMESH_INSERT_ROW" ), aButFrame ); - myButtons[ RemoveRowBtn ] = new QPushButton( tr( "SMESH_REMOVE_ROW" ), aButFrame ); + myButtons[ InsertRowBtn ] = new QPushButton( tr( "SMESH_INSERT_ROW" ), this ); + myButtons[ RemoveRowBtn ] = new QPushButton( tr( "SMESH_REMOVE_ROW" ), this ); - butLay->addWidget( myButtons[ InsertRowBtn ] ); - butLay->addWidget( myButtons[ RemoveRowBtn ] ); - butLay->addStretch(); // --- - main->addWidget( myTable ); - main->addWidget( aButFrame ); + main->addWidget( myTable , 0, 0, 1, 3); + main->addWidget( myButtons[ InsertRowBtn ] , 1, 0); + main->addWidget( myButtons[ RemoveRowBtn ] , 1, 1); + main->setColumnStretch(2, 1); + main->setSpacing( SPACING ); // --- connect( myButtons[ InsertRowBtn ], SIGNAL( clicked() ), this, SLOT( onInsert() ) ); connect( myButtons[ RemoveRowBtn ], SIGNAL( clicked() ), this, SLOT( onRemove() ) ); connect( myTable, SIGNAL( currentCellChanged( int, int, int, int ) ), - this, SIGNAL( currentChanged( int, int ) ) ); + this, SIGNAL( currentChanged( int, int ) ) ); connect( myTable, SIGNAL( cellChanged( int, int ) ), - this, SIGNAL( valueChanged( int, int ) ) ); + this, SIGNAL( valueChanged( int, int ) ) ); } StdMeshersGUI_DistrTableFrame:: diff --git a/src/StdMeshersGUI/StdMeshersGUI_DistrTable.h b/src/StdMeshersGUI/StdMeshersGUI_DistrTable.h index 11a4ab333..9ba227c3a 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_DistrTable.h +++ b/src/StdMeshersGUI/StdMeshersGUI_DistrTable.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_DistrTable.h // Author : Open CASCADE S.A.S. // diff --git a/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.cxx b/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.cxx new file mode 100644 index 000000000..13b575e85 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.cxx @@ -0,0 +1,394 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshersGUI_FixedPointsParamWdg.cxx +// Author : Open CASCADE S.A.S. +// SMESH includes +// +#include "StdMeshersGUI_FixedPointsParamWdg.h" + +#include + +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPACING 6 +#define MARGIN 0 +#define SAME_TEXT "-/-" + +#define TOLERANCE 1e-7 +#define EQUAL_DBL(a,b) (fabs(a-b)b)&&!EQUAL_DBL(a,b)) + +/* + * class : Tree Widget Item Delegate + * purpose : Custom item delegate + */ + +class StdMeshersGUI_FixedPointsParamWdg::LineDelegate : public QItemDelegate +{ +public: + LineDelegate( QTreeWidget* ); + ~LineDelegate() {} + + QWidget* createEditor( QWidget*, const QStyleOptionViewItem&, const QModelIndex& ) const; + void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const; + +private: + QTreeWidget* myTreeWidget; +}; + +StdMeshersGUI_FixedPointsParamWdg::LineDelegate::LineDelegate( QTreeWidget* parent ) + : QItemDelegate( parent ), + myTreeWidget( parent ) +{ +} + +QWidget* StdMeshersGUI_FixedPointsParamWdg::LineDelegate::createEditor( QWidget* parent, + const QStyleOptionViewItem& option, + const QModelIndex& index ) const +{ + QWidget* w = 0; + if ( (index.column() == 1 ) ) { + SalomeApp_IntSpinBox* sb = new SalomeApp_IntSpinBox( parent ); + sb->setAcceptNames( false ); // No Notebook variables allowed + sb->setFrame( false ); + sb->setRange( 1, 999); + w = sb; + } + + return w; +} + +void StdMeshersGUI_FixedPointsParamWdg::LineDelegate::setModelData( QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index ) const +{ + model->setData( index, qobject_cast( editor )->value(), Qt::EditRole ); + model->setData( index, qobject_cast( editor )->value(), Qt::UserRole ); +} + +//================================================================================ +/*! + * Constructor + */ +//================================================================================ + +StdMeshersGUI_FixedPointsParamWdg +::StdMeshersGUI_FixedPointsParamWdg( QWidget * parent ): + QWidget( parent ) +{ + QGridLayout* edgesLayout = new QGridLayout( this ); + edgesLayout->setMargin( MARGIN ); + edgesLayout->setSpacing( SPACING ); + + myListWidget = new QListWidget( this ); + myTreeWidget = new QTreeWidget( this ); + mySpinBox = new SMESHGUI_SpinBox( this ); + myAddButton = new QPushButton( tr( "SMESH_BUT_ADD" ), this ); + myRemoveButton = new QPushButton( tr( "SMESH_BUT_REMOVE" ), this ); + mySameValues = new QCheckBox( tr("SMESH_SAME_NB_SEGMENTS"), this); + + myListWidget->setSelectionMode( QListWidget::ExtendedSelection ); + + myTreeWidget->setColumnCount(2); + myTreeWidget->setHeaderLabels( QStringList() << tr( "SMESH_RANGE" ) << tr( "SMESH_NB_SEGMENTS" ) ); + myTreeWidget->setColumnWidth( 1, 40 ); + myTreeWidget->setColumnWidth( 2, 30 ); + myTreeWidget->setItemDelegate( new LineDelegate( myTreeWidget ) ); + + edgesLayout->addWidget(myListWidget, 0, 0, 4, 1); + edgesLayout->addWidget(mySpinBox, 0, 1); + edgesLayout->addWidget(myAddButton, 1, 1); + edgesLayout->addWidget(myRemoveButton, 2, 1); + edgesLayout->addWidget(myTreeWidget, 0, 2, 4, 1); + edgesLayout->addWidget(mySameValues, 4, 0, 1, 3); + edgesLayout->setRowStretch( 3, 5 ); + edgesLayout->setColumnStretch(0, 1); + edgesLayout->setColumnStretch(1, 0); + edgesLayout->setColumnStretch(2, 2); + + myListWidget->setMinimumWidth( 80 ); + myTreeWidget->setMinimumWidth( 200 ); + + mySpinBox->setAcceptNames( false ); // No Notebook variables allowed + mySpinBox->RangeStepAndValidator( 0., 1., .1, "parametric_precision" ); + myListWidget->setMinimumWidth( 70 ); + + connect( myAddButton, SIGNAL( clicked() ), SLOT( onAdd() ) ); + connect( myRemoveButton, SIGNAL( clicked() ), SLOT( onRemove() ) ); + connect( mySameValues, SIGNAL( stateChanged( int ) ), SLOT( onCheckBoxChanged() ) ); + connect( mySpinBox, SIGNAL( valueChanged( double ) ), SLOT( updateState() ) ); + connect( myListWidget, SIGNAL( itemSelectionChanged() ), SLOT( updateState() ) ); + myListWidget->installEventFilter( this ); + + clear(); +} + +//================================================================================ +/*! + * Destructor + */ +//================================================================================ + +StdMeshersGUI_FixedPointsParamWdg::~StdMeshersGUI_FixedPointsParamWdg() +{ +} + +//================================================================================ +/*! + * Event filter + */ +//================================================================================ +bool StdMeshersGUI_FixedPointsParamWdg::eventFilter( QObject* o, QEvent* e ) +{ + if ( o == myListWidget && e->type() == QEvent::KeyPress ) { + QKeyEvent* ke = (QKeyEvent*)e; + if ( ke->key() == Qt::Key_Delete ) + removePoints(); + } + return QWidget::eventFilter( o, e ); +} + +//================================================================================ +/*! + * Clear widget + */ +//================================================================================ +void StdMeshersGUI_FixedPointsParamWdg::clear() +{ + myTreeWidget->clear(); + myListWidget->clear(); + myTreeWidget->addTopLevelItem( newTreeItem( 0, 1 ) ); + mySpinBox->setValue( 0. ); + onCheckBoxChanged(); + updateState(); +} + +//================================================================================= +// function : onAdd() +// purpose : Called when Add Button Clicked +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::onAdd() +{ + addPoint( mySpinBox->value() ); +} + +//================================================================================= +// function : onRemove() +// purpose : Called when Remove Button Clicked +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::onRemove() +{ + removePoints(); +} + +//================================================================================= +// function : newTreeItem() +// purpose : Called to create TreeItem +//================================================================================= + +QTreeWidgetItem* StdMeshersGUI_FixedPointsParamWdg::newTreeItem( double v1, double v2 ) +{ + QTreeWidgetItem* anItem = new QTreeWidgetItem(); + anItem->setText( 0, treeItemText( v1, v2 ) ); + anItem->setText( 1, QString::number( 1 ) ); + anItem->setData( 1, Qt::UserRole, 1 ); + return anItem; +} + +//================================================================================= +// function : newListItem() +// purpose : Called to create ListItem +//================================================================================= + +QListWidgetItem* StdMeshersGUI_FixedPointsParamWdg::newListItem( double v ) +{ + QListWidgetItem* anItem = new QListWidgetItem( QString::number( v ) ); + anItem->setData( Qt::UserRole, v ); + return anItem; +} + +//================================================================================= +// function : itemText() +// purpose : Called to convert Values to Text +//================================================================================= + +QString StdMeshersGUI_FixedPointsParamWdg::treeItemText( double v1, double v2 ) +{ + return QString( "%1 - %2" ).arg( v1 ).arg( v2 ); +} + +//================================================================================= +// function : addPoint() +// purpose : Called to Add new Point +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::addPoint( double v) +{ + if ( GT_DBL(v, 0.0) && LT_DBL(v, 1.0)) { + bool toInsert = true; + int idx = myTreeWidget->topLevelItemCount()-1; + for ( int i = 0 ; i < myListWidget->count(); i++ ) { + double lv = point( i ); + if ( EQUAL_DBL(lv, v) ) { toInsert = false; break; } + else if ( GT_DBL(lv, v) ) { + idx = i; break; + } + } + if ( toInsert ) { + double v1 = idx == 0 ? 0 : point( idx-1 ); + double v2 = idx == myTreeWidget->topLevelItemCount()-1 ? 1 : point( idx ); + myTreeWidget->insertTopLevelItem( idx, newTreeItem( v1, v ) ); + myTreeWidget->topLevelItem( idx+1 )->setText( 0, treeItemText( v, v2 ) ); + myListWidget->insertItem( idx, newListItem( v ) ); + onCheckBoxChanged(); + } + } + updateState(); +} + +//================================================================================= +// function : removePoints() +// purpose : Called to remove selected points +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::removePoints() +{ + QList selItems = myListWidget->selectedItems(); + QListWidgetItem* item; + foreach ( item, selItems ) { + int idx = myListWidget->row( item ); + delete myTreeWidget->topLevelItem( idx ); + delete item; + myTreeWidget->topLevelItem( idx )->setText( 0, treeItemText( idx == 0 ? 0 : point( idx-1 ), + idx > myListWidget->count()-1 ? 1 : point( idx ) ) ); + } + onCheckBoxChanged(); + updateState(); +} + +double StdMeshersGUI_FixedPointsParamWdg::point( int idx ) const +{ + return idx >= 0 && idx < myListWidget->count() ? myListWidget->item( idx )->data( Qt::UserRole ).toDouble() : 0.; +} + +void StdMeshersGUI_FixedPointsParamWdg::setNbSegments( int idx, int val ) +{ + if ( idx >= 0 && idx < myTreeWidget->topLevelItemCount() ) { + myTreeWidget->topLevelItem( idx )->setData( 1, Qt::UserRole, val ); + myTreeWidget->topLevelItem( idx )->setText( 1, idx > 0 && mySameValues->isChecked() ? QString( SAME_TEXT ) : QString::number( val ) ); + } +} + +int StdMeshersGUI_FixedPointsParamWdg::nbSegments( int idx ) const +{ + return idx >= 0 && idx < myTreeWidget->topLevelItemCount() ? myTreeWidget->topLevelItem( idx )->data( 1, Qt::UserRole ).toInt() : 1; +} + +//================================================================================= +// function : onCheckBoxChanged() +// purpose : Called when Check Box Clicked +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::onCheckBoxChanged() +{ + for ( int i = 0; i < myTreeWidget->topLevelItemCount(); i++ ) { + QTreeWidgetItem* anItem = myTreeWidget->topLevelItem(i); + setNbSegments( i, nbSegments( i ) ); + anItem->setFlags( mySameValues->isChecked() && i > 0 ? anItem->flags() & ~Qt::ItemIsEditable : anItem->flags() | Qt::ItemIsEditable ); + } +} + +//================================================================================= +// function : updateState() +// purpose : Update widgets state +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::updateState() +{ + double v = mySpinBox->value(); + myAddButton->setEnabled( GT_DBL(v, 0.0) && LT_DBL(v, 1.0) ); + myRemoveButton->setEnabled( myListWidget->selectedItems().count() > 0 ); +} + +//================================================================================= +// function : GetListOfPoints +// purpose : Called to get the list of Edges IDs +//================================================================================= +SMESH::double_array_var StdMeshersGUI_FixedPointsParamWdg::GetListOfPoints() +{ + SMESH::double_array_var anArray = new SMESH::double_array; + int size = myListWidget->count(); + anArray->length( size ); + for (int i = 0; i < size; i++) { + anArray[i] = point(i); + } + return anArray; +} + +//================================================================================= +// function : SetListOfPoints +// purpose : Called to set the list of Points +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::SetListOfPoints( SMESH::double_array_var thePoints) +{ + clear(); + for ( int i = 0; i < thePoints->length(); i++ ) { + addPoint( thePoints[ i ] ); + } +} + +//================================================================================= +// function : GetListOfSegments +// purpose : Called to get the list Number of Segments +//================================================================================= +SMESH::long_array_var StdMeshersGUI_FixedPointsParamWdg::GetListOfSegments() +{ + SMESH::long_array_var anArray = new SMESH::long_array; + int size = mySameValues->isChecked() ? 1 : myTreeWidget->topLevelItemCount(); + anArray->length( size ); + for (int i = 0; i < size; i++) { + anArray[i] = nbSegments( i ); + } + return anArray; +} + +//================================================================================= +// function : SetListOfPoints +// purpose : Called to set the list of Points +//================================================================================= +void StdMeshersGUI_FixedPointsParamWdg::SetListOfSegments( SMESH::long_array_var theSegments) +{ + if ( myListWidget->count() > 0 && theSegments->length() == 1) + mySameValues->setChecked(true); + for ( int i = 0; i < theSegments->length(); i++ ) { + setNbSegments( i, theSegments[i] ); + } +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.h b/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.h new file mode 100644 index 000000000..312b00a34 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_FixedPointsParamWdg.h @@ -0,0 +1,92 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshersGUI_FixedPointsParamWdg.h +// Author : Open CASCADE S.A.S. (dmv) +// +#ifndef STDMESHERSGUI_FIXEDPOINTSPARAMWGD_H +#define STDMESHERSGUI_FIXEDPOINTSPARAMWGD_H + +// SMESH includes +#include +#include "SMESH_StdMeshersGUI.hxx" + +// Qt includes +#include +#include + +class SMESHGUI; +class SMESHGUI_SpinBox; +class QPushButton; +class QLineEdit; +class QCheckBox; +class QListWidget; +class QListWidgetItem; +class QTreeWidget; +class QTreeWidgetItem; + +class STDMESHERSGUI_EXPORT StdMeshersGUI_FixedPointsParamWdg : public QWidget +{ + Q_OBJECT + + class LineDelegate; + +public: + StdMeshersGUI_FixedPointsParamWdg( QWidget* parent = 0 ); + ~StdMeshersGUI_FixedPointsParamWdg(); + + bool eventFilter( QObject*, QEvent* ); + + SMESH::double_array_var GetListOfPoints(); + void SetListOfPoints( SMESH::double_array_var ); + + SMESH::long_array_var GetListOfSegments(); + void SetListOfSegments( SMESH::long_array_var ); + + QString GetValue() const { return myParamValue; } + +private slots: + void onAdd(); + void onRemove(); + void onCheckBoxChanged(); + void updateState(); + +private: + void clear(); + void addPoint( double ); + void removePoints(); + double point( int ) const; + void setNbSegments( int, int ); + int nbSegments( int ) const; + + static QTreeWidgetItem* newTreeItem( double v1, double v2 ); + static QListWidgetItem* newListItem( double v1 ); + static QString treeItemText( double v1, double v2 ); + +private: + QListWidget* myListWidget; + QTreeWidget* myTreeWidget; + SMESHGUI_SpinBox* mySpinBox; + QPushButton* myAddButton; + QPushButton* myRemoveButton; + QCheckBox* mySameValues; + QString myParamValue; +}; + +#endif // STDMESHERSGUI_FIXEDPOINTSPARAMWGD_H diff --git a/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.cxx b/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.cxx index ffdc4a7f1..ee46af5b1 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_LayerDistributionParamWdg.cxx // Author : Open CASCADE S.A.S. // SMESH includes @@ -50,7 +51,7 @@ StdMeshersGUI_LayerDistributionParamWdg ::StdMeshersGUI_LayerDistributionParamWdg(SMESH::SMESH_Hypothesis_ptr hyp, - const QString& theName, + const QString& theName, QDialog* dlg): QWidget(), myName(theName), myDlg( dlg ) { @@ -198,19 +199,26 @@ void StdMeshersGUI_LayerDistributionParamWdg::onEdit() return; CORBA::String_var hypType = myHyp->GetName(); - SMESHGUI_GenericHypothesisCreator* - editor = SMESH::GetHypothesisCreator( hypType.in() ); + // BUG 0020378 + SMESHGUI_GenericHypothesisCreator* editor = SMESH::GetHypothesisCreator(hypType.in()); if ( !editor ) return; - if ( myDlg ) myDlg->hide(); + if ( myDlg ) + myDlg->hide(); try { QWidget* parent = this; - if ( myDlg ) parent = myDlg->parentWidget(); - editor->edit( myHyp, myName, parent ); + if ( myDlg ) + parent = myDlg->parentWidget(); + editor->edit( myHyp, myName, parent, this, SLOT( onEdited( int ) ) ); } - catch(...) { + catch(...) + { } +} - if ( myDlg ) myDlg->show(); +void StdMeshersGUI_LayerDistributionParamWdg::onEdited( int result ) +{ + if ( myDlg ) + myDlg->show(); } diff --git a/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.h b/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.h index b04f2a605..41224a412 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.h +++ b/src/StdMeshersGUI/StdMeshersGUI_LayerDistributionParamWdg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_LayerDistributionParamWdg.h // Author : Open CASCADE S.A.S. // @@ -51,7 +52,7 @@ class STDMESHERSGUI_EXPORT StdMeshersGUI_LayerDistributionParamWdg : public QWid public: StdMeshersGUI_LayerDistributionParamWdg(SMESH::SMESH_Hypothesis_ptr, - const QString&, + const QString&, QDialog*); ~StdMeshersGUI_LayerDistributionParamWdg(); @@ -65,6 +66,7 @@ private slots: void onCreate(); void onEdit(); void onHypTypePopup( QAction* ); + void onEdited(int); private: void init(); diff --git a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx index 9e8e0bda8..f4e65c3f6 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_NbSegmentsCreator.cxx // Author : Open CASCADE S.A.S. // SMESH includes @@ -26,6 +27,7 @@ #include "StdMeshersGUI_NbSegmentsCreator.h" #include "StdMeshersGUI_DistrTable.h" #include "StdMeshersGUI_DistrPreview.h" +#include "StdMeshersGUI_SubShapeSelectorWdg.h" #include #include @@ -96,17 +98,17 @@ bool StdMeshersGUI_NbSegmentsCreator::checkParams( QString& msg ) const QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() { QFrame* fr = new QFrame(); + fr->setMinimumWidth(460); QVBoxLayout* lay = new QVBoxLayout( fr ); lay->setMargin( 0 ); - lay->setSpacing( 0 ); + lay->setSpacing( SPACING ); QGroupBox* GroupC1 = new QGroupBox( tr( "SMESH_ARGUMENTS" ), fr ); lay->addWidget( GroupC1 ); StdMeshers::StdMeshers_NumberOfSegments_var h = StdMeshers::StdMeshers_NumberOfSegments::_narrow( hypothesis() ); - myPreview = new StdMeshersGUI_DistrPreview( GroupC1, h.in() ); myGroupLayout = new QGridLayout( GroupC1 ); myGroupLayout->setSpacing( SPACING ); @@ -125,6 +127,7 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() row++; } + // 1) number of segments myGroupLayout->addWidget( new QLabel( tr( "SMESH_NB_SEGMENTS_PARAM" ), GroupC1 ), row, 0 ); myNbSeg = new SalomeApp_IntSpinBox( GroupC1 ); @@ -133,6 +136,7 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() myGroupLayout->addWidget( myNbSeg, row, 1 ); row++; + // 2) type of distribution myGroupLayout->addWidget( new QLabel( tr( "SMESH_DISTR_TYPE" ), GroupC1 ), row, 0 ); myDistr = new QtxComboBox( GroupC1 ); @@ -145,32 +149,43 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() myGroupLayout->addWidget( myDistr, row, 1 ); row++; + // 3) scale myGroupLayout->addWidget( myLScale = new QLabel( tr( "SMESH_NB_SEGMENTS_SCALE_PARAM" ), GroupC1 ), row, 0 ); myScale = new SMESHGUI_SpinBox( GroupC1 ); - myScale->RangeStepAndValidator( 1E-5, 1E+5, 0.1, 6 ); + myScale->RangeStepAndValidator( 1E-5, 1E+5, 0.1, "parametric_precision" ); myGroupLayout->addWidget( myScale, row, 1 ); row++; - myInfo = new QLabel( tr( "SMESH_FUNC_DOMAIN" ), GroupC1 ); - myGroupLayout->addWidget( myInfo, row, 0, 1, 2 ); - row++; - // 4) table - myGroupLayout->addWidget( myLTable = new QLabel( tr( "SMESH_TAB_FUNC" ), GroupC1 ), row, 0 ); - myTable = new StdMeshersGUI_DistrTableFrame( GroupC1 ); - myGroupLayout->addWidget( myTable, row, 1 ); + // 4) Distribution definition + QGridLayout* myDistLayout = new QGridLayout(GroupC1); + myGroupLayout->addLayout( myDistLayout, row, 0, 1, 2 ); myGroupLayout->setRowStretch( row, 1 ); - myTableRow = row; - row++; + row ++; - // 5) expression - myGroupLayout->addWidget( myLExpr = new QLabel( tr( "SMESH_EXPR_FUNC" ), GroupC1 ), row, 0 ); + // a) expression + QHBoxLayout* myExprLayout = new QHBoxLayout(GroupC1); + myExprLayout->addWidget( myLExpr = new QLabel( "f(t)=", GroupC1 ), 0); myExpr = new QLineEdit( GroupC1 ); - myGroupLayout->addWidget( myExpr, row, 1 ); - row++; + myExprLayout->addWidget( myExpr,1); + myDistLayout->addLayout(myExprLayout,1 ,0); + myDistLayout->setRowStretch(2, 1); - // 6) conversion (radiogroup) + // b) warning + myInfo = new QLabel( tr( "SMESH_FUNC_DOMAIN" ), GroupC1 ); + myDistLayout->addWidget( myInfo, 0, 0, 1, 2); + + // c) table + myTable = new StdMeshersGUI_DistrTableFrame( GroupC1 ); + myDistLayout->addWidget( myTable, 1, 0, 2, 1 ); + + // d) preview + myPreview = new StdMeshersGUI_DistrPreview( GroupC1, h.in() ); + myPreview->setMinimumHeight(220); + myDistLayout->addWidget( myPreview, 1, 1, 2, 1 ); + + // 5) conversion (radiogroup) myConvBox = new QGroupBox( tr( "SMESH_CONV_MODE" ), GroupC1 ); myConv = new QButtonGroup( GroupC1 ); @@ -189,17 +204,30 @@ QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame() myGroupLayout->addWidget( myConvBox, row, 0, 1, 2 ); row++; - // 7) distribution preview - myGroupLayout->addWidget( myPreview, row, 0, 1, 2 ); - myGroupLayout->setRowStretch( row, 1 ); - myPreviewRow = row; - row++; + // 6) reverse edge parameters + myReversedEdgesBox = new QGroupBox(tr( "SMESH_REVERSED_EDGES" ), fr); + QHBoxLayout* edgeLay = new QHBoxLayout( myReversedEdgesBox ); + + myDirectionWidget = new StdMeshersGUI_SubShapeSelectorWdg( myReversedEdgesBox ); + QString aGeomEntry = getShapeEntry(); + QString aMainEntry = getMainShapeEntry(); + if ( aGeomEntry == "" ) + aGeomEntry = h->GetObjectEntry(); + myDirectionWidget->SetGeomShapeEntry( aGeomEntry ); + myDirectionWidget->SetMainShapeEntry( aMainEntry ); + myDirectionWidget->SetListOfIDs( h->GetReversedEdges() ); + edgeLay->addWidget( myDirectionWidget ); + + lay->addWidget( myReversedEdgesBox ); + lay->setStretchFactor( GroupC1, 2); + lay->setStretchFactor( myReversedEdgesBox, 1); + connect( myNbSeg, SIGNAL( valueChanged( const QString& ) ), this, SLOT( onValueChanged() ) ); connect( myDistr, SIGNAL( activated( int ) ), this, SLOT( onValueChanged() ) ); connect( myTable, SIGNAL( valueChanged( int, int ) ), this, SLOT( onValueChanged() ) ); connect( myExpr, SIGNAL( textChanged( const QString& ) ), this, SLOT( onValueChanged() ) ); - connect( myConv, SIGNAL( cuttonClicked( int ) ), this, SLOT( onValueChanged() ) ); + connect( myConv, SIGNAL( buttonClicked( int ) ), this, SLOT( onValueChanged() ) ); return fr; } @@ -271,10 +299,12 @@ QString StdMeshersGUI_NbSegmentsCreator::storeParams() const break; } if ( hasConv ) + { if ( data.myConv ) valStr += "; " + tr("SMESH_CUT_NEG_MODE"); else valStr += "; " + tr("SMESH_EXP_MODE"); + } return valStr; } @@ -287,17 +317,17 @@ bool StdMeshersGUI_NbSegmentsCreator::readParamsFromHypo( NbSegmentsHypothesisDa h_data.myName = hypName(); h_data.myNbSeg = (int) h->GetNumberOfSegments(); - - SMESH::ListOfParameters_var aParameters = h->GetLastParameters(); - h_data.myNbSegVarName = (aParameters->length() > 0) ? QString(aParameters[0].in()) : QString(""); + CORBA::String_var aVaribaleName = h->GetVarParameter( "SetNumberOfSegments" ); + h_data.myNbSegVarName = aVaribaleName.in(); int distr = (int) h->GetDistrType(); h_data.myDistrType = distr; h_data.myScale = distr==1 ? h->GetScaleFactor() : 1.0; - if(distr==1){ - h_data.myScaleVarName = (aParameters->length() > 1) ? QString(aParameters[1].in()) : QString(""); + if(distr==1) { + aVaribaleName = h->GetVarParameter( "SetScaleFactor" ); + h_data.myScaleVarName = aVaribaleName.in(); } else h_data.myScaleVarName = QString(""); @@ -334,20 +364,23 @@ bool StdMeshersGUI_NbSegmentsCreator::storeParamsToHypo( const NbSegmentsHypothe if( isCreation() ) SMESH::SetName( SMESH::FindSObject( h ), h_data.myName.toLatin1().data() ); - QStringList aVariablesList; - aVariablesList.append(h_data.myNbSegVarName); - + h->SetVarParameter( h_data.myNbSegVarName.toLatin1().constData(), "SetNumberOfSegments" ); h->SetNumberOfSegments( h_data.myNbSeg ); int distr = h_data.myDistrType; h->SetDistrType( distr ); if( distr==1 ) { + h->SetVarParameter( h_data.myScaleVarName.toLatin1().constData(), "SetScaleFactor" ); h->SetScaleFactor( h_data.myScale ); - aVariablesList.append(h_data.myScaleVarName); } if( distr==2 || distr==3 ) h->SetConversionMode( h_data.myConv ); + if( distr==1 || distr==2 || distr==3 ) { + h->SetReversedEdges( myDirectionWidget->GetListOfIDs() ); + h->SetObjectEntry( myDirectionWidget->GetMainShapeEntry() ); + } + if( distr==2 ) h->SetTableFunction( h_data.myTable ); @@ -356,8 +389,6 @@ bool StdMeshersGUI_NbSegmentsCreator::storeParamsToHypo( const NbSegmentsHypothe //setting of function must follow after setConversionMode, because otherwise //the function will be checked with old conversion mode, so that it may occurs //unexpected errors for user - - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); } catch(const SALOME::SALOME_Exception& ex) { @@ -400,23 +431,17 @@ void StdMeshersGUI_NbSegmentsCreator::onValueChanged() myScale->setShown( distr==1 ); myLScale->setShown( distr==1 ); + myReversedEdgesBox->setShown( !distr==0 ); + myDirectionWidget->showPreview( !distr==0 ); bool isFunc = distr==2 || distr==3; myPreview->setShown( isFunc ); - myGroupLayout->setRowStretch( myPreviewRow, isFunc ? 1 : 0 ); - myConvBox->setShown( isFunc ); - - if( distr==2 ) - myTable->show(); - else - myTable->hide(); - myLTable->setShown( distr==2 ); - myGroupLayout->setRowStretch( myTableRow, distr==2 ? 1 : 0 ); - + + myTable->setShown( distr==2 ); myExpr->setShown( distr==3 ); myLExpr->setShown( distr==3 ); - myInfo->setShown( isFunc ); + myInfo->setShown( distr==3); //change of preview int nbSeg = myNbSeg->value(); @@ -434,8 +459,11 @@ void StdMeshersGUI_NbSegmentsCreator::onValueChanged() if ( (QtxComboBox*)sender() == myDistr && dlg() ) { QApplication::instance()->processEvents(); + myGroupLayout->invalidate(); + dlg()->layout()->invalidate(); dlg()->updateGeometry(); - dlg()->setMinimumSize( dlg()->minimumSizeHint().width(), dlg()->minimumSizeHint().height() ); + dlg()->setMinimumSize( dlg()->minimumSizeHint() ); dlg()->resize( dlg()->minimumSize() ); + QApplication::instance()->processEvents(); } } diff --git a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h index ba7c7bff8..66456e0ca 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h +++ b/src/StdMeshersGUI/StdMeshersGUI_NbSegmentsCreator.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_NbSegmentsCreator.h // Author : Open CASCADE S.A.S. // @@ -44,6 +45,7 @@ class QButtonGroup; class QGroupBox; class QGridLayout; class QRadioButton; +class StdMeshersGUI_SubShapeSelectorWdg; typedef struct { @@ -90,7 +92,10 @@ private: QLabel *myLScale, *myLTable, *myLExpr, *myInfo; QGridLayout* myGroupLayout; int myTableRow, myPreviewRow; - QRadioButton* myCutNeg; + //QRadioButton* myCutNeg; + QGroupBox* myReversedEdgesBox; + + StdMeshersGUI_SubShapeSelectorWdg* myDirectionWidget; }; #endif // STDMESHERSGUI_NBSEGMENTSCREATOR_H diff --git a/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.cxx b/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.cxx index 37a3ce7ee..1efeb0162 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_ObjectReferenceParamWdg.cxx // Author : Open CASCADE S.A.S. // SMESH includes @@ -34,9 +35,11 @@ #include #include #include +#include // SALOME KERNEL incldues #include +#include // Qt includes #include @@ -53,10 +56,11 @@ //================================================================================ StdMeshersGUI_ObjectReferenceParamWdg::StdMeshersGUI_ObjectReferenceParamWdg -( SUIT_SelectionFilter* f, QWidget* parent) - : QWidget( parent ) +( SUIT_SelectionFilter* f, QWidget* parent, bool multiSelection, bool stretch ) + : QWidget( parent ), myMultiSelection( multiSelection ) { myFilter = f; + myStretchActivated = stretch; init(); } @@ -68,8 +72,8 @@ StdMeshersGUI_ObjectReferenceParamWdg::StdMeshersGUI_ObjectReferenceParamWdg //================================================================================ StdMeshersGUI_ObjectReferenceParamWdg::StdMeshersGUI_ObjectReferenceParamWdg -( MeshObjectType objType, QWidget* parent ) - : QWidget( parent ) +( MeshObjectType objType, QWidget* parent, bool multiSelection ) + : QWidget( parent ), myMultiSelection( multiSelection ) { myFilter = new SMESH_TypeFilter( objType ); init(); @@ -83,7 +87,10 @@ StdMeshersGUI_ObjectReferenceParamWdg::StdMeshersGUI_ObjectReferenceParamWdg StdMeshersGUI_ObjectReferenceParamWdg::~StdMeshersGUI_ObjectReferenceParamWdg() { if ( myFilter ) + { + mySelectionMgr->removeFilter( myFilter ); delete myFilter; + } } @@ -104,6 +111,8 @@ void StdMeshersGUI_ObjectReferenceParamWdg::init() mySelectionMgr = SMESH::GetSelectionMgr( mySMESHGUI ); mySelectionActivated = false; myParamValue = ""; + myEmptyText = ""; + myEmptyStyleSheet =""; SUIT_ResourceMgr* mgr = SMESH::GetResourceMgr( mySMESHGUI ); QPixmap iconSlct ( mgr->loadPixmap("SMESH", tr("ICON_SELECT"))); @@ -114,10 +123,13 @@ void StdMeshersGUI_ObjectReferenceParamWdg::init() myObjNameLineEdit = new QLineEdit(this); myObjNameLineEdit->setReadOnly(true); + myObjNameLineEdit->setStyleSheet(myEmptyStyleSheet); aHBox->addWidget( mySelButton ); aHBox->addWidget( myObjNameLineEdit ); - aHBox->addStretch(); + if (myStretchActivated){ + aHBox->addStretch(); + } connect( mySelButton, SIGNAL(clicked()), SLOT(activateSelection())); } @@ -141,6 +153,7 @@ void StdMeshersGUI_ObjectReferenceParamWdg::activateSelection() connect(mySelectionMgr, SIGNAL(currentSelectionChanged()), SLOT(onSelectionDone())); } emit selectionActivated(); + onSelectionDone(); mySelButton->setChecked( mySelectionActivated ); } @@ -183,8 +196,9 @@ void StdMeshersGUI_ObjectReferenceParamWdg::AvoidSimultaneousSelection void StdMeshersGUI_ObjectReferenceParamWdg::SetObject(CORBA::Object_ptr obj) { - myObject = CORBA::Object::_nil(); - myObjNameLineEdit->setText( "" ); + myObjects.clear(); + myObjNameLineEdit->setText( myEmptyText ); + myObjNameLineEdit->setStyleSheet(myEmptyStyleSheet); myParamValue = ""; _PTR(SObject) sobj; @@ -192,10 +206,52 @@ void StdMeshersGUI_ObjectReferenceParamWdg::SetObject(CORBA::Object_ptr obj) sobj = SMESH::FindSObject (obj); if ( sobj ) { std::string name = sobj->GetName(); - myObjNameLineEdit->setText( name.c_str() ); - myObject = CORBA::Object::_duplicate( obj ); + myObjNameLineEdit->setText( QString( name.c_str() ).trimmed() ); + myObjNameLineEdit->setStyleSheet(""); + myObjects.push_back( CORBA::Object::_duplicate( obj )); myParamValue = sobj->GetID().c_str(); + emit contentModified(); + } +} + +//================================================================================ +/*! + * \brief Initialize selected objects + * \param objects - entries of objects + */ +//================================================================================ + +void StdMeshersGUI_ObjectReferenceParamWdg::SetObjects(SMESH::string_array_var& objects) +{ + myObjects.clear(); + myObjNameLineEdit->setText( myEmptyText ); + myObjNameLineEdit->setStyleSheet(myEmptyStyleSheet); + myParamValue = ""; + bool selChanged = false; + + for ( unsigned i = 0; i < objects->length(); ++i ) + { + _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); + _PTR(SObject) aSObj = aStudy->FindObjectID(objects[i].in()); + CORBA::Object_var anObj = SMESH::SObjectToObject(aSObj,aStudy); + if ( !CORBA::is_nil( anObj )) { + std::string name = aSObj->GetName(); + QString text = myObjNameLineEdit->text(); + if ( text != myEmptyText ) + text += " "; + else + text = ""; + text += QString( name.c_str() ).trimmed(); + myObjNameLineEdit->setText( text ); + myObjNameLineEdit->setStyleSheet(""); + myObjects.push_back( anObj ); + myParamValue += " "; + myParamValue += objects[i]; + selChanged = true; + } } + if (selChanged) + emit contentModified(); } //================================================================================ @@ -211,7 +267,34 @@ void StdMeshersGUI_ObjectReferenceParamWdg::onSelectionDone() SALOME_ListIO aList; mySelectionMgr->selectedObjects(aList); if (aList.Extent() == 1) + { obj = SMESH::IObjectToObject( aList.First() ); - SetObject( obj.in() ); + SetObject( obj.in() ); + } + else if (myMultiSelection) + { + SMESH::string_array_var objIds = new SMESH::string_array; + objIds->length( aList.Extent()); + SALOME_ListIteratorOfListIO io( aList ); + int i = 0; + for ( ; io.More(); io.Next(), ++i ) + { + Handle(SALOME_InteractiveObject) anIO = io.Value(); + if ( anIO->hasEntry() ) + objIds[i] = anIO->getEntry(); + else + i--; + } + objIds->length(i); + SetObjects( objIds ); + } } } + +void StdMeshersGUI_ObjectReferenceParamWdg::SetDefaultText(QString defaultText, QString styleSheet) +{ + myEmptyText = defaultText; + myEmptyStyleSheet = styleSheet; + myObjNameLineEdit->setText( myEmptyText ); + myObjNameLineEdit->setStyleSheet( myEmptyStyleSheet); +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.h b/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.h index 62e5096af..26448cf22 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.h +++ b/src/StdMeshersGUI/StdMeshersGUI_ObjectReferenceParamWdg.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_ObjectReferenceParamWdg.h // Author : Open CASCADE S.A.S. // @@ -36,6 +37,9 @@ // CORBA includes #include +#include +#include CORBA_SERVER_HEADER(SMESH_Mesh) + class SUIT_SelectionFilter; class SMESHGUI; class LightApp_SelectionMgr; @@ -51,24 +55,34 @@ class STDMESHERSGUI_EXPORT StdMeshersGUI_ObjectReferenceParamWdg : public QWidge public: StdMeshersGUI_ObjectReferenceParamWdg( SUIT_SelectionFilter* filter, - QWidget* parent); + QWidget* parent, + bool multiSelection=false, + bool stretch=true); StdMeshersGUI_ObjectReferenceParamWdg( MeshObjectType objType, - QWidget* parent); + QWidget* parent, + bool multiSelection=false); ~StdMeshersGUI_ObjectReferenceParamWdg(); void SetObject(CORBA::Object_ptr obj); + void SetObjects(SMESH::string_array_var& objEntries); + template - typename TInterface::_var_type GetObject() const { - if ( IsObjectSelected() ) return TInterface::_narrow(myObject); + typename TInterface::_var_type GetObject(unsigned i=0) const { + if ( IsObjectSelected(i) ) return TInterface::_narrow(myObjects[i]); return TInterface::_nil(); } + int NbObjects() const { return myObjects.size(); } + QString GetValue() const { return myParamValue; } - bool IsObjectSelected() const { return !CORBA::is_nil(myObject); } + bool IsObjectSelected(unsigned i=0) const + { return i < myObjects.size() && !CORBA::is_nil(myObjects[i]); } void AvoidSimultaneousSelection( StdMeshersGUI_ObjectReferenceParamWdg* other); + + void SetDefaultText(QString defaultText="", QString styleSheet=""); public slots: /*! @@ -88,6 +102,7 @@ signals: * one is activated */ void selectionActivated(); + void contentModified(); private slots: void onSelectionDone(); @@ -96,9 +111,13 @@ private: void init(); private: - CORBA::Object_var myObject; + + bool myMultiSelection; + std::vector myObjects; + SUIT_SelectionFilter* myFilter; bool mySelectionActivated; + bool myStretchActivated; SMESHGUI* mySMESHGUI; LightApp_SelectionMgr* mySelectionMgr; @@ -106,6 +125,8 @@ private: QLineEdit* myObjNameLineEdit; QPushButton* mySelButton; QString myParamValue; + QString myEmptyText; + QString myEmptyStyleSheet; }; #endif // STDMESHERSGUI_OBJECTREFERENCEPARAMWDG_H diff --git a/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.cxx b/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.cxx new file mode 100644 index 000000000..cb951878a --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.cxx @@ -0,0 +1,100 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshersGUI_QuadrangleParamWdg.cxx +// Author : Open CASCADE S.A.S. (jfa) +// SMESH includes + +#include "StdMeshersGUI_QuadrangleParamWdg.h" + +#include "SMESHGUI.h" + +#include "SUIT_ResourceMgr.h" + +// Qt includes +#include +#include +#include +#include + +// IDL includes +#include +#include CORBA_CLIENT_HEADER(SMESH_BasicHypothesis) + +#define SPACING 6 +#define MARGIN 0 + +//================================================================================ +// function : Constructor +// purpose : +//================================================================================ +StdMeshersGUI_QuadrangleParamWdg::StdMeshersGUI_QuadrangleParamWdg (QWidget * parent) + : QWidget(parent), + myType(0) +{ + myType = new QButtonGroup (this); + + QGridLayout* typeLay = new QGridLayout( this ); + + typeLay->setMargin(MARGIN); + typeLay->setSpacing(SPACING); + + QString aTypeKey ("SMESH_QUAD_TYPE_%1"); + QString aPictKey ("ICON_StdMeshers_Quadrangle_Params_%1"); + + int itype = 0; + for (; itype < int(StdMeshers::QUAD_NB_TYPES); itype++) { + QRadioButton* rbi = new QRadioButton (tr(aTypeKey.arg(itype).toLatin1()), this); + QPixmap pmi (SMESHGUI::resourceMgr()->loadPixmap("SMESH", tr(aPictKey.arg(itype).toLatin1()))); + QLabel* pli = new QLabel (this); + pli->setPixmap(pmi); + typeLay->addWidget(rbi, itype, 0, 1, 1); + typeLay->addWidget(pli, itype, 1, 1, 1); + myType->addButton(rbi, itype); + } + myType->button(0)->setChecked(true); + + setLayout(typeLay); + setMinimumWidth(300); +} + +//================================================================================ +// function : Destructor +// purpose : +//================================================================================ +StdMeshersGUI_QuadrangleParamWdg::~StdMeshersGUI_QuadrangleParamWdg() +{ +} + +//================================================================================= +// function : SetType +// purpose : +//================================================================================= +void StdMeshersGUI_QuadrangleParamWdg::SetType (int theType) +{ + myType->button(theType)->setChecked(true); +} + +//================================================================================= +// function : GetType +// purpose : +//================================================================================= +int StdMeshersGUI_QuadrangleParamWdg::GetType() +{ + return myType->checkedId(); +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.h b/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.h new file mode 100644 index 000000000..b58445b6d --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_QuadrangleParamWdg.h @@ -0,0 +1,49 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshersGUI_QuadrangleParamWdg.h +// Author : Open CASCADE S.A.S. (jfa) + +#ifndef STDMESHERSGUI_QUADRANGLEPARAMWDG_H +#define STDMESHERSGUI_QUADRANGLEPARAMWDG_H + +// SMESH includes +#include "SMESH_StdMeshersGUI.hxx" + +// Qt includes +#include + +class QButtonGroup; + +class STDMESHERSGUI_EXPORT StdMeshersGUI_QuadrangleParamWdg : public QWidget +{ + Q_OBJECT + +public: + StdMeshersGUI_QuadrangleParamWdg (QWidget* parent = 0); + ~StdMeshersGUI_QuadrangleParamWdg(); + + void SetType (int theType); + int GetType (); + +private: + // Quadranle preference, Triangle preference, Reduced + QButtonGroup* myType; +}; + +#endif // STDMESHERSGUI_QUADRANGLEPARAMWDG_H diff --git a/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.cxx b/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.cxx index 56e31b5a6..6e902a7f1 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.cxx +++ b/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.cxx @@ -1,38 +1,45 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 -// -// File : StdMeshersGUI_StdHypothesisCreator.cxx -// Author : Alexander SOLOVYOV, Open CASCADE S.A.S. -// SMESH includes +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // +// File : StdMeshersGUI_StdHypothesisCreator.cxx +// Author : Alexander SOLOVYOV, Open CASCADE S.A.S. +// SMESH includes + #include "StdMeshersGUI_StdHypothesisCreator.h" #include #include #include #include +#include + #include #include -#include -#include + +#include "StdMeshersGUI_FixedPointsParamWdg.h" +#include "StdMeshersGUI_LayerDistributionParamWdg.h" +#include "StdMeshersGUI_ObjectReferenceParamWdg.h" +#include "StdMeshersGUI_QuadrangleParamWdg.h" +#include "StdMeshersGUI_SubShapeSelectorWdg.h" + #include // SALOME GUI includes @@ -42,6 +49,7 @@ #include #include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) #include CORBA_SERVER_HEADER(SMESH_Mesh) +#include CORBA_SERVER_HEADER(SMESH_Group) // Qt includes #include @@ -184,10 +192,10 @@ namespace { */ //================================================================================ - class TDoubleSliderWith2Lables: public QWidget + class TDoubleSliderWith2Labels: public QWidget { public: - TDoubleSliderWith2Lables( const QString& leftLabel, const QString& rightLabel, + TDoubleSliderWith2Labels( const QString& leftLabel, const QString& rightLabel, const double initValue, const double bottom, const double top , const double precision, QWidget * parent=0 , const char * name=0 ) @@ -196,11 +204,11 @@ namespace { setObjectName(name); QHBoxLayout* aHBoxL = new QHBoxLayout(this); - + if ( !leftLabel.isEmpty() ) { - QLabel* aLeftLabel = new QLabel( this ); - aLeftLabel->setText( leftLabel ); - aHBoxL->addWidget( aLeftLabel ); + QLabel* aLeftLabel = new QLabel( this ); + aLeftLabel->setText( leftLabel ); + aHBoxL->addWidget( aLeftLabel ); } _slider = new QSlider( Qt::Horizontal, this ); @@ -209,9 +217,9 @@ namespace { aHBoxL->addWidget( _slider ); if ( !rightLabel.isEmpty() ) { - QLabel* aRightLabel = new QLabel( this ); - aRightLabel->setText( rightLabel ); - aHBoxL->addWidget( aRightLabel ); + QLabel* aRightLabel = new QLabel( this ); + aRightLabel->setText( rightLabel ); + aHBoxL->addWidget( aRightLabel ); } setLayout( aHBoxL ); @@ -255,11 +263,30 @@ namespace { return SMESH::SMESH_Mesh::_nil(); } //================================================================================ + /*! + * \brief Retrieve SMESH_Mesh held by widget + */ + //================================================================================ + + inline SMESH::ListOfGroups_var groupsFromWdg(const QWidget* wdg) + { + SMESH::ListOfGroups_var groups = new SMESH::ListOfGroups; + const StdMeshersGUI_ObjectReferenceParamWdg * objRefWdg = + dynamic_cast( wdg ); + if ( objRefWdg ) + { + groups->length( objRefWdg->NbObjects() ); + for ( unsigned i = 0; i < groups->length(); ++i ) + groups[i] = objRefWdg->GetObject< SMESH::SMESH_GroupBase >(i); + } + return groups; + } + //================================================================================ /*! * \brief creates a filter for selection of shapes of given dimension * \param dim - dimension - * \param subShapeType - required type of subshapes, number of which must be \a nbSubShapes - * \param nbSubShapes - number of subshapes of given type + * \param subShapeType - required type of sub-shapes, number of which must be \a nbSubShapes + * \param nbSubShapes - number of sub-shapes of given type * \param closed - required closeness flag of a shape * \retval SUIT_SelectionFilter* - created filter */ @@ -311,6 +338,16 @@ namespace { w->SetObject( object.in() ); return w; } + QWidget* newObjRefParamWdg( SUIT_SelectionFilter* filter, + SMESH::string_array_var& objEntries) + { + StdMeshersGUI_ObjectReferenceParamWdg* w = + new StdMeshersGUI_ObjectReferenceParamWdg( filter, 0, /*multiSel=*/true); + //RNV: Firstly, activate selection, then set objects + w->activateSelection(); + w->SetObjects( objEntries ); + return w; + } //================================================================================ /*! @@ -355,7 +392,8 @@ bool StdMeshersGUI_StdHypothesisCreator::checkParams( QString& msg ) const ok = ( w->IsObjectSelected() ); if ( !ok ) w->SetObject( CORBA::Object::_nil() ); int nbAssocVert = ( hypType() == "ProjectionSource1D" ? 1 : 2 ); - for ( int i = 0; ok && i < nbAssocVert; i += 2) + int nbNonEmptyAssoc = 0; + for ( int i = 0; ok && i < nbAssocVert*2; i += 2) { QString srcV, tgtV; StdMeshersGUI_ObjectReferenceParamWdg* w1 = @@ -370,18 +408,43 @@ bool StdMeshersGUI_StdHypothesisCreator::checkParams( QString& msg ) const w1->SetObject( CORBA::Object::_nil() ); w2->SetObject( CORBA::Object::_nil() ); } + nbNonEmptyAssoc += !srcV.isEmpty(); + } + if ( ok && nbNonEmptyAssoc == 1 && nbAssocVert == 2 ) + { + // only one pair of VERTEXes is given for a FACE, + // then the FACE must have only one VERTEX + GEOM::GEOM_Object_var face = w->GetObject< GEOM::GEOM_Object >(); + + GEOM::GEOM_Gen_var geomGen = SMESH::GetGEOMGen(); + _PTR(Study) aStudy = SMESH::GetActiveStudyDocument(); + GEOM::GEOM_IShapesOperations_var shapeOp; + if ( !geomGen->_is_nil() && aStudy ) + shapeOp = geomGen->GetIShapesOperations( aStudy->StudyId() ); + if ( !shapeOp->_is_nil() ) + { + GEOM::ListOfLong_var vertices = + shapeOp->GetAllSubShapesIDs (face, GEOM::VERTEX, /*isSorted=*/false); + ok = ( vertices->length() == 1 ); + } } - // Uninstall filters of StdMeshersGUI_ObjectReferenceParamWdg if ( ok ) deactivateObjRefParamWdg( customWidgets() ); } - else if ( hypType() == "LayerDistribution" ) + else if ( hypType().startsWith("ImportSource" )) + { + StdMeshersGUI_ObjectReferenceParamWdg* w = + widget< StdMeshersGUI_ObjectReferenceParamWdg >( 0 ); + ok = ( w->IsObjectSelected() ); + } + else if ( hypType() == "LayerDistribution" || hypType() == "LayerDistribution2D" ) { StdMeshersGUI_LayerDistributionParamWdg* w = widget< StdMeshersGUI_LayerDistributionParamWdg >( 0 ); ok = ( w && w->IsOk() ); } + return ok; } @@ -403,25 +466,26 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const } QString valueStr = stdParamValues( params ); - QStringList aVariablesList = getVariablesFromDlg(); + //QStringList aVariablesList = getVariablesFromDlg(); if( res && !params.isEmpty() ) { if( hypType()=="LocalLength" ) { StdMeshers::StdMeshers_LocalLength_var h = - StdMeshers::StdMeshers_LocalLength::_narrow( hypothesis() ); + StdMeshers::StdMeshers_LocalLength::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetLength" ); h->SetLength( params[0].myValue.toDouble() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); + h->SetVarParameter( params[1].text(), "SetPrecision" ); h->SetPrecision( params[1].myValue.toDouble() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); } else if( hypType()=="MaxLength" ) { StdMeshers::StdMeshers_MaxLength_var h = - StdMeshers::StdMeshers_MaxLength::_narrow( hypothesis() ); + StdMeshers::StdMeshers_MaxLength::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetLength" ); h->SetLength( params[0].myValue.toDouble() ); h->SetUsePreestimatedLength( widget< QCheckBox >( 1 )->isChecked() ); if ( !h->HavePreestimatedLength() && !h->_is_equivalent( initParamsHypothesis() )) { @@ -433,83 +497,133 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const else if( hypType()=="SegmentLengthAroundVertex" ) { StdMeshers::StdMeshers_SegmentLengthAroundVertex_var h = - StdMeshers::StdMeshers_SegmentLengthAroundVertex::_narrow( hypothesis() ); + StdMeshers::StdMeshers_SegmentLengthAroundVertex::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetLength" ); h->SetLength( params[0].myValue.toDouble() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); } else if( hypType()=="Arithmetic1D" ) { StdMeshers::StdMeshers_Arithmetic1D_var h = - StdMeshers::StdMeshers_Arithmetic1D::_narrow( hypothesis() ); + StdMeshers::StdMeshers_Arithmetic1D::_narrow( hypothesis() ); + + StdMeshersGUI_SubShapeSelectorWdg* w = + widget< StdMeshersGUI_SubShapeSelectorWdg >( 2 ); + + h->SetVarParameter( params[0].text(), "SetStartLength" ); + h->SetStartLength( params[0].myValue.toDouble() ); + h->SetVarParameter( params[1].text(), "SetEndLength" ); + h->SetEndLength( params[1].myValue.toDouble() ); + if (w) { + h->SetReversedEdges( w->GetListOfIDs() ); + h->SetObjectEntry( w->GetMainShapeEntry() ); + } + } + else if( hypType()=="FixedPoints1D" ) + { + StdMeshers::StdMeshers_FixedPoints1D_var h = + StdMeshers::StdMeshers_FixedPoints1D::_narrow( hypothesis() ); + + StdMeshersGUI_FixedPointsParamWdg* w1 = + widget< StdMeshersGUI_FixedPointsParamWdg >( 0 ); - h->SetLength( params[0].myValue.toDouble(), true ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); - h->SetLength( params[1].myValue.toDouble(), false ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); + StdMeshersGUI_SubShapeSelectorWdg* w2 = + widget< StdMeshersGUI_SubShapeSelectorWdg >( 1 ); + + if (w1) { + h->SetPoints( w1->GetListOfPoints() ); + h->SetNbSegments( w1->GetListOfSegments() ); + } + if (w2) { + h->SetReversedEdges( w2->GetListOfIDs() ); + h->SetObjectEntry( w2->GetMainShapeEntry() ); + } } else if( hypType()=="MaxElementArea" ) { StdMeshers::StdMeshers_MaxElementArea_var h = - StdMeshers::StdMeshers_MaxElementArea::_narrow( hypothesis() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); + StdMeshers::StdMeshers_MaxElementArea::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetMaxElementArea" ); h->SetMaxElementArea( params[0].myValue.toDouble() ); } else if( hypType()=="MaxElementVolume" ) { StdMeshers::StdMeshers_MaxElementVolume_var h = - StdMeshers::StdMeshers_MaxElementVolume::_narrow( hypothesis() ); + StdMeshers::StdMeshers_MaxElementVolume::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetMaxElementVolume" ); h->SetMaxElementVolume( params[0].myValue.toDouble() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); } else if( hypType()=="StartEndLength" ) { StdMeshers::StdMeshers_StartEndLength_var h = - StdMeshers::StdMeshers_StartEndLength::_narrow( hypothesis() ); - - h->SetLength( params[0].myValue.toDouble(), true ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); - h->SetLength( params[1].myValue.toDouble(), false ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); + StdMeshers::StdMeshers_StartEndLength::_narrow( hypothesis() ); + + StdMeshersGUI_SubShapeSelectorWdg* w = + widget< StdMeshersGUI_SubShapeSelectorWdg >( 2 ); + + h->SetVarParameter( params[0].text(), "SetStartLength" ); + h->SetStartLength( params[0].myValue.toDouble() ); + h->SetVarParameter( params[1].text(), "SetEndLength" ); + h->SetEndLength( params[1].myValue.toDouble() ); + if (w) { + h->SetReversedEdges( w->GetListOfIDs() ); + h->SetObjectEntry( w->GetMainShapeEntry() ); + } } else if( hypType()=="Deflection1D" ) { StdMeshers::StdMeshers_Deflection1D_var h = - StdMeshers::StdMeshers_Deflection1D::_narrow( hypothesis() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); + StdMeshers::StdMeshers_Deflection1D::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetDeflection" ); h->SetDeflection( params[0].myValue.toDouble() ); } else if( hypType()=="AutomaticLength" ) { StdMeshers::StdMeshers_AutomaticLength_var h = - StdMeshers::StdMeshers_AutomaticLength::_narrow( hypothesis() ); + StdMeshers::StdMeshers_AutomaticLength::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetFineness" ); h->SetFineness( params[0].myValue.toDouble() ); } else if( hypType()=="NumberOfLayers" ) { StdMeshers::StdMeshers_NumberOfLayers_var h = - StdMeshers::StdMeshers_NumberOfLayers::_narrow( hypothesis() ); + StdMeshers::StdMeshers_NumberOfLayers::_narrow( hypothesis() ); + h->SetVarParameter( params[0].text(), "SetNumberOfLayers" ); h->SetNumberOfLayers( params[0].myValue.toInt() ); - h->SetParameters(SMESHGUI::JoinObjectParameters(aVariablesList)); } else if( hypType()=="LayerDistribution" ) { StdMeshers::StdMeshers_LayerDistribution_var h = - StdMeshers::StdMeshers_LayerDistribution::_narrow( hypothesis() ); + StdMeshers::StdMeshers_LayerDistribution::_narrow( hypothesis() ); + StdMeshersGUI_LayerDistributionParamWdg* w = + widget< StdMeshersGUI_LayerDistributionParamWdg >( 0 ); + + h->SetLayerDistribution( w->GetHypothesis() ); + } + else if( hypType()=="NumberOfLayers2D" ) + { + StdMeshers::StdMeshers_NumberOfLayers2D_var h = + StdMeshers::StdMeshers_NumberOfLayers2D::_narrow( hypothesis() ); + + h->SetVarParameter( params[0].text(), "SetNumberOfLayers" ); + h->SetNumberOfLayers( params[0].myValue.toInt() ); + } + else if( hypType()=="LayerDistribution2D" ) + { + StdMeshers::StdMeshers_LayerDistribution2D_var h = + StdMeshers::StdMeshers_LayerDistribution2D::_narrow( hypothesis() ); StdMeshersGUI_LayerDistributionParamWdg* w = widget< StdMeshersGUI_LayerDistributionParamWdg >( 0 ); h->SetLayerDistribution( w->GetHypothesis() ); - h->SetParameters(w->GetHypothesis()->GetParameters()); - w->GetHypothesis()->ClearParameters(); } else if( hypType()=="ProjectionSource1D" ) { StdMeshers::StdMeshers_ProjectionSource1D_var h = - StdMeshers::StdMeshers_ProjectionSource1D::_narrow( hypothesis() ); + StdMeshers::StdMeshers_ProjectionSource1D::_narrow( hypothesis() ); h->SetSourceEdge ( geomFromWdg ( getWidgetForParam( 0 ))); h->SetSourceMesh ( meshFromWdg ( getWidgetForParam( 1 ))); @@ -519,7 +633,7 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const else if( hypType()=="ProjectionSource2D" ) { StdMeshers::StdMeshers_ProjectionSource2D_var h = - StdMeshers::StdMeshers_ProjectionSource2D::_narrow( hypothesis() ); + StdMeshers::StdMeshers_ProjectionSource2D::_narrow( hypothesis() ); h->SetSourceFace ( geomFromWdg ( getWidgetForParam( 0 ))); h->SetSourceMesh ( meshFromWdg ( getWidgetForParam( 1 ))); @@ -531,7 +645,7 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const else if( hypType()=="ProjectionSource3D" ) { StdMeshers::StdMeshers_ProjectionSource3D_var h = - StdMeshers::StdMeshers_ProjectionSource3D::_narrow( hypothesis() ); + StdMeshers::StdMeshers_ProjectionSource3D::_narrow( hypothesis() ); h->SetSource3DShape ( geomFromWdg ( getWidgetForParam( 0 ))); h->SetSourceMesh ( meshFromWdg ( getWidgetForParam( 1 ))); @@ -540,6 +654,63 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const geomFromWdg ( getWidgetForParam( 3 )), // tgt1 geomFromWdg ( getWidgetForParam( 5 ))); // tgt2 } + else if( hypType()=="ImportSource1D" ) + { + StdMeshers::StdMeshers_ImportSource1D_var h = + StdMeshers::StdMeshers_ImportSource1D::_narrow( hypothesis() ); + + SMESH::ListOfGroups_var groups = groupsFromWdg( getWidgetForParam( 0 )); + h->SetSourceEdges( groups.in() ); + QCheckBox* toCopyMesh = widget< QCheckBox >( 1 ); + QCheckBox* toCopyGroups = widget< QCheckBox >( 2 ); + h->SetCopySourceMesh( toCopyMesh->isChecked(), toCopyGroups->isChecked()); + } + else if( hypType()=="ImportSource2D" ) + { + StdMeshers::StdMeshers_ImportSource2D_var h = + StdMeshers::StdMeshers_ImportSource2D::_narrow( hypothesis() ); + + SMESH::ListOfGroups_var groups = groupsFromWdg( getWidgetForParam( 0 )); + h->SetSourceFaces( groups.in() ); + QCheckBox* toCopyMesh = widget< QCheckBox >( 1 ); + QCheckBox* toCopyGroups = widget< QCheckBox >( 2 ); + h->SetCopySourceMesh( toCopyMesh->isChecked(), toCopyGroups->isChecked()); + } + else if( hypType()=="ViscousLayers" ) + { + StdMeshers::StdMeshers_ViscousLayers_var h = + StdMeshers::StdMeshers_ViscousLayers::_narrow( hypothesis() ); + + h->SetVarParameter( params[0].text(), "SetTotalThickness" ); + h->SetTotalThickness( params[0].myValue.toDouble() ); + h->SetVarParameter( params[1].text(), "SetNumberLayers" ); + h->SetNumberLayers ( params[1].myValue.toInt() ); + h->SetVarParameter( params[2].text(), "SetStretchFactor" ); + h->SetStretchFactor ( params[2].myValue.toDouble() ); + + if ( StdMeshersGUI_SubShapeSelectorWdg* idsWg = + widget< StdMeshersGUI_SubShapeSelectorWdg >( 3 )) + { + h->SetIgnoreFaces( idsWg->GetListOfIDs() ); + } + } + else if( hypType()=="QuadrangleParams" ) + { + StdMeshers::StdMeshers_QuadrangleParams_var h = + StdMeshers::StdMeshers_QuadrangleParams::_narrow( hypothesis() ); + StdMeshersGUI_SubShapeSelectorWdg* w1 = + widget< StdMeshersGUI_SubShapeSelectorWdg >( 0 ); + StdMeshersGUI_QuadrangleParamWdg* w2 = + widget< StdMeshersGUI_QuadrangleParamWdg >( 1 ); + if (w1 && w2) { + if (w1->GetListSize() > 0) { + h->SetTriaVertex(w1->GetListOfIDs()[0]); // getlist must be called once + const char * entry = w1->GetMainShapeEntry(); + h->SetObjectEntry(entry); + } + h->SetQuadType(StdMeshers::QuadType(w2->GetType())); + } + } } return valueStr; } @@ -547,15 +718,15 @@ QString StdMeshersGUI_StdHypothesisCreator::storeParams() const //================================================================================ /*! * \brief Return parameter values as SMESHGUI_GenericHypothesisCreator::StdParam - * \param p - list of parameters - * \retval bool - success flag - * - * Is called from SMESHGUI_GenericHypothesisCreator::buildStdFrame(). - * Parameters will be shown using "standard" controls: - * Int by QtxIntSpinBox - * Double by SMESHGUI_SpinBox - * String by QLineEdit - * getCustomWidget() allows to redefine control for a parameter + * \param p - list of parameters + * \retval bool - success flag + * + * Is called from SMESHGUI_GenericHypothesisCreator::buildStdFrame(). + * Parameters will be shown using "standard" controls: + * Int by QtxIntSpinBox + * Double by SMESHGUI_SpinBox + * String by QLineEdit + * getCustomWidget() allows to redefine control for a parameter */ //================================================================================ @@ -574,7 +745,7 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const p.append( item ); customWidgets()->append(0); } - + SMESH::SMESH_Hypothesis_var hyp = initParamsHypothesis(); SMESH::ListOfParameters_var aParameters = hyp->GetLastParameters(); @@ -582,17 +753,16 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const { StdMeshers::StdMeshers_LocalLength_var h = StdMeshers::StdMeshers_LocalLength::_narrow( hyp ); - + item.myName = tr("SMESH_LOCAL_LENGTH_PARAM"); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetLength")) item.myValue = h->GetLength(); p.append( item ); - + item.myName = tr("SMESH_LOCAL_LENGTH_PRECISION"); - if(!initVariableName(aParameters,item,1)) + if(!initVariableName( hyp, item, "SetPrecision")) item.myValue = h->GetPrecision(); p.append( item ); - } else if( hypType()=="MaxLength" ) { @@ -610,7 +780,8 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const } item.myName = tr("SMESH_LOCAL_LENGTH_PARAM"); - item.myValue = h->GetLength(); + if(!initVariableName( hyp, item, "SetLength")) + item.myValue = h->GetLength(); p.append( item ); customWidgets()->append(0); @@ -633,7 +804,7 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const StdMeshers::StdMeshers_SegmentLengthAroundVertex::_narrow( hyp ); item.myName = tr("SMESH_LOCAL_LENGTH_PARAM"); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetLength")) item.myValue = h->GetLength(); p.append( item ); @@ -644,22 +815,78 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const StdMeshers::StdMeshers_Arithmetic1D::_narrow( hyp ); item.myName = tr( "SMESH_START_LENGTH_PARAM" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetStartLength" )) item.myValue = h->GetLength( true ); p.append( item ); + customWidgets()->append (0); + item.myName = tr( "SMESH_END_LENGTH_PARAM" ); - if(!initVariableName(aParameters,item,1)) + if(!initVariableName( hyp, item, "SetEndLength" )) item.myValue = h->GetLength( false ); p.append( item ); + + customWidgets()->append (0); + + item.myName = tr( "SMESH_REVERSED_EDGES" ); + p.append( item ); + + StdMeshersGUI_SubShapeSelectorWdg* aDirectionWidget = + new StdMeshersGUI_SubShapeSelectorWdg(); + QString aGeomEntry = SMESHGUI_GenericHypothesisCreator::getShapeEntry(); + QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry(); + if ( aGeomEntry == "" ) + aGeomEntry = h->GetObjectEntry(); + + aDirectionWidget->SetGeomShapeEntry( aGeomEntry ); + aDirectionWidget->SetMainShapeEntry( aMainEntry ); + aDirectionWidget->SetListOfIDs( h->GetReversedEdges() ); + aDirectionWidget->showPreview( true ); + customWidgets()->append ( aDirectionWidget ); } + + + else if( hypType()=="FixedPoints1D" ) + { + StdMeshers::StdMeshers_FixedPoints1D_var h = + StdMeshers::StdMeshers_FixedPoints1D::_narrow( hyp ); + + item.myName = tr( "SMESH_FIXED_POINTS" ); + p.append( item ); + + StdMeshersGUI_FixedPointsParamWdg* aFixedPointsWidget = + new StdMeshersGUI_FixedPointsParamWdg(); + + if ( !isCreation() ) { + aFixedPointsWidget->SetListOfPoints( h->GetPoints() ); + aFixedPointsWidget->SetListOfSegments( h->GetNbSegments() ); + } + customWidgets()->append( aFixedPointsWidget ); + + item.myName = tr( "SMESH_REVERSED_EDGES" ); + p.append( item ); + + StdMeshersGUI_SubShapeSelectorWdg* aDirectionWidget = + new StdMeshersGUI_SubShapeSelectorWdg(); + QString anEntry = SMESHGUI_GenericHypothesisCreator::getShapeEntry(); + QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry(); + if ( anEntry == "" ) + anEntry = h->GetObjectEntry(); + aDirectionWidget->SetGeomShapeEntry( anEntry ); + aDirectionWidget->SetMainShapeEntry( aMainEntry ); + aDirectionWidget->SetListOfIDs( h->GetReversedEdges() ); + aDirectionWidget->showPreview( true ); + customWidgets()->append ( aDirectionWidget ); + } + + else if( hypType()=="MaxElementArea" ) { StdMeshers::StdMeshers_MaxElementArea_var h = StdMeshers::StdMeshers_MaxElementArea::_narrow( hyp ); item.myName = tr( "SMESH_MAX_ELEMENT_AREA_PARAM" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetMaxElementArea" )) item.myValue = h->GetMaxElementArea(); p.append( item ); @@ -670,7 +897,7 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const StdMeshers::StdMeshers_MaxElementVolume::_narrow( hyp ); item.myName = tr( "SMESH_MAX_ELEMENT_VOLUME_PARAM" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetMaxElementVolume" )) item.myValue = h->GetMaxElementVolume(); p.append( item ); } @@ -681,15 +908,31 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const item.myName = tr( "SMESH_START_LENGTH_PARAM" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetStartLength" )) item.myValue = h->GetLength( true ); p.append( item ); + customWidgets()->append(0); item.myName = tr( "SMESH_END_LENGTH_PARAM" ); - if(!initVariableName(aParameters,item,1)) + if(!initVariableName( hyp, item, "SetEndLength" )) item.myValue = h->GetLength( false ); p.append( item ); - + customWidgets()->append(0); + + item.myName = tr( "SMESH_REVERSED_EDGES" ); + p.append( item ); + + StdMeshersGUI_SubShapeSelectorWdg* aDirectionWidget = + new StdMeshersGUI_SubShapeSelectorWdg(); + QString anEntry = SMESHGUI_GenericHypothesisCreator::getShapeEntry(); + QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry(); + if ( anEntry == "" ) + anEntry = h->GetObjectEntry(); + aDirectionWidget->SetGeomShapeEntry( anEntry ); + aDirectionWidget->SetMainShapeEntry( aMainEntry ); + aDirectionWidget->SetListOfIDs( h->GetReversedEdges() ); + aDirectionWidget->showPreview( true ); + customWidgets()->append ( aDirectionWidget ); } else if( hypType()=="Deflection1D" ) { @@ -697,7 +940,7 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const StdMeshers::StdMeshers_Deflection1D::_narrow( hyp ); item.myName = tr( "SMESH_DEFLECTION1D_PARAM" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetDeflection" )) item.myValue = h->GetDeflection(); p.append( item ); } @@ -709,8 +952,10 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const item.myName = tr( "SMESH_FINENESS_PARAM" ); //item.myValue = h->GetFineness(); p.append( item ); - customWidgets()->append - ( new TDoubleSliderWith2Lables( "0 ", " 1", h->GetFineness(), 0, 1, 0.01, 0 )); + SMESHGUI_SpinBox* _autoLengthSpinBox = new SMESHGUI_SpinBox(dlg()); + _autoLengthSpinBox->RangeStepAndValidator(0, 1, 0.01, "length_precision"); + _autoLengthSpinBox->SetValue(h->GetFineness()); + customWidgets()->append( _autoLengthSpinBox); } else if( hypType()=="NumberOfLayers" ) { @@ -718,25 +963,37 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const StdMeshers::StdMeshers_NumberOfLayers::_narrow( hyp ); item.myName = tr( "SMESH_NUMBER_OF_LAYERS" ); - if(!initVariableName(aParameters,item,0)) + if(!initVariableName( hyp, item, "SetNumberOfLayers" )) item.myValue = (int) h->GetNumberOfLayers(); p.append( item ); } else if( hypType()=="LayerDistribution" ) - { - StdMeshers::StdMeshers_LayerDistribution_var h = + { + StdMeshers::StdMeshers_LayerDistribution_var h = StdMeshers::StdMeshers_LayerDistribution::_narrow( hyp ); - + item.myName = tr( "SMESH_LAYERS_DISTRIBUTION" ); p.append( item ); + initVariableName( hyp, item, "SetLayerDistribution" ); + customWidgets()->append + ( new StdMeshersGUI_LayerDistributionParamWdg( h->GetLayerDistribution(), hypName(), dlg())); + } + else if( hypType()=="NumberOfLayers2D" ) + { + StdMeshers::StdMeshers_NumberOfLayers2D_var h = + StdMeshers::StdMeshers_NumberOfLayers2D::_narrow( hyp ); - //Set into not published hypo last variables - QStringList aLastVarsList; - for(int i = 0;ilength();i++) - aLastVarsList.append(QString(aParameters[i].in())); + item.myName = tr( "SMESH_NUMBER_OF_LAYERS" ); + if(!initVariableName( hyp, item, "SetNumberOfLayers" )) + item.myValue = (int) h->GetNumberOfLayers(); + p.append( item ); + } + else if( hypType()=="LayerDistribution2D" ) + { + StdMeshers::StdMeshers_LayerDistribution2D_var h = + StdMeshers::StdMeshers_LayerDistribution2D::_narrow( hyp ); - if(!aLastVarsList.isEmpty()) - h->GetLayerDistribution()->SetLastParameters(SMESHGUI::JoinObjectParameters(aLastVarsList)); - + item.myName = tr( "SMESH_LAYERS_DISTRIBUTION" ); p.append( item ); + initVariableName( hyp, item, "SetLayerDistribution" ); customWidgets()->append ( new StdMeshersGUI_LayerDistributionParamWdg( h->GetLayerDistribution(), hypName(), dlg())); } @@ -806,6 +1063,135 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const customWidgets()->append( newObjRefParamWdg( filterForShapeOfDim( 0 ), h->GetTargetVertex( 2 ))); } + else if( hypType()=="ImportSource1D" ) + { + StdMeshers::StdMeshers_ImportSource1D_var h = + StdMeshers::StdMeshers_ImportSource1D::_narrow( hyp ); + + SMESH::string_array_var groupEntries = h->GetSourceEdges(); + CORBA::Boolean toCopyMesh, toCopyGroups; + h->GetCopySourceMesh(toCopyMesh, toCopyGroups); + + item.myName = tr( "SMESH_SOURCE_EDGES" ); p.append( item ); + customWidgets()->append( newObjRefParamWdg( new SMESH_TypeFilter( GROUP_EDGE ), + groupEntries)); + + item.myName = tr( "SMESH_COPY_MESH" ); p.append( item ); + QCheckBox* aQCheckBox = new QCheckBox(dlg()); + aQCheckBox->setChecked( toCopyMesh ); + connect( aQCheckBox, SIGNAL( stateChanged(int) ), this, SLOT( onValueChanged() )); + customWidgets()->append( aQCheckBox ); + + item.myName = tr( "SMESH_TO_COPY_GROUPS" ); p.append( item ); + aQCheckBox = new QCheckBox(dlg()); + aQCheckBox->setChecked( toCopyGroups ); + aQCheckBox->setEnabled( toCopyMesh ); + customWidgets()->append( aQCheckBox ); + } + else if( hypType()=="ImportSource2D" ) + { + StdMeshers::StdMeshers_ImportSource2D_var h = + StdMeshers::StdMeshers_ImportSource2D::_narrow( hyp ); + + SMESH::string_array_var groupEntries = h->GetSourceFaces(); + CORBA::Boolean toCopyMesh, toCopyGroups; + h->GetCopySourceMesh(toCopyMesh, toCopyGroups); + + item.myName = tr( "SMESH_SOURCE_FACES" ); p.append( item ); + customWidgets()->append( newObjRefParamWdg( new SMESH_TypeFilter( GROUP_FACE ), + groupEntries)); + + item.myName = tr( "SMESH_COPY_MESH" ); p.append( item ); + QCheckBox* aQCheckBox = new QCheckBox(dlg()); + aQCheckBox->setChecked( toCopyMesh ); + connect( aQCheckBox, SIGNAL( stateChanged(int) ), this, SLOT( onValueChanged() )); + customWidgets()->append( aQCheckBox ); + + item.myName = tr( "SMESH_COPY_GROUPS" ); p.append( item ); + aQCheckBox = new QCheckBox(dlg()); + aQCheckBox->setChecked( toCopyGroups ); + aQCheckBox->setEnabled( toCopyMesh ); + customWidgets()->append( aQCheckBox ); + } + else if( hypType()=="ViscousLayers" ) + { + StdMeshers::StdMeshers_ViscousLayers_var h = + StdMeshers::StdMeshers_ViscousLayers::_narrow( hyp ); + + item.myName = tr( "SMESH_TOTAL_THICKNESS" ); + if(!initVariableName( hyp, item, "SetTotalThickness" )) + item.myValue = h->GetTotalThickness(); + p.append( item ); + customWidgets()->append (0); + + item.myName = tr( "SMESH_NUMBER_OF_LAYERS" ); + if(!initVariableName( hyp, item, "SetNumberLayers" )) + item.myValue = h->GetNumberLayers(); + p.append( item ); + customWidgets()->append (0); + + item.myName = tr( "SMESH_STRETCH_FACTOR" ); + if(!initVariableName( hyp, item, "SetStretchFactor" )) + item.myValue = h->GetStretchFactor(); + p.append( item ); + customWidgets()->append (0); + + QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry(); + if ( !aMainEntry.isEmpty() ) + { + item.myName = tr( "SMESH_FACES_WO_LAYERS" ); + p.append( item ); + + StdMeshersGUI_SubShapeSelectorWdg* idsWg = + new StdMeshersGUI_SubShapeSelectorWdg(0,TopAbs_FACE); + + idsWg->SetGeomShapeEntry( aMainEntry ); + idsWg->SetMainShapeEntry( aMainEntry ); + idsWg->SetListOfIDs( h->GetIgnoreFaces() ); + idsWg->showPreview( true ); + customWidgets()->append ( idsWg ); + } + } + else if (hypType() == "QuadrangleParams") + { + StdMeshers::StdMeshers_QuadrangleParams_var h = + StdMeshers::StdMeshers_QuadrangleParams::_narrow(hyp); + + item.myName = tr("SMESH_BASE_VERTEX"); + p.append(item); + + StdMeshersGUI_SubShapeSelectorWdg* aDirectionWidget = + new StdMeshersGUI_SubShapeSelectorWdg(0, TopAbs_VERTEX); + aDirectionWidget->SetMaxSize(1); + QString anEntry = SMESHGUI_GenericHypothesisCreator::getShapeEntry(); + QString aMainEntry = SMESHGUI_GenericHypothesisCreator::getMainShapeEntry(); + if (anEntry == "") + anEntry = h->GetObjectEntry(); + aDirectionWidget->SetGeomShapeEntry(anEntry); + aDirectionWidget->SetMainShapeEntry(aMainEntry); + if (!isCreation()) { + SMESH::long_array_var aVec = new SMESH::long_array; + int vertID = h->GetTriaVertex(); + if (vertID > 0) { + aVec->length(1); + aVec[0] = vertID; + aDirectionWidget->SetListOfIDs(aVec); + } + } + aDirectionWidget->showPreview(true); + + item.myName = tr("SMESH_QUAD_TYPE"); + p.append(item); + + StdMeshersGUI_QuadrangleParamWdg* aTypeWidget = + new StdMeshersGUI_QuadrangleParamWdg(); + if (!isCreation()) { + aTypeWidget->SetType(int(h->GetQuadType())); + } + + customWidgets()->append(aDirectionWidget); + customWidgets()->append(aTypeWidget); + } else res = false; return res; @@ -814,50 +1200,66 @@ bool StdMeshersGUI_StdHypothesisCreator::stdParams( ListOfStdParams& p ) const //================================================================================ /*! * \brief tune "standard" control - * \param w - control widget - * \param int - parameter index + * \param w - control widget + * \param int - parameter index */ //================================================================================ void StdMeshersGUI_StdHypothesisCreator::attuneStdWidget (QWidget* w, const int) const { SMESHGUI_SpinBox* sb = w->inherits( "SMESHGUI_SpinBox" ) ? ( SMESHGUI_SpinBox* )w : 0; - if( hypType()=="LocalLength" && sb ) - { - if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PARAM")) - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); - else if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PRECISION")) - sb->RangeStepAndValidator( 0.0, 1.0, 0.05, 7 ); - } - else if( hypType()=="Arithmetic1D" && sb ) - { - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); - } - else if( hypType()=="MaxLength" && sb ) - { - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); - sb->setEnabled( !widget< QCheckBox >( 1 )->isChecked() ); - } - else if( hypType()=="MaxElementArea" && sb ) - { - sb->RangeStepAndValidator( VALUE_SMALL_2, VALUE_MAX_2, 1.0, 6 ); - } - else if( hypType()=="MaxElementVolume" && sb ) + if ( sb ) { - sb->RangeStepAndValidator( VALUE_SMALL_3, VALUE_MAX_3, 1.0, 6 ); - } - else if( hypType()=="StartEndLength" && sb ) - { - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); - } - else if( hypType()=="Deflection1D" && sb ) - { - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); - } - else if ( sb ) // default validator for possible ancestors - { - sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, 6 ); + if( hypType()=="LocalLength" ) + { + if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PARAM")) + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" ); + else if (sb->objectName() == tr("SMESH_LOCAL_LENGTH_PRECISION")) + sb->RangeStepAndValidator( 0.0, 1.0, 0.05, "len_tol_precision" ); + } + else if( hypType()=="Arithmetic1D" ) + { + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" ); + } + else if( hypType()=="MaxLength" ) + { + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" ); + sb->setEnabled( !widget< QCheckBox >( 1 )->isChecked() ); + } + else if( hypType()=="MaxElementArea" ) + { + sb->RangeStepAndValidator( VALUE_SMALL_2, VALUE_MAX_2, 1.0, "area_precision" ); + } + else if( hypType()=="MaxElementVolume" ) + { + sb->RangeStepAndValidator( VALUE_SMALL_3, VALUE_MAX_3, 1.0, "vol_precision" ); + } + else if( hypType()=="StartEndLength" ) + { + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" ); + } + else if( hypType()=="Deflection1D" ) + { + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "parametric_precision" ); + } + else if( hypType()=="ViscousLayers" ) + { + if (sb->objectName() == tr("SMESH_STRETCH_FACTOR")) + sb->RangeStepAndValidator( 1.0, VALUE_MAX, 0.1, "parametric_precision" ); + else + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" ); + } + else // default validator for possible ancestors + { + sb->RangeStepAndValidator( VALUE_SMALL, VALUE_MAX, 1.0, "length_precision" ); + } } +// else if ( QtxIntSpinBox* ib = w->inherits( "QtxIntSpinBox" ) ? ( QtxIntSpinBox* )w : 0) +// { +// if( hypType()=="ViscousLayers" ) +// { +// } +// } } //================================================================================ @@ -918,14 +1320,22 @@ QString StdMeshersGUI_StdHypothesisCreator::hypTypeName( const QString& t ) cons types.insert( "StartEndLength", "START_END_LENGTH" ); types.insert( "Deflection1D", "DEFLECTION1D" ); types.insert( "Arithmetic1D", "ARITHMETIC_1D" ); + types.insert( "FixedPoints1D", "FIXED_POINTS_1D" ); types.insert( "AutomaticLength", "AUTOMATIC_LENGTH" ); types.insert( "ProjectionSource1D", "PROJECTION_SOURCE_1D" ); types.insert( "ProjectionSource2D", "PROJECTION_SOURCE_2D" ); types.insert( "ProjectionSource3D", "PROJECTION_SOURCE_3D" ); + types.insert( "ImportSource1D", "IMPORT_SOURCE_1D" ); + types.insert( "ImportSource2D", "IMPORT_SOURCE_2D" ); types.insert( "NumberOfLayers", "NUMBER_OF_LAYERS" ); types.insert( "LayerDistribution", "LAYER_DISTRIBUTION" ); + types.insert( "NumberOfLayers2D", "NUMBER_OF_LAYERS_2D" ); + types.insert( "LayerDistribution2D", "LAYER_DISTRIBUTION" ); types.insert( "SegmentLengthAroundVertex", "SEGMENT_LENGTH_AROUND_VERTEX" ); types.insert( "MaxLength", "MAX_LENGTH" ); + types.insert( "ViscousLayers", "VISCOUS_LAYERS" ); + types.insert( "QuadrangleParams", "QUADRANGLE_PARAMS" ); + types.insert( "CartesianParameters3D", "CARTESIAN_PARAMS" ); } QString res; @@ -971,9 +1381,9 @@ bool StdMeshersGUI_StdHypothesisCreator::getParamFromCustomWidget( StdParam & pa QWidget* widget) const { if ( hypType()=="AutomaticLength" ) { - TDoubleSliderWith2Lables* w = dynamic_cast( widget ); + SMESHGUI_SpinBox* w = dynamic_cast( widget ); if ( w ) { - param.myValue = w->value(); + param.myValue = w->GetValue(); return true; } } @@ -998,6 +1408,33 @@ bool StdMeshersGUI_StdHypothesisCreator::getParamFromCustomWidget( StdParam & pa param.myValue = w->GetValue(); return true; } + if ( widget->inherits( "StdMeshersGUI_SubShapeSelectorWdg" )) + { + const StdMeshersGUI_SubShapeSelectorWdg * w = + static_cast( widget ); + param.myValue = w->GetValue(); + return true; + } + if ( widget->inherits( "StdMeshersGUI_QuadrangleParamWdg" )) + { + //const StdMeshersGUI_QuadrangleParamWdg * w = + // static_cast( widget ); + param.myValue = "QuadType"; + return true; + } + if ( widget->inherits( "StdMeshersGUI_FixedPointsParamWdg" )) + { + const StdMeshersGUI_FixedPointsParamWdg * w = + static_cast( widget ); + param.myValue = w->GetValue(); + return true; + } + if ( widget->inherits( "QCheckBox" )) + { + //const QCheckBox * w = static_cast( widget ); + //param.myValue = w->isChecked(); + return true; + } return false; } @@ -1009,7 +1446,8 @@ bool StdMeshersGUI_StdHypothesisCreator::getParamFromCustomWidget( StdParam & pa void StdMeshersGUI_StdHypothesisCreator::onReject() { - if ( hypType().startsWith("ProjectionSource" )) + if ( hypType().startsWith("ProjectionSource" ) || + hypType().startsWith("ImportSource" )) { // Uninstall filters of StdMeshersGUI_ObjectReferenceParamWdg deactivateObjRefParamWdg( customWidgets() ); @@ -1018,13 +1456,14 @@ void StdMeshersGUI_StdHypothesisCreator::onReject() //================================================================================ /*! - * \brief + * \brief Update widgets dependent on paramWidget */ //================================================================================ void StdMeshersGUI_StdHypothesisCreator::valueChanged( QWidget* paramWidget) { - if ( hypType() == "MaxLength" && paramWidget == getWidgetForParam(1) ) { + if ( hypType() == "MaxLength" && paramWidget == getWidgetForParam(1) ) + { getWidgetForParam(0)->setEnabled( !widget< QCheckBox >( 1 )->isChecked() ); if ( !getWidgetForParam(0)->isEnabled() ) { StdMeshers::StdMeshers_MaxLength_var h = @@ -1032,6 +1471,20 @@ void StdMeshersGUI_StdHypothesisCreator::valueChanged( QWidget* paramWidget) widget< QtxDoubleSpinBox >( 0 )->setValue( h->GetPreestimatedLength() ); } } + else if ( hypType().startsWith("ImportSource") && paramWidget == getWidgetForParam(1) ) + { + QCheckBox* toCopyMesh = (QCheckBox*) paramWidget; + QCheckBox* toCopyGroups = widget< QCheckBox >( 2 ); + if ( !toCopyMesh->isChecked() ) + { + toCopyGroups->setChecked( false ); + toCopyGroups->setEnabled( false ); + } + else + { + toCopyGroups->setEnabled( true ); + } + } } //================================================================================ @@ -1040,13 +1493,13 @@ void StdMeshersGUI_StdHypothesisCreator::valueChanged( QWidget* paramWidget) */ //================================================================================ -bool StdMeshersGUI_StdHypothesisCreator::initVariableName(SMESH::ListOfParameters_var theParameters, - StdParam &theParams, - int order) const +bool StdMeshersGUI_StdHypothesisCreator::initVariableName(SMESH::SMESH_Hypothesis_var theHyp, + StdParam & theParams, + const char* theMethod) const { - QString aVaribaleName = (theParameters->length() > order) ? QString(theParameters[order].in()) : QString(""); + QString aVaribaleName = getVariableName( theMethod ); theParams.isVariable = !aVaribaleName.isEmpty(); - if(theParams.isVariable) + if (theParams.isVariable) theParams.myValue = aVaribaleName; return theParams.isVariable; diff --git a/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.h b/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.h index 0973c5c03..c08e255e7 100644 --- a/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.h +++ b/src/StdMeshersGUI/StdMeshersGUI_StdHypothesisCreator.h @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : StdMeshersGUI_StdHypothesisCreator.h // Author : Alexander SOLOVYOV, Open CASCADE S.A.S. // @@ -59,7 +60,7 @@ protected: virtual QWidget* getWidgetForParam( int paramIndex ) const; virtual ListOfWidgets* customWidgets() const; virtual void onReject(); - virtual bool initVariableName(SMESH::ListOfParameters_var theParameters, StdParam& theParams, int order) const; + bool initVariableName(SMESH::SMESH_Hypothesis_var theHyp, StdParam& theParams, const char* theMethod) const; virtual void valueChanged( QWidget* ); diff --git a/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.cxx b/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.cxx new file mode 100644 index 000000000..8c2240803 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.cxx @@ -0,0 +1,610 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshersGUI_SubShapeSelectorWdg.cxx +// Author : Open CASCADE S.A.S. (dmv) +// SMESH includes +// +#include "StdMeshersGUI_SubShapeSelectorWdg.h" + +// SMESH Includes +#include "SMESH_Type.h" +#include "SMESHGUI_MeshUtils.h" +#include "SMESH_Actor.h" +#include "SMESH_PreviewActorsCollection.h" +#include "SMESH_ActorUtils.h" +#include "SMESHGUI_GroupUtils.h" +#include "SMESH_Gen_i.hxx" +#include "SMESHGUI_GEOMGenUtils.h" +#include "SMESH_LogicalFilter.hxx" + +// SVTK Includes +#include +#include +#include +#include + +// SALOME GUI includes +#include +#include +#include + +// SUIT Includes +#include + +// GEOM Includes +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include + +// OCCT includes +#include +#include +#include +#include +#include +#include + +// SALOME KERNEL includes +#include + + +#define SPACING 6 +#define MARGIN 0 + +//================================================================================ +/*! + * Constructor + */ +//================================================================================ + +StdMeshersGUI_SubShapeSelectorWdg +::StdMeshersGUI_SubShapeSelectorWdg( QWidget * parent, TopAbs_ShapeEnum aSubShType ): + QWidget( parent ), + myPreviewActor( 0 ), + myMaxSize( -1 ) +{ + QPixmap image0( SMESH::GetResourceMgr( mySMESHGUI )->loadPixmap( "SMESH", tr( "ICON_SELECT" ) ) ); + + QGridLayout* edgesLayout = new QGridLayout( this ); + edgesLayout->setMargin( MARGIN ); + edgesLayout->setSpacing( SPACING ); + + myListWidget = new QListWidget( this ); + myAddButton = new QPushButton( tr( "SMESH_BUT_ADD" ), this ); + myRemoveButton = new QPushButton( tr( "SMESH_BUT_REMOVE" ), this ); + myInfoLabel = new QLabel( this ); + myPrevButton = new QPushButton( "<<", this ); + myNextButton = new QPushButton( ">>", this ); + myListWidget->setSelectionMode( QListWidget::ExtendedSelection ); + + edgesLayout->addWidget(myListWidget, 0, 0, 3, 3); + edgesLayout->addWidget(myAddButton, 0, 3); + edgesLayout->addWidget(myRemoveButton, 1, 3); + edgesLayout->addWidget(myInfoLabel, 3, 0, 1, 3); + edgesLayout->addWidget(myPrevButton, 4, 0); + edgesLayout->addWidget(myNextButton, 4, 2); + + edgesLayout->setRowStretch(2, 5); + edgesLayout->setColumnStretch(1, 5); + + myListWidget->setMinimumWidth(300); + myInfoLabel->setMinimumWidth(300); + myInfoLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); + myInfoLabel->setAlignment(Qt::AlignCenter); + + mySubShType = aSubShType; + + init(); +} + +//================================================================================ +/*! + * Destructor + */ +//================================================================================ + +StdMeshersGUI_SubShapeSelectorWdg::~StdMeshersGUI_SubShapeSelectorWdg() +{ + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) { + if ( myPreviewActor ) { + myPreviewActor->RemoveFromRender( myRenderer ); + aViewWindow->Repaint(); + + delete myPreviewActor; + myPreviewActor = 0; + } + } + myEntry = ""; + myParamValue = ""; + myMainShape.Nullify(); + + if ( mySelectionMgr && myFilter ) + mySelectionMgr->removeFilter( myFilter ); + delete myFilter; myFilter=0; + + SUIT_SelectionFilter* filter; + foreach( filter, myGeomFilters ) + delete filter; +} + +//================================================================================ +/*! + * Create a layout, initialize fields + */ +//================================================================================ + +void StdMeshersGUI_SubShapeSelectorWdg::init() +{ + myParamValue = ""; + myIsNotCorrected = true; // to dont call the GetCorrectedValue method twice + myListOfIDs.clear(); + mySelectedIDs.clear(); + + myAddButton->setEnabled( false ); + myRemoveButton->setEnabled( false ); + + mySMESHGUI = SMESHGUI::GetSMESHGUI(); + mySelectionMgr = SMESH::GetSelectionMgr( mySMESHGUI ); + mySelector = (SMESH::GetViewWindow( mySMESHGUI ))->GetSelector(); + + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->SetSelectionMode( ActorSelection ); + + myFilter=0; + //setFilter(); + + connect( myAddButton, SIGNAL(clicked()), SLOT(onAdd())); + connect( myRemoveButton, SIGNAL(clicked()), SLOT(onRemove())); + connect( myPrevButton, SIGNAL(clicked()), SLOT(onPrevious())); + connect( myNextButton, SIGNAL(clicked()), SLOT(onNext())); + + connect( mySelectionMgr, SIGNAL(currentSelectionChanged()), this, SLOT(SelectionIntoArgument())); + connect( myListWidget, SIGNAL(itemSelectionChanged()), this, SLOT(onListSelectionChanged())); + + updateState(); +} + +//================================================================================ +/*! + * \brief Install filters to select sub-shapes of mySubShType or their groups + */ +//================================================================================ + +void StdMeshersGUI_SubShapeSelectorWdg::setFilter() +{ + SalomeApp_Study* study = mySMESHGUI->activeStudy(); + GEOM_TypeFilter* typeFilter = new GEOM_TypeFilter(study, mySubShType, /*isShapeType=*/true ); + GEOM_CompoundFilter* gpoupFilter = new GEOM_CompoundFilter(study); + gpoupFilter->addSubType( mySubShType ); + myGeomFilters.append( typeFilter ); + myGeomFilters.append( gpoupFilter ); + myFilter = new SMESH_LogicalFilter( myGeomFilters, SMESH_LogicalFilter::LO_OR ); + mySelectionMgr->installFilter( myFilter ); +} + +//================================================================================ +/*! + * Create a layout, initialize fields + */ +//================================================================================ + +void StdMeshersGUI_SubShapeSelectorWdg::showPreview( bool visible) +{ + if ( !myPreviewActor ) + return; + + if ( myIsShown != visible ) { + myPreviewActor->SetShown( visible ); + + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->Repaint(); + + myIsShown = visible; + } +} + +//================================================================================= +// function : SelectionIntoArgument() +// purpose : Called when selection as changed or other case +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::SelectionIntoArgument() +{ + if ( !myPreviewActor ) + return; + + mySelectedIDs.clear(); + + // get selected mesh + SALOME_ListIO aList; + mySelectionMgr->selectedObjects( aList ); + int nbSel = aList.Extent(); + + if (nbSel > 0) { + SALOME_ListIteratorOfListIO anIt (aList); + + for ( ; anIt.More(); anIt.Next()) { // Loop on selected objects + Handle(SALOME_InteractiveObject) IO = anIt.Value(); + + GEOM::GEOM_Object_var aGeomObj = GetGeomObjectByEntry( IO->getEntry() ); + if ( !CORBA::is_nil( aGeomObj ) ) { // Selected Object From Study + GEOM::GEOM_Object_ptr aGeomFatherObj = aGeomObj->GetMainShape(); + QString aFatherEntry = ""; + QString aMainFatherEntry = ""; + TopoDS_Shape shape; + if ( !CORBA::is_nil( aGeomFatherObj ) ) { + // Get Main Shape + GEOM::GEOM_Object_var aGeomMain = GetGeomObjectByEntry( myEntry ); + if ( !CORBA::is_nil( aGeomMain ) && aGeomMain->GetType() == 37 ) { // Main Shape is a Group + GEOM::GEOM_Object_ptr aMainFatherObj = aGeomMain->GetMainShape(); + if ( !CORBA::is_nil( aMainFatherObj ) ) + aMainFatherEntry = aMainFatherObj->GetStudyEntry(); + } + aFatherEntry = aGeomFatherObj->GetStudyEntry(); + } + + if ( aFatherEntry != "" && ( aFatherEntry == myEntry || aFatherEntry == aMainFatherEntry ) ) + { + if ( aGeomObj->GetType() == 37 /*GEOM_GROUP*/ ) { // Selected Group that belongs the main object + GEOMBase::GetShape(aGeomObj, shape); + if ( !shape.IsNull() ) { + TopExp_Explorer exp( shape, mySubShType ); + for ( ; exp.More(); exp.Next() ) { + int index = myPreviewActor->GetIndexByShape( exp.Current() ); + if ( index ) { + mySelectedIDs.append( index ); + myPreviewActor->HighlightID( index ); + } + } + } + } else if ( aGeomObj->GetType() == 28 /*GEOM_SUBSHAPE*/ ) { + GEOMBase::GetShape(aGeomObj, shape); + if ( !shape.IsNull() && shape.ShapeType() == mySubShType ) { + int index = myPreviewActor->GetIndexByShape( shape ); + if ( index ) { + mySelectedIDs.append( index ); + myPreviewActor->HighlightID( index ); + } + } + } + } + } else { // Selected Actor from Actor Collection + QString anEntry = IO->getEntry(); + QString str = "_"; + int index = anEntry.lastIndexOf( str ); + anEntry.remove(0, index+1); + int ind = anEntry.toInt(); + if ( ind ) + mySelectedIDs.append( ind ); + } + } + } + // update add button + myAddButton->setEnabled( ( myListWidget->count() < myMaxSize || myMaxSize == -1 ) && mySelectedIDs.size() > 0 && ( mySelectedIDs.size() <= myMaxSize || myMaxSize == -1 ) ); + + //Connect Selected Ids in viewer and dialog's Ids list + bool signalsBlocked = myListWidget->blockSignals( true ); + myListWidget->clearSelection(); + if ( mySelectedIDs.size() > 0 ) { + for (int i = 0; i < mySelectedIDs.size(); i++) { + QString anID = QString(" %1").arg( mySelectedIDs.at(i) ); + QList anItems = myListWidget->findItems ( anID, Qt::MatchExactly ); + QListWidgetItem* item; + foreach(item, anItems) + item->setSelected(true); + } + } + myListWidget->blockSignals( signalsBlocked ); +} + +//================================================================================= +// function : onAdd() +// purpose : Called when Add Button Clicked +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::onAdd() +{ + if ( mySelectedIDs.size() < 1 ) + return; + + myListWidget->blockSignals( true ); + for (int i = 0; i < mySelectedIDs.size() && (myMaxSize == -1 || myListOfIDs.size() < myMaxSize); i++) { + if ( myListOfIDs.indexOf( mySelectedIDs.at(i) ) == -1 ) { + QString anID = QString(" %1").arg( mySelectedIDs.at(i) ); + + QListWidgetItem* anItem = new QListWidgetItem( anID, myListWidget ); + anItem->setSelected(true); + + myListOfIDs.append( mySelectedIDs.at(i) ); + } + } + onListSelectionChanged(); + myListWidget->blockSignals( false ); + myAddButton->setEnabled( myMaxSize == -1 || myListOfIDs.size() < myMaxSize ); +} + +//================================================================================= +// function : onRemove() +// purpose : Called when Remove Button Clicked +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::onRemove() +{ + if ( myListWidget->count() < 1 ) + return; + + myListWidget->blockSignals( true ); + QList selItems = myListWidget->selectedItems(); + QListWidgetItem* item; + foreach(item, selItems) { + QString idStr = item->text(); + int id = idStr.toInt(); + + int index = myListOfIDs.indexOf( id ); + myListOfIDs.removeAt( index ); + delete item; + } + + onListSelectionChanged(); + myListWidget->blockSignals( false ); + + myAddButton->setEnabled( true ); +} + +void StdMeshersGUI_SubShapeSelectorWdg::onPrevious() +{ + if ( myPreviewActor ) { + myPreviewActor->previous(); + myListWidget->clearSelection(); + updateButtons(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->Repaint(); + } +} + +void StdMeshersGUI_SubShapeSelectorWdg::onNext() +{ + if ( myPreviewActor ) { + myPreviewActor->next(); + myListWidget->clearSelection(); + updateButtons(); + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) + aViewWindow->Repaint(); + } +} + +//================================================================================= +// function : onListSelectionChanged() +// purpose : Called when selection in element list is changed +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::onListSelectionChanged() +{ + if ( !myPreviewActor ) + return; + + mySelectionMgr->clearSelected(); + TColStd_MapOfInteger aIndexes; + QList selItems = myListWidget->selectedItems(); + QListWidgetItem* anItem; + foreach(anItem, selItems) + myPreviewActor->HighlightID( anItem->text().toInt() ); + + // update remove button + myRemoveButton->setEnabled( selItems.size() > 0 ); +} + +//================================================================================= +// function : setGeomShape +// purpose : Called to set geometry whose sub-shapes are selected +//================================================================================ +void StdMeshersGUI_SubShapeSelectorWdg::SetGeomShapeEntry( const QString& theEntry ) +{ + if ( theEntry != "") { + myParamValue = theEntry; + myEntry = theEntry; + myGeomShape = GetTopoDSByEntry( theEntry ); + updateState(); + myIsNotCorrected = true; + } +} + +//================================================================================= +// function : updateState +// purpose : update Widget state +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::updateState() +{ + bool state = false; + if ( !myGeomShape.IsNull() ) + state = true; + myInfoLabel->setVisible( false ); + myPrevButton->setVisible( false ); + myNextButton->setVisible( false ); + + myListWidget->setEnabled( state ); + myAddButton->setEnabled( mySelectedIDs.size() > 0 ); + + if (state) { + myPreviewActor = new SMESH_PreviewActorsCollection(); + myPreviewActor->SetSelector( mySelector ); + myPreviewActor->Init( myGeomShape, mySubShType, myEntry ); + myPreviewActor->SetShown( false ); + myIsShown = false; + if ( SVTK_ViewWindow* aViewWindow = SMESH::GetViewWindow( mySMESHGUI )) { + myRenderer = aViewWindow->getRenderer(); + myPreviewActor->AddToRender( myRenderer ); + aViewWindow->Repaint(); + } + updateButtons(); + } +} + +//================================================================================= +// function : GetGeomObjectByEntry +// purpose : Called to get GeomObject +//================================================================================= +GEOM::GEOM_Object_var StdMeshersGUI_SubShapeSelectorWdg::GetGeomObjectByEntry( const QString& theEntry ) +{ + GEOM::GEOM_Object_var aGeomObj; + SALOMEDS::Study_var aStudy = SMESHGUI::GetSMESHGen()->GetCurrentStudy(); + if (aStudy != 0) { + SALOMEDS::SObject_var aSObj = aStudy->FindObjectID( theEntry.toLatin1().data() ); + SALOMEDS::GenericAttribute_var anAttr; + + if (!aSObj->_is_nil() && aSObj->FindAttribute(anAttr, "AttributeIOR")) { + SALOMEDS::AttributeIOR_var anIOR = SALOMEDS::AttributeIOR::_narrow(anAttr); + CORBA::String_var aVal = anIOR->Value(); + CORBA::Object_var obj = aStudy->ConvertIORToObject(aVal); + aGeomObj = GEOM::GEOM_Object::_narrow(obj); + } + } + return aGeomObj; +} + +//================================================================================= +// function : setObjectByEntry +// purpose : Called to get GeomObject +//================================================================================= +TopoDS_Shape StdMeshersGUI_SubShapeSelectorWdg::GetTopoDSByEntry( const QString& theEntry ) +{ + TopoDS_Shape shape; + GEOM::GEOM_Object_var aGeomObj = GetGeomObjectByEntry( theEntry ); + GEOMBase::GetShape(aGeomObj, shape); + return shape; +} + +//================================================================================= +// function : GetListOfIds +// purpose : Called to get the list of SubShapes IDs +//================================================================================= +SMESH::long_array_var StdMeshersGUI_SubShapeSelectorWdg::GetListOfIDs() +{ + SMESH::long_array_var anArray = new SMESH::long_array; + + if ( myMainEntry != "" && myIsNotCorrected ) + myListOfIDs = GetCorrectedListOfIDs( true ); + + int size = myListOfIDs.size(); + anArray->length( size ); + if ( size ) { + for (int i = 0; i < size; i++) { + anArray[i] = myListOfIDs.at(i); + } + } + return anArray; +} + +//================================================================================= +// function : SetListOfIds +// purpose : Called to set the list of SubShapes IDs +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::SetListOfIDs( SMESH::long_array_var theIds) +{ + mySelectedIDs.clear(); + myListOfIDs.clear(); + int size = theIds->length(); + for ( int i = 0; i < size; i++ ) + mySelectedIDs.append( theIds[ i ] ); + + mySelectedIDs = GetCorrectedListOfIDs( false ); + onAdd(); +} + +//================================================================================= +// function : SetMainShapeEntry +// purpose : Called to set the Entry of main shape of the mesh +//================================================================================= +void StdMeshersGUI_SubShapeSelectorWdg::SetMainShapeEntry( const QString& theEntry ) +{ + myMainEntry = theEntry; + myMainShape = GetTopoDSByEntry( theEntry ); + myIsNotCorrected = true; +} + +//================================================================================= +// function : GetMainShapeEntry +// purpose : Called to get the Main Object Entry +//================================================================================= +const char* StdMeshersGUI_SubShapeSelectorWdg::GetMainShapeEntry() +{ + if ( myMainEntry == "") + return myEntry.toLatin1().data(); + + return myMainEntry.toLatin1().data(); +} + +//================================================================================= +// function : GetCorrectedListOfIds +// purpose : Called to convert the list of IDs from sub-shape IDs to main shape IDs +//================================================================================= +QList StdMeshersGUI_SubShapeSelectorWdg::GetCorrectedListOfIDs( bool fromSubshapeToMainshape ) +{ + if ( ( myMainShape.IsNull() || myGeomShape.IsNull() ) && fromSubshapeToMainshape ) + return myListOfIDs; + else if ( ( myMainShape.IsNull() || myGeomShape.IsNull() ) && !fromSubshapeToMainshape ) + return mySelectedIDs; + + QList aList; + TopTools_IndexedMapOfShape aGeomMap; + TopTools_IndexedMapOfShape aMainMap; + TopExp::MapShapes(myGeomShape, aGeomMap); + TopExp::MapShapes(myMainShape, aMainMap); + + if ( fromSubshapeToMainshape ) { // convert indexes from sub-shape to mainshape + int size = myListOfIDs.size(); + for (int i = 0; i < size; i++) { + TopoDS_Shape aSubShape = aGeomMap.FindKey( myListOfIDs.at(i) ); + int index = aMainMap.FindIndex( aSubShape ); + aList.append( index ); + } + myIsNotCorrected = false; + } else { // convert indexes from main shape to sub-shape + int size = mySelectedIDs.size(); + for (int i = 0; i < size; i++) { + TopoDS_Shape aSubShape = aMainMap.FindKey( mySelectedIDs.at(i) ); + int index = aGeomMap.FindIndex( aSubShape ); + aList.append( index ); + } + } + + return aList; +} + +void StdMeshersGUI_SubShapeSelectorWdg::updateButtons() +{ + if ( myPreviewActor ) { + int total = myPreviewActor->count(); + int chunk = myPreviewActor->currentChunk(); + int chunkSize = myPreviewActor->chunkSize(); + int imin = chunk*chunkSize+1; + int imax = std::min((chunk+1)*chunkSize, total); + bool vis = imax > 0 && total > chunkSize; + myInfoLabel->setVisible( vis ); + myPrevButton->setVisible( vis ); + myNextButton->setVisible( vis ); + myInfoLabel->setText( tr( "X_FROM_Y_ITEMS_SHOWN" ).arg(imin).arg(imax).arg(total) ); + myPrevButton->setEnabled( myPreviewActor->hasPrevious() ); + myNextButton->setEnabled( myPreviewActor->hasNext() ); + } +} diff --git a/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.h b/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.h new file mode 100644 index 000000000..6d3cbbe7d --- /dev/null +++ b/src/StdMeshersGUI/StdMeshersGUI_SubShapeSelectorWdg.h @@ -0,0 +1,136 @@ +// Copyright (C) 2007-2012 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 +// + +// File : StdMeshersGUI_SubShapeSelectorWdg.h +// Author : Open CASCADE S.A.S. (dmv) +// +#ifndef STDMESHERSGUI_SUBSHAPESELECTORWDG_H +#define STDMESHERSGUI_SUBSHAPESELECTORWDG_H + +// SMESH includes +#include +#include "SMESH_StdMeshersGUI.hxx" +#include "SMESH_SMESHGUI.hxx" + +// Qt includes +#include +#include +#include + +#include + +class SMESHGUI; +class LightApp_SelectionMgr; +class SVTK_Selector; +class QPushButton; +class QLabel; +class QLineEdit; +class QCheckBox; +class QListWidget; +class SMESH_Actor; +class SMESH_PreviewActorsCollection; +class vtkRenderer; +class SUIT_SelectionFilter; + +class STDMESHERSGUI_EXPORT StdMeshersGUI_SubShapeSelectorWdg : public QWidget +{ + Q_OBJECT + +public: + StdMeshersGUI_SubShapeSelectorWdg( QWidget* parent = 0, + TopAbs_ShapeEnum aSubShType = TopAbs_EDGE ); + ~StdMeshersGUI_SubShapeSelectorWdg(); + + SMESH::long_array_var GetListOfIDs(); + void SetListOfIDs( SMESH::long_array_var ); + + void SetGeomShapeEntry( const QString& theEntry ); + const char* GetGeomShapeEntry() { return myEntry.toLatin1().data();} + + void SetMainShapeEntry( const QString& theEntry ); + const char* GetMainShapeEntry(); + + TopoDS_Shape GetGeomShape() { return myGeomShape; } + TopoDS_Shape GetMainShape() { return myMainShape; } + + QList GetCorrectedListOfIDs( bool fromSubshapeToMainshape = true ); + + static GEOM::GEOM_Object_var GetGeomObjectByEntry( const QString& ); + static TopoDS_Shape GetTopoDSByEntry( const QString& ); + + QString GetValue() const { return myParamValue; } + + void showPreview ( bool ); + + int GetListSize() { return myListOfIDs.size(); } + + void SetMaxSize(int aMaxSize) { myMaxSize = aMaxSize; } + //void SetSubShType(TopAbs_ShapeEnum aSubShType) { mySubShType = aSubShType; } + +private: + void updateState(); + void setFilter(); + void updateButtons(); + +private slots: + void onAdd(); + void onRemove(); + void onPrevious(); + void onNext(); + void SelectionIntoArgument(); + void onListSelectionChanged(); + +private: + void init(); + +private: + SMESHGUI* mySMESHGUI; + LightApp_SelectionMgr* mySelectionMgr; /* User shape selection */ + SVTK_Selector* mySelector; + SMESH::SMESH_Mesh_var myMesh; + TopoDS_Shape myGeomShape; // shape whose sub-shapes are selected + TopoDS_Shape myMainShape; // main shape of the mesh + QString myEntry; + QString myMainEntry; + vtkRenderer* myRenderer; + + QListWidget* myListWidget; + QPushButton* myAddButton; + QPushButton* myRemoveButton; + QLabel* myInfoLabel; + QPushButton* myPrevButton; + QPushButton* myNextButton; + QList mySelectedIDs; + QList myListOfIDs; + + QString myParamValue; + bool myIsShown; + bool myIsNotCorrected; + + // for manage possible size of myListOfIDs + int myMaxSize; + // for manage type of selected subshape + TopAbs_ShapeEnum mySubShType; + + SMESH_PreviewActorsCollection* myPreviewActor; + QList myGeomFilters; + SUIT_SelectionFilter* myFilter; +}; + +#endif // STDMESHERSGUI_SUBSHAPESELECTORWDG_H diff --git a/src/StdMeshersGUI/StdMeshers_images.ts b/src/StdMeshersGUI/StdMeshers_images.ts index 132661aa0..aaf16607c 100644 --- a/src/StdMeshersGUI/StdMeshers_images.ts +++ b/src/StdMeshersGUI/StdMeshers_images.ts @@ -1,34 +1,16 @@ + - - + @default ICON_DLG_ARITHMETIC_1D mesh_hypo_length.png + + ICON_DLG_FIXED_POINTS_1D + mesh_hypo_length.png + ICON_DLG_AUTOMATIC_LENGTH mesh_hypo_length.png @@ -53,6 +35,14 @@ ICON_DLG_MAX_LENGTH mesh_hypo_length.png + + ICON_DLG_CARTESIAN_PARAMS + mesh_hypo_length.png + + + ICON_DLG_VISCOUS_LAYERS + mesh_hypo_viscous_layers.png + ICON_DLG_MAX_ELEMENT_AREA mesh_hypo_area.png @@ -69,6 +59,10 @@ ICON_DLG_NUMBER_OF_LAYERS mesh_hypo_layer_distribution.png + + ICON_DLG_NUMBER_OF_LAYERS_2D + mesh_hypo_layer_distribution.png + ICON_DLG_PROJECTION_SOURCE_1D mesh_hypo_source_edge.png @@ -81,6 +75,18 @@ ICON_DLG_PROJECTION_SOURCE_3D mesh_hypo_source_3d.png + + ICON_DLG_QUADRANGLE_PARAMS + mesh_hypo_length.png + + + ICON_DLG_IMPORT_SOURCE_1D + mesh_hypo_source_edge.png + + + ICON_DLG_IMPORT_SOURCE_2D + mesh_hypo_source_face.png + ICON_DLG_SEGMENT_LENGTH_AROUND_VERTEX mesh_hypo_length.png @@ -97,6 +103,14 @@ ICON_SMESH_TREE_ALGO_CompositeSegment_1D mesh_tree_algo_regular.png + + ICON_SMESH_TREE_ALGO_UseExisting_2D + mesh_tree_algo_existing_2D.png + + + ICON_SMESH_TREE_ALGO_UseExisting_1D + mesh_tree_algo_regular.png + ICON_SMESH_TREE_ALGO_Hexa_3D mesh_tree_algo_hexa.png @@ -121,6 +135,14 @@ ICON_SMESH_TREE_ALGO_Quadrangle_2D mesh_tree_algo_quad.png + + ICON_SMESH_TREE_ALGO_RadialQuadrangle_1D2D + mesh_tree_algo_radial_quadrangle_1D2D.png + + + ICON_SMESH_TREE_ALGO_Prism_3D + mesh_tree_algo_prism.png + ICON_SMESH_TREE_ALGO_RadialPrism_3D mesh_tree_algo_radial_prism.png @@ -131,7 +153,7 @@ ICON_SMESH_TREE_ALGO_SegmentAroundVertex_0D - mesh_tree_algo_regular.png + mesh_tree_algo_0D.png ICON_SMESH_TREE_HYPO_Arithmetic1D @@ -181,6 +203,10 @@ ICON_SMESH_TREE_HYPO_NumberOfSegments mesh_tree_hypo_segment.png + + ICON_SMESH_TREE_HYPO_ViscousLayers + mesh_tree_hypo_viscous_layers.png + ICON_SMESH_TREE_HYPO_ProjectionSource1D mesh_tree_hypo_source_edge.png @@ -218,4 +244,27 @@ mesh_tree_hypo_length.png + + StdMeshersGUI_QuadrangleParamWdg + + ICON_StdMeshers_Quadrangle_Params_0 + mesh_quadrangle_standard.png + + + ICON_StdMeshers_Quadrangle_Params_1 + mesh_quadrangle_triapref.png + + + ICON_StdMeshers_Quadrangle_Params_2 + mesh_quadrangle_quadpref.png + + + ICON_StdMeshers_Quadrangle_Params_3 + mesh_quadrangle_quadpref_reversed.png + + + ICON_StdMeshers_Quadrangle_Params_4 + mesh_quadrangle_reduced.png + + diff --git a/src/StdMeshersGUI/StdMeshers_msg_en.ts b/src/StdMeshersGUI/StdMeshers_msg_en.ts index f11a5d675..df97c12a1 100644 --- a/src/StdMeshersGUI/StdMeshers_msg_en.ts +++ b/src/StdMeshersGUI/StdMeshers_msg_en.ts @@ -1,332 +1,484 @@ + - - - - @default - - SMESH_ARITHMETIC_1D_HYPOTHESIS - Arithmetic 1D - - - SMESH_ARITHMETIC_1D_PARAM - Arithmetic Reason - - - SMESH_ARITHMETIC_1D_TITLE - Hypothesis Construction - - - SMESH_AUTOMATIC_LENGTH_HYPOTHESIS - Automatic Length - - - SMESH_AUTOMATIC_LENGTH_TITLE - Hypothesis Construction - - - SMESH_CONV_MODE - Conversion mode - - - SMESH_CUT_NEG_MODE - Cut negative - - - SMESH_DEFLECTION1D_HYPOTHESIS - Deflection 1D - - - SMESH_DEFLECTION1D_PARAM - Deflection - - - SMESH_DEFLECTION1D_TITLE - Hypothesis Construction - - - SMESH_DENSITY_FUNC - Density function - - - SMESH_DISTR - Distribution - - - SMESH_DISTR_EXPR - Distribution with analitic density - - - SMESH_DISTR_REGULAR - Equidistant distribution - - - SMESH_DISTR_SCALE - Scale distribution - - - SMESH_DISTR_TAB - Distribution with table density - - - SMESH_DISTR_TYPE - Type of distribution - - - SMESH_END_LENGTH_PARAM - End Length - - - SMESH_EXPR_FUNC - Density function f(t) = - - - SMESH_EXP_MODE - Exponent - - - SMESH_FINENESS_PARAM - Fineness - - - SMESH_FUNC_DOMAIN - Warning: function must be defined on segment [0..1] - - - SMESH_INSERT_ROW - Insert row - - - SMESH_INVALID_FUNCTION - Function is invalid - - - SMESH_LAYERS_DISTRIBUTION - 1D Hypothesis - - - SMESH_LAYER_DISTRIBUTION_HYPOTHESIS - Distribution of Layers - - - SMESH_LAYER_DISTRIBUTION_TITLE - Hypothesis Construction - - - SMESH_LOCAL_LENGTH_HYPOTHESIS - Average Length - - - SMESH_LOCAL_LENGTH_PARAM - Length - - - SMESH_LOCAL_LENGTH_PRECISION - Precision - - - SMESH_LOCAL_LENGTH_TITLE - Hypothesis Construction - - - SMESH_MAX_LENGTH_HYPOTHESIS - Max Length - - - SMESH_USE_PREESTIMATED_LENGTH - Use preestimated length - - - SMESH_MAX_LENGTH_TITLE - Hypothesis Construction - - - SMESH_MAX_ELEMENT_AREA_HYPOTHESIS - Max. Element Area - - - SMESH_MAX_ELEMENT_AREA_PARAM - Max. Area - - - SMESH_MAX_ELEMENT_AREA_TITLE - Hypothesis Construction - - - SMESH_MAX_ELEMENT_VOLUME_HYPOTHESIS - Max. Element Volume - - - SMESH_MAX_ELEMENT_VOLUME_PARAM - Max. Volume - - - SMESH_MAX_ELEMENT_VOLUME_TITLE - Hypothesis Construction - - - SMESH_NB_SEGMENTS_HYPOTHESIS - Number of Segments - - - SMESH_NB_SEGMENTS_PARAM - Number of Segments - - - SMESH_NB_SEGMENTS_SCALE_PARAM - Scale Factor - - - SMESH_NB_SEGMENTS_TITLE - Hypothesis Construction - - - SMESH_NO_CONV - No conversion - - - SMESH_NUMBER_OF_LAYERS - Number of Layers - - - SMESH_NUMBER_OF_LAYERS_HYPOTHESIS - Radial Prism Parameter - - - SMESH_NUMBER_OF_LAYERS_TITLE - Hypothesis Construction - - - SMESH_PROJECTION_SOURCE_1D_HYPOTHESIS - Projection Source 1D - - - SMESH_PROJECTION_SOURCE_1D_TITLE - Hypothesis Construction - - - SMESH_PROJECTION_SOURCE_2D_HYPOTHESIS - Projection Source 2D - - - SMESH_PROJECTION_SOURCE_2D_TITLE - Hypothesis Construction - - - SMESH_PROJECTION_SOURCE_3D_HYPOTHESIS - Projection Source 3D - - - SMESH_PROJECTION_SOURCE_3D_TITLE - Hypothesis Construction - - - SMESH_REMOVE_ROW - Remove row - - - SMESH_SEGMENT_LENGTH_AROUND_VERTEX_HYPOTHESIS - Segment Length Around Vertex - - - SMESH_SEGMENT_LENGTH_AROUND_VERTEX_PARAM - Length - - - SMESH_SEGMENT_LENGTH_AROUND_VERTEX_TITLE - Hypothesis Construction - - - SMESH_SOURCE_3DSHAPE - 3D shape - - - SMESH_SOURCE_EDGE - Edge - - - SMESH_SOURCE_FACE - Face - - - SMESH_SOURCE_MESH - Mesh - - - SMESH_SOURCE_VERTEX - Source Vertex - - - SMESH_SOURCE_VERTEX1 - Source Vertex 1 - - - SMESH_SOURCE_VERTEX2 - Source Vertex 2 - - - SMESH_START_END_LENGTH_HYPOTHESIS - Start and End local Length - - - SMESH_START_END_LENGTH_TITLE - Hypothesis Construction - - - SMESH_START_LENGTH_PARAM - Start Length - - - SMESH_TAB_FUNC - Table function - - - SMESH_TARGET_VERTEX - Target Vertex - - - SMESH_TARGET_VERTEX1 - Target Vertex 1 - - - SMESH_TARGET_VERTEX2 - Target Vertex 2 - - - - StdMeshersGUI_LayerDistributionParamWdg - - CHANGE_TYPE - Change Type - - - CREATE - Create - - - EDIT - Edit - - + + + @default + + SMESH_ARITHMETIC_1D_HYPOTHESIS + Arithmetic 1D + + + SMESH_ARITHMETIC_1D_PARAM + Arithmetic Reason + + + SMESH_ARITHMETIC_1D_TITLE + Hypothesis Construction + + + SMESH_AUTOMATIC_LENGTH_HYPOTHESIS + Automatic Length + + + SMESH_AUTOMATIC_LENGTH_TITLE + Hypothesis Construction + + + SMESH_CONV_MODE + Conversion mode + + + SMESH_CUT_NEG_MODE + Cut negative + + + SMESH_DEFLECTION1D_HYPOTHESIS + Deflection 1D + + + SMESH_DEFLECTION1D_PARAM + Deflection + + + SMESH_DEFLECTION1D_TITLE + Hypothesis Construction + + + SMESH_DENSITY_FUNC + Density function + + + SMESH_DISTR + Distribution + + + SMESH_DISTR_EXPR + Distribution with analitic density + + + SMESH_DISTR_REGULAR + Equidistant distribution + + + SMESH_DISTR_SCALE + Scale distribution + + + SMESH_DISTR_TAB + Distribution with table density + + + SMESH_DISTR_TYPE + Type of distribution + + + SMESH_END_LENGTH_PARAM + End Length + + + SMESH_EXPR_FUNC + Density function + + + SMESH_EXP_MODE + Exponent + + + SMESH_FINENESS_PARAM + Fineness + + + SMESH_FUNC_DOMAIN + Warning: function must be defined on segment [0..1] + + + SMESH_INSERT_ROW + Insert row + + + SMESH_INVALID_FUNCTION + Function is invalid + + + SMESH_LAYERS_DISTRIBUTION + 1D Hypothesis + + + SMESH_LAYER_DISTRIBUTION_HYPOTHESIS + Distribution of Layers + + + SMESH_LAYER_DISTRIBUTION_TITLE + Hypothesis Construction + + + SMESH_LOCAL_LENGTH_HYPOTHESIS + Local Length + + + SMESH_LOCAL_LENGTH_PARAM + Length + + + SMESH_LOCAL_LENGTH_PRECISION + Precision + + + SMESH_LOCAL_LENGTH_TITLE + Hypothesis Construction + + + SMESH_FIXED_POINTS_1D_HYPOTHESIS + Fixed points 1D + + + SMESH_FIXED_POINTS_1D_TITLE + Hypothesis Construction + + + SMESH_MAX_LENGTH_HYPOTHESIS + Max Length + + + SMESH_CARTESIAN_PARAMS_HYPOTHESIS + Body Fitting Parameters + + + SMESH_USE_PREESTIMATED_LENGTH + Use preestimated length + + + SMESH_VISCOUS_LAYERS_HYPOTHESIS + Viscous Layers + + + SMESH_VISCOUS_LAYERS_TITLE + Hypothesis Construction + + + SMESH_TOTAL_THICKNESS + Total thickness + + + SMESH_STRETCH_FACTOR + Stretch factor + + + SMESH_FACES_WO_LAYERS + Faces without layers +(inlets and oulets) + + + SMESH_MAX_LENGTH_TITLE + Hypothesis Construction + + + SMESH_CARTESIAN_PARAMS_TITLE + Hypothesis Construction + + + SMESH_MAX_ELEMENT_AREA_HYPOTHESIS + Max. Element Area + + + SMESH_MAX_ELEMENT_AREA_PARAM + Max. Area + + + SMESH_MAX_ELEMENT_AREA_TITLE + Hypothesis Construction + + + SMESH_MAX_ELEMENT_VOLUME_HYPOTHESIS + Max. Element Volume + + + SMESH_MAX_ELEMENT_VOLUME_PARAM + Max. Volume + + + SMESH_MAX_ELEMENT_VOLUME_TITLE + Hypothesis Construction + + + SMESH_NB_SEGMENTS_HYPOTHESIS + Number of Segments + + + SMESH_NB_SEGMENTS_PARAM + Number of Segments + + + SMESH_NB_SEGMENTS_SCALE_PARAM + Scale Factor + + + SMESH_NB_SEGMENTS_TITLE + Hypothesis Construction + + + SMESH_NO_CONV + No conversion + + + SMESH_NUMBER_OF_LAYERS + Number of layers + + + SMESH_NUMBER_OF_LAYERS_HYPOTHESIS + Radial Prism Parameter + + + SMESH_NUMBER_OF_LAYERS_2D_HYPOTHESIS + Radial Quadrangle Parameter + + + SMESH_NUMBER_OF_LAYERS_TITLE + Hypothesis Construction + + + SMESH_NUMBER_OF_LAYERS_2D_TITLE + Hypothesis Construction + + + SMESH_PROJECTION_SOURCE_1D_HYPOTHESIS + Projection Source 1D + + + SMESH_PROJECTION_SOURCE_1D_TITLE + Hypothesis Construction + + + SMESH_PROJECTION_SOURCE_2D_HYPOTHESIS + Projection Source 2D + + + SMESH_PROJECTION_SOURCE_2D_TITLE + Hypothesis Construction + + + SMESH_PROJECTION_SOURCE_3D_HYPOTHESIS + Projection Source 3D + + + SMESH_PROJECTION_SOURCE_3D_TITLE + Hypothesis Construction + + + SMESH_IMPORT_SOURCE_1D_HYPOTHESIS + Source edges + + + SMESH_IMPORT_SOURCE_1D_TITLE + Hypothesis Construction + + + SMESH_IMPORT_SOURCE_2D_HYPOTHESIS + Source faces + + + SMESH_IMPORT_SOURCE_2D_TITLE + Hypothesis Construction + + + SMESH_REMOVE_ROW + Remove row + + + SMESH_REVERSED_EDGES + Reversed Edges + + + SMESH_FIXED_POINTS + Fixed Points + + + SMESH_RANGE + Range + + + SMESH_NB_SEGMENTS + Nb. Segments + + + SMESH_SAME_NB_SEGMENTS + Same Nb. Segments for All Intervals + + + SMESH_BASE_VERTEX + Base vertex + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_HYPOTHESIS + Segment Length Around Vertex + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_PARAM + Length + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_TITLE + Hypothesis Construction + + + SMESH_SOURCE_3DSHAPE + Source 3D shape + + + SMESH_SOURCE_EDGE + Source Edge + + + SMESH_SOURCE_EDGES + Groups of Edges + + + SMESH_SOURCE_FACE + Source Face + + + SMESH_SOURCE_FACES + Groups of Faces + + + SMESH_SOURCE_MESH + Source Mesh + + + SMESH_COPY_MESH + To copy mesh + + + SMESH_TO_COPY_GROUPS + To copy groups + + + SMESH_SOURCE_VERTEX + Source Vertex + + + SMESH_SOURCE_VERTEX1 + Source Vertex 1 + + + SMESH_SOURCE_VERTEX2 + Source Vertex 2 + + + SMESH_START_END_LENGTH_HYPOTHESIS + Start and End local Length + + + SMESH_START_END_LENGTH_TITLE + Hypothesis Construction + + + SMESH_START_LENGTH_PARAM + Start Length + + + SMESH_TAB_FUNC + Table function + + + SMESH_TARGET_VERTEX + Target Vertex + + + SMESH_TARGET_VERTEX1 + Target Vertex 1 + + + SMESH_TARGET_VERTEX2 + Target Vertex 2 + + + SMESH_QUADRANGLE_PARAMS_HYPOTHESIS + Quadrangle parameters + + + SMESH_QUADRANGLE_PARAMS_TITLE + Hypothesis Construction + + + SMESH_QUAD_TYPE + Type + + + + StdMeshersGUI_QuadrangleParamWdg + + SMESH_QUAD_TYPE_0 + Standard + + + SMESH_QUAD_TYPE_1 + Triangle preference + + + SMESH_QUAD_TYPE_2 + Quadrangle preference + + + SMESH_QUAD_TYPE_3 + Quadrangle preference (reversed) + + + SMESH_QUAD_TYPE_4 + Reduced + + + + StdMeshersGUI_LayerDistributionParamWdg + + CHANGE_TYPE + Change Type + + + CREATE + Create + + + EDIT + Edit + + + + StdMeshersGUI_CartesianParamCreator + + THRESHOLD + Threshold + + + AXIS_X + Axis X + + + AXIS_Y + Axis Y + + + AXIS_Z + Axis Z + + + + StdMeshersGUI::GridAxisTab + + GRID_DEF_MODE + Definition mode + + + SPACING + Spacing + + + INSERT + Insert + + + COORD_STEP + Step + + diff --git a/src/StdMeshersGUI/StdMeshers_msg_fr.ts b/src/StdMeshersGUI/StdMeshers_msg_fr.ts new file mode 100755 index 000000000..836a9fb20 --- /dev/null +++ b/src/StdMeshersGUI/StdMeshers_msg_fr.ts @@ -0,0 +1,484 @@ + + + + + @default + + SMESH_ARITHMETIC_1D_HYPOTHESIS + Arithmétique 1D + + + SMESH_ARITHMETIC_1D_PARAM + Raison arithmétique + + + SMESH_ARITHMETIC_1D_TITLE + Construction de l'hypothèse + + + SMESH_AUTOMATIC_LENGTH_HYPOTHESIS + Longueur automatique + + + SMESH_AUTOMATIC_LENGTH_TITLE + Construction de l'hypothèse + + + SMESH_CONV_MODE + Mode de conversion + + + SMESH_CUT_NEG_MODE + Section négative + + + SMESH_DEFLECTION1D_HYPOTHESIS + Déflection 1D + + + SMESH_DEFLECTION1D_PARAM + Déflection + + + SMESH_DEFLECTION1D_TITLE + Construction de l'hypothèse + + + SMESH_DENSITY_FUNC + Fonction de densité + + + SMESH_DISTR + Distribution + + + SMESH_DISTR_EXPR + Distribution de densité analytique + + + SMESH_DISTR_REGULAR + Distribution équidistante + + + SMESH_DISTR_SCALE + Progression géométrique + + + SMESH_DISTR_TAB + Table de densités + + + SMESH_DISTR_TYPE + Type de distribution + + + SMESH_END_LENGTH_PARAM + Longueur finale + + + SMESH_EXPR_FUNC + Expression de la densité + + + SMESH_EXP_MODE + Exposant + + + SMESH_FINENESS_PARAM + Finesse + + + SMESH_FUNC_DOMAIN + Avertissement: La fonction doit être définie sur le segment [0..1] + + + SMESH_INSERT_ROW + Insérer une ligne + + + SMESH_INVALID_FUNCTION + La fonction n'est pas valide + + + SMESH_LAYERS_DISTRIBUTION + Hypothèse 1D + + + SMESH_LAYER_DISTRIBUTION_HYPOTHESIS + Distribution des couches + + + SMESH_LAYER_DISTRIBUTION_TITLE + Construction de l'hypothèse + + + SMESH_LOCAL_LENGTH_HYPOTHESIS + Longueur moyenne + + + SMESH_LOCAL_LENGTH_PARAM + Longueur + + + SMESH_LOCAL_LENGTH_PRECISION + Précision + + + SMESH_LOCAL_LENGTH_TITLE + Construction de l'hypothèse + + + SMESH_FIXED_POINTS_1D_HYPOTHESIS + Points fixes 1D + + + SMESH_FIXED_POINTS_1D_TITLE + Construction de l'hypothèse + + + SMESH_MAX_LENGTH_HYPOTHESIS + Longueur maximale + + + SMESH_CARTESIAN_PARAMS_HYPOTHESIS + Paramètres de Body Fitting + + + SMESH_USE_PREESTIMATED_LENGTH + Utiliser la longueur pré-estimée + + + SMESH_VISCOUS_LAYERS_HYPOTHESIS + Couches limites + + + SMESH_VISCOUS_LAYERS_TITLE + Construction de l'hypothèse + + + SMESH_TOTAL_THICKNESS + Epaisseur totale + + + SMESH_STRETCH_FACTOR + Facteur d'échelle + + + SMESH_FACES_WO_LAYERS + Faces sans couche limite +(entrées et sorties) + + + SMESH_MAX_LENGTH_TITLE + Construction de l'hypothèse + + + SMESH_CARTESIAN_PARAMS_TITLE + Construction de l'hypothèse + + + SMESH_MAX_ELEMENT_AREA_HYPOTHESIS + Aire maximale d'une maille + + + SMESH_MAX_ELEMENT_AREA_PARAM + Aire maximale + + + SMESH_MAX_ELEMENT_AREA_TITLE + Construction de l'hypothèse + + + SMESH_MAX_ELEMENT_VOLUME_HYPOTHESIS + Volume maximal d'une maille + + + SMESH_MAX_ELEMENT_VOLUME_PARAM + Volume maximal + + + SMESH_MAX_ELEMENT_VOLUME_TITLE + Construction de l'hypothèse + + + SMESH_NB_SEGMENTS_HYPOTHESIS + Nombre de segments + + + SMESH_NB_SEGMENTS_PARAM + Nombre de segments + + + SMESH_NB_SEGMENTS_SCALE_PARAM + Facteur d'échelle + + + SMESH_NB_SEGMENTS_TITLE + Construction de l'hypothèse + + + SMESH_NO_CONV + Sans conversion + + + SMESH_NUMBER_OF_LAYERS + Nombre de couches + + + SMESH_NUMBER_OF_LAYERS_HYPOTHESIS + Paramètre des prismes radiaux + + + SMESH_NUMBER_OF_LAYERS_2D_HYPOTHESIS + Paramètre des quadrangles radiaux + + + SMESH_NUMBER_OF_LAYERS_TITLE + Construction de l'hypothèse + + + SMESH_NUMBER_OF_LAYERS_2D_TITLE + Construction de l'hypothèse + + + SMESH_PROJECTION_SOURCE_1D_HYPOTHESIS + Source pour le projection 1D + + + SMESH_PROJECTION_SOURCE_1D_TITLE + Construction de l'hypothèse + + + SMESH_PROJECTION_SOURCE_2D_HYPOTHESIS + Source pour la projection 2D + + + SMESH_PROJECTION_SOURCE_2D_TITLE + Construction de l'hypothèse + + + SMESH_PROJECTION_SOURCE_3D_HYPOTHESIS + Source pour la projection 3D + + + SMESH_PROJECTION_SOURCE_3D_TITLE + Construction de l'hypothèse + + + SMESH_IMPORT_SOURCE_1D_HYPOTHESIS + Arêtes source + + + SMESH_IMPORT_SOURCE_1D_TITLE + Construction de l'hypothèse + + + SMESH_IMPORT_SOURCE_2D_HYPOTHESIS + Faces sources + + + SMESH_IMPORT_SOURCE_2D_TITLE + Construction de l'hypothèse + + + SMESH_REMOVE_ROW + Supprimer une ligne + + + SMESH_REVERSED_EDGES + Arêtes inversées + + + SMESH_FIXED_POINTS + Points fixés + + + SMESH_RANGE + Intervalle + + + SMESH_NB_SEGMENTS + Nb. segments + + + SMESH_SAME_NB_SEGMENTS + Le même Nb. segments dans chaque intervalle + + + SMESH_BASE_VERTEX + Point de base + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_HYPOTHESIS + Longueur des segments autour d'un point + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_PARAM + Longueur + + + SMESH_SEGMENT_LENGTH_AROUND_VERTEX_TITLE + Construction de l'hypothèse + + + SMESH_SOURCE_3DSHAPE + Objet 3D + + + SMESH_SOURCE_EDGE + Arête + + + SMESH_SOURCE_EDGES + Groupes d'arêtes + + + SMESH_SOURCE_FACE + Face + + + SMESH_SOURCE_FACES + Groupes de faces + + + SMESH_SOURCE_MESH + Maillage + + + SMESH_COPY_MESH + Copier le maillage + + + SMESH_TO_COPY_GROUPS + Copier les groupes + + + SMESH_SOURCE_VERTEX + Point source + + + SMESH_SOURCE_VERTEX1 + Point source 1 + + + SMESH_SOURCE_VERTEX2 + Point source 2 + + + SMESH_START_END_LENGTH_HYPOTHESIS + Start and end local Length + + + SMESH_START_END_LENGTH_TITLE + Construction de l'hypothèse + + + SMESH_START_LENGTH_PARAM + Longueur initiale + + + SMESH_TAB_FUNC + Table de valeurs de la fonction + + + SMESH_TARGET_VERTEX + Point cible + + + SMESH_TARGET_VERTEX1 + Point cible 1 + + + SMESH_TARGET_VERTEX2 + Point cible 2 + + + SMESH_QUADRANGLE_PARAMS_HYPOTHESIS + Paramètres pour le maillage quadrangulaire + + + SMESH_QUADRANGLE_PARAMS_TITLE + Construction de l'hypothèse + + + SMESH_QUAD_TYPE + Type + + + + StdMeshersGUI_QuadrangleParamWdg + + SMESH_QUAD_TYPE_0 + Standard + + + SMESH_QUAD_TYPE_1 + Triangles privilégiés + + + SMESH_QUAD_TYPE_2 + Quadrangles privilégiés + + + SMESH_QUAD_TYPE_3 + Quadrangles privilégiés (inversé) + + + SMESH_QUAD_TYPE_4 + Réduction + + + + StdMeshersGUI_LayerDistributionParamWdg + + CHANGE_TYPE + Changer le type + + + CREATE + Créer + + + EDIT + Editer + + + + StdMeshersGUI_CartesianParamCreator + + THRESHOLD + Seuil + + + AXIS_X + Axe X + + + AXIS_Y + Axe Y + + + AXIS_Z + Axe Z + + + + StdMeshersGUI::GridAxisTab + + GRID_DEF_MODE + Mode de définition + + + SPACING + Espacement + + + INSERT + Insérer + + + COORD_STEP + Pas + + + diff --git a/src/StdMeshers_I/Makefile.am b/src/StdMeshers_I/Makefile.am index 526d71fc5..4bc901d2d 100644 --- a/src/StdMeshers_I/Makefile.am +++ b/src/StdMeshers_I/Makefile.am @@ -1,30 +1,28 @@ -# Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +# Copyright (C) 2007-2012 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 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. # -# 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 # -# 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 +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com # + # SMESH StdMeshers_I : idl implementation based on 'StdMeshersPlugin' unit's classes # File : Makefile.in # Author : Julia DOROVSKIKH # Modified by : Alexander BORODIN (OCN) - autotools usage # Module : SMESH - +# include $(top_srcdir)/adm_local/unix/make_common_starter.am # header files @@ -32,6 +30,7 @@ salomeinclude_HEADERS = \ StdMeshers_LocalLength_i.hxx \ StdMeshers_StartEndLength_i.hxx \ StdMeshers_Arithmetic1D_i.hxx \ + StdMeshers_FixedPoints1D_i.hxx \ StdMeshers_NumberOfSegments_i.hxx \ StdMeshers_Deflection1D_i.hxx \ StdMeshers_Propagation_i.hxx \ @@ -47,6 +46,7 @@ salomeinclude_HEADERS = \ StdMeshers_QuadranglePreference_i.hxx \ StdMeshers_QuadraticMesh_i.hxx \ StdMeshers_NumberOfLayers_i.hxx \ + StdMeshers_NumberOfLayers2D_i.hxx \ StdMeshers_Prism_3D_i.hxx \ StdMeshers_ProjectionSource1D_i.hxx \ StdMeshers_ProjectionSource2D_i.hxx \ @@ -54,13 +54,22 @@ salomeinclude_HEADERS = \ StdMeshers_Projection_1D_2D_3D_i.hxx \ StdMeshers_ObjRefUlils.hxx \ StdMeshers_LayerDistribution_i.hxx \ + StdMeshers_LayerDistribution2D_i.hxx \ StdMeshers_CompositeSegment_1D_i.hxx \ StdMeshers_SegmentAroundVertex_0D_i.hxx \ StdMeshers_SegmentLengthAroundVertex_i.hxx \ StdMeshers_UseExisting_1D2D_i.hxx \ - StdMeshers_TrianglePreference_i.hxx \ StdMeshers_MaxLength_i.hxx \ - SMESH_StdMeshers_I.hxx + StdMeshers_QuadrangleParams_i.hxx \ + StdMeshers_RadialQuadrangle_1D2D_i.hxx \ + SMESH_StdMeshers_I.hxx \ + StdMeshers_ImportSource1D_i.hxx \ + StdMeshers_ImportSource2D_i.hxx \ + StdMeshers_Import_1D_i.hxx \ + StdMeshers_Import_1D2D_i.hxx \ + StdMeshers_ViscousLayers_i.hxx \ + StdMeshers_CartesianParameters3D_i.hxx \ + StdMeshers_Cartesian_3D_i.hxx # Libraries targets lib_LTLIBRARIES = libStdMeshersEngine.la @@ -70,6 +79,7 @@ dist_libStdMeshersEngine_la_SOURCES = \ StdMeshers_LocalLength_i.cxx \ StdMeshers_StartEndLength_i.cxx \ StdMeshers_Arithmetic1D_i.cxx \ + StdMeshers_FixedPoints1D_i.cxx \ StdMeshers_NumberOfSegments_i.cxx \ StdMeshers_Deflection1D_i.cxx \ StdMeshers_Propagation_i.cxx \ @@ -85,6 +95,7 @@ dist_libStdMeshersEngine_la_SOURCES = \ StdMeshers_QuadranglePreference_i.cxx \ StdMeshers_QuadraticMesh_i.cxx \ StdMeshers_NumberOfLayers_i.cxx \ + StdMeshers_NumberOfLayers2D_i.cxx \ StdMeshers_Prism_3D_i.cxx \ StdMeshers_ProjectionSource1D_i.cxx \ StdMeshers_ProjectionSource2D_i.cxx \ @@ -92,12 +103,21 @@ dist_libStdMeshersEngine_la_SOURCES = \ StdMeshers_Projection_1D_2D_3D_i.cxx \ StdMeshers_ObjRefUlils.cxx \ StdMeshers_LayerDistribution_i.cxx \ + StdMeshers_LayerDistribution2D_i.cxx \ StdMeshers_CompositeSegment_1D_i.cxx \ StdMeshers_SegmentAroundVertex_0D_i.cxx \ StdMeshers_SegmentLengthAroundVertex_i.cxx \ StdMeshers_UseExisting_1D2D_i.cxx \ - StdMeshers_TrianglePreference_i.cxx \ - StdMeshers_MaxLength_i.cxx + StdMeshers_MaxLength_i.cxx \ + StdMeshers_QuadrangleParams_i.cxx \ + StdMeshers_RadialQuadrangle_1D2D_i.cxx \ + StdMeshers_ImportSource1D_i.cxx \ + StdMeshers_ImportSource2D_i.cxx \ + StdMeshers_Import_1D_i.cxx \ + StdMeshers_Import_1D2D_i.cxx \ + StdMeshers_ViscousLayers_i.cxx \ + StdMeshers_CartesianParameters3D_i.cxx \ + StdMeshers_Cartesian_3D_i.cxx # additionnal information to compil and link file libStdMeshersEngine_la_CPPFLAGS = \ @@ -107,19 +127,19 @@ libStdMeshersEngine_la_CPPFLAGS = \ $(GEOM_CXXFLAGS) \ $(MED_CXXFLAGS) \ $(BOOST_CPPFLAGS) \ + $(VTK_INCLUDES) \ $(CORBA_CXXFLAGS) \ $(CORBA_INCLUDES) \ -I$(srcdir)/../SMESHImpl \ -I$(srcdir)/../MEFISTO2 \ -I$(srcdir)/../SMESH \ + -I$(srcdir)/../SMESHUtils \ -I$(srcdir)/../SMESH_I \ -I$(srcdir)/../SMESHDS \ -I$(srcdir)/../SMDS \ -I$(srcdir)/../Controls \ -I$(srcdir)/../StdMeshers \ - -I$(top_builddir)/idl \ - -I$(top_builddir)/salome_adm/unix - + -I$(top_builddir)/idl libStdMeshersEngine_la_LDFLAGS = \ ../../idl/libSalomeIDLSMESH.la \ diff --git a/src/StdMeshers_I/SMESH_StdMeshers_I.hxx b/src/StdMeshers_I/SMESH_StdMeshers_I.hxx index 70a1bda44..6352b0734 100755 --- a/src/StdMeshers_I/SMESH_StdMeshers_I.hxx +++ b/src/StdMeshers_I/SMESH_StdMeshers_I.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // File : SMESH_StdMeshers_I.hxx // Author : Alexander BORODIN // Module : SMESH @@ -28,7 +29,7 @@ #define _SMESH_StdMeshers_I_HXX_ #ifdef WNT - #if defined STDMESHERS_I_EXPORTS + #if defined STDMESHERS_I_EXPORTS || defined StdMeshersEngine_EXPORTS #define STDMESHERS_I_EXPORT __declspec( dllexport ) #else #define STDMESHERS_I_EXPORT __declspec( dllimport ) diff --git a/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.cxx b/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.cxx index 20a0b2909..8d57e1e9a 100644 --- a/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Arithmetic1D_i.cxx // Author : Damien COQUERET, OCC // Module : SMESH -// $Header$ // #include "StdMeshers_Arithmetic1D_i.hxx" #include "SMESH_Gen_i.hxx" @@ -46,15 +46,15 @@ using namespace std; //============================================================================= StdMeshers_Arithmetic1D_i::StdMeshers_Arithmetic1D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_Arithmetic1D_i::StdMeshers_Arithmetic1D_i" ); myBaseImpl = new ::StdMeshers_Arithmetic1D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -79,7 +79,7 @@ StdMeshers_Arithmetic1D_i::~StdMeshers_Arithmetic1D_i() //============================================================================= void StdMeshers_Arithmetic1D_i::SetLength(CORBA::Double theLength, - CORBA::Boolean theIsStart ) + CORBA::Boolean theIsStart ) throw ( SALOME::SALOME_Exception ) { MESSAGE( "StdMeshers_StartEndLength_i::SetLength" ); @@ -89,12 +89,37 @@ void StdMeshers_Arithmetic1D_i::SetLength(CORBA::Double theLength, } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetLength( " - << theLength << ", " << theIsStart << " )"; + SMESH::TPythonDump() + << _this() << ( theIsStart ? ".SetStartLength( " : ".SetEndLength( " ) + << SMESH::TVar(theLength) << " )"; +} + +//============================================================================= +/*! + * Sets parameter value + */ +//============================================================================= + +void StdMeshers_Arithmetic1D_i::SetStartLength( CORBA::Double length) + throw (SALOME::SALOME_Exception) +{ + SetLength( length, true ); +} + +//============================================================================= +/*! + * Sets parameter value + */ +//============================================================================= + +void StdMeshers_Arithmetic1D_i::SetEndLength( CORBA::Double length) + throw (SALOME::SALOME_Exception) +{ + SetLength( length, false ); } //============================================================================= @@ -107,11 +132,105 @@ void StdMeshers_Arithmetic1D_i::SetLength(CORBA::Double theLength, CORBA::Double StdMeshers_Arithmetic1D_i::GetLength( CORBA::Boolean theIsStart) { - MESSAGE( "StdMeshers_StartEndLength_i::GetLength" ); + MESSAGE( "StdMeshers_Arithmetic1D_i::GetLength" ); ASSERT( myBaseImpl ); return this->GetImpl()->GetLength( theIsStart ); } +//============================================================================= +/*! + * StdMeshers_Arithmetic1D_i::SetReversedEdges + * + * Set edges to reverse + */ +//============================================================================= + +void StdMeshers_Arithmetic1D_i::SetReversedEdges( const SMESH::long_array& theIds ) +{ + ASSERT( myBaseImpl ); + try { + std::vector ids( theIds.length() ); + CORBA::Long iEnd = theIds.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + ids[ i ] = theIds[ i ]; + + this->GetImpl()->SetReversedEdges( ids ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetReversedEdges( " << theIds << " )"; +} + +//============================================================================= +/*! + * StdMeshers_Arithmetic1D_i::SetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +void StdMeshers_Arithmetic1D_i::SetObjectEntry( const char* theEntry ) +{ + ASSERT( myBaseImpl ); + string entry(theEntry); // actually needed as theEntry is spoiled by moment of dumping + try { + this->GetImpl()->SetObjectEntry( entry.c_str() ); + // Update Python script + SMESH::TPythonDump() << _this() << ".SetObjectEntry( \"" << entry.c_str() << "\" )"; + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(),SALOME::BAD_PARAM ); + } +} + +//============================================================================= +/*! + * StdMeshers_Arithmetic1D_i::GetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +char* StdMeshers_Arithmetic1D_i::GetObjectEntry() +{ + MESSAGE( "StdMeshers_Arithmetic1D_i::SetObjectEntry" ); + ASSERT( myBaseImpl ); + const char* entry; + try { + entry = this->GetImpl()->GetObjectEntry(); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + return CORBA::string_dup( entry ); +} + +//============================================================================= +/*! + * StdMeshers_Arithmetic1D_i::GetReversedEdges + * + * Get reversed edges + */ +//============================================================================= + +SMESH::long_array* StdMeshers_Arithmetic1D_i::GetReversedEdges() +{ + MESSAGE( "StdMeshers_StartEndLength_i::GetReversedEdges" ); + ASSERT( myBaseImpl ); + SMESH::long_array_var anArray = new SMESH::long_array; + std::vector ids = this->GetImpl()->GetReversedEdges(); + anArray->length( ids.size() ); + for ( CORBA::Long i = 0; i < ids.size(); i++) + anArray [ i ] = ids [ i ]; + + return anArray._retn(); +} + //============================================================================= /*! * StdMeshers_Arithmetic1D_i::GetImpl @@ -140,3 +259,14 @@ CORBA::Boolean StdMeshers_Arithmetic1D_i::IsDimSupported( SMESH::Dimension type return type == SMESH::DIM_1D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_Arithmetic1D_i::getMethodOfParameter(const int paramIndex, + int nbVars) const +{ + return paramIndex == 0 ? "SetStartLength" : "SetEndLength"; +} diff --git a/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.hxx b/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.hxx index 3d8ec8642..6dfeb10fd 100644 --- a/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Arithmetic1D_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Arithmetic1D_i.hxx // Author : Damien COQUERET, OCC // Module : SMESH -// $Header$ // #ifndef _SMESH_ARITHMETIC1D_I_HXX_ #define _SMESH_ARITHMETIC1D_I_HXX_ @@ -47,21 +47,44 @@ public: // Constructor StdMeshers_Arithmetic1D_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_Arithmetic1D_i(); // Set length + // * OBSOLETE *. Avoid such a way of interface design void SetLength( CORBA::Double theLength, CORBA::Boolean theIsStart ) throw ( SALOME::SALOME_Exception ); + + // Sets parameter value + void SetStartLength( CORBA::Double length) throw (SALOME::SALOME_Exception); + + // Sets parameter value + void SetEndLength( CORBA::Double length) throw (SALOME::SALOME_Exception); + // Get length CORBA::Double GetLength(CORBA::Boolean theIsStart); + //Set Reversed Edges + void SetReversedEdges( const SMESH::long_array& theIDs); + + //Get Reversed Edges + SMESH::long_array* GetReversedEdges(); + + //Set the Entry of the Object + void SetObjectEntry( const char* theEntry); + + //Get Object Entry + char* GetObjectEntry(); + // Get implementation ::StdMeshers_Arithmetic1D* GetImpl(); - + // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_AutomaticLength_i.cxx b/src/StdMeshers_I/StdMeshers_AutomaticLength_i.cxx index 2085f1f52..9d8f15e65 100644 --- a/src/StdMeshers_I/StdMeshers_AutomaticLength_i.cxx +++ b/src/StdMeshers_I/StdMeshers_AutomaticLength_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_AutomaticLength_i.cxx // Author : Edward AGAPOV @@ -87,7 +88,7 @@ void StdMeshers_AutomaticLength_i::SetFineness( CORBA::Double theFineness ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script SMESH::TPythonDump() << _this() << ".SetFineness( " << theFineness << " )"; diff --git a/src/StdMeshers_I/StdMeshers_AutomaticLength_i.hxx b/src/StdMeshers_I/StdMeshers_AutomaticLength_i.hxx index 7bb85cf45..c3492bf38 100644 --- a/src/StdMeshers_I/StdMeshers_AutomaticLength_i.hxx +++ b/src/StdMeshers_I/StdMeshers_AutomaticLength_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_AutomaticLength_i.hxx // Author : Edward AGAPOV diff --git a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx new file mode 100644 index 000000000..d8b072476 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.cxx @@ -0,0 +1,293 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_CartesianParameters3D_i.cxx +// Module : SMESH +// +#include "StdMeshers_CartesianParameters3D_i.hxx" + +#include "StdMeshers_CartesianParameters3D.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_PythonDump.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#define _vec2array( v, a,conversion ) \ + { \ + a->length( v.size() ); \ + for ( size_t i = 0; i < v.size(); ++i ) \ + a[i] = conversion( v[i] ); \ + } +#define _array2vec(a,v,conversion) \ + { \ + v.resize( a.length() ); \ + for ( size_t i = 0; i < v.size(); ++i ) \ + v[i] = conversion ( a[i] ); \ + } +namespace +{ + const char* _string2chars(const std::string& s ) { return s.c_str(); } +} + +//============================================================================= +/*! + * StdMeshers_CartesianParameters3D_i::StdMeshers_CartesianParameters3D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_CartesianParameters3D_i:: +StdMeshers_CartesianParameters3D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_CartesianParameters3D_i::StdMeshers_CartesianParameters3D_i" ); + myBaseImpl = new ::StdMeshers_CartesianParameters3D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//============================================================================= +/*! + * StdMeshers_CartesianParameters3D_i::~StdMeshers_CartesianParameters3D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_CartesianParameters3D_i::~StdMeshers_CartesianParameters3D_i() +{ + MESSAGE( "StdMeshers_CartesianParameters3D_i::~StdMeshers_CartesianParameters3D_i" ); +} + +//============================================================================= +/*! + * SetGrid + */ +//============================================================================= + +void StdMeshers_CartesianParameters3D_i::SetGrid(const SMESH::double_array& coords, + CORBA::Short axis) + throw (SALOME::SALOME_Exception) +{ + std::vector coordVec;//, yCoords, zCoords; + _array2vec( coords, coordVec, ); + + ASSERT( myBaseImpl ); + try { + this->GetImpl()->SetGrid( coordVec, axis ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetGrid( " << coords << ", " << axis << " )"; +} + +//============================================================================= +/*! + * GetGrid + */ +//============================================================================= + +SMESH::double_array* StdMeshers_CartesianParameters3D_i::GetGrid(CORBA::Short axis) + throw (SALOME::SALOME_Exception) +{ + std::vector coordVec; + ASSERT( myBaseImpl ); + try { + this->GetImpl()->GetGrid(coordVec, axis); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + SMESH::double_array_var coords = new SMESH::double_array(); + _vec2array( coordVec, coords, ); + + return coords._retn(); +} + +//============================================================================= +/*! + * SetSizeThreshold + */ +//============================================================================= + +void StdMeshers_CartesianParameters3D_i::SetSizeThreshold(CORBA::Double threshold) + throw (SALOME::SALOME_Exception) +{ + ASSERT( myBaseImpl ); + try { + this->GetImpl()->SetSizeThreshold(threshold); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetSizeThreshold( " << SMESH::TVar(threshold) << " )"; +} + +//============================================================================= +/*! + * GetSizeThreshold + */ +//============================================================================= + +CORBA::Double StdMeshers_CartesianParameters3D_i::GetSizeThreshold() +{ + return this->GetImpl()->GetSizeThreshold(); +} + +//======================================================================= +//function : SetGridSpacing +//\brief Set grid spacing along the three axes +// \param spaceFunctions - functions defining spacing values at given point on axis +// \param internalPoints - points dividing a grid into parts along each direction +// Parameter t of spaceFunction f(t) is a position [0,1] withing bounding box of +// the shape to mesh or withing an interval defined by internal points +//======================================================================= + +void StdMeshers_CartesianParameters3D_i::SetGridSpacing(const SMESH::string_array& spaceFunctions, + const SMESH::double_array& internalPoints, + CORBA::Short axis) + throw (SALOME::SALOME_Exception) +{ + vector funVec; + vector pointVec; + _array2vec( spaceFunctions, funVec, (const char*) ); + _array2vec( internalPoints, pointVec, ); + + ASSERT( myBaseImpl ); + try { + this->GetImpl()->SetGridSpacing( funVec, pointVec, axis ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetGridSpacing( " + << spaceFunctions << ", " + << internalPoints << ", " + << axis << " )"; +} + +//======================================================================= +//function : GetGridSpacing +//======================================================================= + +void StdMeshers_CartesianParameters3D_i::GetGridSpacing(SMESH::string_array_out xSpaceFunctions, + SMESH::double_array_out xInternalPoints, + CORBA::Short axis) + throw (SALOME::SALOME_Exception) +{ + ASSERT( myBaseImpl ); + try { + vector funVec; + vector pointVec; + this->GetImpl()->GetGridSpacing( funVec, pointVec, axis ); + + xSpaceFunctions = new SMESH::string_array(); + xInternalPoints = new SMESH::double_array(); + + _vec2array( funVec, xSpaceFunctions, _string2chars ); + _vec2array( pointVec, xInternalPoints, ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } +} + +//======================================================================= +//function : IsGridBySpacing +//purpose : Return true if the grid is defined by spacing functions and +// not by node coordinates +//======================================================================= + +CORBA::Boolean StdMeshers_CartesianParameters3D_i::IsGridBySpacing(CORBA::Short axis) +{ + return this->GetImpl()->IsGridBySpacing(axis); +} + +//======================================================================= +//function : ComputeCoordinates +//purpose : Computes node coordinates by spacing functions +//======================================================================= + +SMESH::double_array* +StdMeshers_CartesianParameters3D_i::ComputeCoordinates(CORBA::Double x0, + CORBA::Double x1, + const SMESH::string_array& spaceFuns, + const SMESH::double_array& points, + const char* axisName ) + throw (SALOME::SALOME_Exception) +{ + vector xFuns; + vector xPoints, coords; + _array2vec( spaceFuns, xFuns, (const char*) ); + _array2vec( points, xPoints, ); + + try { + this->GetImpl()->ComputeCoordinates( x0, x1, xFuns, xPoints, coords, axisName ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + SMESH::double_array_var res = new SMESH::double_array; + _vec2array( coords, res, ); + + return res._retn(); +} + +//============================================================================= +/*! + * Get implementation + */ +//============================================================================= + +::StdMeshers_CartesianParameters3D* StdMeshers_CartesianParameters3D_i::GetImpl() +{ + MESSAGE( "StdMeshers_CartesianParameters3D_i::GetImpl" ); + return ( ::StdMeshers_CartesianParameters3D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ + +CORBA::Boolean StdMeshers_CartesianParameters3D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_3D; +} diff --git a/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx new file mode 100644 index 000000000..e09422d0f --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_CartesianParameters3D_i.hxx @@ -0,0 +1,116 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_CartesianParameters3D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_CartesianParameters3D_I_HXX_ +#define _SMESH_CartesianParameters3D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" + +class SMESH_Gen; +class StdMeshers_CartesianParameters3D; + +// ====================================================== +// "CartesianParameters3D" hypothesis +// ====================================================== + +class STDMESHERS_I_EXPORT StdMeshers_CartesianParameters3D_i: + public virtual POA_StdMeshers::StdMeshers_CartesianParameters3D, + public virtual SMESH_Hypothesis_i +{ + public: + // Constructor + StdMeshers_CartesianParameters3D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_CartesianParameters3D_i(); + + /*! + * Set size threshold. A polyhedral cell got by cutting an initial + * hexahedron by geometry boundary is considered small and is removed if + * it's size is \athreshold times less than the size of the initial hexahedron. + * threshold must be > 1.0 + */ + void SetSizeThreshold(CORBA::Double threshold) throw (SALOME::SALOME_Exception); + CORBA::Double GetSizeThreshold(); + + /*! + * \brief Set node coordinates along an axis (counterd from zero) + */ + void SetGrid(const SMESH::double_array& coords, + CORBA::Short axis) throw (SALOME::SALOME_Exception); + SMESH::double_array* GetGrid(CORBA::Short axis) throw (SALOME::SALOME_Exception); + + /*! + * \brief Set grid spacing along an axis + * \param spaceFunctions - functions defining spacing value at given point on axis + * \param internalPoints - points dividing a grid into parts along each direction + * \param axis - index of an axis counterd from zero, i.e. 0==X, 1==Y, 2==Z + * + * Parameter t of spaceFunction f(t) is a position [0,1] withing bounding box of + * the shape to mesh or withing an interval defined by internal points + */ + void SetGridSpacing(const SMESH::string_array& spaceFunctions, + const SMESH::double_array& internalPoints, + CORBA::Short axis) throw (SALOME::SALOME_Exception); + + void GetGridSpacing(SMESH::string_array_out xSpaceFunctions, + SMESH::double_array_out xInternalPoints, + CORBA::Short axis) throw (SALOME::SALOME_Exception); + + /*! + * \brief Return true if the grid is defined by spacing functions and + * not by node coordinates + */ + CORBA::Boolean IsGridBySpacing(CORBA::Short axis); + + /*! + * \brief Computes node coordinates by spacing functions + * \param x0 - lower coordinate + * \param x1 - upper coordinate + * \param spaceFuns - space functions + * \param points - internal points + * \param coords - the computed coordinates + */ + SMESH::double_array* ComputeCoordinates(CORBA::Double x0, + CORBA::Double x1, + const SMESH::string_array& spaceFuns, + const SMESH::double_array& points, + const char* axisName ) + throw (SALOME::SALOME_Exception); + + // Get implementation + ::StdMeshers_CartesianParameters3D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); +}; + +#endif + diff --git a/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.cxx b/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.cxx new file mode 100644 index 000000000..445495f7a --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.cxx @@ -0,0 +1,79 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_Cartesian_3D_i.cxx +// Module : SMESH +// +#include "StdMeshers_Cartesian_3D_i.hxx" +#include "StdMeshers_Cartesian_3D.hxx" +#include "SMESH_Gen.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +//============================================================================= +/*! + * StdMeshers_Cartesian_3D_i::StdMeshers_Cartesian_3D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_Cartesian_3D_i::StdMeshers_Cartesian_3D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_3D_Algo_i( thePOA ) +{ + MESSAGE( "StdMeshers_Cartesian_3D_i::StdMeshers_Cartesian_3D_i" ); + myBaseImpl = new ::StdMeshers_Cartesian_3D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//============================================================================= +/*! + * StdMeshers_Cartesian_3D_i::~StdMeshers_Cartesian_3D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_Cartesian_3D_i::~StdMeshers_Cartesian_3D_i() +{ + MESSAGE( "StdMeshers_Cartesian_3D_i::~StdMeshers_Cartesian_3D_i" ); +} + +//============================================================================= +/*! + * StdMeshers_Cartesian_3D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_Cartesian_3D* StdMeshers_Cartesian_3D_i::GetImpl() +{ + MESSAGE( "StdMeshers_Cartesian_3D_i::GetImpl" ); + return ( ::StdMeshers_Cartesian_3D* )myBaseImpl; +} diff --git a/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.hxx b/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.hxx new file mode 100644 index 000000000..7b2d72a28 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Cartesian_3D_i.hxx @@ -0,0 +1,58 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_Cartesian_3D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_Cartesian_3D_I_HXX_ +#define _SMESH_Cartesian_3D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_3D_Algo_i.hxx" + +class SMESH_Gen; +class StdMeshers_Cartesian_3D; + +// ====================================================== +// Cartesianedron 3d algorithm +// ====================================================== +class STDMESHERS_I_EXPORT StdMeshers_Cartesian_3D_i: + public virtual POA_StdMeshers::StdMeshers_Cartesian_3D, + public virtual SMESH_3D_Algo_i +{ +public: + // Constructor + StdMeshers_Cartesian_3D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + + // Destructor + virtual ~StdMeshers_Cartesian_3D_i(); + + // Get implementation + ::StdMeshers_Cartesian_3D* GetImpl(); +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.cxx b/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.cxx index c9d1d7693..5dc09cbc5 100644 --- a/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_CompositeSegment_1D_i.cxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.hxx b/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.hxx index d8ffd9b9f..4f40a2d7a 100644 --- a/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_CompositeSegment_1D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_CompositeSegment_1D_i.hxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_Deflection1D_i.cxx b/src/StdMeshers_I/StdMeshers_Deflection1D_i.cxx index 2386c6aa7..93b208b7a 100644 --- a/src/StdMeshers_I/StdMeshers_Deflection1D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Deflection1D_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Deflection1D_i.cxx // Moved here from SMESH_LocalLength_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_Deflection1D_i.hxx" #include "SMESH_Gen_i.hxx" @@ -47,15 +47,15 @@ using namespace std; //============================================================================= StdMeshers_Deflection1D_i::StdMeshers_Deflection1D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_Deflection1D_i::StdMeshers_Deflection1D_i" ); myBaseImpl = new ::StdMeshers_Deflection1D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -89,11 +89,11 @@ void StdMeshers_Deflection1D_i::SetDeflection( CORBA::Double theValue ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetDeflection( " << theValue << " )"; + SMESH::TPythonDump() << _this() << ".SetDeflection( " << SMESH::TVar(theValue) << " )"; } //============================================================================= @@ -139,3 +139,14 @@ CORBA::Boolean StdMeshers_Deflection1D_i::IsDimSupported( SMESH::Dimension type return type == SMESH::DIM_1D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_Deflection1D_i::getMethodOfParameter(const int paramIndex, + int /*nbVars*/) const +{ + return "SetDeflection"; +} diff --git a/src/StdMeshers_I/StdMeshers_Deflection1D_i.hxx b/src/StdMeshers_I/StdMeshers_Deflection1D_i.hxx index 8fd89d4cd..f4e9a9b26 100644 --- a/src/StdMeshers_I/StdMeshers_Deflection1D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Deflection1D_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Deflection1D_i.hxx // Moved here from SMESH_LocalLength_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_Deflection1D_I_HXX_ #define _SMESH_Deflection1D_I_HXX_ @@ -65,7 +65,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif - diff --git a/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.cxx b/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.cxx new file mode 100644 index 000000000..1d39c8edd --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.cxx @@ -0,0 +1,288 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_FixedPoints1D_i.cxx +// Author : Damien COQUERET, OCC +// Module : SMESH +// $Header$ +// +#include "StdMeshers_FixedPoints1D_i.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_PythonDump.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::StdMeshers_FixedPoints1D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_FixedPoints1D_i::StdMeshers_FixedPoints1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::StdMeshers_FixedPoints1D_i" ); + myBaseImpl = new ::StdMeshers_FixedPoints1D(theGenImpl->GetANewId(), + theStudyId, + theGenImpl); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::~StdMeshers_FixedPoints1D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_FixedPoints1D_i::~StdMeshers_FixedPoints1D_i() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::~StdMeshers_FixedPoints1D_i" ); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::SetNbSegments + */ +//============================================================================= + +void StdMeshers_FixedPoints1D_i::SetNbSegments(const SMESH::long_array& listNbSeg) + throw ( SALOME::SALOME_Exception ) +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::SetNbSegments" ); + ASSERT( myBaseImpl ); + try { + std::vector nbsegs( listNbSeg.length() ); + CORBA::Long iEnd = listNbSeg.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + nbsegs[ i ] = listNbSeg[ i ]; + this->GetImpl()->SetNbSegments( nbsegs ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetNbSegments( " << listNbSeg << " )"; +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::SetPoints + */ +//============================================================================= + +void StdMeshers_FixedPoints1D_i::SetPoints(const SMESH::double_array& listParams) + throw ( SALOME::SALOME_Exception ) +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::SetPoints" ); + ASSERT( myBaseImpl ); + try { + std::vector params( listParams.length() ); + CORBA::Long iEnd = listParams.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + params[ i ] = listParams[ i ]; + this->GetImpl()->SetPoints( params ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetPoints( " << listParams << " )"; +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::GetPoints + * + * Get list of point's parameters + */ +//============================================================================= + +SMESH::double_array* StdMeshers_FixedPoints1D_i::GetPoints() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::GetPoints" ); + ASSERT( myBaseImpl ); + SMESH::double_array_var anArray = new SMESH::double_array; + std::vector params = this->GetImpl()->GetPoints(); + anArray->length( params.size() ); + for ( CORBA::Long i = 0; i < params.size(); i++) + anArray [ i ] = params [ i ]; + + return anArray._retn(); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::GetNbSegments + * + * Get list of point's parameters + */ +//============================================================================= + +SMESH::long_array* StdMeshers_FixedPoints1D_i::GetNbSegments() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::GetNbSegments" ); + ASSERT( myBaseImpl ); + SMESH::long_array_var anArray = new SMESH::long_array; + std::vector nbsegs = this->GetImpl()->GetNbSegments(); + anArray->length( nbsegs.size() ); + for ( CORBA::Long i = 0; i < nbsegs.size(); i++) + anArray [ i ] = nbsegs [ i ]; + + return anArray._retn(); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::SetReversedEdges + * + * Set edges to reverse + */ +//============================================================================= + +void StdMeshers_FixedPoints1D_i::SetReversedEdges( const SMESH::long_array& theIds ) +{ + ASSERT( myBaseImpl ); + try { + std::vector ids( theIds.length() ); + CORBA::Long iEnd = theIds.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + ids[ i ] = theIds[ i ]; + + this->GetImpl()->SetReversedEdges( ids ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetReversedEdges( " << theIds << " )"; +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::SetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +void StdMeshers_FixedPoints1D_i::SetObjectEntry( const char* theEntry ) +{ + ASSERT( myBaseImpl ); + string entry(theEntry); // actually needed as theEntry is spoiled by moment of dumping + try { + this->GetImpl()->SetObjectEntry( entry.c_str() ); + // Update Python script + SMESH::TPythonDump() << _this() << ".SetObjectEntry( \"" << entry.c_str() << "\" )"; + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(),SALOME::BAD_PARAM ); + } +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::GetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +char* StdMeshers_FixedPoints1D_i::GetObjectEntry() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::SetObjectEntry" ); + ASSERT( myBaseImpl ); + const char* entry; + try { + entry = this->GetImpl()->GetObjectEntry(); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + return CORBA::string_dup( entry ); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::GetReversedEdges + * + * Get reversed edges + */ +//============================================================================= + +SMESH::long_array* StdMeshers_FixedPoints1D_i::GetReversedEdges() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::GetReversedEdges" ); + ASSERT( myBaseImpl ); + SMESH::long_array_var anArray = new SMESH::long_array; + std::vector ids = this->GetImpl()->GetReversedEdges(); + anArray->length( ids.size() ); + for ( CORBA::Long i = 0; i < ids.size(); i++) + anArray [ i ] = ids [ i ]; + + return anArray._retn(); +} + +//============================================================================= +/*! + * StdMeshers_FixedPoints1D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_FixedPoints1D* StdMeshers_FixedPoints1D_i::GetImpl() +{ + MESSAGE( "StdMeshers_FixedPoints1D_i::GetImpl" ); + return ( ::StdMeshers_FixedPoints1D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_FixedPoints1D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_1D; +} + diff --git a/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.hxx b/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.hxx new file mode 100644 index 000000000..ee43e5ca9 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_FixedPoints1D_i.hxx @@ -0,0 +1,85 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_FixedPoints1D_i.hxx +// Author : Damien COQUERET, OCC +// Module : SMESH +// +#ifndef _SMESH_FIXEDPOINTS1D_I_HXX_ +#define _SMESH_FIXEDPOINTS1D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_FixedPoints1D.hxx" + +// ====================================================== +// Fixed points 1D hypothesis +// ====================================================== +class STDMESHERS_I_EXPORT StdMeshers_FixedPoints1D_i: + public virtual POA_StdMeshers::StdMeshers_FixedPoints1D, + public virtual SMESH_Hypothesis_i +{ +public: + // Constructor + StdMeshers_FixedPoints1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_FixedPoints1D_i(); + + // Sets some points on edge using parameter on curve from 0 to 1 + // (additionally it is neecessary to check orientation of edges and + // create list of reversed edges if it is needed) and sets numbers + // of segments between given points (default values are equals 1) + void SetPoints(const SMESH::double_array& listParams) + throw ( SALOME::SALOME_Exception ); + void SetNbSegments(const SMESH::long_array& listNbSeg) + throw ( SALOME::SALOME_Exception ); + + // Returns list of point's parameters + SMESH::double_array* GetPoints(); + + // Returns list of numbers of segments + SMESH::long_array* GetNbSegments(); + + //Set Reversed Edges + void SetReversedEdges( const SMESH::long_array& theIDs); + + //Get Reversed Edges + SMESH::long_array* GetReversedEdges(); + + //Set the Entry of the Object + void SetObjectEntry( const char* theEntry); + + //Get Object Entry + char* GetObjectEntry(); + + // Get implementation + ::StdMeshers_FixedPoints1D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_Hexa_3D_i.cxx b/src/StdMeshers_I/StdMeshers_Hexa_3D_i.cxx index cac9b45e2..d75efe8e8 100644 --- a/src/StdMeshers_I/StdMeshers_Hexa_3D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Hexa_3D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Hexa_3D_i.cxx // Moved here from SMESH_Hexa_3D_i.cxx @@ -43,8 +44,8 @@ using namespace std; //============================================================================= StdMeshers_Hexa_3D_i::StdMeshers_Hexa_3D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ), SMESH_Algo_i( thePOA ), @@ -52,8 +53,8 @@ StdMeshers_Hexa_3D_i::StdMeshers_Hexa_3D_i( PortableServer::POA_ptr thePOA, { MESSAGE( "StdMeshers_Hexa_3D_i::StdMeshers_Hexa_3D_i" ); myBaseImpl = new ::StdMeshers_Hexa_3D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= diff --git a/src/StdMeshers_I/StdMeshers_Hexa_3D_i.hxx b/src/StdMeshers_I/StdMeshers_Hexa_3D_i.hxx index 9c07aed6f..6316fea71 100644 --- a/src/StdMeshers_I/StdMeshers_Hexa_3D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Hexa_3D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Hexa_3D_i.hxx // Moved here from SMESH_Hexa_3D_i.hxx @@ -50,7 +51,7 @@ public: // Constructor StdMeshers_Hexa_3D_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_Hexa_3D_i(); diff --git a/src/StdMeshers_I/StdMeshers_ImportSource1D_i.cxx b/src/StdMeshers_I/StdMeshers_ImportSource1D_i.cxx new file mode 100644 index 000000000..e2ad32141 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ImportSource1D_i.cxx @@ -0,0 +1,280 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ImportSource1D_i.cxx +// Module : SMESH +// +#include "StdMeshers_ImportSource1D_i.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_PythonDump.hxx" +#include "StdMeshers_ObjRefUlils.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +#include CORBA_SERVER_HEADER(SMESH_Group) + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_ImportSource1D_i::StdMeshers_ImportSource1D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_ImportSource1D_i::StdMeshers_ImportSource1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_ImportSource1D_i::StdMeshers_ImportSource1D_i" ); + myBaseImpl = new ::StdMeshers_ImportSource1D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); + _groupEntries = new SMESH::string_array(); +} + +//============================================================================= +/*! + * StdMeshers_ImportSource1D_i::~StdMeshers_ImportSource1D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_ImportSource1D_i::~StdMeshers_ImportSource1D_i() +{ + MESSAGE( "StdMeshers_ImportSource1D_i::~StdMeshers_ImportSource1D_i" ); +} + +//============================================================================= +/*! + * SetSourceEdges + */ +//============================================================================= + +void StdMeshers_ImportSource1D_i::SetSourceEdges(const SMESH::ListOfGroups& groups) +{ + MESSAGE( "StdMeshers_ImportSource1D_i::SetSourceEdges" ); + ASSERT( myBaseImpl ); + try + { + std::vector smesh_groups; + std::vector entries; + SALOMEDS::Study_var study = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); + for ( int i = 0; i < groups.length(); ++i ) + if ( SMESH_GroupBase_i* gp_i = SMESH::DownCast( groups[i] )) + { + if ( gp_i->GetType() != SMESH::EDGE ) + THROW_SALOME_CORBA_EXCEPTION("Wrong group type", SALOME::BAD_PARAM); + smesh_groups.push_back( gp_i->GetSmeshGroup() ); + + SALOMEDS::SObject_var so = SMESH_Gen_i::GetSMESHGen()->ObjectToSObject(study, groups[i]); + if ( !so->_is_nil()) + { + CORBA::String_var entry = so->GetID(); + entries.push_back( entry.in() ); + } + } + this->GetImpl()->SetGroups( smesh_groups ); + + _groupEntries = new SMESH::string_array; + _groupEntries->length( entries.size ()); + for ( int i = 0; i < entries.size(); ++i ) + _groupEntries[i] = entries[i].c_str(); + } + catch ( SALOME_Exception& S_ex ) + { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetSourceEdges( " << groups << " )"; +} + +//============================================================================= +/*! + * Return entries of groups + */ +//============================================================================= + +SMESH::string_array* StdMeshers_ImportSource1D_i::GetSourceEdges() +{ + MESSAGE( "StdMeshers_ImportSource1D_i::GetImportSource" ); + SMESH::string_array_var res = new SMESH::string_array( _groupEntries ); + return res._retn(); +} + +//================================================================================ +/*! + * \brief Set to copy mesh and groups + */ +//================================================================================ + +void StdMeshers_ImportSource1D_i::SetCopySourceMesh(CORBA::Boolean toCopyMesh, + CORBA::Boolean toCopyGroups) +{ + GetImpl()->SetCopySourceMesh(toCopyMesh,toCopyGroups); + SMESH::TPythonDump() << _this() << ".SetCopySourceMesh( " + << toCopyMesh << ", " << toCopyGroups << " )"; +} + +//================================================================================ +/*! + * \brief Return "to copy mesh and groups" + */ +//================================================================================ + +void StdMeshers_ImportSource1D_i::GetCopySourceMesh(CORBA::Boolean& toCopyMesh, + CORBA::Boolean& toCopyGroups) +{ + GetImpl()->GetCopySourceMesh(toCopyMesh,toCopyGroups); +} + +//================================================================================ +/*! + * \brief Write parameters in a string + * \retval char* - resulting string + */ +//================================================================================ + +char* StdMeshers_ImportSource1D_i::SaveTo() +{ + std::ostringstream os; + os << " " << _groupEntries->length(); + + SALOMEDS::Study_var study = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); + for ( int i = 0; i < _groupEntries->length(); ++i ) + { + // entry + os << " " << _groupEntries[i]; + + // id + SALOMEDS::SObject_var groupSO = study->FindObjectID( _groupEntries[i] ); + CORBA::Object_var groupObj; + if ( !groupSO->_is_nil() ) + groupObj = groupSO->GetObject(); + StdMeshers_ObjRefUlils::SaveToStream( groupObj, os ); + } + + myBaseImpl->SaveTo( os ); + + return CORBA::string_dup( os.str().c_str() ); +} + +//================================================================================ +/*! + * \brief Retrieve parameters from the string + * \param theStream - the input string + */ +//================================================================================ + +void StdMeshers_ImportSource1D_i::LoadFrom( const char* theStream ) +{ + std::istringstream is( theStream ); + + int nbGroups; + is >> nbGroups; + + _groupEntries = new SMESH::string_array; + _groupEntries->length( nbGroups ); + std::string id, entry; + for ( int i = 0; i < _groupEntries->length(); ++i ) + { + if ( is >> entry ) + _groupEntries[i] = entry.c_str(); + else + { + _groupEntries->length( i ); + is.clear(ios::badbit | is.rdstate()); + break; + } + if ( is >> id ) + _groupIDs.push_back( id ); + else + { + is.clear(ios::badbit | is.rdstate()); + break; + } + } + + myBaseImpl->LoadFrom( is ); +} + +//================================================================================ +/*! + * \brief Retrieve groups by their ids loaded by LoadFrom() + * This is possible only when all meshes are fully loaded + */ +//================================================================================ + +void StdMeshers_ImportSource1D_i::UpdateAsMeshesRestored() +{ + std::vector smesh_groups; + for ( unsigned i = 0; i < _groupIDs.size(); ++i ) + { + std::istringstream is( _groupIDs[i].c_str() ); + SMESH::SMESH_GroupBase_var group = + StdMeshers_ObjRefUlils::LoadObjectFromStream( is ); + if ( SMESH_GroupBase_i* gp_i = SMESH::DownCast( group )) + smesh_groups.push_back( gp_i->GetSmeshGroup() ); + } + GetImpl()->RestoreGroups(smesh_groups); +} + +//============================================================================= +/*! + * StdMeshers_ImportSource1D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_ImportSource1D* StdMeshers_ImportSource1D_i::GetImpl() +{ + MESSAGE( "StdMeshers_ImportSource1D_i::GetImpl" ); + return ( ::StdMeshers_ImportSource1D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_ImportSource1D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_1D; +} + diff --git a/src/StdMeshers_I/StdMeshers_ImportSource1D_i.hxx b/src/StdMeshers_I/StdMeshers_ImportSource1D_i.hxx new file mode 100644 index 000000000..1c3fafaa7 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ImportSource1D_i.hxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ImportSource1D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_ImportSource1D_I_HXX_ +#define _SMESH_ImportSource1D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_ImportSource.hxx" + +class SMESH_Gen; + +class STDMESHERS_I_EXPORT StdMeshers_ImportSource1D_i: + public virtual POA_StdMeshers::StdMeshers_ImportSource1D, + public virtual SMESH_Hypothesis_i +{ + public: + // Constructor + StdMeshers_ImportSource1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_ImportSource1D_i(); + + void SetSourceEdges(const ::SMESH::ListOfGroups& groups); + SMESH::string_array* GetSourceEdges(); + void SetCopySourceMesh(::CORBA::Boolean toCopyMesh, ::CORBA::Boolean toCopyGroups); + void GetCopySourceMesh(::CORBA::Boolean& toCopyMesh, ::CORBA::Boolean& toCopyGroups); + + // Get implementation + ::StdMeshers_ImportSource1D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + // Redefined Persistence + virtual char* SaveTo(); + virtual void LoadFrom( const char* theStream ); + virtual void UpdateAsMeshesRestored(); + + private: + SMESH::string_array_var _groupEntries; + std::vector< std::string > _groupIDs; +}; + +#endif + diff --git a/src/StdMeshers_I/StdMeshers_ImportSource2D_i.cxx b/src/StdMeshers_I/StdMeshers_ImportSource2D_i.cxx new file mode 100644 index 000000000..35f9d3fb0 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ImportSource2D_i.cxx @@ -0,0 +1,280 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ImportSource2D_i.cxx +// Module : SMESH +// +#include "StdMeshers_ImportSource2D_i.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Group_i.hxx" +#include "SMESH_PythonDump.hxx" +#include "StdMeshers_ObjRefUlils.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +#include CORBA_SERVER_HEADER(SMESH_Group) + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_ImportSource2D_i::StdMeshers_ImportSource2D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_ImportSource2D_i::StdMeshers_ImportSource2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_ImportSource2D_i::StdMeshers_ImportSource2D_i" ); + myBaseImpl = new ::StdMeshers_ImportSource2D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); + _groupEntries = new SMESH::string_array(); +} + +//============================================================================= +/*! + * StdMeshers_ImportSource2D_i::~StdMeshers_ImportSource2D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_ImportSource2D_i::~StdMeshers_ImportSource2D_i() +{ + MESSAGE( "StdMeshers_ImportSource2D_i::~StdMeshers_ImportSource2D_i" ); +} + +//============================================================================= +/*! + * SetSourceFaces + */ +//============================================================================= + +void StdMeshers_ImportSource2D_i::SetSourceFaces(const SMESH::ListOfGroups& groups) +{ + MESSAGE( "StdMeshers_ImportSource2D_i::SetSourceFaces" ); + ASSERT( myBaseImpl ); + try + { + std::vector smesh_groups; + std::vector entries; + SALOMEDS::Study_var study = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); + for ( int i = 0; i < groups.length(); ++i ) + if ( SMESH_GroupBase_i* gp_i = SMESH::DownCast( groups[i] )) + { + if ( gp_i->GetType() != SMESH::FACE ) + THROW_SALOME_CORBA_EXCEPTION("Wrong group type", SALOME::BAD_PARAM); + smesh_groups.push_back( gp_i->GetSmeshGroup() ); + + SALOMEDS::SObject_var so = SMESH_Gen_i::GetSMESHGen()->ObjectToSObject(study, groups[i]); + if ( !so->_is_nil()) + { + CORBA::String_var entry = so->GetID(); + entries.push_back( entry.in() ); + } + } + this->GetImpl()->SetGroups( smesh_groups ); + + _groupEntries = new SMESH::string_array; + _groupEntries->length( entries.size ()); + for ( int i = 0; i < entries.size(); ++i ) + _groupEntries[i] = entries[i].c_str(); + } + catch ( SALOME_Exception& S_ex ) + { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetSourceFaces( " << groups << " )"; +} + +//============================================================================= +/*! + * Return entries of groups + */ +//============================================================================= + +SMESH::string_array* StdMeshers_ImportSource2D_i::GetSourceFaces() +{ + MESSAGE( "StdMeshers_ImportSource2D_i::GetImportSource" ); + SMESH::string_array_var res = new SMESH::string_array( _groupEntries ); + return res._retn(); +} + +//================================================================================ +/*! + * \brief Set to copy mesh and groups + */ +//================================================================================ + +void StdMeshers_ImportSource2D_i::SetCopySourceMesh(CORBA::Boolean toCopyMesh, + CORBA::Boolean toCopyGroups) +{ + GetImpl()->SetCopySourceMesh(toCopyMesh,toCopyGroups); + SMESH::TPythonDump() << _this() << ".SetCopySourceMesh( " + << toCopyMesh << ", " << toCopyGroups << " )"; +} + +//================================================================================ +/*! + * \brief Return "to copy mesh and groups" + */ +//================================================================================ + +void StdMeshers_ImportSource2D_i::GetCopySourceMesh(CORBA::Boolean& toCopyMesh, + CORBA::Boolean& toCopyGroups) +{ + GetImpl()->GetCopySourceMesh(toCopyMesh,toCopyGroups); +} + +//================================================================================ +/*! + * \brief Write parameters in a string + * \retval char* - resulting string + */ +//================================================================================ + +char* StdMeshers_ImportSource2D_i::SaveTo() +{ + std::ostringstream os; + os << " " << _groupEntries->length(); + + SALOMEDS::Study_var study = SMESH_Gen_i::GetSMESHGen()->GetCurrentStudy(); + for ( int i = 0; i < _groupEntries->length(); ++i ) + { + // entry + os << " " << _groupEntries[i]; + + // id + SALOMEDS::SObject_var groupSO = study->FindObjectID( _groupEntries[i] ); + CORBA::Object_var groupObj; + if ( !groupSO->_is_nil() ) + groupObj = groupSO->GetObject(); + StdMeshers_ObjRefUlils::SaveToStream( groupObj, os ); + } + + myBaseImpl->SaveTo( os ); + + return CORBA::string_dup( os.str().c_str() ); +} + +//================================================================================ +/*! + * \brief Retrieve parameters from the string + * \param theStream - the input string + */ +//================================================================================ + +void StdMeshers_ImportSource2D_i::LoadFrom( const char* theStream ) +{ + std::istringstream is( theStream ); + + int nbGroups; + is >> nbGroups; + + _groupEntries = new SMESH::string_array; + _groupEntries->length( nbGroups ); + std::string id, entry; + for ( int i = 0; i < _groupEntries->length(); ++i ) + { + if ( is >> entry ) + _groupEntries[i] = entry.c_str(); + else + { + _groupEntries->length( i ); + is.clear(ios::badbit | is.rdstate()); + break; + } + if ( is >> id ) + _groupIDs.push_back( id ); + else + { + is.clear(ios::badbit | is.rdstate()); + break; + } + } + + myBaseImpl->LoadFrom( is ); +} + +//================================================================================ +/*! + * \brief Retrieve groups by their ids loaded by LoadFrom() + * This is possible only when all meshes are fully loaded + */ +//================================================================================ + +void StdMeshers_ImportSource2D_i::UpdateAsMeshesRestored() +{ + std::vector smesh_groups; + for ( unsigned i = 0; i < _groupIDs.size(); ++i ) + { + std::istringstream is( _groupIDs[i].c_str() ); + SMESH::SMESH_GroupBase_var group = + StdMeshers_ObjRefUlils::LoadObjectFromStream( is ); + if ( SMESH_GroupBase_i* gp_i = SMESH::DownCast( group )) + smesh_groups.push_back( gp_i->GetSmeshGroup() ); + } + GetImpl()->RestoreGroups(smesh_groups); +} + +//============================================================================= +/*! + * StdMeshers_ImportSource2D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_ImportSource2D* StdMeshers_ImportSource2D_i::GetImpl() +{ + MESSAGE( "StdMeshers_ImportSource2D_i::GetImpl" ); + return ( ::StdMeshers_ImportSource2D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_ImportSource2D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} + diff --git a/src/StdMeshers_I/StdMeshers_ImportSource2D_i.hxx b/src/StdMeshers_I/StdMeshers_ImportSource2D_i.hxx new file mode 100644 index 000000000..eea15bacd --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ImportSource2D_i.hxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ImportSource2D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_ImportSource2D_I_HXX_ +#define _SMESH_ImportSource2D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_ImportSource.hxx" + +class SMESH_Gen; + +class STDMESHERS_I_EXPORT StdMeshers_ImportSource2D_i: + public virtual POA_StdMeshers::StdMeshers_ImportSource2D, + public virtual SMESH_Hypothesis_i +{ + public: + // Constructor + StdMeshers_ImportSource2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_ImportSource2D_i(); + + void SetSourceFaces(const ::SMESH::ListOfGroups& groups); + SMESH::string_array* GetSourceFaces(); + void SetCopySourceMesh(::CORBA::Boolean toCopyMesh, ::CORBA::Boolean toCopyGroups); + void GetCopySourceMesh(::CORBA::Boolean& toCopyMesh, ::CORBA::Boolean& toCopyGroups); + + // Get implementation + ::StdMeshers_ImportSource2D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + // Redefined Persistence + virtual char* SaveTo(); + virtual void LoadFrom( const char* theStream ); + virtual void UpdateAsMeshesRestored(); + + private: + SMESH::string_array_var _groupEntries; + std::vector< std::string > _groupIDs; +}; + +#endif + diff --git a/src/StdMeshers_I/StdMeshers_Import_1D2D_i.cxx b/src/StdMeshers_I/StdMeshers_Import_1D2D_i.cxx new file mode 100644 index 000000000..104b7773b --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Import_1D2D_i.cxx @@ -0,0 +1,67 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_Import_1D2D_i.cxx +// Module : SMESH +// +#include "StdMeshers_Import_1D2D_i.hxx" +#include "SMESH_Gen.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +using namespace std; + + +//============================================================================= +/*! + * StdMeshers_Import_1D2D_i::StdMeshers_Import_1D2D_i + */ +//============================================================================= + +StdMeshers_Import_1D2D_i::StdMeshers_Import_1D2D_i (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_2D_Algo_i( thePOA ) +{ + MESSAGE( "StdMeshers_Import_1D2D_i::StdMeshers_Import_1D2D_i" ); + myBaseImpl = new ::StdMeshers_Import_1D2D(theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//----------------------------------------------------------------------------- + +StdMeshers_Import_1D2D_i::~StdMeshers_Import_1D2D_i() +{ + MESSAGE( "StdMeshers_Import_1D2D_i::~StdMeshers_Import_1D2D_i" ); +} + +//----------------------------------------------------------------------------- + +::StdMeshers_Import_1D2D* StdMeshers_Import_1D2D_i::GetImpl() +{ + MESSAGE( "StdMeshers_Import_1D2D_i::GetImpl" ); + return ( ::StdMeshers_Import_1D2D* )myBaseImpl; +} + diff --git a/src/StdMeshers_I/StdMeshers_Import_1D2D_i.hxx b/src/StdMeshers_I/StdMeshers_Import_1D2D_i.hxx new file mode 100644 index 000000000..150f5aa3a --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Import_1D2D_i.hxx @@ -0,0 +1,53 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_Import_1D2D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_Import_1D2D_I_HXX_ +#define _SMESH_Import_1D2D_I_HXX_ + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_2D_Algo_i.hxx" +#include "StdMeshers_Import_1D2D.hxx" + +class SMESH_Gen; + +class StdMeshers_Import_1D2D_i: + public virtual POA_StdMeshers::StdMeshers_Import_1D2D, + public virtual SMESH_2D_Algo_i +{ +public: + // Constructor + StdMeshers_Import_1D2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + + // Destructor + virtual ~StdMeshers_Import_1D2D_i(); + + // Get implementation + ::StdMeshers_Import_1D2D* GetImpl(); +}; + + +#endif diff --git a/src/StdMeshers_I/StdMeshers_Import_1D_i.cxx b/src/StdMeshers_I/StdMeshers_Import_1D_i.cxx new file mode 100644 index 000000000..2a73e4e2e --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Import_1D_i.cxx @@ -0,0 +1,85 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_Import_1D_i.cxx +// Moved here from SMESH_Import_1D_i.cxx +// Author : Paul RASCLE, EDF +// Module : SMESH +// +#include "StdMeshers_Import_1D_i.hxx" +#include "SMESH_Gen.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_Import_1D_i::StdMeshers_Import_1D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_Import_1D_i::StdMeshers_Import_1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_1D_Algo_i( thePOA ) +{ + MESSAGE( "StdMeshers_Import_1D_i::StdMeshers_Import_1D_i" ); + myBaseImpl = new ::StdMeshers_Import_1D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//============================================================================= +/*! + * StdMeshers_Import_1D_i::~StdMeshers_Import_1D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_Import_1D_i::~StdMeshers_Import_1D_i() +{ + MESSAGE( "StdMeshers_Import_1D_i::~StdMeshers_Import_1D_i" ); +} + +//============================================================================= +/*! + * StdMeshers_Import_1D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_Import_1D* StdMeshers_Import_1D_i::GetImpl() +{ + MESSAGE( "StdMeshers_Import_1D_i::GetImpl" ); + return ( ::StdMeshers_Import_1D* )myBaseImpl; +} + diff --git a/src/StdMeshers_I/StdMeshers_Import_1D_i.hxx b/src/StdMeshers_I/StdMeshers_Import_1D_i.hxx new file mode 100644 index 000000000..eec55f242 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_Import_1D_i.hxx @@ -0,0 +1,54 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_Import_1D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_Import_1D_I_HXX_ +#define _SMESH_Import_1D_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_1D_Algo_i.hxx" +#include "StdMeshers_Import_1D.hxx" + +class STDMESHERS_I_EXPORT StdMeshers_Import_1D_i: + public virtual POA_StdMeshers::StdMeshers_Import_1D, + public virtual SMESH_1D_Algo_i +{ + public: + // Constructor + StdMeshers_Import_1D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_Import_1D_i(); + + // Get implementation + ::StdMeshers_Import_1D* GetImpl(); +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.cxx b/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.cxx new file mode 100644 index 000000000..5fd33f413 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.cxx @@ -0,0 +1,90 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_LayerDistribution2D_i.cxx +// Author : Edward AGAPOV +// Module : SMESH +// +#include "StdMeshers_LayerDistribution2D_i.hxx" + +#include "utilities.h" + +//============================================================================= +/*! + * StdMeshers_LayerDistribution2D_i::StdMeshers_LayerDistribution2D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_LayerDistribution2D_i::StdMeshers_LayerDistribution2D_i + (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) +: StdMeshers_LayerDistribution_i(thePOA,theStudyId,theGenImpl), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_LayerDistribution2D_i::StdMeshers_LayerDistribution2D_i" ); + myBaseImpl = new ::StdMeshers_LayerDistribution2D(theGenImpl->GetANewId(), + theStudyId, + theGenImpl); +} + +//============================================================================= +/*! + * StdMeshers_LayerDistribution2D_i::~StdMeshers_LayerDistribution2D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_LayerDistribution2D_i::~StdMeshers_LayerDistribution2D_i() +{ + MESSAGE("StdMeshers_LayerDistribution2D_i::~StdMeshers_LayerDistribution2D_i"); +} + +//============================================================================= +/*! + * StdMeshers_LayerDistribution2D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_LayerDistribution2D* StdMeshers_LayerDistribution2D_i::GetImpl() +{ + return ( ::StdMeshers_LayerDistribution2D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_LayerDistribution2D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} + + diff --git a/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.hxx b/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.hxx new file mode 100644 index 000000000..25acd65ce --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_LayerDistribution2D_i.hxx @@ -0,0 +1,61 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_LayerDistribution2D_i.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_LayerDistribution2D_I_HXX_ +#define _SMESH_LayerDistribution2D_I_HXX_ + +#include "StdMeshers_LayerDistribution_i.hxx" +#include "StdMeshers_LayerDistribution2D.hxx" + + +// ========================================================= +/*! + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies 1D hypothesis defining distribution of segments + * between the internal and the external surfaces. + */ +// ========================================================= + +class StdMeshers_LayerDistribution2D_i: + public virtual POA_StdMeshers::StdMeshers_LayerDistribution2D, + public virtual StdMeshers_LayerDistribution_i +{ +public: + // Constructor + StdMeshers_LayerDistribution2D_i(PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_LayerDistribution2D_i(); + + // Get implementation + ::StdMeshers_LayerDistribution2D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + +}; + +#endif + diff --git a/src/StdMeshers_I/StdMeshers_LayerDistribution_i.cxx b/src/StdMeshers_I/StdMeshers_LayerDistribution_i.cxx index 53707613c..e162f22aa 100644 --- a/src/StdMeshers_I/StdMeshers_LayerDistribution_i.cxx +++ b/src/StdMeshers_I/StdMeshers_LayerDistribution_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_LayerDistribution_i.cxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #include "StdMeshers_LayerDistribution_i.hxx" #include "SMESH_Gen_i.hxx" @@ -168,9 +168,9 @@ char* StdMeshers_LayerDistribution_i::SaveTo() else { os << hyp1D->GetName() << " " << hyp1D->GetLibName() << " " - << hyp1D_i->SaveTo(); + << hyp1D_i->SaveTo() << " "; } - //myBaseImpl->SaveTo( os ); + os << SMESH_Hypothesis_i::SaveTo(); // to have a mark of storage version ("VARS...") return CORBA::string_dup( os.str().c_str() ); } @@ -200,11 +200,14 @@ void StdMeshers_LayerDistribution_i::LoadFrom( const char* theStream ) gen->CreateHypothesis( typeName.c_str(), libName.c_str() ); SMESH_Hypothesis_i* hyp1D_i = SMESH::DownCast< SMESH_Hypothesis_i*>( hyp1D ); if ( hyp1D_i ) { - hyp1D_i->LoadFrom( & theStream[ is.tellg() ]); + hyp1D_i->LoadFrom( & theStream[ (streamoff) is.tellg()+1 ]); this->GetImpl()->SetLayerDistribution( hyp1D_i->GetImpl() ); myHyp = hyp1D; // as hyp1D is not published, its ID changes //SMESH::TPythonDump() << _this() << ".SetLayerDistribution( " << hyp1D << " )"; + + // restore a mark of storage version ("VARS...") + SMESH_Hypothesis_i::LoadFrom( & theStream[ (streamoff)is.tellg()+1 ]); } } catch (...) { @@ -213,3 +216,14 @@ void StdMeshers_LayerDistribution_i::LoadFrom( const char* theStream ) } } +//================================================================================ +/*! + * \brief Restore myMethod2VarParams by parameters stored in an old study + */ +//================================================================================ + +void StdMeshers_LayerDistribution_i::setOldParameters (const char* theParameters) +{ + if ( SMESH_Hypothesis_i* hyp1D_i = SMESH::DownCast< SMESH_Hypothesis_i*>( myHyp )) + hyp1D_i->setOldParameters( theParameters ); +} diff --git a/src/StdMeshers_I/StdMeshers_LayerDistribution_i.hxx b/src/StdMeshers_I/StdMeshers_LayerDistribution_i.hxx index f08a9d7ef..78bf71d5a 100644 --- a/src/StdMeshers_I/StdMeshers_LayerDistribution_i.hxx +++ b/src/StdMeshers_I/StdMeshers_LayerDistribution_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LayerDistribution_i.hxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #ifndef _SMESH_LayerDistribution_I_HXX_ #define _SMESH_LayerDistribution_I_HXX_ @@ -79,9 +79,12 @@ public: virtual char* SaveTo(); virtual void LoadFrom( const char* theStream ); +protected: + // restore myMethod2VarParams by parameters stored in an old study + virtual void setOldParameters (const char* theParameters); + private: SMESH::SMESH_Hypothesis_var myHyp; }; #endif - diff --git a/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.cxx b/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.cxx index 0d78830d6..324a9b064 100644 --- a/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.cxx +++ b/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LengthFromEdges_i.cxx // Moved here from SMESH_LengthFromEdges_i.cxx @@ -43,15 +44,15 @@ using namespace std; //============================================================================= StdMeshers_LengthFromEdges_i::StdMeshers_LengthFromEdges_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_LengthFromEdges_i::StdMeshers_LengthFromEdges_i" ); myBaseImpl = new ::StdMeshers_LengthFromEdges( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -85,7 +86,7 @@ void StdMeshers_LengthFromEdges_i::SetMode( CORBA::Long theMode ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } diff --git a/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.hxx b/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.hxx index 77ebc48ad..38f50d3df 100644 --- a/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.hxx +++ b/src/StdMeshers_I/StdMeshers_LengthFromEdges_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LengthFromEdges_i.hxx // Moved here from SMESH_LengthFromEdges_i.hxx diff --git a/src/StdMeshers_I/StdMeshers_LocalLength_i.cxx b/src/StdMeshers_I/StdMeshers_LocalLength_i.cxx index fdc8c1c27..5d6ec1b51 100644 --- a/src/StdMeshers_I/StdMeshers_LocalLength_i.cxx +++ b/src/StdMeshers_I/StdMeshers_LocalLength_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LocalLength_i.cxx // Moved here from SMESH_LocalLength_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_LocalLength_i.hxx" #include "SMESH_Gen_i.hxx" @@ -88,11 +88,11 @@ void StdMeshers_LocalLength_i::SetLength( CORBA::Double theLength ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetLength( " << theLength << " )"; + SMESH::TPythonDump() << _this() << ".SetLength( " << SMESH::TVar(theLength) << " )"; } //============================================================================= @@ -112,11 +112,11 @@ void StdMeshers_LocalLength_i::SetPrecision( CORBA::Double thePrecision ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetPrecision( " << thePrecision << " )"; + SMESH::TPythonDump() << _this() << ".SetPrecision( " << SMESH::TVar(thePrecision) << " )"; } //============================================================================= @@ -173,3 +173,9 @@ CORBA::Boolean StdMeshers_LocalLength_i::IsDimSupported( SMESH::Dimension type ) { return type == SMESH::DIM_1D; } + +std::string StdMeshers_LocalLength_i::getMethodOfParameter(const int paramIndex, + int /*nbVars*/) const +{ + return paramIndex == 0 ? "SetLength" : "SetPrecision"; +} diff --git a/src/StdMeshers_I/StdMeshers_LocalLength_i.hxx b/src/StdMeshers_I/StdMeshers_LocalLength_i.hxx index e31b08ece..dd2b239e1 100644 --- a/src/StdMeshers_I/StdMeshers_LocalLength_i.hxx +++ b/src/StdMeshers_I/StdMeshers_LocalLength_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_LocalLength_i.hxx // Moved here from SMESH_LocalLength_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_LOCALLENGTH_I_HXX_ #define _SMESH_LOCALLENGTH_I_HXX_ @@ -71,6 +71,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.cxx b/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.cxx index 4731e8888..5eb244e89 100644 --- a/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MEFISTO_2D_i.cxx // Moved here from SMESH_MEFISTO_2D_i.cxx @@ -43,8 +44,8 @@ using namespace std; //============================================================================= StdMeshers_MEFISTO_2D_i::StdMeshers_MEFISTO_2D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ), SMESH_Algo_i( thePOA ), @@ -52,8 +53,8 @@ StdMeshers_MEFISTO_2D_i::StdMeshers_MEFISTO_2D_i( PortableServer::POA_ptr thePOA { MESSAGE( "StdMeshers_MEFISTO_2D_i::StdMeshers_MEFISTO_2D_i" ); myBaseImpl = new ::StdMeshers_MEFISTO_2D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= diff --git a/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.hxx b/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.hxx index f368c76c5..3700c7cea 100644 --- a/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_MEFISTO_2D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MEFISTO_2D_i.hxx // Moved here from SMESH_MEFISTO_2D_i.hxx @@ -50,7 +51,7 @@ public: // Constructor StdMeshers_MEFISTO_2D_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_MEFISTO_2D_i(); diff --git a/src/StdMeshers_I/StdMeshers_MaxElementArea_i.cxx b/src/StdMeshers_I/StdMeshers_MaxElementArea_i.cxx index 8a8169135..7af8090bc 100644 --- a/src/StdMeshers_I/StdMeshers_MaxElementArea_i.cxx +++ b/src/StdMeshers_I/StdMeshers_MaxElementArea_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxElementArea_i.cxx // Moved here from SMESH_MaxElementArea_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_MaxElementArea_i.hxx" #include "SMESH_Gen_i.hxx" @@ -47,15 +47,15 @@ using namespace std; //============================================================================= StdMeshers_MaxElementArea_i::StdMeshers_MaxElementArea_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_MaxElementArea_i::StdMeshers_MaxElementArea_i" ); myBaseImpl = new ::StdMeshers_MaxElementArea( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -89,11 +89,11 @@ void StdMeshers_MaxElementArea_i::SetMaxElementArea( CORBA::Double theArea ) } catch (SALOME_Exception& S_ex) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetMaxElementArea( " << theArea << " )"; + SMESH::TPythonDump() << _this() << ".SetMaxElementArea( " << SMESH::TVar(theArea) << " )"; } //============================================================================= @@ -139,3 +139,14 @@ CORBA::Boolean StdMeshers_MaxElementArea_i::IsDimSupported( SMESH::Dimension typ return type == SMESH::DIM_2D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_MaxElementArea_i::getMethodOfParameter(const int paramIndex, + int /*nbVars*/) const +{ + return "SetMaxElementArea"; +} diff --git a/src/StdMeshers_I/StdMeshers_MaxElementArea_i.hxx b/src/StdMeshers_I/StdMeshers_MaxElementArea_i.hxx index 199c4bb4b..51e6033b9 100644 --- a/src/StdMeshers_I/StdMeshers_MaxElementArea_i.hxx +++ b/src/StdMeshers_I/StdMeshers_MaxElementArea_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxElementArea_i.hxx // Moved here from SMESH_MaxElementArea_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_MAXELEMENTAREA_I_HXX_ #define _SMESH_MAXELEMENTAREA_I_HXX_ @@ -48,7 +48,7 @@ public: // Constructor StdMeshers_MaxElementArea_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_MaxElementArea_i(); @@ -63,6 +63,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.cxx b/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.cxx index 7ee1eefdc..a9e8b657d 100644 --- a/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.cxx +++ b/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxElementVolume_i.cxx // Moved here from SMESH_MaxElementVolume_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_MaxElementVolume_i.hxx" #include "SMESH_Gen_i.hxx" @@ -47,15 +47,15 @@ using namespace std; //============================================================================= StdMeshers_MaxElementVolume_i::StdMeshers_MaxElementVolume_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_MaxElementVolume_i::StdMeshers_MaxElementVolume_i" ); myBaseImpl = new ::StdMeshers_MaxElementVolume( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -89,11 +89,11 @@ void StdMeshers_MaxElementVolume_i::SetMaxElementVolume( CORBA::Double theVolume } catch (SALOME_Exception& S_ex) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetMaxElementVolume( " << theVolume << " )"; + SMESH::TPythonDump() << _this() << ".SetMaxElementVolume( " << SMESH::TVar(theVolume) << " )"; } //============================================================================= @@ -139,3 +139,13 @@ CORBA::Boolean StdMeshers_MaxElementVolume_i::IsDimSupported( SMESH::Dimension t return type == SMESH::DIM_3D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_MaxElementVolume_i::getMethodOfParameter(const int, int) const +{ + return "SetMaxElementVolume"; +} diff --git a/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.hxx b/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.hxx index a01df5444..829097c4d 100644 --- a/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.hxx +++ b/src/StdMeshers_I/StdMeshers_MaxElementVolume_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxElementVolume_i.hxx // Moved here from SMESH_MaxElementVolume_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_MAXELEMENTVOLUME_I_HXX_ #define _SMESH_MAXELEMENTVOLUME_I_HXX_ @@ -63,6 +63,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_MaxLength_i.cxx b/src/StdMeshers_I/StdMeshers_MaxLength_i.cxx index bbed6c448..5ce5b34f9 100644 --- a/src/StdMeshers_I/StdMeshers_MaxLength_i.cxx +++ b/src/StdMeshers_I/StdMeshers_MaxLength_i.cxx @@ -1,27 +1,26 @@ -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// Copyright (C) 2007-2012 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 // -// Copyright (C) 2003 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 // + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxLength_i.cxx // Module : SMESH - +// #include "StdMeshers_MaxLength_i.hxx" #include "SMESH_Gen_i.hxx" #include "SMESH_Gen.hxx" @@ -81,11 +80,11 @@ void StdMeshers_MaxLength_i::SetLength( CORBA::Double theLength ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetLength( " << theLength << " )"; + SMESH::TPythonDump() << _this() << ".SetLength( " << SMESH::TVar(theLength) << " )"; } //============================================================================= @@ -102,7 +101,7 @@ void StdMeshers_MaxLength_i::SetUsePreestimatedLength( CORBA::Boolean toUse ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // this is an internal kitchen call - no Python dump @@ -123,7 +122,7 @@ void StdMeshers_MaxLength_i::SetPreestimatedLength( CORBA::Double theLength ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // this is an internal kitchen call - no Python dump // Update Python script @@ -202,3 +201,14 @@ CORBA::Boolean StdMeshers_MaxLength_i::IsDimSupported( SMESH::Dimension type ) { return type == SMESH::DIM_1D; } + +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_MaxLength_i::getMethodOfParameter(const int, int) const +{ + return "SetLength"; +} diff --git a/src/StdMeshers_I/StdMeshers_MaxLength_i.hxx b/src/StdMeshers_I/StdMeshers_MaxLength_i.hxx index 715da7a23..f088f7895 100644 --- a/src/StdMeshers_I/StdMeshers_MaxLength_i.hxx +++ b/src/StdMeshers_I/StdMeshers_MaxLength_i.hxx @@ -1,27 +1,26 @@ -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// Copyright (C) 2007-2012 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 // -// Copyright (C) 2003 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 // + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_MaxLength_i.hxx // Module : SMESH - +// #ifndef _SMESH_MaxLength_I_HXX_ #define _SMESH_MaxLength_I_HXX_ @@ -78,6 +77,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.cxx b/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.cxx index 59f13b891..78831a397 100644 --- a/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.cxx +++ b/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_NotConformAllowed_i.cxx // Author : Paul RASCLE, EDF diff --git a/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.hxx b/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.hxx index be9f764c2..862baae57 100644 --- a/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.hxx +++ b/src/StdMeshers_I/StdMeshers_NotConformAllowed_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_NotConformAllowed_i.hxx // Author : Paul RASCLE, EDF diff --git a/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.cxx b/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.cxx new file mode 100644 index 000000000..e4e5a1ecf --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.cxx @@ -0,0 +1,88 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes +// File : StdMeshers_NumberOfLayers2D_i.cxx +// Author : Edward AGAPOV +// Module : SMESH +// +#include "StdMeshers_NumberOfLayers2D_i.hxx" + +#include "utilities.h" + +//============================================================================= +/*! + * StdMeshers_NumberOfLayers2D_i::StdMeshers_NumberOfLayers2D_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_NumberOfLayers2D_i::StdMeshers_NumberOfLayers2D_i + (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl) +: StdMeshers_NumberOfLayers_i(thePOA,theStudyId,theGenImpl), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE("StdMeshers_NumberOfLayers2D_i::StdMeshers_NumberOfLayers2D_i"); + myBaseImpl = new ::StdMeshers_NumberOfLayers2D(theGenImpl->GetANewId(), + theStudyId, + theGenImpl); +} + +//============================================================================= +/*! + * StdMeshers_NumberOfLayers2D_i::~StdMeshers_NumberOfLayers2D_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_NumberOfLayers2D_i::~StdMeshers_NumberOfLayers2D_i() +{ + MESSAGE( "StdMeshers_NumberOfLayers2D_i::~StdMeshers_NumberOfLayers2D_i" ); +} + +//============================================================================= +/*! + * StdMeshers_NumberOfLayers2D_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_NumberOfLayers2D* StdMeshers_NumberOfLayers2D_i::GetImpl() +{ + return ( ::StdMeshers_NumberOfLayers2D* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_NumberOfLayers2D_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} diff --git a/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.hxx b/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.hxx new file mode 100644 index 000000000..ae684594d --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_NumberOfLayers2D_i.hxx @@ -0,0 +1,58 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_NumberOfLayers2D_i.hxx +// Author : Edward AGAPOV +// Module : SMESH +// +#ifndef _SMESH_NumberOfLayers2D_I_HXX_ +#define _SMESH_NumberOfLayers2D_I_HXX_ + +#include "StdMeshers_NumberOfLayers2D.hxx" +#include "StdMeshers_NumberOfLayers_i.hxx" + +// ========================================================= +/*! + * This hypothesis is used by "Radial quadrangle" algorithm. + * It specifies number of segments between the internal + * and the external surfaces. + */ +// ========================================================= + +class StdMeshers_NumberOfLayers2D_i: + public virtual POA_StdMeshers::StdMeshers_NumberOfLayers2D, + public virtual StdMeshers_NumberOfLayers_i +{ +public: + // Constructor + StdMeshers_NumberOfLayers2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_NumberOfLayers2D_i(); + + // Get implementation + ::StdMeshers_NumberOfLayers2D* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.cxx b/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.cxx index 9779cfbdb..8c12ab727 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.cxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_NumberOfLayers_i.cxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #include "StdMeshers_NumberOfLayers_i.hxx" #include "SMESH_Gen_i.hxx" @@ -89,7 +89,7 @@ void StdMeshers_NumberOfLayers_i::SetNumberOfLayers(CORBA::Long numberOfLayers) THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetNumberOfLayers( " << numberOfLayers << " )"; + SMESH::TPythonDump() << _this() << ".SetNumberOfLayers( " << SMESH::TVar(numberOfLayers) << " )"; } //============================================================================= @@ -133,3 +133,13 @@ CORBA::Boolean StdMeshers_NumberOfLayers_i::IsDimSupported( SMESH::Dimension typ return type == SMESH::DIM_3D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_NumberOfLayers_i::getMethodOfParameter(const int, int) const +{ + return "SetNumberOfLayers"; +} diff --git a/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.hxx b/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.hxx index 3d926b03b..cf8b1bcb2 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.hxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfLayers_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_NumberOfLayers_i.hxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #ifndef _SMESH_NumberOfLayers_I_HXX_ #define _SMESH_NumberOfLayers_I_HXX_ @@ -68,7 +68,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif - diff --git a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx index 7b2d215a5..d0195c9a7 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_NumberOfSegments_i.cxx // Moved here from SMESH_NumberOfSegments_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_NumberOfSegments_i.hxx" #include "SMESH_Gen_i.hxx" @@ -46,15 +46,15 @@ using namespace std; //============================================================================= StdMeshers_NumberOfSegments_i::StdMeshers_NumberOfSegments_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ) { MESSAGE( "StdMeshers_NumberOfSegments_i::StdMeshers_NumberOfSegments_i" ); myBaseImpl = new ::StdMeshers_NumberOfSegments( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= @@ -78,8 +78,8 @@ StdMeshers_NumberOfSegments_i::~StdMeshers_NumberOfSegments_i() */ //============================================================================= SMESH::double_array* StdMeshers_NumberOfSegments_i::BuildDistributionExpr( const char* func, - CORBA::Long nbSeg, - CORBA::Long conv ) + CORBA::Long nbSeg, + CORBA::Long conv ) throw ( SALOME::SALOME_Exception ) { ASSERT( myBaseImpl ); @@ -99,8 +99,8 @@ SMESH::double_array* StdMeshers_NumberOfSegments_i::BuildDistributionExpr( const } SMESH::double_array* StdMeshers_NumberOfSegments_i::BuildDistributionTab( const SMESH::double_array& func, - CORBA::Long nbSeg, - CORBA::Long conv ) + CORBA::Long nbSeg, + CORBA::Long conv ) throw ( SALOME::SALOME_Exception ) { ASSERT( myBaseImpl ); @@ -141,11 +141,11 @@ void StdMeshers_NumberOfSegments_i::SetNumberOfSegments( CORBA::Long theSegments } catch (SALOME_Exception& S_ex) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetNumberOfSegments( " << theSegmentsNumber << " )"; + SMESH::TPythonDump() << _this() << ".SetNumberOfSegments( " << SMESH::TVar(theSegmentsNumber) << " )"; } //============================================================================= @@ -162,6 +162,101 @@ CORBA::Long StdMeshers_NumberOfSegments_i::GetNumberOfSegments() return this->GetImpl()->GetNumberOfSegments(); } +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::SetReversedEdges + * + * Set edges to reverse + */ +//============================================================================= + +void StdMeshers_NumberOfSegments_i::SetReversedEdges( const SMESH::long_array& theIds ) +{ + ASSERT( myBaseImpl ); + try { + std::vector ids( theIds.length() ); + CORBA::Long iEnd = theIds.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + ids[ i ] = theIds[ i ]; + + this->GetImpl()->SetReversedEdges( ids ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetReversedEdges( " << theIds << " )"; +} + +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::SetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +void StdMeshers_NumberOfSegments_i::SetObjectEntry( const char* theEntry ) +{ + ASSERT( myBaseImpl ); + string entry(theEntry); // actually needed as theEntry is spoiled by moment of dumping + try { + this->GetImpl()->SetObjectEntry( entry.c_str() ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + // Update Python script + SMESH::TPythonDump() << _this() << ".SetObjectEntry( \"" << entry.c_str() << "\" )"; +} + +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::GetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +char* StdMeshers_NumberOfSegments_i::GetObjectEntry() +{ + ASSERT( myBaseImpl ); + + const char* entry; + try { + entry = this->GetImpl()->GetObjectEntry(); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + return CORBA::string_dup( entry ); +} + +//============================================================================= +/*! + * StdMeshers_NumberOfSegments_i::GetReversedEdges + * + * Get reversed edges + */ +//============================================================================= + +SMESH::long_array* StdMeshers_NumberOfSegments_i::GetReversedEdges() +{ + MESSAGE( "StdMeshers_NumberOfSegments_i::GetReversedEdges" ); + ASSERT( myBaseImpl ); + SMESH::long_array_var anArray = new SMESH::long_array; + std::vector ids = this->GetImpl()->GetReversedEdges(); + anArray->length( ids.size() ); + for ( CORBA::Long i = 0; i < ids.size(); i++) + anArray [ i ] = ids [ i ]; + + return anArray._retn(); +} + //============================================================================= /*! */ @@ -179,7 +274,7 @@ void StdMeshers_NumberOfSegments_i::SetDistrType(CORBA::Long typ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } @@ -209,11 +304,11 @@ void StdMeshers_NumberOfSegments_i::SetScaleFactor( CORBA::Double theScaleFactor try { this->GetImpl()->SetScaleFactor( theScaleFactor ); // Update Python script - SMESH::TPythonDump() << _this() << ".SetScaleFactor( " << theScaleFactor << " )"; + SMESH::TPythonDump() << _this() << ".SetScaleFactor( " << SMESH::TVar(theScaleFactor) << " )"; } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } @@ -235,7 +330,7 @@ CORBA::Double StdMeshers_NumberOfSegments_i::GetScaleFactor() } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } return scale; } @@ -259,7 +354,7 @@ void StdMeshers_NumberOfSegments_i::SetTableFunction(const SMESH::double_array& } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } @@ -278,7 +373,7 @@ SMESH::double_array* StdMeshers_NumberOfSegments_i::GetTableFunction() } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } SMESH::double_array_var aRes = new SMESH::double_array(); aRes->length(tbl->size()); @@ -303,7 +398,7 @@ void StdMeshers_NumberOfSegments_i::SetExpressionFunction(const char* expr) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } @@ -322,7 +417,7 @@ char* StdMeshers_NumberOfSegments_i::GetExpressionFunction() } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } return CORBA::string_dup(expr); } @@ -343,7 +438,7 @@ void StdMeshers_NumberOfSegments_i::SetConversionMode(CORBA::Long conv ) } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } } @@ -362,7 +457,7 @@ CORBA::Long StdMeshers_NumberOfSegments_i::ConversionMode() } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } return conv; } @@ -394,3 +489,13 @@ CORBA::Boolean StdMeshers_NumberOfSegments_i::IsDimSupported( SMESH::Dimension t return type == SMESH::DIM_1D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_NumberOfSegments_i::getMethodOfParameter(const int paramIndex, int ) const +{ + return paramIndex == 0 ? "SetNumberOfSegments" : "SetScaleFactor"; +} diff --git a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx index 208a59b0b..3745a736c 100644 --- a/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx +++ b/src/StdMeshers_I/StdMeshers_NumberOfSegments_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_NumberOfSegments_i.hxx // Moved here from SMESH_NumberOfSegments_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_NUMBEROFSEGMENTS_I_HXX_ #define _SMESH_NUMBEROFSEGMENTS_I_HXX_ @@ -49,7 +49,7 @@ public: // Constructor StdMeshers_NumberOfSegments_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_NumberOfSegments_i(); @@ -104,6 +104,21 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + //Set Reversed Edges + void SetReversedEdges( const SMESH::long_array& theIDs); + + //Get Reversed Edges + SMESH::long_array* GetReversedEdges(); + + //Set Object Entry + void SetObjectEntry( const char* entry); + + //Get Object Entry + char* GetObjectEntry(); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_ObjRefUlils.cxx b/src/StdMeshers_I/StdMeshers_ObjRefUlils.cxx index 53330557b..910d0aaa8 100644 --- a/src/StdMeshers_I/StdMeshers_ObjRefUlils.cxx +++ b/src/StdMeshers_I/StdMeshers_ObjRefUlils.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_ObjRefUlils.cxx // Created : Wed Oct 18 15:38:22 2006 @@ -30,6 +31,47 @@ using namespace std; +//======================================================================= +//function : GeomObjectToEntry +//purpose : Return study entry of GEOM Object +//======================================================================= + +std::string StdMeshers_ObjRefUlils::GeomObjectToEntry(GEOM::GEOM_Object_ptr& theGeomObject) +{ + if ( CORBA::is_nil( theGeomObject )) + return "NULL_OBJECT"; + + CORBA::String_var entry = theGeomObject->GetStudyEntry(); + return entry.in(); +} + +//======================================================================= +//function : EntryOrShapeToGeomObject +//purpose : Return GEOM Object by its sytudy entry or TopoDS_Shape +//======================================================================= + +GEOM::GEOM_Object_ptr +StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject (const std::string& theEntry, + const TopoDS_Shape& theShape) +{ + GEOM::GEOM_Object_var geom = GEOM::GEOM_Object::_nil(); + + // try by entry + if (SMESH_Gen_i* gen = SMESH_Gen_i::GetSMESHGen()) { + SALOMEDS::Study_var study = gen->GetCurrentStudy(); + if ( ! theEntry.empty() && ! study->_is_nil() ) { + SALOMEDS::SObject_var sobj= study->FindObjectID( theEntry.c_str() ); + CORBA::Object_var obj = gen->SObjectToObject( sobj ); + geom = GEOM::GEOM_Object::_narrow( obj ); + } + } + // try by TopoDS_Shape + if ( geom->_is_nil() ) + geom = ShapeToGeomObject( theShape ); + + return geom._retn(); +} + //================================================================================ /*! * \brief Store the shape in the stream @@ -103,3 +145,17 @@ void StdMeshers_ObjRefUlils::SaveToStream( CORBA::Object_ptr obj, if ( ! ok ) stream << " NULL_OBJECT "; } + +//======================================================================= +//function : SaveToStream +//purpose : Store the study entry of object in the stream +//======================================================================= + +void StdMeshers_ObjRefUlils::SaveToStream( const std::string& studyEntry, + std::ostream & stream) +{ + if ( studyEntry.find_first_not_of( ' ' ) == std::string::npos ) + stream << " NULL_OBJECT "; + else + stream << " " << studyEntry; +} diff --git a/src/StdMeshers_I/StdMeshers_ObjRefUlils.hxx b/src/StdMeshers_I/StdMeshers_ObjRefUlils.hxx index 4556810cf..2f13689c1 100644 --- a/src/StdMeshers_I/StdMeshers_ObjRefUlils.hxx +++ b/src/StdMeshers_I/StdMeshers_ObjRefUlils.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH : implementaion of SMESH idl descriptions // File : StdMeshers_ObjRefUlils.hxx // Created : Wed Oct 18 15:15:27 2006 @@ -64,6 +65,18 @@ public: return TopoDS_Shape(); } + /*! + * \brief Return study entry of GEOM Object + */ + static std::string GeomObjectToEntry(GEOM::GEOM_Object_ptr& theGeomObject); + + /*! + * \brief Return GEOM Object by its study entry or TopoDS_Shape + */ + static GEOM::GEOM_Object_ptr EntryOrShapeToGeomObject (const std::string& theEntry, + const TopoDS_Shape& theShape); + + /*! * \brief Store the shape in the stream * \param theShape - shape to store @@ -76,14 +89,14 @@ public: * \param stream - the stream * \retval TopoDS_Shape - resulting shape */ - static TopoDS_Shape LoadFromStream( std::istream & stream); + static TopoDS_Shape LoadFromStream( std::istream & stream ); /*! * \brief Store the CORBA object in the stream * \param obj - object to store * \param stream - the stream */ - static void SaveToStream( CORBA::Object_ptr obj, std::ostream & stream); + static void SaveToStream( CORBA::Object_ptr obj, std::ostream & stream ); /*! * \brief Retrieve a CORBA object from the stream @@ -106,6 +119,14 @@ public: } return TInterface::_nil(); } + + /*! + * \brief Store the study entry of object in the stream + * \param studyEntry - the study entry + * \param stream - the stream + */ + static void SaveToStream( const std::string& studyEntry, std::ostream & stream); + }; #endif diff --git a/src/StdMeshers_I/StdMeshers_Prism_3D_i.cxx b/src/StdMeshers_I/StdMeshers_Prism_3D_i.cxx index f6e2a3f70..4daffc4a4 100644 --- a/src/StdMeshers_I/StdMeshers_Prism_3D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Prism_3D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Prism_3D_i.cxx // Moved here from SMESH_Prism_3D_i.cxx @@ -41,8 +42,8 @@ using namespace std; //============================================================================= StdMeshers_Prism_3D_i::StdMeshers_Prism_3D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ), SMESH_Algo_i( thePOA ), @@ -75,8 +76,8 @@ StdMeshers_Prism_3D_i::~StdMeshers_Prism_3D_i() //============================================================================= StdMeshers_RadialPrism_3D_i::StdMeshers_RadialPrism_3D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ), SMESH_Algo_i( thePOA ), @@ -84,8 +85,8 @@ StdMeshers_RadialPrism_3D_i::StdMeshers_RadialPrism_3D_i( PortableServer::POA_pt { MESSAGE( "StdMeshers_RadialPrism_3D_i::StdMeshers_RadialPrism_3D_i" ); myBaseImpl = new ::StdMeshers_RadialPrism_3D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //----------------------------------------------------------------------------- diff --git a/src/StdMeshers_I/StdMeshers_Prism_3D_i.hxx b/src/StdMeshers_I/StdMeshers_Prism_3D_i.hxx index fc09a08db..c7d179c09 100644 --- a/src/StdMeshers_I/StdMeshers_Prism_3D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Prism_3D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Prism_3D_i.hxx // Moved here from SMESH_Prism_3D_i.hxx diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.cxx b/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.cxx index 62d7d7af7..092e9b451 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource1D_i.cxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #include "StdMeshers_ProjectionSource1D_i.hxx" @@ -83,6 +83,8 @@ void StdMeshers_ProjectionSource1D_i::SetSourceEdge(GEOM::GEOM_Object_ptr edge) ASSERT( myBaseImpl ); try { this->GetImpl()->SetSourceEdge( StdMeshers_ObjRefUlils::GeomObjectToShape( edge )); + CORBA::String_var entry = edge->GetStudyEntry(); + myShapeEntries[ SRC_EDGE ] = entry.in(); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -107,6 +109,9 @@ void StdMeshers_ProjectionSource1D_i::SetVertexAssociation(GEOM::GEOM_Object_ptr TopoDS_Shape v1 = StdMeshers_ObjRefUlils::GeomObjectToShape( sourceVertex ); TopoDS_Shape v2 = StdMeshers_ObjRefUlils::GeomObjectToShape( targetVertex ); this->GetImpl()->SetVertexAssociation( v1, v2 ); + + myShapeEntries[ SRC_VERTEX ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( sourceVertex ); + myShapeEntries[ TGT_VERTEX ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( targetVertex ); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -171,7 +176,9 @@ SMESH::SMESH_Mesh_ptr StdMeshers_ProjectionSource1D_i::GetSourceMesh() GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource1D_i::GetSourceEdge() { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSourceEdge() ); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ SRC_EDGE ], + this->GetImpl()->GetSourceEdge() ); } //============================================================================= @@ -184,7 +191,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource1D_i::GetSourceEdge() GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource1D_i::GetSourceVertex() { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSourceVertex() ); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ SRC_VERTEX ], + this->GetImpl()->GetSourceVertex() ); } //============================================================================= @@ -197,7 +206,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource1D_i::GetSourceVertex() GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource1D_i::GetTargetVertex() { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetTargetVertex() ); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ TGT_VERTEX ], + this->GetImpl()->GetTargetVertex() ); } //============================================================================= @@ -239,12 +250,8 @@ char* StdMeshers_ProjectionSource1D_i::SaveTo() ASSERT( myBaseImpl ); std::ostringstream os; - TopoDS_Shape s1, s2, s3; - GetImpl()->GetStoreParams( s1, s2, s3 ); - - StdMeshers_ObjRefUlils::SaveToStream( s1, os ); - StdMeshers_ObjRefUlils::SaveToStream( s2, os ); - StdMeshers_ObjRefUlils::SaveToStream( s3, os ); + for ( int i = 0; i < NB_SHAPES; ++i ) + StdMeshers_ObjRefUlils::SaveToStream( myShapeEntries[ i ], os ); StdMeshers_ObjRefUlils::SaveToStream( GetSourceMesh(), os ); myBaseImpl->SaveTo( os ); @@ -264,9 +271,9 @@ void StdMeshers_ProjectionSource1D_i::LoadFrom( const char* theStream ) ASSERT( myBaseImpl ); std::istringstream is( theStream ); - TopoDS_Shape s1 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s2 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s3 = StdMeshers_ObjRefUlils::LoadFromStream( is ); + TopoDS_Shape shapes[ NB_SHAPES ]; + for ( int i = 0; i < NB_SHAPES; ++i ) + shapes[ i ] = StdMeshers_ObjRefUlils::LoadFromStream( is ); SMESH::SMESH_Mesh_var mesh = StdMeshers_ObjRefUlils::LoadObjectFromStream< SMESH::SMESH_Mesh >( is ); @@ -280,8 +287,15 @@ void StdMeshers_ProjectionSource1D_i::LoadFrom( const char* theStream ) } myCorbaMesh = SMESH::SMESH_Mesh::_duplicate( mesh ); - GetImpl()->RestoreParams( s1, s2, s3, meshImpl ); + GetImpl()->SetSourceMesh ( meshImpl ); + GetImpl()->SetSourceEdge ( shapes[ SRC_EDGE ] ); + GetImpl()->SetVertexAssociation( shapes[ SRC_VERTEX ], + shapes[ TGT_VERTEX ]); myBaseImpl->LoadFrom( is ); + + std::istringstream str( theStream ); + for ( int i = 0; i < NB_SHAPES; ++i ) + str >> myShapeEntries[ i ]; } diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.hxx b/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.hxx index f73d75762..7326d3730 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource1D_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource1D_i.hxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #ifndef _SMESH_ProjectionSource1D_I_HXX_ #define _SMESH_ProjectionSource1D_I_HXX_ @@ -111,6 +111,11 @@ public: virtual void LoadFrom( const char* theStream ); private: + // keep entries because the same shape can be published several times with + // different names and in this case a correct name can't be restored by a TopoDS_Shape + // kept by ::StdMeshers_ProjectionSource1D + enum { SRC_EDGE=0, SRC_VERTEX, TGT_VERTEX, NB_SHAPES }; + std::string myShapeEntries[NB_SHAPES]; SMESH::SMESH_Mesh_var myCorbaMesh; }; diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.cxx b/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.cxx index 6186a7b04..2c14db459 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource2D_i.cxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #include "StdMeshers_ProjectionSource2D_i.hxx" @@ -83,6 +83,8 @@ void StdMeshers_ProjectionSource2D_i::SetSourceFace(GEOM::GEOM_Object_ptr face) ASSERT( myBaseImpl ); try { this->GetImpl()->SetSourceFace( StdMeshers_ObjRefUlils::GeomObjectToShape( face )); + CORBA::String_var entry = face->GetStudyEntry(); + myShapeEntries[ SRC_FACE ] = entry.in(); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -158,6 +160,11 @@ void StdMeshers_ProjectionSource2D_i::SetVertexAssociation(GEOM::GEOM_Object_ptr TopoDS_Shape v3 = StdMeshers_ObjRefUlils::GeomObjectToShape( targetVertex1 ); TopoDS_Shape v4 = StdMeshers_ObjRefUlils::GeomObjectToShape( targetVertex2 ); this->GetImpl()->SetVertexAssociation( v1, v2, v3, v4 ); + + myShapeEntries[ SRC_VERTEX1 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( sourceVertex1 ); + myShapeEntries[ SRC_VERTEX2 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( sourceVertex2 ); + myShapeEntries[ TGT_VERTEX1 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( targetVertex1 ); + myShapeEntries[ TGT_VERTEX2 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( targetVertex2 ); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -179,7 +186,9 @@ void StdMeshers_ProjectionSource2D_i::SetVertexAssociation(GEOM::GEOM_Object_ptr GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource2D_i::GetSourceFace() { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSourceFace() ); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ SRC_FACE ], + this->GetImpl()->GetSourceFace() ); } //============================================================================= @@ -192,7 +201,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource2D_i::GetSourceFace() GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource2D_i::GetSourceVertex(CORBA::Long i) { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSourceVertex((int) i )); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ i == 1 ? SRC_VERTEX1 : SRC_VERTEX2 ], + this->GetImpl()->GetSourceVertex((int) i )); } //============================================================================= @@ -205,7 +216,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource2D_i::GetSourceVertex(CORBA::Lo GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource2D_i::GetTargetVertex(CORBA::Long i) { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetTargetVertex( (int)i )); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ i == 1 ? TGT_VERTEX1 : TGT_VERTEX2 ], + this->GetImpl()->GetTargetVertex( (int)i )); } //============================================================================= @@ -247,14 +260,8 @@ char* StdMeshers_ProjectionSource2D_i::SaveTo() ASSERT( myBaseImpl ); std::ostringstream os; - TopoDS_Shape s1, s2, s3, s4, s5; - GetImpl()->GetStoreParams( s1, s2, s3, s4, s5 ); - - StdMeshers_ObjRefUlils::SaveToStream( s1, os ); - StdMeshers_ObjRefUlils::SaveToStream( s2, os ); - StdMeshers_ObjRefUlils::SaveToStream( s3, os ); - StdMeshers_ObjRefUlils::SaveToStream( s4, os ); - StdMeshers_ObjRefUlils::SaveToStream( s5, os ); + for ( int i = 0; i < NB_SHAPES; ++i ) + StdMeshers_ObjRefUlils::SaveToStream( myShapeEntries[ i ], os ); StdMeshers_ObjRefUlils::SaveToStream( GetSourceMesh(), os ); myBaseImpl->SaveTo( os ); @@ -274,11 +281,9 @@ void StdMeshers_ProjectionSource2D_i::LoadFrom( const char* theStream ) ASSERT( myBaseImpl ); std::istringstream is( theStream ); - TopoDS_Shape s1 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s2 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s3 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s4 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s5 = StdMeshers_ObjRefUlils::LoadFromStream( is ); + TopoDS_Shape shapes[ NB_SHAPES ]; + for ( int i = 0; i < NB_SHAPES; ++i ) + shapes[ i ] = StdMeshers_ObjRefUlils::LoadFromStream( is ); SMESH::SMESH_Mesh_var mesh = StdMeshers_ObjRefUlils::LoadObjectFromStream< SMESH::SMESH_Mesh >( is ); @@ -292,8 +297,17 @@ void StdMeshers_ProjectionSource2D_i::LoadFrom( const char* theStream ) } myCorbaMesh = SMESH::SMESH_Mesh::_duplicate( mesh ); - GetImpl()->RestoreParams( s1, s2, s3, s4, s5, meshImpl ); + GetImpl()->SetSourceMesh ( meshImpl ); + GetImpl()->SetSourceFace ( shapes[ SRC_FACE ] ); + GetImpl()->SetVertexAssociation( shapes[ SRC_VERTEX1 ], + shapes[ SRC_VERTEX2 ], + shapes[ TGT_VERTEX1 ], + shapes[ TGT_VERTEX2 ]); myBaseImpl->LoadFrom( is ); + + std::istringstream str( theStream ); + for ( int i = 0; i < NB_SHAPES; ++i ) + str >> myShapeEntries[ i ]; } diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.hxx b/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.hxx index 4aa63c7a5..454744439 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource2D_i.hxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource2D_i.hxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #ifndef _SMESH_ProjectionSource2D_I_HXX_ #define _SMESH_ProjectionSource2D_I_HXX_ @@ -93,6 +93,7 @@ public: /*! * Returns the -th source vertex associated with the -th target vertex. * Result may be nil if association not set. + * Valid indices are 1 and 2 */ GEOM::GEOM_Object_ptr GetSourceVertex(CORBA::Long i); @@ -114,8 +115,12 @@ public: virtual void LoadFrom( const char* theStream ); private: + // keep entries because the same shape can be published several times with + // different names and in this case a correct name can't be restored by a TopoDS_Shape + // kept by ::StdMeshers_ProjectionSource2D + enum { SRC_FACE=0, SRC_VERTEX1, SRC_VERTEX2, TGT_VERTEX1, TGT_VERTEX2, NB_SHAPES }; + std::string myShapeEntries[NB_SHAPES]; SMESH::SMESH_Mesh_var myCorbaMesh; }; #endif - diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.cxx b/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.cxx index 5ee44b3fc..ea2a2b31f 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.cxx @@ -1,29 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's classes // File : StdMeshers_ProjectionSource3D_i.cxx // Author : Edward AGAPOV // Module : SMESH -// $Header$ // #include "StdMeshers_ProjectionSource3D_i.hxx" @@ -83,6 +83,9 @@ void StdMeshers_ProjectionSource3D_i::SetSource3DShape(GEOM::GEOM_Object_ptr sha ASSERT( myBaseImpl ); try { this->GetImpl()->SetSource3DShape( StdMeshers_ObjRefUlils::GeomObjectToShape( shape )); + + CORBA::String_var entry = shape->GetStudyEntry(); + myShapeEntries[ SRC_SHAPE3D ] = entry.in(); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -158,6 +161,11 @@ void StdMeshers_ProjectionSource3D_i::SetVertexAssociation(GEOM::GEOM_Object_ptr TopoDS_Shape v3 = StdMeshers_ObjRefUlils::GeomObjectToShape( targetVertex1 ); TopoDS_Shape v4 = StdMeshers_ObjRefUlils::GeomObjectToShape( targetVertex2 ); this->GetImpl()->SetVertexAssociation( v1, v2, v3, v4 ); + + myShapeEntries[ SRC_VERTEX1 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( sourceVertex1 ); + myShapeEntries[ SRC_VERTEX2 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( sourceVertex2 ); + myShapeEntries[ TGT_VERTEX1 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( targetVertex1 ); + myShapeEntries[ TGT_VERTEX2 ] = StdMeshers_ObjRefUlils::GeomObjectToEntry( targetVertex2 ); } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); @@ -179,7 +187,9 @@ void StdMeshers_ProjectionSource3D_i::SetVertexAssociation(GEOM::GEOM_Object_ptr GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource3D_i::GetSource3DShape() { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSource3DShape() ); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ SRC_SHAPE3D ], + this->GetImpl()->GetSource3DShape() ); } //============================================================================= @@ -192,7 +202,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource3D_i::GetSource3DShape() GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource3D_i::GetSourceVertex(CORBA::Long i) { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetSourceVertex((int) i )); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ i == 1 ? SRC_VERTEX1 : SRC_VERTEX2 ], + this->GetImpl()->GetSourceVertex((int) i )); } //============================================================================= @@ -205,7 +217,9 @@ GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource3D_i::GetSourceVertex(CORBA::Lo GEOM::GEOM_Object_ptr StdMeshers_ProjectionSource3D_i::GetTargetVertex(CORBA::Long i) { ASSERT( myBaseImpl ); - return StdMeshers_ObjRefUlils::ShapeToGeomObject( this->GetImpl()->GetTargetVertex( (int)i )); + return StdMeshers_ObjRefUlils::EntryOrShapeToGeomObject + ( myShapeEntries[ i == 1 ? TGT_VERTEX1 : TGT_VERTEX2 ], + this->GetImpl()->GetTargetVertex( (int)i )); } //============================================================================= @@ -247,14 +261,8 @@ char* StdMeshers_ProjectionSource3D_i::SaveTo() ASSERT( myBaseImpl ); std::ostringstream os; - TopoDS_Shape s1, s2, s3, s4, s5; - GetImpl()->GetStoreParams( s1, s2, s3, s4, s5 ); - - StdMeshers_ObjRefUlils::SaveToStream( s1, os ); - StdMeshers_ObjRefUlils::SaveToStream( s2, os ); - StdMeshers_ObjRefUlils::SaveToStream( s3, os ); - StdMeshers_ObjRefUlils::SaveToStream( s4, os ); - StdMeshers_ObjRefUlils::SaveToStream( s5, os ); + for ( int i = 0; i < NB_SHAPES; ++i ) + StdMeshers_ObjRefUlils::SaveToStream( myShapeEntries[ i ], os ); StdMeshers_ObjRefUlils::SaveToStream( GetSourceMesh(), os ); myBaseImpl->SaveTo( os ); @@ -274,11 +282,9 @@ void StdMeshers_ProjectionSource3D_i::LoadFrom( const char* theStream ) ASSERT( myBaseImpl ); std::istringstream is( theStream ); - TopoDS_Shape s1 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s2 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s3 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s4 = StdMeshers_ObjRefUlils::LoadFromStream( is ); - TopoDS_Shape s5 = StdMeshers_ObjRefUlils::LoadFromStream( is ); + TopoDS_Shape shapes[ NB_SHAPES ]; + for ( int i = 0; i < NB_SHAPES; ++i ) + shapes[ i ] = StdMeshers_ObjRefUlils::LoadFromStream( is ); SMESH::SMESH_Mesh_var mesh = StdMeshers_ObjRefUlils::LoadObjectFromStream< SMESH::SMESH_Mesh >( is ); @@ -292,7 +298,18 @@ void StdMeshers_ProjectionSource3D_i::LoadFrom( const char* theStream ) } myCorbaMesh = SMESH::SMESH_Mesh::_duplicate( mesh ); - GetImpl()->RestoreParams( s1, s2, s3, s4, s5, meshImpl ); + + GetImpl()->SetSourceMesh ( meshImpl ); + GetImpl()->SetSource3DShape ( shapes[ SRC_SHAPE3D ] ); + GetImpl()->SetVertexAssociation( shapes[ SRC_VERTEX1 ], + shapes[ SRC_VERTEX2 ], + shapes[ TGT_VERTEX1 ], + shapes[ TGT_VERTEX2 ]); + myBaseImpl->LoadFrom( is ); + + std::istringstream str( theStream ); + for ( int i = 0; i < NB_SHAPES; ++i ) + str >> myShapeEntries[ i ]; } diff --git a/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.hxx b/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.hxx index d8eff8b7f..f09340130 100644 --- a/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_ProjectionSource3D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_ProjectionSource3D_i.hxx // Author : Edward AGAPOV @@ -93,6 +94,7 @@ public: /*! * Returns the -th source vertex associated with the -th target vertex. * Result may be nil if association not set. + * Valid indices are 1 and 2 */ GEOM::GEOM_Object_ptr GetSourceVertex(CORBA::Long i); @@ -114,6 +116,11 @@ public: virtual void LoadFrom( const char* theStream ); private: + // keep entries because the same shape can be published several times with + // different names and in this case a correct name can't be restored by a TopoDS_Shape + // kept by ::StdMeshers_ProjectionSource3D + enum { SRC_SHAPE3D=0, SRC_VERTEX1, SRC_VERTEX2, TGT_VERTEX1, TGT_VERTEX2, NB_SHAPES }; + std::string myShapeEntries[NB_SHAPES]; SMESH::SMESH_Mesh_var myCorbaMesh; }; diff --git a/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.cxx b/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.cxx index 185bdd6ad..ed4063315 100644 --- a/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.cxx @@ -1,30 +1,26 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses -// File : StdMeshers_Projection_3D_i.cxx -// Moved here from SMESH_Projection_3D_i.cxx -// Author : Paul RASCLE, EDF +// File : StdMeshers_Projection_1D_2D_3D_i.cxx // Module : SMESH -// $Header$ // #include "StdMeshers_Projection_1D_2D_3D_i.hxx" @@ -53,8 +49,8 @@ StdMeshers_Projection_3D_i::StdMeshers_Projection_3D_i( PortableServer::POA_ptr { MESSAGE( "StdMeshers_Projection_3D_i::StdMeshers_Projection_3D_i" ); myBaseImpl = new ::StdMeshers_Projection_3D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //----------------------------------------------------------------------------- @@ -87,8 +83,8 @@ StdMeshers_Projection_2D_i::StdMeshers_Projection_2D_i( PortableServer::POA_ptr { MESSAGE( "StdMeshers_Projection_2D_i::StdMeshers_Projection_2D_i" ); myBaseImpl = new ::StdMeshers_Projection_2D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //----------------------------------------------------------------------------- @@ -105,6 +101,40 @@ StdMeshers_Projection_2D_i::~StdMeshers_Projection_2D_i() } +//============================================================================= +/*! + * StdMeshers_Projection_1D2D_i::StdMeshers_Projection_1D2D_i + */ +//============================================================================= + +StdMeshers_Projection_1D2D_i::StdMeshers_Projection_1D2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_2D_Algo_i( thePOA ) +{ + MESSAGE( "StdMeshers_Projection_1D2D_i::StdMeshers_Projection_1D2D_i" ); + myBaseImpl = new ::StdMeshers_Projection_1D2D( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} +//----------------------------------------------------------------------------- + +StdMeshers_Projection_1D2D_i::~StdMeshers_Projection_1D2D_i() +{ + MESSAGE( "StdMeshers_Projection_1D2D_i::~StdMeshers_Projection_1D2D_i" ); +} +//----------------------------------------------------------------------------- + +::StdMeshers_Projection_1D2D* StdMeshers_Projection_1D2D_i::GetImpl() +{ + MESSAGE( "StdMeshers_Projection_1D2D_i::GetImpl" ); + return ( ::StdMeshers_Projection_1D2D* )myBaseImpl; +} + + //============================================================================= /*! * StdMeshers_Projection_1D_i::StdMeshers_Projection_1D_i @@ -121,8 +151,8 @@ StdMeshers_Projection_1D_i::StdMeshers_Projection_1D_i( PortableServer::POA_ptr { MESSAGE( "StdMeshers_Projection_1D_i::StdMeshers_Projection_1D_i" ); myBaseImpl = new ::StdMeshers_Projection_1D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //----------------------------------------------------------------------------- diff --git a/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.hxx b/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.hxx index 7fdba6588..fb17dd609 100644 --- a/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Projection_1D_2D_3D_i.hxx @@ -1,33 +1,29 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // -// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses -// File : StdMeshers_Hexa_3D_i.hxx -// Moved here from SMESH_Hexa_3D_i.hxx -// Author : Paul RASCLE, EDF +// File : StdMeshers_Projection_1D_2D_3D_i.hxx // Module : SMESH -// $Header$ // -#ifndef _SMESH_Projection_3D_I_HXX_ -#define _SMESH_Projection_3D_I_HXX_ +#ifndef _SMESH_Projection_1D2D3D_I_HXX_ +#define _SMESH_Projection_1D2D3D_I_HXX_ #include #include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) @@ -36,6 +32,7 @@ #include "SMESH_2D_Algo_i.hxx" #include "SMESH_3D_Algo_i.hxx" #include "StdMeshers_Projection_1D.hxx" +#include "StdMeshers_Projection_1D2D.hxx" #include "StdMeshers_Projection_2D.hxx" #include "StdMeshers_Projection_3D.hxx" @@ -63,7 +60,7 @@ public: }; // ====================================================== -// Projection 3D algorithm +// Projection 2D algorithm // ====================================================== class StdMeshers_Projection_2D_i: @@ -84,7 +81,28 @@ public: }; // ====================================================== -// Projection 3D algorithm +// Projection 1D-2D algorithm +// ====================================================== + +class StdMeshers_Projection_1D2D_i: + public virtual POA_StdMeshers::StdMeshers_Projection_1D2D, + public virtual SMESH_2D_Algo_i +{ +public: + // Constructor + StdMeshers_Projection_1D2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + + // Destructor + virtual ~StdMeshers_Projection_1D2D_i(); + + // Get implementation + ::StdMeshers_Projection_1D2D* GetImpl(); +}; + +// ====================================================== +// Projection 1D algorithm // ====================================================== class StdMeshers_Projection_1D_i: diff --git a/src/StdMeshers_I/StdMeshers_Propagation_i.cxx b/src/StdMeshers_I/StdMeshers_Propagation_i.cxx index 707e41600..a288d1c3b 100644 --- a/src/StdMeshers_I/StdMeshers_Propagation_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Propagation_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Propagation_i.cxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_Propagation_i.hxx b/src/StdMeshers_I/StdMeshers_Propagation_i.hxx index 77f863175..a79465a3e 100644 --- a/src/StdMeshers_I/StdMeshers_Propagation_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Propagation_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Propagation_i.hxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.cxx b/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.cxx new file mode 100644 index 000000000..4d72e8de1 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.cxx @@ -0,0 +1,245 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_QuadrangleParams_i.cxx +// Author : Sergey KUUL, OCC +// Module : SMESH + +#include "StdMeshers_QuadrangleParams_i.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_Gen.hxx" +#include "SMESH_PythonDump.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::StdMeshers_QuadrangleParams_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_QuadrangleParams_i::StdMeshers_QuadrangleParams_i + (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::StdMeshers_QuadrangleParams_i" ); + myBaseImpl = new ::StdMeshers_QuadrangleParams(theGenImpl->GetANewId(), + theStudyId, + theGenImpl); +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::~StdMeshers_QuadrangleParams_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_QuadrangleParams_i::~StdMeshers_QuadrangleParams_i() +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::~StdMeshers_QuadrangleParams_i" ); +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::SetTriaVertex + * + * Set base vertex for triangles + */ +//============================================================================= + +void StdMeshers_QuadrangleParams_i::SetTriaVertex(CORBA::Long vertID) +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::SetTriaVertex" ); + ASSERT( myBaseImpl ); + try { + this->GetImpl()->SetTriaVertex( vertID ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetTriaVertex( " + << vertID << " )"; +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::GetTriaVertex + * + * Get base vertex for triangles + */ +//============================================================================= + +CORBA::Long StdMeshers_QuadrangleParams_i::GetTriaVertex() +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::GetTriaVertex" ); + ASSERT( myBaseImpl ); + return this->GetImpl()->GetTriaVertex(); +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::SetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +void StdMeshers_QuadrangleParams_i::SetObjectEntry( const char* entry ) +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::SetObjectEntry" ); + ASSERT( myBaseImpl ); + + try { + this->GetImpl()->SetObjectEntry( entry ); + // Update Python script + // SMESH::TPythonDump() << _this() << ".SetObjectEntry( '" << entry << "' )"; + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::GetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +char* StdMeshers_QuadrangleParams_i::GetObjectEntry() +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::SetObjectEntry" ); + ASSERT( myBaseImpl ); + const char* entry; + try { + entry = this->GetImpl()->GetObjectEntry(); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + return CORBA::string_dup( entry ); +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::SetQuadType + * + * Set the type of quadrangulation + */ +//============================================================================= +void StdMeshers_QuadrangleParams_i::SetQuadType(StdMeshers::QuadType type) +{ + //static char* quadTypes[5] = {"StdMeshers.QUAD_STANDARD", + // "StdMeshers.QUAD_TRIANGLE_PREF", + // "StdMeshers.QUAD_QUADRANGLE_PREF", + // "StdMeshers.QUAD_QUADRANGLE_PREF_REVERSED", + // "StdMeshers.QUAD_REDUCED"}; + + MESSAGE("StdMeshers_QuadrangleParams_i::SetQuadType"); + ASSERT(myBaseImpl); + + if (int(type) >= int(StdMeshers::QUAD_NB_TYPES)) { + THROW_SALOME_CORBA_EXCEPTION("Bad type of quadrangulation", SALOME::BAD_PARAM); + } + + try { + this->GetImpl()->SetQuadType(StdMeshers_QuadType(int(type))); + } + catch (SALOME_Exception& S_ex) { + THROW_SALOME_CORBA_EXCEPTION(S_ex.what(), SALOME::BAD_PARAM); + } + + // Update Python script + const char* quadType; + switch (type) { + case StdMeshers::QUAD_STANDARD: + quadType = "StdMeshers.QUAD_STANDARD"; break; + case StdMeshers::QUAD_TRIANGLE_PREF: + quadType = "StdMeshers.QUAD_TRIANGLE_PREF"; break; + case StdMeshers::QUAD_QUADRANGLE_PREF: + quadType = "StdMeshers.QUAD_QUADRANGLE_PREF"; break; + case StdMeshers::QUAD_QUADRANGLE_PREF_REVERSED: + quadType = "StdMeshers.QUAD_QUADRANGLE_PREF_REVERSED"; break; + case StdMeshers::QUAD_REDUCED: + quadType = "StdMeshers.QUAD_REDUCED"; break; + default: + quadType = "UNKNOWN"; + } + SMESH::TPythonDump() << _this() << ".SetQuadType( " << quadType << " )"; + //SMESH::TPythonDump() << _this() << ".SetQuadType( " << quadTypes[int(type)] << " )"; +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::GetQuadType + * + * Get the type of quadrangulation + */ +//============================================================================= +StdMeshers::QuadType StdMeshers_QuadrangleParams_i::GetQuadType() +{ + MESSAGE("StdMeshers_QuadrangleParams_i::GetQuadType"); + ASSERT(myBaseImpl); + return StdMeshers::QuadType(int(this->GetImpl()->GetQuadType())); +} + +//============================================================================= +/*! + * StdMeshers_QuadrangleParams_i::GetImpl + * + * Get implementation + */ +//============================================================================= + +::StdMeshers_QuadrangleParams* StdMeshers_QuadrangleParams_i::GetImpl() +{ + MESSAGE( "StdMeshers_QuadrangleParams_i::GetImpl" ); + return ( ::StdMeshers_QuadrangleParams* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_QuadrangleParams_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_2D; +} diff --git a/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.hxx b/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.hxx new file mode 100644 index 000000000..7558ea36c --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_QuadrangleParams_i.hxx @@ -0,0 +1,82 @@ +// Copyright (C) 2007-2012 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 +// +// File : StdMeshers_QuadrangleParams_i.hxx +// Author : Sergey KUUL, OCC +// Module : SMESH +// $Header$ + +#ifndef _SMESH_QUADRANGLEPARAMS_I_HXX_ +#define _SMESH_QUADRANGLEPARAMS_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_QuadrangleParams.hxx" + +// ====================================================== +// QuadrangleParams hypothesis +// ====================================================== +class STDMESHERS_I_EXPORT StdMeshers_QuadrangleParams_i: + public virtual POA_StdMeshers::StdMeshers_QuadrangleParams, + public virtual SMESH_Hypothesis_i +{ +public: + // Constructor + StdMeshers_QuadrangleParams_i (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl); + // Destructor + virtual ~StdMeshers_QuadrangleParams_i(); + + // Set length + //void SetLength( CORBA::Double theLength, CORBA::Boolean theIsStart ) + // throw ( SALOME::SALOME_Exception ); + + // Get length + //CORBA::Double GetLength(CORBA::Boolean theIsStart); + + // Set base vertex for triangles + void SetTriaVertex (CORBA::Long vertID); + + // Get base vertex for triangles + CORBA::Long GetTriaVertex(); + + // Set the Entry of the Object + void SetObjectEntry (const char* theEntry); + + // Get Object Entry + char* GetObjectEntry(); + + // Set the type of quadrangulation + void SetQuadType (StdMeshers::QuadType type); + + // Get the type of quadrangulation + StdMeshers::QuadType GetQuadType(); + + // Get implementation + ::StdMeshers_QuadrangleParams* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.cxx b/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.cxx index c199fcebd..57a053d98 100644 --- a/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.cxx +++ b/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_QuadranglePreference_i.cxx // Moved here from SMESH_LocalLength_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_QuadranglePreference_i.hxx" #include "SMESH_Gen_i.hxx" diff --git a/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.hxx b/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.hxx index fc63462a9..b0d2bc712 100644 --- a/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.hxx +++ b/src/StdMeshers_I/StdMeshers_QuadranglePreference_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_QuadranglePreference_i.hxx // Moved here from SMESH_LocalLength_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_QuadranglePreference_I_HXX_ #define _SMESH_QuadranglePreference_I_HXX_ diff --git a/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.cxx b/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.cxx index 7b0ce1e63..24d44ac6a 100644 --- a/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Quadrangle_2D_i.cxx // Moved here from SMESH_Quadrangle_2D_i.cxx @@ -43,8 +44,8 @@ using namespace std; //============================================================================= StdMeshers_Quadrangle_2D_i::StdMeshers_Quadrangle_2D_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ) + int theStudyId, + ::SMESH_Gen* theGenImpl ) : SALOME::GenericObj_i( thePOA ), SMESH_Hypothesis_i( thePOA ), SMESH_Algo_i( thePOA ), @@ -52,8 +53,8 @@ StdMeshers_Quadrangle_2D_i::StdMeshers_Quadrangle_2D_i( PortableServer::POA_ptr { MESSAGE( "StdMeshers_Quadrangle_2D_i::StdMeshers_Quadrangle_2D_i" ); myBaseImpl = new ::StdMeshers_Quadrangle_2D( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); + theStudyId, + theGenImpl ); } //============================================================================= diff --git a/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.hxx b/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.hxx index abe6ce46a..2dfcce71d 100644 --- a/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Quadrangle_2D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Quadrangle_2D_i.hxx // Moved here from SMESH_Quadrangle_2D_i.hxx @@ -50,7 +51,7 @@ public: // Constructor StdMeshers_Quadrangle_2D_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_Quadrangle_2D_i(); diff --git a/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.cxx b/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.cxx index 46052eb94..71553993f 100644 --- a/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.cxx +++ b/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_QuadraticMesh_i.cxx // Moved here from SMESH_LocalLength_i.cxx diff --git a/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.hxx b/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.hxx index ecd7baaa3..522f69644 100644 --- a/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.hxx +++ b/src/StdMeshers_I/StdMeshers_QuadraticMesh_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_QuadraticMesh_i.hxx // Moved here from SMESH_LocalLength_i.hxx diff --git a/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.cxx b/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.cxx new file mode 100644 index 000000000..202faa256 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.cxx @@ -0,0 +1,68 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_RadialQuadrangle_1D2D_i.cxx +// Module : SMESH +// +#include "StdMeshers_RadialQuadrangle_1D2D_i.hxx" +#include "SMESH_Gen.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +using namespace std; + + +//============================================================================= +/*! + * StdMeshers_RadialQuadrangle_1D2D_i::StdMeshers_RadialQuadrangle_1D2D_i + */ +//============================================================================= + +StdMeshers_RadialQuadrangle_1D2D_i::StdMeshers_RadialQuadrangle_1D2D_i + (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ), + SMESH_Algo_i( thePOA ), + SMESH_2D_Algo_i( thePOA ) +{ + MESSAGE( "StdMeshers_RadialQuadrangle_1D2D_i::StdMeshers_RadialQuadrangle_1D2D_i" ); + myBaseImpl = new ::StdMeshers_RadialQuadrangle_1D2D(theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//----------------------------------------------------------------------------- + +StdMeshers_RadialQuadrangle_1D2D_i::~StdMeshers_RadialQuadrangle_1D2D_i() +{ + MESSAGE( "StdMeshers_RadialQuadrangle_1D2D_i::~StdMeshers_RadialQuadrangle_1D2D_i" ); +} + +//----------------------------------------------------------------------------- + +::StdMeshers_RadialQuadrangle_1D2D* StdMeshers_RadialQuadrangle_1D2D_i::GetImpl() +{ + MESSAGE( "StdMeshers_RadialQuadrangle_1D2D_i::GetImpl" ); + return ( ::StdMeshers_RadialQuadrangle_1D2D* )myBaseImpl; +} + diff --git a/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.hxx b/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.hxx new file mode 100644 index 000000000..ae5b731cb --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_RadialQuadrangle_1D2D_i.hxx @@ -0,0 +1,53 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_RadialQuadrangle_1D2D_i.hxx +// Module : SMESH +// +#ifndef _SMESH_RadialQuadrangle_1D2D_I_HXX_ +#define _SMESH_RadialQuadrangle_1D2D_I_HXX_ + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_2D_Algo_i.hxx" +#include "StdMeshers_RadialQuadrangle_1D2D.hxx" + +class SMESH_Gen; + +class StdMeshers_RadialQuadrangle_1D2D_i: + public virtual POA_StdMeshers::StdMeshers_RadialQuadrangle_1D2D, + public virtual SMESH_2D_Algo_i +{ +public: + // Constructor + StdMeshers_RadialQuadrangle_1D2D_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + + // Destructor + virtual ~StdMeshers_RadialQuadrangle_1D2D_i(); + + // Get implementation + ::StdMeshers_RadialQuadrangle_1D2D* GetImpl(); +}; + + +#endif diff --git a/src/StdMeshers_I/StdMeshers_Regular_1D_i.cxx b/src/StdMeshers_I/StdMeshers_Regular_1D_i.cxx index 1ac655bda..a9b8cd8ac 100644 --- a/src/StdMeshers_I/StdMeshers_Regular_1D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_Regular_1D_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Regular_1D_i.cxx // Moved here from SMESH_Regular_1D_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_Regular_1D_i.hxx" #include "SMESH_Gen.hxx" diff --git a/src/StdMeshers_I/StdMeshers_Regular_1D_i.hxx b/src/StdMeshers_I/StdMeshers_Regular_1D_i.hxx index 2c3e89ba1..ea299fc98 100644 --- a/src/StdMeshers_I/StdMeshers_Regular_1D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_Regular_1D_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Regular_1D_i.hxx // Moved here from SMESH_Regular_1D_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_REGULAR_1D_I_HXX_ #define _SMESH_REGULAR_1D_I_HXX_ @@ -48,7 +48,7 @@ public: // Constructor StdMeshers_Regular_1D_i( PortableServer::POA_ptr thePOA, int theStudyId, - ::SMESH_Gen* theGenImpl ); + ::SMESH_Gen* theGenImpl ); // Destructor virtual ~StdMeshers_Regular_1D_i(); diff --git a/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.cxx b/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.cxx index fb81f9e9f..0836c36e4 100644 --- a/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.cxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Projection_3D_i.cxx // Moved here from SMESH_Projection_3D_i.cxx diff --git a/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.hxx b/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.hxx index 526bfca8f..64d559097 100644 --- a/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_SegmentAroundVertex_0D_i.hxx @@ -1,24 +1,25 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_SegmentAroundVertex_0D.hxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.cxx b/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.cxx index 9ee69e626..7eba3661c 100644 --- a/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.cxx +++ b/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.cxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_SegmentLengthAroundVertex_i.cxx // Module : SMESH -// $Header$ // #include "StdMeshers_SegmentLengthAroundVertex_i.hxx" #include "SMESH_Gen.hxx" @@ -90,7 +90,7 @@ void StdMeshers_SegmentLengthAroundVertex_i::SetLength( CORBA::Double theLength } // Update Python script - SMESH::TPythonDump() << _this() << ".SetLength( " << theLength << " )"; + SMESH::TPythonDump() << _this() << ".SetLength( " << SMESH::TVar(theLength) << " )"; } //============================================================================= @@ -137,3 +137,13 @@ CORBA::Boolean StdMeshers_SegmentLengthAroundVertex_i::IsDimSupported( SMESH::Di return type == SMESH::DIM_1D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_SegmentLengthAroundVertex_i::getMethodOfParameter(const int, int ) const +{ + return "SetLength"; +} diff --git a/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.hxx b/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.hxx index 2add0396a..220a7e733 100644 --- a/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.hxx +++ b/src/StdMeshers_I/StdMeshers_SegmentLengthAroundVertex_i.hxx @@ -1,28 +1,28 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_SegmentLengthAroundVertex_i.hxx // Module : SMESH -// $Header$ // #ifndef _SMESH_SegmentLengthAroundVertex_I_HXX_ #define _SMESH_SegmentLengthAroundVertex_I_HXX_ @@ -63,6 +63,9 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif diff --git a/src/StdMeshers_I/StdMeshers_StartEndLength_i.cxx b/src/StdMeshers_I/StdMeshers_StartEndLength_i.cxx index 1154b63f2..61b855a1d 100644 --- a/src/StdMeshers_I/StdMeshers_StartEndLength_i.cxx +++ b/src/StdMeshers_I/StdMeshers_StartEndLength_i.cxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_StartEndLength_i.cxx // Moved here from SMESH_LocalLength_i.cxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #include "StdMeshers_StartEndLength_i.hxx" #include "SMESH_Gen_i.hxx" @@ -90,12 +90,37 @@ void StdMeshers_StartEndLength_i::SetLength(CORBA::Double theLength, } catch ( SALOME_Exception& S_ex ) { THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), - SALOME::BAD_PARAM ); + SALOME::BAD_PARAM ); } // Update Python script - SMESH::TPythonDump() << _this() << ".SetLength( " - << theLength << ", " << theIsStart << " )"; + SMESH::TPythonDump() << + _this() << ( theIsStart ? ".SetStartLength( " : ".SetEndLength( " ) << + SMESH::TVar(theLength) << " )"; +} + +//============================================================================= +/*! + * Sets parameter value + */ +//============================================================================= + +void StdMeshers_StartEndLength_i::SetStartLength( CORBA::Double length) + throw (SALOME::SALOME_Exception) +{ + SetLength( length, true ); +} + +//============================================================================= +/*! + * Sets parameter value + */ +//============================================================================= + +void StdMeshers_StartEndLength_i::SetEndLength( CORBA::Double length) + throw (SALOME::SALOME_Exception) +{ + SetLength( length, false ); } //============================================================================= @@ -113,6 +138,97 @@ CORBA::Double StdMeshers_StartEndLength_i::GetLength( CORBA::Boolean theIsStart) return this->GetImpl()->GetLength( theIsStart ); } +//============================================================================= +/*! + * StdMeshers_StartEndLength_i::SetReversedEdges + * + * Set edges to reverse + */ +//============================================================================= + +void StdMeshers_StartEndLength_i::SetReversedEdges( const SMESH::long_array& theIds ) +{ + ASSERT( myBaseImpl ); + try { + std::vector ids( theIds.length() ); + CORBA::Long iEnd = theIds.length(); + for ( CORBA::Long i = 0; i < iEnd; i++ ) + ids[ i ] = theIds[ i ]; + + this->GetImpl()->SetReversedEdges( ids ); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), + SALOME::BAD_PARAM ); + } + + // Update Python script + SMESH::TPythonDump() << _this() << ".SetReversedEdges( " << theIds << " )"; +} + +//============================================================================= +/*! + * StdMeshers_StartEndLength_i::SetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +void StdMeshers_StartEndLength_i::SetObjectEntry( const char* theEntry ) +{ + ASSERT( myBaseImpl ); + string entry(theEntry); // actually needed as theEntry is spoiled by moment of dumping + try { + this->GetImpl()->SetObjectEntry( entry.c_str() ); + // Update Python script + SMESH::TPythonDump() << _this() << ".SetObjectEntry( '" << entry.c_str() << "' )"; + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(),SALOME::BAD_PARAM ); + } +} + +//============================================================================= +/*! + * StdMeshers_StartEndLength_i::GetObjectEntry + * + * Set the Entry for the Main Object + */ +//============================================================================= + +char* StdMeshers_StartEndLength_i::GetObjectEntry() +{ + ASSERT( myBaseImpl ); + const char* entry; + try { + entry = this->GetImpl()->GetObjectEntry(); + } + catch ( SALOME_Exception& S_ex ) { + THROW_SALOME_CORBA_EXCEPTION( S_ex.what(), SALOME::BAD_PARAM ); + } + return CORBA::string_dup( entry ); +} + +//============================================================================= +/*! + * StdMeshers_StartEndLength_i::GetReversedEdges + * + * Get reversed edges + */ +//============================================================================= + +SMESH::long_array* StdMeshers_StartEndLength_i::GetReversedEdges() +{ + ASSERT( myBaseImpl ); + SMESH::long_array_var anArray = new SMESH::long_array; + std::vector ids = this->GetImpl()->GetReversedEdges(); + anArray->length( ids.size() ); + for ( CORBA::Long i = 0; i < ids.size(); i++) + anArray [ i ] = ids [ i ]; + + return anArray._retn(); +} + //============================================================================= /*! * StdMeshers_StartEndLength_i::GetImpl @@ -141,3 +257,14 @@ CORBA::Boolean StdMeshers_StartEndLength_i::IsDimSupported( SMESH::Dimension typ return type == SMESH::DIM_1D; } +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_StartEndLength_i::getMethodOfParameter(const int paramIndex, + int /*nbVars*/) const +{ + return paramIndex == 0 ? "SetStartLength" : "SetEndLength"; +} diff --git a/src/StdMeshers_I/StdMeshers_StartEndLength_i.hxx b/src/StdMeshers_I/StdMeshers_StartEndLength_i.hxx index d7a4c4bcd..7eeb70e17 100644 --- a/src/StdMeshers_I/StdMeshers_StartEndLength_i.hxx +++ b/src/StdMeshers_I/StdMeshers_StartEndLength_i.hxx @@ -1,30 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_StartEndLength_i.hxx // Moved here from SMESH_LocalLength_i.hxx // Author : Paul RASCLE, EDF // Module : SMESH -// $Header$ // #ifndef _SMESH_StartEndLength_I_HXX_ #define _SMESH_StartEndLength_I_HXX_ @@ -55,8 +55,16 @@ public: virtual ~StdMeshers_StartEndLength_i(); // Set length + // * OBSOLETE *. Avoid such a way of interface design void SetLength( CORBA::Double theLength, CORBA::Boolean theIsStart ) throw ( SALOME::SALOME_Exception ); + + // Sets parameter value + void SetStartLength( CORBA::Double length) throw (SALOME::SALOME_Exception); + + // Sets parameter value + void SetEndLength( CORBA::Double length) throw (SALOME::SALOME_Exception); + // Get length CORBA::Double GetLength(CORBA::Boolean theIsStart); @@ -65,7 +73,21 @@ public: // Verify whether hypothesis supports given entity type CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + //Set Reversed Edges + void SetReversedEdges( const SMESH::long_array& theIDs); + + //Get Reversed Edges + SMESH::long_array* GetReversedEdges(); + + //Set Object Entry + void SetObjectEntry( const char* entry); + + //Get Object Entry + char* GetObjectEntry(); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; }; #endif - diff --git a/src/StdMeshers_I/StdMeshers_TrianglePreference_i.cxx b/src/StdMeshers_I/StdMeshers_TrianglePreference_i.cxx deleted file mode 100644 index 330216d89..000000000 --- a/src/StdMeshers_I/StdMeshers_TrianglePreference_i.cxx +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File : StdMeshers_TrianglePreference_i.cxx -// Author : -// Module : SMESH -// -#include "StdMeshers_TrianglePreference_i.hxx" -#include "SMESH_Gen_i.hxx" -#include "SMESH_Gen.hxx" - -#include "Utils_CorbaException.hxx" -#include "utilities.h" - -#include - -using namespace std; - -//============================================================================= -/*! - * StdMeshers_TrianglePreference_i::StdMeshers_TrianglePreference_i - * - * Constructor - */ -//============================================================================= - -StdMeshers_TrianglePreference_i::StdMeshers_TrianglePreference_i -( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ): SALOME::GenericObj_i( thePOA ), - SMESH_Hypothesis_i( thePOA ) -{ - myBaseImpl = new ::StdMeshers_TrianglePreference( theGenImpl->GetANewId(), - theStudyId, - theGenImpl ); -} - -//============================================================================= -/*! - * StdMeshers_TrianglePreference_i::~StdMeshers_TrianglePreference_i - * - * Destructor - */ -//============================================================================= - -StdMeshers_TrianglePreference_i::~StdMeshers_TrianglePreference_i() -{ -} - -//============================================================================= -/*! - * StdMeshers_TrianglePreference_i::GetImpl - * - * Get implementation - */ -//============================================================================= - -::StdMeshers_TrianglePreference* StdMeshers_TrianglePreference_i::GetImpl() -{ - return ( ::StdMeshers_TrianglePreference* )myBaseImpl; -} - -//================================================================================ -/*! - * \brief Verify whether hypothesis supports given entity type - * \param type - dimension (see SMESH::Dimension enumeration) - * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise - * - * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) - */ -//================================================================================ - -CORBA::Boolean StdMeshers_TrianglePreference_i::IsDimSupported( SMESH::Dimension type ) -{ - return type == SMESH::DIM_2D; -} - diff --git a/src/StdMeshers_I/StdMeshers_TrianglePreference_i.hxx b/src/StdMeshers_I/StdMeshers_TrianglePreference_i.hxx deleted file mode 100644 index ce4826305..000000000 --- a/src/StdMeshers_I/StdMeshers_TrianglePreference_i.hxx +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2007-2008 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 -// -// File : StdMeshers_TrianglePreference_i.hxx -// Author : -// Module : SMESH -// -#ifndef _SMESH_TrianglePreference_I_HXX_ -#define _SMESH_TrianglePreference_I_HXX_ - -#include "SMESH_StdMeshers_I.hxx" - -#include -#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) - -#include "SMESH_Hypothesis_i.hxx" -#include "StdMeshers_TrianglePreference.hxx" - -class SMESH_Gen; - -class STDMESHERS_I_EXPORT StdMeshers_TrianglePreference_i: - public virtual POA_StdMeshers::StdMeshers_TrianglePreference, - public virtual SMESH_Hypothesis_i -{ -public: - // Constructor - StdMeshers_TrianglePreference_i( PortableServer::POA_ptr thePOA, - int theStudyId, - ::SMESH_Gen* theGenImpl ); - // Destructor - virtual ~StdMeshers_TrianglePreference_i(); - - // Get implementation - ::StdMeshers_TrianglePreference* GetImpl(); - - // Verify whether hypothesis supports given entity type - CORBA::Boolean IsDimSupported( SMESH::Dimension type ); -}; - -#endif //_SMESH_TrianglePreference_I_HXX_ diff --git a/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.cxx b/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.cxx index 63814d218..1ca4f2038 100644 --- a/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.cxx +++ b/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.cxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_Projection_3D_i.cxx // Moved here from SMESH_Projection_3D_i.cxx diff --git a/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.hxx b/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.hxx index 32035e3c1..77f019fbf 100644 --- a/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.hxx +++ b/src/StdMeshers_I/StdMeshers_UseExisting_1D2D_i.hxx @@ -1,24 +1,22 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 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. // -// 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 // -// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses // File : StdMeshers_UseExisting_1D2D.hxx // Module : SMESH diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx new file mode 100644 index 000000000..55ca30d75 --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.cxx @@ -0,0 +1,231 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ViscousLayers_i.cxx +// Module : SMESH +// +#include "StdMeshers_ViscousLayers_i.hxx" + +#include "SMESH_Gen.hxx" +#include "SMESH_Gen_i.hxx" +#include "SMESH_PythonDump.hxx" + +#include "Utils_CorbaException.hxx" +#include "utilities.h" + +#include + +#include CORBA_SERVER_HEADER(SMESH_Group) + +using namespace std; + +//============================================================================= +/*! + * StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i + * + * Constructor + */ +//============================================================================= + +StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ) + : SALOME::GenericObj_i( thePOA ), + SMESH_Hypothesis_i( thePOA ) +{ + MESSAGE( "StdMeshers_ViscousLayers_i::StdMeshers_ViscousLayers_i" ); + myBaseImpl = new ::StdMeshers_ViscousLayers( theGenImpl->GetANewId(), + theStudyId, + theGenImpl ); +} + +//============================================================================= +/*! + * StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i + * + * Destructor + */ +//============================================================================= + +StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i() +{ + MESSAGE( "StdMeshers_ViscousLayers_i::~StdMeshers_ViscousLayers_i" ); +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +void StdMeshers_ViscousLayers_i::SetIgnoreFaces(const ::SMESH::long_array& faceIDs) +throw ( SALOME::SALOME_Exception ) +{ + vector ids( faceIDs.length() ); + for ( unsigned i = 0; i < ids.size(); ++i ) + if (( ids[i] = faceIDs[i] ) < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid face id", SALOME::BAD_PARAM ); + GetImpl()->SetIgnoreFaces( ids ); + SMESH::TPythonDump() << _this() << ".SetIgnoreFaces( " << faceIDs << " )"; +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +SMESH::long_array* StdMeshers_ViscousLayers_i::GetIgnoreFaces() +{ + vector idsVec = GetImpl()->GetIgnoreFaces(); + SMESH::long_array_var ids = new SMESH::long_array; + ids->length( idsVec.size() ); + for ( unsigned i = 0; i < idsVec.size(); ++i ) + ids[i] = idsVec[i]; + return ids._retn(); +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +void StdMeshers_ViscousLayers_i::SetTotalThickness(::CORBA::Double thickness) +throw ( SALOME::SALOME_Exception ) +{ + if ( thickness < 1e-100 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid thickness", SALOME::BAD_PARAM ); + GetImpl()->SetTotalThickness(thickness); + SMESH::TPythonDump() << _this() << ".SetTotalThickness( " << SMESH::TVar(thickness) << " )"; +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +::CORBA::Double StdMeshers_ViscousLayers_i::GetTotalThickness() +{ + return GetImpl()->GetTotalThickness(); +} + +//================================================================================ +/*! + * \brief + * \param nb - + */ +//================================================================================ + +void StdMeshers_ViscousLayers_i::SetNumberLayers(::CORBA::Short nb) +throw ( SALOME::SALOME_Exception ) +{ + if ( nb < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid number of layers", SALOME::BAD_PARAM ); + GetImpl()->SetNumberLayers( nb ); + SMESH::TPythonDump() << _this() << ".SetNumberLayers( " << SMESH::TVar(nb) << " )"; +} + +//================================================================================ +/*! + * \brief + */ +//================================================================================ + +::CORBA::Short StdMeshers_ViscousLayers_i::GetNumberLayers() +{ + return CORBA::Short( GetImpl()->GetNumberLayers() ); +} + +//================================================================================ +/*! + * \brief + * \param factor - + */ +//================================================================================ + +void StdMeshers_ViscousLayers_i::SetStretchFactor(::CORBA::Double factor) +throw ( SALOME::SALOME_Exception ) +{ + if ( factor < 1 ) + THROW_SALOME_CORBA_EXCEPTION( "Invalid stretch factor, it must be >= 1.0", SALOME::BAD_PARAM ); + GetImpl()->SetStretchFactor(factor); + SMESH::TPythonDump() << _this() << ".SetStretchFactor( " << SMESH::TVar(factor) << " )"; +} + +//================================================================================ +/*! + * \brief + * + */ +//================================================================================ + +::CORBA::Double StdMeshers_ViscousLayers_i::GetStretchFactor() +{ + return GetImpl()->GetStretchFactor(); +} + +//============================================================================= +/*! + * Get implementation + */ +//============================================================================= + +::StdMeshers_ViscousLayers* StdMeshers_ViscousLayers_i::GetImpl() +{ + MESSAGE( "StdMeshers_ViscousLayers_i::GetImpl" ); + return ( ::StdMeshers_ViscousLayers* )myBaseImpl; +} + +//================================================================================ +/*! + * \brief Verify whether hypothesis supports given entity type + * \param type - dimension (see SMESH::Dimension enumeration) + * \retval CORBA::Boolean - TRUE if dimension is supported, FALSE otherwise + * + * Verify whether hypothesis supports given entity type (see SMESH::Dimension enumeration) + */ +//================================================================================ +CORBA::Boolean StdMeshers_ViscousLayers_i::IsDimSupported( SMESH::Dimension type ) +{ + return type == SMESH::DIM_3D; +} + +//================================================================================ +/*! + * \brief Return method name corresponding to index of variable parameter + */ +//================================================================================ + +std::string StdMeshers_ViscousLayers_i::getMethodOfParameter(const int paramIndex, int ) const +{ + // order of methods was defined by StdMeshersGUI_StdHypothesisCreator::storeParams() + switch ( paramIndex ) + { + case 0: return "SetTotalThickness"; + case 1: return "SetNumberLayers"; + case 2: return "SetStretchFactor"; + } + return ""; +} diff --git a/src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx new file mode 100644 index 000000000..e4d5d6e2e --- /dev/null +++ b/src/StdMeshers_I/StdMeshers_ViscousLayers_i.hxx @@ -0,0 +1,74 @@ +// Copyright (C) 2007-2012 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 +// + +// SMESH SMESH_I : idl implementation based on 'SMESH' unit's calsses +// File : StdMeshers_ViscousLayers_i.hxx +// Module : SMESH +// +#ifndef _SMESH_ViscousLayers_I_HXX_ +#define _SMESH_ViscousLayers_I_HXX_ + +#include "SMESH_StdMeshers_I.hxx" + +#include +#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis) + +#include "SMESH_Hypothesis_i.hxx" +#include "StdMeshers_ViscousLayers.hxx" + +class SMESH_Gen; + +class STDMESHERS_I_EXPORT StdMeshers_ViscousLayers_i: + public virtual POA_StdMeshers::StdMeshers_ViscousLayers, + public virtual SMESH_Hypothesis_i +{ + public: + // Constructor + StdMeshers_ViscousLayers_i( PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl ); + // Destructor + virtual ~StdMeshers_ViscousLayers_i(); + + void SetIgnoreFaces(const ::SMESH::long_array& faceIDs) throw ( SALOME::SALOME_Exception ); + SMESH::long_array* GetIgnoreFaces(); + + void SetTotalThickness(::CORBA::Double thickness) throw ( SALOME::SALOME_Exception ); + ::CORBA::Double GetTotalThickness(); + + void SetNumberLayers(::CORBA::Short nb) throw ( SALOME::SALOME_Exception ); + ::CORBA::Short GetNumberLayers(); + + void SetStretchFactor(::CORBA::Double factor) throw ( SALOME::SALOME_Exception ); + ::CORBA::Double GetStretchFactor(); + + // Get implementation + ::StdMeshers_ViscousLayers* GetImpl(); + + // Verify whether hypothesis supports given entity type + CORBA::Boolean IsDimSupported( SMESH::Dimension type ); + + protected: + virtual std::string getMethodOfParameter(const int paramIndex, int nbVars) const; +}; + +#endif diff --git a/src/StdMeshers_I/StdMeshers_i.cxx b/src/StdMeshers_I/StdMeshers_i.cxx index 8d73b56d8..20104d9c8 100644 --- a/src/StdMeshers_I/StdMeshers_i.cxx +++ b/src/StdMeshers_I/StdMeshers_i.cxx @@ -1,29 +1,30 @@ -// Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE +// Copyright (C) 2007-2012 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 +// 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 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. +// 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 +// 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 +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com // + // SMESH StdMeshers : implementaion of SMESH idl descriptions // File : StdMeshers_i.cxx // Author : Julia DOROVSKIKH // Module : SMESH - +// #include "SMESH_StdMeshers_I.hxx" #include "SMESH_Gen_i.hxx" @@ -34,12 +35,13 @@ #include "StdMeshers_AutomaticLength_i.hxx" #include "StdMeshers_StartEndLength_i.hxx" #include "StdMeshers_Arithmetic1D_i.hxx" +#include "StdMeshers_FixedPoints1D_i.hxx" #include "StdMeshers_NumberOfSegments_i.hxx" #include "StdMeshers_Deflection1D_i.hxx" #include "StdMeshers_Propagation_i.hxx" #include "StdMeshers_LengthFromEdges_i.hxx" #include "StdMeshers_QuadranglePreference_i.hxx" -#include "StdMeshers_TrianglePreference_i.hxx" +//#include "StdMeshers_TrianglePreference_i.hxx" #include "StdMeshers_QuadraticMesh_i.hxx" #include "StdMeshers_MaxElementArea_i.hxx" #include "StdMeshers_MaxElementVolume_i.hxx" @@ -49,8 +51,14 @@ #include "StdMeshers_ProjectionSource1D_i.hxx" #include "StdMeshers_NumberOfLayers_i.hxx" #include "StdMeshers_LayerDistribution_i.hxx" +#include "StdMeshers_NumberOfLayers2D_i.hxx" +#include "StdMeshers_LayerDistribution2D_i.hxx" #include "StdMeshers_SegmentLengthAroundVertex_i.hxx" #include "StdMeshers_MaxLength_i.hxx" +#include "StdMeshers_QuadrangleParams_i.hxx" +#include "StdMeshers_ImportSource1D_i.hxx" +#include "StdMeshers_ImportSource2D_i.hxx" +#include "StdMeshers_Cartesian_3D_i.hxx" #include "StdMeshers_Regular_1D_i.hxx" #include "StdMeshers_MEFISTO_2D_i.hxx" @@ -61,14 +69,41 @@ #include "StdMeshers_SegmentAroundVertex_0D_i.hxx" #include "StdMeshers_CompositeSegment_1D_i.hxx" #include "StdMeshers_UseExisting_1D2D_i.hxx" - +#include "StdMeshers_RadialQuadrangle_1D2D_i.hxx" +#include "StdMeshers_Import_1D_i.hxx" +#include "StdMeshers_Import_1D2D_i.hxx" +#include "StdMeshers_ViscousLayers_i.hxx" +#include "StdMeshers_CartesianParameters3D_i.hxx" template class StdHypothesisCreator_i:public HypothesisCreator_i { +public: // as we have 'module StdMeshers' in SMESH_BasicHypothesis.idl virtual std::string GetModuleName() { return "StdMeshers"; } }; +//============================================================================= +/*! + * \brief Creates StdMeshers_QuadrangleParams_i instead of + * StdMeshers_TrianglePreference_i and StdMeshers_QuadranglePreference_i + */ +//============================================================================= + +template +class QuadrangleParamsCreator : public StdHypothesisCreator_i +{ +public: + virtual SMESH_Hypothesis_i* Create (PortableServer::POA_ptr thePOA, + int theStudyId, + ::SMESH_Gen* theGenImpl) + { + StdMeshers_QuadrangleParams_i* h = + new StdMeshers_QuadrangleParams_i( thePOA, theStudyId, theGenImpl); + h->SetQuadType( TYPE ); + return h; + } +}; + //============================================================================= /*! * @@ -105,14 +140,19 @@ STDMESHERS_I_EXPORT aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "Deflection1D") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "FixedPoints1D") == 0) + aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "Arithmetic1D") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "AutomaticLength") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "QuadranglePreference") == 0) + // do not convert to StdMeshers_QuadrangleParams_i as it is used by NETGEN 2D aCreator = new StdHypothesisCreator_i; + //aCreator = new QuadrangleParamsCreator< StdMeshers::QUAD_QUADRANGLE_PREF >(); else if (strcmp(aHypName, "TrianglePreference") == 0) - aCreator = new StdHypothesisCreator_i; + //aCreator = new StdHypothesisCreator_i; + aCreator = new QuadrangleParamsCreator< StdMeshers::QUAD_TRIANGLE_PREF >(); else if (strcmp(aHypName, "QuadraticMesh") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "ProjectionSource3D") == 0) @@ -125,8 +165,22 @@ STDMESHERS_I_EXPORT aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "LayerDistribution") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "NumberOfLayers2D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "LayerDistribution2D") == 0) + aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "SegmentLengthAroundVertex") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "QuadrangleParams") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "ImportSource1D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "ImportSource2D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "ViscousLayers") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "CartesianParameters3D") == 0) + aCreator = new StdHypothesisCreator_i; // Algorithms else if (strcmp(aHypName, "Regular_1D") == 0) @@ -139,6 +193,8 @@ STDMESHERS_I_EXPORT aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "Projection_1D") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "Projection_1D2D") == 0) + aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "Projection_2D") == 0) aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "Projection_3D") == 0) @@ -155,6 +211,14 @@ STDMESHERS_I_EXPORT aCreator = new StdHypothesisCreator_i; else if (strcmp(aHypName, "UseExisting_2D") == 0) aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "RadialQuadrangle_1D2D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "Import_1D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "Import_1D2D") == 0) + aCreator = new StdHypothesisCreator_i; + else if (strcmp(aHypName, "Cartesian_3D") == 0) + aCreator = new StdHypothesisCreator_i; else ; return aCreator; diff --git a/src/Tools/Makefile.am b/src/Tools/Makefile.am new file mode 100644 index 000000000..cae255b85 --- /dev/null +++ b/src/Tools/Makefile.am @@ -0,0 +1,31 @@ +# Copyright (C) 2007-2012 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 +# + +# File : Makefile.in +# Author : Patrick GOLDBRONN (CEA) +# Modified by : Alexander BORODIN (OCN) - autotools usage +# Module : SMESH +# $Header$ +# +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +SUBDIRS = MeshCut padder + +salomeplugins_PYTHON = \ + smesh_plugins.py diff --git a/src/Tools/MeshCut/AUTHORS b/src/Tools/MeshCut/AUTHORS new file mode 100644 index 000000000..28f414d71 --- /dev/null +++ b/src/Tools/MeshCut/AUTHORS @@ -0,0 +1,19 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// +// Authors: Jean Claude LALEUF, Jean Francois HERY, XMESHLAB project, EDF R&D diff --git a/src/Tools/MeshCut/Makefile.am b/src/Tools/MeshCut/Makefile.am new file mode 100644 index 000000000..d09c3bb2a --- /dev/null +++ b/src/Tools/MeshCut/Makefile.am @@ -0,0 +1,59 @@ +# Copyright (C) 2007-2012 EDF R&D +# +# 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 + + +bin_PROGRAMS = MeshCut + +MeshCut_SOURCES = \ + MeshCut_Globals.hxx \ + MeshCut_Carre.hxx \ + MeshCut_Carre.cxx \ + MeshCut_Cube.hxx \ + MeshCut_Cube.cxx \ + MeshCut_Maillage.hxx \ + MeshCut_Maillage.cxx \ + MeshCut_Fonctions.hxx \ + MeshCut_Fonctions.cxx \ + MeshCut_Utils.hxx \ + MeshCut_Utils.cxx \ + MeshCut_Cas.hxx \ + MeshCut_Cas.cxx \ + MeshCut_DC.cxx + +MeshCut_CPPFLAGS = $(MED3_INCLUDES) + +MeshCut_LDFLAGS = $(MED3_LIBS) $(HDF5_LIBS) + +salomeplugins_PYTHON = \ + meshcut_plugin.py + +UIPY_FILES = MeshCutDialog.py + +if SMESH_ENABLE_GUI + nodist_salomescript_SCRIPTS = $(UIPY_FILES) +endif + +CLEANFILES = $(UIPY_FILES) + +EXTRA_DIST += $(UIPY_FILES:%.py=%.ui) + +%.py : %.ui + $(PYUIC) $< -o $@ diff --git a/src/Tools/MeshCut/MeshCutDialog.ui b/src/Tools/MeshCut/MeshCutDialog.ui new file mode 100644 index 000000000..a7f29b453 --- /dev/null +++ b/src/Tools/MeshCut/MeshCutDialog.ui @@ -0,0 +1,273 @@ + + + Dialog + + + + 0 + 0 + 416 + 431 + + + + cut a mesh by a plane + + + false + + + + + + + + original mesh file + + + + + + + + + + + + output mesh name + + + + + + + meshCut + + + + + + + name of group above cut + + + + + + + above + + + + + + + name of group below cut + + + + + + + below + + + + + + + + + cut plane + + + + + + + + normal vector + + + + + + + vertex in plane + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + + + + + -999999999.000000000000000 + + + 999999999.000000000000000 + + + + + + + + + + + + Qt::Horizontal + + + + Tolerance 0 < T <= 1 + + + + + 1.000000000000000 + + + 0.010000000000000 + + + 0.010000000000000 + + + + + + + + Qt::Vertical + + + + 396 + 90 + + + + + + + + Qt::Horizontal + + + + Help + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + cut mesh file + + + + + + + + + + + pb_okCancel + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + pb_okCancel + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/Tools/MeshCut/MeshCut_Carre.cxx b/src/Tools/MeshCut/MeshCut_Carre.cxx new file mode 100644 index 000000000..181a6e5f9 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Carre.cxx @@ -0,0 +1,54 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Carre.hxx" + +#include + +using namespace MESHCUT; +using namespace std; + +Carre::Carre(float _x0, float _x1, float _y0, float _y1) +{ + x0 = _x0; + x1 = _x1; + y0 = _y0; + y1 = _y1; +} + +bool Carre::disjoint(Carre* c2) +{ + return (x0 > c2->x1 || x1 < c2->x0 || y0 > c2->y1 || y1 < c2->y0); +} + +bool Carre::contientNoeud(int ngnoeud, Maillage *MAILLAGE) +{ + float x = *(MAILLAGE->XX + ngnoeud - 1); + float y = *(MAILLAGE->YY + ngnoeud - 1); + return (x >= x0 && x <= x1 && y >= y0 && y <= y1); +} + +void Carre::affichage() +{ + cout << "x0=" << x0 << " "; + cout << "x1=" << x1 << " "; + cout << "y0=" << y0 << " "; + cout << "y1=" << y1 << " "; +} + diff --git a/src/Tools/MeshCut/MeshCut_Carre.hxx b/src/Tools/MeshCut/MeshCut_Carre.hxx new file mode 100644 index 000000000..9f6f303b8 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Carre.hxx @@ -0,0 +1,39 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_CARRE_HXX__ +#define __MESHCUT_CARRE_HXX__ + +#include "MeshCut_Maillage.hxx" + +namespace MESHCUT + { + class Carre + { + public: + float x0, x1, y0, y1; + public: + Carre(float _x0, float _x1, float _y0, float _y1); + bool disjoint(Carre* c2); + bool contientNoeud(int ngnoeud, Maillage *MAILLAGE); + void affichage(); + }; + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_Cas.cxx b/src/Tools/MeshCut/MeshCut_Cas.cxx new file mode 100644 index 000000000..16c8455a4 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Cas.cxx @@ -0,0 +1,945 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Cas.hxx" + +#include "MeshCut_Globals.hxx" + +using namespace MESHCUT; +using namespace std; + +/*! + * Le cas 1 traduit le fait que deux des sommets du T4 initial sont dans le plan de coupe. + * Le point d'intersection franc trouvé est sur l'arête opposée à ces deux points du T4. + + * Le T4 initial produit deux nouveaux T4. + */ +void MESHCUT::cas1(int VN[6], int it4) +{ + cutTetras.push_back(it4); + // cout << "Cas 1 - it4=" << it4 << ", VN = " << VN[0] << " " << VN[1] << " " << VN[2] << " " << VN[3] << " " << VN[4] + // << " " << VN[5] << " " << endl; + + // Numéros des noeuds du TETRA4 + int ng0 = MAILLAGE1->CNX[TETRA4][4 * it4 + 0]; + int ng1 = MAILLAGE1->CNX[TETRA4][4 * it4 + 1]; + int ng2 = MAILLAGE1->CNX[TETRA4][4 * it4 + 2]; + int ng3 = MAILLAGE1->CNX[TETRA4][4 * it4 + 3]; + + int i1, i2; + + if (VN[0] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 1 du T4 d'origine + + // cout << "cas 1/0" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else if (VN[1] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 0 du T4 d'origine + + // cout << "cas 1/1" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else if (VN[2] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 0 du T4 d'origine + + // cout << "cas 1/2" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[2]); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else if (VN[3] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 1 du T4 d'origine + + // cout << "cas 1/3" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else if (VN[4] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 1 du T4 d'origine + + // cout << "cas 1/4" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[4]); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else if (VN[5] != -1) + { + // Le sommet de T4new1 servant à la détermination du groupe est le noeud 3 du T4 d'origine + + // cout << "cas 1/5" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[5]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + i1 = cptNouvellesMailles[TETRA4] - 1; + + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[5]); + cptNouvellesMailles[TETRA4]++; + i2 = cptNouvellesMailles[TETRA4] - 1; + + if (POSN[ng3 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + } + else + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2); + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1); + } + } + + else + ERREUR(" Intersections configuration not taken into account (case cptPI=1) "); + + //int nl1 = MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i1; + //int nl2 = MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + i2; + // cout << "La maille TETRA4 " << it4 << " produit les mailles TETRA4-" << nl1 << " et TETRA4-" << nl2 << endl; + +} + +/*! Deux points d'intersection + * Le cas 2 traduit le fait qu'un des sommets du T4 est dans le plan de coupe. + * Ce sommet est celui des quatre qui n'appartient à aucune des deux arêtes sur lesquelles + * un point d'intersection non -1 a été calculé. + * + * Le T4 initial produit un nouveau T4 et un élément PYRAM5. + * + */ +void MESHCUT::cas2(int VN[6], int it4) +{ + cutTetras.push_back(it4); + // cout << "Cas 2 - it4=" << it4 << ", VN = " << VN[0] << " " << VN[1] << " " << VN[2] << " " << VN[3] << " " << VN[4] + // << " " << VN[5] << " " << endl; + + // Numéros des noeuds du TETRA4 + int ng0 = MAILLAGE1->CNX[TETRA4][4 * it4 + 0]; + int ng1 = MAILLAGE1->CNX[TETRA4][4 * it4 + 1]; + int ng2 = MAILLAGE1->CNX[TETRA4][4 * it4 + 2]; + int ng3 = MAILLAGE1->CNX[TETRA4][4 * it4 + 3]; + + if (VN[0] != -1 && VN[1] != -1) + { + // Le sommet du nouveau T4 est le noeud 0 du T4 d'origine + + // cout << "cas 2.01" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(ng3); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(ng0); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[0]); + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(VN[1]); + newCNX[PYRAM5].push_back(ng3); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + else if (VN[0] != -1 && VN[2] != -1) + { + // Le sommet du nouveau T4 est le noeud 0 du T4 d'origine + + // cout << "cas 2.02" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng0); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[0]); + newCNX[PYRAM5].push_back(VN[2]); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(ng2); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + else if (VN[0] != -1 && VN[3] != -1) + { + // Le sommet du nouveau T4 est le noeud 1 du T4 d'origine + + // cout << "cas 2.03" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(ng3); + newCNX[TETRA4].push_back(ng1); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[0]); + newCNX[PYRAM5].push_back(VN[3]); + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(ng3); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + else if (VN[0] != -1 && VN[4] != -1) + { + // Le sommet du nouveau T4 est le noeud 1 du T4 d'origine + + // cout << "cas 2.04" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(ng1); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(VN[4]); + newCNX[PYRAM5].push_back(VN[0]); + newCNX[PYRAM5].push_back(ng2); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[0] != -1 && VN[5] != -1) + ERREUR("Case 2/05 forbidden"); + + else if (VN[1] != -1 && VN[2] != -1) + { + // Le sommet du nouveau T4 est le noeud 0 du T4 d'origine + + // cout << "cas 2.12" << endl; + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(ng0); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(VN[2]); + newCNX[PYRAM5].push_back(VN[1]); + newCNX[PYRAM5].push_back(ng1); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + else if (VN[1] != -1 && VN[3] != -1) + { + // Le sommet du nouveau T4 est le noeud 2 du T4 d'origine + + // cout << "cas 2.13" << endl; + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(ng3); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(ng2); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(VN[3]); + newCNX[PYRAM5].push_back(VN[1]); + newCNX[PYRAM5].push_back(ng3); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng2 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[1] != -1 && VN[4] != -1) + ERREUR("Case 2/14 excluded"); + + else if (VN[1] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 2 du T4 d'origine + + // cout << "cas 2.15" << endl; + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(VN[5]); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[1]); + newCNX[PYRAM5].push_back(VN[5]); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(ng1); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng2 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[2] != -1 && VN[3] != -1) + ERREUR("Case 2/23 excluded"); + + else if (VN[2] != -1 && VN[4] != -1) + { + // Le sommet du nouveau T4 est le noeud 3 du T4 d'origine + + // cout << "cas 2.24" << endl; + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(VN[2]); + newCNX[PYRAM5].push_back(VN[4]); + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(ng2); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng3 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[2] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 3 du T4 d'origine + + // cout << "cas 2.25" << endl; + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[5]); + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng0); + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(VN[5]); + newCNX[PYRAM5].push_back(VN[2]); + newCNX[PYRAM5].push_back(ng1); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng3 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[3] != -1 && VN[4] != -1) + { + // Le sommet du nouveau T4 est le noeud 1 du T4 d'origine + + // cout << "cas 2.34" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(ng1); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[3]); + newCNX[PYRAM5].push_back(VN[4]); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(ng0); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[3] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 2 du T4 d'origine + + // cout << "cas 2.35" << endl; + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(VN[5]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(ng0); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(VN[3]); + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(ng3); + newCNX[PYRAM5].push_back(VN[5]); + newCNX[PYRAM5].push_back(ng0); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng2 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else if (VN[4] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 3 du T4 d'origine + + // cout << "cas 2.35" << endl; + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(VN[5]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + + newCNX[PYRAM5].push_back(ng1); + newCNX[PYRAM5].push_back(VN[4]); + newCNX[PYRAM5].push_back(VN[5]); + newCNX[PYRAM5].push_back(ng2); + newCNX[PYRAM5].push_back(ng0); + cptNouvellesMailles[PYRAM5]++; + + if (POSN[ng3 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PYRAM5].push_back(MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1); + } + } + + else + ERREUR(" Intersections configuration not taken into account (case cptPI=2) "); + + // int ngT4 = MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1; + // int ngP5 = MAILLAGE1->EFFECTIFS_TYPES[PYRAM5] + cptNouvellesMailles[PYRAM5] - 1; + // cout << "La maille TETRA4 " << it4 << " produit les mailles TETRA4-" << ngT4 << " et PYRAM5-" << ngP5 << endl; + +} + +/*! Trois points d'intersection + * ATTENTION: pour les PENTA6 on adopte la convention d'orientation SALOME : + * + * N1 N2 N3 N4 N5 N6 + * + * où N1 N2 N3 sont les sommets du haut et N4 N5 N6 les sommets du bas + * (selon l'orientation donnée par le sens des triangles) + */ +void MESHCUT::cas3(int VN[6], int it4) +{ + cutTetras.push_back(it4); + // cout << "Cas 3 - it4="<CNX[TETRA4][4 * it4 + 0]; + int ng1 = MAILLAGE1->CNX[TETRA4][4 * it4 + 1]; + int ng2 = MAILLAGE1->CNX[TETRA4][4 * it4 + 2]; + int ng3 = MAILLAGE1->CNX[TETRA4][4 * it4 + 3]; + + if (VN[0] != -1 && VN[1] != -1 && VN[2] != -1) + { + + // Le sommet du nouveau T4 est le noeud 0 du T4 d'origine + + newCNX[TETRA4].push_back(ng0); + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(VN[2]); + cptNouvellesMailles[TETRA4]++; + + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(VN[0]); + newCNX[PENTA6].push_back(VN[2]); + newCNX[PENTA6].push_back(VN[1]); + cptNouvellesMailles[PENTA6]++; + + if (POSN[ng0 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + } + + else if (VN[0] != -1 && VN[3] != -1 && VN[4] != -1) + { + // Le sommet du nouveau T4 est le noeud 1 du T4 d'origine + + //cout << "cas 3.2 (noeud sommet 1)" << endl; + newCNX[TETRA4].push_back(VN[0]); + newCNX[TETRA4].push_back(ng1); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(VN[4]); + cptNouvellesMailles[TETRA4]++; + + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(VN[0]); + newCNX[PENTA6].push_back(VN[3]); + newCNX[PENTA6].push_back(VN[4]); + cptNouvellesMailles[PENTA6]++; + + if (POSN[ng1 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + } + else if (VN[1] != -1 && VN[3] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 2 du T4 d'origine + + //cout << "cas 3.3 (noeud sommet 2)" << endl; + + newCNX[TETRA4].push_back(VN[1]); + newCNX[TETRA4].push_back(VN[3]); + newCNX[TETRA4].push_back(ng2); + newCNX[TETRA4].push_back(VN[5]); + cptNouvellesMailles[TETRA4]++; + + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(VN[1]); + newCNX[PENTA6].push_back(VN[5]); + newCNX[PENTA6].push_back(VN[3]); + cptNouvellesMailles[PENTA6]++; + + if (POSN[ng2 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + } + else if (VN[2] != -1 && VN[4] != -1 && VN[5] != -1) + { + // Le sommet du nouveau T4 est le noeud 3 du T4 d'origine + + newCNX[TETRA4].push_back(VN[2]); + newCNX[TETRA4].push_back(VN[4]); + newCNX[TETRA4].push_back(VN[5]); + newCNX[TETRA4].push_back(ng3); + cptNouvellesMailles[TETRA4]++; + + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(VN[2]); + newCNX[PENTA6].push_back(VN[4]); + newCNX[PENTA6].push_back(VN[5]); + cptNouvellesMailles[PENTA6]++; + + if (POSN[ng3 - 1] == 1) + { + GMplus[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + else + { + GMmoins[TETRA4].push_back(MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1); + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1); + } + } + else + { + // cout << "Cas 3 - it4=" << it4 << ", VN = " << VN[0] << " " << VN[1] << " " << VN[2] << " " << VN[3] << " " + // << VN[4] << " " << VN[5] << " " << endl; + // + // int n0 = *(MAILLAGE1->CNX[TETRA4] + it4 * 4 + 0); + // float x0 = MAILLAGE1->XX[n0 - 1]; + // float y0 = MAILLAGE1->YY[n0 - 1]; + // float z0 = MAILLAGE1->ZZ[n0 - 1]; + // + // int n1 = *(MAILLAGE1->CNX[TETRA4] + it4 * 4 + 1); + // float x1 = MAILLAGE1->XX[n1 - 1]; + // float y1 = MAILLAGE1->YY[n1 - 1]; + // float z1 = MAILLAGE1->ZZ[n1 - 1]; + // + // int n2 = *(MAILLAGE1->CNX[TETRA4] + it4 * 4 + 2); + // float x2 = MAILLAGE1->XX[n2 - 1]; + // float y2 = MAILLAGE1->YY[n2 - 1]; + // float z2 = MAILLAGE1->ZZ[n2 - 1]; + // + // int n3 = *(MAILLAGE1->CNX[TETRA4] + it4 * 4 + 3); + // float x3 = MAILLAGE1->XX[n3 - 1]; + // float y3 = MAILLAGE1->YY[n3 - 1]; + // float z3 = MAILLAGE1->ZZ[n3 - 1]; + // + // cout << x0 << " " << y0 << " " << z0 << " " << endl; + // cout << x1 << " " << y1 << " " << z1 << " " << endl; + // cout << x2 << " " << y2 << " " << z2 << " " << endl; + // cout << x3 << " " << y3 << " " << z3 << " " << endl; + + ERREUR(" Intersections configuration not taken into account (case cptPI=3) "); + } + + // int ngT4 = MAILLAGE1->EFFECTIFS_TYPES[TETRA4] + cptNouvellesMailles[TETRA4] - 1; + // int ngP6 = MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + cptNouvellesMailles[PENTA6] - 1; + // cout << "La maille TETRA4 " << it4 << " produit les mailles TETRA4-" << ngT4 << " et PENTA6-" << ngP6 << endl; +} + +/*! Quatre points d'intersection + * + * ATTENTION: pour les PENTA6 on adopte la convention d'orientation SALOME + * + * N1 N2 N3 N4 N5 N6 + * + * où N1 N2 N3 sont les sommets du haut et N4 N5 N6 les sommets du bas + * (selon l'orientation donnée par le sens des triangles) + */ +void MESHCUT::cas4(int VN[6], int it4) +{ + cutTetras.push_back(it4); + // cout << "Cas 4 - it4=" << it4 << ", VN = " << VN[0] << " " << VN[1] << " " << VN[2] << " " << VN[3] << " " << VN[4] + // << " " << VN[5] << " " << endl; + + // Numéros des noeuds du TETRA4 + int ng0 = MAILLAGE1->CNX[TETRA4][4 * it4 + 0]; + int ng1 = MAILLAGE1->CNX[TETRA4][4 * it4 + 1]; + int ng2 = MAILLAGE1->CNX[TETRA4][4 * it4 + 2]; + int ng3 = MAILLAGE1->CNX[TETRA4][4 * it4 + 3]; + + int i1, i2; // Numéros locaux dans le type des mailles créées + + if (VN[0] == -1 && VN[5] == -1) + { + // Les deux arêtes opposées sont [0,1] et [2,3] + + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(VN[1]); + newCNX[PENTA6].push_back(VN[2]); + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(VN[3]); + newCNX[PENTA6].push_back(VN[4]); + cptNouvellesMailles[PENTA6]++; + i1 = cptNouvellesMailles[PENTA6] - 1; + + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(VN[4]); + newCNX[PENTA6].push_back(VN[2]); + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(VN[3]); + newCNX[PENTA6].push_back(VN[1]); + cptNouvellesMailles[PENTA6]++; + i2 = cptNouvellesMailles[PENTA6] - 1; + + if (POSN[ng0 - 1] == 1) + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + } + else + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + } + + } // if ( VN[0]==-1 && VN[5]==-1 ) + + else if (VN[1] == -1 && VN[4] == -1) + { + // Les deux arêtes opposées sont [0,2] et [1,3] + + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(VN[3]); + newCNX[PENTA6].push_back(VN[5]); + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(VN[0]); + newCNX[PENTA6].push_back(VN[2]); + cptNouvellesMailles[PENTA6]++; + i1 = cptNouvellesMailles[PENTA6] - 1; + + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(VN[3]); + newCNX[PENTA6].push_back(VN[0]); + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(VN[5]); + newCNX[PENTA6].push_back(VN[2]); + cptNouvellesMailles[PENTA6]++; + i2 = cptNouvellesMailles[PENTA6] - 1; + + if (POSN[ng0 - 1] == 1) + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + } + else + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + } + } + + else if (VN[2] == -1 && VN[3] == -1) + { + // Les deux arêtes opposées sont [0,3] et [1,2] + + newCNX[PENTA6].push_back(ng0); + newCNX[PENTA6].push_back(VN[0]); + newCNX[PENTA6].push_back(VN[1]); + newCNX[PENTA6].push_back(ng3); + newCNX[PENTA6].push_back(VN[4]); + newCNX[PENTA6].push_back(VN[5]); + cptNouvellesMailles[PENTA6]++; + i1 = cptNouvellesMailles[PENTA6] - 1; + + newCNX[PENTA6].push_back(ng2); + newCNX[PENTA6].push_back(VN[5]); + newCNX[PENTA6].push_back(VN[1]); + newCNX[PENTA6].push_back(ng1); + newCNX[PENTA6].push_back(VN[4]); + newCNX[PENTA6].push_back(VN[0]); + cptNouvellesMailles[PENTA6]++; + i2 = cptNouvellesMailles[PENTA6] - 1; + + if (POSN[ng0 - 1] == 1) + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + } + else + { + GMplus[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2); + GMmoins[PENTA6].push_back(MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1); + } + } + else + ERREUR(" Intersection configuration not taken into account (case cptPI=4) "); + + // int nl1 = MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i1; + // int nl2 = MAILLAGE1->EFFECTIFS_TYPES[PENTA6] + i2; + // cout << "La maille TETRA4 " << it4 << " produit les mailles PENTA6-" << nl1 << " et PENTA6-" << nl2 << endl; +} diff --git a/src/Tools/MeshCut/MeshCut_Cas.hxx b/src/Tools/MeshCut/MeshCut_Cas.hxx new file mode 100644 index 000000000..0840280f8 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Cas.hxx @@ -0,0 +1,31 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_CAS_HXX__ +#define __MESHCUT_CAS_HXX__ + +namespace MESHCUT + { + void cas1(int VN[6], int it4); + void cas2(int VN[6], int it4); + void cas3(int VN[6], int it4); + void cas4(int VN[6], int it4); + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_Cube.cxx b/src/Tools/MeshCut/MeshCut_Cube.cxx new file mode 100644 index 000000000..62ef84613 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Cube.cxx @@ -0,0 +1,58 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Cube.hxx" + +#include + +using namespace MESHCUT; +using namespace std; + +Cube::Cube(float _x0, float _x1, float _y0, float _y1, float _z0, float _z1) +{ + x0 = _x0; + x1 = _x1; + y0 = _y0; + y1 = _y1; + z0 = _z0; + z1 = _z1; +} + +bool Cube::disjoint(Cube* c2) +{ + return (x0 > c2->x1 || x1 < c2->x0 || y0 > c2->y1 || y1 < c2->y0 || z0 > c2->z1 || z1 < c2->z0); +} + +bool Cube::contientNoeud(int ngnoeud, Maillage *MAILLAGE) +{ + float x = *(MAILLAGE->XX + ngnoeud - 1); + float y = *(MAILLAGE->YY + ngnoeud - 1); + float z = *(MAILLAGE->ZZ + ngnoeud - 1); + return (x >= x0 && x <= x1 && y >= y0 && y <= y1 && z >= z0 && z <= z1); +} + +void Cube::affichage() +{ + cout << "x0=" << x0 << " "; + cout << "x1=" << x1 << " "; + cout << "y0=" << y0 << " "; + cout << "y1=" << y1 << " "; + cout << "z0=" << z0 << " "; + cout << "z1=" << z1 << " "; +} diff --git a/src/Tools/MeshCut/MeshCut_Cube.hxx b/src/Tools/MeshCut/MeshCut_Cube.hxx new file mode 100644 index 000000000..03068d933 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Cube.hxx @@ -0,0 +1,39 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_CUBE_HXX__ +#define __MESHCUT_CUBE_HXX__ + +#include "MeshCut_Maillage.hxx" + +namespace MESHCUT + { + class Cube + { + public: + float x0, x1, y0, y1, z0, z1; + public: + Cube(float _x0, float _x1, float _y0, float _y1, float _z0, float _z1); + bool disjoint(Cube* c2); + bool contientNoeud(int ngnoeud, Maillage *MAILLAGE); + void affichage(); + }; + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_DC.cxx b/src/Tools/MeshCut/MeshCut_DC.cxx new file mode 100644 index 000000000..36bcae4b0 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_DC.cxx @@ -0,0 +1,1124 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +// Classes et fonctions XMeshLab + +#include "MeshCut_Utils.hxx" +#include "MeshCut_Maillage.hxx" + +#include "MeshCut_Carre.hxx" +#include "MeshCut_Cube.hxx" + +#include "MeshCut_Fonctions.hxx" +#include "MeshCut_Cas.hxx" + +#include "MeshCut_Globals.hxx" + +#include +#include +#include +#include + +using namespace MESHCUT; +using namespace std; + +// ================================== DECLARATION DES VARIABLES GLOBALES ================================================== + +std::map MESHCUT::intersections; + +int MESHCUT::indexNouvellesMailles, MESHCUT::indexNouveauxNoeuds, MESHCUT::offsetMailles; +std::string MESHCUT::str_id_GMplus, MESHCUT::str_id_GMmoins; +Maillage *MESHCUT::MAILLAGE1, *MESHCUT::MAILLAGE2; + +std::vector MESHCUT::newXX, MESHCUT::newYY, MESHCUT::newZZ; +std::map > MESHCUT::newCNX; +std::map MESHCUT::cptNouvellesMailles; +std::map > MESHCUT::GMplus, MESHCUT::GMmoins; +std::vector MESHCUT::cutTetras; + +float *MESHCUT::DNP; +int *MESHCUT::POSN; + +std::string MESHCUT::str_id_maillagenew; + +float MESHCUT::normale[3], MESHCUT::pointPlan[3]; +float MESHCUT::d; +float MESHCUT::epsilon; + +bool MESHCUT::debug; +int MESHCUT::Naretes; + +// ================================== PROGRAMME PRINCIPAL ================================================== + +int main(int argc, char *argv[]) +{ + + debug = false; + string ficMEDin; + string ficMEDout; + float xNormal; + float yNormal; + float zNormal; + float xm; + float ym; + float zm; + float tolerance; + try + { + if (argc != 13) + throw std::exception(); + char *ficMEDin0 = argv[1]; + ficMEDin = (string) ficMEDin0; + char *ficMEDout0 = argv[2]; + ficMEDout = (string) ficMEDout0; + char *id_maillagenew = argv[3]; + str_id_maillagenew = (string) id_maillagenew; + + // Groupes créés + char *id_GMplus = argv[4]; + str_id_GMplus = (string) id_GMplus; + char *id_GMmoins = argv[5]; + str_id_GMmoins = (string) id_GMmoins; + + // Vecteur normal au plan de coupe + char *charxn = argv[6]; + xNormal = char2float(charxn); + char *charyn = argv[7]; + yNormal = char2float(charyn); + char *charzn = argv[8]; + zNormal = char2float(charzn); + + // Point du plan de coupe + char *charxm = argv[9]; + xm = char2float(charxm); + char *charym = argv[10]; + ym = char2float(charym); + char *charzm = argv[11]; + zm = char2float(charzm); + + // Tolérance : epsilon = tolérance * longueur arête moyenne - où epsilon est la tolérance absolue (distance) + char *chtolerance = argv[12]; + tolerance = char2float(chtolerance); + } + catch (...) + { + cout << endl; + cout << " Cut a tetrahedron mesh by a plane" << endl; + cout << " ---------------------------------" << endl; + cout << "Syntax:" << endl << endl; + cout << argv[0] << " input.med output.med resuMeshName aboveGroup belowGroup nx ny nz px py pz T " << endl; + cout << endl << "where:" << endl; + cout << " input.med = name of the original mesh file in med format" << endl; + cout << " output.med = name of the result mesh file in med format" << endl; + cout << " resuMeshName = name of the result mesh" << endl; + cout << " aboveGroup = name of the group of volumes above the cut plane" << endl; + cout << " belowGroups = name of the group of volumes below the cut plane" << endl; + cout << " nx ny nz = vector normal to the cut plane" << endl; + cout << " px py pz = a point of the cut plane" << endl; + cout << " T = 0 < T < 1 : vertices of a tetrahedron are considered as belonging" << endl; + cout << " the cut plane if their distance to the plane is inferior to L*T" << endl; + cout << " where L is the mean edge size of the tetrahedron" << endl; + ERREUR("--> check arguments!"); + } + + cout << "Cut by a plane :" << endl; + cout << " source mesh: " << ficMEDin << endl; + cout << " result mesh: " << ficMEDout << endl; + cout << " mesh name: " << str_id_maillagenew << endl; + cout << " group above plane: " << str_id_GMplus << endl; + cout << " group below plane: " << str_id_GMmoins << endl; + cout << " vector normal to the cut plane: xn=" << xNormal << " yn=" << yNormal << " zn=" << zNormal << endl; + cout << " point in the cut plane: xm=" << xm << " ym=" << ym << " zm=" << zm << endl; + cout << " tolerance: " << tolerance << endl; + cout << endl; + + if (tolerance <= 0.0) + ERREUR("Tolerance must not be negative or null"); + + // Il faut normer la normale + float normeNormal = sqrt(xNormal * xNormal + yNormal * yNormal + zNormal * zNormal); + if (normeNormal == 0.0) + ERREUR("null normal vector"); + normale[0] = xNormal / normeNormal; + normale[1] = yNormal / normeNormal; + normale[2] = zNormal / normeNormal; + + pointPlan[0] = xm; + pointPlan[1] = ym; + pointPlan[2] = zm; + + // Calcul du coefficient d de l'équation du plan xn x + yn y + zn n + d = 0 + d = -normale[0] * xm - normale[1] * ym - normale[2] * zm; + + intersections.clear(); + + // Initialisation des compteurs de nouvelles mailles + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + cptNouvellesMailles[tm] = 0; + } + + int V[6]; + int S[4]; // Signature du T4 courant + //int NG[4]; // Num. globaux des sommets + + // Acquisition maillage initial + //cout << chrono() << " - Acquisition du maillage initial" << endl; + MAILLAGE1 = new Maillage((string) "TEMP"); + MAILLAGE1->inputMED(ficMEDin); + cout << chrono() << " - End of mesh read" << endl; + indexNouveauxNoeuds = MAILLAGE1->nombreNoeudsMaillage; + + // Le maillage ne contient aucun TETRA4 : on rend le maillage initial sans modification + if (!MAILLAGE1->EFFECTIFS_TYPES[TETRA4]) + { + cout << "WARNING: mesh does not contain tetra4 elements, it will not be modified" << endl; + MAILLAGE1->ID = str_id_maillagenew; + MAILLAGE1->outputMED(ficMEDout); + cout << chrono() << " - Finished!" << endl << endl; + exit(0); + } + // A partir de cet instant le maillage contient forcément des TETRA4 + + + // Chargement des distances noeud-plan DNP + DNP = (float*) malloc(sizeof(float) * MAILLAGE1->nombreNoeudsMaillage); + for (int k = 0; k < MAILLAGE1->nombreNoeudsMaillage; k++) + DNP[k] = distanceNoeudPlan(k + 1); + cout << chrono() << " - End of computation of distances between nodes and plane" << endl; + + // Longueur d'arête moyenne des T4 intersectant le plan de coupe + float LONGUEURS = 0.0; + int cptLONGUEURS = 0; + for (int it4 = 0; it4 < MAILLAGE1->EFFECTIFS_TYPES[TETRA4]; it4++) + { + bool plus = false; + bool moins = false; + int *offset = MAILLAGE1->CNX[TETRA4] + 4 * it4; + for (int is = 0; is < 4; is++) + { + int ng = *(offset + is); + if (DNP[ng - 1] > 0.0) + plus = true; + else if (DNP[ng - 1] < 0.0) + moins = true; + } + if (plus && moins) + { + // Ce tetra est à cheval sur le plan de coupe: on calcule ses longueurs d'arêtes + LONGUEURS += longueurSegment(*(offset + 0), *(offset + 1)); + cptLONGUEURS++; + LONGUEURS += longueurSegment(*(offset + 0), *(offset + 2)); + cptLONGUEURS++; + LONGUEURS += longueurSegment(*(offset + 0), *(offset + 3)); + cptLONGUEURS++; + LONGUEURS += longueurSegment(*(offset + 1), *(offset + 2)); + cptLONGUEURS++; + LONGUEURS += longueurSegment(*(offset + 1), *(offset + 3)); + cptLONGUEURS++; + LONGUEURS += longueurSegment(*(offset + 2), *(offset + 3)); + cptLONGUEURS++; + } + } + + // Aucun TETRA4 intercepté par le plan de coupe : on rend MAILLAGE1 + if (cptLONGUEURS == 0) + { + cout + << "WARNING: the cut plane does not cut any tetra4 element, initial mesh will not be modified" + << endl; + MAILLAGE1->ID = str_id_maillagenew; + MAILLAGE1->outputMED(ficMEDout); + cout << chrono() << " - Finished!" << endl << endl; + exit(0); + } + // A partir de cet instant le maillage contient forcément des TETRA4 intersectant le plan de coupe + + + float longueurMoyenne = LONGUEURS / cptLONGUEURS; + epsilon = tolerance * longueurMoyenne; + + int nT4coupe = cptLONGUEURS / 6; + cout << chrono() << " - End of computation of mean length of tetra4 edges near the cut plane" << endl; + + cout << "Number of tetra4 to be cut = " << nT4coupe << endl; + cout << "Mean length = " << longueurMoyenne << endl; + cout << "Tolerance = " << tolerance << endl; + cout << "Epsilon = " << epsilon << endl; + + // Détermination des positions de noeuds par rapport au plan de coupe - POSN + POSN = (int*) malloc(sizeof(int) * MAILLAGE1->nombreNoeudsMaillage); + for (int k = 0; k < MAILLAGE1->nombreNoeudsMaillage; k++) + { + if (DNP[k] > epsilon) + POSN[k] = 1; + else if (DNP[k] < -epsilon) + POSN[k] = -1; + else + POSN[k] = 0; + } + cout << chrono() << " - End of nodes qualification above or below the cut plane" << endl; + cout << "Start of iteration on tetra4" << endl; + + for (int it4 = 0; it4 < MAILLAGE1->EFFECTIFS_TYPES[TETRA4]; it4++) + { + + for (int is = 0; is < 4; is++) + { + int ng = *(MAILLAGE1->CNX[TETRA4] + 4 * it4 + is); + //NG[is] = ng; + S[is] = *(POSN + ng - 1); + } + + // ------------------------------------------------------------------- + + if (S[0] == -1 && S[1] == -1 && S[2] == -1 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == -1 && S[2] == -1 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == -1 && S[2] == -1 && S[3] == 1) + { // Cas 3 - Arêtes 2 4 5 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas3(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == -1 && S[2] == 0 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == -1 && S[2] == 0 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == -1 && S[2] == 0 && S[3] == 1) + { // Cas 2, arêtes 2 4 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == -1 && S[2] == 1 && S[3] == -1) + { // Cas 3, arêtes 1 3 5 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas3(V, it4); + } + + else if (S[0] == -1 && S[1] == -1 && S[2] == 1 && S[3] == 0) + { // Cas 2, arêtes 1 3 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == -1 && S[1] == -1 && S[2] == 1 && S[3] == 1) + { // Cas 4, arêtes 1 2 3 4 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas4(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 0 && S[2] == -1 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == 0 && S[2] == -1 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == 0 && S[2] == -1 && S[3] == 1) + { // Cas 2, arêtes 2 5 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 0 && S[2] == 0 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == 0 && S[2] == 0 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == -1 && S[1] == 0 && S[2] == 0 && S[3] == 1) + { // Cas 1, arête 2 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 0 && S[2] == 1 && S[3] == -1) + { // Cas 2, arêtes 1 5 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + else if (S[0] == -1 && S[1] == 0 && S[2] == 1 && S[3] == 0) + { // Cas 1, arête 1 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == -1 && S[1] == 0 && S[2] == 1 && S[3] == 1) + { // Cas 2, arêtes 1 2 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 1 && S[2] == -1 && S[3] == -1) + { // Cas 3, arêtes 0 3 4 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas3(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == -1 && S[3] == 0) + { // Cas 2, arêtes 0 3 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == -1 && S[3] == 1) + { // Cas 4, arêtes 0 2 3 5 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas4(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 1 && S[2] == 0 && S[3] == -1) + { // Cas 2, arêtes 0 4 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == 0 && S[3] == 0) + { // Cas 1, arête 0 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == 0 && S[3] == 1) + { // Cas 2, arêtes 0 2 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == -1 && S[1] == 1 && S[2] == 1 && S[3] == -1) + { // Cas 4, arêtes 0 1 4 5 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas4(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == 1 && S[3] == 0) + { // Cas 2, arêtes 0 1 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == -1 && S[1] == 1 && S[2] == 1 && S[3] == 1) + { // Cas 3, arêtes 0 1 2 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas3(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == -1 && S[2] == -1 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == -1 && S[2] == -1 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == -1 && S[2] == -1 && S[3] == 1) + { // Cas 2, arêtes 4 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == -1 && S[2] == 0 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == -1 && S[2] == 0 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == -1 && S[2] == 0 && S[3] == 1) + { // Cas 1, arête 4 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas1(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == -1 && S[2] == 1 && S[3] == -1) + { // Cas 2, arêtes 3 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + else if (S[0] == 0 && S[1] == -1 && S[2] == 1 && S[3] == 0) + { // Cas 1, arête 3 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 0 && S[1] == -1 && S[2] == 1 && S[3] == 1) + { // Cas 2, arêtes 3 4 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 0 && S[2] == -1 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 0 && S[2] == -1 && S[3] == 0) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 0 && S[2] == -1 && S[3] == 1) + { // Cas 1, arête 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas1(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 0 && S[2] == 0 && S[3] == -1) + GMmoins[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 0 && S[2] == 0 && S[3] == 0) + { + cout << "WARNING: TETRA4 number " << it4 + << " entirely in the tolerance zone near the cut plane" << endl; + cout << " --> affected to group " << str_id_GMmoins << endl; + GMmoins[TETRA4].push_back(it4); + } + + else if (S[0] == 0 && S[1] == 0 && S[2] == 0 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 0 && S[2] == 1 && S[3] == -1) + { // Cas 1, arête 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas1(V, it4); + } + + else if (S[0] == 0 && S[1] == 0 && S[2] == 1 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 0 && S[2] == 1 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 1 && S[2] == -1 && S[3] == -1) + { // Cas 2, arêtes 3 4 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 0 && S[1] == 1 && S[2] == -1 && S[3] == 0) + { // Cas 1, arête 3 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 0 && S[1] == 1 && S[2] == -1 && S[3] == 1) + { // Cas 2, arêtes 3 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 1 && S[2] == 0 && S[3] == -1) + { // Cas 1, arête 4 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 0 && S[1] == 1 && S[2] == 0 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 1 && S[2] == 0 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 0 && S[1] == 1 && S[2] == 1 && S[3] == -1) + { // Cas 2, arêtes 4 5 + V[0] = -1; + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + else if (S[0] == 0 && S[1] == 1 && S[2] == 1 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 0 && S[1] == 1 && S[2] == 1 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == -1 && S[2] == -1 && S[3] == -1) + { // Cas 3, arêtes 0 1 2 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas3(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == -1 && S[3] == 0) + { // Cas 2, arêtes 0 1 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == -1 && S[3] == 1) + { // Cas 4, arêtes 0 1 4 5 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas4(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == -1 && S[2] == 0 && S[3] == -1) + { // Cas 2, arêtes 0 2 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == 0 && S[3] == 0) + { // Cas 1, arête 0 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == 0 && S[3] == 1) + { // Cas 2, arêtes 0 4 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == -1 && S[2] == 1 && S[3] == -1) + { // Cas 4, arêtes 0 2 3 5 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas4(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == 1 && S[3] == 0) + { // Cas 2, arêtes 0 3 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == -1 && S[2] == 1 && S[3] == 1) + { // Cas 3, arêtes 0 3 4 + V[0] = intersectionSegmentPlan(it4, 0); + V[1] = -1; + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas3(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 0 && S[2] == -1 && S[3] == -1) + { // Cas 2, arêtes 1 2 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == 0 && S[2] == -1 && S[3] == 0) + { // Cas 1, arête 1 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 1 && S[1] == 0 && S[2] == -1 && S[3] == 1) + { // Cas 2, arêtes 1 5 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 0 && S[2] == 0 && S[3] == -1) + { // Cas 1, arête 2 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = -1; + cas1(V, it4); + } + + else if (S[0] == 1 && S[1] == 0 && S[2] == 0 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 1 && S[1] == 0 && S[2] == 0 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 0 && S[2] == 1 && S[3] == -1) + { // Cas 2, arêtes 2 5 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == 0 && S[2] == 1 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 1 && S[1] == 0 && S[2] == 1 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 1 && S[2] == -1 && S[3] == -1) + { // Cas 4, arêtes 1 2 3 4 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas4(V, it4); + } + + else if (S[0] == 1 && S[1] == 1 && S[2] == -1 && S[3] == 0) + { // Cas 2, arêtes 1 3 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == 1 && S[2] == -1 && S[3] == 1) + { // Cas 3, arêtes 1 3 5 + V[0] = -1; + V[1] = intersectionSegmentPlan(it4, 1); + V[2] = -1; + V[3] = intersectionSegmentPlan(it4, 3); + V[4] = -1; + V[5] = intersectionSegmentPlan(it4, 5); + cas3(V, it4); + } + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 1 && S[2] == 0 && S[3] == -1) + { // Cas 2, arêtes 2 4 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = -1; + cas2(V, it4); + } + + else if (S[0] == 1 && S[1] == 1 && S[2] == 0 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 1 && S[1] == 1 && S[2] == 0 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + // ------------------------------------------------------------------- + + else if (S[0] == 1 && S[1] == 1 && S[2] == 1 && S[3] == -1) + { // Cas 3, arêtes 2 4 5 + V[0] = -1; + V[1] = -1; + V[2] = intersectionSegmentPlan(it4, 2); + V[3] = -1; + V[4] = intersectionSegmentPlan(it4, 4); + V[5] = intersectionSegmentPlan(it4, 5); + cas3(V, it4); + } + + else if (S[0] == 1 && S[1] == 1 && S[2] == 1 && S[3] == 0) + GMplus[TETRA4].push_back(it4); + + else if (S[0] == 1 && S[1] == 1 && S[2] == 1 && S[3] == 1) + GMplus[TETRA4].push_back(it4); + + else + ERREUR("Case not taken into account"); + + } + cout << chrono() << " - End of iteration on tetra4" << endl; + + // cout << "indexNouveauxNoeuds = " << indexNouveauxNoeuds << endl; + newXX.resize(indexNouveauxNoeuds - MAILLAGE1->nombreNoeudsMaillage); + newYY.resize(indexNouveauxNoeuds - MAILLAGE1->nombreNoeudsMaillage); + newZZ.resize(indexNouveauxNoeuds - MAILLAGE1->nombreNoeudsMaillage); + + if (cptNouvellesMailles[TETRA4]) + newCNX[TETRA4].resize(4 * cptNouvellesMailles[TETRA4]); + if (cptNouvellesMailles[PYRAM5]) + newCNX[PYRAM5].resize(5 * cptNouvellesMailles[PYRAM5]); + if (cptNouvellesMailles[PENTA6]) + newCNX[PENTA6].resize(6 * cptNouvellesMailles[PENTA6]); + + // ========================================================================================= + // 2. Constitution du maillage final + // ========================================================================================= + + cout << chrono() << " - Constitution of final mesh" << endl; + + MAILLAGE2 = new Maillage(str_id_maillagenew); + MAILLAGE2->dimensionMaillage = MAILLAGE1->dimensionMaillage; + MAILLAGE2->dimensionEspace = MAILLAGE1->dimensionEspace; + strcpy(MAILLAGE2->axisname, MAILLAGE1->axisname); + strcpy(MAILLAGE2->unitname, MAILLAGE1->unitname); + MAILLAGE2->nombreNoeudsMaillage = indexNouveauxNoeuds; + MAILLAGE2->nombreMaillesMaillage = MAILLAGE1->nombreMaillesMaillage + cptNouvellesMailles[TETRA4] + + cptNouvellesMailles[PYRAM5] + cptNouvellesMailles[PENTA6]; + + // ---------- Coordonnées + // Optimisation de la mémoire au détriment du temps + + // Héritage des coordonnées MAILLAGE1 + MAILLAGE2->XX = (float*) malloc(sizeof(float) * MAILLAGE2->nombreNoeudsMaillage); + for (int i = 0; i < MAILLAGE1->nombreNoeudsMaillage; i++) + *(MAILLAGE2->XX + i) = *(MAILLAGE1->XX + i); + free(MAILLAGE1->XX); + MAILLAGE2->YY = (float*) malloc(sizeof(float) * MAILLAGE2->nombreNoeudsMaillage); + for (int i = 0; i < MAILLAGE1->nombreNoeudsMaillage; i++) + *(MAILLAGE2->YY + i) = *(MAILLAGE1->YY + i); + free(MAILLAGE1->YY); + MAILLAGE2->ZZ = (float*) malloc(sizeof(float) * MAILLAGE2->nombreNoeudsMaillage); + for (int i = 0; i < MAILLAGE1->nombreNoeudsMaillage; i++) + *(MAILLAGE2->ZZ + i) = *(MAILLAGE1->ZZ + i); + free(MAILLAGE1->ZZ); + + // Coordonnées des noeuds créés + for (int i = 0; i < MAILLAGE2->nombreNoeudsMaillage - MAILLAGE1->nombreNoeudsMaillage; i++) + { + *(MAILLAGE2->XX + MAILLAGE1->nombreNoeudsMaillage + i) = newXX[i]; + *(MAILLAGE2->YY + MAILLAGE1->nombreNoeudsMaillage + i) = newYY[i]; + *(MAILLAGE2->ZZ + MAILLAGE1->nombreNoeudsMaillage + i) = newZZ[i]; + // cout << "Nouveaux noeuds, indice " << i << " : " << newXX[i] << " " << newYY[i] << " " << newZZ[i] << " " << endl; + } + + // Legacy mailles maillage 1 (volumes seulement) + for (int itm = (int) TETRA4; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + if (tm != TETRA4 && tm != PYRAM5 && tm != PENTA6) + { + // Pour les types autres que TETRA4 PYRAM5 PENTA6 on fait seulement pointer CNX2 vers CNX1 + if (MAILLAGE1->EFFECTIFS_TYPES[tm]) + MAILLAGE2->CNX[tm] = MAILLAGE1->CNX[tm]; + MAILLAGE2->EFFECTIFS_TYPES[tm] = MAILLAGE1->EFFECTIFS_TYPES[tm]; + } + else + { + // Pour les types TETRA4 PYRAM5 PENTA6 on recopie CNX1 et on ajoute à la suite les newCNX + // cout << "Legacy " << tm << " effectif " << MAILLAGE1->EFFECTIFS_TYPES[tm] << endl; + int tailleType = Nnoeuds(tm); + + MAILLAGE2->CNX[tm] = (int*) malloc(sizeof(int) * tailleType * (MAILLAGE1->EFFECTIFS_TYPES[tm] + + cptNouvellesMailles[tm])); + for (int i = 0; i < MAILLAGE1->EFFECTIFS_TYPES[tm]; i++) + for (int j = 0; j < tailleType; j++) + *(MAILLAGE2->CNX[tm] + tailleType * i + j) = *(MAILLAGE1->CNX[tm] + tailleType * i + j); + + for (int i = 0; i < cptNouvellesMailles[tm]; i++) + for (int j = 0; j < tailleType; j++) + *(MAILLAGE2->CNX[tm] + tailleType * (MAILLAGE1->EFFECTIFS_TYPES[tm] + i) + j) = newCNX[tm][i * tailleType + + j]; + + MAILLAGE2->EFFECTIFS_TYPES[tm] = MAILLAGE1->EFFECTIFS_TYPES[tm] + cptNouvellesMailles[tm]; + } + } + + // Restit CNX + + // cout << "Maillage 2 - CNX TETRA4 : " << endl; + // ; + // for (int i = 0; i < MAILLAGE2->EFFECTIFS_TYPES[TETRA4]; i++) + // { + // cout << "Maille " << i << " : "; + // for (int j = 0; j < 4; j++) + // cout << MAILLAGE2->CNX[TETRA4][i * 4 + j] << " "; + // cout << endl; + // } + // cout << endl; + // cout << "Maillage 2 - CNX PENTA6 : " << endl; + // ; + // for (int i = 0; i < MAILLAGE2->EFFECTIFS_TYPES[PENTA6]; i++) + // { + // cout << "Maille " << i << " : "; + // for (int j = 0; j < 6; j++) + // cout << MAILLAGE2->CNX[PENTA6][i * 6 + j] << " "; + // cout << endl; + // } + // cout << endl; + + // Groupes de mailles + // MAILLAGE2->GM = MAILLAGE1->GM; + MAILLAGE2->GN.clear(); + MAILLAGE2->GM.clear(); + MAILLAGE2->GM[str_id_GMplus] = GMplus; + MAILLAGE2->GM[str_id_GMmoins] = GMmoins; + + // MAILLAGE2->GN = MAILLAGE1->GN; + + MAILLAGE2->eliminationMailles(TETRA4, cutTetras); + + cout << chrono() << " - MED file writing" << endl; + + MAILLAGE2->outputMED(ficMEDout); + cout << chrono() << " - Finished!" << endl << endl; + + return 0; + +} diff --git a/src/Tools/MeshCut/MeshCut_Fonctions.cxx b/src/Tools/MeshCut/MeshCut_Fonctions.cxx new file mode 100644 index 000000000..28ca3dc56 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Fonctions.cxx @@ -0,0 +1,188 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Fonctions.hxx" +#include "MeshCut_Globals.hxx" + +#include +#include + +using namespace MESHCUT; +using namespace std; + +//================================================================================================= +// intersectionSegmentPlan +//================================================================================================= + +float MESHCUT::longueurSegment(int ngA, int ngB) +{ + float A[3], B[3]; + A[0] = MAILLAGE1->XX[ngA - 1]; + A[1] = MAILLAGE1->YY[ngA - 1]; + A[2] = MAILLAGE1->ZZ[ngA - 1]; + B[0] = MAILLAGE1->XX[ngB - 1]; + B[1] = MAILLAGE1->YY[ngB - 1]; + B[2] = MAILLAGE1->ZZ[ngB - 1]; + float dx = B[0] - A[0]; + float dy = B[1] - A[1]; + float dz = B[2] - A[2]; + return sqrt(dx * dx + dy * dy + dz * dz); +} + +float MESHCUT::distanceNoeudPlan(float point[3]) +{ + return normale[0] * point[0] + normale[1] * point[1] + normale[2] * point[2] + d; +} + +float MESHCUT::distanceNoeudPlan(int ng) +{ + float A[3]; + A[0] = MAILLAGE1->XX[ng - 1]; + A[1] = MAILLAGE1->YY[ng - 1]; + A[2] = MAILLAGE1->ZZ[ng - 1]; + return distanceNoeudPlan(A); +} + +int MESHCUT::positionNoeudPlan(int indiceNoeud) +{ + if (distanceNoeudPlan(indiceNoeud + 1) > epsilon) + return 1; + else if (distanceNoeudPlan(indiceNoeud + 1) < -epsilon) + return -1; + else + return 0; +} + +/*! + * Equation paramétrique de la droite AB: OP = OA + lambda AB + * + * Fonction caractéristique du plan : PI(X,Y,Z) = nx X + ny Y + nz Z + d + * + * Pour un point P de la droite: PI(OP) = PI(OA) + lambda n.AB avec n=(nx,ny,nz), + * L'intersection AB/plan est donnée par le point P tel que PI(OP)=0. + * + * Il lui correspond la coordonnée lambda = - PI(OA) / n.AB. + * + * Cette intersection est dans le segment si lambda est dans [0,1] + */ +int MESHCUT::intersectionSegmentPlan(int it4, int na) +{ + + int ngA, ngB; // Numéros des noeuds extrémités AB + float lambda, ps; //, ab; // ab = longueur AB + float A[3], B[3]; + + // Détermination des ng des extrémités de l'arête passée en argument na + int * offset = MAILLAGE1->CNX[TETRA4] + 4 * it4; + if (na == 0) + { + ngA = *(offset + 0); + ngB = *(offset + 1); + } + else if (na == 1) + { + ngA = *(offset + 0); + ngB = *(offset + 2); + } + else if (na == 2) + { + ngA = *(offset + 0); + ngB = *(offset + 3); + } + else if (na == 3) + { + ngA = *(offset + 1); + ngB = *(offset + 2); + } + else if (na == 4) + { + ngA = *(offset + 1); + ngB = *(offset + 3); + } + else if (na == 5) + { + ngA = *(offset + 2); + ngB = *(offset + 3); + } + else + ERREUR("Edge number superior to 6"); + + string cle1 = int2string(ngA) + (string) "_" + int2string(ngB); + string cle2 = int2string(ngB) + (string) "_" + int2string(ngA); + + if (intersections[cle1]) + return intersections[cle1]; + + else + { + + A[0] = MAILLAGE1->XX[ngA - 1]; + A[1] = MAILLAGE1->YY[ngA - 1]; + A[2] = MAILLAGE1->ZZ[ngA - 1]; + B[0] = MAILLAGE1->XX[ngB - 1]; + B[1] = MAILLAGE1->YY[ngB - 1]; + B[2] = MAILLAGE1->ZZ[ngB - 1]; + + // // Longueur AB + // float lx = B[0] - A[0], ly = B[1] - A[1], lz = B[2] - A[2]; + // ab = sqrt(lx * lx + ly * ly + lz * lz); + // // La longueur maximale théorique est 2 epsilon + // if (ab < 2 * epsilon * 0.9) + // ERREUR("Arête de longueur inférieure au minimum théorique 2 epsilon"); + + // Calcul du produit scalaire AB.n + ps = 0.0; + for (int k = 0; k < 3; k++) + ps += (B[k] - A[k]) * normale[k]; + // ps = ps / ab ; + + if (debug) + { + cout << "Routine ISP : arête " << na << " - ngA=" << ngA << " ngB=" << ngB << endl; + cout << "A : " << A[0] << ' ' << A[1] << ' ' << A[2] << endl; + cout << "B : " << B[0] << ' ' << B[1] << ' ' << B[2] << endl; + cout << "N : " << normale[0] << ' ' << normale[1] << ' ' << normale[2] << endl; + } + + if (fabs(ps) == 0.0) + ERREUR("Error on null scalar product"); + + // PS non nul: l'intersection AB/plan existe + + lambda = -distanceNoeudPlan(A) / ps; + + float inter[3]; + for (int k = 0; k < 3; k++) + inter[k] = A[k] + lambda * (B[k] - A[k]); + newXX.push_back(inter[0]); + newYY.push_back(inter[1]); + newZZ.push_back(inter[2]); + indexNouveauxNoeuds++; + intersections[cle1] = indexNouveauxNoeuds; + intersections[cle2] = indexNouveauxNoeuds; + + // cout << "création noeud " << indexNouveauxNoeuds << " : " << inter[0] << " " << inter[1] << " " << inter[2] + // << endl; + if (debug) + cout << " sortie nouveau noeud, lambda = " << lambda << " , noeud = " << indexNouveauxNoeuds << endl; + return indexNouveauxNoeuds; + + } +} + diff --git a/src/Tools/MeshCut/MeshCut_Fonctions.hxx b/src/Tools/MeshCut/MeshCut_Fonctions.hxx new file mode 100644 index 000000000..cee002f34 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Fonctions.hxx @@ -0,0 +1,36 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_FONCTION_HXX__ +#define __MESHCUT_FONCTION_HXX__ + +namespace MESHCUT + { + float longueurSegment(int ngA, int ngB); + + float distanceNoeudPlan(float point[3]); + + float distanceNoeudPlan(int ng); + + int positionNoeudPlan(int indiceNoeud); + + int intersectionSegmentPlan(int it4, int na); + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_Globals.hxx b/src/Tools/MeshCut/MeshCut_Globals.hxx new file mode 100644 index 000000000..fc1f83153 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Globals.hxx @@ -0,0 +1,62 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_GLOBALS_HXX__ +#define __MESHCUT_GLOBALS_HXX__ + +#include "MeshCut_Maillage.hxx" + +#include +#include +#include + +namespace MESHCUT + { + /*! Table des points d'intersection calculés. + * Si on a calculé une intersection entre le plan et un segment reliant N1 et N2 + * de numéros globaux n1 et n2, on stocke dans ce tableau, sous les libellés "n1_n2" et "n2_n1", + * le numéro global du point d'intersection (noeud créé). + * On évite ainsi de calculer deux fois l'intersection d'une même arête de T4 avec le plan + */ + extern std::map intersections; + + extern int indexNouvellesMailles, indexNouveauxNoeuds, offsetMailles; + extern std::string str_id_GMplus, str_id_GMmoins; + extern Maillage *MAILLAGE1, *MAILLAGE2; + + extern std::vector newXX, newYY, newZZ; + extern std::map > newCNX; + extern std::map cptNouvellesMailles; + extern std::map > GMplus, GMmoins; + extern std::vector cutTetras; + + extern float *DNP; //!< Distance Noeud Plan + extern int *POSN; //!< Version -1/0/+1 du précédent, selon epsilon + + extern std::string str_id_maillagenew; + + extern float normale[3], pointPlan[3]; //!< Définition du plan de coupe + extern float d; //!< coefficient constant de l'équation du plan de coupe + extern float epsilon; //!< distance en dessous de laquelle un point est considéré comme appartenant au plan de coupe + + extern bool debug; + extern int Naretes; + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_Maillage.cxx b/src/Tools/MeshCut/MeshCut_Maillage.cxx new file mode 100644 index 000000000..0ae4437e0 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Maillage.cxx @@ -0,0 +1,1789 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Maillage.hxx" +#include "MeshCut_Cube.hxx" + +#include +#include +#include +#include +#include +#include + +using namespace MESHCUT; +using namespace std; + +Maillage::Maillage(std::string _ID) +{ + ID = _ID; + nombreNoeudsMaillage = 0; + nombreMaillesMaillage = 0; + //nPOI1=0; nSEG2=0; nSEG3=0; nTRIA3=0; nTRIA6=0; nQUAD4=0; nQUAD8=0; nTETRA4=0; nTETRA10=0; nPYRAM5=0; nPYRAM13=0; nPENTA6=0; nPENTA15=0; nHEXA8=0; nHEXA20=0; + GM.clear(); + GN.clear(); + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + EFFECTIFS_TYPES[(TYPE_MAILLE) itm] = 0; +} + +Maillage::~Maillage() +{ +} + +void Maillage::creationGMtype(TYPE_MAILLE tm, std::string nomGMtype) +{ + //cout << "Creation GM type, groupe " << nomGMtype << endl; + for (int nl = 0; nl < EFFECTIFS_TYPES[tm]; nl++) + GM[nomGMtype][tm].push_back(nl); + GM[nomGMtype][tm].resize(EFFECTIFS_TYPES[tm]); + sort(GM[nomGMtype][tm].begin(), GM[nomGMtype][tm].end()); +} + +void Maillage::afficheMailles(TYPE_MAILLE tm) +{ + cout << "Affichage des mailles du type " << TM2string(tm) << " (effectif " << EFFECTIFS_TYPES[tm] << "): " << endl; + if (EFFECTIFS_TYPES[tm]) + { + // Boucle sur les connectivités d'un type tm + int nnoeuds = Nnoeuds(tm); + for (int i = 0; i < EFFECTIFS_TYPES[tm]; i++) + { + cout << "\tMaille " << i << " :" << endl; + //Boucle sur les noeuds de la maille de numéro local i dans le type tm + int * offset = CNX[tm] + nnoeuds * i; + for (int j = 0; j < nnoeuds; j++) + { + int ngnoeud = *(offset + j); + //cout << "\t\t" << X[ngnoeud-1] << " " << Y[ngnoeud-1] << " " << Z[ngnoeud-1] << endl; + cout << "\t" << ngnoeud << "\t" << *(XX + ngnoeud - 1) << " " << *(YY + ngnoeud - 1) << " " << *(ZZ + ngnoeud - 1) << endl; + } + } + cout << endl; + } +} + +void Maillage::listeMaillesType(TYPE_MAILLE tm) +{ + cout << "La fonction \"Restitution des mailles par type\" est obsolète " << endl; + + // cout << "Restitution des mailles du type " << TM2string(tm) << " (effectif " << EFFECTIFS_TYPES[tm] << "): " << endl; + // if (EFFECTIFS_TYPES[tm]) + // for (int i = 0; i < IDS_MAILLES[tm].size(); i++) + // cout << IDS_MAILLES[tm][i] << " "; + // cout << endl; +} + +void Maillage::listeMaillesTousTypes() +{ + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + listeMaillesType(tm); + } +} + +void Maillage::listeMaillesParGM() +{ + cout << "Liste des mailles par GM : " << endl; + for (map > >::iterator I = GM.begin(); I != GM.end(); I++) + listeMaillesGM(I->first); +} + +void Maillage::listeMaillesGM(std::string nomGM) +{ + cout << "Liste des mailles du groupe " << nomGM << " : " << endl; + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + if (GM[nomGM][tm].size()) + { + cout << "\t" << TM2string(tm) << " : "; + for (unsigned int j = 0; j < GM[nomGM][tm].size(); j++) + cout << GM[nomGM][tm][j] << " "; + cout << endl; + } + } +} + +//void Maillage::listeMaillesGMordonne(std::string nomGM) +//{ +// cout << "Liste ordonnée des mailles du groupe " << nomGM << " (" << GM[nomGM].size() << " mailles) : "; +// sort(GM[nomGM].begin(), GM[nomGM].end()); +// for (int j = 0; j < GM[nomGM].size(); j++) +// cout << GM[nomGM][j] << " "; +// cout << endl; +//} + +void Maillage::listeNoeuds() +{ + cout << "Liste des noeuds du maillage : " << endl; + for (int i = 0; i < nombreNoeudsMaillage; i++) + cout << "\t" << *(XX + i) << " " << *(YY + i) << " " << *(ZZ + i) << endl; + cout << endl; +} + +void Maillage::listeNoeudsGN(std::string nomGN) +{ + cout << "Liste brute des noeuds du groupe " << nomGN << " (" << GN[nomGN].size() << " noeuds) : "; + for (unsigned int j = 0; j < GN[nomGN].size(); j++) + cout << GN[nomGN][j] << " "; + cout << endl; +} + +void Maillage::listeNoeudsGNordonne(std::string nomGN) +{ + cout << "Liste ordonnée des noeuds du groupe " << nomGN << " (" << GN[nomGN].size() << " noeuds) : "; + sort(GN[nomGN].begin(), GN[nomGN].end()); + for (unsigned int j = 0; j < GN[nomGN].size(); j++) + cout << GN[nomGN][j] << " "; + cout << endl; +} + +std::vector Maillage::G(int i, TYPE_MAILLE tm) +{ + vector G; + float x = 0.0; + float y = 0.0; + float z = 0.0; + int nn = NnoeudsGeom(tm); + for (int j = 0; j < nn; j++) + { + int ng = CNX[tm][nn * i + j]; + x += XX[ng - 1]; + y += YY[ng - 1]; + z += ZZ[ng - 1]; + } + G.push_back(x / nn); + G.push_back(y / nn); + G.push_back(z / nn); + G.resize(3); + return G; +} + +float Maillage::distanceNoeudMaille(int ngnoeud, int imaille, TYPE_MAILLE tm) +{ + float x, y, z; + float x0 = XX[ngnoeud - 1]; + float y0 = YY[ngnoeud - 1]; + float z0 = ZZ[ngnoeud - 1]; + int nn = NnoeudsGeom(tm); + float d1 = 1000000000000.0; + float d; + for (int j = 0; j < nn; j++) + { + int ng = CNX[tm][nn * imaille + j]; // Noeud courant dans la maille + x = XX[ng - 1]; + y = YY[ng - 1]; + z = ZZ[ng - 1]; + d = sqrt((x - x0) * (x - x0) + (y - y0) * (y - y0) + (z - z0) * (z - z0)); + if (d < d1) + d1 = d; + } + return d1; +} + +/*! + * Retourne le ng du noeud le plus proche de ngnoeud dans la maille imaille du type tm + */ +int Maillage::noeudVoisin(int ngnoeud, int imaille, TYPE_MAILLE tm) +{ + float x, y, z; + int ngv; + float x0 = XX[ngnoeud - 1]; + float y0 = YY[ngnoeud - 1]; + float z0 = ZZ[ngnoeud - 1]; + int nn = NnoeudsGeom(tm); + float d1 = 1000000000000.0; + float d; + for (int j = 0; j < nn; j++) + { + int ng = CNX[tm][nn * imaille + j]; // Noeud courant dans la maille + x = XX[ng - 1]; + y = YY[ng - 1]; + z = ZZ[ng - 1]; + d = sqrt((x - x0) * (x - x0) + (y - y0) * (y - y0) + (z - z0) * (z - z0)); + if (d < d1) + { + d1 = d; + ngv = ng; + } + } + return ngv; +} + +float Maillage::distanceNoeudNoeud(int ng1, int ng2) +{ + float x1, x2, y1, y2, z1, z2; + x1 = XX[ng1 - 1]; + y1 = YY[ng1 - 1]; + z1 = ZZ[ng1 - 1]; + x2 = XX[ng2 - 1]; + y2 = YY[ng2 - 1]; + z2 = ZZ[ng2 - 1]; + return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2)); +} + +//void Maillage::encombrements() +//{ +// float ex = 0.0; +// float ey = 0.0; +// float ez = 0.0; +// +// for (int itm = (int) SEG2; itm <= (int) HEXA20; itm++) +// { +// TYPE_MAILLE tm = (TYPE_MAILLE) itm; +// if (MAILLAGE->EFFECTIFS_TYPES[tm]) +// { +// int nnoeuds = Nnoeuds(tm); +// for (int i = 0; i < CON[tm].size() / nnoeuds; i++) +// { +// //Boucle sur les noeuds de la maille de numéro local i dans le type tm +// for (int j = 0; j < nnoeuds; j++) +// { +// //..... CON[tm][nnoeuds*i+j]; +// } +// } +// } +// } +// // Boucle sur les connectivités d'un type tm +//} + +void Maillage::inputMED(std::string fichierMED) +{ + //cout << endl << "Début procédure inputMED, fichier "<< fichierMED << endl; + + // int i, j, k, ichamp, igauss, ipt, ival; + int j; + // med_err ret = 0; // Code retour + med_idt fid; // Descripteur de fichier MED + char maa[MED_NAME_SIZE + 1]; // nom du maillage de longueur maxi MED_NAME_SIZE + med_int spacedim; + med_int mdim; // Dimension du maillage + med_mesh_type type; + char desc[MED_COMMENT_SIZE + 1]; // Description du maillage + + // Profils + // med_int nprofils; + // int iprofil; + // char nomprofil[MED_NAME_SIZE + 1] = ""; + // med_int nvalprofil, nvalprofil2; + // med_int *pflval; + + // Champs + // med_int nChamps, nCompChamp, nval; + // char *compChamp, *unitChamp, *nomChamp; + //char nomChamp [ MED_NAME_SIZE+1 ] = ""; + // med_field_type typeChamp; + // med_int nGauss, ngpdt, numdt, numo; + med_int nPasTemps; + // char locname[MED_NAME_SIZE + 1] = ""; + // med_geometry_type type_geo; + // med_int ngauss; + char dtunit[MED_SNAME_SIZE + 1] = ""; + // med_float dt = 0.0; + // med_bool local; + // med_int nbrefmaa; + + med_sorting_type sortingtype; + med_axis_type axistype; + + // Initialisations + FAMILLES.clear(); + FAM_TYPES.clear(); + FAMILLES_NOEUDS.clear(); + GROUPES_MAILLES.clear(); + GROUPES_NOEUDS.clear(); + RESIDU.clear(); // Sera initialisé à 1 par la routine acquisitionTYPE_inputMED + tailleFAMILLES.clear(); + tailleGROUPES.clear(); + + // Ouverture du fichier MED en lecture seule + fid = MEDfileOpen(string2char(fichierMED), MED_ACC_RDONLY); + if (fid < 0) + { + ERREUR("Error file open\n"); + } + //cout << chrono() << " --- inputMED: MEDfileOpen: ouverture du maillage en lecture seule, OK" << endl; + + // Lecture des infos concernant le premier maillage + if (MEDmeshInfo(fid, 1, maa, &spacedim, &mdim, &type, desc, dtunit, &sortingtype, &nPasTemps, &axistype, axisname, + unitname) < 0) + ERREUR("Error while reading mesh informations "); + //cout << chrono() << " --- inputMED: MEDmeshInfo: OK" << endl; + +// cerr << "maa=" << maa << endl; +// cerr << "spacedim=" << spacedim << endl; +// cerr << "mdim=" << mdim << endl; +// cerr << "type=" << type << endl; +// cerr << "desc=" << desc << endl; +// cerr << "dtunit=" << dtunit << endl; +// cerr << "sortingtype=" << sortingtype << endl; +// cerr << "nPasTemps=" << nPasTemps << endl; +// cerr << "axistype=" << axistype << endl; +// cerr << "axisname=" << axisname << endl; +// cerr << "unitname=" << unitname << endl; + + dimensionMaillage = mdim; + dimensionEspace = spacedim; + + ID = (string) maa; + + // nGauss = MEDnGauss(fid); + // if (debug > 0) + // cout << "Nombre d'éléments portant des points de Gauss: " << (int) nGauss << endl; + // map REFGAUSS; + // map::iterator iterGAUSS; + // for (igauss = 1; igauss <= nGauss; igauss++) + // { + // if (MEDgaussInfo(fid, igauss, locname, &type_geo, &ngauss) < 0) + // ERREUR("Erreur MEDgaussInfo"); + // if (debug == 2) + // { + // cout << endl << " Retour MEDgaussInfo, localisation gauss n°" << igauss << " : " << endl; + // cout << " locname = " << locname << endl; + // cout << " type_geo = " << type_geo << endl; + // cout << " ngauss = " << ngauss << endl; + // cout << endl; + // } + // REFGAUSS[(string) locname] = (int) ngauss; + // } + // + // if (debug == 2) + // { + // cout << endl << "Restitution de la table REFGAUSS:" << endl; + // for (iterGAUSS = REFGAUSS.begin(); iterGAUSS != REFGAUSS.end(); iterGAUSS++) + // { + // cout << iterGAUSS->first << " : " << iterGAUSS->second << endl; + // } + // } + // + // nprofils = MEDnProfil(fid); + // if (debug) + // cout << endl << endl << "Nombre de profils: " << nprofils << endl; + // for (iprofil = 1; iprofil <= nprofils; iprofil++) + // { + // if (MEDprofilInfo(fid, iprofil, nomprofil, &nvalprofil) < 0) + // ERREUR("ERREUR MEDprofilInfo"); + // nvalprofil2 = MEDnValProfil(fid, nomprofil); + // if (debug == 2) + // { + // cout << "Profil " << iprofil << " : " << endl; + // cout << " Nom profil : " << nomprofil << endl; + // cout << " Nombre de valeurs profil (par MEDprofilInfo) : " << nvalprofil << endl; + // cout << " Nombre de valeurs profil (par MEDnValProfil) : " << nvalprofil2 << endl; + // } + // if (nvalprofil != nvalprofil2) + // ERREUR("Discordance nvalprofil (entre MEDprofilInfo et MEDnValProfil)"); + // pflval = (med_int*) malloc(sizeof(med_int) * nvalprofil); + // if (MEDprofilLire(fid, pflval, nomprofil) < 0) + // ERREUR("ERREUR MEDprofilLire"); + // //cout << " Affichage des 100 premières valeurs:" << endl; + // //for (ival=0;ival 0) + // { + // + // for (ichamp = 1; ichamp <= nChamps; ichamp++) + // { + // //for (ichamp=4; ichamp<=4; ichamp++ ) { + // cout << endl << endl; + // cout << endl << endl << " ====================================================================" << endl; + // cout << endl << endl << " CHAMP " << ichamp << endl; + // cout << endl << endl << " ====================================================================" << endl; + // cout << endl << endl; + // + // nCompChamp = MEDnChamp(fid, ichamp); + // if (nCompChamp < 0) + // ERREUR("Erreur Ncomposantes champ"); + // cout << "Nombre de composantes du champ " << ichamp << " : " << (int) nCompChamp << endl; + // + // nomChamp = (char*) malloc(MED_NAME_SIZE + 1); + // compChamp = (char*) malloc(nCompChamp * MED_SNAME_SIZE + 1); + // unitChamp = (char*) malloc(nCompChamp * MED_SNAME_SIZE + 1); + // + // if (MEDchampInfo(fid, ichamp, nomChamp, &typeChamp, compChamp, unitChamp, nCompChamp) < 0) + // ERREUR("Erreur MEDchampInfo"); + // + // cout << "Infos sur le champ " << ichamp << " : " << endl; + // cout << " Nom: " << (string) nomChamp << endl; + // cout << " Type: " << typeChamp << endl; + // cout << " Noms des composantes: " << (string) compChamp << endl; + // cout << " Unités des composantes: " << (string) unitChamp << endl; + // + // infoChamps((string) "NOEUDS", MED_NODE, MED_NONE, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "POI1", MED_CELL, MED_POINT1, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "SEG2", MED_CELL, MED_SEG2, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "SEG3", MED_CELL, MED_SEG3, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "TRIA3", MED_CELL, MED_TRIA3, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "TRIA6", MED_CELL, MED_TRIA6, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "QUAD4", MED_CELL, MED_QUAD4, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "QUAD8", MED_CELL, MED_QUAD8, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "TETRA4", MED_CELL, MED_TETRA4, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "TETRA10", MED_CELL, MED_TETRA10, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "PYRAM5", MED_CELL, MED_PYRA5, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "PYRAM13", MED_CELL, MED_PYRA13, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "PENTA6", MED_CELL, MED_PENTA6, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "PENTA15", MED_CELL, MED_PENTA15, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "HEXA8", MED_CELL, MED_HEXA8, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // infoChamps((string) "HEXA20", MED_CELL, MED_HEXA20, fid, maa, nomChamp, typeChamp, nCompChamp, REFGAUSS); + // + // } + // + // cout << endl << "Rappel des codes de géométries: " << endl; + // cout << " TETRA4 = " << MED_TETRA4 << endl; + // cout << " PENTA6 = " << MED_PENTA6 << endl; + // cout << " HEXA8 = " << MED_HEXA8 << endl; + // + // } + // else + // cout << "Pas de champs dans ce maillage" << endl; + + // ################################################################################################## + // ################################################################################################## + // + // A C Q U I S I T I O N D E S F A M I L L E S + // + // ################################################################################################## + // ################################################################################################## + + + med_int nFamilles; + char nomGroupeChar[MED_LNAME_SIZE + 1]; + if ((nFamilles = MEDnFamily(fid, maa)) < 0) + ERREUR("ERROR MEDnFamily"); + + // Initialisation des tailles: tailleFAMILLES et tailleGROUPES + + // for (int i = 0; i < nFamilles; i++) + // { + // char nomfam[MED_NAME_SIZE + 1]; + // char *attdes, *gro; + // med_int numfam, *attide, *attval, natt, ngro; + // + // if ((ngro = MEDnFamilyGroup(fid, maa, i + 1)) < 0) + // ERREUR("ERREUR MEDnFamilyGroup"); + // if ((natt = MEDnFamily23Attribute(fid, maa, i + 1)) < 0) + // ERREUR("ERREUR MEDnFamily23Attribute"); + // + // attide = (med_int *) malloc(sizeof(med_int) * natt); + // attval = (med_int *) malloc(sizeof(med_int) * natt); + // attdes = (char *) malloc(MED_COMMENT_SIZE * natt + 1); + // gro = (char *) malloc(MED_LNAME_SIZE * ngro + 1); + // + // if (MEDfamilyInfo(fid, maa, (med_int)(i + 1), nomfam, &numfam, attide, attval, attdes, &natt, gro, &ngro) < 0) + // ERREUR("ERREUR MEDfamilyInfo"); + // + // free(attide); + // free(attval); + // free(attdes); + // free(gro); + // } + + for (int i = 0; i < nFamilles; i++) + { + char nomfam[MED_NAME_SIZE + 1]; + char *attdes, *gro; + med_int numfam, *attide, *attval, natt, ngro; + + if ((ngro = MEDnFamilyGroup(fid, maa, i + 1)) < 0) + ERREUR("ERROR MEDnFamilyGroup"); + if ((natt = MEDnFamily23Attribute(fid, maa, i + 1)) < 0) + ERREUR("ERROR MEDnFamily23Attribute"); + + attide = (med_int *) malloc(sizeof(med_int) * natt); + attval = (med_int *) malloc(sizeof(med_int) * natt); + attdes = (char *) malloc(MED_COMMENT_SIZE * natt + 1); + gro = (char *) malloc(MED_LNAME_SIZE * ngro + 1); + + if (MEDfamilyInfo(fid, maa, (med_int) (i + 1), nomfam, &numfam, gro) < 0) + ERREUR("ERROR MEDfamilyInfo"); + + for (int ig = 1; ig <= ngro; ig++) + { + for (j = 0; j < MED_LNAME_SIZE; j++) + nomGroupeChar[j] = gro[(ig - 1) * MED_LNAME_SIZE + j]; + nomGroupeChar[MED_LNAME_SIZE] = '\0'; + //cout << "Groupe lu : " << (string)nomGroupeChar << endl; + tailleGROUPES[strip((string) nomGroupeChar)]++; + if (numfam > 0) + GROUPES_NOEUDS[strip((string) nomGroupeChar)].push_back((int) numfam); + else if (numfam < 0) + GROUPES_MAILLES[strip((string) nomGroupeChar)].push_back((int) numfam); + } + + free(attide); + free(attval); + free(attdes); + free(gro); + } + + // ################################################################################################## + // ################################################################################################## + // + // A C Q U I S I T I O N D E S N O E U D S + // + // ################################################################################################## + // ################################################################################################## + + // class Noeud *n; + // list listeNoeuds; + // float x, y, z, rx, ry, rz, tx, ty, tz; + string line, IDnoeud; + float x0, x1, y0, y1, z0, z1; + + vector RESIDU_NOEUDS; // Table de vérité du résidu des noeuds + + ostringstream OSCOORD; + + med_int nnoe = 0; // Nbre de noeuds + med_float *coo1; // Table des coordonnées + // char nomcoo[mdim * MED_SNAME_SIZE + 1]; // Table des noms des coordonnées + // char unicoo[mdim * MED_SNAME_SIZE + 1]; // Table des unités des coordonnées + char *nomnoe; + + med_int *numnoe; + med_int *nufano; + // med_grid_type rep; + // med_bool inonoe, inunoe; + // med_int profil[2] = { 2, 3 }; + med_bool coordinatechangement; + med_bool geotransformation; + + // Combien de noeuds a lire ? + nnoe = MEDmeshnEntity(fid, maa, MED_NO_DT, MED_NO_IT, MED_NODE, MED_NO_GEOTYPE, MED_COORDINATE, MED_NO_CMODE, + &coordinatechangement, &geotransformation); + + if (nnoe < 0) + ERREUR("Error while reading number of nodes"); + + nombreNoeudsMaillage = nnoe; + + // Lecture des familles des noeuds + med_int *famNoeuds = (med_int*) malloc(sizeof(med_int) * nnoe); + if (nnoe > 0) + { + if (MEDmeshEntityFamilyNumberRd(fid, maa, MED_NO_DT, MED_NO_IT, MED_NODE, MED_NONE, famNoeuds) < 0) + ERREUR("Error while reading family node number (MEDmeshEntityFamilyNumberRd)"); + } + + /* Allocations memoires */ + if (nnoe > 0) + { + // table des coordonnees - profil : (dimension * nombre de noeuds ) + coo1 = (med_float*) calloc(nnoe * mdim, sizeof(med_float)); + // table des des numeros, des numeros de familles des noeuds - profil : (nombre de noeuds) + numnoe = (med_int*) malloc(sizeof(med_int) * nnoe); + nufano = (med_int*) malloc(sizeof(med_int) * nnoe); + // table des noms des noeuds - profil : (nnoe*MED_SNAME_SIZE+1) + nomnoe = (char*) malloc(MED_SNAME_SIZE * nnoe + 1); + } + + // Lecture des composantes des coordonnees des noeuds + if (nnoe > 0) + if (MEDmeshNodeCoordinateRd(fid, maa, MED_NO_DT, MED_NO_IT, MED_FULL_INTERLACE, coo1) < 0) + ERREUR("Error while reading nodes coordinates"); + + // // Les noeuds ont-ils un nom? un numéro? + // if (nnoe > 0) + // { + // if (MEDnomLire(fid, maa, nomnoe, nnoe, MED_NODE, (med_geometry_type) 0) < 0) + // inonoe = MED_FALSE; + // else + // inonoe = MED_TRUE; + // if (MEDnumLire(fid, maa, numnoe, nnoe, MED_NODE, (med_geometry_type) 0) < 0) + // inunoe = MED_FALSE; + // else + // inunoe = MED_TRUE; + // } + // + // if (inonoe) + // cout << "WARNING input MED : les noms des noeuds sont ignorés" << endl; + // if (inunoe) + // cout << "WARNING input MED : les numéros des noeuds sont ignorés" << endl; + // + // if (inonoe) + // { + // char str[MED_SNAME_SIZE + 1]; + // for (int inoeud = 0; inoeud < nnoe; inoeud++) + // { + // strncpy(str, nomnoe + inoeud * MED_SNAME_SIZE, MED_SNAME_SIZE); + // str[MED_SNAME_SIZE] = '\0'; + // IDS_NOEUDS.push_back((string) str); + // } + // } + // else if (inunoe) + // { + // for (int inoeud = 0; inoeud < nnoe; inoeud++) + // { + // int numnoeud = *(numnoe + inoeud); + // IDS_NOEUDS.push_back((string) "N" + int2string(numnoeud)); + // } + // } + // else + // for (int inoeud = 0; inoeud < nnoe; inoeud++) + // IDS_NOEUDS.push_back((string) "N" + int2string(inoeud + 1)); + // IDS_NOEUDS.resize(nnoe); + + /* ====================================================================== */ + /* BOUCLE SUR LES NOEUDS LUS DANS LE FICHIER MED */ + /* ====================================================================== */ + + // X.resize(nnoe); + // Y.resize(nnoe); + // Z.resize(nnoe); + + // Initialisation de l'enveloppe + x0 = coo1[0]; + x1 = coo1[0]; + y0 = coo1[1]; + y1 = coo1[1]; + if (mdim == 3) + { + z0 = coo1[2]; + z1 = coo1[2]; + } + else + { + z0 = 0.0; + z1 = 0.0; + } + + // Allocation mémoire pour les coordonnées XX YY ZZ + XX = (float*) malloc(sizeof(float) * nombreNoeudsMaillage); + if (mdim > 1) + YY = (float*) malloc(sizeof(float) * nombreNoeudsMaillage); + if (mdim > 2) + ZZ = (float*) malloc(sizeof(float) * nombreNoeudsMaillage); + + for (int i = 0; i < nnoe; i++) + { + + // Chargement des coordonnées X, Y et Z + // Calcul de l'enveloppe du maillage + + FAMILLES_NOEUDS[*(famNoeuds + i)].push_back(i + 1); // ATTENTION! Les num. de noeuds commencent à 1 + tailleFAMILLES[*(famNoeuds + i)]++; + + // IDnoeud = "N"+int2string(i+1); + + float * XXi = XX + i; + float * YYi = YY + i; + float * ZZi = ZZ + i; + + if (mdim == 3) + { + *XXi = (float) coo1[3 * i]; + *YYi = (float) coo1[3 * i + 1]; + *ZZi = (float) coo1[3 * i + 2]; + if (*XXi < x0) + x0 = *XXi; + else if (*XXi > x1) + x1 = *XXi; + if (*YYi < y0) + y0 = *YYi; + else if (*YYi > y1) + y1 = *YYi; + if (*ZZi < z0) + z0 = *ZZi; + else if (*ZZi > z1) + z1 = *ZZi; + } + else if (mdim == 2) + { + *XXi = (float) coo1[2 * i]; + *YYi = (float) coo1[2 * i + 1]; + if (*XXi < x0) + x0 = *XXi; + else if (*XXi > x1) + x1 = *XXi; + if (*YYi < y0) + y0 = *YYi; + else if (*YYi > y1) + y1 = *YYi; + } + else if (mdim == 1) + { + *XXi = (float) coo1[1 * i]; + if (*XXi < x0) + x0 = *XXi; + else if (*XXi > x1) + x1 = *XXi; + } + + // Chargement des numéros de noeuds + // if (inunoe) + // NUM_NOEUDS.push_back(*(numnoe + i)); + // else + // NUM_NOEUDS.push_back(i + 1); + + } // boucle sur les noeuds + + // NUM_NOEUDS.resize(nnoe); + + // Enveloppe du maillage + enveloppeMaillage = new Cube(x0, x1, y0, y1, z0, z1); + + // Libération mémoire + if (nnoe > 0) + { + free(coo1); + free(nomnoe); + free(numnoe); + free(nufano); + } + + // ################################################################################################## + // ################################################################################################## + // + // A C Q U I S I T I O N D E S M A I L L E S + // + // ################################################################################################## + // ################################################################################################## + + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + EFFECTIFS_TYPES[tm] = MEDmeshnEntity(fid, maa, MED_NO_DT, MED_NO_IT, MED_CELL, InstanceMGE(tm), MED_CONNECTIVITY, + MED_NODAL, &coordinatechangement, &geotransformation); + if (EFFECTIFS_TYPES[tm]) + acquisitionTYPE_inputMED(tm, EFFECTIFS_TYPES[tm], fid, maa, mdim); + } + + // Resize des vecteurs des maps FAMILLES et FAM_TYPES + map >::iterator IF; + for (IF = FAMILLES.begin(); IF != FAMILLES.end(); IF++) + { + IF->second.resize(tailleFAMILLES[IF->first]); + FAM_TYPES[IF->first].resize(tailleFAMILLES[IF->first]); + } + + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + nombreMaillesMaillage += EFFECTIFS_TYPES[(TYPE_MAILLE) itm]; + + // ################################################################################################## + // ################################################################################################## + // + // A C Q U I S I T I O N D E S G R O U P E S + // D E M A I L L E S E T D E N O E U D S + // + // ################################################################################################## + // ################################################################################################## + + // ============================================================================================= + // Chargement des groupes dans GM et GN + // ============================================================================================= + + string nomGM; + vector vfam; + map >::iterator iterGRO; + + int cptGM = 0; + for (iterGRO = GROUPES_MAILLES.begin(); iterGRO != GROUPES_MAILLES.end(); iterGRO++) + { + nomGM = iterGRO->first; + vfam = iterGRO->second; + cptGM++; + map effectifGroupeType; + for (unsigned int i = 0; i < vfam.size(); i++) + { + int numf = vfam[i]; + // Parcours simultané des vecteurs FAMILLES[numf] et FAM_TYPES[numfam] pour obtention du num local + // et du type des mailles de la famille numf + for (unsigned int imaille = 0; imaille < FAMILLES[numf].size(); imaille++) + { + TYPE_MAILLE tm = FAM_TYPES[numf][imaille]; + int nl = FAMILLES[numf][imaille]; + GM[nomGM][tm].push_back(nl); + effectifGroupeType[tm]++; + } + } + + // Resize d'un GM + for (map >::iterator I = GM[nomGM].begin(); I != GM[nomGM].end(); I++) + { + TYPE_MAILLE tm = I->first; + GM[nomGM][tm].resize(effectifGroupeType[tm]); + sort(GM[nomGM][tm].begin(), GM[nomGM][tm].end()); + } + } + + int cptGN = 0; + for (iterGRO = GROUPES_NOEUDS.begin(); iterGRO != GROUPES_NOEUDS.end(); iterGRO++) + { + nomGM = iterGRO->first; + vfam = iterGRO->second; + cptGN++; + int cptNoeudsGN = 0; + for (unsigned int i = 0; i < vfam.size(); i++) + { + int numf = vfam[i]; + // Parcours vecteurs FAMILLES_NOEUDS[numf] + for (unsigned int inoeud = 0; inoeud < FAMILLES_NOEUDS[numf].size(); inoeud++) + { + GN[nomGM].push_back(FAMILLES_NOEUDS[numf][inoeud]); + cptNoeudsGN++; + } + } + GN[nomGM].resize(cptNoeudsGN); + sort(GN[nomGM].begin(), GN[nomGM].end()); + } + + MEDfileClose(fid); + // cout << "Fin procédure inputMED" << endl << endl; +} + +void Maillage::acquisitionTYPE_inputMED(TYPE_MAILLE TYPE, int nTYPE, med_idt fid, char maa[MED_NAME_SIZE + 1], + med_int mdim) +{ + + // int taille, numeromaille, numeroFamille; + int numeroFamille; + string line, IDmaille, IDnoeud, typeMaille; + // char str[MED_SNAME_SIZE + 1]; // Conteneur pour un nom de maille + // bool rejetMaille; + med_int tTYPE = (med_int) Nnoeuds(TYPE); + char *nomTYPE = (char*) malloc(MED_SNAME_SIZE * nTYPE + 1); + med_int *numTYPE = (med_int*) malloc(sizeof(med_int) * nTYPE); + med_int *famTYPE = (med_int*) malloc(sizeof(med_int) * nTYPE); + + //med_int *conTYPE = (med_int*) malloc(sizeof(med_int)*tTYPE*nTYPE); + CNX[TYPE] = (int*) malloc(sizeof(int) * tTYPE * nTYPE); + + med_bool inomTYPE, inumTYPE, ifamTYPE; + med_geometry_type typeBanaliseMED = InstanceMGE(TYPE); + + if (MEDmeshElementRd(fid, maa, MED_NO_DT, MED_NO_IT, MED_CELL, typeBanaliseMED, MED_NODAL, MED_FULL_INTERLACE, + CNX[TYPE], &inomTYPE, nomTYPE, &inumTYPE, numTYPE, &ifamTYPE, famTYPE) < 0) + ERREUR("Error while reading elements"); + + // Conversion HL + conversionCNX(CNX[TYPE], TYPE, nTYPE); + + // CON[TYPE].resize(tTYPE * nTYPE); + // for (int i = 0; i < tTYPE * nTYPE; i++) + // CON[TYPE][i] = (int) *(conTYPE + i); + // CNX[TYPE] = (int*) malloc(sizeof(int) * tTYPE * nTYPE); + // for (int i = 0; i < tTYPE * nTYPE; i++) + // *(CNX[TYPE] + i) = (int) *(conTYPE + i); + + for (int i = 0; i < nTYPE; i++) + { + numeroFamille = (int) *(famTYPE + i); + FAMILLES[numeroFamille].push_back(i); + tailleFAMILLES[numeroFamille]++; + FAM_TYPES[numeroFamille].push_back(TYPE); + + // Chargement des numéros de mailles + // if (inumTYPE) + // NUM_MAILLES[TYPE].push_back(*(numTYPE + i)); + // else + // NUM_MAILLES[TYPE].push_back(NGLOBAL(TYPE, i)); + + } + // NUM_MAILLES[TYPE].resize(nTYPE); + + // if (inomTYPE) + // { + // char str[MED_SNAME_SIZE + 1]; + // for (int imaille = 0; imaille < nTYPE; imaille++) + // { + // strncpy(str, nomTYPE + imaille * MED_SNAME_SIZE, MED_SNAME_SIZE); + // str[MED_SNAME_SIZE] = '\0'; + // IDS_MAILLES[TYPE].push_back((string) str); + // } + // } + // else if (inumTYPE) + // { + // for (int imaille = 0; imaille < nTYPE; imaille++) + // { + // int nummaille = *(numTYPE + imaille); + // IDS_MAILLES[TYPE].push_back((string) "M" + int2string(nummaille)); + // } + // } + // else + // { + // for (int imaille = 0; imaille < nTYPE; imaille++) + // { + // IDS_MAILLES[TYPE].push_back((string) "M" + int2string(imaille + 1)); + // } + // } + // IDS_MAILLES[TYPE].resize(nTYPE); +} + +void Maillage::outputMED(std::string fichierMED) +{ + // int i, j, k; + int nTYPE, tTYPE; + string line, s, stype, nomnoeud; + // med_err ret = 0; // Code retour + // int ig, jg; + // cout << endl << endl << "Début procédure outputMED, fichier " << fichierMED << endl; + + // Sortie sur erreur en cas de maillage sans noeuds + if (nombreNoeudsMaillage <= 0) + { + ERREUR("This mesh does not contain any node\n"); /* cout << "Maillage sans noeuds" << endl; */ + } + + // ######################################################################## + // Ouverture du fichier MED et création du maillage + // ######################################################################## + + // Ouverture du fichier MED en création + med_idt fid = MEDfileOpen(string2char(fichierMED), MED_ACC_CREAT); + if (fid < 0) + { + ERREUR("Error MEDfileOpen\n"); + cout << "Error MEDfileOpen" << endl; + } + + // Création du maillage + char maa[MED_NAME_SIZE + 1]; // Nom du maillage de longueur maxi MED_NAME_SIZE + strcpy(maa, string2char(ID)); + + med_int mdim; // Dimension du maillage + if (dimensionMaillage == 0) + { + mdim = 3; + cout << "ATTENTION, dimension 3 attribuée par défaut!" << endl; + } + else + mdim = dimensionMaillage; + med_int spacedim = 3; + if (dimensionEspace) + spacedim = dimensionEspace; + + //med_int profil[2] = { 2, 3 }; + char desc[MED_COMMENT_SIZE + 1]; // Description du maillage + strcpy(desc, string2char(ID)); + med_mesh_type type = MED_UNSTRUCTURED_MESH; +// cerr << "maa=" << maa << endl; +// cerr << "spacedim=" << spacedim << endl; +// cerr << "mdim=" << mdim << endl; +// cerr << "type=" << type << endl; +// cerr << "axisname=" << axisname << endl; +// cerr << "unitname=" << unitname << endl; + if (MEDmeshCr(fid, maa, spacedim, mdim, type, desc, "s", MED_SORT_DTIT, MED_CARTESIAN, axisname, unitname) < 0) + { + ERREUR("Error MEDmeshCr"); + cout << "Error MEDmeshCr" << endl; + } + + // ============================= CREATION FAMILLE ZERO + char nomfam[MED_NAME_SIZE + 1]; + med_int numfam; + // char attdes[MED_COMMENT_SIZE + 1]; + // med_int natt, attide, attval; + int ngro; + // char gro[MED_LNAME_SIZE + 1]; + + strcpy(nomfam, "FAMILLE_0"); + numfam = 0; + if (MEDfamilyCr(fid, maa, nomfam, numfam, 0, MED_NO_GROUP) < 0) + ERREUR("Error MEDfamilyCr (create family 0)"); + + // ######################################################################## + // GROUPES DE NOEUDS + // ######################################################################## + + int nGroupesNoeuds = GN.size(); + + vector > ETIQUETTES_N; + ETIQUETTES_N.resize(nombreNoeudsMaillage); + vector INDEX_N; + INDEX_N.resize(GN.size()); + vector NOMSFAM; + vector > ETIQFAM; + int cptNOMFAM = 0; + map NUMFAMETIQ; // clé = étiquette - valeur = numéros de familles + + + if (nGroupesNoeuds) + { + + // Pérennisation d'un ordre sur les GM dans le vecteur NOMS_GROUPES_MAILLES + vector NOMS_GROUPES_NOEUDS; + for (map >::iterator ITGN = GN.begin(); ITGN != GN.end(); ITGN++) + { + string nomGN = ITGN->first; + NOMS_GROUPES_NOEUDS.push_back(nomGN); + } + NOMS_GROUPES_NOEUDS.resize(GN.size()); + + // Tri des vecteurs de noeuds de GN + for (unsigned int ig = 0; ig < NOMS_GROUPES_NOEUDS.size(); ig++) + sort(GN[NOMS_GROUPES_NOEUDS[ig]].begin(), GN[NOMS_GROUPES_NOEUDS[ig]].end()); + + // Construction des étiquettes (familles) + + // Initialisation des index de groupes + for (unsigned int ig = 0; ig < NOMS_GROUPES_NOEUDS.size(); ig++) + INDEX_N[ig] = 0; + + for (int k = 1; k <= nombreNoeudsMaillage; k++) + { // k: num. global de noeud + int tailleEtiquette = 0; + string etiq = (string) ""; + // Boucle sur les groupes + for (unsigned int ig = 0; ig < NOMS_GROUPES_NOEUDS.size(); ig++) + { + if (INDEX_N[ig] < GN[NOMS_GROUPES_NOEUDS[ig]].size()) + { + string nomgroupe = NOMS_GROUPES_NOEUDS[ig]; + if (k == GN[nomgroupe][INDEX_N[ig]]) + { + // Attention: l'indice 0 dans le vecteur ETIQUETTES correspond + // à l'élément (noeud ou maille) de num. global 1 + // Par ailleurs, le numéro de groupe dans l'étiquette commence à 0 + ETIQUETTES_N[k - 1].push_back(ig); + tailleEtiquette++; + etiq += int2string(ig); + INDEX_N[ig]++; + } + } + } + ETIQUETTES_N[k - 1].resize(tailleEtiquette); + // Stockage de l'étiquette dans NOMSFAM ETIQFAM, si pas déjà stockée + // bool trouve = false; + // for (int i = 0; i < NOMSFAM.size(); i++) + // if (NOMSFAM[i] == ((string) "ETIQUETTE_" + etiq)) + // { + // trouve = true; + // break; + // } + if (!NUMFAMETIQ[etiq] && etiq != (string) "") + { + NOMSFAM.push_back((string) "ETIQN_" + etiq); + ETIQFAM.push_back(ETIQUETTES_N[k - 1]); + NUMFAMETIQ[etiq] = cptNOMFAM + 1; // Famille de noeuds, num>0 + cptNOMFAM++; + } + } + + NOMSFAM.resize(cptNOMFAM); + ETIQFAM.resize(cptNOMFAM); + + // Création des familles de noeuds + for (unsigned int ifam = 0; ifam < NOMSFAM.size(); ifam++) + { + strcpy(nomfam, string2char(NOMSFAM[ifam])); + // Numéro de famille: ifam+1 (positif pour les noeuds + non nul) + numfam = ifam + 1; + ngro = ETIQFAM[ifam].size(); + + // Noms des groupes de la famille: variable nomsGN + char *gro = new char[ngro * MED_LNAME_SIZE + 1]; + int cptGN = 0; + for (unsigned int ign = 0; ign < ETIQFAM[ifam].size(); ign++) + { + string nomGNcourant = NOMS_GROUPES_NOEUDS[ETIQFAM[ifam][ign]]; + // ATTENTION! Il faut mettre à la fin de chaque segment un \0 qui est ensuite écrasé + // par le premier caractère du champ suivant dans le strcat !!!! + if (ign == 0) + { + // Premier groupe + strcpy(gro, string2char(nomGNcourant)); + for (int jg = nomGNcourant.size(); jg < MED_LNAME_SIZE; jg++) + gro[jg] = ' '; + gro[MED_LNAME_SIZE] = '\0'; + } + else + { + strcat(gro, string2char(nomGNcourant)); + for (int jg = nomGNcourant.size(); jg < MED_LNAME_SIZE; jg++) + gro[cptGN * MED_LNAME_SIZE + jg] = ' '; + gro[(cptGN + 1) * MED_LNAME_SIZE] = '\0'; + } + cptGN++; + } + + // Création de la famille + if (MEDfamilyCr(fid, maa, nomfam, numfam, 0, MED_NO_GROUP) < 0) + ERREUR("Error MEDfamilyCr"); + delete gro; + } + + } + + // ######################################################################## + // NOEUDS + // ######################################################################## + + // float x, y, z; + + med_int nnoe = nombreNoeudsMaillage; // Nombre de noeuds + med_float *coo; // Table des coordonnées + + // Noms des coordonnées (variable nomcoo) + char* nomcoo = new char[mdim * MED_SNAME_SIZE + 1]; + string strX = (string) "X"; + while (strX.size() < MED_SNAME_SIZE) + strX += (string) " "; + string strY = (string) "Y"; + while (strY.size() < MED_SNAME_SIZE) + strY += (string) " "; + string strZ = (string) "Z"; + while (strZ.size() < MED_SNAME_SIZE) + strZ += (string) " "; + if (mdim == 3) + strcpy(nomcoo, string2char(strX + strY + strZ)); + else if (mdim == 2) + strcpy(nomcoo, string2char(strX + strY)); + else + strcpy(nomcoo, string2char(strX)); + nomcoo[mdim * MED_SNAME_SIZE] = '\0'; + + // Unités des coordonnées (variable unicoo) + char* unicoo = new char[mdim * MED_SNAME_SIZE + 1]; + string strmesure = (string) "SI"; + while (strmesure.size() < MED_SNAME_SIZE) + strmesure += (string) " "; + if (mdim == 3) + strcpy(unicoo, string2char(strmesure + strmesure + strmesure)); + else if (mdim == 2) + strcpy(unicoo, string2char(strmesure + strmesure)); + else + strcpy(unicoo, string2char(strmesure)); + unicoo[mdim * MED_SNAME_SIZE] = '\0'; + + // Tables des noms, numeros, numeros de familles des noeuds + // autant d'elements que de noeuds - les noms ont pout longueur MED_SNAME_SIZE + char *nomnoe; + med_int *numnoe = NULL; + med_int *nufano; + med_bool inonoe = MED_FALSE; + med_bool inunoe = MED_FALSE; + + // Allocations memoire + if (nnoe > 0) + { + // table des coordonnees - profil : (dimension * nombre de noeuds ) + coo = (med_float*) calloc(nnoe * mdim, sizeof(med_float)); + + // table des des numeros, des numeros de familles des noeuds - profil : (nombre de noeuds) + // numnoe = (med_int*) malloc(sizeof(med_int) * nnoe); + nufano = (med_int*) malloc(sizeof(med_int) * nnoe); + + // table des noms des noeuds - profil : (nnoe*MED_SNAME_SIZE+1) + nomnoe = (char*) ""; // ATTENTION! + + // nomnoe = (char*) malloc(MED_SNAME_SIZE * nnoe + 1); + // for (int inoeud = 0; inoeud < IDS_NOEUDS.size(); inoeud++) + // { + // string nomNoeud = IDS_NOEUDS[inoeud]; + // if (inoeud == 0) + // { + // // Premier groupe + // strcpy(nomnoe, string2char(nomNoeud)); + // for (int jg = nomNoeud.size(); jg < MED_SNAME_SIZE; jg++) + // nomnoe[jg] = ' '; + // nomnoe[MED_SNAME_SIZE] = '\0'; + // } + // else + // { + // strcat(nomnoe, string2char(nomNoeud)); + // for (int jg = nomNoeud.size(); jg < MED_SNAME_SIZE; jg++) + // nomnoe[inoeud * MED_SNAME_SIZE + jg] = ' '; + // nomnoe[(inoeud + 1) * MED_SNAME_SIZE] = '\0'; + // } + // } + } + + // Chargement des coordonnées, numéros de familles et numéros de noeuds + if (dimensionMaillage == 3) + { + int i3 = 0; + for (int i = 0; i < nnoe; i++) + { + // coo[i3] = X[i]; + // coo[i3 + 1] = Y[i]; + // coo[i3 + 2] = Z[i]; + // i3 = i3 + 3; + coo[i3] = *(XX + i); + coo[i3 + 1] = *(YY + i); + coo[i3 + 2] = *(ZZ + i); + i3 = i3 + 3; + + // Numéros de familles - Le num. global de noeud est i+1 + if (nGroupesNoeuds) + { + vector v = ETIQUETTES_N[i]; + string sv = (string) ""; + for (unsigned int j = 0; j < v.size(); j++) + sv += int2string(v[j]); // Etiquette du noeud au format string + // cout << "Noeud " << i + 1 << " : sv=" << sv << endl; + *(nufano + i) = (med_int) NUMFAMETIQ[sv]; + } + else + *(nufano + i) = (med_int) 0; + + // Numéros de noeuds + // *(numnoe + i) = (med_int) NUM_NOEUDS[i]; + } + } + else /* dimension 2 */ + { + int i2 = 0; + for (int i = 0; i < nnoe; i++) + { + coo[i2] = *(XX + i); + coo[i2 + 1] = *(YY + i); + i2 = i2 + 2; + // Numéros de familles - Le num. global de noeud est i+1 + if (nGroupesNoeuds) + { + vector v = ETIQUETTES_N[i]; + string sv = (string) ""; + for (unsigned int j = 0; j < v.size(); j++) + sv += int2string(v[j]); // Etiquette du noeud au format string + // cout << "Noeud " << i + 1 << " : sv=" << sv << endl; + *(nufano + i) = (med_int) NUMFAMETIQ[sv]; + } + else + *(nufano + i) = (med_int) 0; + // Numéros de noeuds + // *(numnoe + i) = (med_int) NUM_NOEUDS[i]; + } + } + + // // Restitution coo + // int i3 = 0; + // for (int i = 0; i < nnoe; i++) + // { + // cout << "Noeud " << i << " : " << coo[i3] << " " << coo[i3 + 1] << " " << coo[i3 + 2] << endl; + // i3 = i3 + 3; + // } + + if (MEDmeshNodeWr(fid, maa, MED_NO_DT, MED_NO_IT, MED_UNDEF_DT, MED_FULL_INTERLACE, nnoe, coo, inonoe, nomnoe, + inunoe, numnoe, MED_TRUE, nufano) < 0) + { + ERREUR("Error MEDmeshNodeWr"); + cout << "Error MEDmeshNodeWr" << endl; + } + + // ######################################################################## + // GROUPES DE MAILLES + // ######################################################################## + + int nGroupesMailles = GM.size(); + + map > > ETIQUETTES_M; // [ tm => [ nl => [ig1, ig2, ... ] ] ] + // INDEX_M : + // Clé : tm + // Valeur : vect. des compteurs par indice de GM dans NOMS_GROUPES_MAILLES + map > INDEX_M; + NOMSFAM.clear(); + ETIQFAM.clear(); + NUMFAMETIQ.clear(); // clé = étiquette - valeur = numéros de familles + cptNOMFAM = 0; + + if (nGroupesMailles) + { + + // Pérennisation d'un ordre sur les GM dans le vecteur NOMS_GROUPES_MAILLES + vector NOMS_GROUPES_MAILLES; + for (map > >::iterator ITGM = GM.begin(); ITGM != GM.end(); ITGM++) + { + string nomGM = ITGM->first; + NOMS_GROUPES_MAILLES.push_back(nomGM); + } + NOMS_GROUPES_MAILLES.resize(GM.size()); + // Tri des vecteurs d'entiers de GM + for (unsigned int ig = 0; ig < NOMS_GROUPES_MAILLES.size(); ig++) + { + string nomGM = NOMS_GROUPES_MAILLES[ig]; + for (map >::iterator I = GM[nomGM].begin(); I != GM[nomGM].end(); I++) + { + TYPE_MAILLE tm = I->first; + sort(GM[nomGM][tm].begin(), GM[nomGM][tm].end()); + } + } + + // Construction des étiquettes (familles) + + // Initialisation 0 des index de groupes, et resize ETIQUETTES_M[tm] + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + if (EFFECTIFS_TYPES[tm]) + { + for (unsigned int ig = 0; ig < NOMS_GROUPES_MAILLES.size(); ig++) + INDEX_M[tm].push_back(0); + ETIQUETTES_M[tm].resize(EFFECTIFS_TYPES[tm]); + } + } + + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + int efftm = EFFECTIFS_TYPES[tm]; + // cout << endl << "*************** coucou ***************" << endl; + // cout << "*************** Type " << TM2string(tm) << " effectif = " << efftm << endl; + if (efftm) + { + // cout << "Traitement du type " << TM2string(tm) << endl; + for (int nl = 0; nl < efftm; nl++) + { + // nl = num. local de la maille dans son type + // cout << "\tMaille " << TM2string(tm) << " n° " << nl << endl; + + int tailleEtiquette = 0; + string etiq = (string) ""; + // Boucle sur les groupes + for (unsigned int ig = 0; ig < NOMS_GROUPES_MAILLES.size(); ig++) + { + string nomGM = NOMS_GROUPES_MAILLES[ig]; + // cout << "\t\t" << "Groupe " << nomGM << endl; + + if (INDEX_M[tm][ig] < GM[nomGM][tm].size()) + { + if (nl == GM[nomGM][tm][INDEX_M[tm][ig]]) + { + // Attention: l'indice 0 dans le vecteur ETIQUETTES correspond + // à l'élément (noeud ou maille) de num. global 1 + // Par ailleurs, le numéro de groupe dans l'étiquette commence à 0 + // cout << "\t\t\t" << "La maille est dans le groupe " << nomGM << endl; + ETIQUETTES_M[tm][nl].push_back(ig); + tailleEtiquette++; + etiq += int2string(ig); + INDEX_M[tm][ig]++; + // cout << "\t\t\t OK" << endl; + } + } + } + + ETIQUETTES_M[tm][nl].resize(tailleEtiquette); + // Stockage de l'étiquette dans NOMSFAM ETIQFAM, si pas déjà stockée + // bool trouve = false; + // for (int i = 0; i < NOMSFAM.size(); i++) + // if (NOMSFAM[i] == ((string) "ETIQUETTE_" + etiq)) + // { + // trouve = true; + // break; + // } + + if (!NUMFAMETIQ[etiq] && etiq != (string) "") + { + NOMSFAM.push_back((string) "ETIQM_" + etiq); + ETIQFAM.push_back(ETIQUETTES_M[tm][nl]); + NUMFAMETIQ[etiq] = -cptNOMFAM - 1; // Famille de mailles, num<0 + cptNOMFAM++; + } + + } + + } // if (efftm) + } + + NOMSFAM.resize(cptNOMFAM); + ETIQFAM.resize(cptNOMFAM); + + // Création des familles de mailles + for (unsigned int ifam = 0; ifam < NOMSFAM.size(); ifam++) + { + strcpy(nomfam, string2char(NOMSFAM[ifam])); + // Numéro de famille: -ifam-1 (négatif pour les mailles, et non nul) + numfam = -ifam - 1; + ngro = ETIQFAM[ifam].size(); + + // Noms des groupes de la famille + char* gro = new char[ngro * MED_LNAME_SIZE + 1]; + int cptGM = 0; + for (unsigned int ign = 0; ign < ETIQFAM[ifam].size(); ign++) + { + string nomGMcourant = NOMS_GROUPES_MAILLES[ETIQFAM[ifam][ign]]; + // ATTENTION! Il faut mettre à la fin de chaque segment un \0 qui est ensuite écrasé + // par le premier caractère du champ suivant dans le strcat !!!! + if (ign == 0) + { + // Premier groupe + strcpy(gro, string2char(nomGMcourant)); + for (int jg = nomGMcourant.size(); jg < MED_LNAME_SIZE; jg++) + gro[jg] = ' '; + gro[MED_LNAME_SIZE] = '\0'; + } + else + { + strcat(gro, string2char(nomGMcourant)); + for (int jg = nomGMcourant.size(); jg < MED_LNAME_SIZE; jg++) + gro[cptGM * MED_LNAME_SIZE + jg] = ' '; + gro[(cptGM + 1) * MED_LNAME_SIZE] = '\0'; + } + cptGM++; + } + + // Création de la famille + if (MEDfamilyCr(fid, maa, nomfam, numfam, 1, gro) < 0) + ERREUR("Error MEDfamilyCr"); + + delete gro; + } + } + + // ######################################################################## + // MAILLES + // ######################################################################## + // Appel de la routine ecritureTypeNew + + med_bool inomTYPE = MED_FALSE; + med_bool inumTYPE = MED_FALSE; + + med_geometry_type MGE; + + for (int itm = (int) POI1; itm <= (int) HEXA20; itm++) + { + TYPE_MAILLE tm = (TYPE_MAILLE) itm; + if (EFFECTIFS_TYPES[tm]) + { + nTYPE = EFFECTIFS_TYPES[tm]; + tTYPE = Nnoeuds(tm); + MGE = InstanceMGE(tm); + stype = TM2string(tm); + + // Noms des mailles + // char *nomTYPE = (char*) malloc(MED_SNAME_SIZE * nTYPE + 1); + // strcpy(nomTYPE, ""); // ATTENTION! + + char *nomTYPE = (char*)""; // Attention! Ne pas faire strcpy ! + + // for (int imaille = 0; imaille < IDS_MAILLES[tm].size(); imaille++) + // { + // string nomMaille = IDS_MAILLES[tm][imaille]; + // if (imaille == 0) + // { + // // Premier groupe + // strcpy(nomTYPE, string2char(nomMaille)); + // for (int jg = nomMaille.size(); jg < MED_SNAME_SIZE; jg++) + // nomTYPE[jg] = ' '; + // nomTYPE[MED_SNAME_SIZE] = '\0'; + // } + // else + // { + // strcat(nomTYPE, string2char(nomMaille)); + // for (int jg = nomMaille.size(); jg < MED_SNAME_SIZE; jg++) + // nomTYPE[imaille * MED_SNAME_SIZE + jg] = ' '; + // nomTYPE[(imaille + 1) * MED_SNAME_SIZE] = '\0'; + // } + // } + + med_int *numTYPE = NULL; // (med_int*) malloc(sizeof(med_int)*nTYPE); + + med_int *famTYPE = (med_int*) malloc(sizeof(med_int) * nTYPE); + + // med_int *conTYPE = (med_int*) malloc(sizeof(med_int) * tTYPE * nTYPE); + // for (int i = 0; i < nTYPE * tTYPE; i++) + // *(conTYPE + i) = (med_int) CON[tm][i]; + + // Chargement famTYPE + if (nGroupesMailles) + { + // Boucle sur les mailles du type (indice = num. local) + for (int nl = 0; nl < nTYPE; nl++) + { + // Construction de l'étiquette de la maille au format string + vector v = ETIQUETTES_M[tm][nl]; + string sv = (string) ""; + for (unsigned int j = 0; j < v.size(); j++) + sv += int2string(v[j]); + // Accès au num. de la famille + *(famTYPE + nl) = (med_int) NUMFAMETIQ[sv]; + } // Boucle sur les mailles du type + } // if (nGroupesMailles) + else + for (int nl = 0; nl < nTYPE; nl++) + *(famTYPE + nl) = (med_int) 0; + + // Formatage MED des CNX + conversionCNX(CNX[tm], tm, nTYPE); + + // Chargement numTYPE + //for (int nl=0; nl listeMaillesSuppr) +{ + map TABLE_NL; // Table des num. locaux dans le type tm + + cout << "Method eliminationMailles, listeMaillesSuppr.size()=" << listeMaillesSuppr.size() << endl; + + // ************* Modification de la connectivité du type concerné + + int* CNX2; + int nNoeudsType = Nnoeuds(tm); + int tailleCNX2 = nNoeudsType * (EFFECTIFS_TYPES[tm] - listeMaillesSuppr.size()); + CNX2 = (int*) malloc(sizeof(int) * tailleCNX2); + // Recopie sélective des connectivités + int isuppr = 0; // indice dans listeMaillesSuppr + int ih2 = 0; // nouveau numéro local ( remarque: ih2 = ih1 - isuppr ) + for (int ih1 = 0; ih1 < EFFECTIFS_TYPES[tm]; ih1++) + { + if (listeMaillesSuppr[isuppr] != ih1) + { + for (int jh1 = 0; jh1 < nNoeudsType; jh1++) + *(CNX2 + nNoeudsType * ih2 + jh1) = *(CNX[tm] + nNoeudsType * ih1 + jh1); + ih2++; + } + else + isuppr++; + } + free(CNX[tm]); + CNX[tm] = CNX2; + + // ************* Construction de la table de correspondance des NL dans le type concerné + unsigned int offset = 0; + for (int i = 0; i < EFFECTIFS_TYPES[tm]; i++) + { + if (offset < listeMaillesSuppr.size()) + { + if (i < listeMaillesSuppr[offset]) + TABLE_NL[i] = i - offset; + else if (i == listeMaillesSuppr[offset]) + { + TABLE_NL[i] = -1; // Element à supprimer + offset++; + } + } + else + TABLE_NL[i] = i - offset; + } + + // Contrôle + if (offset != listeMaillesSuppr.size()) + { + ERREUR("Incoherent offset, method eliminationMailles"); + exit(0); + } + + // ************* Mise à jour du type concerné dans les GM + for (map > >::iterator I = GM.begin(); I != GM.end(); I++) + { + string nomGM = I->first; + + if (GM[nomGM][tm].size()) + { + //cout << "GM[" << nomGM <<"][" << tm << "].size()=" << GM[nomGM][tm].size() << endl; + vector mailles = GM[nomGM][tm]; + vector mailles2; //mailles2.resize(mailles.size()-listeMaillesSuppr.size()); + unsigned int cptMailles = 0; + for (unsigned int i = 0; i < mailles.size(); i++) + { + int nl2 = TABLE_NL[mailles[i]]; + if (nl2 != -1) + { + mailles2.push_back(nl2); + cptMailles++; + } + } + + GM[nomGM][tm].clear(); + mailles2.resize(cptMailles); + GM[nomGM][tm] = mailles2; + + } + } + + // ************* Mise à jour des effectifs + + EFFECTIFS_TYPES[tm] = EFFECTIFS_TYPES[tm] - listeMaillesSuppr.size(); + nombreMaillesMaillage = nombreMaillesMaillage - listeMaillesSuppr.size(); + + TABLE_NL.clear(); +} + diff --git a/src/Tools/MeshCut/MeshCut_Maillage.hxx b/src/Tools/MeshCut/MeshCut_Maillage.hxx new file mode 100644 index 000000000..06dbc5c34 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Maillage.hxx @@ -0,0 +1,165 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_MAILLAGE_HXX__ +#define __MESHCUT_MAILLAGE_HXX__ + +#include "MeshCut_Utils.hxx" + +#include +#include +#include + +namespace MESHCUT + { + class Maillage + { + public: + // Toutes ces variables doivent être placées au niveau du principal pour être connues de toutes les fonctions + + // Vecteurs des familles d'éléments + std::map > FAMILLES; + std::map > FAM_TYPES; + + // Vecteurs des familles de noeuds + std::map > FAMILLES_NOEUDS; + + // Description des groupes (par familles) + std::map > GROUPES_MAILLES; + std::map > GROUPES_NOEUDS; + + // Commun noeuds et mailles + std::map tailleFAMILLES; // la clé est un num. de famille + std::map tailleGROUPES; // Tailles des vecteurs GROUPES_MAILLES et GROUPES_NOEUDS - la clé est un nom de groupe (de noeuds ou de mailles) + + // Résidus mailles + std::map > RESIDU; + + public: + std::string ID; + int nombreNoeudsMaillage; // ****** MED-OBLIGATOIRE ****** + int nombreMaillesMaillage; // ****** MED-OBLIGATOIRE ****** + + class Cube *enveloppeMaillage; + + int dimensionMaillage; // ****** MED-OBLIGATOIRE ****** + int dimensionEspace; // ****** MED-OBLIGATOIRE ****** + + char axisname[3*MED_SNAME_SIZE+1]; // ****** MED-OBLIGATOIRE ****** + char unitname[3*MED_SNAME_SIZE+1]; // ****** MED-OBLIGATOIRE ****** + + float *XX; + float *YY; + float *ZZ; // ****** MED-OBLIGATOIRE ****** + + // Effectifs des éléments par type + std::map EFFECTIFS_TYPES; // ****** MED-OBLIGATOIRE ****** + + // Connectivités des types + // Le numéro global du j-ième noeud de la maille de numéro global i est stocké à l'adresse + // CNX[tm]+t*(i-1)+(j-1) + // (t = taille du type, i.e. nombre de noeuds de l'élément) + std::map CNX; // ****** MED-OBLIGATOIRE ****** + + // Enveloppes cubiques + std::map EC; + + // Description des groupes + std::map > > GM; + std::map > GN; + + // std::vector IDS_NOEUDS; // Indice = num. global - 1 + // std::map > IDS_MAILLES; // Indice = num local de maille dans le type + // + // std::vector NUM_NOEUDS; // Indice = num. global - 1 + // std::map > NUM_MAILLES; // Indice = num local de maille dans le type + + Maillage(std::string _ID); + virtual ~Maillage(); + + void creationGMtype(TYPE_MAILLE tm, std::string nomGMtype); + void afficheMailles(TYPE_MAILLE tm); + void listeMaillesType(TYPE_MAILLE tm); + void listeMaillesTousTypes(); + void listeMaillesParGM(); + void listeMaillesGM(std::string nomGM); + // void listeMaillesGMordonne(std::string nomGM); + void listeNoeuds(); + void listeNoeudsGN(std::string nomGN); + void listeNoeudsGNordonne(std::string nomGN); + std::vector G(int i, TYPE_MAILLE tm); + float distanceNoeudMaille(int ngnoeud, int imaille, TYPE_MAILLE tm); + int noeudVoisin(int ngnoeud, int imaille, TYPE_MAILLE tm); + float distanceNoeudNoeud(int ng1, int ng2); + // void encombrements() + + void inputMED(std::string fichierMED); + void outputMED(std::string fichierMED); + void outputMEDold(std::string fichierMED); + + void inputHL(std::string fichierHL /*, std::string ficDICnoeuds, std::string ficDICmailles, bool DICO */); + void outputHL(std::string fichierHL); + + void outputVRML(std::string ficVRML, float rNoeuds, char *renduAretes, char *renduFaces, float transparence); + // std::string vrmlType(TYPE_MAILLE tm, char *renduAretes, char *renduFaces, float transparence); + // void Maillage::creationGMtype(TYPE_MAILLE tm, std::vector CON_TYPE); + int NGLOBAL(TYPE_MAILLE typeMaille, int nlocal); + int NLOCAL(int nglobal, TYPE_MAILLE tm); + TYPE_MAILLE TYPE(int nglobal); + void eliminationMailles(TYPE_MAILLE typeMaille, std::vector listeMaillesSuppr); + + // acquisitionTYPE_inputMED appelée par inputMED + void + acquisitionTYPE_inputMED(TYPE_MAILLE TYPE, int nTYPE, med_idt fid, char maa[MED_NAME_SIZE + 1], med_int mdim); + + // void infoChamps(std::string type, med_entity_type MEM, med_geometry_type MGE, med_idt fid, char *maa, + // char *nomChamp, med_field_type typeChamp, med_int nCompChamp, std::map REFGAUSS); + + bool NoeudDansHEXA8(int n, int n0, int n1, int n2, int n3, int n4, int n5, int n6, int n7, float epsilon); + bool NoeudDansPENTA6(int n, int n0, int n1, int n2, int n3, int n4, int n5, float epsilon); + bool NoeudDansPYRAM5(int n, int n0, int n1, int n2, int n3, int n4, float epsilon); + bool NoeudDansTETRA4(int n, int n1, int n2, int n3, int n4, float epsilon); + bool NoeudDansQUAD4(int n, int n1, int n2, int n3, int n4, float epsilon); + bool NoeudDansTRIA3(int n, int n1, int n2, int n3, float epsilon); + double volumeTETRA(int n1, int n2, int n3, int n4); + double aireTRIA(int n1, int n2, int n3); + double DET3(int n1, int n2, int n3); + double DET2(int n1, int n2); + void * chargeEnveloppesCubiques(TYPE_MAILLE tm); + void * chargeEnveloppesCarrees(TYPE_MAILLE tm); + bool noeudDeMaille(int ngnoeud, int i, TYPE_MAILLE tm); + bool NoeudDansMaille3D(int n, int i, TYPE_MAILLE tm, float epsilon); + bool NoeudDansMaille2D(int n, int i, TYPE_MAILLE tm, float epsilon); + bool NoeudDansEnveloppeMaille2D(int n, int i, TYPE_MAILLE tm, float epsilon); + bool NoeudDansEnveloppeMaille3D(int n, int i, TYPE_MAILLE tm, float epsilon); + void * afficheEnveloppesCubiques(TYPE_MAILLE tm); + void * afficheEnveloppesCarrees(TYPE_MAILLE tm); + + std::vector noeudsGeomCommuns(int i1, TYPE_MAILLE tm1, int i2, TYPE_MAILLE tm2); + void creationGMresidu(); + + float longueurMoyenne(); + // void Maillage::infoChamps2(std::string type, med_entity_type MEM, med_geometry_type MGE, med_idt fid, + // char *maa, char *nomChamp, med_field_type typeChamp, med_int nCompChamp, map REFGAUSS); + + }; + } + +#endif diff --git a/src/Tools/MeshCut/MeshCut_Utils.cxx b/src/Tools/MeshCut/MeshCut_Utils.cxx new file mode 100644 index 000000000..e7cf9e0b4 --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Utils.cxx @@ -0,0 +1,1104 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 "MeshCut_Utils.hxx" + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MESHCUT; + +//string pathUsers = (string) "/var/www/XMeshLab/users/"; + +bool MESHCUT::estUnTypeMaille(std::string S) +{ + if (S == (string) "POI1" || S == (string) "SEG2" || S == (string) "SEG3" || S == (string) "TRIA3" || S + == (string) "TRIA6" || S == (string) "QUAD4" || S == (string) "QUAD8" || S == (string) "QUAD9" || S + == (string) "TETRA4" || S == (string) "TETRA10" || S == (string) "PYRAM5" || S == (string) "PYRAM13" || S + == (string) "PENTA6" || S == (string) "PENTA15" || S == (string) "HEXA8" || S == (string) "HEXA20" || S + == (string) "HEXA27") + return true; + else + return false; +} + +void MESHCUT::ERREUR(const char* msg) +{ + cout << endl << "====== ERROR ====== " << msg << endl << endl; + exit(-1); +} + +char* MESHCUT::string2char(std::string str) +{ + // créer le buffer pour copier la chaîne + size_t size = str.size() + 1; + char* buffer = new char[size]; + // copier la chaîne + strncpy(buffer, str.c_str(), size); + + // libérer la mémoire + //delete [] buffer; + + return buffer; +} + +std::string MESHCUT::int2string(int k) +{ + std::stringstream oss; + oss << k; + return oss.str(); // oss.seekp (ios_base::beg); +} + +float MESHCUT::char2float(const char* ch) +{ + return atof(ch); +} + +std::string MESHCUT::float2string(float f) +{ + stringstream buf; + buf << fixed << f; + string s = buf.str(); + return s; +} + +bool MESHCUT::appartient(std::string e, std::string tableau[], int taille) +{ + for (int i = 0; i < taille; i++) + if (tableau[i] == e) + return true; + return false; +} + +float MESHCUT::arrondi(float x) +{ + if (x > 0 && x < 1.0e-5) + return 0; + else if (x < 0 && x > -1.0e-5) + return 0; + else + return x; +} + +int MESHCUT::numNoeudPointe(std::string b1, std::string b2, std::string b3) +{ + if (b1 == "1" && b2 == "1" && b3 == "2") + return 2; + else if (b1 == "1" && b2 == "2" && b3 == "1") + return 1; + else if (b1 == "1" && b2 == "2" && b3 == "2") + return 2; + else if (b1 == "2" && b2 == "1" && b3 == "1") + return 3; + else if (b1 == "2" && b2 == "1" && b3 == "2") + return 3; + else if (b1 == "2" && b2 == "2" && b3 == "1") + return 1; + else + return -1; +} + +std::string MESHCUT::strip(std::string S) +{ + if (S.empty()) + return S; + int startIndex = S.find_first_not_of(" "); + int endIndex = S.find_last_not_of(" "); + return S.substr(startIndex, (endIndex - startIndex + 1)); +} + +std::string MESHCUT::entierSur10_g(int i) +{ + if (i > 999999999) + ERREUR("trying to write a number superior to 999999999 on more than 10 chars"); + if (i < 10) + return int2string(i) + (string) " "; + else if (i < 100) + return int2string(i) + (string) " "; + else if (i < 1000) + return int2string(i) + (string) " "; + else if (i < 10000) + return int2string(i) + (string) " "; + else if (i < 100000) + return int2string(i) + (string) " "; + else if (i < 1000000) + return int2string(i) + (string) " "; + else if (i < 10000000) + return int2string(i) + (string) " "; + else if (i < 100000000) + return int2string(i) + (string) " "; + else if (i < 1000000000) + return int2string(i) + (string) " "; + else + return int2string(i); +} + +std::string MESHCUT::entierSur10_d(int i) +{ + if (i > 999999999) + ERREUR("trying to write a number superior to 999999999 on more than 10 chars"); + if (i < 10) + return (string) " " + int2string(i); + else if (i < 100) + return (string) " " + int2string(i); + else if (i < 1000) + return (string) " " + int2string(i); + else if (i < 10000) + return (string) " " + int2string(i); + else if (i < 100000) + return (string) " " + int2string(i); + else if (i < 1000000) + return (string) " " + int2string(i); + else if (i < 10000000) + return (string) " " + int2string(i); + else if (i < 100000000) + return (string) " " + int2string(i); + else if (i < 1000000000) + return (string) " " + int2string(i); + else + return int2string(i); +} + +std::string MESHCUT::typeEnsight(std::string type) +{ + if (type == (string) "POI1") + return (string) "point"; + else if (type == (string) "SEG2") + return (string) "bar2"; + else if (type == (string) "SEG3") + return (string) "bar2";// ATTENTION, triche! + else if (type == (string) "TRIA3") + return (string) "tria3"; + else if (type == (string) "TRIA6") + return (string) "tria3";// ATTENTION, triche! + else if (type == (string) "QUAD4") + return (string) "quad4"; + else if (type == (string) "QUAD8") + return (string) "quad4"; // ATTENTION, triche! + else if (type == (string) "QUAD9") + ERREUR("Type QUAD9 not supported by Ensight"); + else if (type == (string) "TETRA4") + return (string) "tetra4"; + else if (type == (string) "TETRA10") + return (string) "tetra4"; // ATTENTION, triche! + else if (type == (string) "PYRAM5") + return (string) "pyramid5"; + else if (type == (string) "PYRAM13") + return (string) "pyramid5"; // ATTENTION, triche! + else if (type == (string) "PENTA6") + return (string) "penta6"; + else if (type == (string) "PENTA15") + return (string) "penta6"; // ATTENTION, triche! + else if (type == (string) "HEXA8") + return (string) "hexa8"; + else if (type == (string) "HEXA20") + return (string) "hexa8"; // ATTENTION, triche! + else if (type == (string) "HEXA27") + ERREUR("Type HEXA27 not supported by Ensight"); + else + ERREUR("Type of element not accepted (method \"typeEnsight\""); + return (string) ""; +} + +int MESHCUT::Nnoeuds(TYPE_MAILLE type) +{ + switch (type) + { + case POI1: + { + return 1; + break; + } + case SEG2: + { + return 2; + break; + } + case SEG3: + { + return 3; + break; + } + case TRIA3: + { + return 3; + break; + } + case TRIA6: + { + return 6; + break; + } + case QUAD4: + { + return 4; + break; + } + case QUAD8: + { + return 8; + break; + } + //case QUAD9: { return 9; break; } + case TETRA4: + { + return 4; + break; + } + case TETRA10: + { + return 10; + break; + } + case PYRAM5: + { + return 5; + break; + } + case PYRAM13: + { + return 13; + break; + } + case PENTA6: + { + return 6; + break; + } + case PENTA15: + { + return 15; + break; + } + case HEXA8: + { + return 8; + break; + } + case HEXA20: + { + return 20; + break; + } + //case HEXA27: { return 27; break; } + default: + ERREUR("Type of elem not accepted (method Nnoeuds)"); + } + return 0; +} + +int MESHCUT::NnoeudsGeom(TYPE_MAILLE type) +{ + switch (type) + { + case POI1: + { + return 1; + break; + } + case SEG2: + { + return 2; + break; + } + case SEG3: + { + return 2; + break; + } + case TRIA3: + { + return 3; + break; + } + case TRIA6: + { + return 3; + break; + } + case QUAD4: + { + return 4; + break; + } + case QUAD8: + { + return 4; + break; + } + //case QUAD9: { return 9; break; } + case TETRA4: + { + return 4; + break; + } + case TETRA10: + { + return 4; + break; + } + case PYRAM5: + { + return 5; + break; + } + case PYRAM13: + { + return 5; + break; + } + case PENTA6: + { + return 6; + break; + } + case PENTA15: + { + return 6; + break; + } + case HEXA8: + { + return 8; + break; + } + case HEXA20: + { + return 8; + break; + } + //case HEXA27: { return 27; break; } + default: + ERREUR("Type of elem not accepted (method NnoeudsGeom)"); + } + return 0; +} + +int MESHCUT::codeGMSH(std::string type) +{ + if (type == (string) "POI1") + ERREUR("POI1 not taken into account by GMSH"); + else if (type == (string) "SEG2") + return 1; + else if (type == (string) "SEG3") + return 8; + else if (type == (string) "TRIA3") + return 2; + else if (type == (string) "TRIA6") + return 9; + else if (type == (string) "QUAD4") + return 3; + else if (type == (string) "QUAD8") + return 16; + else if (type == (string) "QUAD9") + return 10; + else if (type == (string) "TETRA4") + return 4; + else if (type == (string) "TETRA10") + return 11; + else if (type == (string) "PYRAM5") + return 7; + else if (type == (string) "PENTA6") + return 6; + else if (type == (string) "PENTA15") + return 18; + else if (type == (string) "HEXA8") + return 5; + else if (type == (string) "HEXA20") + return 17; + else if (type == (string) "HEXA27") + return 12; + else + ERREUR("Type of elem not accepted (method codeGMSH)"); + return 0; +} + +std::string MESHCUT::floatEnsight(float x) +{ + char buf[12]; + string s; + if (x < 0.0) + sprintf(buf, "%1.5E", x); + else + sprintf(buf, " %1.5E", x); + s = (string) buf; + s.erase(10, 1); + return s; +} + +bool MESHCUT::typeComplexe(std::string type) +{ + if (type == (string) "SEG3") + return true; + else if (type == (string) "TRIA6") + return true; + else if (type == (string) "QUAD8") + return true; + else if (type == (string) "QUAD9") + return true; + else if (type == (string) "TETRA10") + return true; + else if (type == (string) "PYRAM13") + return true; + else if (type == (string) "PENTA15") + return true; + else if (type == (string) "HEXA20") + return true; + else if (type == (string) "HEXA27") + return true; + else + return false; +} + +std::string MESHCUT::ASTER8(std::string s) +{ + if (s.size() == 0) + return (s + (string) " "); + else if (s.size() == 1) + return (s + (string) " "); + else if (s.size() == 2) + return (s + (string) " "); + else if (s.size() == 3) + return (s + (string) " "); + else if (s.size() == 4) + return (s + (string) " "); + else if (s.size() == 5) + return (s + (string) " "); + else if (s.size() == 6) + return (s + (string) " "); + else if (s.size() == 7) + return (s + (string) " "); + else if (s.size() == 8) + return (s); + else + ERREUR("More than 8 char for an ASTER string"); + return (s); +} + +/*! + * Distance à laquelle doit se tenir l'observateur sur un axe + * pour voir sous 90° un objet centré de dimensions a et b selon les deux autres axes. + * Si on ne tient pas compte de la dimension de l'objet selon l'axe choisi, + * la formule d_obs=max(a,b)/2 donne la cote + * qui permet de voir l'objet plat dans un angle de 90°. + * A cela il faut ajouter la dimension de l'objet selon l'axe d'observation = c. + * + * @param a dimensions de l'objet selon un des axes normal à l'axe d'observation + * @param b dimensions de l'objet selon l'autre axe normal à l'axe d'observation + * @param c est la dimension de l'objet selon l'axe d'observation + */ +float MESHCUT::dObservateur(float a, float b, float c) +{ + return (max(a, b) / 2.0 + c); +} + +int MESHCUT::copieFichier(std::string source, std::string cible) +{ + FILE *fsource, *fcible; + char buffer[512]; + int NbLu; + if ((fsource = fopen(string2char(source), "rb")) == NULL) + return -1; + if ((fcible = fopen(string2char(cible), "wb")) == NULL) + { + fclose(fsource); + return -2; + } + while ((NbLu = fread(buffer, 1, 512, fsource)) != 0) + fwrite(buffer, 1, NbLu, fcible); + fclose(fcible); + fclose(fsource); + return 0; +} + +med_geometry_type MESHCUT::InstanceMGE(TYPE_MAILLE TYPE) +{ + med_geometry_type typeBanaliseMED; + + switch (TYPE) + { + case POI1: + typeBanaliseMED = MED_POINT1; + break; // Attention, piège ! + case SEG2: + typeBanaliseMED = MED_SEG2; + break; + case SEG3: + typeBanaliseMED = MED_SEG3; + break; + case TRIA3: + typeBanaliseMED = MED_TRIA3; + break; + case TRIA6: + typeBanaliseMED = MED_TRIA6; + break; + case QUAD4: + typeBanaliseMED = MED_QUAD4; + break; + case QUAD8: + typeBanaliseMED = MED_QUAD8; + break; + case TETRA4: + typeBanaliseMED = MED_TETRA4; + break; + case TETRA10: + typeBanaliseMED = MED_TETRA10; + break; + case PYRAM5: + typeBanaliseMED = MED_PYRA5; + break; // Attention, piège ! + case PYRAM13: + typeBanaliseMED = MED_PYRA13; + break; // Attention, piège ! + case PENTA6: + typeBanaliseMED = MED_PENTA6; + break; + case PENTA15: + typeBanaliseMED = MED_PENTA15; + break; + case HEXA8: + typeBanaliseMED = MED_HEXA8; + break; + case HEXA20: + typeBanaliseMED = MED_HEXA20; + break; + default: + ERREUR("Method InstanceMGE, unknown type "); + } + return typeBanaliseMED; +} + +int MESHCUT::chrono() +{ + return clock() / CLOCKS_PER_SEC; +} + +TYPE_MAILLE MESHCUT::typeMaille(std::string type) +{ + if (type == (string) "POI1") + return POI1; + else if (type == (string) "SEG2") + return SEG2; + else if (type == (string) "SEG3") + return SEG3; + else if (type == (string) "TRIA3") + return TRIA3; + else if (type == (string) "TRIA6") + return TRIA6; + else if (type == (string) "QUAD4") + return QUAD4; + else if (type == (string) "QUAD8") + return QUAD8; + else if (type == (string) "TETRA4") + return TETRA4; + else if (type == (string) "TETRA10") + return TETRA10; + else if (type == (string) "PYRAM5") + return PYRAM5; + else if (type == (string) "PYRAM13") + return PYRAM13; + else if (type == (string) "PENTA6") + return PENTA6; + else if (type == (string) "PENTA15") + return PENTA15; + else if (type == (string) "HEXA8") + return HEXA8; + else if (type == (string) "HEXA20") + return HEXA20; + else + ERREUR("ERROR method typeMaille, unknown type"); + return POI1; +} + +std::string MESHCUT::MGE2string(med_geometry_type MGE) +{ + if (MGE == MED_NONE) + return (string) "NOEUD"; + else if (MGE == MED_POINT1) + return (string) "POI1"; + else if (MGE == MED_SEG2) + return (string) "SEG2"; + else if (MGE == MED_SEG3) + return (string) "SEG3"; + else if (MGE == MED_TRIA3) + return (string) "TRIA3"; + else if (MGE == MED_TRIA6) + return (string) "TRIA6"; + else if (MGE == MED_QUAD4) + return (string) "QUAD4"; + else if (MGE == MED_QUAD8) + return (string) "QUAD8"; + else if (MGE == MED_TETRA4) + return (string) "TETRA4"; + else if (MGE == MED_TETRA10) + return (string) "TETRA10"; + else if (MGE == MED_PYRA5) + return (string) "PYRAM5"; + else if (MGE == MED_PYRA13) + return (string) "PYRAM13"; + else if (MGE == MED_PENTA6) + return (string) "PENTA6"; + else if (MGE == MED_PENTA15) + return (string) "PENTA15"; + else if (MGE == MED_HEXA8) + return (string) "HEXA8"; + else if (MGE == MED_HEXA20) + return (string) "HEXA20"; + else + ERREUR("ERROR method MGE2string, unknown type"); + return (string) "NOEUD"; +} + +std::string MESHCUT::TM2string(TYPE_MAILLE MGE) +{ + if (MGE == POI1) + return (string) "POI1"; + else if (MGE == SEG2) + return (string) "SEG2"; + else if (MGE == SEG3) + return (string) "SEG3"; + else if (MGE == TRIA3) + return (string) "TRIA3"; + else if (MGE == TRIA6) + return (string) "TRIA6"; + else if (MGE == QUAD4) + return (string) "QUAD4"; + else if (MGE == QUAD8) + return (string) "QUAD8"; + else if (MGE == TETRA4) + return (string) "TETRA4"; + else if (MGE == TETRA10) + return (string) "TETRA10"; + else if (MGE == PYRAM5) + return (string) "PYRAM5"; + else if (MGE == PYRAM13) + return (string) "PYRAM13"; + else if (MGE == PENTA6) + return (string) "PENTA6"; + else if (MGE == PENTA15) + return (string) "PENTA15"; + else if (MGE == HEXA8) + return (string) "HEXA8"; + else if (MGE == HEXA20) + return (string) "HEXA20"; + else + ERREUR("ERROR method TM2string, unknown type"); + return (string) "POI1"; +} + +TYPE_MAILLE MESHCUT::string2TM(std::string stm) +{ + if (stm == (string) "POI1") + return POI1; + else if (stm == (string) "SEG2") + return SEG2; + else if (stm == (string) "SEG3") + return SEG3; + else if (stm == (string) "TRIA3") + return TRIA3; + else if (stm == (string) "TRIA6") + return TRIA6; + else if (stm == (string) "QUAD4") + return QUAD4; + else if (stm == (string) "QUAD8") + return QUAD8; + else if (stm == (string) "TETRA4") + return TETRA4; + else if (stm == (string) "TETRA10") + return TETRA10; + else if (stm == (string) "PYRAM5") + return PYRAM5; + else if (stm == (string) "PYRAM13") + return PYRAM13; + else if (stm == (string) "PENTA6") + return PENTA6; + else if (stm == (string) "PENTA15") + return PENTA15; + else if (stm == (string) "HEXA8") + return HEXA8; + else if (stm == (string) "HEXA20") + return HEXA20; + else + ERREUR("ERROR method string2TM, unknown type"); + return POI1; +} + +std::string MESHCUT::coordIndex_ILS(TYPE_MAILLE tm) +{ + if (tm == SEG2) + return (string) " 0,1 "; + else if (tm == SEG3) + return (string) " 0,1 "; // Idem SEG2 + else if (tm == TRIA3) + return (string) " 0,1,2,0 "; + else if (tm == TRIA6) + return (string) " 0,1,2,0 "; + else if (tm == QUAD4) + return (string) " 0,1,2,3,0 "; + else if (tm == QUAD8) + return (string) " 0,1,2,3,0 "; + else if (tm == TETRA4) + return (string) " 0,1,2,0,-1, 0,3,-1, 1,3,-1, 2,3,-1 "; + else if (tm == TETRA10) + return (string) " 0,1,2,0,-1, 0,3,-1, 1,3,-1, 2,3,-1 "; + else if (tm == PYRAM5) + return (string) " 0,1,2,3,0,-1, 0,4,-1, 1,4,-1, 2,4,-1, 3,4,-1 "; + else if (tm == PYRAM13) + return (string) " 0,1,2,3,0,-1, 0,4,-1, 1,4,-1, 2,4,-1, 3,4,-1 "; + else if (tm == PENTA6) + return (string) " 0,1,2,0,-1, 3,4,5,3,-1, 0,3,-1, 1,4,-1, 2,5,-1 "; + else if (tm == PENTA15) + return (string) " 0,1,2,0,-1, 3,4,5,3,-1, 0,3,-1, 1,4,-1, 2,5,-1 "; + else if (tm == HEXA8) + return (string) " 0,1,2,3,0,-1, 4,5,6,7,4,-1, 0,4,-1, 1,5,-1, 2,6,-1, 3,7,-1 "; + else if (tm == HEXA20) + return (string) " 0,1,2,3,0,-1, 4,5,6,7,4,-1, 0,4,-1, 1,5,-1, 2,6,-1, 3,7,-1 "; + else + return (string) ""; +} + +std::string MESHCUT::coordIndex_IFS(TYPE_MAILLE tm) +{ + if (tm == SEG2) + return (string) " "; + else if (tm == SEG3) + return (string) " "; // Idem SEG2 + else if (tm == TRIA3) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1 "; + else if (tm == TRIA6) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1 "; + else if (tm == QUAD4) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1 "; + else if (tm == QUAD8) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1 "; + else if (tm == TETRA4) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1, 0,3,1,0,-1, 0,1,3,0,-1, 1,3,2,1,-1, 1,2,3,1,-1, 0,2,3,0,-1, 0,3,2,0,-1 "; + else if (tm == TETRA10) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1, 0,3,1,0,-1, 0,1,3,0,-1, 1,3,2,1,-1, 1,2,3,1,-1, 0,2,3,0,-1, 0,3,2,0,-1 "; + else if (tm == PYRAM5) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1, 0,1,4,0,-1, 0,4,1,0,-1, 1,2,4,1,-1, 1,4,2,1,-1, 2,4,3,2,-1, 2,3,4,2,-1, 3,4,0,3,-1, 3,0,4,3,-1 "; + else if (tm == PYRAM13) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1, 0,1,4,0,-1, 0,4,1,0,-1, 1,2,4,1,-1, 1,4,2,1,-1, 2,4,3,2,-1, 2,3,4,2,-1, 3,4,0,3,-1, 3,0,4,3,-1 "; + else if (tm == PENTA6) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1, 3,4,5,3,-1, 3,5,4,3,-1, 0,1,4,3,0,-1, 0,3,4,1,0,-1, 1,4,5,2,1,-1, 1,2,5,4,1,-1, 0,3,5,2,0,-1, 0,2,5,3,0,-1 "; + else if (tm == PENTA15) + return (string) " 0,1,2,0,-1, 0,2,1,0,-1, 3,4,5,3,-1, 3,5,4,3,-1, 0,1,4,3,0,-1, 0,3,4,1,0,-1, 1,4,5,2,1,-1, 1,2,5,4,1,-1, 0,3,5,2,0,-1, 0,2,5,3,0,-1 "; + else if (tm == HEXA8) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1, 1,5,6,2,1,-1, 1,2,6,5,1,-1, 5,4,7,6,5,-1, 5,6,7,4,5,-1, 4,0,3,7,4,-1, 4,7,3,0,4,-1, 0,4,5,1,0,-1, 0,1,5,4,0,-1, 3,7,6,2,3,-1, 3,2,6,7,3,-1 "; + else if (tm == HEXA20) + return (string) " 0,1,2,3,0,-1, 0,3,2,1,0,-1, 1,5,6,2,1,-1, 1,2,6,5,1,-1, 5,4,7,6,5,-1, 5,6,7,4,5,-1, 4,0,3,7,4,-1, 4,7,3,0,4,-1, 0,4,5,1,0,-1, 0,1,5,4,0,-1, 3,7,6,2,3,-1, 3,2,6,7,3,-1 "; + else + return (string) ""; +} + +std::string MESHCUT::SIGNE(double x) +{ + if (x < 0) + return "-"; + else if (x > 0) + return "+"; + else + return "0"; +} + +void MESHCUT::champType(std::string type, med_entity_type MEM, med_geometry_type MGE, med_idt fid, med_idt fidout, + char *maa, char *nomChamp, char *nomChampMoy, med_field_type typeChamp, char *compChamp, + char *unitChamp, med_int nCompChamp, std::map REFGAUSS, int ichamp) +{ + + bool debug = true; + int ipt, nmailles, ngauss, imaille, igauss, icomp; + // int ival, ngpdt; + med_int nval, numdt, numo, nPasTemps; + char dtunit[MED_SNAME_SIZE + 1] = ""; + char locname[MED_NAME_SIZE + 1] = ""; + char nomprofil[MED_NAME_SIZE + 1] = ""; + med_float dt = 0.0; + med_float *valr = NULL; + med_float *valr2 = NULL; + med_bool local; + // med_int nbrefmaa; + med_field_type fieldType; + + if (MEDfieldInfo(fid, ichamp, nomChamp, maa, &local, &fieldType, compChamp, unitChamp, dtunit, &nPasTemps) < 0) + ERREUR("Error MEDfieldInfo"); + cout << type << " : " << (int) nPasTemps << " timestep " << endl; + + for (ipt = 1; ipt <= nPasTemps; ipt++) + { + //for (ipt=1; ipt<=min(nPasTemps,1); ipt++) { + if (debug) + cout << endl; + if (debug) + cout << "************************************************************" << endl; + if (debug) + cout << " FIELD " << ichamp << endl; + if (debug) + cout << " " << nomChamp << endl; + if (debug) + cout << " " << type << " --- Timestep " << ipt << endl; + if (debug) + cout << "************************************************************" << endl; + if (debug) + cout << endl; + + if (MEDfieldComputingStepInfo(fid, nomChamp, ipt, &numdt, &numo, &dt) < 0) + { + cout << endl; + cout << endl << "####################################################################" << endl; + cout << " ERROR MEDpasdetempsInfo " << endl; + cout << endl << "####################################################################" << endl; + cout << " Field: " << (string) nomChamp << endl; + cout << " Geometrie: " << MGE2string(MGE) << endl; + cout << " Timestep " << ipt << " ignored" << endl; + + continue; + } + + med_int profilesize, nintegrationpoint; + nval = MEDfieldnValueWithProfile(fid, nomChamp, numdt, numo, MEM, MGE, ipt, MED_COMPACT_PFLMODE, nomprofil, + &profilesize, locname, &nintegrationpoint); + if (debug) + cout << " Number of values in this timestep: " << (int) nval << endl; + + if (typeChamp == MED_FLOAT64) + valr = (med_float*) calloc(nCompChamp * nval, sizeof(med_float)); + else + ERREUR("Type of field not taken into account"); + + if (MEDfieldValueWithProfileRd(fid, maa, numdt, numo, MEM, MGE, MED_COMPACT_PFLMODE, nomprofil, + MED_FULL_INTERLACE, MED_ALL_CONSTITUENT, (unsigned char*) valr) < 0) + { + cout << endl; + cout << endl << "####################################################################" << endl; + cout << " ERROR MEDchampLire " << endl; + cout << endl << "####################################################################" << endl; + cout << endl; + cout << " Field: " << (string) nomChamp << endl; + cout << " Geometry: " << MGE2string(MGE) << endl; + cout << " Timestep " << ipt << " ignored" << endl; + cout << endl << endl; + continue; + } + + if (debug) + cout << " profile = " << (string) nomprofil << endl; + // Localisation du champ aux points de Gauss + if (debug) + cout << " locname = " << (string) locname << endl; + + if (REFGAUSS[(string) locname]) + { + ngauss = REFGAUSS[(string) locname]; + if (debug) + cout << " " << ngauss << " Gauss points by element)" << endl; + } + else + ngauss = 1; + + nmailles = nval / ngauss; + if (debug) + cout << " Nbre de mailles: " << nmailles << endl; + + if (debug) + { + cout << endl << " Liste des valeurs du champ brut aux 3 premiers éléments:" << endl; + for (imaille = 0; imaille < min(nmailles, 3); imaille++) + { + cout << " Maille " << imaille << endl; + for (igauss = 0; igauss < ngauss; igauss++) + { + cout << " PG " << igauss << " : "; + for (icomp = 0; icomp < nCompChamp; icomp++) + cout << " " << *(valr + imaille * ngauss * nCompChamp + igauss * nCompChamp + icomp); + cout << endl; + } + cout << endl; + } + cout << endl; + } + + if (ngauss > 1) + { + + valr2 = (med_float*) calloc(nCompChamp * nmailles, sizeof(med_float)); + + if (debug) + cout << endl << " Moyenne sur les PG des mailles" << endl; + for (imaille = 0; imaille < nmailles; imaille++) + { + for (icomp = 0; icomp < nCompChamp; icomp++) + { + float valCompMaille = 0.0; + for (igauss = 0; igauss < ngauss; igauss++) + valCompMaille += *(valr + imaille * ngauss * nCompChamp + igauss * nCompChamp + icomp); + *(valr2 + imaille * nCompChamp + icomp) = valCompMaille / ngauss; + + } + } + + //cout << endl << "Nom champ moy = " << (string)nomChampMoy << endl; + //cout << endl << "Type champ = " << typeChamp << endl; + //cout << endl << "Comp champ = " << (string)compChamp << endl; + //cout << endl << "Unit champ = " << (string)unitChamp << endl; + //cout << endl << "N comp champ = " << nCompChamp << endl; + + if (MEDfieldValueWithProfileWr(fidout, nomChampMoy, numdt, numo, dt, MEM, MGE, MED_COMPACT_PFLMODE, + nomprofil, MED_NO_LOCALIZATION, MED_FULL_INTERLACE, MED_ALL_CONSTITUENT, + (med_int) nmailles, (unsigned char*) valr2) < 0) + { + cout << endl; + cout << endl << "********************************************************************" << endl; + cout << "******************** ***********************" << endl; + cout << "******************** ERROR MEDchampEcr ***********************" << endl; + cout << "******************** ***********************" << endl; + cout << "********************************************************************" << endl; + cout << endl; + cout << " Champ: " << (string) nomChampMoy << endl; + cout << " Géométrie: " << MGE2string(MGE) << endl; + cout << " Pas de temps " << ipt << " ignoré" << endl; + cout << endl << endl; + continue; + } + + if (debug) + cout << " Writing mean values in new field: OK " << endl; + + // Restitution du champ moyenné + if (debug) + { + cout << endl << " Liste des valeurs du champ moyenné aux 3 premiers éléments:" << endl; + for (imaille = 0; imaille < min(nmailles, 3); imaille++) + { + cout << " Maille " << imaille << endl; + for (icomp = 0; icomp < nCompChamp; icomp++) + cout << " " << *(valr2 + imaille * nCompChamp + icomp); + cout << endl; + } + cout << endl; + } + + } + + free(valr); + free(valr2); + + } // boucle sur les pas de temps + + cout << endl; +} + +std::string MESHCUT::nomMaille(TYPE_MAILLE tm, int nl) +{ + return (TM2string(tm) + (string) "_" + int2string(nl)); +} + +bool MESHCUT::appartientVN(int n, std::vector V) +{ + bool app = false; + for (unsigned int i = 0; i < V.size(); i++) + if (n == V[i]) + { + app = true; + break; + } + return app; +} + +float MESHCUT::distance2(float x1, float y1, float z1, float x2, float y2, float z2) +{ + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1) + (z2 - z1) * (z2 - z1); +} + +/*! + * Conversion HL-MED d'une table de connectivités + */ +void MESHCUT::conversionCNX(int *CNXtm, TYPE_MAILLE tm, int N) +{ + + int n = Nnoeuds(tm); + + if (tm == TETRA4) + { + for (int i = 0; i < N; i++) + { + int i1 = CNXtm[i * n + 1]; + int i2 = CNXtm[i * n + 2]; + CNXtm[i * n + 1] = i2; + CNXtm[i * n + 2] = i1; + } + } + else if (tm == PYRAM5) + { + for (int i = 0; i < N; i++) + { + int i1 = CNXtm[i * n + 1]; + int i3 = CNXtm[i * n + 3]; + CNXtm[i * n + 1] = i3; + CNXtm[i * n + 3] = i1; + } + } + else if (tm == PENTA6) + { + for (int i = 0; i < N; i++) + { + int i0 = CNXtm[i * n + 0]; + int i1 = CNXtm[i * n + 1]; + int i2 = CNXtm[i * n + 2]; + int i3 = CNXtm[i * n + 3]; + int i4 = CNXtm[i * n + 4]; + int i5 = CNXtm[i * n + 5]; + CNXtm[i * n + 0] = i3; + CNXtm[i * n + 1] = i4; + CNXtm[i * n + 2] = i5; + CNXtm[i * n + 3] = i0; + CNXtm[i * n + 4] = i1; + CNXtm[i * n + 5] = i2; + } + } + + else if (tm == HEXA8) + { + for (int i = 0; i < N; i++) + { + int i0 = CNXtm[i * n + 0]; + int i1 = CNXtm[i * n + 1]; + int i2 = CNXtm[i * n + 2]; + int i3 = CNXtm[i * n + 3]; + int i4 = CNXtm[i * n + 4]; + int i5 = CNXtm[i * n + 5]; + int i6 = CNXtm[i * n + 6]; + int i7 = CNXtm[i * n + 7]; + CNXtm[i * n + 0] = i4; + CNXtm[i * n + 1] = i5; + CNXtm[i * n + 2] = i6; + CNXtm[i * n + 3] = i7; + CNXtm[i * n + 4] = i0; + CNXtm[i * n + 5] = i1; + CNXtm[i * n + 6] = i2; + CNXtm[i * n + 7] = i3; + } + } +} + diff --git a/src/Tools/MeshCut/MeshCut_Utils.hxx b/src/Tools/MeshCut/MeshCut_Utils.hxx new file mode 100644 index 000000000..36eb22d0f --- /dev/null +++ b/src/Tools/MeshCut/MeshCut_Utils.hxx @@ -0,0 +1,81 @@ +// Copyright (C) 2006-2012 EDF R&D +// +// 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 +// + +#ifndef __MESHCUT_UTILS_HXX__ +#define __MESHCUT_UTILS_HXX__ + +extern "C" + { +#include + } + +#include +#include +#include + +namespace MESHCUT + { + + enum TYPE_MAILLE + { + POI1, SEG2, SEG3, TRIA3, TRIA6, QUAD4, QUAD8, TETRA4, TETRA10, PYRAM5, PYRAM13, PENTA6, PENTA15, HEXA8, HEXA20 + }; + + bool estUnTypeMaille(std::string S); + void ERREUR(const char* msg); + char* string2char(std::string str); + std::string int2string(int k); + float char2float(const char* ch); + std::string float2string(float f); + bool appartient(std::string e, std::string tableau[], int taille); + float arrondi(float x); + int numNoeudPointe(std::string b1, std::string b2, std::string b3); + std::string strip(std::string S); + std::string entierSur10_g(int i); + std::string entierSur10_d(int i); + std::string typeEnsight(std::string type); + int Nnoeuds(TYPE_MAILLE type); + int NnoeudsGeom(TYPE_MAILLE type); + int codeGMSH(std::string type); + std::string floatEnsight(float x); + bool typeComplexe(std::string type); + std::string ASTER8(std::string s); + float dObservateur(float a, float b, float c); + + int copieFichier(std::string source, std::string cible); + med_geometry_type InstanceMGE(TYPE_MAILLE TYPE); + int chrono(); + TYPE_MAILLE typeMaille(std::string type); + std::string MGE2string(med_geometry_type MGE); + std::string TM2string(TYPE_MAILLE MGE); + TYPE_MAILLE string2TM(std::string stm); + std::string coordIndex_ILS(TYPE_MAILLE tm); + std::string coordIndex_IFS(TYPE_MAILLE tm); + std::string SIGNE(double x); + void champType(std::string type, med_entity_type MEM, med_geometry_type MGE, med_idt fid, med_idt fidout, + char *maa, char *nomChamp, char *nomChampMoy, med_field_type typeChamp, char *compChamp, + char *unitChamp, med_int nCompChamp, std::map REFGAUSS, int ichamp); + std::string nomMaille(TYPE_MAILLE tm, int nl); + bool appartientVN(int n, std::vector V); + float distance2(float x1, float y1, float z1, float x2, float y2, float z2); + void conversionCNX(int *CNXtm, TYPE_MAILLE tm, int N); + + } + +#endif diff --git a/src/Tools/MeshCut/README b/src/Tools/MeshCut/README new file mode 100644 index 000000000..599806b18 --- /dev/null +++ b/src/Tools/MeshCut/README @@ -0,0 +1,26 @@ + + + Cut a tetrahedron mesh by a plane + --------------------------------- + +MeshCut allows to cut a mesh constituted of linear tetrahedrons by a plane. +The tetrahedrons intersected by the plane are cut and replaced by elements of various types, +(tetrahedron, pyramid, pentahedron). + +MeshCut is a standalone program, reading and producing med files. + +Syntax: + +MeshCut input.med output.med resuMeshName aboveGroup belowGroup nx ny nz px py pz T + +where: + input.med = name of the original mesh file in med format + output.med = name of the result mesh file in med format + resuMeshName = name of the result mesh + aboveGroup = name of the group of volumes above the cut plane + belowGroups = name of the group of volumes below the cut plane + nx ny nz = vector normal to the cut plane + px py pz = a point of the cut plane + T = 0 < T < 1 : vertices of a tetrahedron are considered as belonging to + the cut plane if their distance to the plane is inferior to L*T + where L is the mean edge size of the tetrahedron diff --git a/src/Tools/MeshCut/meshcut_plugin.py b/src/Tools/MeshCut/meshcut_plugin.py new file mode 100644 index 000000000..eaab60897 --- /dev/null +++ b/src/Tools/MeshCut/meshcut_plugin.py @@ -0,0 +1,140 @@ +# Copyright (C) 2006-2012 EDF R&D +# +# 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 +# + +# if you already have plugins defined in a salome_plugins.py file, add this file at the end. +# if not, copy this file as ${HOME}/Plugins/smesh_plugins.py or ${APPLI}/Plugins/smesh_plugins.py + +def MeshCut(context): + # get context study, studyId, salomeGui + study = context.study + studyId = context.studyId + sg = context.sg + + import os + import subprocess + import tempfile + from PyQt4 import QtCore + from PyQt4 import QtGui + from PyQt4.QtGui import QFileDialog + from PyQt4.QtGui import QMessageBox + from MeshCutDialog import Ui_Dialog + + class CutDialog(QtGui.QDialog): + + def __init__(self): + QtGui.QDialog.__init__(self) + # Set up the user interface from Designer. + self.ui = Ui_Dialog() + self.ui.setupUi(self) + # Connect up the buttons. + self.connect(self.ui.pb_origMeshFile, QtCore.SIGNAL("clicked()"), + self.setInputFile) + self.connect(self.ui.pb_cutMeshFile, QtCore.SIGNAL("clicked()"), + self.setOutputFile) + self.connect(self.ui.pb_help, QtCore.SIGNAL("clicked()"), + self.helpMessage) + pass + + def setInputFile(self): + fd = QFileDialog(self, "select an existing Med file", self.ui.le_origMeshFile.text(), "MED-Files (*.med);;All Files (*)") + if fd.exec_(): + infile = fd.selectedFiles()[0] + self.ui.le_origMeshFile.setText(infile) + insplit = os.path.splitext(infile.toLocal8Bit().data()) + outfile = insplit[0] + '_cut' + insplit[1] + self.ui.le_cutMeshFile.setText(outfile) + pass + + def setOutputFile(self): + fd = QFileDialog(self, "select an output Med file", self.ui.le_cutMeshFile.text(), "MED-Files (*.med);;All Files (*)") + if fd.exec_(): + self.ui.le_cutMeshFile.setText(fd.selectedFiles()[0]) + pass + + def helpMessage(self): + QMessageBox.about(None, "About MeshCut", + """ + Cut a tetrahedron mesh by a plane + --------------------------------- + +MeshCut allows to cut a mesh constituted of linear +tetrahedrons by a plane. The tetrahedrons intersected +by the plane are cut and replaced by elements of +various types (tetrahedron, pyramid, pentahedron). + +MeshCut is a standalone program, reading and +producing med files. The cutting plane is defined +by a vector normal to the plane and a vertex +belonging to the plane. + +Vertices of a tetrahedron are considered as belonging to +the cut plane if their distance to the plane is inferior +to L*T where L is the mean edge size of the tetrahedron +and T the tolerance. + """) + pass + pass + + + + window = CutDialog() + window.ui.dsb_tolerance.setValue(0.01) + retry = True + while(retry): + retry = False + window.exec_() + result = window.result() + if result: + # dialog accepted + args = ['MeshCut'] + args += [window.ui.le_origMeshFile.text().toLocal8Bit().data()] + args += [window.ui.le_cutMeshFile.text().toLocal8Bit().data()] + args += [window.ui.le_outMeshName.text().toLocal8Bit().data()] + args += [window.ui.le_groupAbove.text().toLocal8Bit().data()] + args += [window.ui.le_groupBelow.text().toLocal8Bit().data()] + args += [str(window.ui.dsb_normX.value())] + args += [str(window.ui.dsb_normY.value())] + args += [str(window.ui.dsb_normZ.value())] + args += [str(window.ui.dsb_vertX.value())] + args += [str(window.ui.dsb_vertY.value())] + args += [str(window.ui.dsb_vertZ.value())] + args += [str(window.ui.dsb_tolerance.value())] + f= tempfile.NamedTemporaryFile(delete=False) + fname = f.name + p = subprocess.Popen(args, stdout=f, stderr=f) + err = p.wait() + f.close() + if err==0: + os.remove(fname) + else: + f = open(fname, 'r') + m = f.read() + msgBox = QMessageBox() + msgBox.setText("Parameters are not OK") + msgBox.setInformativeText("Do you want to retry ?") + msgBox.setDetailedText(m) + msgBox.setStandardButtons(QMessageBox.Retry | QMessageBox.Cancel) + msgBox.setDefaultButton(QMessageBox.Retry) + ret = msgBox.exec_() + if ret == QMessageBox.Retry: + retry = True + pass + pass + pass + pass diff --git a/src/Tools/padder/Makefile.am b/src/Tools/padder/Makefile.am new file mode 100644 index 000000000..e771536bd --- /dev/null +++ b/src/Tools/padder/Makefile.am @@ -0,0 +1,22 @@ +# Copyright (C) 2007-2012 CEA/DEN, EDF R&D +# +# 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 +# + +SUBDIRS = meshjob spadderpy unittests resources doc + +EXTRA_DIST = README.txt diff --git a/src/Tools/padder/README.txt b/src/Tools/padder/README.txt new file mode 100644 index 000000000..2ecebb32c --- /dev/null +++ b/src/Tools/padder/README.txt @@ -0,0 +1,38 @@ + + +PADDER overview +--------------- + +PADDER is an algorithm that creates a set of particules called a "discrete mesh". +The particules are characterized by a location in space and a weight that can be considered +as the radius of a sphere whose center is the location of the particule. + +Discrete meshes are typically used to modelize civil components in rapid dynamic +computation problems (seisms, chocs). These components consists in concrete parts +embedding steal bares for reinforcement. These parts are input to the algorithm +as standard finite elements meshes. The cells of theses meshes drive the location +and sizing of particules. + +In the med representation, a discrete mesh is described as MED_BALL elements. +A MED_BALL element is defined by a location and a radius. + +PADDER plugin +------------- + +This directory provides SMESH with a SALOME plugin that can be used to define +and then run a PADDER execution. The inputs are the FE meshes that describe +the concrete parts and steal bares parts. The output is a discrete mesh +containing MED_BALL elements. + +A graphical interface is used to drive the user for data input and computation +supervision (the algorithm may last more than an hour long), and finally the publication +of the resulting mesh (when succeed) in the SALOME study. + +Technically speaking, the PADDER plugin consists in: + +* a SALOME component MESHJOB that do the computation job (wrapper to the padder executable program) +* a graphical interface composed of two dialog windows +* a configuration mechanism (data file and read function), to define + the computation resource (a SALOME resource + the software configuration of the padder executable + program on this resource) +* an integration file (salomeplugin.py) diff --git a/src/Tools/padder/doc/Makefile.am b/src/Tools/padder/doc/Makefile.am new file mode 100755 index 000000000..ec45f7474 --- /dev/null +++ b/src/Tools/padder/doc/Makefile.am @@ -0,0 +1,39 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +# Author : Guillaume Boulant (EDF/R&D) + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +EXTRA_DIST += images input + +# +# The simplest way to extend the documentation of SMESH with the +# documentation for PADDER is to add path to the padder documentation +# in the SMESH gui documentation (see the doxyfile). +# + +# For test purpose, we let the user generate a local dosygen +# documentation including only the local pages +# +test_docs: doxyfile + echo "===========================================" ; \ + echo "Generating PADDER documentation" ; \ + echo "===========================================" ; \ + $(DOXYGEN) doxyfile ; diff --git a/src/Tools/padder/doc/doxyfile.in b/src/Tools/padder/doc/doxyfile.in new file mode 100755 index 000000000..7ce741cfa --- /dev/null +++ b/src/Tools/padder/doc/doxyfile.in @@ -0,0 +1,77 @@ +# Copyright (C) 2007-2012 CEA/DEN, EDF R&D +# +# 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 +# + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = "SALOME Mesh User's Guide" +OUTPUT_DIRECTORY = . +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +TAB_SIZE = 5 + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES + +#--------------------------------------------------------------------------- +#Input related options +#--------------------------------------------------------------------------- +INPUT = @srcdir@/input +FILE_PATTERNS = *.doc +EXCLUDE = +IMAGE_PATH = @srcdir@/images +EXAMPLE_PATH = @top_srcdir@/src/SMESH_SWIG + +#--------------------------------------------------------------------------- +#HTML related options +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = . +HTML_HEADER = @top_builddir@/doc/salome/gui/SMESH/static/header.html +HTML_FOOTER = @top_srcdir@/doc/salome/gui/SMESH/static/footer.html +HTML_STYLESHEET = @top_srcdir@/doc/salome/gui/SMESH/static/doxygen.css +TOC_EXPAND = YES +DISABLE_INDEX = NO +GENERATE_TREEVIEW = YES +TREEVIEW_WIDTH = 300 + +#--------------------------------------------------------------------------- +#SORT related options +#--------------------------------------------------------------------------- +SORT_GROUP_NAMES = NO + + +#--------------------------------------------------------------------------- +#LaTeX related option +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +EXTRA_PACKAGES = amsmath + +#--------------------------------------------------------------------------- +#RTF related options +#--------------------------------------------------------------------------- +GENERATE_RTF = NO + +SEARCHENGINE = YES diff --git a/src/Tools/padder/doc/images/SMESH_spadder_end.png b/src/Tools/padder/doc/images/SMESH_spadder_end.png new file mode 100644 index 0000000000000000000000000000000000000000..12e2b7cf293ec21f3fb5e71fcfee51e90e7181ee GIT binary patch literal 82471 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 z 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

                    ua=GJ?lZV7#Rt>VJ^+ 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 &k}bR$?Y0lo}N~Fjl~m@c@vXY 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°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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_concrete.png b/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_concrete.png new file mode 100644 index 0000000000000000000000000000000000000000..ed0a8a72c9c9f3fef9b4234c5e6f91c796171846 GIT binary patch literal 85319 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�hgj< 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ηQgZ!}Z+=6G4ZqE*<+$+YJNP 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

                    AAO*8X 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_start.png b/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_start.png new file mode 100644 index 0000000000000000000000000000000000000000..8725d71651fdf3200def853683d826b649e6784c GIT binary patch literal 94767 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

                    f%$AHys3?l3ZZGW@9S<|IOif^S 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�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
                    ^7v~tW5yK*+tBflfKD}k#*NS@NAHhxXdxwD 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_published.png b/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_published.png new file mode 100644 index 0000000000000000000000000000000000000000..19e1ae71f7e15576ff1bf4c0d23ec00f2495d7a1 GIT binary patch literal 108235 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

                    gwX-BK zr1MwR`TofWY@E2Dck0-xpsSu;=v!vk_`4(xcyg(qG-B@Bpiw(>Ux1+4lnBjV(G-0R zD|jh?&TfSLzfnk3&J~ZD4N^KD^)8de{30AT8gQal+K}r{awH54D&eK><9OV zi+#q`25$U09ItU%cC0xRv)+L+4MlW!3q0CAc{;SomgyFATrKJGX3s{yU$|O znGlXtYO#W@*cFNr7*C305o6}_`Uf*_{^Ba&JZ>X5OQhfGc zi}Xt?8yl*deGAb7e*{vZML6=ix>(SqGx8G=W7YC=a~}4HZ%6FOK;l7DleM|igW!@_ zm_w-{&9z8w$jij(D8Kfc_OY-EWQ4pKZ~PD4gaJQ!CBa(+hRsBK-gJ8>TbfJD;RH#n zdL5JISYl2|bNH=t1*L?`?yFs!(g`p9Mv%DUYvz{?X~gT}(pe3!XD$S~$k`%qe>}sj z^+Rq5;sw=zPY(p%?*H7wT+`vfzr&q*18jdk=IRinIAxFBC_q$i!mvKb-d^9HLy4&4 z`hW(ze3ha=*eoR)wv-6qvpnYtbwWF!d-IBfFhB{#M}5rhUVw6!-AaoCI6Kj>>YR7^ z>ZFc>@HXtiONtMWKvq_0Q9}LOU+{f;)S>OS`~=XVw_E3{qZ-3N@o)MsU94BNHKGf+ ziX;EF5tVE}Nojeko8$?*T(2|cmmm&|q6?+XEncb^CK5g8Z`U+2sVC;sglj_sA$BP{h`dMeE?YjQ; zU=|b*?mJR5KIJklh|>4Z^XB7|`r1n$;iJypN8P7Dz8OpU%jGn!T78}f7gEzU_?fIs z>9_sR5tg7(P?SW}#{?j}vNF{ZFvp&l!S8=^4C>Lp{z}toITVZ8x|dQbuB;QI9&z(z zFHWt@O>$bC8!XZ_N-i%iGg$4P<1;bAy8QO1IVwdL7bh=Yp+TohG$kZXrixmFzkUUJ zdIhF2=E=<>+Yk~zRRe0~Jcky)*Yd}SERkAi8Olnw51)zt|4K2458&gzWz)aE-oCwj z8{Yly-Me*2^fo6nUoqe3`UFyGI!yTVc-J+M)QI35iN{X}85^)uWPDTWM=E|73`lY5 z4&3GwSf$E-|HBT^ND|Wj-0{hX0lIEwcHLN0ehN`Xt~gfbsgZuU;hIusQDd>5(udSf z4MOkb<%xD6gK$k%K|%w)+>6hf zGYW21Ezwv#2;dWP@q+TqErz8xL&m_f^M!fGPZ>O~^XoZX`|JBwY<7-Dy$k>`I!9*K zgCSqLm3S^cx^MO)gy~csP&t4t?78nz((ZAv;IL6Pjv|xvnxc>V1tvs-@Pi$I{0Tut z1qMX_AyJte)CJvQ0{~y;zhHjzC(cCU?;j2{p;X0SmMbI9zkB@%o#E})tgD<**=~{U zxo9mnywJCNtO0wvg;Q|*2t}0TO@INYn;wV>FUAZ4b78dRlsNhWIGLASE|83#t=h8I z=C3>;dDj0tzuLEJyOiulSS#li@f+VicNC!K@QuFjYIb40oQ*OQ|9&;@u^an9E9Mc) zj@cEdo-(hu9^#yD&A=~+ZbUl`7>ofM{x4TnbLypqbBxH2gDjtPba&G#b45pCc z&w%xvPg88RJ}Bw-9G}T?ckxd~Pw-28lKHKvf+0kE@Acx%?bAYOLeMIid;TcDNKXs9 zzL{!jWgQuyxtZsx>gTA_bpqWUz%hx8l(%rFa-eFJN<`USocHZxaiy1jb$w&XCcP%( z7gW5*Yop=vKaJCDD`0(~$~UPt+M~Z&^qQOgaT>*->B!|USBmxE{b}p8(VY4wL$=0J za|nR2P7ML4vb3^L+=GyElck4_g@V@fdkbh+oFaQkGVZ~slFjqIt@$$TAc~%|gyED~ z?cg*WB!i<&Ke{M)+thFud}@-T1?c zbY-Jp7=-jnb2X6oSqoznCQU#RjY@kQYk8==USU{B{^{*9Eh-GGq#C<6ph^WUH|k$1 zc;AsqChvg-&2f61fURyF}T7KbN6#n6i~fO=2lz^=Cxi96Z3oo`kRni zucsR_9W0{_lQ9RoPdssE2)VYVTx-(`lJZsbRp(wck@LQiO=~nX+pKkK896%|agyVV zSMxU!`v{zjNooMe1ALFROY%RWhuWJTwhLU)Uv~8WRjK6q+BSnnbhtg;ZrWm(&ZZ$N zW!^GxuOq%c-SnNcYH?q5FA}@f3@zDf zkHtJhB+oBh9@yaRtMLkN0iEm38RRGJIk4wd3f8BpDL_pe&p*2=M5N<(+ZqRoa_lSc zVx^sHLrd=PEH_Vo;`UQC8*%_E`FJ8E=r^5sRB!PAfd9#6jtco9vP** z*4iA~>}9!KOn&PN5NIO?%K*-&>6>lXMNMLE<F#?5q^M@Xpq>!S)*Unh`Js%h}t z6UQNn(e?xRA39g&X>S}~gq>0~f47aeaQSsr{%;g2THJuo+FVU%ZJ`6nG+3a_*G^Q{ z)o)jnzBzp0G4DAp+xHC-v)BphqxeSwDt0H;qcX?tCk30JsCwxRT}=zz2VY)0#iy2t z0923;&uy4LNlQw1 zC?(w>E!`lw=}idIu;~Wr?rx>KySv$Rv$+euukShMp5GmJ+%fL-pY2+#XFY2^HRChq zG@z!2ZLNet0>6|je^yKl^mxyW$$l5qPwMZDjX%XbkpAka&y0FiY|d z==k;GA1ROYDTgiy^jFXW8RNh0|10X9zv#6^CaV6b(s!t7Rlt#W6e@EKcvDi(ur5jh zei7X(oM_x~dTDDn6I zM+A@vuhL1DMO^qP97t)*$b#&-Cm9?w%I{Xd9a_ zZIR!tO@tH_Mn509sZ7d5+i)yU7VsF2nxvEN3n9g0(EpD7xs}FL=~uSU zV7?OIL+ zjzrl2{#4bepTC-u%D{0LuJc@Ci~_+*43= zImfaB>i_{C2|wOony*k+miF71s4V#%DzhU^($6kgg(GAX6e#!{CBvI>ad99teEi#u zzCsHqklhDxUf@5V^Gj%3%UD{6Sfu*na`73j5!KblvEYlgo69UXo6?-@SF^9~)X7i( zAafs8CHI<47K;jt+S2CHO$q0Z-^IC;HmESDKOacptaO_+317o}l0xS65ZHwxS4`Ei2(3v& z^i(JGnclv9LUd4d4IHDscWtybBOvDNzJ!ztq=^1tCec0d53 z9F~%5u3WC->({TbgmvCL`~Hb9w6iR+Tff2W2Bnwj@z0k2@&5Vw*7o+e!OcmQ98%?F z4o||LfQXg;KO7S>UI+sNO^D@j)b#W{-QBr4HL%=6J1{r~F!@R|`3re1a{y5+no3GQ zC~;n11x3Ws@wyQ73l=`s-u{07=Dp^!ZsE=_w@$op7~qk4aCHE$SO?-VEEgJb$9IHu z(Zw)q*qtVeG=X><9i5**-H!Zm{`=$JrH|f;Nl9nUZnCoMfN5Dn<9C@k;HgCP@fkeZ zr$I~om_Na5hEVlzNcp|9-#;}Fs}r5yxxQwP=08?0+&L;6Ly^glzLPj{jCl?Smr%Ee zA6c21N^{Lb7$a_5LqFZ!-GS2I1_pV*f2U>8s4y`zt47;L0_8&b`}!KLZ(vtfSIzM2 zgg!AgyRIs20=GXB=NBN!7CW3)D{QWi$pZ1wMRl@HrW=tx= z5P9EQHi!0Fj2Q8M1NYIu9nf}=jTU74Sgr*TT@p@4LG9&eSz2` zrCM<+G>`{iP(&ie9#>cJrLVsp1&cOaohMi|-EAbKFQ&lvL-AS1_gpxPiY5{Y7ePC2 z?K@=y5Xk%bnT~__DY3UYZ?J=ffUsJ({}C9lIzR^4-FYWw^jY`mL+6i=zb7DoWZRov zn=Cgka)E7z^f(F}(}$b3#M=!IQ~sUDVC8Aui%exOE z(iKY~Vks)%`$JQ83lxxV5CgVrJm+m|>n^E&Jb%-j{@HK01Ed8MbiQsrsB()bz14Ks z9Js&f!kOQZJq#S2e5y0JRLIP~;Bb{6HGHOWuo_g$Z?TYwfnROus=^qrg7|UfMaU`u ze18wQT?VSWY@Pt?K`HMzUj-7=JFl2$6aVv(2ia#86`aaCc@@ol03*IdRXDNzA~1l2 zl;m^SFb$*c@*B_+PiZ;$;t|Pc-1pg?qDIY>%XZuthd|uPeX)z<>Xkogk%Q{_QqD_cW)J49;;_-Y}yU zOnqZxZvFzGf6?BPbo4pi-;03r`m6V8r>At7)yJbOB04LT!Z#ArciB@uclXp=Jpsv0 zc5_#wNMAb6Z>wKrs=WYZc(&?lZ_C>pjWJ9j=YP!E;&op*u727Bgd&b=yR2fn+A6ey z4zPHf&yO#(pP{Ki(6bK+6R$k2+kaKp^IeMmEW2o`jjuLc43h3=JD$Bx(V=nYPQ*Lh z`03}{?^mPOb{h9+wAsJd503_Nh^v`gVKomaom6%oTC5~Ds{)j{QiwDeB_$;iw5Ft_ zYj98|e;lwJMn*;FDXxF|2@(((WszbAoNFTK-IJZ7WEhxFRApraFurGJi+T%wv!}a7 zS-@QGKgyWeNyMa=lRd9xSXR{OFLNvFdC}cI<+B^ZNe50ZD6P3cvbkx3A?7b`LX61^wvQw+m?4m>(%Xd*oC-e`SjkzU5n3KKgjqPNe^feX%l&owUfxj z&}%;P(&voUQ;p9ui8oX=0s-ZLUHL+Fj4Rdl_f}}Jxmg;GM2N!Br~YCARCJa&Us!t> zetPlDAF^gUY$;EePSY=acQ?Z7s?X2hdG_{+0yH|ZfA=->g6O9q=U^1FCm>`G?x(L5 z9m4$`IrK&%4av3d;^ad@?9B3293FSdMlu6JF$M)nQYp#C#0GD-Qn1xP-B_j`?WaT^ zof9zobg3-SKWZLb;Qso%4S!x|K+l_F-XFS3i=Q%W8eCe2!lgfXQJ%S3jvxhoQb0+j zF~l=>KmTevwSr2)yGgT-{)zIwEB0LztJJKrSBV+3n9A$78BNltR+kwOp<}X&<5Z~$ z^jtTvf}ccmcsNDhmX65&D0~Nrc3iVx6lgyg;fTIN?F8zpe?2`##1v8)G)#!^lgH#X z*Uj&UY2C0+pca!{+n`=wx?wbUk@OA0{g8_linvfPK0yIzxA>_dq37vv>srI*sGkuy!-?%hue# zt%4i90peBGD5}a(KkZR4soS&=hOV z<)RXG9Lff`O0+*`OD3QDfjl3|8<`c{x#D6o6X&83i-vLUMO9xz4@xYwH(z;5Bg$iQ zuCQ0RKMV2TgPwWz(Nutq3YLoEtIA)hHCa7EsEB?T-~XGVC2OTK2xEJlG1n~}&v?6g zO|$}y?u9WDHT0k7%`!rdo^ac}a)-F`~LmWhh`=5=V0Ir$dG8U;`eI_7XGIF4} zcz^*@+-EXF)ha+~&ZuddZw3AdQeowczw95Hm^4W5!3ZHj1hrgY;Gi}i>dfC|ggP57 zbGmSD!A!O-CsI^0%tDg~nr~gxJ`b~!v2-oAJL5c|cfcM2OVSF=20IA-FXh{;JVbWa zY9y4A9b-EO%?z2^B#hUwID-bcl7vCDceaGX_?KtnDi5vnv`yNV#I{|v@c6FqLIP#} z34a!@1HIPI4e(!#kMpM~qri4Yoz23uQ{Tv_hlE=qll&9hv%32@&f?Ijc6Zy3e<8px zVf$hJfpjYZgwU4i>a;!GI>Uyv;XP4v1Lse1DnZ2{0aTCY4cAfu1Uws|t-JS?wBTV; zOhNLTdcu!t$#>Sspq3BjQo7+}65;{lruITYLf^i90KEwoDW6V^ux01vP12hV3p?Nl zS>=$9>kn36=L99YE=|2#I8%Q_X!ip2)qMY>DJFknTh4OwP96FVC{*#^L#KKPYuNNr))0brEKQplGqUJm% zV_xz<21$a6aVeoC4(*k$;@#R7LS=@XyAUzw5J=H&c3|#iNX)4l`JR|_5}in|2KKRk5#Iw~q zQf15l$!%j+oyY=(~bXk(q~2OL-{T+|x5KOw9vWb4)_;A%Zp9OZ>wiPC#c%kG=>SLQqRdN38r)mJ2>6!A94mH`>Z z&nD(?w_^g|Pm`42F{5(|zdT4jJGu=$n@3`$z4NMY;Ad_S&Ca!|zS~XmKLHcfJ5YA& zGG=LU!>_zz7O(RA)fKp$ux?fqkn zNm*I@dwcVh3)LuPl5ejzQ}m=eV*=S{&aI{)zjsvXT~35DjxDCijAnDRh+_Rdug{n4hSk0mwWGE1>~i5@avPl+ zoUn*v)fJ2XEW`yn-0&9HDE*x~-4->CGI<6>kbE&#|&RL8b;%Mo8LyA=(zHiSHdcdhyANqWjOGBA$35eotUog{|-a z&Re$%qinuc$Kv6^{`a>|eBD%-xf-{-RVJJ1BxR9K`8-q|e!V??-)t{(_ToW`RBp-} z!|9@cFrcw^BoRZ8E+-_^Hf5bpovt;Gsu$$)oW?~E=kC{J`0UoxZi@|?~f zBPB03ojaC2k9d(ol8Vhxl8k!;o`8%h3on6_~zki5Qx>&y!6Y#?UikTRc{y)t~{#JE6nm&XL+xggCT85vYcBcmu{v`%!!!1nO)M2wWMu*As7 z*sxf=zISPUe!g5uMH(V^Dj(*o0HhY*w%Wku`C(8`5n?2-#cH<;icHa-8c0AXb=-8e z(!%AcCkjXiS7*P=Vm>8MIFQhzSh9nzM@#h~$LVBab1P;Z&Y zAhCs%4mf#00; zsKh?~=i-HZv)X7+PJtRTufK9IIc+8D$eGm-K_`*utP@Ht1~5ITxfx>Tz=n1|gVh-p znyVz=%#vim&iNlhJQS=+cmcBMoUR89#L4^BbvPX8V4zGgmK@i zsyAD%lA|$BhhLtV731&J>YRP8E}NFiU7!W!Jc^}_=@dQ`6btWxM>C`ue^djCe)z~8 zL=~1-Ar>YN%_%vJ2wFejmG1kG{XOQL+{kp;rXS3ROn*yY(au8;CI88d* zI=#kF<+ThLHSe}72E+Ob8;%d;dQJ=$5VW2az5<~l#4)ONxS>k;l$Kdh1-1uO0Hr&F zs*Wx>=g;Z19E`_rLT{qhVLf?6+3c=&b_N#GLLDaw*>ufgCwH0>YA*W8GmT&2r-g86 zF-xv5jjKe*sRpHXOue3}4^ZF7oj2AmC#Zv%*U=22MUeOd6%_?n9pER6(uD53J3rrV zNlh)IQ)r3*43b4OY5ma`@95NX%X=2hubv%wvB8?8dDZbti)PXIU}>iQD@OzkI`ceF zjY<0iEFIIf(z6h$fyF|%_*+sXL#wOBWY8u~#~cgVrgHu0(ou2f4qnFlGR`mSQTQpWLWy4Eqk& zX~6d|=PXIQlxR|w0@u=H3aik3c8L96mS@Eyx|?8^lc)tm)~fr+#-1sDR}2LiSNQS< z1EfBZ8smr_Zm;$-mea~Eo#d67U-ERd8C+Y9Aq20=sKP$y3;OwvA?!n7qiUW6v^*Ma zx9#xBkRBbXAiLX?p4~K;Ee%DW?MDKE@)A5eKqPfGS2hMJ8P3mMz9_5uf~(sWh7C%6 zvC_yb<8c`zNoTfCI+-o-Qx~pijI>%V#Aagytd5jluE%g&Weaa#!#Iy~ zxe0~v);H`VENYTmR_(4_U8Pjp);mvWEyWL5#otu+X1uzo^D&j`X4h5qi}za#TJ2q& z`t93Xdv{0CM0<5QNrNM~h<1DQmM)Qi*PiId;A!?m1JABTXE(9~*lZ0Z$!UGF=(xR! zWeu>(01Fq@kfTi0MATt7%QaTKjhk_EjKO?*mZnRoH(}H&`LcN(Ijivb+?BE!&Yaex z)Tht>(2%kC)u>#*0sTBdF)WIz_cJn3xzBn)N_5cK^*S+_azfGqre%CpMbd1lp3Siv z-(33B=}0%y{|JdMz~pM8vsJCuR%oEmxAEls3a6SN2*$z_>BsVO-lgg}n4CoO%a6?k zmy@1AC=v!23UcZ6ZT9|f|04Xd7R3@D-O0Dw>9(|_-^I`GWjm$Lrb>7jMv$ciFD%(! zDfy>m$Ed3A@+4EB+?tzfLy{5?3~!2<1QNd*bgPi-MYEgYI5ifP zX-LmP@g53FiUd#1c6HeM+3fS9@~YbWnCexn7ozxz>PH{;m+iC`4!BqW0d&^^mvujP zap87b&2ksJ3dvE12n5Qp?AexoBi@E=ngNJEd1K2m{cny3g6un$2lsSuT2I^tZRo?^ zx=@pE9X+eQ?fp7HmnI>5JXl)WDj3)lX=^g}o_Kr6B0@^7E0{M9|Ikq|Q*5e8bAphs zpg{p4%5lu zLjIj^cp=+ddD21FLM5}6mY`A_fnl^Z?L9IG)bmJL7SV%R>h+k_wco0hyJJT8ns_~D z&@t-nMX6e74{w($QNp`$Q7s^-eC1U7*MYE1YkZ042|t9zaIrIC0tlvsZJ|yES?&o5 z4FqQiTjJu&77&!}^V{?5`&(20-1EJhDohyBiMFY32!K!>q(1@4o*hv?0vjQCb0HC`rOxIy-c{C75t>W{2S(9@^#8eGtl|E_kSJ)a8<{V6reLdNKR^^sSTlb+zKei~p4;%&fq)6T>K8)j15sV( zC5&q_IWXF->ccA~lOiKY0z003Y$8vRO^TP&;ItS`6CM=F@}q867R)MsvpAN!71$QZ z5AOHpf$F{+L9E2(*`ME#p5T?9Ewh6~ceU&B`SnteEU;$W*&^fk)SKe-7$_7Jb;rIB zW%$Bx-?bxx+^jmAOIYRBJA;dm&C19^hYyR`m2OvAN zDOAY+|9?>zJ?6!NEh`1NE2s5wWjFU?hucd~>h08xmT#H;VW*qf1WYBD#Vb`Vcg!nw z#p(4RG7rNck4&32zNip%HcPEJ{o>$iuN_rJhYSNW_N1&mJmGS4u(XLzFmmI$0O9nv zyS8qLQWH+>II*mX-A)JkS%W>soJVPoK?1>K%kIEdOBy>+^oGrZs1lDA^7Wat3yNPi zWKZ>K+Fv~mn`A}9E8c;S#de%I7#$k~GNpC3cr5TL)r@m>ww0N>9(&v)>%b4yAvgyR z4gu-#*BcQM!0reFc{~o`c-X(x`c&BY?{7EqH%yywR=3IZ7I-adISib$ae=-(dhWc= zs!hP7lowu(8S&DfCf@uCTfK_A--?Uz;9kqkrUy*E7+w@~{?J-0@jVae((0q}H?0r4 zq*{S?v8|~|P&T$?&`z!UQF_sOZHx`o+k;Ww#?>Ikj$Hbo&?J6aC14ldP;*VEp%|a6W#D2GA-gs-?aTvn4I_Cz86Q?3k%|%YP(}+M6@e0Ws+T z`fTl0H0(T*ZNdd>rLEDoP40EBy9!Wo>%HhU!QL#0BSraoc~z(l9;K8(%=6v_OSxH5 znk+5e+wR}0FitCVUMg9%2zUS66}7Dhn6QH>-Ufa4ovm?i1Fo$`Rq@vDV8qewhDk5O znJZ*6e1b48+kqjL-z^`qMk8f6kpP|DYq8re2S;b~E~X8=x~6^3KLBw$vdSIHTQfyt zjL<@HGwl^sz@wqnGH<$@pPQUqG(GC;+8!LtBU)LFhtyhtX^=YCQ;&7$RG5w2)m=s! z29?=Pmzj%+Zp^#GbE7?Ew94t4rmQ&&e$KPeP8}ai_Z`f>kUg`0kSe04G&&OeUk)?h zIFP3`Xd@}bkSuC8+^_7DHTw9??ZAzuBF->T_iy&?G}yMiW>B2&=3}^Sa=T6kLoRS2 zkd4JP^SICZqdBu-N5y=zzhnw)aKf4O92m4~Zy=M*1|ydh__ez0foJ+Z$k*WI2FDeH z^!nm$E+VIKhX-TuN_%{CDChE_s9BY(+n#SS)XcGWW_!cSE&6b5r(c_(-h|jfpx1qM z!8H?H6GD1&#w(%h>T-PC&w`20$?E7n-|;*c3x@Sek{#7jt@P|>s}0DeyZeYiDq)Id zPVF6uM2~j;q}B9IGen*)SZ$3M4NQw>)BCP{o^6t)--eXM7A}4 zd3|l+1#6WXDHU0-4r@XGTuQ$CZznO$n&Pkh9dYit=$`f^ve%)b#})6#1L&*Rgr_TW zreHiC_?euhH%myunCuRMoVm_uW|yi2q|T1}9dnFd*ZUm%#bE{Q1BK3+^B0}&7j#W` zE^BG1=iIy}@)7fp*E8sw7>t)behtkzcO--mcy7*f%GEya$4QDG5paZ2XdN>lqmPP% z?sV3EnRPV52jVFC__mmwM{`gKvNrw?s<;4-*3tbOaPR;jjb z<~CQA<}TCSp=dpP4a0YX{Rq$hNVc94XO}!6j*wzo?8;IT47g88`3GS?ppxTixiL#Cdn-ou5s&kjQP&-J^nc z!;NcocfFi|9L+X&_EE-)k^DNyq3=A!8OijE^ho=nL!mQl{reTv%AxD6r<^fE)=9#K z`#zINYm?LyabRVb*Z;Pi^EB<4l+ON-s$)=!Ey9mAOs$~!5E zwO8{KzjpSfkel}tA&hZ;e)xR$u2*l>rw-WxkYig6c?#Tl-D~@;7r%SDPdO*Ni}7SM z;o=Jm_9e?qn zkX2#Umn#7_CoPYpE*P-#Zpuv|V6x2}m}TXs`~}!fodm2&-aD^(I{%+l22v6d3*%yT zLt0~u9bNxWxZymo6+WLebU0okU^U5oUsd8rxsiD7L+9dxSZnHJd)<$DU+wyf)T}-r2!vT&2rJ`p{ys{p5t-YSZDYq_Nz0y(1?7-kjZD@UFg{adcd>d?J&--0H=EslM0=W7dLcf3Od&}W*!0*AwbkolVkiU0h5bZX zA{Rm=zI!Pt%CN9(&6%U)Z(&@GH2Mpv4^F9kq>U}$1BBt1Cq7EeqjK6|%sxA|8$WEA zNrz+V`Hm}@nX*4~n9La?w7)-D8TFhDS?KF`iThD6-F{*^nTn0D@C9y|l&+3KCNA!x zgPq=0f9c4b?^tQ})!Q$%LHiOvvV|Mb7s<&OQEPW{BUh&qQ)X^}9AgzSLpXoCf04Y4 zwvmNxm%MC22%+tB#t%uEv99ghV@IlfX(N}fDL51AXd5|sbT=tE!NqZPU1Q>yJXv#n z?UM5va*MNy>WVdbTnP59eDO`08>s^Cd|;MNmyhoa1BRFwQWd`0>BJs1)YrxMNu`;< zyz_z+&YOTAO9IgN&*%9T#k{ak?<`x{MlW7*e4q|}_x)nZ4Toa%SScqq62qcpwG=7-d6)ExPLetms< zT&Vz8Fb{Hj)C%p^tiR753}a+w>%4GigI=d}wzy>qd_bD$nhEH&}krv&{TmYn3{W(69^>Q-c@6^(9sJ8j0fkHj#Vi#Ly z@Nt%=UU|^%V*bLY>2_z~kUiUeP{P9@wD;pH2~)v(BWBTi!+nTu@3}U1OXCv9gT+e8 z+N%7yV)8r7=;olSNo?xSuak59k-qLHS{8(;s5pgiV&rZ7S@19t0)nX6&-HCuR$pnpZu&*QZ+|v(G|UUe zo>5Qe8_|y4=tvfS?#%O>uKC-4(iXCkNcT()yjS_+MZ=AYojO5b$DQh8!=PJ(zK%@6-($LliHsSG6}2)| zFE4DPM~iQ+ z-~=R#5?_?rA^3FPQM0N`%9MOkE~p1_wS)o#q=QyBQ1XT{t-Cky!2Kd@pLlyu4)!Mw)5u z7zEnCeun~vLvpPBn$0JL#2a4-Z`A^D>4{`s7~^Cq%^;(r=ent*Bv-i)*8ReD0VlC@ z(Y|9p(3elz6oF@Z7Du$4;HiLv&ydysqublnX}Fq+%J@_r1>S96@-DJ^tR5Htto&Jc zixHwVvdNA)s?8O@G)bW-k@4JQIpVT2YA=n>7=~sZXkU z-4cL@>@t5ow_L`&xT!6WH>b}QB?>sP!pX9q%E;tFyP-2ziF2FrpEP#rPP~}-67H^j zGm)p>xY;l>Fn>O)^>(6E`g$1?e7yK^d%(XH*Ph#VzOk-q4oEFgCwlR9md1K=Am5$e zbrRlQs2NyXkf#{@du&06u27hcw=Pfq>VC2D~`SX;i@Bu~8`X@t-5ugwoVtbPot7kB;)%~rg zYf*dHH_~Z4nMt{g+2%VNF1AtHK;Ih7dK}kpMf+2fUW(!_&@&a8#M&eSsDW*j+{*V` zn%6Gm#s~?{^HVqD-HzT}e*Rgv$UI82uo%L|D)Z|+HzUlKBzocRndcRc6@IxgNO`oM7DEWHMNWXkk0!Or+ z441AWBQ~d9LWZ5_N~k2@Gr^3N;;4cI_8y#D*$Qf)AhOr|Tx9y!yD2QQ`uY{Xh*rvL ze|BRZ)udW>H58>7(Wn1@G7Jh;cg=2Jv__IL3bp7T*SQm64OP8e2qC>Uj3+PWTmD_j zk4bw~te5&H2UQS=7d_>vp@fCp-fA>~gA;JI24 z+qxXUI6~nEZMPPdGabzp%q9d%q$exI&FyoN%mGZ8wg(!#$JjI z^AW{#E9X^Ul9Bk0)dZY!Vj=ED>TP!#A}JIwGgB>C7o$LqD=8O}Z~9gPyVBP$D`~WI zTu|HBI}Ws&98@(>pz4mE}CK zq#M$ouf8syK*6*^8E4N5CTA6dg&I0ZN7?)D%;%$3Nux9Tg)&Z1toXXB*Rr!LRO`SJ zYb%UIZ3dX=FuW0Xtxw=@g82-IN&UXaPMm@iE`9rSpT&ahiT{H~_hiHJm@l;dI&8Mo zq5m?N{Y=Odu;RxQ<11mRK9 zo9g=6iv`rzA!d63X0zk^1ZYf+!AiT@WF$3Cm5K8QORmA!0U;sk#b_y;s^Fb7Pk%qf zg;(_`arM?hqPv--FpFw22`A$GDD|n2NwAwydIL?)I+u&%KALuVRwwal}8{J^hF7MxmUcYjm^X0XrC_V~e`?(2DzNCSf;3-w~n)-+nX zd)?d%)SbiD(}g^+@%ABH&}`zTaX)8vKxypw>-8C_l8mY zr2$6N9xs0BcNiEL*3`NvI!J-2Bl&1fwuaLI;4uC1Wu`=o9~B9S?p}>jlw9%74@QwT zAgzpnSTwmRHn|vTI$TV95!M6SS?8$+=&m@x#%^y4SkUZGMt7 zk@1~@Qo4!FX>+#Up1k?Z5~DEy*7*CQv^OFmFAOEHR@mV5xeb7aV=+|M_Kr|JM#i0N5QK zDE0fhe;)kzipMJAzilw%0(RpEiag{2_U;FwT4Ds!$vqI(bL!XsUVM;69*8Pj82z7T zus;8NiQk`3d$=vmu>zQV9!TK%gC*^O#NROfePPR+>woV0r^^4wF$>u2w8)A7>Ez?& zP3S*=;7KO-w|N0( z%fAmWs*3z`jU0dCA0~7E?KtkgAN}DUPV51W)c>@A|M%YS&t!kDlpz0Y^K-c0-vMzX z1h&Bk5_sNu^0(^(9e^?Lfn>@4Wjj_^(BJWSERFWJ1D?b=|NayY{>#PKAl82x^`}+) z_db>Q-}XH=OT4K6ykN@n?=x@yb{9yF_D??}|Gx0wne=xa_55x0Uj}@A2{=9eG)W%x z_vZ7ztakW2Xe1$`f0@~k`spvH8i@bif2|IE;@KZ5GZ|6{zxcvw^q zuk&E?1w^z5N`3a1lhD4uJ@6Oz`S*#Af9Jr5zl+7-pErC({rmA>{~fhuN5a3u^2wf4-9DmppxtZQ7t?EJN0FApZq(0$)5jx{4>+v)-eqJy^XG*mN|3d^wn+sA~XROm$v; zP~mv=onK!ub3Lq}CHS{3b&zAMkgz5xb!-uu$SKZnS5D5SdE{pgz7tt-d7pJ>1~!n*-*<=LnIphGu_2@zqSLy=!eiAV!{r1oIc z1>Epy@l-#2!yo1eali~w%cwf-8s_$++ljUAd_qB@t zfMI#N@rQ3SBJM!tm-|6=FY%it?mQubVQJy+vhNij+4R?IPj}UtO~MGADmk% zhEFh&sMhWnj$5lv<$97}RC-DA_ym6ZMECZ|lVJXBtAqoAh21DQI zS|&xtY@`= ze-3PWJIRnZ$aQ-YN->=YE~siCfnB|jSAO}6Qgmb%;xbxi*VS(fzb*=$}&_Pj1Q-5O=nw4%z>4}|+2IB1O9VPqS&Dhg6M%Czm1I2-$1wJDn)_j?v zFBuJeiPo?~o708sms0OK*e$aEQ!#3WE08!H8$_tJ1;C>`;w8}>L*%}ES&I7pk>8bI zzQ`b}I{zbG0xq*nA{AzLnhz;7#x`^BWuS0RPxxsTOERYE_zM~hjNSma@oH@qGdBNZ zmp>_qyvwBNaHDcy4@X`=`-fiKB^@7n;EZ&9#?ux5S@oLMUknb17ZJYelum5;HvC48 zvfd$Vcv5g|JC-;{i6?vfxYp3&s!D;$zDi$RV@_jKlJn=gcRmi(*-4ihu9t@fuk6H| z7nyq!G@%zkqt?Zy7QrZ~ zVy~*DTQlSFrtN))!5Rj%=zOUbIk_;uHf5DAvsu9YdDNx@zNonPwVUye_mdG+be zYO$_p+!V-`i2Pb#+E47%L}f9G#wmq%cWeT&e%j+mLfdMoU7q2#V!u^5<+}LI+hrl4 zouy7~RhE{)P9LTLNTqB0wV)LCDRKo_3Wv#dwXt3(vC+q#?!Iu%{ITOg9z#w9o{R!c z9T}|0z>%Y9g!;kiSARP>E2~ntP@u4#%}Ax`c!|mAb@l3~eE8_7ZMlMiWVkt0KUm$u z2!~*?L@QRfB9R+&K2=UIN?ge}JDjmM?P$olhgAnL30X*f1*Q$Z>00w?{5>ut1n);` zJRQ@vh16eZq>HGLj9fpxK}UyzM;Gosp%MlU?!Rq$bEGCCWAs`y!{+=n>*j!r3gBiO z;6iQ@w3r&2c1hcicXtGKj~vc9-Z?t#axwt7xmaWEy;|ev=f_63`~0!hhgch1^0@9Y zV}F?%ay44hU@o?UD%#$&or(9(<%n9FMWy=j`ZmiFQ<#LDMwTNvFB$LSEwmOiom(+3<3q5guGaz5*3JRP>rk9oaFNtC;qiOHe0zn51;7_(g9tcVHf5&CFIbw^Qkv=o?A+d9k* z99OUX*9+P0h;DE1k`j|fM@NE0NYQ~DA7bG)Ha0-+PoN&JX{BNL^mU|2x_@A3e?KdD zZ^(1CMwc8aae_X0b;knfK1f%At@)Kj=F8MwU;NT8exu*vSPincU0!{-&IDWBj|(xW zj}|7Zi`6gbDl_g8uXE>|AZE-uwpc>HSoO3On@;P~AJ)fEg`59C;^-hbqqW62Sbr&t zFu$w+)a$T<#pRn(jXb=u2)VE@mA|O)2uj4#v3M}PXOGbrV`5f;M~Tk#LpbSl#Z-#FKx;_8E|rm^%}tSn}(GxMN7qm!8ak0V%TLG1F6NA z(4!M)EVsm&hU!A8I7zOriswnlUc_I?Fa-LQdOXD@T0C-q1vV}Xq`BzDrYmD&UR_-k z8Ybxl3aJ6OLiC6)Y6BVp^20tX zl_#h?g!lehp+;|CZZc>zI36V?B>VuXyib!H4l#xMiQlLec}%68H+taZ}OsVV<+G2pt_A$lVzjm{m6NX=m1| zhWMiPJ7aK}!DM^0NqXl{L}C(>Kbj)3%tynn0f_!9^mQ4P79k^`Q;vI&4%RgU&=f;? zMoLx74S~A14feYZuDT?mYnz+1IXdZTmA~z{2%-l2Ur>K>!uO&Ic#sIYN`97ug#a;f zcd0*#O-AR>cKIQ$J6w^i_bE_jQy2*25fZ|3YAEO_wp+I$evrXHK7E1lF5LXoU+y6v zOf4U1iLvxiueAbVG2Z%9RlC=K&e6xeSaYF2NmRjJV-(S;r#7TkkZhn@L(fU8n(;X1JL{-&=2C*gaIweKaeXw!V-(mX^f zi8-rMw6sW@b9XN0Z8Iw`-*#oy0tXlYB!Kvp2{4zW<}E!*213R5_Y~^;TU2N%y+UDrW4SDvqc_XgLE~8lKGTwWWI5`+&IBQ1_<0-bMP1 zSXTF77ard0xPf14AzimtV>^YSMU+`v{X#-q%@mqHf}E=)a;gr6n-bkuW&_{J&{^)N zZCMXgc86sP8oGj+8prh(oc^yKt~?&>=dHvyr=BD4@h>=&;!=%NVA2siP z;N&*fR_EP`Q^vRFU8sm)rR6qtwWEWpO7CHo?32ug4Sd>)nG98r`Rc9km<=Ug*eCfx(=+tkh*ut@$?aROX2Wl9e^TKpU-J3b zVBVPgfm1#u@Uomfez7%n6>!0`PATUst;u$#m)a7KqLx8xTpmk}|1a~RkW?0T} z?QOdR16QCmR8Z&Mm~(WKpIgqn^tV$3>fFo_)_i%Ic%X;a?sX$f75NJQbU59XtY0%d zwQ`?ODIrl79kehFT|#fr^{pp6sZI+*wDlJlY1yGauU@=AJ}`3gdN(mIl7!kEQ&CR6 zZfX_rW(n&SFdnPYY!)PtG;Tjvvt)ANk<)@*_<+ZEKkBDbaY(Va0Y*{4ySs1(;^2Wa zB^m2)WmAc_kTanQ$^RuzRbyCvjcTNjW54DAyott@t<)t3D^5bXL@tc0nISR zoB})bpAnT#^~l6i!P@1A+$XdBH{&Y*nV2qM5p{SEE!Yg3$oakaIHd2=C^f@TkPHO0>DBIymWuDOzikhT){@384w0<#p!5ppD?E0>$I&lb@-eYTA zc~aJ(VAb=pB=PeM>kS4jBRlnA+3niX@Oj2>5*Kp45CK->Rsr7Sb3B~0w~yI>tas%U zV$=ablzWC-ao2rBW&+YN&Pw>{gql*zgN(0kT56=1Mb1IyBicYlrF#oZu_tz^ZT6<47J4QT{{ezHO)FrTx@-i%1tO7t7Ja3Kl zw6VciY7e*O(#_qX5P7Br%gLhb)#R2hb1D1E51QQhq_N!`BA}`QmW#i|Fv_fNyukEPJF!^Lqw2z8~M1 z?xG7m_HnZ@nv(1<=^_(Ff}eWhpQU~tasHvgigUH2HmNd9Y)8z*!9*o3;f#{*1LuiU zW*MJW>|4C6T>?Qh_Mx^;(im7<;g?g_FN4<&KrFk)nq5d$r#k;1rSF7n0NIkYB)Rbf)ORYVWijPPY<|XSsyQC% z4cRfam6#E!v!DR0U)#K3nP?fEa0}dFD3=6aH^FcS>l2D9{_n=?qjhj9E19_Qg=XmD=Tj-?IDFAK!FMt|st6Qs!8)N3X5Z}jA>sI;;)T)14i zH%}iSNc3fSZGl95una*@WJRQm0GeZ)&*gjYC|aq-#|aP=8N|X;= z&1{+^ttslaVX5u*FZRyNRz0xT&k1on%pE-w%l_`(MDzVaN^x-B8EVdpe<&o{@ur^= z8-vGCs#oO|WrdFHvOyWA&XzZ5mdj~ZhP9xT)lmGvkYSR8=5#JSnh@<12qdC`Xu_zS@i3GS&y4syNlro0ChVB#g!f}E18K1KSngi5f*;ZF z*CB&gxFM2o0PKK8rxKyu(h&jRSs;hRMfv?>f?)7=DX4ZyLn_VBtLI***4=Q8C}V5h)=10gQ(q2VJS098^OrZB~yqV1u4{fK4 z(dg(Y&0a5S(>4ph+>rU5-Z{J+N=BUhg%5jJSmH|HG{m-9z!}~iuGW93Glq$@*3}|r z*+~Fa_uN$0{IZaDM=CyATVHRpq?`Y#l9up5lWqsIOs58$!cX@@R~-CzHH5$VG)Gl- zYoT)wt)lVCMHI=4*QIeh^!%kc|AZZUJFzC`!$t+l1jAw)grq`pP){zqdki#YsZsbzEJPOcXb|eGIzv|e@R8U5GHPV;LKVvvb>C=b9voCk zevqGGDeXHNgZg=9y#J+;3N^p=lklPMTTlhfkNl(?7gHL_=~@~c0i3nAGG?&~)CUY} cn=ezoqdAHfdD7{)>!VTCBLcyA^l;^StLd=lgT* zTr1hh(mgY4);;$`s4B~%AQ2(~0DvO*K}sC}VBY}%bUO$da%U%_)gDs7I7`TBfx72uOr(7o7k-Gei5`28ui+qv!@){nAa|sMB%@1sjo&Q4zj`t~i&k)Q zH^6MY;s_l*O+0ho6KuO$W%$mBnz-&V?Fvkk>#&)yKf?$ay7)jKu!WB z{B8F4b%!$k!r@yOG-+SGg3-sg{yRtEP<8q6&vlD9-_+%wZbv^TC`84mghxeLTU*=N z*i@92t#x$~LwZ4%IZA~oT(cK0OhivlPEOCt!oec4%u9`S15x z5e)%OI&(9y|AuizAg@x;oS;Ciy{>fT78JQn(!pD}`*xoTH{b%R8aQnbLVq(ZGMS;2k^_mvV|m zGIstXpWTvXv}`=RT27+s4dZVR6ed>?<#eG!N~y5ltQ>jBk;{_xbdgPc5Fv7jJ9NBg zGIJb_LUQY>JvT0(D$l97q@z4Os}T`iHFjwdwe~G!IXOjBfF>$u(u!NW?{w0Isw|w< z_t(RGHm~Q0?2k}ljU>#AKku*L%8A7D-J&DRwq-{it{-2C`LsJ+p5AURWJsX^8djZ^ zkrd-La>TMOpZiX-WpJROlXN{pavt)Sp!b)z5k3}Gcge%(b^m9#%~tqd1)D(lLC_~O zKVMs1vC)(}(MQAbv2G~UzD>D~qnogCaByAWz9ky`Bj;& zeFc7w?NhaPX#g9ygUdRqy)# z%KH7ZX`4|JO+*2J!jM?>DK%*~L37E?5xO&e&gHB9m1WU|nBLZory=Iaj`x_ev#iuE zF8>;w&^&Z$ZNN%xl3w0CMWYDnUYm+aHyKu3LzBzt;S#3|F9#l9gNxAgE59Md=fNOy zFcGg_RG|POXd`Zup*??a+YvrxaFX@_9D}0Jx!Hsy7w7RC0cAG$+pwl1L)yHs-vIVo z@q&kQ_3yxBm3_Gooplu^)}`Y-=O>8a?$1lx-g|k{!JGOF98BnFg}=eZ8TxFFzIFP3 z4mx{3MPq^1Wiof3m;LH6HqeGGtYU_pjSHD1!@iM0^UiEmWMF=t6{+uJJs%K|TL00A zpC0Z+xNW7DSJwZwcQ=B-``EWBTFp5A`&=u}5EY~A!hJ-fa_;$A-J4N~Aa(-dA11ye zgWuiyq^-VS41oS?%}f9KW$L8oc*e3BMNrFIVo+0ChySYr+Ur#0bqjAhe#I9C)ezrw zjFj2EEiN@$QjJ9m92_x$I-@Xgx0b(@(m^~!-K9op12nBP5>U`GN#O$)&P*F=>pM^&chLW{aebNSwO9 z=E_pucWIqz=nX-46|ZT1#Ww|`$5ZQ8^A;2qYiVgyNHKg%Of*ZFJaB0RO!^{|YMI?X z_#Dj)##29-LIH)>e>HtET|d~30;*@Rgof(kLEc&6;&Gj`|5vj+TW4xH@eT z574XY&`FH%hwudj;DPj%=ckLiCf#4DGi-E4rKGTm*l+=uO|%YsGq8ZHXD|g-&6%fV z!Q1<4y~)o3w}LzvM_9o_q%Y@f4we;gC9Cg=k)+h|IBBS+B2j+kD5ip7byyu;}ArF z!vE@?3~=Oqo;ZV@GF(WH1hIHN-l$L|Oo-?bAQ*sxnwwkt

                    INX2z^(xsX1R(Cuhu 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%0AvRTTXv^Z-B3#)f< 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 Iqz(Q414(1wY5)KL literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_start.png b/src/Tools/padder/doc/images/SMESH_spadder_start.png new file mode 100644 index 0000000000000000000000000000000000000000..160da9d261fd044cbc993290df667d76c9abc625 GIT binary patch literal 66799 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

                    H)7n%ClH9$Q=W|t* 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/plugins): + +\code +# This section specify the configurations to be used respectively for +# the local execution and the remote execution. The value for 'local' +# and 'remote' keys must be the name of a configuration section in +# this file. The default key must specify a value between "local" or +# "remote" to indicate the user preference. +[resources] +local = localhost +remote = nepal + +[preferences] +defaultres = local + +# The following sections defines the available configurations. +# The name of the section can be choosen arbitrary. But the value of +# the resname key MUST be the name of a SALOME resource defined in the +# catalog of resources (CatalogResources.xml). + +# For each section: +# - resname : the name of the SALOME resource to be used in this configuration +# - binpath : the path to the padder executable program on this resource +# - envpath : the path to the environment file on this resource +[localhost] +resname = localhost +binpath = /home/.programs/salome/workspace/V6_4_BR/SMESH/install/share/salome/resources/smesh/padderexe/padder.exe +envpath = /home/.programs/salome/workspace/V6_4_BR/SMESH/install/share/salome/resources/smesh/padderexe/envPadder.sh + +[nepal] +resname = nepal@nepal +binpath = /usr/local/bin/padder.exe +envpath = /usr/local/share/envPadder.sh +\endcode + +*/ + diff --git a/src/Tools/padder/meshjob/Makefile.am b/src/Tools/padder/meshjob/Makefile.am new file mode 100644 index 000000000..b1a47b9d9 --- /dev/null +++ b/src/Tools/padder/meshjob/Makefile.am @@ -0,0 +1,20 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +SUBDIRS = idl impl diff --git a/src/Tools/padder/meshjob/idl/MESHJOB.idl b/src/Tools/padder/meshjob/idl/MESHJOB.idl new file mode 100644 index 000000000..a5559cd77 --- /dev/null +++ b/src/Tools/padder/meshjob/idl/MESHJOB.idl @@ -0,0 +1,126 @@ +// Copyright (C) 2011-2012 CEA/DEN, EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 31/01/2011 + +#ifndef _MESHJOB_IDL_ +#define _MESHJOB_IDL_ + +#include "SALOME_Exception.idl" +#include "SALOME_Component.idl" + +// +// This interface is used for mesh job submission from within the +// SALOME plugin for PADDER. +// +module MESHJOB +{ + + // + // Structure to transmit the parameters requiered for the job to run + // the executable program on the target resource. See configure + // service. + // + struct ConfigParameter { + string resname; // The name of the SALOME resource to be used + string binpath; // The path of the executable program on this resource + string envpath; // The path of the environment file on this resource + }; + + // + // This set of specification defines the data structure used to + // initialize a job on a specific resource, then supervise the job + // and finally retrieve the result data. + // + + // This defines the set of temporary folders used by the jobmanager + // when executing a job (may depends on the job). + struct MeshJobPaths + { + string local_inputdir; + string local_resultdir; + string remote_workdir; + }; + + // This defines the possible types for a job parameter + enum FileType {MED_CONCRETE, MED_STEELBAR}; + + // This defines a single parameter for the job initialization (a med file) + struct MeshJobParameter + { + string file_name; + FileType file_type; + string group_name; + }; + + // This defines a set of parameters for the job initialization + typedef sequence MeshJobParameterList; + + // This defines the result data of a job + struct MeshJobResults + { + string results_dirname; + string outputmesh_filename; + string status; + }; + + // This defines the possible states of a job + enum MeshJobState {CREATED, IN_PROCESS, QUEUED, RUNNING, PAUSED, FINISHED, ERROR}; + + // + // This interface defines the computation services of the component + // + + interface MeshJobManager: Engines::EngineComponent + { + + /*! Add a resource configuration, identified by the string + configId and characterized by the parameters in + configParameter */ + boolean configure(in string configId, in MESHJOB::ConfigParameter configParameter) + raises (SALOME::SALOME_Exception); + + /*! Initialize a smesh computation job and return the job identifier */ + long initialize(in MESHJOB::MeshJobParameterList meshJobParameterList, in string configId) + raises (SALOME::SALOME_Exception); + + /*! Submit the job execution and return true if submission is OK */ + boolean start(in long jobId) + raises (SALOME::SALOME_Exception); + + /*! Request the launch manager for the state of the specified job */ + string getState(in long jobId) + raises (SALOME::SALOME_Exception); + + /*! Request the launch manager for downloading the results */ + MeshJobResults finalize(in long jobid) + raises (SALOME::SALOME_Exception); + + /*! Clean all data associated to this job and remove the job from the launch manager */ + boolean clean(in long jobId) + raises (SALOME::SALOME_Exception); + + /*! Returns the set of temporary folders used by the job instance */ + MeshJobPaths getPaths(in long jobId) + raises (SALOME::SALOME_Exception); + + }; + +}; + +#endif // _MESHJOB_IDL_ diff --git a/src/Tools/padder/meshjob/idl/Makefile.am b/src/Tools/padder/meshjob/idl/Makefile.am new file mode 100644 index 000000000..15bb1a87c --- /dev/null +++ b/src/Tools/padder/meshjob/idl/Makefile.am @@ -0,0 +1,95 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +# This Makefile is responsible of generating the client and server +# implementation of IDL interfaces for both C++ and python usage. +# The building process of the C++ files is in charge of each source +# package and then is not manage here. +# +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +BUILT_SOURCES = MESHJOBSK.cc +IDL_FILES = MESHJOB.idl + +# For test purpose, we add a little component: +BUILT_SOURCES += SPADDERPluginTestSK.cc +IDL_FILES += SPADDERPluginTest.idl + +IDL_FILES_PY=$(IDL_FILES:%.idl=%_idl.py) + +salomeidl_DATA = $(IDL_FILES) + +lib_LTLIBRARIES = libSalomeIDLSPADDER.la +libSalomeIDLSPADDER_la_SOURCES = +nodist_libSalomeIDLSPADDER_la_SOURCES = $(BUILT_SOURCES) +nodist_salomeinclude_HEADERS= $(IDL_FILES:%idl=%hh) + +OMNIORB_CXXFLAGS=@OMNIORB_CXXFLAGS@ @OMNIORB_INCLUDES@ +OMNIORB_LIBS=@OMNIORB_LIBS@ + +libSalomeIDLSPADDER_la_CXXFLAGS = \ + $(KERNEL_CXXFLAGS) \ + $(OMNIORB_CXXFLAGS) \ + -I. + +libSalomeIDLSPADDER_la_LIBADD = \ + $(KERNEL_LDFLAGS) -lSalomeIDLKernel \ + $(OMNIORB_LIBS) + + +# These variables defines the building process of CORBA files +IDLCXXFLAGS = \ + -bcxx -I. \ + @OMNIORB_IDLCXXFLAGS@ \ + -I$(KERNEL_ROOT_DIR)/idl/salome + +IDLPYFLAGS = \ + -I. \ + @OMNIORB_IDLPYFLAGS@ \ + -I$(KERNEL_ROOT_DIR)/idl/salome + +########################################################## +SUFFIXES = .idl .hh SK.cc + +%SK.cc %.hh : %.idl + $(OMNIORB_IDL) $(IDLCXXFLAGS) $< + +%_idl.py : %.idl + $(OMNIORB_IDL) $(IDLPYFLAGS) $< + +CLEANFILES = *.hh *SK.cc *.py *.hxx *.cxx + +EXTRA_DIST += $(IDL_FILES) + +install-data-local: $(IDL_FILES) + $(INSTALL) -d $(DESTDIR)$(salomepythondir) + ls $^ | while read file; do \ + $(OMNIORB_IDL) $(IDLPYFLAGS) -C$(DESTDIR)$(salomepythondir) $$file ; \ + done + +# we want to remove only staff generated for IDL files and nothing more +uninstall-local: + @for modulen in MESHJOB SPADDERPluginTest ; do \ + test -d $(DESTDIR)$(salomepythondir)/$${modulen} && echo "Removing $(DESTDIR)$(salomepythondir)/$${modulen}" && rm -rf $(DESTDIR)$(salomepythondir)/$${modulen} ; \ + test -d $(DESTDIR)$(salomepythondir)/$${modulen}__POA && echo "Removing $(DESTDIR)$(salomepythondir)/$${modulen}__POA" && rm -rf $(DESTDIR)$(salomepythondir)/$${modulen}__POA ; \ + done ; \ + for filen in $(IDL_FILES_PY) ; do \ + echo "Removing $(DESTDIR)$(salomepythondir)/$${filen}" && rm -f $(DESTDIR)$(salomepythondir)/$${filen}* ; \ + done + diff --git a/src/Tools/padder/meshjob/idl/SPADDERPluginTest.idl b/src/Tools/padder/meshjob/idl/SPADDERPluginTest.idl new file mode 100644 index 000000000..e0beec0e9 --- /dev/null +++ b/src/Tools/padder/meshjob/idl/SPADDERPluginTest.idl @@ -0,0 +1,46 @@ +// Copyright (C) 2011-2012 CEA/DEN, EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 31/01/2011 + +#ifndef _SPADDERPLUGINTEST_IDL_ +#define _SPADDERPLUGINTEST_IDL_ + +#include "SALOME_Exception.idl" +#include "SALOME_Component.idl" + +module SPADDERPluginTest { + + // + // ======================================================================== + // Thi module defines an interface provided for testing the usage + // of SPADDERPlugin components and underlying classes from within a + // C++ unit test running in a SALOME container (easy to run from a + // python client).. + // ======================================================================== + // + + interface SPADDERPluginTester: Engines::EngineComponent + { + void demo(in double a,in double b,out double c) raises (SALOME::SALOME_Exception); + boolean testkernel() raises (SALOME::SALOME_Exception); + boolean testsmesh(in long studyId) raises (SALOME::SALOME_Exception); + }; +}; + +#endif // _SPADDERPLUGINTEST_IDL_ diff --git a/src/Tools/padder/meshjob/impl/Makefile.am b/src/Tools/padder/meshjob/impl/Makefile.am new file mode 100644 index 000000000..31a6dbbdf --- /dev/null +++ b/src/Tools/padder/meshjob/impl/Makefile.am @@ -0,0 +1,89 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 + +AM_CFLAGS=$(SALOME_INCLUDES) -fexceptions + +lib_LTLIBRARIES= libMeshJobManagerEngine.la + +salomeinclude_HEADERS= \ + MeshJobManager_i.hxx + + +# ============================================================= +# Definition of MeshJobManagerEngine construction +# ============================================================= +libMeshJobManagerEngine_la_SOURCES = \ + MeshJobManager_i.cxx + +LIBXML_LIBS=@LIBXML_LIBS@ +KERNEL_CXXFLAGS=@KERNEL_CXXFLAGS@ + +libMeshJobManagerEngine_la_CXXFLAGS = \ + -I$(top_builddir)/src/Tools/padder/meshjob/idl $(KERNEL_CXXFLAGS) \ + @CORBA_CXXFLAGS@ @CORBA_INCLUDES@ @LIBXML_INCLUDES@ + +libMeshJobManagerEngine_la_FFLAGS = -fexceptions +libMeshJobManagerEngine_la_LDFLAGS = \ + $(top_builddir)/src/Tools/padder/meshjob/idl/libSalomeIDLSPADDER.la \ + @KERNEL_LDFLAGS@ -lSalomeContainer -lSalomeKernelHelpers -lSalomeLifeCycleCORBA \ + @LIBXML_LIBS@ + +# ============================================================= +# Definition of the SPADDERPluginTester engine construction +# ============================================================= +lib_LTLIBRARIES += libSPADDERPluginTesterEngine.la + +libSPADDERPluginTesterEngine_la_SOURCES = \ + SPADDERPluginTester_i.hxx \ + SPADDERPluginTester_i.cxx + +nodist_libSPADDERPluginTesterEngine_la_SOURCES = + +libSPADDERPluginTesterEngine_la_CXXFLAGS = \ + -I$(top_builddir)/src/Tools/padder/meshjob/idl $(KERNEL_CXXFLAGS) \ + @CORBA_CXXFLAGS@ @CORBA_INCLUDES@ @LIBXML_INCLUDES@ + +libSPADDERPluginTesterEngine_la_FFLAGS = -fexceptions +libSPADDERPluginTesterEngine_la_LIBADD = \ + $(top_builddir)/src/Tools/padder/meshjob/idl/libSalomeIDLSPADDER.la \ + @KERNEL_LDFLAGS@ -lSalomeContainer -lSalomeKernelHelpers \ + @LIBXML_LIBS@ + + +# For testing SMESH +# +libSPADDERPluginTesterEngine_la_CXXFLAGS += \ + @GEOM_CXXFLAGS@ @MED_CXXFLAGS@ \ + -I$(top_builddir)/idl \ + -I$(top_srcdir)/src/SMESH \ + -I$(top_srcdir)/src/SMESH_I \ + -I$(top_srcdir)/src/SMESHDS \ + -I$(top_srcdir)/src/SMDS \ + -I$(top_srcdir)/src/SMESHUtils \ + $(VTK_INCLUDES) $(CAS_CPPFLAGS) $(BOOST_CPPFLAGS) + +libSPADDERPluginTesterEngine_la_LIBADD += \ + $(top_builddir)/src/SMESH/libSMESHimpl.la \ + $(top_builddir)/src/SMESH_I/libSMESHEngine.la \ + $(top_builddir)/src/SMESHDS/libSMESHDS.la \ + $(top_builddir)/src/SMDS/libSMDS.la \ + $(top_builddir)/src/SMESHUtils/libSMESHUtils.la + diff --git a/src/Tools/padder/meshjob/impl/MeshJobManager_i.cxx b/src/Tools/padder/meshjob/impl/MeshJobManager_i.cxx new file mode 100644 index 000000000..72520fbab --- /dev/null +++ b/src/Tools/padder/meshjob/impl/MeshJobManager_i.cxx @@ -0,0 +1,631 @@ +// Copyright (C) 2011-2012 EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 01/03/2011 + +#ifdef WIN32 +#include +#include +#else +#include +#endif + +#include "MeshJobManager_i.hxx" + +#include +#include CORBA_SERVER_HEADER(SALOME_Exception) + + +#include "Basics_Utils.hxx" // For standard logging +#undef LOG +#include "SALOME_KernelServices.hxx" // For CORBA logging +#undef LOG + +#define LOG STDLOG + +// +// ==================================================================== +// General purpose helper functions (to put elsewhere at least) +// ==================================================================== +// + +/*! + * This function must be used to associate a datetime tag to a job + */ + +#ifndef WIN32 +static long timetag() { + timeval tv; + gettimeofday(&tv,0); + long tag = tv.tv_usec + tv.tv_sec*1000000; + return tag; +} +#endif + +/*! + * This function returns true if the string text starts with the string + * token. + */ +static bool myStartsWith(const std::string& text,const std::string& token){ + if(text.length() < token.length()) + return false; + return (text.compare(0, token.length(), token) == 0); +} + +// +// ==================================================================== +// Constructor/Destructor +// ==================================================================== +// +MeshJobManager_i::MeshJobManager_i(CORBA::ORB_ptr orb, + PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, + const char *interfaceName) + : Engines_Component_i(orb, poa, contId, instanceName, interfaceName) +{ + LOG("Activating MESHJOB::MeshJobManager object"); + _thisObj = this ; + _id = _poa->activate_object(_thisObj); + + _salomeLauncher = KERNEL::getSalomeLauncher(); + if(CORBA::is_nil(_salomeLauncher)){ + LOG("The SALOME launcher can't be reached ==> STOP"); + throw KERNEL::createSalomeException("SALOME launcher can't be reached"); + } + + _resourcesManager = KERNEL::getResourcesManager(); + if(CORBA::is_nil(_resourcesManager)){ + LOG("The SALOME resource manager can't be reached ==> STOP"); + throw KERNEL::createSalomeException("The SALOME resource manager can't be reached"); + } +} + +MeshJobManager_i::~MeshJobManager_i() { + LOG("MeshJobManager_i::~MeshJobManager_i()"); +} + +// +// ==================================================================== +// Helper functions to deals with the local and remote file systems +// ==================================================================== +// +#include // to get the file streams +#ifdef WNT +#include // to get _splitpath +#include // to get _mkdir +#else +#include // to get basename +#include // to get mkdir +#include // to get mkdir options +#endif + +#include // to get system and getenv + +static std::string OUTPUTFILE("output.med"); +static std::string DATAFILE("data.txt"); +static std::string SCRIPTFILE("padder.sh"); +static std::string SEPARATOR(" "); + +static std::string USER(getenv("USER")); +static std::string LOCAL_INPUTDIR("/tmp/spadder.local.inputdir."+USER); +static std::string LOCAL_RESULTDIR("/tmp/spadder.local.resultdir."+USER); +static std::string REMOTE_WORKDIR("/tmp/spadder.remote.workdir."+USER); + +/*! + * This function creates the padder text input file containing the + * input data (list of filenames and groupnames) and returns the path + * of the created file. This function is the one that knows the format + * of the padder input file. If the input file format changes, then + * this function (and only this one) should be updated. + */ +const char * MeshJobManager_i::_writeDataFile(std::vector listConcreteMesh, + std::vector listSteelBarMesh) { +#ifdef WIN32 + _mkdir(LOCAL_INPUTDIR.c_str()); +#else + mkdir(LOCAL_INPUTDIR.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#endif + + // Make it static so that it's allocated once (constant name) + static std::string * dataFilename = new std::string(LOCAL_INPUTDIR+"/"+DATAFILE); + std::ofstream dataFile(dataFilename->c_str()); + + // We first specify the concrete mesh data (filename and groupname) + std::string line; +#ifdef WIN32 + char fname[ _MAX_FNAME ]; + _splitpath( listConcreteMesh[0].file_name, NULL, NULL, fname, NULL ); + char* bname = &fname[0]; +#else + char* bname = basename(listConcreteMesh[0].file_name); +#endif + line = std::string(bname) + " " + std::string(listConcreteMesh[0].group_name); + dataFile << line.c_str() << std::endl; + // Note that we use here the basename because the files are supposed + // to be copied in the REMOTE_WORKDIR for execution. + + // The, we can specify the steelbar mesh data, starting by the + // number of meshes + int nbSteelBarMesh=listSteelBarMesh.size(); + line = std::string("nbSteelbarMesh") + SEPARATOR + ToString(nbSteelBarMesh); + dataFile << line.c_str() << std::endl; + for (int i=0; ic_str(); +} + +/*! + * This function creates a shell script that runs padder whith the + * specified data file, and returns the path of the created script + * file. The config id is used to retrieve the path to the binary file + * and other required files. + */ +const char* MeshJobManager_i::_writeScriptFile(const char * dataFileName, const char * configId) { +#ifdef WIN32 + _mkdir(LOCAL_INPUTDIR.c_str()); +#else + mkdir(LOCAL_INPUTDIR.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#endif + + // Make it static so that it's allocated once (constant name) + static std::string * scriptFilename = new std::string(LOCAL_INPUTDIR+"/"+SCRIPTFILE); + + char * binpath = _configMap[configId].binpath; + char * envpath = _configMap[configId].envpath; + +#ifdef WIN32 + char fname[ _MAX_FNAME ]; + _splitpath( dataFileName, NULL, NULL, fname, NULL ); + const char* bname = &fname[0]; +#else + const char* bname = basename(dataFileName); +#endif + + + std::ofstream script(scriptFilename->c_str()); + script << "#!/bin/sh" << std::endl; + script << "here=$(dirname $0)" << std::endl; + script << ". " << envpath << std::endl; + script << binpath << " $here/" << bname << std::endl; + // Note that we use the basename of the datafile because all data + // files are supposed to have been copied in the REMOTE_WORKDIR. + script.close(); + return scriptFilename->c_str(); +} + +// +// ==================================================================== +// Functions to initialize and supervise the mesh computation job +// ==================================================================== +// +bool MeshJobManager_i::configure(const char *configId, + const MESHJOB::ConfigParameter & configParameter) +{ + beginService("MeshJobManager_i::configure"); + + _configMap[configId] = configParameter; + + LOG("Adding configuration for " << configId); + LOG("- binpath = " << _configMap[configId].binpath); + LOG("- envpath = " << _configMap[configId].envpath); + + endService("MeshJobManager_i::configure"); + return true; +} + +long MeshJobManager_i::JOBID_UNDEFINED = -1; + +/*! Initialize a smesh computation job and return the job identifier */ +CORBA::Long MeshJobManager_i::initialize(const MESHJOB::MeshJobParameterList & meshJobParameterList, + const char * configId) +{ + beginService("MeshJobManager_i::initialize"); + std::cerr << "##################################### initialize" << std::endl; + std::cerr << "#####################################" << std::endl; + + // + // We first analyse the CORBA sequence to store data in C++ vectors + // + std::vector listConcreteMesh; + std::vector listSteelBarMesh; + for(CORBA::ULong i=0; iremote_workdir = (REMOTE_WORKDIR + "." + ToString(jobDatetimeTag)).c_str(); + + // + // Then, we have to create the padder input data file. This input + // data is a text file containing the list of file names and group + // names. + // + const char * dataFilename = this->_writeDataFile(listConcreteMesh, listSteelBarMesh); + LOG("dataFilename = " << dataFilename); + const char * scriptFilename = this->_writeScriptFile(dataFilename, configId); + LOG("scriptFilename = " << scriptFilename); + + // + // Then, the following instructions consists in preparing the job + // parameters to request the SALOME launcher for creating a new + // job. + // + Engines::JobParameters_var jobParameters = new Engines::JobParameters; + jobParameters->job_type = CORBA::string_dup("command"); + // CAUTION: the job_file must be a single filename specifying a + // self-consistent script to be executed without any argument on the + // remote host. + jobParameters->job_file = CORBA::string_dup(scriptFilename); + + // + // Specification of the working spaces: + // + // - local_directory: can be used to specify where to find the input + // files on the local resource. It's optionnal if you specify the + // absolute path name of input files. + // + // - result_directory: must be used to specify where to download the + // output files on the local resources + // + // - work_directory: must be used to specify the remote directory + // where to put all the stuff to run the job. Note that the job + // will be executed from within this directory, i.e. a change + // directory toward this working directory is done by the batch + // system before running the specified job script. + // + jobParameters->local_directory = CORBA::string_dup(""); + jobParameters->result_directory = CORBA::string_dup(jobPaths->local_resultdir); + jobParameters->work_directory = CORBA::string_dup(jobPaths->remote_workdir); + + // We specify the input files that are required to execute the + // job_file. If basenames are specified, then the files are supposed + // to be located in local_directory. + int nbFiles = listSteelBarMesh.size()+2; + // The number of input file is: + // (nb. of steelbar meshfile) + // + (1 concrete meshfile) + // + (1 padder input file) + // = nb steelbar meshfile + 2 + jobParameters->in_files.length(nbFiles); + jobParameters->in_files[0] = CORBA::string_dup(listConcreteMesh[0].file_name); + for (int i=0; iin_files[1+i] = CORBA::string_dup(listSteelBarMesh[i].file_name); + } + jobParameters->in_files[1+listSteelBarMesh.size()] = CORBA::string_dup(dataFilename); + // Note that all these input files will be copied in the + // REMOTE_WORKDIR on the remote host + + // Then, we have to specify the existance of an output + // filenames. The path is supposed to be a path on the remote + // resource, i.e. where the job is executed. + jobParameters->out_files.length(1); + std::string outputfile_name = std::string(jobPaths->remote_workdir)+"/"+OUTPUTFILE; + jobParameters->out_files[0] = CORBA::string_dup(outputfile_name.c_str()); + + // CAUTION: the maximum duration has to be set with a format like "hh:mm" + jobParameters->maximum_duration = CORBA::string_dup("01:00"); + jobParameters->queue = CORBA::string_dup(""); + + // Setting resource and additionnal properties (if needed) + // The resource parameters can be initiated from scratch, for + // example by specifying the values in hard coding: + // >>> + //jobParameters->resource_required.name = CORBA::string_dup("localhost"); + //jobParameters->resource_required.hostname = CORBA::string_dup("localhost"); + //jobParameters->resource_required.mem_mb = 1024 * 10; + //jobParameters->resource_required.nb_proc = 1; + // <<< + // But it's better to initiate these parameters from a resource + // definition known by the resource manager. This ensures that the + // resource will be available: + //const char * resourceName = "localhost"; + //const char * resourceName = "boulant@claui2p1"; + //const char * resourceName = "nepal@nepal"; + const char * resourceName = _configMap[configId].resname; + Engines::ResourceDefinition * resourceDefinition = _resourcesManager->GetResourceDefinition(resourceName); + // CAUTION: This resource should have been defined in the + // CatalogResource.xml associated to the SALOME application. + // + // Then, the values can be used to initiate the resource parameters + // of the job: + jobParameters->resource_required.name = CORBA::string_dup(resourceDefinition->name.in()); + // CAUTION: the additionnal two following parameters MUST be + // specified explicitly, because they are not provided by the + // resource definition: + jobParameters->resource_required.mem_mb = resourceDefinition->mem_mb; + jobParameters->resource_required.nb_proc = resourceDefinition->nb_proc_per_node; + // CAUTION: the parameter mem_mb specifies the maximum memory value + // that could be allocated for executing the job. This takes into + // account not only the data that could be loaded by the batch + // process but also the linked dynamic library. + // + // A possible problem, for exemple in the case where you use the ssh + // emulation of a batch system, is to get an error message as below + // when libBatch try to run the ssh command: + // + // ## /usr/bin/ssh: error while loading shared libraries: libcrypto.so.0.9.8: failed + // ## to map segment from shared object: Cannot allocate memory + // + // In this exemple, the mem_mb was set to 1MB, value that is not + // sufficient to load the dynamic libraries linked to the ssh + // executable (libcrypto.so in the error message). + // + // So, even in the case of a simple test shell script, you should + // set this value at least to a standard threshold as 500MB + + int jobId = JOBID_UNDEFINED; + try { + std::cerr << "#####################################" << std::endl; + std::cerr << "#####################################" << std::endl; + std::cerr << "jobUndef = " << JOBID_UNDEFINED << std::endl; + jobId = _salomeLauncher->createJob(jobParameters); + std::cerr << "#####################################" << std::endl; + std::cerr << "#####################################" << std::endl; + std::cerr << "#####################################" << std::endl; + std::cerr << "jobId = " << jobId << std::endl; + // We register the datetime tag of this job + _jobDateTimeMap[jobId]=jobDatetimeTag; + _jobPathsMap[jobId] = jobPaths; + } + catch (const SALOME::SALOME_Exception & ex) { + LOG("SALOME Exception in createJob !" <launchJob(jobId); + } + catch (const SALOME::SALOME_Exception & ex) { + LOG("SALOME Exception in launchjob !" <getJobState(jobId); + } + catch (const SALOME::SALOME_Exception & ex) + { + LOG("SALOME Exception in getJobState !"); + state = ex.details.text; + } + catch (const CORBA::SystemException& ex) + { + LOG("Receive SALOME System Exception: " << ex); + state="SALOME System Exception - see logs"; + } + LOG("jobId="<getPaths(jobId); + std::string local_resultdir(jobPaths->local_resultdir); + result->results_dirname = local_resultdir.c_str(); + try + { + _salomeLauncher->getJobResults(jobId, local_resultdir.c_str()); + + // __BUG__: to prevent from a bug of the MED driver (SALOME + // 5.1.5), we change the basename of the output file to force the + // complete reloading of data by the med driver. + long jobDatetimeTag = _jobDateTimeMap[jobId]; + std::string outputFileName = "output"+ToString(jobDatetimeTag)+".med"; + rename((local_resultdir+"/"+OUTPUTFILE).c_str(), (local_resultdir+"/"+outputFileName).c_str()); + + result->outputmesh_filename = outputFileName.c_str(); + result->status = "OK"; + } + catch (const SALOME::SALOME_Exception & ex) + { + LOG("SALOME Exception in getResults !"); + result->status = "SALOME Exception in getResults !"; + } + catch (const CORBA::SystemException& ex) + { + LOG("Receive CORBA System Exception: " << ex); + result->status = "Receive CORBA System Exception: see log"; + } + endService("MeshJobManager_i::getResults"); + return result; +} + + +/*! Clean all data associated to this job and remove the job from the launch manager */ +bool MeshJobManager_i::clean(CORBA::Long jobId) { + beginService("MeshJobManager_i::clean"); + + // __GBO__ WORK IN PROGRESS: we just clean the temporary local + // directories. The remote working directories are tag with the + // execution datetime and the we prevent the task from conflict + // with files of another task. + MESHJOB::MeshJobPaths * jobPaths = this->getPaths(jobId); + if ( jobPaths == NULL ) return false; + + // WARN: !!!!! + // For safety reason (and prevent from bug that could erase the + // filesystem), we cancel the operation in the case where the + // directories to delete are not in the /tmp folder. + std::string shell_command("rm -rf "); + std::string inputdir(jobPaths->local_inputdir); + std::string resultdir(jobPaths->local_resultdir); + if ( !myStartsWith(inputdir,"/tmp/") ) { + LOG("WRN: The directory "< * MeshJobManager_i::_getResourceNames() { + + // + // These part is just to control the available resources + // + Engines::ResourceParameters params; + KERNEL::getLifeCycleCORBA()->preSet(params); + + Engines::ResourceList * resourceList = _resourcesManager->GetFittingResources(params); + Engines::ResourceDefinition * resourceDefinition = NULL; + LOG("### resource list:"); + std::vector* resourceNames = new std::vector(); + if (resourceList) { + for (int i = 0; i < resourceList->length(); i++) { + const char* aResourceName = (*resourceList)[i]; + resourceNames->push_back(std::string(aResourceName)); + LOG("resource["<GetResourceDefinition(aResourceName); + LOG("protocol["<protocol); + } + } + + // Note: a ResourceDefinition is used to create a batch configuration + // in the Launcher. This operation is done at Launcher startup from + // the configuration file CatalogResources.xml provided by the + // SALOME application. + // In the code instructions, you just have to choose a resource + // configuration by its name and then define the ResourceParameters + // that specify additionnal properties for a specific job submission + // (use the attribute resource_required of the JobParameters). + + return resourceNames; +} + + +// +// ========================================================================== +// Factory services +// ========================================================================== +// +extern "C" +{ + PortableServer::ObjectId * MeshJobManagerEngine_factory( CORBA::ORB_ptr orb, + PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, + const char *interfaceName) + { + LOG("PortableServer::ObjectId * MeshJobManagerEngine_factory()"); + MeshJobManager_i * myEngine = new MeshJobManager_i(orb, poa, contId, instanceName, interfaceName); + return myEngine->getId() ; + } +} diff --git a/src/Tools/padder/meshjob/impl/MeshJobManager_i.hxx b/src/Tools/padder/meshjob/impl/MeshJobManager_i.hxx new file mode 100644 index 000000000..283a92d7a --- /dev/null +++ b/src/Tools/padder/meshjob/impl/MeshJobManager_i.hxx @@ -0,0 +1,81 @@ +// Copyright (C) 2011-2012 EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 01/03/2011 + +#ifndef _MESHJOBMANAGER_HXX_ +#define _MESHJOBMANAGER_HXX_ + +// include the stubs generating from MESHJOB.idl +#include +#include CORBA_SERVER_HEADER(MESHJOB) +#include CORBA_SERVER_HEADER(SALOME_Component) +#include "SALOME_Component_i.hxx" + +#include "SALOME_Launcher.hxx" +#include +#include +#include + +class MeshJobManager_i: public virtual POA_MESHJOB::MeshJobManager, + public Engines_Component_i +{ +public: + MeshJobManager_i(CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, const char *interfaceName); + ~MeshJobManager_i(); + + bool configure (const char *configId, + const MESHJOB::ConfigParameter & configParameter); + CORBA::Long initialize (const MESHJOB::MeshJobParameterList & meshJobParameterList, + const char *configId); + bool start (CORBA::Long jobId); + char* getState (CORBA::Long jobId); + MESHJOB::MeshJobResults * finalize(CORBA::Long jobId); + MESHJOB::MeshJobPaths * getPaths(CORBA::Long jobId); + bool clean (CORBA::Long jobId); + + static long JOBID_UNDEFINED; + +private: + Engines::SalomeLauncher_var _salomeLauncher; + Engines::ResourcesManager_var _resourcesManager; + + // This maps the config identifier to the config parameters. A + // config is a resource with additionnal data specifying the + // location of the binary program to be executed by the task + std::map _configMap; + + // This maps a job identifier to its associated datetime tag. When + // a job is created during the initialize function, a datetime tag + // is associated to this job and can be used to characterized files + // and directories associated to this job. + std::map _jobDateTimeMap; + std::map _jobPathsMap; + + const char* _writeDataFile (std::vector listConcreteMesh, + std::vector listSteelBarMesh); + const char* _writeScriptFile (const char * dataFileName, const char * configId); + + std::vector * _getResourceNames(); + +}; + +#endif + diff --git a/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.cxx b/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.cxx new file mode 100644 index 000000000..805eb4f91 --- /dev/null +++ b/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.cxx @@ -0,0 +1,157 @@ +// Copyright (C) 2011-2012 CEA/DEN, EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 01/03/2011 + +#include "SPADDERPluginTester_i.hxx" + +#include +#include + +// For standard logging +#include "Basics_Utils.hxx" + +// +// ========================================================================== +// Implementation of the SPADDER component interface +// ========================================================================== +// +using namespace std; + +/*! + * Constructor for component "SPADDER" instance + */ +SPADDERPluginTester_i::SPADDERPluginTester_i(CORBA::ORB_ptr orb, + PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, + const char *interfaceName) + : Engines_Component_i(orb, poa, contId, instanceName, interfaceName) +{ + LOG("Activating SPADDERPluginTester_i::SPADDERPluginTester object"); + _thisObj = this ; + _id = _poa->activate_object(_thisObj); +} + +//! Destructor for component "SPADDER" instance +SPADDERPluginTester_i::~SPADDERPluginTester_i() +{ + std::cerr << "SPADDERPluginTester destruction" << std::endl; +} + +/*! + * This test is just to check the component SPADDER. + */ +void SPADDERPluginTester_i::demo(CORBA::Double a,CORBA::Double b,CORBA::Double& c) +{ + beginService("SPADDERPluginTester_i::demo"); + try { + //BODY + + std::cerr << "a: " << a << std::endl; + std::cerr << "b: " << b << std::endl; + c=a+b; + std::cerr << "c: " << c << std::endl; + + } + catch ( const SALOME_Exception & ex) { + SALOME::ExceptionStruct es; + es.text=CORBA::string_dup(ex.what()); + es.type=SALOME::INTERNAL_ERROR; + throw SALOME::SALOME_Exception(es); + } + catch ( const SALOME::SALOME_Exception & ex) { + throw; + } + catch (...) { + std::cerr << "unknown exception" << std::endl; + SALOME::ExceptionStruct es; + es.text=CORBA::string_dup(" unknown exception"); + es.type=SALOME::INTERNAL_ERROR; + throw SALOME::SALOME_Exception(es); + } + endService("SPADDERPluginTester_i::demo"); +} + +#ifdef LOG +#undef LOG +#endif + +#include +#include "SALOME_Launcher.hxx" +bool SPADDERPluginTester_i::testkernel() +{ + beginService("SPADDERPluginTester_i::testplugin"); + + Engines::SalomeLauncher_ptr salomeLauncher = KERNEL::getSalomeLauncher(); + + endService("SPADDERPluginTester_i::testplugin"); + return true; +} + + +#include +#include +#include +#include CORBA_CLIENT_HEADER(SALOMEDS) + +#include + +/*! + * This test checks the constructor of the basic classes of the SMESH + * plugin for PADDER. + */ +bool SPADDERPluginTester_i::testsmesh(CORBA::Long studyId) +{ + beginService("SPADDERPluginTester_i::testsmesh"); + + // Resolve the SMESH engine and the SALOME study + // _WARN_ The SMESH engine should have been loaded first + SMESH_Gen_i* smeshGen_i = SMESH_Gen_i::GetSMESHGen(); + CORBA::Object_var anObject = smeshGen_i->GetNS()->Resolve("/myStudyManager"); + SALOMEDS::StudyManager_var aStudyMgr = SALOMEDS::StudyManager::_narrow(anObject); + SALOMEDS::Study_var myStudy = aStudyMgr->GetStudyByID(studyId); + + // + // _MEM_ CAUTION: SMESH_Gen define a data structure for local usage + // while SMESH_Gen_i is the implementation of the SMESH_Gen IDL + // interface. + // + + endService("SPADDERPluginTester_i::testsmesh"); + return true; +} + +// +// ========================================================================== +// Factory services +// ========================================================================== +// +extern "C" +{ + PortableServer::ObjectId * SPADDERPluginTesterEngine_factory( CORBA::ORB_ptr orb, + PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, + const char *interfaceName) + { + MESSAGE("PortableServer::ObjectId * SPADDERPluginTesterEngine_factory()"); + SPADDERPluginTester_i * myEngine = new SPADDERPluginTester_i(orb, poa, contId, instanceName, interfaceName); + return myEngine->getId() ; + } +} diff --git a/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.hxx b/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.hxx new file mode 100644 index 000000000..26a7416cd --- /dev/null +++ b/src/Tools/padder/meshjob/impl/SPADDERPluginTester_i.hxx @@ -0,0 +1,47 @@ +// Copyright (C) 2011-2012 CEA/DEN, EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 01/03/2011 + +#ifndef _SPADDER_PLUGINTESTER_HXX_ +#define _SPADDER_PLUGINTESTER_HXX_ + +// include the stubs generating from SPADDERPluginTest.idl +#include +#include CORBA_SERVER_HEADER(SPADDERPluginTest) +#include +#include "SALOME_Component_i.hxx" + +class SPADDERPluginTester_i: + public virtual POA_SPADDERPluginTest::SPADDERPluginTester, + public Engines_Component_i +{ +public: + SPADDERPluginTester_i(CORBA::ORB_ptr orb, PortableServer::POA_ptr poa, + PortableServer::ObjectId * contId, + const char *instanceName, const char *interfaceName); + virtual ~SPADDERPluginTester_i(); + + void demo(CORBA::Double a,CORBA::Double b,CORBA::Double& c); + bool testkernel(); + bool testsmesh(CORBA::Long studyId); + +}; + +#endif + diff --git a/src/Tools/padder/meshjob/impl/testhelper.hxx b/src/Tools/padder/meshjob/impl/testhelper.hxx new file mode 100644 index 000000000..adc7fdf29 --- /dev/null +++ b/src/Tools/padder/meshjob/impl/testhelper.hxx @@ -0,0 +1,66 @@ +// Copyright (C) 2011-2012 CEA/DEN, EDF R&D +// +// 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 +// +// Authors : Guillaume Boulant (EDF) - 01/03/2011 + +#ifndef __TESTHELPER_HXX__ +#define __TESTHELPER_HXX__ + +// >>> +// WARN: this file is DEPRECATED and/or should be used for test +// purpose only. The PADDER configuration is now read in a +// configuration file (padder.cfg). +// <<< + +#include // Standard C include (for getenv) +#include +#include + +/*! + * This function returns the module SMESH installation root directory + * as a string. + */ +static std::string SMESH_ROOT_DIR() { + static std::string * smesh_root_dir; + if ( smesh_root_dir == NULL ) { + char * SMESH_ROOT_DIR = getenv("SMESH_ROOT_DIR"); + if ( SMESH_ROOT_DIR == NULL ) { + smesh_root_dir = new std::string(""); + } + else { + smesh_root_dir = new std::string(SMESH_ROOT_DIR); + } + } + return *smesh_root_dir; +} + +/*! Relative path of the directory containing data and exe for tests */ +static std::string PADDEREXE_RPATH("/share/salome/resources/smesh/padderexe"); +/*! Absolute path of the directory containing data and exe for tests */ +static std::string PADDEREXE_APATH(SMESH_ROOT_DIR()+PADDEREXE_RPATH); +/*! Absolute path of the exe shell script for tests */ +static std::string PADDEREXE_SCRIPT_FILENAME(PADDEREXE_APATH+"/padder.sh"); + + +static int testssh_using_system() { + const char * cmd = "/usr/bin/ssh claui2p1 -l boulant 'cd /tmp && ./runCommand_padder_Mon_Feb_28_14_28_36_2011.sh'"; + int result = system(cmd); + return result; +} + +#endif // __TESTHELPER_HXX__ diff --git a/src/Tools/padder/resources/Makefile.am b/src/Tools/padder/resources/Makefile.am new file mode 100644 index 000000000..207faadad --- /dev/null +++ b/src/Tools/padder/resources/Makefile.am @@ -0,0 +1,25 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +SUBDIRS = appligen padderexe + +include $(top_srcdir)/adm_local/unix/make_common_starter.am + +mysalomeresdir=$(salomeresdir) +dist_mysalomeres_DATA = SPADDERCatalog.xml diff --git a/src/Tools/padder/resources/SPADDERCatalog.xml b/src/Tools/padder/resources/SPADDERCatalog.xml new file mode 100644 index 000000000..4e4cafe42 --- /dev/null +++ b/src/Tools/padder/resources/SPADDERCatalog.xml @@ -0,0 +1,16 @@ + + + + + + + + + + MeshJobManager + + + SPADDERPluginTester + + + diff --git a/src/Tools/padder/resources/appligen/CatalogResources.xml b/src/Tools/padder/resources/appligen/CatalogResources.xml new file mode 100644 index 000000000..4900b5fd3 --- /dev/null +++ b/src/Tools/padder/resources/appligen/CatalogResources.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/src/Tools/padder/resources/appligen/Makefile.am b/src/Tools/padder/resources/appligen/Makefile.am new file mode 100644 index 000000000..c22ab6534 --- /dev/null +++ b/src/Tools/padder/resources/appligen/Makefile.am @@ -0,0 +1,40 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 + +appligendir = $(salomeresdir)/appligen + +dist_appligen_DATA= \ + appli-splashscreen.jpg \ + SalomeApp.xml \ + CatalogResources.xml + +nodist_appligen_DATA= \ + envappli.sh \ + config_appli.xml + +nodist_appligen_SCRIPTS= \ + appligen.sh + +envappli.sh: + $(srcdir)/genenv.sh envappli.sh + +EXTRA_DIST += README.txt genenv.sh +CLEANFILES = envappli.sh diff --git a/src/Tools/padder/resources/appligen/README.txt b/src/Tools/padder/resources/appligen/README.txt new file mode 100644 index 000000000..6fb376cba --- /dev/null +++ b/src/Tools/padder/resources/appligen/README.txt @@ -0,0 +1,24 @@ +This package defines a build procedure that creates a +set of files ready to use to generate a SALOME application +embedding this module. + +The files are created in the directory: + + /share/salome/resources//appligen + +Where is the installation directory of the module. + +To generate a SALOME application, just change directory to go +where you want to install the SALOME application and type the +following command in a standard shell (the SALOME environment +is not required, all paths are "hard" coded in the script): + + $ /share/salome/resources//appligen/appligen.sh + +This script generates an application in a directory ./appli. +Then type the following command to run a SALOME application +embedding your module: + + $ ./appli/runAppli -k + + diff --git a/src/Tools/padder/resources/appligen/SalomeApp.xml b/src/Tools/padder/resources/appligen/SalomeApp.xml new file mode 100644 index 000000000..87ca36264 --- /dev/null +++ b/src/Tools/padder/resources/appligen/SalomeApp.xml @@ -0,0 +1,40 @@ + +
                    + + + + + + + + + + + + + + + +
                    +
                    + +
                    +
                    + + + + + + + + + + + + + + + + +
                    +
                    diff --git a/src/Tools/padder/resources/appligen/appli-splashscreen.jpg b/src/Tools/padder/resources/appligen/appli-splashscreen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..45377b17d3c3b94c546662d7eeaab577fe4813a4 GIT binary patch literal 111329 zcmb4pMNk|}wDsWb3~s?axVyW%26uvMAV{!52Z9Y2Tn8T3>U(-|yZXxcZ{@!o0IrIHvH}1O4i50?-vIo#4v+;PAs`|mA|U;nkdTm&Q7}
                    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 a&Zu9mqt$?NNUf5`Jis-%7l^~1G4MYr?XKVe literal 0 HcmV?d00001 diff --git a/src/Tools/padder/resources/padderexe/med3/padder.exe b/src/Tools/padder/resources/padderexe/med3/padder.exe new file mode 100755 index 0000000000000000000000000000000000000000..3eca741f14d1d7df8f34438a6d5c66dcceca87b0 GIT binary patch literal 861247 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 f%M5wAB0!(nm>> This part should be written by the component +binpath=$here/padder.exe +envpath=$here/envPadder.sh +# <<< + +. $envpath +$binpath $here/data.txt diff --git a/src/Tools/padder/resources/padderexe/particules.png b/src/Tools/padder/resources/padderexe/particules.png new file mode 100644 index 0000000000000000000000000000000000000000..a1b97eca4f4a92b927363e010377f33a7d38682b GIT binary patch literal 335310 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�-3OD5ZQN3XrDniR{g#)FYG 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�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�Le&dk_aAJq?CxPIGFq_!XDwI0oiD1ZnGjPguq z91ro*SjHwsaqNqm2u2c$TeLEh*4Rd!HFw~Et|*H;BV)AqwX;DTBO9hZQsY1EWO9do zR?AOb##ci>*V_L2>$fGo?qD1qhULVJAWWCSY-pqtspiPl$YLuWH(OQ-3NtBZDq)|6 zJUp29>2V21sp6R6;D4l`?({(xYoKu3YGoDpA4mYL<-2>f=}+5NzS9nK6m)+Wp;IEi z8S}H8=WN)*Wf5D~%GzEKzkZPos&nr#J4Hp#1)wP_nK80sT;5qo35)Xj4J6ZfQo5w; ze#CWlL#DL#T}6ofqe+2x8>2Sz$&bfJ3t-{#gK;!b8AH;xBH6@ox}T&tD-KZt&8gf- z4#JiYfu=gBgJ$;Z0C!?s{%{so&3WgR+4bS!teh&qH>2SLoXoh~NYbtrB|1`=&Xab4 zoAcj4p+8~o(zs$(`;aNXk*9-IZxKs=`{4}Rs6Dt;fe<~?*4{vafN-l1fO&ycy|xy- zRgChlc2m^rVpN0y$wyjxzGh~}B)kDp=f{n|c~;8|3XK*#TK#RZLpfvAd<6+)ZqC|% z@S3Zt>aA+f`ns=OW`mcgz=4E#38lZzuN&yYrz|pX`I}^SowP+zkgKvl6QUj0%o~cG zLfJ=^Bx-*3@G7Zizx=nK-uZ+jEK;viNQ71DyU^SY~VIxn>lhnEW1lq)wa6#Fr|_x`lH zKCQD(sWUOXxgU(7rqV6nDCrTw)3#tZ>rJf)utt_0Jp&&{H+-1)=n{i$q|%L2I$^9wE2pkKebh81{X zo*pddf{`!^g6_hDjQ)~Mq|@RAI)t!;&@PXIT1G^sRB%}*3{n~^H!KZ@F`7KtYiaDV z7cv}*il&MLgD=B*>MMcCaR|+ZTNmPtkN5GppGFH_$`U2k*}c;;5ox1fYqEojdJUI` zV+PUJPBu*z=KRE8nIJ;R`dUURtkHPsdx&^Sgu^PBINdvgG}eV_$s9O;b8#0|%H_UD zrWv;^U0R*e3E(;YDu=8WnDqBvL}g)-X(1pV8(>RzfZS=PFDQO{+tK0Gs8?2-G+mN; z7BNZZ5wAKI9JPG119RM(IUYTIMGYqYrnxi6hrb8APbait=dMutMC7tSXDFu{|H@7B z9|CkPcAn{bw7!N@y0uZ@C7)6%^O|a`Lxcyc%kMrP{t&1x1vYNUBE}HW;Enw?`}g5F zYK@B4~HGlZkHS6%o?Y1JfnsGIxe;~~3x zFxV{3?RLoK&j*~jmSQur+17RoIU0%;9NCX1w&)%kaol#F*VgW)KJY{A*T`C2q;r6p zH6M$bh8u13M3y1HOIHwk%O z41(2B2w->WHs?Stv9#{3MMnR6q7o^YjvA!&}92)&5+27uHnR zYKf{SbzmWlepg%NIq_Mq$oKgYJb@!3D#1vp7?5vCdAV>RUhW+T>V$xNjfVvnPu&y8;lYEyjV))qk$%XpT+fgf#W z2c&}&{5Af0*L#g@Z_$1qE|W~>k9?XbYRkaozhCnhd9D9(+X-yEtWg1kqJ?OTj@3xo zM<6;H80>oW_tz=4M+Tp=9?6?|^sT;PTAd4C`4^px=8j@pE$sfUOj1$d9A zCyu3DIKgkKbYMt1Dpyx`G?-r~tWmUCK49Aj?G~d8P5~Y`tVjP}``J3ZhNKbcX+k|qP~U%y_ou2&G4g!hLrTGZfKBPmj&4VF%- zA!w?v;-di@TI$?4n^y+5_~&MhiKv*m(E5_OL!i)>8;e~TbE1Q$O>@Y$yFz!@h@VaMyen6V}rQ-90TxRpB2LCA@|Bn@JP~n_sND#tpP7 zYHz>#-mWw-ke&AGcz#!}Q71{wmdHn0cog}onqfMJ?dmssts|tAsj8p;@u!;|?}r;3 z)AkHI!^h>Ts^{v+mCEPnJf-+84Gb7xYnQ*F92rEE9b=-@_K;VK6K5x6=r0lOGL^ET z1{U{^hYuUrx7UnOz-d2Z*XxN}h_gQ>p`HFhQc1a}9{cLR%#*~(4{2@?z8XXi0pY2G=Z=OpOsSuNznFkD#_bvQhoi%9^NWje^19Vppch+e zQc&I;7ng_!=5xLj#IKnQg%8HNC1;$=%LsnDUhPlYYgKy!X|tXdIFM=t#>IhkDM(_J zVa+%4xkv|%$oh3w+s!RhXLqeD&uO@k`5`)RA58b3$*ALxjWiS$1=_S^Sjvx|ba{>Q zMqYZZaQ$1)j5MBn=ExSV=;?WT8iB3?TDA^ z+r=^quNK!SDJj-fJA}=N;)u>Qc(?H8xLnPbnrB6Z2yd{ z)~a{dA{SuBwA{|Acr7Xa923E6y5b(>l3Lym0o)MV^6LO-Wt`P2m}Blw!NL?RJ}q|; zg6za8N*`?kAjo*Eg}trFYe1e(C{t@}#G6P&#n&1&;6{5djZluk080ZR9_Vt2x3U5|gAUr!9A(MPtA=MlD; zcrl=;=mjpvQ;!J+Sdr(!nXt zHM0Plk3#yTajp23F%v2X5%cp8B%FMa8PFIkQyZrM0UPfQ5(<#7(qENwQC=o29n#P$ ztJ)BSZ1|x#V+i|_NOoIbRucUF4g19HGzV>EcHJ!~U0(%IDIxgPwZ?}}#?XWc9UL`J(Zg`a=KGK+SxsCtWP zZU26Fw`BNo4=Kx@+`yosK4OXamEEixZbES@aqIV@n#H4I&HXHSuXq%A^LvoS86P)Z1s2B^ha(ICwj=ob+#~e($Z_J;a<7p=qby<&eQi&5RY zlw6dXD?x(b<>Zpl-!x^GRAwou@ohck)+6_xrI?)&Rb8|Ml$)DqWKjrB)f;fR5O4=O zi0?p@S%GR`;6;9eLJ8RV#_NKw;A-B6HIM+XU~&Wh$YC0X99iR_Qo@Ls6mgi%_K_xi zvkX4MZ;S<5z<_PTDU15$?m!l@Hv#sN-ef5$UPo6+w9jSYH{%69Yif?SI=O0$1qfZr zwuz3`1vPY%3VsH2SZyswT5LTF%_v~|>JC3JohLw! zpUc-V2HHfJJ)0eo(lmp3)W;Y43u-IE`Imc)%po{{&?hJyzhrm}h_CT7R9LjyYkOVc zmep=5N?W-e`%GXAduaAvjh*;!?{M#3iRoCfa_%u3GL*AUz+|=3ezA;{tGPb(?emd5 z%13p~g!kReDaLdDBEG^2CM@PPaaWi)FGuKYb@GHdZ5Cgzds!V28!@I^G zv$Z=cA7z+SPs6@uDW+z`izQ$#;g|w^G@M-XmX7i78W&a%jcf@^4rC#Iz+a1Abw$tqc-kxpkdTr ziv^4$_s&2&7BGN3(8BqxrdWo42TZ$i6P-P8jwV+>Omy%AS&D6V6dLHI*8vBf(6-b3 zlKRBr^*s@pG`u4Picvt^ku7%tqlR~(2$J6Ax#vf7xrJTxt}cq9p)#9wvnw1j-Q0jO|+_wm_#){*x<1&ho(<5=34?3u*{g?w5lgJ!>p)lGyau#m4ZhFUHYEb@D3B_|`?J`?lkSU|h>NWY`$Q(fIB5a{Wz z9Qi&EqY@4k2RH0?d)-_U-dOg&FHr>D_uoE&C&$RY5_2b5tf?6Q90AG<&_zysB3q4B$< z_*CW55r0e!Az9oo6!yp$-w0Xiwp}A%+(XQCe61H+-=pSkrY8c+8oAovdh{An&(9kk zp)$L%ou4mgf)Nw4&%ttb9=-lUU7O9@Q&Pz=mbhxzWa<)uzR(9st@`PA?MXXvGcvz| zbz0*CZc+*0!DWsjbcdQmGN9M_aW>^^?Yo2S+jSG?W@c6*|9~))5^EI@2s&P@gsYqx z!IJoD$_f=z!3NqkO=n4D*xUpe;2{#kd#u(V_`D6{4X*;d=z9qV@zjU-gdORFFck2vk2r?K?E z*(hk^t=FklE!)}Qd^};)sy!+RsjQLIBn1CGuMNL98H>FPIu@inyjYHv()So_Og`iS z^5z@co{mLrqYn(`@>j#;Kn3c77xAb2mW`t*2WMhJRWG`c0|-_h?|JpMK$ZgnwL^!ele(tn%PD`1rNSP$k>NiTjidUL zLsNtHc+#AcmkLj5XF*q`k8Ze_Gj%8v`Ku=H?zyoCK@UI zNgjUfX+oi1<9>;Vn8PrM@3T0d1kj+fP6HB378XoNPd__J^B;h*X+@94NO>8Xq4axq z=iBeOnPoDO(gnCAx1M$U%Hq|Hvur^H%OEO0F%CPo;{r}FM`4yhSq`Wfd+OWYPpLT{ zwjYaK`9WRPlOhN*Jp3Oq2;d_0-if^~%d0p}HZ%BB^fr@AFxK3eA~7xOAYX>73TCpp zs%n$~f(TSeS&MvcVyD6{dQ33E**}d4011&WV7xPTyqU+iT&CfK20SrKMpSw&vC64* zr2f0VVVas5S!$2QLf}3FVGo4EbS$MvS9AKI4mJPeHKsZc=%7IJHAjYY0uq0`@=4M- zCkMkPJ_K%{Z@s(iv_+8CdL+JzR%V!tZ1bX`f2B}j{8=&)TB;~5?Ko#igX)uyQNC?a zgm{}bAvq6DT&S1ei`1&QH=kw8KlEV8$-*~Li$*FD zStj*~FAtL>Sgmlb&2b)F=ks3#{GM!E<7^>+z5M>u94Z^NIa;Eqcpx1e9S&1v$;$1x zp7Ts>+wH@}dPipRq5F)-w1@DIy4!hd%}ORtj&14h9$sns&s_F5o0s0c!+?Z@-2G}z zn)-#KHsO?EF=cMAM177j5|uRKP912RD4@)KkQzTXl9toO zdFmwaB9m{!Pjg$p)nq8Huy1okYo`De?PYNek2F#YI=oSH02SByDC&WZsWBGI9UbGe zi^?zHbOlxfruK+!sF=*DJi#AJaJ99YQUkV<;9Y@x4i+M{h2cv|#+5Fwi|h~nTDoY% zIG6x@v9RE6M@7ky9B5jDWn2j>5lYTkiDfk5h=^X`CBWIb82 zs5%Bl0U(`8N!9j5yeMWg1?X+hc(E{D^1RQ4NYPDP2?`VI7$J{a*sl@iIGCkWS%`U z-4Y+%iT-Fl_ylrp(S%;P`b?#2(BE3n1-P!02UT#9NpMx?Jugm1sx(oRB)y&Bf)x3N1sV_=TSDlVD zD5E9X0{VzwjOl>G5L1>#rX>?SYl6_QI<)qa9C2wi#?6iQ1qC06qn&Ek;>5(8bm+p0RcMt8PHDBpJZot!2B^fcK=8l$ zS&;?XpIYdWjbv2aX)L6UPhbpJnw=duh>REl+LLGkuuR#d^o_))E+q(U|LGVDktfxL z$+?=tf$AwwLsFIYgak@_tY%VE&B5aKt&Jwxm!nQbZxjQHxYJ$^gopl0NI29&xqjYQ z=N7{%)t$*HBYZGVMn%Am7cf}1u84FOA;xmZBbxtBu)PSZW=oylzP<{gtHA!MI8#2)f=B$=QV*dgh)v&*MMD~$Qz*$1w4fiX5 zfx54)Pw(;>rEbng`|s{&h(&y(WIzIRGPh#JHWVYR!3mdYAQf~W&TTw02DJ1^fb(DH zBS>9VsDOB70_97|du6~<@S%3;#-G?Q%c`^{TLk)pFeW!}W_ZAKLv!i0oe9x9xuipll%eP2+7o^<-T>`l#8 zCZ||N>VH*aa|L?P+s20F-;>%9p2}3^tDEge%2pJ{ic~;VU)G(>&zV6Sp8y+hE)hMa zAHPjIKO8Oy%;p#-6$KaS?Oz2V4~MFUz(0X9TFGFEOs)vLE_eg7u&^#HFl(XG92B&5 zQIz17YPq{i8!Sf{tEViH=H!xhGLH_fFTLUaY6GBX zWr!mq>s-LA8W`C1_bhOBx?Wo$T3Tjbk-oZaF~WehSjVD#_;SX-`@I0KAPPf;HVQT| z#@3a+nmrm)`sqs&S^w7*;Hzq3fxutH`g4}O!fqgyzQSZPD?&yn;vPc^gFCfSKcYHM z2K%EUM-vPNz$k+qo9ec_caQy}1>V3Fz{n^Q-3jr62N!JD{)9Hi%gO5>ri;r*>@uh7 z4P*R0rCAj#wj4OdJYR~(7~oJpjfunQv4$Vz5CoFYnQs_Ji^6i@&o?%2z*q4&L0M76 zWdk0@MmFIlF{mG@Ao~lM8mFv{*m3!1h91$m!-1Sue|{uZI!@mM+j65KTf~3#{pzQ6 zJ9l2uSfE$}4gP(4mgBl;ii`IN7RKD)-z}svbnkEsu|Q58*=1B)i8(D9`e)TmCoWVI z>VL~STJ!m|@9uCR=N%aoXZ}<}^=Fy}z?HiGF~;Gu1!gkEBnK(dxAROD?(CrnQijp^ zumDjnkc_^h+oOG zuMZJe2T*_OnK_$$lIjrq5lpjCNmmeQonW~9Sh&9+_Wm7Sc+=k@8sHLx`vPYhFMd~i zbOKvzSr1I5){I=1$0dAuck{zvscLTVig9Mud((NXcVlW7I0j=e{^LD&4tII2^EG?~ z#X+lm-vckFJ~kiupL!)o)TL)jZYGSJ4Wh2Lpl)&FBJIK9SdzMT8f)RJ=MN+T@H}2l zeCEf}ijB>rs-o$Br>(!gGi-?$d9m39+v}uT_}E1-WyMnj24xm-W`18!UNu%ZHYCd7HrSrDERRG_i9=BA?Udp zPQN!?y<_h5mG~;C1-%yF^Cgo!DQ>@KZ90KSS5?s-V~mrL+X;ErM}4fhgSOm4q!;KB zPIT>PZX$Sh^kmxd@?WhY$97_BQz}&27A{j6(W1_Ow}2K2{@5c2IKUc;#OA)ZO#h6F z13;GVn9`t-6bh(l!Z_sVwX-ug07sL+PX>SR(@?!Tn6-kurdR_L`C{qKPLR;Bu8o`A`hgCcwadF3e@NoJKPa zQ&0pLb#gvDjFM^=hGF{RH#Ky%{A7-J^vrU)(NqO$NCc2>84H zQ+xMgb!|;oH}c3qcBcAgKsPiPZ=0U(+LGr9Ivm0HE|@!dzX&x%8^dGoz>;Y+$%jf_ z{%62sv$}Oltov^N-wEph;^q;SI_Cp(EQxTQ)SpeB= z&@!k^kMfKc5POhoKeqK_xN+Nkh99_0=jmzuI21(#`cOwEbAp%<%X*K6fjeN_`j27H zIZ8vnDGl{L+!v{*29pki(SUyeyg7-ZO9$1JXC(9Tu+fyMJ=7#Q8*tT35c=W<3D?K< zEEIpNIdz2iaeytr#h)|qeIn%=E|FdRjBnI#x7_Vq_7Rhj@k+XZ`zAh}u#g#hc+*W& zH?oAT97>A-)xhO424DJ59FHYYBna#wWou3wd9LHB1c08#u2&4!goB;*KbrWhb<_kvLW}70$oVOf+6Ygl#tO$=cuzC z2;3SX#8tu*60UaXkp3MR0cU%&wVm@w;BDs>L;T3an>gD-`}-4blZvw_N%Jv+^o-pM zy66l!l2ayX-{Jb&c`}AK19(gOZ7ZL=Um-e#CgbfNZqKIyn+XT;c6(J(Q0OA2p;6P@ zp?@O^ng%sqyz$wToSagm#1X_1bH6z2{Ksx1QFIUaO>ViS&2Dd|t)u!twgrw^!>+93QMa zJ&l0gP@umFzbH7->zK?keto`5!}9v*m6lG@2seMY=fk>C?r%bspQX?p6wMS>hoq`3 zGX;7e(}0FAb?Cm;aIM{Cmi-o1YR*~=?iU*-Os`|nj0@}q%XtHhmtfZIF;7f~)L`Ku zufyf3E0r8c;#t*^=_AF%m*poBZ`Y88Iqyu8PZrj1K||pX;9U$x;;r39X0-Q4Mh=eo z)Di9{`1!;4_QVFm_=7P~Ty~@=lc5e$2VZ<|rlyv-f z@Vs`AqxX4qewTQE+34qoSADvd*OpDmc%FjR)dj9!tDmG<1qOXpA=XF#?1-SHo#ef} zxz3<~p6gZ8i=nf3HZz58=2A6vP%uBnC%vQ?dAJ4_>4nNcH@qavHQvdhB<B3Y@x4Vs9Gl2qwq^Nl!CMw^D}rGusdPi93paKg|dSKxR7lNEL=zR z=s_GE?VmeIZd(YzQkWQ~gml`ED;3u>C*$DqKKS8HpiT6p6mwO%94?*xs*)urK>cQ0 zGSlzxg>SOuU4&P?=^6x@pkfQ)_S|y(qdp1?)UD- zeSaH$Z^8=sBAT%mv3cyoS=*KZw-!rViDtyYUcA2eMrp_fdc(h_WcL46S!v&TghnGR zxxReq^L(G_8ZcC0!AkX>!;zSqWtZ52PHjZ{FJ;_+FU!6kO_os`-}0FmlFwyKzGvOx zJ$V2MtczO{idcMtNyQosmiq<{uWtFG4^tXrN0}y?ygXd=R#F$;*KT4|t-&o72o6?WbDz(!+~{-7d|R%kCGQc?{+KUFs@d+Hp83ka0-nz%Y? zH)$q>D(9&0r220LdW51BqmgX~L^u5oMLg;{e)kiOgiB62=EvP3O^GJsevT@W@`Dn? zs1*l?6Xou%=sg}n{t8b!ta&}+D}KSsaD7KtxuxZ>xSH0MFu1C5{t7-XeGY`VOA#rN zQS!0^PsQeQJ2Gf`IBB(~@V}{2I_Z-r@TC$B@AN1kLO}(PmwO3O9pIX9)XqM&ym7+9 zvfG-_X0zXD?7f0E_fU0Gp`gNFv1eQb7KOi5JI<{n%)vrI;r8ZA_J#uD`f0%aplzY= zw6MOGi{I2)D4rUFz#HQ_^igd|&DZ~O-J>%%_ofWRGtG%fxy;vizxP+Yf#jA02WcyM zi0ejK_%sE1=#HJaGF!yDA$R2*FS7eb3SCpf*gN0^i&R)`5f^c&@LLset0WFN zcjsOzNC{6Pvs$*-S2@eeb2kQ`(H>x=@;1}?3|kcY=gcoV>0bB=2|K zzYn0|v-uhK%FZs#7zYt?y<0R*ntrSQelIGH^tcebKsgF@cXsXuaXoK(ygq`|9iQ!| z01?KkZ|y34IXp0J(FCxL7E3h7$!DLJsN62{%#@`A)yH!Za&=(-@vrJR`;t-cTCsf6 zrX?09Bvxf672B~AypB3H7=+{F?XnSw=qV_I*2^_m znm0X}tVR+w1s-uMQGNi}?>=13FGMa=i__TEyh*bd!N5yjY}Oa82kFP#E-(;chQyJk zjF~?Nh=*ZBe74Sj8}k&!q|KAJW(2Q6kd~ypCZkSF@7e@I1h*{?6O%>!;kK_2*+zRx zcIc@vV&n%6s-$4+*~{@(W{x+6>{P5UPNjskRK@Rh=Mr8>fYtJ4xZ%^0wWNRd5 zCiF>2xQ2hyP8Wpj&=$ZlIYB)y@F|1W{RR*XLgDdJ?`$O<@8^w>wwb1t8q1l7f_l^B94(5b9rc}C~z zC1x(M-D&fdro9jOoi@DlTOc*63iE?{df@)J(b0X_iXT3ehpVaemj7<1NRi<~o~wc~ zI@!w-(>g;hV%^aBWuYGfxdOS)^P5SUXwcd?(zJ=J{hV#rKHrOFqp-fl20NsTy=F2@m2>8!Q3|+~O&`Vag z=a1zy0&I?%ys_dILTv1=F!eR!0%Z#sn%5`~w1rW2+O`|vwEf3iS-NvY?jjhSRoZ%h z=19H)0Hh?I2ACq7;S@CF!02YslC0{G9cvv5sxvY#Sr`NQk}yd%-ylxo$JI>b@x^2F z-r&<_#46{oQ<35ome;aLe#85~ql>_SKroPuhYkzK4MGbYihA9n5)xW4ya0JGyRBwS`a4}^&1 z&uW#zBZijZbTiq+kZ^1R!aR`QnguhJHRjT-{40iCm%du zft$=JbAxzR5$fH|GjbS~zq-YIrRuaiNxzawj@h6p zhBZSo0Z|BQV{F$cWo8yo5(p>xprm&VbwD5>Mob(MSrPLeFW(HNi6adw@3Lz!GBzH$ z=bsSGn%J$ay-FKwzVQ0s%(v077eL4TTbR_N6U+BCNb(*oT+R;xIR;61KVv|SDGiyy zw>&)^xO4ym4>UOUQ5O^#`p!fvrcm64BAPv~nsqPlQjI1Z|1)WU39!u|*Lr`rWSgD{ zg%%ez;a5PRei0$P=6E1NYW7SL{MT}V=3g`*RmR0~i^ksmSmF`pm*l0W z_xIh@58cua(4AR0J|>@>%9GLa> z@2G%J1UpO|7i1b6-q*1I+o9boqFJ6F%`t8rbOzVknGOxgo(;DJHp4QkiZ=?lJhnKYT z1sPDjO6VYt={r^#^NC&zBl=nZbHzFDI?Itb6j2Z(p#-7x^T@ua%tIyc~cKc~F9)$CB z(vjy=TR6D)9)s$dv>uxSukallFGGHRImd6B^!INH=yZ`0;s;Q2fcXy4-?-t4bke0f z65uy?Qe83cqk-x)I~`pW?DV4NFBn5jc}@xQ*H^mo>rsV1y|d-{(-H99sUPtu#zl#MhAqS z35&kQ12)?hQ09lb34ZSvS)ijCyQKU*oFy+_G?dc93XE!(S*EGr0=3TnGz@2_kd90M zmv+JCI3jbU=l^gxiPKr#lxyT%;{=(hTHmfOO{(e{A(Z<)AYGhBxVPbCqb zOA()F@k8wD%G?U+kl(>UDMNZf^r}d!B2p>PD#^c^85zKO;`xCxZVN_< zk5Tej9~Tvke`ysB7R@@180rUug)8BiPP4p9LD%Dl%!|kJ22FI0d*AEzfBAsil?)cp zU@PAzfjz*NVagOXr}ga}qHg`X`hb8yIOpJZZ_DR^WJD7!e9VXsnF7Z(RTAu+OLD=R zinh!ooZ~Bc?bsW8@q?FECA(kpMGlUKUIqIu(KC-QzsV*?Nu&&}!B^T3vjSB0cAc9Ub8x5(?-;N)1^0J&eZ(E8Q{MS2 zzylAX*g3ajvSh;^yJy)ah1j=ihpy!L#y!d>GV(*lG_4aUbVUdeK+N>zk zGFI+_k#NZe5mCPrO1RC;1f&L-0-ykQi2^eDG?f*vUoH{GZo#5PKG+HKl3Q<>iQRrX zl#k=CVVNq|78lPJz+~5@rH~iKqO~{3@(_`C1$h0Xk2%osTerW$B%EI|*t@>Oj{*X_ zowK|~QrF`N+CDm4?U?t6UwgT^+``dYZx@$kSb;611b&b>3(@^Lqi1z@CgLXM?j6y)Dl%ypp{Hv@9ii|&x zCqNj@twoGShe1M#2asVXA{snQ*nIvY7`?hRDL_RWiEC3@$r6Qb1_u}ZbvE8_F-XtY zYMC)F&#C@qffZuTftC&@Fo4HBuAk0>a&Z3r-|K<-y(t&Lk1?_l6+TR1iqvkXs9v2x zY61JNUDT#@!XTG}G`?~j75|Au4*&NUGXce={v*57FFraV=b(I29zk4|tpNS#bi7#Y zu;Px`jk#1li2X`$bt}`QKRTM{FopQ) z)k*QjSdJRPIjwD{_PQ{9c$aOInK9)jqqM20Hq{n&wPySI`9X$D5!({G{X2a8eIE@L zu(9n;ophg?wq*ev>eV-l z&Ead5Cp75_wG99i|D?gz574TwqSC~0G8A+Ru^L|cCH81kS1r2mY zU7k8R08YszoP#AL#lX!UaG5Wcc4{_)-?ITt;3{8x!M1_UyI$smm2OdF zB^E)3g|Dr+)yoRBGbu4$UmLcoHdd%)0Y^XC@GKqxHsf|%!GZ9i5<}5vN{8pFB8xwU zxw=!R6k&t1p&=Y>GNSc%0dn#YV~5_S&zL{UOuB1-1o^MVyHs<-7+PoIi9nw%&1KE5*Gijt4J8i-2PE0^@&!@{8& zSgr?Z{^j>TGkEl$yvdqu3Oz56YQZVAub!T<1h|>S&M)(HWURnAjB6QSjJXpj0|-EW z{#34oRauW>Ww^nF zYitU?b!PIRnm9xCNJzkkn^hU&Pi6=D)wpsq$lHmRwxG{z1xK|Y?CR_=|58!5l6TUz zOJ|JsG&iiVmiOAD@6?ii*8bg7us6$pciYWTZ7m*Rv%Kos`A<9VvXR5!Vf2MUysS5= zaMSQ3kOV@zyb#oX#+oRgE#DD&hAalSbQ#)c3WjKHTkO#$v1t=-Hj}^P@PRATCnt${6qUyv|e8MD_F3mKDa(F;H8{ zG1vRI6YO)m5=Ky?8QuD+Vg7;PnTN-&g28%eFct&xD~^Y0syZ$kW6TGrDURy6fK7|& zps1%3zD)e5z1g*P)H^9ObI6+1vpZV^bJ6hcHAE03e4!nER-!6IF!&XXjs5yxgFzU0 z($(I~s9>J=LCJX==(S!hr1n3$6pks;XL)-A*{L46)9t1Ilb4QZq0mriWBZ#l|7#&tzRRL(jJ@REuz6AWG=qfE5e`-mE5}^l=~4NI02DF~|_j zMD4Et98D{OCQa60gGz3nzQap&0LA$QcR; z5Jt1bZSW1Dk|~w8Vu@04$IFA4;9nqlwG5G1sNDMQt)-K(pQ539P}q9f;UOVa989n9 zMqL3jiRJLc3pgA$;tby9%_MVf+^5nnA+Z<`aeu#JZR*A^`dT_mWa8K|bVy;wXcLzK zbODB40s_EHQ;5oXnMm?0RH<=ov#}R;ctV>eVkilLKMU1jU0{lm2&P`Ed;XawL6(uf z31@uuv;d6SZNwh?+r77~4hFn2Fa>R0i*pNx|GHf2OG*BVa#$H?Lg~Y+m8zmDw6U@M zFd)R1C0(FIH76Zd^HcONAx)ygCgL`Athn0RWE)gLnj23A8(ZTZNpqgfWJ7QXYlZ#WY_ z-pG$3MzIMspO>iY2p=3T7z5!eGDG@u;Beu3afJ+G!`lbdntlUOo>8Gp^%G4{q+zVg zEWeaZsyjLgs4yf0JA=h?Zdxgd8T2iHgM6XW=JINw8DO1tFd^d=s98_mlx zBz!DECt3*}^#ak2A3^W;rxiA{+~=!)Q)sxv0^8GCvpjp(bBzbpb+!rwEg)l8Oht(x zRU;?e#uhHX%(}%FZbp&j?RU}1!h#uG8M-}T`S^2VuEA_Y|BX6>^@Uvb51UWZGggtV zp9crZ_7V6HLS=JQ^9j03=?h*%O)0VHsiT@GsHi6Mbo>jg>XMp-GRE(N>hKaJ#jv0| zC4npYx^J7Sxl=E##-;bIhEM{YMFyX&X}P6@on)-=C9FJ&aK%0$DDf^APFTy*iHnp_ ziIc^Poee-~!GpMu!L!U?shjNj!q$72EK@74N$YQ{wM05o8qf zQ)>+S%Bd`14S4stj~_qlRt%J0$2e#&}Lo6T%13h&LI6Q`dgmnw5T@1%lm=SRZbaweM8I8fv*zE zn#2Q!Ow8;H_to}&VH%a1g2hH-XA)&T;|#Q zvib!6xPk1*XUdlz8%!l72J!XiNH-$tym(TgoN6xzXg!1#WfAA?dFkAgJ#vt6ne_S) zdQu6*RdR%Iv$#q+MNW-K(cZz5ryy)g4vyz1-TKYpCxV!cj-#_Cwk%27{?!Vb)+awiB)t6&!mZ+N@8p|uk z7*SuffqwnhgFCYOTxO7r1b{2I2&IG7cN8!mM!{kMwlZFvDoqhbu?< zqJu3d+=j6#__?XTnFaRzy|lCh6tqxP1w-9j51J4#>x*Bm(kb&$I6NJP*$^odf8!5~ zM^`BM5gB9T;s*ArHv7WF`9cUBcy?*L3OLOFzEB0RRc`+q&1M6C98-?``9sk}%Sbg^ zRgiR${qzKq$ltxY8+(`CFQ;5gUJ#)7T*SH{gC6mT`3f%QrE{O<_E&7c1y^K)fByMo zo?Pcm8ZNny=uZ~nxAATbPLz<|BVR3uFCjWmbH*XYouxwcPP&G=sFq=cSzm3uwtW(! zN9zo7WvQ@aAWx?K&t5Z6w|wg}&>Bb95Rg31%ox0#+e`x6liiyQ6N@~&_0aG@u$rL_ z0m_*rd}?}vg7jy!W@J42f$XdaWmtgf`c_6dEy=>+%a{&f^(MT_(BhqdqesH>YvrER zRDytWWDp^+K*o8&tAqmg1Tub(+*n&@I`ShigxRl!X4n!(G))^4hJg4xX^|C6!PdxT>tT!P7iUhuWhK!n^EBEvu#vBO86u9KK>SdxIyOX?Ms ziFTd0TM$vnJ*tO3gwg@lV(oF$&08>y)frGwYzgCic0tk6Tdx;kJN$eBJur(S}~!2;X!_x0+7B8v2(GZI5r zhkC>yow(YbFr2#`&%=8C>EPkGDOeIv<2-OJnp3BgygNw#iFo6D1cV#8{4kGcvV`l@ zOaXIklAymk?2>=qo)c;i-3N_dG86<8zzVWwMV&I+FwPJ|Th^SI$QBnO`}#iSFTNK2 zZBp(N zUweqrj@uu8_Z1V4jB!V+-b9U5-?xLAZ#~nNLA8dc9Isj}0m5 zrnI^y!=k6!Yd66S4?jSe=O51FLxnSV=Mz$v&b|2Ihv{Ttf+~tpNAvBb9>iq#O$x+* zz}A`(R6UR&JW)P16+O3F-@ALKB3MyV7)Osko;D&d75Y}@QM&+B!zcJHZct3bA6xJm z=v0QoF-+*Nst=Pp0K~f)xCGjGMiLiL;#G1gGRV;mws7gxS=(K`WlI7?wuC3$fP$Ql zNvF`VhNWeEFsLIuT3qB&Sdn25=U2Yi~d?JUTda6B+2T($Y_%};}ze;2{DYZmdpr`C}$Ez<0(4e{)> z{%uFUv*f>k>02$sT#ZUgo~v8SKZcx-mv=iZfxVT2(>XhZ!a(LXg1fqL7*07IY8R&% zC)~efrJ4SJUVzhiMs9_hcfRRj0(Y;B2l7Rt<%rHYp0*^F=J>E|OsJ#7!YZwdj)`;>%0rjB#!;yTz|{_(|MjRpkD2|A zFfxh*t}_tiBqly-45|>yetqg`J3ZXv>+AIN!kc@-;#D6DC+5%)c)Rmm;%DlN>0?_? zx{0Pklnxhyk;A%>^#@8kLnw-=4)(-s!atq#2mb)_G&GkIYujk^%SaqZJMAa|+A<9T zHX~FD0|h?89Cp5wd*+aSy|K<iM*_ zz3po_DCCw%-%LFlc2r9D4cx{ zhxU(*oZ@{;t2Pulnt)ID2XoWrc#!){;tIL6ztdmd`cQ-ZgY(hPCtW={rJ)hY{MZ?7 z;bdE4bm0gn6<0v0*;^Q-O*xf~UqTGbXHFPqMt-ru5Ib2_)eHUdmlS?;=xxC*-l`36 z_dI$1JvwL0(_@qOtKPPsJt$;TR=gn>VnoEiXqAzHhw%Noek&*=1GeXyH?(PuhYJ9X z-{}D?Du2m}F(~IJGD|v0S4^U+iX!*7e)tQNl~pQuPoeM1#|I-0-v$EJDAbKLN?e1y ze0(RV#hA+lD%({I%cIX31`+Y)&3{i(2?-A(QZYN{JHQPR7PhY3+d{}^Lz^{}D{vO5 zs#;u+y}SEh?(|xb!h(_U;6Td0=6w;MLWS(vfSPO{G8wx^c{WA+T09KtnpQmfd6^ev zvwm>Q8bCX=<`y~R2Va&q8vp&rcn

                      jW1kHTMueQbX;F4DE#z&IAhu>$aS(Y;GyH? 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 a%rOVQZm&xDcX0;-{ykQH0z)cVhW$U{x=)7y literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/Makefile.am b/src/Tools/padder/spadderpy/Makefile.am new file mode 100644 index 000000000..c28a874ce --- /dev/null +++ b/src/Tools/padder/spadderpy/Makefile.am @@ -0,0 +1,36 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 = + +if SMESH_ENABLE_GUI +SUBDIRS += gui plugin +endif + +DIST_SUBDIRS = gui plugin + +spadderpydir=$(smeshpypkgdir)/spadder +spadderpy_PYTHON = \ + __init__.py \ + configreader.py + +salomeplugins_DATA = \ + padder.cfg diff --git a/src/Tools/padder/spadderpy/__init__.py b/src/Tools/padder/spadderpy/__init__.py new file mode 100644 index 000000000..911cf5b96 --- /dev/null +++ b/src/Tools/padder/spadderpy/__init__.py @@ -0,0 +1,93 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author(s): Guillaume Boulant (23/03/2011) +# + +# TODO: put all this stuff in the unitests package + +import os +def getRootDir(): + ''' + This returns the root directory where the module SPADDER is + installed. All test files are looked up from this location. + ''' + return os.environ['SMESH_ROOT_DIR'] + +def getTestDataDir(): + ''' + This function gives the absolute path to the directory containing + the data files for test (realistic med files). + ''' + datadir=os.path.join(getRootDir(),"share/salome/resources/smesh/padderexe") + return datadir + +import MESHJOB # to get the enum constant values +from MESHJOB import MeshJobParameter, MeshJobParameterList + +DEFAULT_CONCRETE_FILENAME=os.path.join(getTestDataDir(),'concrete.med') +DEFAULT_STEELBAR_LISTFILENAME=[ + os.path.join(getTestDataDir(),'ferraill.med') + ] + +def getMeshJobParameterList(concrete_filename=DEFAULT_CONCRETE_FILENAME, + steelbar_listfilename=DEFAULT_STEELBAR_LISTFILENAME): + ''' + This helper function creates a complete set of parameters (a + MeshJobParameterList) for a simple test case, i.e. a case with a + concrete filename and a single steelbar filename. + ''' + # Note that a CORBA sequence (MeshJobParameterList) is mapped on a + # simple list in python + meshJobParameterList = [] + # We can add some parameters + param = MeshJobParameter( + file_name = concrete_filename, + file_type = MESHJOB.MED_CONCRETE, + group_name = "concrete") + meshJobParameterList.append(param) + + for steelbar_filename in steelbar_listfilename: + param = MeshJobParameter( + file_name = steelbar_filename, + file_type = MESHJOB.MED_STEELBAR, + group_name = "steelbar") + meshJobParameterList.append(param) + + return meshJobParameterList + + +def getSpadderCatalogFilename(): + filename=os.path.join(getRootDir(),"share/salome/resources/smesh/SPADDERCatalog.xml") + return filename + +def loadSpadderCatalog(): + import salome + salome.salome_init() + obj = salome.naming_service.Resolve('Kernel/ModulCatalog') + import SALOME_ModuleCatalog + catalog = obj._narrow(SALOME_ModuleCatalog.ModuleCatalog) + if not catalog: + raise RuntimeError, "Can't accesss module catalog" + + filename = getSpadderCatalogFilename() + catalog.ImportXmlCatalogFile(filename) + + from salome.kernel import services + print "The list of SALOME components is now:" + print services.getComponentList() diff --git a/src/Tools/padder/spadderpy/configreader.py b/src/Tools/padder/spadderpy/configreader.py new file mode 100644 index 000000000..ceacf543f --- /dev/null +++ b/src/Tools/padder/spadderpy/configreader.py @@ -0,0 +1,140 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author(s): Guillaume Boulant (23/03/2011) +# + +import sys, os +import ConfigParser +from MESHJOB import ConfigParameter +from salome.kernel.uiexception import AdminException, UiException + +from salome_pluginsmanager import PLUGIN_PATH_PATTERN +CONFIG_RELPATH = os.path.join(PLUGIN_PATH_PATTERN,'smesh') +CONFIG_FILENAME = "padder.cfg" +TYPE_LOCAL = 'local' +TYPE_REMOTE = 'remote' +TYPES=[TYPE_LOCAL, TYPE_REMOTE] + +class ConfigReader: + def __init__(self): + # The first step is to look for the config file. This file + # is supposed to be located in the same directory than the + # padder plugin. Then, we have to scan the directories + # specified in the SALOME plugins path. + self.__configFilename = None + try: + smeshpath=os.environ["SMESH_ROOT_DIR"] + except KeyError, ex: + raise AdminException("You should define the variable SMESH_ROOT_DIR") + + pluginspath = os.path.join(smeshpath,CONFIG_RELPATH) + filename = os.path.join(pluginspath,CONFIG_FILENAME) + if os.path.exists(filename): + self.__configFilename = filename + else: + msg = "The configuration file %s can't be found in the SMESH plugins path %s" + raise AdminException(msg%(CONFIG_FILENAME,pluginspath)) + + print "The configuration file is : %s"%self.__configFilename + self.__configparser = ConfigParser.RawConfigParser() + try: + self.__configparser.read(self.__configFilename) + except ConfigParser.ParsingError, ex: + raise AdminException(ex.message) + + def getLocalConfig(self): + return self.__getConfig(TYPE_LOCAL) + + def getRemoteConfig(self): + return self.__getConfig(TYPE_REMOTE) + + def getDefaultConfig(self): + defaultType = self.__getDefaultType() + return self.__getConfig(defaultType) + + def __getConfig(self, type=TYPE_LOCAL): + configName = self.__configparser.get('resources', type) + resname = self.__configparser.get(configName, 'resname') + binpath = self.__configparser.get(configName, 'binpath') + envpath = self.__configparser.get(configName, 'envpath') + config = ConfigParameter(resname, binpath, envpath) + config.resname = resname + return config + + def __getDefaultType(self): + '''This returns the default type read in the config file ([resources], default)''' + defaultType = self.__configparser.get('preferences', 'defaultres') + if defaultType not in TYPES: + return TYPE_LOCAL + return defaultType + + +def printConfig(config): + print "PADDER CONFIGURATION:" + print "\tconfig.resname = %s"%config.resname + print "\tconfig.binpath = %s"%config.binpath + print "\tconfig.envpath = %s"%config.envpath + + +# +# ========================================================================= +# Test runner +# ========================================================================= +# +def TEST_getDefaultConfig(): + try: + configReader = ConfigReader() + defaultConfig = configReader.getDefaultConfig() + print defaultConfig.resname + print defaultConfig.binpath + print defaultConfig.envpath + except Exception, ex: + sys.stderr.write('ERROR: %s\n' % str(ex)) + return False + + return True + +def TEST_getDefaultConfig_withError(): + global CONFIG_FILENAME + CONFIG_FILENAME = "toto.cfg" + try: + configReader = ConfigReader() + defaultConfig = configReader.getDefaultConfig() + except UiException, err: + print 'ERROR: %s' % str(err) + return True + + return False + + +from salome.kernel import unittester +moduleName = "configreader" + +def testsuite(): + unittester.run(moduleName, "TEST_getDefaultConfig") + unittester.run(moduleName, "TEST_getDefaultConfig_withError") + +if __name__ == "__main__": + #import os, sys + #pluginspath=os.environ["SALOME_PLUGINS_PATH"] + #for path in pluginspath.split(":"): + # sys.path.insert(0,path) + + testsuite() diff --git a/src/Tools/padder/spadderpy/gui/Makefile.am b/src/Tools/padder/spadderpy/gui/Makefile.am new file mode 100644 index 000000000..02d4ffd75 --- /dev/null +++ b/src/Tools/padder/spadderpy/gui/Makefile.am @@ -0,0 +1,60 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 + +# +# Files that compose the spadder graphical interface used by the +# plugin. They are installed in the spadder python package, under the +# sub-package plugins. +# +spadderpydir=$(smeshpypkgdir)/spadder/gui +dist_spadderpy_PYTHON = \ + __init__.py \ + plugindialog.py \ + inputdialog.py \ + inputdata.py + +PYUIC_FILES = \ + plugindialog_ui.py \ + inputframe_ui.py + +nodist_spadderpy_PYTHON = $(PYUIC_FILES) +CLEANFILES = $(PYUIC_FILES) + +dist_spadderpy_DATA= \ + parameters.png \ + input.png \ + select.png \ + compute.png \ + refresh.png \ + publish.png \ + clear.png \ + addinput.png \ + deleteinput.png \ + concrete.png \ + steelbar.png + +%_ui.py:%.ui + $(PYUIC) -x $< -o $@ + +%_rc.py:%.qrc + $(PYRCC) $< -o $@ + +EXTRA_DIST += $(PYUIC_FILES:%_ui.py=%.ui) diff --git a/src/Tools/padder/spadderpy/gui/__init__.py b/src/Tools/padder/spadderpy/gui/__init__.py new file mode 100644 index 000000000..4bde9bc53 --- /dev/null +++ b/src/Tools/padder/spadderpy/gui/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author(s): Guillaume Boulant (23/03/2011) +# diff --git a/src/Tools/padder/spadderpy/gui/addinput.png b/src/Tools/padder/spadderpy/gui/addinput.png new file mode 100644 index 0000000000000000000000000000000000000000..408b6b49e202963f9d73b000fb09fc3cd81ee4b6 GIT binary patch literal 1653 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 SDv`qg0000DZyIWIIXFf@5y zqtyTa1rJF?K~#90wUu3nUDXxGe|w*gd+xn+@69-wOw(j;HO2-dA|)u*5~)q08cOi% zp%hA}r70RQp+yL2+w`dqtwm`dP%1^zOa%i)Y6Q`cLZB}umiRSlkeSh&PHu8$=6;=X z&)NIzy?q#)W+YSlP#0|24{QB@YpuQZ-oiP@7isbfRR{dfbJ)B0j}w(j3_Rt=H@OCMFu2Mn|iZN+He(78iS5 zxNwO$UZC6Ubd<{fAcXwQp+kG({|Rv5z+cynk6&|S^XBz;-*U?aMn|ipDWqvZnre&z zkj&4=y!YPw%+I&z_xtaH`~Km>kDmRnfc^WA)SJy~|91QBoA2DVZ9Tmnv|5Ixr9OGC z&{|<_fwhK8rA$#+W@cuXo&AtruRCLm`{uLH?*HhjfTx~%t*o^>cE=qz?!4``jm*x% zxpQrVu#Ap27z_qzt((Z5tXW*lu~su2YOcS2j46!+w_}1JKuU>o77*lF#&DQXE>{SH z0Bg(-?tkE*zXH%~PJU~Ae9hNtwFa$LALk0RRw$*|vSpHo9@@b@_uR zAOsj=u-1Y#NFk_Jt5|2gQz``$aK$~SRI0nj#u{`w8A&q4_XDi8grTHdjsUppuB~j@ zGRaFXo#yoEf3j}fI8hYhoI?mf;CVz*gtb&~ZX0m^as^UyYZ!(ki9#t&Q7E*|84Qw7 z#}Wnu7$0x2d-o3Rz4um>N*JaI0!Qded{5#D2{^#5Sy6!yZqhkHmMMxtBZLJcS(f3P zL@CgEc?kjAw||uzZn%zPFPtJvb4o#glpazFzyi*$02oua{vZ`n`dCw-u{bA)BA;5V z#&EbS?s?!GoO$CSZ=5-Y&J~p?pd1Di)=?A%&N&>SzXDJc#dOji+*B=Bz!^+oDU88d z14xWn&b-}DdHd}TnVI<)MrrER67_O~=SfVSGZ+j2ky&frTmeuje|>2&zG>|>qXZI+ zwdkVwG^2ndh3R+Z_~3&%(qu^JLAhR`UMUlWKH6B4G^5w=B7}I=THE?;i>8*A7I!E8 zE{%~IPFjQ&G}lj%4;80Qo+Z8*<9V>AS|SR3M#>STz{faCq7<$9^Qb}t?!{v-{7SDp zWXY*CO-{^TxUi$XsY1#3h$;~yrI7x`7*A`yG*-hy68Ijaz{8Ug<1E9hVD96Oi5C`d z&P`kEUc9V(7z%VJaaXjRRI9^J@`beULDyk5PJk-o5O8%j>1?o?VLSnjQjKR&;R1}RRul|@c*z+ Y0K$2@H!pQI5+Ti_L0YJ|=5)wx zkF=1qGlSNkDr^3|$ws8mau%VAs!N}G7ur`j3{a)&lwiJCaRceu$939gbwXgD!G7W+ z`tnW&{YVf#zprcNBv=w4wLNbW#_u=%`)~XMSM_D9ZJ#Vp00000NkvXXu0mjfl(La4 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/gui/deleteinput.png b/src/Tools/padder/spadderpy/gui/deleteinput.png new file mode 100644 index 0000000000000000000000000000000000000000..86c3e36f9927e89419f8ecc6f6b6bed68e560cc9 GIT binary patch literal 1619 zcmV-Z2CVssP)DZyIWIUbFf=oW z9Blvq1*AztK~#90wUudzoo5xtf6x0q?|ttwceb0E%pRsmlV<5kp=w0igcQ+;Uo031 z5&ESC!G@6HhQ`JPD`@BkEu^u~SZumLL4zVCFcytWG$t`gQ;XSV%S`Uvxifcr*XKEY zC|!n`e)N1i2hNZGS(S+J|1|wcf$`^+TD*np=I9L?lniW34R;t}I7vy96vr5jb| ze|-EoSz0ZRT1~;(qpFz1?2bYnG1h(9&FWFSj8@7cNem`6q(+EBL$wl-8W~Gd4x}mP zzj<_d!W9a}f33DU@Eh&>4|R@iqR_jUeD_xT;vm}3q3{sfLZnG!?jqwSUuEj%!o$ER zU=Q%;PgF2=L^&?}snC&sbo7z^bdBx<)&h%&tzg1w(s+S1n8U=2Xzg-g>tk<%=2LaZ4K?14*#vHH^86gM+J!h^KI^0?}BN0R=ka8KejT9RLZ3D z3pA#NnL6_xXD{5~)Pd9N_{DZcc5NW82`Ed@3<-k-VE*HM{Rr?R+dG3&*VbP>L#{WX ze*Ag#{2WnBSh#T;-7`Q}f06sc9^RfP^Vc7o;me!XQt0a-sY4V*SZfhW{o?=!;J8w4 z-V9Hz+xJy+YL@9=9^v-d2WZ^&IVPn8z4seLZJkuEPa=+DQz6Ub^%^(cnndSZP#=uB zgS8Y_00efn5B1%@W?eUnCtfDpwu9kkkCK1ld-NPU%+PlZG5quA&;vu%>P_1G3?0DD zzuzLR8yxMCEBH8$gBAMre?>XV02X%ZzO~pJ|3Mld{`7xop8F?-ja%s3wF}2YB$E?_ zjTU5m@IAU5&D`Y*annKPisafmDfQ$K*wXGZvxhWG8o&$wt$Q??1EJJ;YBJ;Vr(>$9e(gM8itczhWkfwB4dI-$;z z9~r^9_Ac`;9i#m6ajqQL&*Is0sCpe8*6An~m~O=c2qX7p@x3zAn#063aygfQeh*`K z^qI%hgCCA*2(MRCbKu>J*Y3P~%Ls*1hUQx*i4%jHw5Y!E3axAg=h{tX>H+V})bWch zGiH*v->g!|`E+(@vZ{fn4FVo6KLmDv_z#X~y*KM;e!F%3AnVr;k}2j;T7##FYE4=T zHEzyUxi~S$QrIB-S&JG08-w;-GQLK;LcN|4h8CsZ&B&|T~Tc)72t?4=)gK027yh$5-GLBxMylyhk}A@9*@X6JkE_{|`0H#89c( RW`qC$002ovPDHLkV1jp&|APPk literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/gui/input.png b/src/Tools/padder/spadderpy/gui/input.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a67db7b4ea4725d5e2daa873410c1eb07567d3 GIT binary patch literal 1493 zcmV;`1uFW9P)DZyIWIUbFf=oW z9Blvq1tv*EK~#90wbfmSoL3pZ@&9?xd1v1F+S!>kJ83fAO?H#gVwY-*7(|1p7hYHd zDOeDtUbIk)6slJiZ566sSP&7V6s0-g4VUeApL ze&!tCdFbad_^-gLPb-DxKHz(CdAeC?-a%#mhbdGSNy;-sxdP%H5>ouQ#oC*H!)2^RY)L*1k;l>Pg-`{WP78zhcKbhz~@Op-?JP zoBaTBp+J^7LI5ui0{08*)c3!ne)X+ii{AtM{s$^Dicd@(Y*O0yIR?9@dF9!=X)X6D zRH_u~8m%-obM$w&+1={Wy!9wavB;n=LGLe! z^)Zg!vqY&ng}4ki9^l6V>}W{Z>!WncQem25yNw%lcEpLqXC7|iYPKv8~a)K(CrMj2Mh*d3Bqd= zt0)8(1EBD@^b%W_E>hih6Ni?jnQkzky55fTCtJg95=`C`KKY=haui|lKZNK=oqh>D0N0xp1wz>T)B zX`2f#pJMyHA%&8rRH=|l@|ai?=krJuAwd}QdaPf(KruN&q!j^_66|C^(8_}yxAC?^ zV{Si(mTFAZ>zG^)r4%9olS_yTB}`!&F-6i&hcIkWuB;NrMB|LcL;%5!M=ZbhX-`%&PT8vqgh=>$6O zT_#sd*xB4+`TR2d-604l4aP(y9im7PX@&Rl?=~@j-djb0^ZZ#Uek;Nkob?D?Lh&MSHVErLDhdUZ z1!jQTyq7z@lO>>z0;Le(3E&SfVJ83hezgyHRKc 1: + self.checkDataMessage = "You define multiple CONCRETE meshes." + self.checkDataMessage += "You should verify first that your version of PADDER support this configuration." + # just warn the user, but don't block + QMessageBox.information(self, "Info", self.checkDataMessage) + return True + if self.__nbSteelbarMesh < 1: + self.checkDataMessage = "You must define at least one STEELBAR mesh" + return False + return True + + +# ============================================================================== +# Basic use case +# ============================================================================== +# +def TEST_InputDialog(): + import sys + from PyQt4.QtCore import QObject, SIGNAL, SLOT + from PyQt4.QtGui import QApplication + app = QApplication(sys.argv) + QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()")) + + dlg=InputDialog() + dlg.displayAndWait() + if dlg.wasOk(): + print "OK has been pressed" + +def TEST_InputDialog_setData(): + import sys + from PyQt4.QtCore import QObject, SIGNAL, SLOT + from PyQt4.QtGui import QApplication + app = QApplication(sys.argv) + QObject.connect(app, SIGNAL("lastWindowClosed()"), app, SLOT("quit()")) + + dlg=InputDialog() + + from inputdata import InputData + inputData = InputData() + inputData.meshName = "myMesh" + inputData.meshObject = None + inputData.meshType = InputData.MESHTYPES.CONCRETE + inputData.groupName = "myGroup" + listInputData = [] + listInputData.append(inputData) + + dlg.setData2(listInputData) + + dlg.displayAndWait() + if dlg.wasOk(): + print "OK has been pressed" + outputListInputData = dlg.getData2() + print outputListInputData + + +if __name__ == "__main__": + #TEST_InputDialog() + TEST_InputDialog_setData() + diff --git a/src/Tools/padder/spadderpy/gui/inputframe.ui b/src/Tools/padder/spadderpy/gui/inputframe.ui new file mode 100644 index 000000000..9f98eb991 --- /dev/null +++ b/src/Tools/padder/spadderpy/gui/inputframe.ui @@ -0,0 +1,204 @@ + + InputFrame + + + + 0 + 0 + 509 + 307 + + + + Input parameters + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + Input MESH / Output group name / Type : + + + + + + + 0 + + + 6 + + + + + 0 + + + 6 + + + + + 0 + + + 6 + + + + + 0 + + + 6 + + + + + + 0 + 0 + 0 + 0 + + + + + 31 + 16777215 + + + + + + + select.png + + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + + + Béton + + + concrete.png + + + + + Acier + + + steelbar.png + + + + + + + + + + + + + + + 0 + + + 6 + + + + + + + + addinput.png + + + + + + + + + + deleteinput.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + diff --git a/src/Tools/padder/spadderpy/gui/parameters.png b/src/Tools/padder/spadderpy/gui/parameters.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a67db7b4ea4725d5e2daa873410c1eb07567d3 GIT binary patch literal 1493 zcmV;`1uFW9P)DZyIWIUbFf=oW z9Blvq1tv*EK~#90wbfmSoL3pZ@&9?xd1v1F+S!>kJ83fAO?H#gVwY-*7(|1p7hYHd zDOeDtUbIk)6slJiZ566sSP&7V6s0-g4VUeApL ze&!tCdFbad_^-gLPb-DxKHz(CdAeC?-a%#mhbdGSNy;-sxdP%H5>ouQ#oC*H!)2^RY)L*1k;l>Pg-`{WP78zhcKbhz~@Op-?JP zoBaTBp+J^7LI5ui0{08*)c3!ne)X+ii{AtM{s$^Dicd@(Y*O0yIR?9@dF9!=X)X6D zRH_u~8m%-obM$w&+1={Wy!9wavB;n=LGLe! z^)Zg!vqY&ng}4ki9^l6V>}W{Z>!WncQem25yNw%lcEpLqXC7|iYPKv8~a)K(CrMj2Mh*d3Bqd= zt0)8(1EBD@^b%W_E>hih6Ni?jnQkzky55fTCtJg95=`C`KKY=haui|lKZNK=oqh>D0N0xp1wz>T)B zX`2f#pJMyHA%&8rRH=|l@|ai?=krJuAwd}QdaPf(KruN&q!j^_66|C^(8_}yxAC?^ zV{Si(mTFAZ>zG^)r4%9olS_yTB}`!&F-6i&hcIkWuB;NrMB|LcL;%5!M=ZbhX-`%&PT8vqgh=>$6O zT_#sd*xB4+`TR2d-604l4aP(y9im7PX@&Rl?=~@j-djb0^ZZ#Uek;Nkob?D?Lh&MSHVErLDhdUZ z1!jQTyq7z@lO>>z0;Le(3E&SfVJ83hezgyHRKc + + PluginDialog + + + true + + + + 0 + 0 + 649 + 367 + + + + Create a mesh with PADDER + + + + 6 + + + 9 + + + + + 6 + + + 0 + + + + + QFrame::Raised + + + 1 + + + 0 + + + + 6 + + + 9 + + + + + + + + 6 + + + 0 + + + + + + + + 6 + + + 0 + + + + + Input + + + + parameters.pngparameters.png + + + + + + + Compute + + + + compute.pngcompute.png + + + + + + + Refresh + + + + refresh.pngrefresh.png + + + + + + + Publish + + + + publish.pngpublish.png + + + + + + + Qt::Vertical + + + + 75 + 101 + + + + + + + + Clear + + + + clear.pngclear.png + + + + + + + + + + + true + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + + + + + + + diff --git a/src/Tools/padder/spadderpy/gui/publish.png b/src/Tools/padder/spadderpy/gui/publish.png new file mode 100644 index 0000000000000000000000000000000000000000..bfe8802a20f1308143e3a5c75520cb845fd97967 GIT binary patch literal 1622 zcmV-c2C4apP)DZyIWIIXFf@5y zqtyTa1*b_wK~#90wU%3Kl*bjvfAd{-*WS zQx&OEsUmo28?`~A>U%3yRpQblZLU%)^5CLE8$b#urj$6CV3!)U1mj(s^=0kY?8Up@ z{jSr8Qme%6Bjjupu{5BqM@6|Nu>gr_Y&Nz)>jojrkEYBB6oge1U{ToRS52MQp z#=_s(v#XI^k3T@9ww8_U?Y%Wd@b|zQFTC)p`Za*-x&w-$fBlIkwsG*_<80l!ffFar zGBHtM;nLsu?3DvdpBluJ27#71W!E5ea*)((2bdrEE3f_TG+&N1a_BqXX3zFs6jgoB zaov4uCOeLOJP@erYi>3~sRYsJ2Bb`I?%W7_pZqFf{}ICR9)gXH7(W}t6;;@JjLwJJ zm?%d{PkhWHJGxPA$^8vY90$j7oY}Pu#>Z2ytgPfeym)bfTenXgMJe2~cbWX+i?2Tx zjYiKt{q(LtsRWtKGUv~qqqQ|eS64g9{X3fs01y!*~ezgf!wpir_br$HgDd<^0G%VS->#r2!|V}sfi$j zpRv&y(y1l3Js2l!1PS(bCd@?s?Stt1tq73U-<4%mSY0ji^H2`j75f21kz`lBQ{ml}e_w zvQnh3u9{-8gp`s@W`WVsafXLS85>J5d2^avE{iGz#Z{AVD9BPN&uY00a81*7cOBqP zyOu1=c1+VI5;0H|$+fHhWN7Fd<#LW_RKYML`TT8$hex?SnZiM0gsLzd6U(;oJed`! zYnoq}MM9HOszL~SejgJzCP>ewc=p+y?B2ZvP$?A3SXPM(7ZRL0J%VGoeB*16pzAt9 z2tXR(igkef-#;Q0g^fPHro|c>m`^XTn3-eWzJ9*4rH7%Rt8{jD(9sdW?+>$gZ!3Z7 zASXY#z|?$(uJ$IX0s+j5)$Tan+ALZtQLdO*HBH?Ui$+9Kb1gHIv$V9e(9^Sp!NDO0 z2HxZNiL(@o6{Li07PddyOMQKeo5>m4z7(N376D|R?YK?r06~9Md%&;tZ`yP}UKyMf zlW;f$0;Z>@$meq`eV!o__GoDV!+>B=!7yqmWLHrX=<9t5UDw+@&wXwkK-1Lis;ahD z2dXLEHt|VVEt@1K7f@8_>g>R=Jq8B;#F1A|F!a$S(&++1cvuxlamk{(Du|}52*LNB zfBqMZcX=S7Y?(G!M*l&jtBVp2>6tXgjvd3cEgE7`q?9C6896DX(6mXa&R@oJJi70z zeNK%mE;YwD}_5!;^)8&eBnjgzyH;k>v=zxQa%Ix(Wh&_M2Z{& z&$ey()>}VZ+kEig>tZ>VRXySQ6jvM;LcFA^s-+0=Qvq+h``%B?b%1*j_xxA!FLe#A U4wl+e_y7O^07*qoM6N<$f*m&u`v3p{ literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/gui/refresh.png b/src/Tools/padder/spadderpy/gui/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..2683e981492167c691c3df19496a9cf3efc71ee5 GIT binary patch literal 1546 zcmV+l2KD)gP)DZyIWI9VFEBK= z`id|B00mk}L_t(|oVAvFY}E%9$3N%yx{u!5%e_D!v{3iZLfFbF3fWX|tgt(~z zOt!kP5gAPky6lgcSpCbOE}2d@7GBGMKQ!C2WE#*6-5@%Ft}9(AFjk=S7TVJLzV~07ERo1S=Jh&+=9U=-}es^a8P$> z`TqgPH3jmJBnA0xsw?%t%AQrskIbhUR2ZB*#>Mf8L)D76{j`dsNMlfCs4YnOe4fxCY~ z_HvGA6FsVfD0 zfJVWCkniXY9g#<5g`2A%&QFB) zVD8^g3^2SnRwa$;8m4R(Q=ie`(*On$mKu)9OfX5zF)=V8M0K~0$ivc3z!RCSM6$cL zjhL0d44SxF%ad+ih!Z#|YS`C?$t@BW5&5VB8?WdVX_o|>94;CsLBtw#iFj!YF z_zb;qfzfCdD`HT?rQ^CUVR%M{5C?E&v#jAZ*7HTOvOx~%etrFYfQcain4!=ViIQ2E zz)4ursQN2_qX-+z=iQd_8MhOgHY{4PXgP4=k1aK7NgOh=O;*^==8N+>n-Za~tZ;xr_-EXl4bT>DWgsFTDnK`vPt?Y+wDMGJ7m#w~lG zyo|w4-LA8Qh*uBDRm!MmkT4SvV1Hy?1L*Tw{h+9U7YmGLyP295$8s!c4VP?ohWeye zBF``M8NDq1ax*c+bPQ%IB_wo+5TR5qjeY{~u?W=-kRaf`&wrP+bWk;B@crGyOgnn+ z^vq7#EaM2>3zsw#NtzU^9@pE;crW|f6mj1Dp8)-(*5$`CYlbFwIQR3&zAf}F?dC^c zeVjin*lQneIo+GRQ0NuYB(pe1u90U|$2v|N8NzeDSLo*ITnZq-JvQ<--#^~R(UV8s zJ2L#n^|e}!4Xd}(JO3??O&nvkmP37u+gjIh=FAu;hfhL+{rWQvWv-hnTjaQf+2Q-a z(^Pnt2zNzHXID?pa&zMy>sj31i4ifVy8(xW4|Cw<{ZvcUJ*3&Bzfpf~z{7G64aS(| ztiGb2JSqlNR^stkJQ`b@>P*m@YUg6^5~YbUUf9@6l&$)EEy0Iw@}~kG5YfG#Y?3}n w5V97ZJ5iP)4CC-ugjaQ!`XBUt)^E#y0H0%Yg?L^~(EtDd07*qoM6N<$g3V^?mjD0& literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/gui/select.png b/src/Tools/padder/spadderpy/gui/select.png new file mode 100644 index 0000000000000000000000000000000000000000..8eea6a94d7a633da5531e72250f9daf1b405327b GIT binary patch literal 1305 zcmV+!1?KvRP)DZyIWI9VFEBK= z`id|B00e1CL_t(|oV}G>Y*a-U$A2?t&e=V?XM2OCT*MZ)NJ&9O5JE%~&_Ez+BEpuc zfrK|AJYb^n0cFuAAB-Uelm`+cAt4JqXhM82Mg)~0Sb+*r3T+Y72BGxEcK7T#XT}#Q zRET)P|J!7~`Tz1=W=0rexSNu9)&2hfs6X3mZhrIrFw~EMm@R~iEK41CocO_AyEb0< zs{nDk9;{ydc5SifzhaDjDVg-*o|i&O$+c_46pQ|#<2d_WH}>w19qZcuHlU^Dtt#Iy zf0jyB%$YZLKGPaAq*6(g5)_7q__?cxW5?ULdUc@Bww+Bocdpw$a<;K?qg=FT)_b;?SX1jvV;`t@YMv z)26)I(a}8**y?%ita3Tpwtf4W_3(!tG&koKf_Y)-(x(|WCQa+%qr|mEWimx%Sp)-R z2D=CG#wB=W*#ag_O0$38=NrzQ>zb}L3zsZ;#E!*04jlNZX2et8FyCU8N~N{+bs2T< zeNuc;U!1s%bJ0}5S8&H{X>JC?rO)6 ze65r^ArSq;esR&ar@yEB$|WA1J0D}!P+jZNc4~lFTw&W1AGtX%^2MSDH@E{*a%$9!I27qz;I)Xv)Np=(el&k%F4{+bLP`Ht`1v6 zfF?4MpjbvLiR(H3D89Fa?ka9iT zSR6|!ur1=HBswx!N;KR85JF%H3ndgWVNso|z>PH{G>``D(EuqAmi*GpUoQ7hS2YfmRD{A{+YZ3PKm%}!J1C#e zT?Q_XtZ%x>;v*0n+Ankvlmjy3C(}PXNbg`DQizg34ByuA4jT2EX0y2#1Mlc4nqVtM z6loA3gjk=?=eGT8z;(!EW6aCOn6*L(UkI@|pU=JjPt0!%_^)->KmFeTgG$g-V$UU$ P00000NkvXXu0mjf=HplM literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/gui/steelbar.png b/src/Tools/padder/spadderpy/gui/steelbar.png new file mode 100644 index 0000000000000000000000000000000000000000..ce27fe3a0345e03e919b54ca3b6a8498743b2ee9 GIT binary patch literal 538 zcmV+#0_FXQP)Tm(Vb6n04%0-0d~Z?N5N>2x}bMx$D(zp$faLbcIm6))=iDcNsK{XO_NUIz9_#=Hl90fplE_S$iRDekpz|Us6%gcl z10>7>u(;bXN$%}9L;!i)H44lO83AA=upo5q1c5U!FDx|env1*bl?cq~cLH;UH^p~Rgca1Ok;(*H5ZUXcXOPFe6C@8ghq04j{~AV5iYe^SBjqm*l4!TaGj6|yQS zl0>W3O7lFgJbe6&SDlNa#1ExQ;_~7(-;>XH?~^Rc>h}P$EUWbUeQtj&SzNDKTyL1C zYZj|t%+odV)rMrXCP_Cezg;mNk8#e`cJr5LHk)4>jfPdL)hmSs`qPQy_Y4;U;qFB literal 0 HcmV?d00001 diff --git a/src/Tools/padder/spadderpy/padder.cfg.in b/src/Tools/padder/spadderpy/padder.cfg.in new file mode 100644 index 000000000..4962a336a --- /dev/null +++ b/src/Tools/padder/spadderpy/padder.cfg.in @@ -0,0 +1,35 @@ +# This section specify the configurations to be used respectively for +# the local execution and the remote execution. The value for 'local' +# and 'remote' keys must be the name of a configuration section in +# this file. The default key must specify a value between "local" or +# "remote" to indicate the user preference. +[resources] +local = localhost +remote = nepal + +[preferences] +defaultres = local + +# The following sections defines the available configurations. +# The name of the section can be choosen arbitrary. But the value of +# the resname key MUST be the name of a SALOME resource defined in the +# catalog of resources (CatalogResources.xml). + +# For each section: +# - resname : the name of the SALOME resource to be used in this configuration +# - binpath : the path to the padder executable program on this resource +# - envpath : the path to the environment file on this resource +[localhost] +resname = localhost +binpath = @PADDERHOME@/padder.exe +envpath = @PADDERHOME@/padder.env + +[venus] +resname = gboulant@venus +binpath = /usr/local/bin/padder.exe +envpath = /usr/local/share/envPadder.sh + +[nepal] +resname = nepal@nepal +binpath = /usr/local/bin/padder.exe +envpath = /usr/local/share/envPadder.sh diff --git a/src/Tools/padder/spadderpy/plugin/Makefile.am b/src/Tools/padder/spadderpy/plugin/Makefile.am new file mode 100644 index 000000000..e05fd636c --- /dev/null +++ b/src/Tools/padder/spadderpy/plugin/Makefile.am @@ -0,0 +1,33 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 + +# +# Files that strictly concern the SALOME plugins registration and +# configuration. They are installed in a dedicated folder named +# plugins and created in the root installation directory. +# +salomeplugins_PYTHON = \ + spadderPlugin.py + +#salomeplugins_DATA = \ +# envPlugins.sh + + diff --git a/src/Tools/padder/spadderpy/plugin/envPlugins.sh.in b/src/Tools/padder/spadderpy/plugin/envPlugins.sh.in new file mode 100644 index 000000000..aa7c39a7f --- /dev/null +++ b/src/Tools/padder/spadderpy/plugin/envPlugins.sh.in @@ -0,0 +1,22 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# + +SPADDER_PLUGINS_PATH=@prefix@/plugins +export SALOME_PLUGINS_PATH=$SALOME_PLUGINS_PATH:$SPADDER_PLUGINS_PATH + diff --git a/src/Tools/padder/spadderpy/plugin/spadderPlugin.py b/src/Tools/padder/spadderpy/plugin/spadderPlugin.py new file mode 100644 index 000000000..67f78264b --- /dev/null +++ b/src/Tools/padder/spadderpy/plugin/spadderPlugin.py @@ -0,0 +1,36 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author : Guillaume Boulant (EDF) +# + +def runSpadderPlugin(context): + from salome.smesh.spadder.gui import plugindialog + from salome.kernel.uiexception import UiException + try: + dialog=plugindialog.getDialog() + except UiException, err: + from PyQt4.QtGui import QMessageBox + QMessageBox.critical(None,"An error occurs during PADDER configuration", + err.getUIMessage()) + return + + dialog.update() + dialog.show() + diff --git a/src/Tools/padder/unittests/Makefile.am b/src/Tools/padder/unittests/Makefile.am new file mode 100644 index 000000000..42fad276c --- /dev/null +++ b/src/Tools/padder/unittests/Makefile.am @@ -0,0 +1,31 @@ +# Copyright (C) 2011-2012 EDF R&D +# +# 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 + +spadderpydir=$(smeshpypkgdir)/spadder/unittests + +spadderpy_PYTHON = \ + __init__.py \ + usecase_meshJobManager.py \ + usecase_spadderPluginTester.py + +spadderbindir=$(bindir)/spadder +spadderbin_SCRIPTS = \ + autotest.sh diff --git a/src/Tools/padder/unittests/__init__.py b/src/Tools/padder/unittests/__init__.py new file mode 100644 index 000000000..faa0a86b0 --- /dev/null +++ b/src/Tools/padder/unittests/__init__.py @@ -0,0 +1,18 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# diff --git a/src/Tools/padder/unittests/autotest.sh.in b/src/Tools/padder/unittests/autotest.sh.in new file mode 100644 index 000000000..7ff1ece28 --- /dev/null +++ b/src/Tools/padder/unittests/autotest.sh.in @@ -0,0 +1,61 @@ +#!/bin/bash +# Copyright (C) 2010-2012 EDF R&D +# +# 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 +# +# Author : Guillaume Boulant (EDF) +# + + +# This script should be executed in a SALOME shell session, +# for example the shell obtained from the command runSession +# provided by the SALOME application. + +# This list contains the filenames where test suites are to be +# executed. The path are defined relative to the PYTHON installation +# directory of the SALOME module. +listfiles="\ + configreader.py \ + unittests/usecase_meshJobManager.py \ + gui/inputdata.py" + +INSTALL_DIR=@prefix@ +PYTHON_DIR=$INSTALL_DIR/lib/python@PYTHON_VERSION@/site-packages/salome +PYTHONPATH=$PYTHON_DIR:$PYTHONPATH +export PYTHONPATH + +stderr=2 +while getopts 'ql' OPTION +do + case $OPTION in + q) stderr=1 ;; + l) for f in $listfiles; do echo $f; done; exit 0;; + ?) printf "Usage: %s: [-q] [-l]\n" $(basename $0) >&2; exit 2;; + esac +done +shift $(($OPTIND - 1)) + +here=$(pwd) +package_path="salome/smesh/spadder" +cd $PYTHON_DIR +for file in $listfiles; do + # Uncomment this line (and comment the next one) to display + # the start line of a test and not only the result: + python $package_path/$file 2>&$stderr | grep '^\[TEST' + #python $package_path/$file $filter | grep '^\[TEST' | grep -v 'test in progress' +done +cd $here diff --git a/src/Tools/padder/unittests/usecase_meshJobManager.py b/src/Tools/padder/unittests/usecase_meshJobManager.py new file mode 100644 index 000000000..09a8c09ea --- /dev/null +++ b/src/Tools/padder/unittests/usecase_meshJobManager.py @@ -0,0 +1,102 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author(s): Guillaume Boulant (23/03/2011) +# + +# This script illustrates the standard use case of the component +# MeshJobManager from within a SALOME script. + + +# +# Preparing the configuration parameters +# +import os +from salome.smesh.spadder.configreader import ConfigReader, printConfig + +configReader = ConfigReader() +defaultConfig = configReader.getDefaultConfig() +printConfig(defaultConfig) + +from salome.smesh import spadder +file_concrete=os.path.join(spadder.getTestDataDir(),"concrete.med") +file_steelbar=os.path.join(spadder.getTestDataDir(),"ferraill.med") + +import salome +import MESHJOB + +# +# Setup the configuration in the component. When first have to load +# the catalog of SPADDER components, then load the component +# MeshJobManager, and finally configure this component. +# +spadder.loadSpadderCatalog() + +salome.salome_init() +component = salome.lcc.FindOrLoadComponent("FactoryServer","MeshJobManager") +config = MESHJOB.ConfigParameter(resname=defaultConfig.resname, + binpath=defaultConfig.binpath, + envpath=defaultConfig.envpath) +component.configure("localhost",config) + +# +# Prepare the job parameters and initialize the job +# +meshJobParameterList = [] +param = MESHJOB.MeshJobParameter(file_name=file_concrete, + file_type=MESHJOB.MED_CONCRETE, + group_name="concrete") +meshJobParameterList.append(param) + +param = MESHJOB.MeshJobParameter(file_name=file_steelbar, + file_type=MESHJOB.MED_STEELBAR, + group_name="steelbar") +meshJobParameterList.append(param) +jobid = component.initialize(meshJobParameterList, "localhost") + +# +# Start the execution of the job identified by its job id. +# +ok=component.start(jobid) + +# +# This part illustrates how you can follow the execution of the job. +# +run_states = ["CREATED", "IN_PROCESS", "QUEUED", "RUNNING", "PAUSED"]; +end_states = ["FINISHED", "ERROR"] +all_states = run_states+end_states; + +ended = False +nbiter = 0 +import time +while not ended: + state = component.getState(jobid) + print "MeshJobManager ["+str(nbiter)+"] : state = "+str(state) + if state not in run_states: + ended=True + time.sleep(0.5) + nbiter+=1 + +if state not in end_states: + print "ERR: jobid = "+str(jobid)+" ended abnormally with state="+str(state) +else: + print "OK: jobid = "+str(jobid)+" ended with state="+str(state) + meshJobResults = component.finalize(jobid) + print meshJobResults + print "You will find the results files in the directory:\n%s"%meshJobResults.results_dirname diff --git a/src/Tools/padder/unittests/usecase_spadderPluginTester.py b/src/Tools/padder/unittests/usecase_spadderPluginTester.py new file mode 100644 index 000000000..164cf8a33 --- /dev/null +++ b/src/Tools/padder/unittests/usecase_spadderPluginTester.py @@ -0,0 +1,51 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011-2012 EDF R&D +# +# 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 +# +# Author(s): Guillaume Boulant (23/03/2011) +# + +# This script illustrates the standard use case of the component +# SPADDERPluginTester from within a SALOME script. This component is +# dedicated to test purpose only + +import salome +import SPADDERPluginTest + +# We first have to update the SALOME components list by loading the +# SPADDER catalog (load on demand only) +from salome.smesh import spadder +spadder.loadSpadderCatalog() + +# Basic test +print "Basic tests" +c=salome.lcc.FindOrLoadComponent("FactoryServer","SPADDERPluginTester") +z=c.demo(2.,3.) + +# Test of usage of KERNEL services from the test component +print "Test of usage of KERNEL services from the test component" +c.testkernel() + +# Test of usage of SMESH engine from the test component +# WARN: the SMESH engine must be loaded first +print "Test of usage of SMESH engine from the test component" +import SMESH +salome.lcc.FindOrLoadComponent("FactoryServer","SMESH") +c.testsmesh(salome.myStudyId) + +print "Test completed : OK" diff --git a/src/Tools/smesh_plugins.py b/src/Tools/smesh_plugins.py new file mode 100644 index 000000000..612e0faa8 --- /dev/null +++ b/src/Tools/smesh_plugins.py @@ -0,0 +1,33 @@ +# -*- coding: iso-8859-1 -*- +# Copyright (C) 2011-2012 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 +# +# Author : Guillaume Boulant (EDF) +# +import salome_pluginsmanager + +from spadderPlugin import runSpadderPlugin +from meshcut_plugin import MeshCut + +salome_pluginsmanager.AddFunction('PADDER mesher', + 'Create a mesh with PADDER', + runSpadderPlugin) + +salome_pluginsmanager.AddFunction('MeshCut', + 'Cut a tetrahedron mesh by a plane', + MeshCut) -- 2.30.2

                      ?R!-Y{!hHDf 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

                    G?XNs_pdn(adVw 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 Ic|R`vKlfNvSpWb4 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/resources/appligen/appligen.sh.in b/src/Tools/padder/resources/appligen/appligen.sh.in new file mode 100755 index 000000000..a27a3eb53 --- /dev/null +++ b/src/Tools/padder/resources/appligen/appligen.sh.in @@ -0,0 +1,50 @@ +#!/bin/sh +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +# +# This script installs or updates a SALOME application for testing the +# PADDER plugin for SMESH. The application is installed in the +# directory ./appli. The configuration file is supposed to be adapted +# to your own environment (see config_appli.xml). +# +# To run this script, you should have first configure your +# shell with the SALOME environment, i.e. source the files +# prerequis.sh and envSalome.sh (or equivalent) that fit +# your configuration. You can alternatively customize the env.sh file +# to fit your SALOME environment and let the script do the job (see +# source below). +# +# (gboulant - 3/2/2011) +# + +here=$(dirname $0) + +# +# Run the appli_gen.py +# +APPLIDIR="./appli" +@KERNEL_ROOT_DIR@/bin/salome/appli_gen.py --prefix=$APPLIDIR --config=$here/config_appli.xml + +# +# Copy customized configuration files in the application +# +cp $here/SalomeApp.xml $APPLIDIR/. +cp $here/CatalogResources.xml $APPLIDIR/. +cp @prefix@/plugins/envPlugins.sh $APPLIDIR/env.d/. diff --git a/src/Tools/padder/resources/appligen/config_appli.xml.in b/src/Tools/padder/resources/appligen/config_appli.xml.in new file mode 100644 index 000000000..2f77f06a3 --- /dev/null +++ b/src/Tools/padder/resources/appligen/config_appli.xml.in @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/Tools/padder/resources/appligen/genenv.sh b/src/Tools/padder/resources/appligen/genenv.sh new file mode 100755 index 000000000..c4d5dc525 --- /dev/null +++ b/src/Tools/padder/resources/appligen/genenv.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +# This script creates a source file that defines a SALOME shell +# environment. We assume here that the SALOME environment has been +# previously set, so that the env command get all environment +# variables required for building and executing SALOME. We talk +# about third party software programs and libraries. The environment +# variables defining SALOME module are exluded (i.e. *_ROOT_DIR) +# because they are automatically set when generating a SALOME application.. +# +# The argument is the filepath to be created. +# + +if [ $# == 1 ]; then + ENVAPPLI_SH=$1 +else + ENVAPPLI_SH=envappli.sh +fi + +function header { + echo "#" + echo "# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + echo "# THIS FILE IS GENERATED from the shell environment used to build the SALOME module." + echo "# IT SHOULD NOT BE EDITED, it is generated for the need of the SALOME application " + echo "# that embeds the module (for test purposes). " + echo "# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" + echo "#" +} +header > $ENVAPPLI_SH +env | grep -v -e PWD -e SalomeAppConfig -e _ROOT_DIR | while read f; do + key=$(echo $f | cut -d"=" -f1) + value=$(echo $f | cut -d"=" -f2-) + + # if the key is a path (LD_LIBRARY_PATH, PATH and PYTHONPATH) then + # we must extends the variable. + if [ $key == "LD_LIBRARY_PATH" -o $key == "PATH" -o $key == "PYTHONPATH" ]; then + echo export $key=\"$value:\$$key\" + else + echo export $key=\"$value\" + fi +done >> $ENVAPPLI_SH diff --git a/src/Tools/padder/resources/padderexe/Makefile.am b/src/Tools/padder/resources/padderexe/Makefile.am new file mode 100644 index 000000000..1388ede61 --- /dev/null +++ b/src/Tools/padder/resources/padderexe/Makefile.am @@ -0,0 +1,42 @@ +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 + +mysalomeresdir=$(salomeresdir)/padderexe + +version=med3 +notinstall_version=med2 + +# We install the padder.exe program and the files required for testing +# the execution from within the installation directory +dist_mysalomeres_SCRIPTS = \ + $(version)/padder.exe \ + padder.sh + +nodist_mysalomeres_SCRIPTS = \ + envPadder.sh + +dist_mysalomeres_DATA = \ + $(version)/data.txt \ + $(version)/concrete.med \ + $(version)/ferraill.med + +NOT_USED_FILES = buildparticules.py particules.png +EXTRA_DIST += $(notinstall_version) $(NOT_USED_FILES) diff --git a/src/Tools/padder/resources/padderexe/buildparticules.py b/src/Tools/padder/resources/padderexe/buildparticules.py new file mode 100755 index 000000000..04035f1f1 --- /dev/null +++ b/src/Tools/padder/resources/padderexe/buildparticules.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +import geompy +import salome +salome.salome_init() +theStudy = salome.myStudy +geompy.init_geom(theStudy) + +filename="/home/gboulant/development/projets/salome/SPADDER/spadder/resources/padderexe/REF_spheres.dat.xyz" +file=open(filename,'rb') + +import csv +datalines = csv.reader(file, delimiter=' ') +i=0 +for row in datalines: + x=float(row[0]) + y=float(row[1]) + z=float(row[2]) + rayon=float(row[3])/2. + + centre = geompy.MakeVertex(x, y, z) + particule = geompy.MakeSpherePntR(centre, rayon) + geompy.addToStudy( particule, 'p'+str(i) ) + i+=1 diff --git a/src/Tools/padder/resources/padderexe/envPadder.sh.in b/src/Tools/padder/resources/padderexe/envPadder.sh.in new file mode 100644 index 000000000..90848a915 --- /dev/null +++ b/src/Tools/padder/resources/padderexe/envPadder.sh.in @@ -0,0 +1,27 @@ +#!/bin/sh +# Copyright (C) 2011-2012 CEA/DEN, EDF R&D +# +# 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 +# + +# This script defines the shell extension required for executing the +# padder executable program on the localhost (for test purposes) + +MED_ROOT="@MED3HOME@" +HDF_ROOT="@HDF5HOME@" +CGAL_ROOT="@CGALHOME@" +export LD_LIBRARY_PATH="$MED_ROOT/lib:$HDF_ROOT/lib:$CGAL_ROOT/lib:$LD_LIBRARY_PATH" diff --git a/src/Tools/padder/resources/padderexe/med2/REF_FinalEDMesh.med b/src/Tools/padder/resources/padderexe/med2/REF_FinalEDMesh.med new file mode 100644 index 0000000000000000000000000000000000000000..0072943fdf3cdf9f836be45d5cecea1e214e37eb GIT binary patch literal 244932 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 z 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/resources/padderexe/med2/REF_spheres.dat.xyz b/src/Tools/padder/resources/padderexe/med2/REF_spheres.dat.xyz new file mode 100644 index 000000000..d511c3821 --- /dev/null +++ b/src/Tools/padder/resources/padderexe/med2/REF_spheres.dat.xyz @@ -0,0 +1,4097 @@ +0.00782971 0.00837862 0.188345 0.00802045 1 0 +0.0154087 0.00912149 0.0068625 0.00705037 1 0 +0.00853443 0.191571 0.191485 0.00877202 1 0 +0.00831246 0.192152 0.00532739 0.00567024 1 0 +0.392373 0.0103119 0.19108 0.00782777 1 0 +0.382012 0.00642122 0.00627707 0.00662885 1 0 +0.393074 0.19203 0.194472 0.00590232 1 0 +0.387096 0.187644 0.0151337 0.0125061 1 0 +0.0107355 0.00843302 0.0669608 0.00880526 1 0 +0.0130229 0.0100937 0.135407 0.0113355 1 0 +0.0066941 0.0661207 0.191646 0.00802792 1 0 +0.0103253 0.133553 0.192494 0.00822172 1 0 +0.0101874 0.193228 0.0665758 0.00716313 1 0 +0.0128864 0.189676 0.134999 0.0115899 1 0 +0.00969349 0.0657247 0.00859538 0.00955034 1 0 +0.00859049 0.132642 0.00899122 0.00969975 1 0 +0.385203 0.00809838 0.0668407 0.00880575 1 0 +0.386131 0.0107039 0.135508 0.0115306 1 0 +0.393588 0.0665381 0.191132 0.00775488 1 0 +0.386598 0.134808 0.191052 0.0102175 1 0 +0.389949 0.190842 0.0653571 0.00998665 1 0 +0.389353 0.192011 0.133631 0.00881068 1 0 +0.392783 0.067271 0.00660849 0.00698802 1 0 +0.388304 0.134196 0.0104575 0.0117876 1 0 +0.078446 0.0111886 0.00562685 0.00698503 1 0 +0.15933 0.010178 0.0071953 0.00782222 1 0 +0.239408 0.0117814 0.00838242 0.00902724 1 0 +0.319266 0.0104094 0.0109799 0.0115214 1 0 +0.0801022 0.00839366 0.191955 0.009342 1 0 +0.158855 0.0101758 0.190771 0.00985613 1 0 +0.240059 0.008372 0.19268 0.00811281 1 0 +0.319477 0.00952385 0.189827 0.0109663 1 0 +0.080239 0.184554 0.0107232 0.0118692 1 0 +0.158896 0.189543 0.0104515 0.0111914 1 0 +0.239206 0.192295 0.0114336 0.00824166 1 0 +0.321165 0.189065 0.0198378 0.011684 1 0 +0.0784293 0.189358 0.189415 0.0114984 1 0 +0.158102 0.183775 0.186583 0.0140474 1 0 +0.240505 0.191585 0.192095 0.00878636 1 0 +0.320737 0.187137 0.173616 0.0136636 1 0 +0.0102439 0.0601788 0.110611 0.011165 1 0 +0.00632993 0.0606389 0.0501703 0.00699327 1 0 +0.00886816 0.118481 0.073089 0.00961398 1 0 +0.0084637 0.130613 0.129338 0.0092135 1 0 +0.390636 0.0614183 0.111012 0.010272 1 0 +0.391413 0.0615332 0.0505572 0.0092585 1 0 +0.392479 0.119396 0.0733079 0.00827305 1 0 +0.389389 0.131268 0.129331 0.0113393 1 0 +0.274388 0.0076649 0.0575634 0.00842627 1 0 +0.134941 0.00959236 0.0653354 0.0105187 1 0 +0.0651853 0.00803465 0.0661969 0.00886309 1 0 +0.110295 0.00934566 0.133492 0.010401 1 0 +0.185332 0.0137269 0.132579 0.0147265 1 0 +0.331786 0.0104332 0.0984378 0.0115849 1 0 +0.207876 0.0106516 0.0633744 0.0116242 1 0 +0.261527 0.00924836 0.126979 0.0103371 1 0 +0.274177 0.19319 0.0574857 0.00758089 1 0 +0.135692 0.189732 0.0648955 0.0112128 1 0 +0.0663 0.187677 0.0661222 0.0131058 1 0 +0.109855 0.18853 0.132715 0.0125194 1 0 +0.185773 0.18914 0.131535 0.0118814 1 0 +0.332133 0.189017 0.0978282 0.0121498 1 0 +0.207852 0.192331 0.0632991 0.00864703 1 0 +0.261572 0.192711 0.126344 0.00840316 1 0 +0.0697814 0.106226 0.00960134 0.0108828 1 0 +0.131623 0.0598217 0.0086532 0.00941678 1 0 +0.266788 0.0652192 0.0102618 0.0112281 1 0 +0.340609 0.0648565 0.00796636 0.00886868 1 0 +0.291252 0.132331 0.011155 0.0121409 1 0 +0.215098 0.131697 0.0133923 0.0143177 1 0 +0.19535 0.0639082 0.0075357 0.00848584 1 0 +0.141228 0.128178 0.0122679 0.0132652 1 0 +0.0684398 0.10719 0.184859 0.0147587 1 0 +0.131467 0.0598993 0.189562 0.0112022 1 0 +0.267697 0.0654378 0.191161 0.00979633 1 0 +0.340927 0.0652784 0.190301 0.0106229 1 0 +0.291838 0.133337 0.1901 0.0109407 1 0 +0.216095 0.132971 0.190766 0.0102258 1 0 +0.194717 0.0641122 0.187867 0.0130967 1 0 +0.141036 0.129092 0.188982 0.0120216 1 0 +0.0669682 0.0665113 0.0941136 0.0147587 1 0 +0.0833322 0.143862 0.12175 0.0147587 1 0 +0.189807 0.104641 0.118969 0.0147587 1 0 +0.344502 0.0601456 0.0998379 0.0141213 1 0 +0.274144 0.081421 0.098988 0.0147587 1 0 +0.329187 0.144678 0.0830052 0.0143816 1 0 +0.243586 0.146288 0.0866495 0.0147193 1 0 +0.132914 0.0731836 0.103217 0.0147587 1 0 +0.0428272 0.119038 0.052029 0.0119519 1 0 +0.261979 0.150932 0.146194 0.0117141 1 0 +0.102115 0.140291 0.0526524 0.0147587 1 0 +0.0340305 0.101951 0.155062 0.0129279 1 0 +0.357144 0.101091 0.153465 0.0134399 1 0 +0.187456 0.162108 0.0494275 0.0113731 1 0 +0.232702 0.0552718 0.0430421 0.0140424 1 0 +0.171625 0.0519527 0.049372 0.0140307 1 0 +0.369195 0.0892426 0.0427442 0.0106064 1 0 +0.282643 0.107185 0.149494 0.0143329 1 0 +0.301665 0.113049 0.0909966 0.0106702 1 0 +0.310165 0.138814 0.141503 0.0130046 1 0 +0.343165 0.122885 0.118235 0.0109302 1 0 +0.315644 0.0912562 0.126227 0.0126514 1 0 +0.324143 0.117021 0.176733 0.0108517 1 0 +0.241903 0.0991953 0.191196 0.00904411 1 0 +0.205529 0.0985492 0.193468 0.00687593 1 0 +0.231275 0.0648555 0.192248 0.00804733 1 0 +0.192501 0.0845542 0.159484 0.0113254 1 0 +0.203006 0.118545 0.159484 0.0112056 1 0 +0.228532 0.0851619 0.159484 0.0128027 1 0 +0.0076378 0.159507 0.0697794 0.00794379 1 0 +0.00797419 0.124577 0.101219 0.00825663 1 0 +0.00724401 0.165552 0.0978756 0.0074128 1 0 +0.0416661 0.137341 0.125342 0.0105815 1 0 +0.0416661 0.131346 0.0975119 0.0124504 1 0 +0.0416661 0.171931 0.0942084 0.0143232 1 0 +0.0327151 0.00826481 0.0665097 0.00864515 1 0 +0.0663095 0.0332556 0.0799531 0.00903774 1 0 +0.0334841 0.0332556 0.0803902 0.0122868 1 0 +0.0573668 0.0559547 0.065866 0.00791185 1 0 +0.0567081 0.0226991 0.0517055 0.00704303 1 0 +0.0238827 0.0226991 0.0521425 0.00900202 1 0 +0.342522 0.0625798 0.149919 0.0125448 1 0 +0.350823 0.0806185 0.126652 0.00858079 1 0 +0.348843 0.0830528 0.176733 0.00764718 1 0 +0.363749 0.071594 0.15777 0.00763325 1 0 +0.357428 0.051121 0.130956 0.008721 1 0 +0.355448 0.0535553 0.181037 0.00667593 1 0 +0.0681372 0.08677 0.147057 0.0141747 1 0 +0.0504993 0.0842312 0.124588 0.00972722 1 0 +0.0516683 0.10449 0.177531 0.00716934 1 0 +0.0399296 0.0726263 0.156605 0.00748802 1 0 +0.0563985 0.0549064 0.12613 0.00891438 1 0 +0.0575674 0.0751651 0.179073 0.00997186 1 0 +0.0329075 0.194096 0.0663952 0.00629114 1 0 +0.054239 0.159519 0.0589108 0.0106545 1 0 +0.0214136 0.159519 0.0593479 0.0115942 1 0 +0.0444173 0.141073 0.0412066 0.00615021 1 0 +0.0558291 0.181554 0.0480883 0.00684724 1 0 +0.0230037 0.181554 0.0485254 0.00865493 1 0 +0.305829 0.166343 0.00930025 0.00968747 1 0 +0.310165 0.138814 0.0415026 0.0115073 1 0 +0.294382 0.156927 0.0589093 0.0106354 1 0 +0.275359 0.151063 0.0174067 0.007417 1 0 +0.289788 0.184588 0.0174067 0.00953043 1 0 +0.00594911 0.0607854 0.0803628 0.0061939 1 0 +0.00479756 0.0897179 0.0617091 0.00502066 1 0 +0.00616272 0.0895967 0.0918222 0.00633686 1 0 +0.0334841 0.0926703 0.0836937 0.0109375 1 0 +0.0334841 0.0638087 0.072279 0.0100164 1 0 +0.0334841 0.0638137 0.10221 0.0086386 1 0 +0.38987 0.00500221 0.0329115 0.0052469 1 0 +0.393944 0.0306307 0.0587043 0.00630491 1 0 +0.381303 0.0482723 0.0447202 0.00584722 1 0 +0.381303 0.0177192 0.0528313 0.0073099 1 0 +0.100494 0.0834848 0.190271 0.00999289 1 0 +0.136589 0.094439 0.188634 0.0116715 1 0 +0.10535 0.11814 0.185976 0.0143114 1 0 +0.137312 0.100876 0.151609 0.013982 1 0 +0.132467 0.0667472 0.151609 0.0122049 1 0 +0.10111 0.0901062 0.151609 0.0133919 1 0 +0.196804 0.00564298 0.0979846 0.00597401 1 0 +0.234676 0.00734643 0.0952439 0.0075486 1 0 +0.223513 0.00751701 0.129666 0.00783969 1 0 +0.267589 0.0407105 0.112875 0.0108776 1 0 +0.241081 0.0407105 0.0814703 0.0138234 1 0 +0.230019 0.0407105 0.115571 0.0123786 1 0 +0.395052 0.090127 0.0918997 0.00512988 1 0 +0.394346 0.124977 0.101082 0.00594888 1 0 +0.394125 0.0960045 0.119809 0.00606523 1 0 +0.372251 0.0954827 0.114386 0.0118067 1 0 +0.372251 0.0894874 0.0865558 0.0106283 1 0 +0.372251 0.0606309 0.105072 0.00706064 1 0 +0.240956 0.194533 0.060616 0.00571904 1 0 +0.225802 0.173144 0.0753011 0.00853777 1 0 +0.258741 0.173144 0.0722814 0.00850498 1 0 +0.251581 0.157732 0.0607315 0.00735967 1 0 +0.233797 0.184588 0.049383 0.00834547 1 0 +0.266736 0.184588 0.0463634 0.00513679 1 0 +0.00936532 0.0328331 0.190733 0.00954134 1 0 +0.03956 0.0329435 0.190241 0.00993531 1 0 +0.0229144 0.0549841 0.179073 0.00828957 1 0 +0.223628 0.194435 0.129147 0.00587496 1 0 +0.25231 0.173144 0.106706 0.00865879 1 0 +0.214741 0.173144 0.109401 0.011377 1 0 +0.230868 0.156087 0.11999 0.00925117 1 0 +0.239592 0.182943 0.140046 0.00761219 1 0 +0.202023 0.182943 0.142742 0.00643796 1 0 +0.360114 0.190042 0.16695 0.0101415 1 0 +0.324593 0.172339 0.141503 0.0102159 1 0 +0.364593 0.172339 0.108169 0.0128744 1 0 +0.344574 0.149581 0.121034 0.010329 1 0 +0.37998 0.177242 0.146199 0.00823187 1 0 +0.0750501 0.153331 0.00691301 0.00725809 1 0 +0.0857105 0.12366 0.0263262 0.00879905 1 0 +0.0740611 0.1517 0.0415182 0.00806666 1 0 +0.0576568 0.135069 0.0151921 0.008488 1 0 +0.0630037 0.181554 0.0151921 0.00733111 1 0 +0.100334 0.00507726 0.0657085 0.00540601 1 0 +0.101348 0.0332556 0.0799218 0.0124732 1 0 +0.0888629 0.0482426 0.0678241 0.00969621 1 0 +0.0882042 0.0149869 0.0536635 0.00741779 1 0 +0.123242 0.0149869 0.0536322 0.00573819 1 0 +0.366088 0.00696468 0.116037 0.00721858 1 0 +0.366109 0.00552948 0.0824399 0.00579393 1 0 +0.338439 0.0300728 0.0991444 0.00767144 1 0 +0.372251 0.0300728 0.116586 0.0110535 1 0 +0.372251 0.0300728 0.0832523 0.0110382 1 0 +0.203006 0.118545 0.0594845 0.0120352 1 0 +0.211255 0.0799562 0.0810055 0.0125263 1 0 +0.224453 0.0938606 0.021521 0.0112369 1 0 +0.202163 0.0536123 0.046207 0.00768668 1 0 +0.180716 0.0782967 0.0841705 0.0111456 1 0 +0.193914 0.0922011 0.024686 0.0130531 1 0 +0.394528 0.0305532 0.122124 0.00575726 1 0 +0.385177 0.0516063 0.13619 0.00782689 1 0 +0.385177 0.0210482 0.147704 0.0073708 1 0 +0.0751502 0.105187 0.107932 0.0104693 1 0 +0.0763192 0.125446 0.160875 0.010952 1 0 +0.108123 0.108523 0.112484 0.0110378 1 0 +0.0999413 0.0698475 0.0986656 0.00836315 1 0 +0.165758 0.116605 0.159484 0.0121527 1 0 +0.160914 0.0824757 0.159484 0.013619 1 0 +0.161361 0.0889121 0.111093 0.00836142 1 0 +0.294009 0.0323892 0.189614 0.01061 1 0 +0.304296 0.0653391 0.191657 0.00865693 1 0 +0.330471 0.0324018 0.19251 0.00785154 1 0 +0.302412 0.0509357 0.177837 0.0115737 1 0 +0.26577 0.0512702 0.177837 0.00661967 1 0 +0.187851 0.15232 0.125561 0.0120432 1 0 +0.216697 0.125464 0.102809 0.00941345 1 0 +0.215521 0.154198 0.0680385 0.00864791 1 0 +0.188632 0.133375 0.0841982 0.0112806 1 0 +0.186676 0.181054 0.0907904 0.0113755 1 0 +0.257158 0.00634884 0.0286278 0.00661142 1 0 +0.24115 0.00797505 0.0605706 0.00822542 1 0 +0.223999 0.00751144 0.0314485 0.00773857 1 0 +0.22036 0.0276359 0.0534973 0.00800536 1 0 +0.253299 0.0276359 0.0504777 0.00881505 1 0 +0.0994942 0.063411 0.0470568 0.014322 1 0 +0.132467 0.0667472 0.0516087 0.0130026 1 0 +0.121836 0.0515788 0.072376 0.00981294 1 0 +0.121389 0.0451423 0.0207672 0.00695694 1 0 +0.199897 0.192878 0.195143 0.00502548 1 0 +0.213141 0.191335 0.165953 0.0089279 1 0 +0.17265 0.189447 0.165926 0.0108864 1 0 +0.2707 0.0735521 0.049494 0.0125585 1 0 +0.28673 0.0582431 0.0706498 0.00967816 1 0 +0.283286 0.0503742 0.0211558 0.00766029 1 0 +0.266009 0.0451685 0.0426768 0.00870186 1 0 +0.253423 0.0683464 0.071015 0.0092965 1 0 +0.249979 0.0604775 0.021521 0.0070212 1 0 +0.227566 0.166162 0.00927722 0.00964504 1 0 +0.23789 0.150813 0.0174067 0.0083311 1 0 +0.249788 0.184588 0.0174067 0.00630637 1 0 +0.20183 0.147279 0.0247137 0.00805173 1 0 +0.21747 0.0316523 0.190068 0.010193 1 0 +0.229739 0.0506625 0.177837 0.0108254 1 0 +0.186798 0.0522662 0.181595 0.00590615 1 0 +0.231976 0.0930308 0.108978 0.011216 1 0 +0.245174 0.106935 0.049494 0.00991656 1 0 +0.258865 0.113855 0.0928188 0.00909479 1 0 +0.229895 0.139369 0.0433248 0.0114901 1 0 +0.309323 0.0707833 0.099413 0.00918863 1 0 +0.30326 0.0407105 0.0987195 0.0125128 1 0 +0.269214 0.0591391 0.127331 0.00909719 1 0 +0.304393 0.0485014 0.127756 0.0125593 1 0 +0.298329 0.0184286 0.127063 0.0120348 1 0 +0.342522 0.0625798 0.049919 0.0125044 1 0 +0.353554 0.047792 0.069417 0.00851466 1 0 +0.351574 0.0502263 0.019498 0.00671085 1 0 +0.365901 0.0623405 0.0408701 0.00679192 1 0 +0.356848 0.0746941 0.0712911 0.0085842 1 0 +0.354868 0.0771284 0.0213721 0.00710973 1 0 +0.393665 0.0612623 0.0805882 0.00656835 1 0 +0.372251 0.0606259 0.0751412 0.00928767 1 0 +0.00800775 0.00481281 0.0330032 0.00501284 1 0 +0.00493794 0.0302994 0.058471 0.0051964 1 0 +0.0238827 0.0532522 0.0440314 0.0064865 1 0 +0.388819 0.00982632 0.167372 0.0110774 1 0 +0.359782 0.0103331 0.167339 0.0105166 1 0 +0.360202 0.00555297 0.194979 0.00521289 1 0 +0.307343 0.0732175 0.149494 0.0100654 1 0 +0.297242 0.00716979 0.112559 0.0074196 1 0 +0.262658 0.0184286 0.141219 0.00586963 1 0 +0.252783 0.14861 0.116422 0.00781161 1 0 +0.261506 0.175466 0.136478 0.00659803 1 0 +0.240065 0.158409 0.149762 0.00585704 1 0 +0.280059 0.00504557 0.194484 0.00529663 1 0 +0.291028 0.0109082 0.163718 0.0111851 1 0 +0.250706 0.00816308 0.163779 0.0085333 1 0 +0.251026 0.191692 0.163287 0.00868018 1 0 +0.0742247 0.0530447 0.190726 0.00951869 1 0 +0.105992 0.0295004 0.189733 0.0105254 1 0 +0.121557 0.0494515 0.179666 0.00632887 1 0 +0.0902001 0.0728104 0.179666 0.0112387 1 0 +0.0400163 0.189738 0.166964 0.0104471 1 0 +0.0416661 0.171931 0.127542 0.0126428 1 0 +0.0611786 0.149062 0.142927 0.00777122 1 0 +0.0195125 0.177131 0.148718 0.00844259 1 0 +0.280463 0.194149 0.194111 0.00609663 1 0 +0.30581 0.166488 0.193747 0.00663919 1 0 +0.265709 0.166873 0.191652 0.00860795 1 0 +0.276561 0.141941 0.173097 0.00797356 1 0 +0.118638 0.186606 0.187079 0.0130349 1 0 +0.149567 0.165601 0.18603 0.0143514 1 0 +0.110778 0.164928 0.189958 0.0102913 1 0 +0.112521 0.136216 0.160875 0.0123521 1 0 +0.168005 0.0965757 0.192944 0.00723432 1 0 +0.178652 0.13081 0.193931 0.00640298 1 0 +0.332251 0.0300728 0.149919 0.0103012 1 0 +0.325711 0.00860382 0.149657 0.00892666 1 0 +0.0548977 0.0927747 0.0730713 0.00893812 1 0 +0.0214136 0.118934 0.0626514 0.00597593 1 0 +0.0214136 0.0900721 0.0512367 0.00900762 1 0 +0.321909 0.0476054 0.0710748 0.0096664 1 0 +0.319929 0.0500397 0.0211558 0.00827911 1 0 +0.267936 0.00808185 0.0922081 0.00836643 1 0 +0.27402 0.0407105 0.0784506 0.0113994 1 0 +0.370562 0.0994924 0.193807 0.00632978 1 0 +0.378572 0.117212 0.176733 0.00887554 1 0 +0.378572 0.0838791 0.176733 0.00900273 1 0 +0.0170152 0.0815337 0.132684 0.00868612 1 0 +0.0229144 0.0522088 0.134226 0.00857542 1 0 +0.00714838 0.192871 0.0328516 0.00736289 1 0 +0.0329743 0.189293 0.0327432 0.0108652 1 0 +0.0230037 0.181554 0.0151921 0.00829238 1 0 +0.280033 0.194371 0.00546809 0.00575251 1 0 +0.265391 0.166364 0.00786568 0.00811157 1 0 +0.336844 0.102412 0.0914216 0.0109427 1 0 +0.364593 0.137749 0.10597 0.0106918 1 0 +0.378572 0.115956 0.1412 0.00720476 1 0 +0.160389 0.00646817 0.0988948 0.0067168 1 0 +0.17157 0.00638728 0.0645314 0.00668162 1 0 +0.189822 0.0259764 0.0566623 0.00813572 1 0 +0.17876 0.0259764 0.0907627 0.0123462 1 0 +0.153676 0.0259764 0.057551 0.00815413 1 0 +0.147479 0.00853593 0.0323513 0.00887264 1 0 +0.183736 0.00755407 0.0314761 0.0077984 1 0 +0.378572 0.0811038 0.131886 0.00909792 1 0 +0.00591154 0.132048 0.1648 0.00629393 1 0 +0.0195125 0.142541 0.146519 0.0071908 1 0 +0.0195125 0.143797 0.182052 0.00712555 1 0 +0.0365277 0.128106 0.159583 0.00666505 1 0 +0.0170152 0.116386 0.141999 0.00646397 1 0 +0.0170152 0.117642 0.177531 0.00806451 1 0 +0.370844 0.0324118 0.191016 0.00912187 1 0 +0.134768 0.188691 0.166564 0.0115827 1 0 +0.148015 0.19024 0.132253 0.0100692 1 0 +0.134614 0.171931 0.126952 0.0146729 1 0 +0.097124 0.171931 0.127505 0.00794981 1 0 +0.392841 0.0977101 0.0250466 0.00727379 1 0 +0.394032 0.126426 0.0363921 0.00628753 1 0 +0.384597 0.104036 0.058009 0.00656267 1 0 +0.384597 0.0751744 0.0465943 0.00530318 1 0 +0.384597 0.111288 0.0213721 0.00858782 1 0 +0.390129 0.167151 0.0101153 0.0100334 1 0 +0.360693 0.166517 0.0106686 0.0108482 1 0 +0.361679 0.190338 0.013115 0.00979974 1 0 +0.364593 0.139006 0.0415026 0.0137119 1 0 +0.325829 0.188443 0.149303 0.0118787 1 0 +0.366315 0.193143 0.115659 0.00709951 1 0 +0.330781 0.172339 0.0907281 0.00719079 1 0 +0.393701 0.16717 0.194106 0.00622614 1 0 +0.389212 0.189767 0.166773 0.0104671 1 0 +0.37998 0.143909 0.179532 0.00763044 1 0 +0.119432 0.00577778 0.0058474 0.00598998 1 0 +0.107374 0.00697596 0.0325843 0.00717186 1 0 +0.0953788 0.0149869 0.0207672 0.00746824 1 0 +0.392788 0.0332226 0.193286 0.00688538 1 0 +0.385177 0.0543815 0.181037 0.00675563 1 0 +0.13657 0.124252 0.12036 0.0141879 1 0 +0.227045 0.070749 0.137322 0.0133981 1 0 +0.165758 0.116605 0.0594845 0.010786 1 0 +0.137312 0.100876 0.0516087 0.0146835 1 0 +0.117515 0.106738 0.0779349 0.0111866 1 0 +0.145961 0.122466 0.0858107 0.0144476 1 0 +0.121912 0.13443 0.0263262 0.00836417 1 0 +0.303644 0.0655245 0.00637762 0.0067013 1 0 +0.307343 0.0732175 0.049494 0.0112366 1 0 +0.0898928 0.0528545 0.0188092 0.0112597 1 0 +0.100339 0.0835407 0.00719202 0.00745889 1 0 +0.0681372 0.08677 0.0470568 0.0128114 1 0 +0.0845416 0.103401 0.073383 0.011455 1 0 +0.117068 0.100301 0.0263262 0.0125396 1 0 +0.227947 0.166669 0.192156 0.00820954 1 0 +0.239091 0.141691 0.173097 0.00912766 1 0 +0.217177 0.149168 0.176665 0.00718058 1 0 +0.359991 0.00561588 0.00622086 0.00588961 1 0 +0.330111 0.0325816 0.00548694 0.00583707 1 0 +0.370436 0.0326136 0.00785919 0.00799892 1 0 +0.187721 0.166242 0.0085089 0.00875112 1 0 +0.17892 0.13031 0.0061861 0.00650468 1 0 +0.150608 0.164404 0.0088986 0.00927567 1 0 +0.164583 0.145339 0.0247137 0.0094048 1 0 +0.163348 0.0622665 0.195283 0.0049871 1 0 +0.184104 0.0723527 0.14108 0.0098866 1 0 +0.155211 0.0501878 0.181595 0.00782231 1 0 +0.0323044 0.00983341 0.0328972 0.00998101 1 0 +0.286387 0.145483 0.0848274 0.0107116 1 0 +0.295583 0.147805 0.114599 0.0115574 1 0 +0.268062 0.116176 0.122591 0.0106126 1 0 +0.286606 0.0175326 0.0501124 0.00575424 1 0 +0.393607 0.159806 0.0697236 0.00670458 1 0 +0.392433 0.165936 0.0977602 0.00773155 1 0 +0.364593 0.131754 0.0781394 0.00950111 1 0 +0.364593 0.172339 0.0748359 0.0114168 1 0 +0.370471 0.0987242 0.00887783 0.00900479 1 0 +0.334864 0.104846 0.0415026 0.0144509 1 0 +0.349191 0.11696 0.0628747 0.00991734 1 0 +0.391274 0.191165 0.0995132 0.00895533 1 0 +0.394266 0.165746 0.131262 0.0061074 1 0 +0.212893 0.00607632 0.166473 0.00634648 1 0 +0.225089 0.0184286 0.143914 0.0112197 1 0 +0.0100767 0.166711 0.0104198 0.0102409 1 0 +0.0401862 0.166512 0.0109898 0.0111838 1 0 +0.0402255 0.193994 0.00591644 0.00621158 1 0 +0.0230037 0.148221 0.0151921 0.00783265 1 0 +0.381303 0.0510526 0.019498 0.00780039 1 0 +0.384597 0.0779546 0.0213721 0.00716514 1 0 +0.00795073 0.100327 0.191944 0.00820322 1 0 +0.00729948 0.0984007 0.164901 0.00747012 1 0 +0.0170152 0.0843089 0.177531 0.00831272 1 0 +0.309658 0.0175326 0.0211558 0.00733963 1 0 +0.144786 0.1512 0.0510399 0.0110181 1 0 +0.0398287 0.0100308 0.167135 0.0102105 1 0 +0.0956244 0.00846175 0.166819 0.00882322 1 0 +0.0553841 0.00785993 0.133774 0.00819954 1 0 +0.0783722 0.0216507 0.145704 0.0102551 1 0 +0.0229144 0.0216507 0.14574 0.00846962 1 0 +0.122004 0.0558879 0.131275 0.00867966 1 0 +0.0125628 0.192161 0.168294 0.00802027 1 0 +0.37998 0.142652 0.143999 0.00692701 1 0 +0.234711 0.192608 0.0948239 0.00758701 1 0 +0.267805 0.192014 0.0920019 0.00827455 1 0 +0.141191 0.0409633 0.0454532 0.00814838 1 0 +0.303395 0.00577704 0.0780608 0.00607367 1 0 +0.315845 0.0175326 0.0703813 0.00924845 1 0 +0.00557333 0.165517 0.131393 0.00595387 1 0 +0.134321 0.0365918 0.0844737 0.0102843 1 0 +0.15227 0.0625682 0.0762948 0.00870398 1 0 +0.00610125 0.1259 0.0362767 0.00640747 1 0 +0.0214136 0.126186 0.0260145 0.00861099 1 0 +0.203979 0.135263 0.13615 0.00946628 1 0 +0.0541656 0.130645 0.182052 0.00832584 1 0 +0.0586813 0.122907 0.138406 0.00909749 1 0 +0.0733752 0.190249 0.0328107 0.0100889 1 0 +0.0838829 0.170146 0.0592225 0.00889822 1 0 +0.253452 0.132342 0.00666335 0.00698182 1 0 +0.267364 0.139619 0.0433248 0.0124672 1 0 +0.122838 0.00676448 0.099532 0.00706562 1 0 +0.088942 0.0332556 0.113687 0.0111015 1 0 +0.121915 0.0365918 0.118239 0.010264 1 0 +0.120406 0.00711212 0.193704 0.00641037 1 0 +0.135269 0.00862405 0.166962 0.00889347 1 0 +0.111005 0.0192961 0.146296 0.00582169 1 0 +0.297338 0.00779496 0.0287033 0.00808652 1 0 +0.325794 0.00845973 0.0487937 0.00864908 1 0 +0.187851 0.0523203 0.125561 0.0131926 1 0 +0.182148 0.0200324 0.147672 0.00640389 1 0 +0.10101 0.193895 0.065773 0.00643011 1 0 +0.0885172 0.194105 0.0997932 0.00613151 1 0 +0.123113 0.194109 0.099166 0.00619423 1 0 +0.0744915 0.171931 0.0937713 0.0101514 1 0 +0.10953 0.171931 0.09374 0.0118815 1 0 +0.13666 0.0941074 0.00611892 0.00642099 1 0 +0.156667 0.0902607 0.024686 0.0119912 1 0 +0.303258 0.19412 0.0778613 0.00617833 1 0 +0.297043 0.193387 0.112406 0.00685793 1 0 +0.29511 0.172339 0.104884 0.0122603 1 0 +0.301542 0.172339 0.0704592 0.00991179 1 0 +0.145949 0.0298672 0.194806 0.00545143 1 0 +0.169201 0.0200324 0.181595 0.00717934 1 0 +0.0346919 0.119789 0.0059911 0.0062676 1 0 +0.0560667 0.113033 0.0260145 0.00745023 1 0 +0.39462 0.0641305 0.0251582 0.00571618 1 0 +0.358552 0.127788 0.156265 0.00671995 1 0 +0.22384 0.19136 0.0315576 0.00886931 1 0 +0.197737 0.181054 0.05669 0.00568653 1 0 +0.31648 0.0993063 0.190149 0.010111 1 0 +0.196881 0.194663 0.0976769 0.00566597 1 0 +0.0927236 0.142077 0.0872012 0.00896175 1 0 +0.325551 0.143717 0.179532 0.0103648 1 0 +0.293068 0.0329148 0.00869786 0.00893395 1 0 +0.145887 0.0300695 0.00590849 0.0061642 1 0 +0.163592 0.0622743 0.00591676 0.00618727 1 0 +0.177744 0.0321437 0.00757873 0.00778291 1 0 +0.18341 0.0582102 0.024686 0.00701536 1 0 +0.151823 0.0561318 0.024686 0.00798044 1 0 +0.148058 0.190384 0.0324284 0.00993649 1 0 +0.171705 0.193063 0.0644836 0.0072308 1 0 +0.183703 0.191741 0.0316221 0.00850022 1 0 +0.161592 0.181054 0.0575787 0.00827033 1 0 +0.0334841 0.0332556 0.113723 0.0127761 1 0 +0.177315 0.0318544 0.193179 0.00702079 1 0 +0.00742114 0.00649798 0.100794 0.00672344 1 0 +0.0327225 0.00713169 0.100056 0.00728828 1 0 +0.00670312 0.0301976 0.122088 0.00698302 1 0 +0.360594 0.166754 0.190849 0.00932946 1 0 +0.360458 0.194671 0.1947 0.00561391 1 0 +0.187984 0.166822 0.189677 0.0105794 1 0 +0.0890311 0.0525517 0.126723 0.0104433 1 0 +0.279526 0.0994358 0.191957 0.00838132 1 0 +0.2707 0.0735521 0.149494 0.0128078 1 0 +0.395059 0.132156 0.164748 0.00532758 1 0 +0.0630797 0.13145 0.0868896 0.010546 1 0 +0.297324 0.192975 0.0285702 0.00731488 1 0 +0.256988 0.193627 0.0285966 0.00663241 1 0 +0.159405 0.0365918 0.117685 0.0118586 1 0 +0.148494 0.0192961 0.145743 0.0110561 1 0 +0.155658 0.0566242 0.133204 0.0102794 1 0 +0.347491 0.0177192 0.0687235 0.00944119 1 0 +0.253944 0.133117 0.19163 0.00868888 1 0 +0.160668 0.192288 0.0983666 0.00794803 1 0 +0.162767 0.15232 0.0923495 0.011823 1 0 +0.118921 0.170146 0.0591912 0.00871951 1 0 +0.393862 0.0306371 0.0888131 0.00630879 1 0 +0.0342825 0.0868882 0.194405 0.00582405 1 0 +0.0724711 0.129665 0.0523407 0.00787313 1 0 +0.0114266 0.193846 0.10058 0.00631329 1 0 +0.00798199 0.0330447 0.00607383 0.00636767 1 0 +0.0238827 0.0560324 0.0188092 0.00805161 1 0 +0.225893 0.127786 0.132581 0.011245 1 0 +0.0878774 0.00698675 0.0995188 0.00723216 1 0 +0.359861 0.0100027 0.0325919 0.0101839 1 0 +0.00634885 0.0635272 0.15549 0.00666141 1 0 +0.198913 0.0523203 0.0914608 0.0110048 1 0 +0.316316 0.0979592 0.0127506 0.0129877 1 0 +0.279109 0.0990261 0.00597457 0.00631869 1 0 +0.282643 0.107185 0.049494 0.0141105 1 0 +0.0948512 0.190047 0.166682 0.0103369 1 0 +0.0557363 0.192315 0.133729 0.0080218 1 0 +0.345858 0.133694 0.191225 0.00911709 1 0 +0.291379 0.188532 0.163401 0.0117574 1 0 +0.147955 0.00690015 0.132871 0.00722402 1 0 +0.392977 0.0637462 0.155599 0.00734253 1 0 +0.205744 0.0981554 0.00574847 0.0060839 1 0 +0.213949 0.0598698 0.021521 0.00722832 1 0 +0.119829 0.194369 0.00555227 0.00581835 1 0 +0.110214 0.164362 0.00948955 0.00975918 1 0 +0.241564 0.0986453 0.00619258 0.0064188 1 0 +0.231155 0.0649184 0.00722361 0.00752111 1 0 +0.072516 0.00571679 0.032973 0.00607668 1 0 +0.0398795 0.0325987 0.0100453 0.0102223 1 0 +0.0749134 0.0529824 0.0074804 0.00770916 1 0 +0.034637 0.0863996 0.00611601 0.00636006 1 0 +0.0585358 0.0762134 0.0188092 0.00941862 1 0 +0.0452963 0.0822181 0.0448237 0.00939987 1 0 +0.0328906 0.192731 0.1 0.0074275 1 0 +0.00681689 0.00655054 0.166864 0.00687372 1 0 +0.00498748 0.0997191 0.00470913 0.00492065 1 0 +0.00722114 0.0969544 0.024775 0.00732295 1 0 +0.0214136 0.0928524 0.0260145 0.0106683 1 0 +0.00611892 0.0301864 0.0885671 0.00629176 1 0 +0.00549623 0.0956741 0.119829 0.00569037 1 0 +0.0334841 0.0986656 0.111524 0.0123951 1 0 +0.0742482 0.153988 0.191585 0.0087894 1 0 +0.346052 0.132703 0.0081435 0.00845818 1 0 +0.172624 0.00676555 0.166485 0.00710748 1 0 +0.105188 0.117628 0.00615975 0.0064399 1 0 +0.10855 0.190097 0.0325934 0.0101185 1 0 +0.168432 0.096057 0.00759595 0.00777318 1 0 +0.10551 0.0300778 0.00791612 0.00819381 1 0 +0.253178 0.0328009 0.00693181 0.00723025 1 0 +0.217325 0.0321298 0.00770683 0.00796454 1 0 +0.199642 0.193449 0.00648802 0.00669564 1 0 +0.19968 0.0060719 0.193867 0.00628404 1 0 +0.245174 0.106935 0.149494 0.0121355 1 0 +0.360308 0.190453 0.0326889 0.00972258 1 0 +0.326158 0.191821 0.0485561 0.00837765 1 0 +0.366278 0.195066 0.0822926 0.0051992 1 0 +0.392933 0.100219 0.00558497 0.00577655 1 0 +0.0342897 0.120289 0.194417 0.00586501 1 0 +0.0109856 0.167283 0.18869 0.0111292 1 0 +0.0394183 0.167057 0.191843 0.00833159 1 0 +0.390892 0.190956 0.0327244 0.0093067 1 0 +0.253763 0.0323933 0.190427 0.00989013 1 0 +0.279981 0.00552827 0.00540814 0.00558839 1 0 +0.390994 0.0986796 0.165026 0.00917001 1 0 +0.199351 0.00925291 0.00631537 0.00641028 1 0 +0.339858 0.0978656 0.116162 0.0143915 1 0 +0.291051 0.0839952 0.134791 0.0143838 1 0 +0.0230061 0.123341 0.115559 0.014715 1 0 +0.0519932 0.0216838 0.0700086 0.0118849 1 0 +0.0659504 0.0385772 0.0588947 0.0126856 1 0 +0.0384246 0.0355656 0.0575836 0.0111628 1 0 +0.0413681 0.0154344 0.0520067 0.00993293 1 0 +0.355919 0.0796688 0.144359 0.00987007 1 0 +0.353269 0.0664527 0.133592 0.00738205 1 0 +0.352004 0.0664607 0.170429 0.0103816 1 0 +0.359379 0.058104 0.157686 0.00654738 1 0 +0.0439492 0.0842267 0.143609 0.0103898 1 0 +0.0555715 0.0700535 0.136294 0.00934517 1 0 +0.0493466 0.0863296 0.163824 0.0106386 1 0 +0.0534492 0.0726907 0.152056 0.00677646 1 0 +0.0386384 0.178144 0.0651692 0.0144339 1 0 +0.0476738 0.170827 0.0475964 0.00663684 1 0 +0.0327376 0.19314 0.0513751 0.00696728 1 0 +0.308907 0.158592 0.0460618 0.0088282 1 0 +0.0166338 0.0802439 0.0709789 0.0135577 1 0 +0.0185188 0.0796215 0.097997 0.0135333 1 0 +0.017346 0.0603505 0.0840744 0.0102698 1 0 +0.385086 0.0340574 0.0498343 0.00972602 1 0 +0.388393 0.106695 0.102392 0.0116154 1 0 +0.38536 0.0845969 0.101285 0.00968738 1 0 +0.387865 0.0846269 0.118425 0.00763409 1 0 +0.387009 0.093955 0.0898924 0.00514799 1 0 +0.241671 0.181504 0.0670229 0.0112165 1 0 +0.226608 0.172233 0.0583028 0.00850386 1 0 +0.255964 0.172978 0.0541895 0.00979963 1 0 +0.249612 0.189723 0.0511142 0.00837173 1 0 +0.022717 0.0142507 0.190427 0.00962286 1 0 +0.0407331 0.0149915 0.192019 0.00699943 1 0 +0.235776 0.18609 0.115207 0.0139961 1 0 +0.248867 0.166126 0.126089 0.0122412 1 0 +0.22212 0.171399 0.131059 0.0115699 1 0 +0.229005 0.186785 0.133425 0.0054521 1 0 +0.0851153 0.146493 0.022762 0.0143185 1 0 +0.0669041 0.130177 0.0330515 0.0122099 1 0 +0.063305 0.151289 0.0168905 0.00877187 1 0 +0.0815405 0.0205122 0.0716063 0.012504 1 0 +0.0780966 0.038364 0.0727091 0.00571037 1 0 +0.0957399 0.0321352 0.0575695 0.010599 1 0 +0.105371 0.0143381 0.0549332 0.0098079 1 0 +0.353367 0.0167339 0.0905831 0.0132159 1 0 +0.385155 0.0166265 0.0999075 0.0139561 1 0 +0.210662 0.0770846 0.015566 0.0112815 1 0 +0.389953 0.0388172 0.108549 0.0102772 1 0 +0.374346 0.0517042 0.120709 0.011067 1 0 +0.370621 0.037495 0.137735 0.0114201 1 0 +0.390558 0.0348163 0.13853 0.00953474 1 0 +0.0614848 0.104058 0.127071 0.013075 1 0 +0.0841309 0.110185 0.141854 0.0146546 1 0 +0.313814 0.0452291 0.190212 0.00980316 1 0 +0.292761 0.0517713 0.19146 0.00875362 1 0 +0.21056 0.154445 0.121274 0.0111641 1 0 +0.206882 0.165797 0.0859801 0.014397 1 0 +0.185081 0.158987 0.101408 0.0131654 1 0 +0.240847 0.0199757 0.0656529 0.0122568 1 0 +0.106587 0.047547 0.0623922 0.00885443 1 0 +0.103367 0.0560932 0.0794102 0.0104592 1 0 +0.116016 0.0593138 0.0591628 0.00656649 1 0 +0.117395 0.0483294 0.048068 0.00910673 1 0 +0.191796 0.19261 0.178099 0.00739796 1 0 +0.190262 0.1898 0.155824 0.010507 1 0 +0.202655 0.189878 0.185887 0.00756968 1 0 +0.284667 0.0527579 0.0492684 0.0124918 1 0 +0.267685 0.0562034 0.0643551 0.0104832 1 0 +0.252363 0.0623362 0.0534503 0.00929825 1 0 +0.217215 0.109656 0.118837 0.0131045 1 0 +0.320736 0.0555155 0.0908827 0.0116948 1 0 +0.292623 0.0664634 0.113613 0.013154 1 0 +0.288493 0.0359165 0.11613 0.0108148 1 0 +0.306189 0.0324051 0.116388 0.00722874 1 0 +0.361999 0.0517455 0.0533655 0.0100488 1 0 +0.35477 0.0616834 0.0642386 0.00636022 1 0 +0.353108 0.0627607 0.0302231 0.00985696 1 0 +0.357697 0.0745456 0.0429063 0.00805419 1 0 +0.388473 0.0786088 0.0762144 0.0115309 1 0 +0.371176 0.0763511 0.0756446 0.00648226 1 0 +0.384953 0.0606811 0.0905716 0.0106986 1 0 +0.0133713 0.0395707 0.0482004 0.0112632 1 0 +0.377054 0.00985857 0.185869 0.00990833 1 0 +0.358234 0.00751157 0.184997 0.00687113 1 0 +0.322977 0.0666139 0.132647 0.0138483 1 0 +0.290648 0.0596014 0.144794 0.0119843 1 0 +0.318269 0.0522183 0.156885 0.0147333 1 0 +0.298642 0.0225801 0.107366 0.00809767 1 0 +0.275372 0.0400522 0.134644 0.0122502 1 0 +0.277295 0.0139269 0.111945 0.0142065 1 0 +0.243332 0.158549 0.107592 0.00849985 1 0 +0.242569 0.143772 0.132518 0.0118557 1 0 +0.250115 0.170904 0.148051 0.0102692 1 0 +0.270953 0.0113225 0.178894 0.0113339 1 0 +0.29312 0.0104373 0.185275 0.010733 1 0 +0.231865 0.191675 0.150453 0.0083346 1 0 +0.0363854 0.187793 0.145368 0.0117962 1 0 +0.037714 0.16799 0.147599 0.00817705 1 0 +0.286593 0.17804 0.189411 0.0105941 1 0 +0.293143 0.15599 0.186423 0.0120585 1 0 +0.264356 0.185135 0.187138 0.0121549 1 0 +0.164858 0.117047 0.185871 0.0144117 1 0 +0.177516 0.0986111 0.17122 0.0123366 1 0 +0.184805 0.120304 0.171268 0.0105479 1 0 +0.311233 0.0306946 0.142974 0.0118427 1 0 +0.304212 0.0102483 0.147532 0.0102694 1 0 +0.318374 0.0131916 0.133019 0.00952195 1 0 +0.0386367 0.0873516 0.0632239 0.0108306 1 0 +0.0346314 0.0785248 0.0759995 0.005206 1 0 +0.0129629 0.103031 0.0634684 0.0107145 1 0 +0.337932 0.040181 0.0586026 0.0119534 1 0 +0.32304 0.0510643 0.0458029 0.0104974 1 0 +0.250929 0.00828934 0.0790767 0.00830518 1 0 +0.279563 0.033216 0.0976184 0.0099148 1 0 +0.259255 0.0237673 0.0795199 0.0110999 1 0 +0.387591 0.0867004 0.191347 0.00868954 1 0 +0.380134 0.100613 0.182544 0.00878057 1 0 +0.363045 0.0756534 0.186215 0.0109639 1 0 +0.391256 0.0917194 0.178525 0.00601633 1 0 +0.0314241 0.0759766 0.119374 0.0117015 1 0 +0.029587 0.0691096 0.140595 0.0106787 1 0 +0.0388408 0.0552642 0.118268 0.0103264 1 0 +0.0157146 0.193858 0.0469593 0.00617142 1 0 +0.277481 0.181533 0.00721478 0.00724873 1 0 +0.271742 0.191377 0.0181054 0.00864813 1 0 +0.357203 0.0992801 0.0980791 0.010705 1 0 +0.346708 0.113725 0.102435 0.00767388 1 0 +0.363235 0.116484 0.111255 0.0112617 1 0 +0.36471 0.092865 0.133798 0.00918174 1 0 +0.179094 0.00946046 0.0850337 0.0094677 1 0 +0.199891 0.0155986 0.0842305 0.0120848 1 0 +0.172 0.021775 0.0668766 0.0128311 1 0 +0.161145 0.0142864 0.0849849 0.00957009 1 0 +0.16531 0.0124429 0.0443094 0.0124479 1 0 +0.0225446 0.0474814 0.0663094 0.0105236 1 0 +0.0363874 0.056185 0.0541821 0.00983428 1 0 +0.365501 0.08116 0.123471 0.00644773 1 0 +0.385763 0.0960578 0.128605 0.00781636 1 0 +0.0139599 0.147592 0.164164 0.0119845 1 0 +0.0193222 0.126877 0.155769 0.0110009 1 0 +0.0212865 0.132071 0.173446 0.00752773 1 0 +0.00730761 0.117082 0.165563 0.00740949 1 0 +0.355834 0.0185177 0.191414 0.00859597 1 0 +0.348607 0.0383027 0.189035 0.0111529 1 0 +0.132639 0.191841 0.11779 0.00827679 1 0 +0.131246 0.18865 0.14727 0.0116651 1 0 +0.116957 0.175433 0.141497 0.00846987 1 0 +0.393127 0.106986 0.048055 0.0069068 1 0 +0.386686 0.0899624 0.0516342 0.00902786 1 0 +0.394727 0.112683 0.030698 0.00538644 1 0 +0.387084 0.104472 0.0346597 0.00655158 1 0 +0.345148 0.184789 0.0466062 0.0120454 1 0 +0.356736 0.19091 0.0990343 0.0093597 1 0 +0.127682 0.00939457 0.0200367 0.00940051 1 0 +0.132097 0.0106245 0.0400132 0.0108593 1 0 +0.114843 0.0126578 0.0419875 0.00632081 1 0 +0.116248 0.014684 0.0122303 0.00542048 1 0 +0.170733 0.133505 0.170743 0.00875461 1 0 +0.218872 0.06169 0.160269 0.0125913 1 0 +0.246232 0.0513181 0.190082 0.0100493 1 0 +0.143967 0.127614 0.0582512 0.0136594 1 0 +0.301316 0.0589064 0.0296002 0.0140002 1 0 +0.305269 0.053935 0.0508793 0.00820659 1 0 +0.306264 0.0475953 0.00980291 0.00981875 1 0 +0.0837573 0.0439635 0.0525451 0.00697184 1 0 +0.0834765 0.0514769 0.0414608 0.00642183 1 0 +0.076722 0.0672725 0.0328524 0.0127936 1 0 +0.0658256 0.110332 0.0584587 0.013466 1 0 +0.0942477 0.102218 0.0133943 0.0136546 1 0 +0.225715 0.148708 0.162646 0.00924061 1 0 +0.221432 0.176832 0.188792 0.00785824 1 0 +0.348751 0.0206444 0.0108774 0.0108922 1 0 +0.354084 0.00754641 0.0149245 0.00773943 1 0 +0.16887 0.150891 0.0104275 0.0104412 1 0 +0.184389 0.138255 0.0194275 0.0122844 1 0 +0.170477 0.0770973 0.185021 0.0141745 1 0 +0.179787 0.0643972 0.161671 0.0126068 1 0 +0.178077 0.0813081 0.167517 0.00536716 1 0 +0.171908 0.0530528 0.187151 0.0100066 1 0 +0.0215599 0.00758453 0.0470124 0.00758974 1 0 +0.295725 0.0372936 0.0668884 0.013429 1 0 +0.257832 0.038671 0.0651608 0.00964449 1 0 +0.375459 0.123248 0.09362 0.0112366 1 0 +0.378156 0.157402 0.0583142 0.0146607 1 0 +0.361717 0.112088 0.0437565 0.0134527 1 0 +0.354214 0.0947525 0.0274061 0.0115302 1 0 +0.343699 0.127706 0.046363 0.0105342 1 0 +0.375771 0.093816 0.0209501 0.0109917 1 0 +0.387431 0.179191 0.116619 0.012603 1 0 +0.38242 0.153218 0.1108 0.0133994 1 0 +0.378499 0.187674 0.094636 0.0118575 1 0 +0.231756 0.00691231 0.151275 0.00691899 1 0 +0.241288 0.00968751 0.13274 0.0100394 1 0 +0.224111 0.0114066 0.159899 0.00626691 1 0 +0.0229603 0.185685 0.00743012 0.00747811 1 0 +0.037411 0.185073 0.00917614 0.00771211 1 0 +0.0152978 0.169501 0.00788204 0.0080064 1 0 +0.365584 0.0584976 0.015229 0.0101092 1 0 +0.377674 0.064919 0.0295789 0.00972316 1 0 +0.371345 0.0763962 0.0124227 0.00890194 1 0 +0.00607016 0.113737 0.183234 0.00610793 1 0 +0.0177482 0.106556 0.166576 0.00753866 1 0 +0.0173924 0.101126 0.188401 0.0116203 1 0 +0.320855 0.0376444 0.0116917 0.00734364 1 0 +0.165208 0.137354 0.0720785 0.0134923 1 0 +0.144079 0.135267 0.0312902 0.014367 1 0 +0.165475 0.13543 0.0466744 0.011986 1 0 +0.0745623 0.00883203 0.149173 0.00893237 1 0 +0.0535073 0.0116626 0.173585 0.00612253 1 0 +0.130865 0.0425638 0.145565 0.0127738 1 0 +0.0174239 0.190396 0.172379 0.00967554 1 0 +0.0419233 0.185353 0.185353 0.0108033 1 0 +0.36587 0.143274 0.129383 0.0133985 1 0 +0.38734 0.15923 0.145213 0.0112514 1 0 +0.250941 0.192171 0.0787188 0.00784625 1 0 +0.240426 0.178773 0.0903638 0.0123165 1 0 +0.26236 0.182441 0.0879309 0.0100541 1 0 +0.115378 0.0852737 0.0465421 0.0127064 1 0 +0.135265 0.0258461 0.0546071 0.0104916 1 0 +0.289878 0.0375387 0.0857934 0.00636108 1 0 +0.294878 0.0501245 0.0832888 0.00741146 1 0 +0.296897 0.0125443 0.0631942 0.0116219 1 0 +0.0319177 0.15258 0.137429 0.0111748 1 0 +0.0461303 0.13768 0.146997 0.011531 1 0 +0.0124959 0.159635 0.144886 0.0107932 1 0 +0.121417 0.0316553 0.0691049 0.0103816 1 0 +0.142505 0.0474971 0.0650048 0.0125079 1 0 +0.136531 0.0567854 0.0846523 0.0100307 1 0 +0.00947917 0.146002 0.0490215 0.00949669 1 0 +0.0189339 0.126393 0.0464796 0.0120048 1 0 +0.0138137 0.140396 0.0740045 0.0136698 1 0 +0.211644 0.133981 0.118885 0.00946723 1 0 +0.203399 0.154344 0.1416 0.0103861 1 0 +0.37496 0.0650114 0.17113 0.0110079 1 0 +0.368208 0.0574165 0.187213 0.00801593 1 0 +0.0599565 0.140497 0.16553 0.0117625 1 0 +0.0649543 0.135088 0.147747 0.00748528 1 0 +0.0507607 0.119256 0.166314 0.0113964 1 0 +0.0611788 0.126711 0.156142 0.00496128 1 0 +0.071849 0.165862 0.0484098 0.00783746 1 0 +0.232227 0.15496 0.0381257 0.0051099 1 0 +0.26539 0.153878 0.0308856 0.00655751 1 0 +0.254226 0.148364 0.010579 0.00954279 1 0 +0.0872335 0.0210381 0.0948407 0.0114235 1 0 +0.105355 0.0511996 0.109046 0.0136553 1 0 +0.116522 0.0454841 0.0897557 0.00935573 1 0 +0.1186 0.0229352 0.0978742 0.0144792 1 0 +0.114751 0.0094733 0.178 0.00947644 1 0 +0.124719 0.0129115 0.190021 0.00698671 1 0 +0.307064 0.0130512 0.042066 0.0118309 1 0 +0.31323 0.00918472 0.0581303 0.00580519 1 0 +0.211591 0.0479259 0.138481 0.0141896 1 0 +0.192181 0.0566818 0.146392 0.00852602 1 0 +0.106678 0.190419 0.0861648 0.00958831 1 0 +0.0908359 0.178847 0.108707 0.0130443 1 0 +0.111001 0.188553 0.106954 0.00940378 1 0 +0.0909946 0.179291 0.086244 0.00942362 1 0 +0.134588 0.0840325 0.0312592 0.0118727 1 0 +0.12406 0.109313 0.00937685 0.00950764 1 0 +0.152886 0.0799858 0.0459999 0.0119702 1 0 +0.162111 0.101123 0.0443798 0.0111489 1 0 +0.367244 0.0678647 0.0600582 0.00817559 1 0 +0.379698 0.0622769 0.0475206 0.00852458 1 0 +0.382411 0.0656188 0.0628647 0.00741194 1 0 +0.00530947 0.148284 0.152208 0.00533782 1 0 +0.288341 0.189851 0.092671 0.0101512 1 0 +0.309853 0.189503 0.10137 0.0106376 1 0 +0.302921 0.179146 0.0872126 0.00822421 1 0 +0.296218 0.186488 0.0765203 0.00637589 1 0 +0.12987 0.0269885 0.191541 0.00851021 1 0 +0.155532 0.0360994 0.188404 0.00782845 1 0 +0.0410591 0.123946 0.0199892 0.0120594 1 0 +0.0498383 0.139312 0.0286472 0.00764205 1 0 +0.031696 0.141924 0.030251 0.01066 1 0 +0.0422405 0.140611 0.0091657 0.00922523 1 0 +0.322469 0.0178805 0.146835 0.00563141 1 0 +0.353699 0.0281999 0.149582 0.011231 1 0 +0.393307 0.0473753 0.0129008 0.0067172 1 0 +0.39101 0.0521887 0.0335625 0.00922384 1 0 +0.387535 0.0310431 0.0114009 0.00871386 1 0 +0.352823 0.136375 0.117279 0.00568851 1 0 +0.371019 0.129497 0.146528 0.00919033 1 0 +0.23975 0.193302 0.0389499 0.00670009 1 0 +0.21322 0.183876 0.0473552 0.012344 1 0 +0.290996 0.0840484 0.162816 0.0136415 1 0 +0.325332 0.0739599 0.153491 0.00837755 1 0 +0.216003 0.193165 0.107949 0.00683835 1 0 +0.208824 0.186868 0.0957084 0.0088923 1 0 +0.0847779 0.120989 0.0918488 0.0140475 1 0 +0.345015 0.130383 0.172191 0.0143434 1 0 +0.312653 0.0320914 0.00504478 0.00513512 1 0 +0.160375 0.0435024 0.012158 0.0121629 1 0 +0.167644 0.0655224 0.0186183 0.0113927 1 0 +0.153084 0.0234026 0.0132979 0.00819621 1 0 +0.165403 0.190889 0.0441825 0.00911561 1 0 +0.180966 0.183623 0.0569599 0.011283 1 0 +0.0703895 0.0374811 0.129595 0.0136996 1 0 +0.0388652 0.0422696 0.134182 0.0102187 1 0 +0.392038 0.0743685 0.0348702 0.00826804 1 0 +0.196846 0.143226 0.0628502 0.0136246 1 0 +0.013777 0.012867 0.0881742 0.0129082 1 0 +0.0326336 0.0202219 0.096746 0.00864434 1 0 +0.0196705 0.0323884 0.0967388 0.00913384 1 0 +0.0212244 0.0499256 0.104219 0.00999515 1 0 +0.0168423 0.0335269 0.131134 0.0113103 1 0 +0.36748 0.185207 0.190326 0.00919273 1 0 +0.352104 0.181131 0.189058 0.00676397 1 0 +0.356398 0.190294 0.182063 0.00553682 1 0 +0.189141 0.142776 0.181993 0.0147268 1 0 +0.209487 0.139272 0.155345 0.0109017 1 0 +0.0562426 0.155027 0.00555774 0.005627 1 0 +0.107317 0.0430345 0.132072 0.0108538 1 0 +0.296461 0.0860378 0.187312 0.0126963 1 0 +0.277728 0.103706 0.177365 0.0141811 1 0 +0.299084 0.0636102 0.162844 0.00833912 1 0 +0.385162 0.120121 0.157934 0.0112558 1 0 +0.37038 0.132965 0.168166 0.0108392 1 0 +0.385632 0.140025 0.158284 0.00865763 1 0 +0.0482981 0.19129 0.0831739 0.00916622 1 0 +0.0612524 0.15544 0.0821634 0.0139729 1 0 +0.0367202 0.15469 0.0757918 0.0113843 1 0 +0.161624 0.0198379 0.103776 0.0100296 1 0 +0.15685 0.0402778 0.0923543 0.0138665 1 0 +0.15098 0.0378477 0.137264 0.00949224 1 0 +0.149986 0.0504614 0.149848 0.0083531 1 0 +0.169736 0.0424577 0.135879 0.00987065 1 0 +0.345062 0.0342228 0.0782216 0.00975469 1 0 +0.33098 0.0194967 0.0588677 0.00986929 1 0 +0.327716 0.0304708 0.0735557 0.00859503 1 0 +0.246079 0.154124 0.191715 0.00829122 1 0 +0.257018 0.141833 0.182154 0.0109571 1 0 +0.0909553 0.0358688 0.131503 0.00701734 1 0 +0.0721281 0.060405 0.134812 0.00987482 1 0 +0.129253 0.179824 0.102862 0.0112379 1 0 +0.164382 0.176216 0.10136 0.0137664 1 0 +0.140089 0.149112 0.0760627 0.0145271 1 0 +0.140634 0.172744 0.0614462 0.013265 1 0 +0.180291 0.151694 0.0809075 0.00911506 1 0 +0.386181 0.0416112 0.0904011 0.00841162 1 0 +0.0516717 0.0525541 0.184848 0.0140982 1 0 +0.0722764 0.0624285 0.179007 0.00948518 1 0 +0.0348474 0.0734227 0.181379 0.0129312 1 0 +0.0506055 0.100507 0.0440875 0.00965794 1 0 +0.0706543 0.0913602 0.0671509 0.00795337 1 0 +0.0816734 0.117759 0.0433435 0.00965938 1 0 +0.0483573 0.188757 0.112117 0.0111442 1 0 +0.00970976 0.0470906 0.0128571 0.00973229 1 0 +0.0123456 0.0550317 0.0325287 0.00990198 1 0 +0.00980577 0.0280851 0.0111555 0.00741917 1 0 +0.2282 0.155122 0.0560451 0.00882879 1 0 +0.213943 0.16944 0.0663647 0.00676659 1 0 +0.232681 0.136413 0.112038 0.0120468 1 0 +0.243686 0.114589 0.120711 0.0138871 1 0 +0.0564921 0.0924674 0.105065 0.0122932 1 0 +0.0498239 0.190957 0.0181107 0.00905355 1 0 +0.238506 0.150955 0.00679354 0.00687541 1 0 +0.106056 0.00804251 0.0861211 0.0080478 1 0 +0.0994817 0.0132035 0.11266 0.0115751 1 0 +0.142361 0.143467 0.102546 0.0126466 1 0 +0.346896 0.0103949 0.0502715 0.0104187 1 0 +0.337578 0.00615697 0.0647539 0.00632648 1 0 +0.365415 0.0149719 0.0720151 0.008988 1 0 +0.0131234 0.045257 0.15088 0.0119557 1 0 +0.0127507 0.0623101 0.141517 0.00750223 1 0 +0.304444 0.185815 0.0513571 0.0136452 1 0 +0.281936 0.176296 0.0604795 0.0124403 1 0 +0.179508 0.0471838 0.102471 0.0118902 1 0 +0.178001 0.0538737 0.0770514 0.0144384 1 0 +0.187521 0.0410831 0.0886018 0.00525026 1 0 +0.174998 0.0847604 0.0147851 0.00955679 1 0 +0.290064 0.084341 0.0344495 0.0142315 1 0 +0.290002 0.0844431 0.0611749 0.0124942 1 0 +0.376134 0.0463674 0.191537 0.00625324 1 0 +0.0513784 0.191572 0.152027 0.00853012 1 0 +0.0678098 0.176773 0.12328 0.0142848 1 0 +0.344998 0.153703 0.184615 0.0120791 1 0 +0.362316 0.140492 0.185516 0.00972086 1 0 +0.18804 0.169771 0.146387 0.0119031 1 0 +0.308172 0.180009 0.1575 0.00797814 1 0 +0.308458 0.182891 0.125762 0.0146732 1 0 +0.323046 0.0647415 0.0612375 0.0101253 1 0 +0.314683 0.0935818 0.0994618 0.0142316 1 0 +0.30898 0.0979252 0.0495522 0.0135253 1 0 +0.347456 0.0862242 0.0563558 0.0124924 1 0 +0.139757 0.0240381 0.124471 0.0124244 1 0 +0.119083 0.0265038 0.135206 0.00967716 1 0 +0.131047 0.00950555 0.13885 0.00995462 1 0 +0.390467 0.0463886 0.154038 0.00872832 1 0 +0.379006 0.0613228 0.150385 0.0104481 1 0 +0.382075 0.0448445 0.00657943 0.00658226 1 0 +0.22257 0.14715 0.140927 0.0101008 1 0 +0.283228 0.192355 0.18211 0.00772919 1 0 +0.251721 0.184881 0.157569 0.00671726 1 0 +0.272656 0.0531788 0.149365 0.0076595 1 0 +0.209222 0.0940953 0.0136833 0.00589358 1 0 +0.199062 0.0655167 0.031442 0.0115316 1 0 +0.193577 0.0745342 0.0173387 0.00608358 1 0 +0.132108 0.179284 0.00894548 0.0089664 1 0 +0.139169 0.152455 0.0124863 0.0115774 1 0 +0.226438 0.0840168 0.00791548 0.00792328 1 0 +0.231146 0.0678308 0.0205716 0.0117466 1 0 +0.242263 0.0852114 0.0147031 0.00970349 1 0 +0.151372 0.0795723 0.192909 0.00712179 1 0 +0.0541621 0.009468 0.0191585 0.00949259 1 0 +0.0663788 0.0137363 0.0309331 0.00688294 1 0 +0.0387704 0.0148417 0.0112337 0.00865432 1 0 +0.353335 0.0998197 0.0757791 0.0119344 1 0 +0.362165 0.119893 0.187596 0.010984 1 0 +0.0728923 0.0772205 0.190884 0.00967805 1 0 +0.288715 0.00701357 0.0926238 0.00701559 1 0 +0.0506495 0.0549668 0.0138225 0.0138284 1 0 +0.0708841 0.0519034 0.0203875 0.00783806 1 0 +0.0365921 0.0763735 0.0199077 0.0125531 1 0 +0.365386 0.0868869 0.0713017 0.00630041 1 0 +0.054346 0.0732162 0.0601268 0.0105279 1 0 +0.05443 0.0886831 0.0309235 0.00844493 1 0 +0.057257 0.0690964 0.0416356 0.00863903 1 0 +0.364393 0.0328048 0.0626499 0.0111804 1 0 +0.262206 0.12561 0.144205 0.0136869 1 0 +0.0690483 0.193069 0.111205 0.00699695 1 0 +0.0517508 0.17195 0.111857 0.00600386 1 0 +0.0317408 0.00808948 0.183343 0.00727501 1 0 +0.0448126 0.132295 0.0726416 0.012636 1 0 +0.0105125 0.0861309 0.00983185 0.0100961 1 0 +0.0124964 0.104975 0.0414812 0.0109114 1 0 +0.00883058 0.0853637 0.0392147 0.00906131 1 0 +0.0142786 0.1082 0.011511 0.0115812 1 0 +0.0377934 0.0960012 0.182614 0.00987213 1 0 +0.0166288 0.068417 0.167964 0.0102407 1 0 +0.306593 0.0605586 0.0743283 0.0106548 1 0 +0.0219073 0.0146401 0.00836378 0.00841415 1 0 +0.00810007 0.0434991 0.0724525 0.00811193 1 0 +0.0221052 0.0452536 0.0887114 0.00622461 1 0 +0.0164182 0.0888751 0.117519 0.0081728 1 0 +0.0358909 0.0913123 0.129594 0.00726122 1 0 +0.0149256 0.101493 0.132387 0.0113841 1 0 +0.0064834 0.0180904 0.106134 0.00649126 1 0 +0.327023 0.0779322 0.11252 0.00959491 1 0 +0.0792518 0.136865 0.0706925 0.0129743 1 0 +0.0720758 0.152592 0.0579455 0.00850425 1 0 +0.0861308 0.159992 0.0801141 0.0114014 1 0 +0.103346 0.171842 0.0714987 0.0112036 1 0 +0.3503 0.125144 0.0244213 0.0125217 1 0 +0.154271 0.00658848 0.154326 0.00659142 1 0 +0.145609 0.0129631 0.162101 0.00672077 1 0 +0.105485 0.124246 0.0203198 0.0118756 1 0 +0.385248 0.0794694 0.158897 0.0105453 1 0 +0.372667 0.0773341 0.146586 0.0071862 1 0 +0.343365 0.0431501 0.146365 0.00722527 1 0 +0.063767 0.142112 0.0487261 0.00773984 1 0 +0.0938688 0.184927 0.0594656 0.00894189 1 0 +0.152705 0.0619648 0.0546286 0.00801086 1 0 +0.39375 0.146995 0.0492081 0.00626239 1 0 +0.383045 0.130686 0.0581556 0.0124989 1 0 +0.167215 0.0563811 0.117252 0.00942078 1 0 +0.171844 0.0640991 0.131713 0.0076115 1 0 +0.152281 0.0794524 0.0082892 0.00831509 1 0 +0.175193 0.0796253 0.0358228 0.0120995 1 0 +0.140635 0.090329 0.0145581 0.00697231 1 0 +0.286716 0.16251 0.0756719 0.00862372 1 0 +0.248275 0.0833534 0.144199 0.0122316 1 0 +0.126338 0.0206836 0.00730592 0.00730955 1 0 +0.0404216 0.100268 0.0163574 0.0119049 1 0 +0.0378094 0.0911662 0.0329799 0.00722532 1 0 +0.235829 0.0443444 0.0110074 0.0110097 1 0 +0.248735 0.0314925 0.0141296 0.00612448 1 0 +0.208742 0.178494 0.0101585 0.0101662 1 0 +0.196251 0.189903 0.0181036 0.0101298 1 0 +0.0887479 0.0103276 0.0365731 0.0103767 1 0 +0.0975155 0.00563253 0.0489598 0.0056578 1 0 +0.0158431 0.106378 0.0939756 0.0136557 1 0 +0.0502105 0.113761 0.109082 0.0102676 1 0 +0.0436462 0.104431 0.0946859 0.00809977 1 0 +0.202061 0.187687 0.0791138 0.00904632 1 0 +0.201503 0.0132524 0.0433349 0.0106019 1 0 +0.191637 0.0115332 0.178539 0.0115424 1 0 +0.204607 0.0138582 0.167907 0.00543025 1 0 +0.194779 0.0189619 0.192232 0.00777998 1 0 +0.168729 0.189848 0.0818483 0.010209 1 0 +0.129854 0.190782 0.0210644 0.00923197 1 0 +0.26258 0.110366 0.161066 0.00904576 1 0 +0.262803 0.106419 0.146864 0.00569588 1 0 +0.36782 0.188057 0.0582292 0.0121309 1 0 +0.363468 0.18552 0.0857521 0.00573485 1 0 +0.355917 0.173687 0.0582736 0.00732894 1 0 +0.14095 0.00809392 0.111729 0.00810161 1 0 +0.139403 0.0520251 0.158761 0.00557152 1 0 +0.391256 0.0859491 0.00909444 0.00878231 1 0 +0.388958 0.10046 0.0129862 0.00578511 1 0 +0.229216 0.108846 0.136684 0.00841708 1 0 +0.252786 0.0911588 0.168619 0.0137994 1 0 +0.0620011 0.109176 0.0885769 0.0118184 1 0 +0.341706 0.111231 0.188779 0.0112243 1 0 +0.035336 0.135001 0.185697 0.0113418 1 0 +0.0334853 0.114705 0.182273 0.00932439 1 0 +0.0245974 0.185509 0.191853 0.00818761 1 0 +0.204741 0.0577915 0.0675464 0.0142103 1 0 +0.206424 0.179996 0.192712 0.00729287 1 0 +0.370418 0.00789946 0.0151283 0.00793645 1 0 +0.297334 0.10541 0.0705256 0.0116052 1 0 +0.289761 0.133647 0.0548894 0.0134367 1 0 +0.26159 0.111988 0.191384 0.00862492 1 0 +0.378576 0.0868718 0.00681008 0.00500929 1 0 +0.0298629 0.0738766 0.05118 0.00925954 1 0 +0.197723 0.039046 0.0100464 0.01024 1 0 +0.273033 0.0212788 0.192444 0.00756068 1 0 +0.271806 0.0218552 0.0091431 0.00915288 1 0 +0.282277 0.0111852 0.0121297 0.00665603 1 0 +0.0912619 0.1637 0.00873324 0.00896896 1 0 +0.187463 0.109838 0.010696 0.0107031 1 0 +0.211959 0.150836 0.0463463 0.0100119 1 0 +0.276616 0.00726373 0.0208333 0.00726634 1 0 +0.269059 0.00801338 0.0365432 0.00799643 1 0 +0.350892 0.112417 0.0889808 0.00647615 1 0 +0.382318 0.104857 0.167515 0.00698831 1 0 +0.391578 0.112555 0.1718 0.00579373 1 0 +0.0383565 0.118252 0.13661 0.0118309 1 0 +0.347168 0.106342 0.0103183 0.0103355 1 0 +0.0531425 0.147131 0.189814 0.0101934 1 0 +0.0600396 0.165464 0.190941 0.00934447 1 0 +0.154216 0.190864 0.116273 0.00922074 1 0 +0.179424 0.193204 0.0949906 0.00683883 1 0 +0.212951 0.124659 0.145298 0.00716684 1 0 +0.204603 0.0185068 0.00721977 0.0072496 1 0 +0.188898 0.0204287 0.0133955 0.00869768 1 0 +0.390733 0.0440608 0.0726776 0.00928055 1 0 +0.375987 0.0444276 0.0718798 0.00765274 1 0 +0.369288 0.114453 0.0681617 0.0110147 1 0 +0.0346381 0.104006 0.194264 0.0058219 1 0 +0.276533 0.149359 0.0667189 0.0102655 1 0 +0.215694 0.192892 0.0133808 0.00725409 1 0 +0.240383 0.0400749 0.0586261 0.00904036 1 0 +0.0898222 0.0384868 0.00728664 0.00748467 1 0 +0.0845856 0.0737115 0.0110843 0.0113386 1 0 +0.315825 0.189883 0.0295578 0.0104679 1 0 +0.37166 0.193887 0.0984797 0.00613497 1 0 +0.0122528 0.0828442 0.152275 0.0115181 1 0 +0.0158326 0.0667114 0.152824 0.00501628 1 0 +0.227332 0.0458339 0.193588 0.00644537 1 0 +0.0277894 0.19127 0.0808528 0.00878933 1 0 +0.392151 0.0865789 0.141861 0.00786235 1 0 +0.168763 0.151455 0.190798 0.0092146 1 0 +0.204381 0.00966975 0.0216306 0.00967165 1 0 +0.0243094 0.150481 0.0968196 0.013393 1 0 +0.0336358 0.0203736 0.0661439 0.00692063 1 0 +0.361559 0.0637907 0.145559 0.00702253 1 0 +0.0619236 0.074084 0.16276 0.00694743 1 0 +0.0382627 0.160366 0.0558773 0.00562937 1 0 +0.0275376 0.0718734 0.0836242 0.00512013 1 0 +0.228784 0.0149795 0.112927 0.0135174 1 0 +0.395161 0.0898583 0.106136 0.00496068 1 0 +0.25985 0.18758 0.0639603 0.00819434 1 0 +0.0299818 0.0347722 0.183721 0.0061519 1 0 +0.21549 0.186948 0.122246 0.00749354 1 0 +0.361217 0.177099 0.150076 0.0109281 1 0 +0.067308 0.149657 0.0302269 0.00524767 1 0 +0.0991941 0.0200913 0.0677805 0.00556428 1 0 +0.367458 0.0270138 0.0999109 0.00656395 1 0 +0.382911 0.0385284 0.126089 0.00554315 1 0 +0.15259 0.102064 0.170509 0.0103504 1 0 +0.204226 0.159285 0.105587 0.00643247 1 0 +0.106211 0.0611613 0.0650592 0.00502375 1 0 +0.200173 0.191619 0.170332 0.00501194 1 0 +0.271079 0.0587953 0.0365451 0.00707767 1 0 +0.311446 0.0565969 0.10969 0.0084572 1 0 +0.375804 0.0736045 0.0865151 0.00564715 1 0 +0.374788 0.00974941 0.169654 0.00494595 1 0 +0.307617 0.0594108 0.141521 0.0052979 1 0 +0.282294 0.0255418 0.127101 0.00550773 1 0 +0.270122 0.0111254 0.154309 0.0114169 1 0 +0.278578 0.161938 0.192902 0.00495567 1 0 +0.185316 0.109793 0.154663 0.00911126 1 0 +0.293049 0.024156 0.145083 0.00759668 1 0 +0.0265287 0.096412 0.0693108 0.00547116 1 0 +0.241683 0.0191683 0.0860944 0.0082177 1 0 +0.368118 0.0918938 0.184249 0.00616362 1 0 +0.0420928 0.0663928 0.129828 0.00604567 1 0 +0.272006 0.172145 0.0138445 0.00574495 1 0 +0.358694 0.104248 0.124013 0.00698951 1 0 +0.172924 0.0295833 0.0470859 0.00846439 1 0 +0.0390121 0.0491533 0.0682909 0.00614668 1 0 +0.375885 0.0784083 0.117374 0.00590407 1 0 +0.129273 0.172145 0.145903 0.00501788 1 0 +0.391118 0.093988 0.0385701 0.00534297 1 0 +0.105125 0.00750422 0.0121776 0.0078184 1 0 +0.245577 0.0643043 0.163627 0.0144505 1 0 +0.133484 0.113119 0.0700113 0.00774841 1 0 +0.292684 0.0677731 0.0443244 0.00523317 1 0 +0.0766266 0.0592614 0.0541753 0.00998487 1 0 +0.0920868 0.096747 0.0403876 0.0139767 1 0 +0.355631 0.0369208 0.00910917 0.00678582 1 0 +0.187383 0.152005 0.00844206 0.00556809 1 0 +0.18167 0.0620785 0.179408 0.00537936 1 0 +0.0159403 0.00601645 0.0343362 0.00609946 1 0 +0.273759 0.040202 0.0598429 0.00721707 1 0 +0.383771 0.152815 0.084718 0.0127205 1 0 +0.365547 0.1106 0.0219998 0.00868864 1 0 +0.382057 0.169898 0.0993032 0.0068623 1 0 +0.381343 0.0643481 0.0134882 0.00679037 1 0 +0.0639149 0.00754483 0.168317 0.00764772 1 0 +0.0255336 0.192066 0.158187 0.00553389 1 0 +0.373082 0.165629 0.128448 0.0101101 1 0 +0.252547 0.191844 0.100086 0.00835748 1 0 +0.0965972 0.0845669 0.0638608 0.0128504 1 0 +0.14744 0.0175978 0.0433787 0.00617132 1 0 +0.284139 0.0201847 0.081076 0.0116354 1 0 +0.0265165 0.16914 0.138529 0.00627887 1 0 +0.137475 0.0306754 0.0699326 0.00572817 1 0 +0.0227494 0.144802 0.0482607 0.00688008 1 0 +0.379247 0.0677962 0.188607 0.00720059 1 0 +0.261495 0.142099 0.0245236 0.00738447 1 0 +0.106169 0.0378184 0.09608 0.00499535 1 0 +0.310725 0.0197461 0.0571708 0.00509167 1 0 +0.19151 0.0413327 0.142497 0.00732389 1 0 +0.139872 0.105686 0.0266184 0.0108939 1 0 +0.373959 0.0564584 0.0614421 0.00513291 1 0 +0.146353 0.0271243 0.18695 0.00509141 1 0 +0.0298808 0.136673 0.014959 0.0056102 1 0 +0.340704 0.0146383 0.141235 0.00932259 1 0 +0.366728 0.120761 0.130942 0.00918468 1 0 +0.221367 0.186752 0.102372 0.00531139 1 0 +0.0996498 0.0980634 0.092229 0.0132821 1 0 +0.170107 0.0277319 0.0112926 0.00509826 1 0 +0.168886 0.177023 0.0442298 0.0074665 1 0 +0.0497445 0.0241692 0.132148 0.0109974 1 0 +0.389449 0.0608805 0.0214952 0.00511992 1 0 +0.188863 0.129071 0.0421877 0.012663 1 0 +0.168 0.0388015 0.189779 0.00500258 1 0 +0.0361613 0.0111184 0.0808431 0.0100163 1 0 +0.0330297 0.027338 0.131563 0.00602515 1 0 +0.380836 0.174758 0.191983 0.00805852 1 0 +0.191985 0.140342 0.160274 0.0073124 1 0 +0.0475619 0.155101 0.0135935 0.00636649 1 0 +0.089078 0.0771357 0.130195 0.0143848 1 0 +0.386624 0.131859 0.169841 0.00552843 1 0 +0.163404 0.0272597 0.139457 0.00697779 1 0 +0.317549 0.0334651 0.0588785 0.00950924 1 0 +0.251437 0.157781 0.182183 0.00593932 1 0 +0.0862738 0.0444398 0.144012 0.00885247 1 0 +0.144082 0.165439 0.105567 0.0095987 1 0 +0.162451 0.163006 0.0706732 0.0123462 1 0 +0.377191 0.0348654 0.0993471 0.00595367 1 0 +0.0292483 0.0558257 0.19181 0.00845751 1 0 +0.0731428 0.104323 0.0409557 0.00643389 1 0 +0.0198718 0.17265 0.11226 0.0139851 1 0 +0.236318 0.163423 0.0677451 0.00765444 1 0 +0.229331 0.119425 0.108368 0.00565285 1 0 +0.0618705 0.108585 0.148216 0.00855224 1 0 +0.0472707 0.190341 0.0339418 0.00579053 1 0 +0.252217 0.157224 0.00623357 0.00624238 1 0 +0.0962091 0.0159385 0.081255 0.00563935 1 0 +0.00817874 0.0253714 0.146558 0.0083663 1 0 +0.291538 0.171609 0.0445923 0.00670552 1 0 +0.192874 0.0346911 0.0760032 0.00985719 1 0 +0.186361 0.103634 0.0473216 0.0134073 1 0 +0.0741433 0.188066 0.14553 0.0114578 1 0 +0.337023 0.158487 0.194499 0.00550687 1 0 +0.314179 0.0840405 0.072824 0.014068 1 0 +0.133116 0.0246177 0.141414 0.00578305 1 0 +0.394011 0.0446063 0.149989 0.00599448 1 0 +0.37493 0.0385825 0.0159213 0.0066536 1 0 +0.22654 0.143138 0.126695 0.00520927 1 0 +0.268222 0.192866 0.175692 0.00715241 1 0 +0.283563 0.0645575 0.16196 0.00723602 1 0 +0.212596 0.0829564 0.0372903 0.0113053 1 0 +0.123976 0.161412 0.0117908 0.00607324 1 0 +0.162715 0.0962505 0.18281 0.00660952 1 0 +0.0501585 0.00739969 0.0403256 0.00761423 1 0 +0.338479 0.102953 0.0632961 0.00772136 1 0 +0.210651 0.0578669 0.112459 0.0136824 1 0 +0.0662949 0.0647975 0.151469 0.0083119 1 0 +0.289129 0.0209543 0.0975938 0.00563666 1 0 +0.031921 0.0468844 0.0111795 0.00631896 1 0 +0.374995 0.0812476 0.0630345 0.00757385 1 0 +0.240684 0.182445 0.153056 0.00545292 1 0 +0.052213 0.0903392 0.0549818 0.00533031 1 0 +0.371036 0.0137309 0.0852819 0.00547397 1 0 +0.286823 0.129083 0.131978 0.0140184 1 0 +0.063446 0.182761 0.102616 0.0076676 1 0 +0.020394 0.00826111 0.173178 0.00829914 1 0 +0.0312729 0.139892 0.0824798 0.00574432 1 0 +0.00863994 0.083511 0.0249144 0.00519484 1 0 +0.0488243 0.0851716 0.190139 0.00732035 1 0 +0.021921 0.0320327 0.0063651 0.00619372 1 0 +0.0320102 0.0409486 0.0967865 0.0058844 1 0 +0.319167 0.0835592 0.143631 0.00670174 1 0 +0.0782541 0.146147 0.0872911 0.00606958 1 0 +0.225196 0.0554171 0.125535 0.00602916 1 0 +0.128068 0.159785 0.0950072 0.0103182 1 0 +0.335086 0.144369 0.0208107 0.0122591 1 0 +0.309413 0.0449647 0.0811385 0.00659358 1 0 +0.139213 0.00758794 0.153068 0.00558374 1 0 +0.112588 0.0974011 0.00905504 0.00553724 1 0 +0.354749 0.0447492 0.15183 0.00550329 1 0 +0.0586951 0.146078 0.0365589 0.00602574 1 0 +0.250268 0.0762195 0.0341256 0.0131473 1 0 +0.0961069 0.193018 0.0469953 0.00699127 1 0 +0.144597 0.0535251 0.0484729 0.00521266 1 0 +0.383755 0.143112 0.0443475 0.00609051 1 0 +0.177372 0.0558999 0.142282 0.0068624 1 0 +0.149436 0.0717535 0.0217132 0.00809978 1 0 +0.0869359 0.175095 0.0724519 0.00555306 1 0 +0.279529 0.172596 0.089291 0.00978439 1 0 +0.250355 0.0597187 0.137615 0.0123911 1 0 +0.119589 0.0328563 0.0105656 0.00746041 1 0 +0.133067 0.163118 0.0460139 0.006435 1 0 +0.230063 0.025654 0.00788834 0.00633899 1 0 +0.0355784 0.114217 0.101586 0.00617914 1 0 +0.217434 0.184185 0.0825201 0.00708441 1 0 +0.335162 0.0848649 0.0984859 0.0080474 1 0 +0.113069 0.114581 0.144575 0.0147412 1 0 +0.174078 0.166055 0.0859442 0.00708755 1 0 +0.274922 0.125237 0.164978 0.0106713 1 0 +0.346199 0.184401 0.0697786 0.0111541 1 0 +0.142237 0.0228484 0.102594 0.00962491 1 0 +0.14345 0.0678292 0.0349685 0.00696443 1 0 +0.394852 0.0819298 0.0230254 0.00518911 1 0 +0.232323 0.0944074 0.142046 0.0072955 1 0 +0.044562 0.116355 0.0848029 0.00741468 1 0 +0.353889 0.109415 0.17346 0.00846082 1 0 +0.0355702 0.125228 0.171647 0.00577467 1 0 +0.0219492 0.169303 0.194138 0.00592133 1 0 +0.189144 0.0431386 0.0602557 0.00839812 1 0 +0.210267 0.167803 0.187758 0.00653748 1 0 +0.371833 0.0125051 0.0272674 0.00523647 1 0 +0.235904 0.0268693 0.129898 0.00839309 1 0 +0.00800912 0.0936409 0.106956 0.00614546 1 0 +0.152239 0.0664404 0.148093 0.00787896 1 0 +0.0727944 0.116542 0.0759453 0.00635603 1 0 +0.256759 0.110831 0.177046 0.00796748 1 0 +0.0171044 0.142267 0.0374209 0.00560163 1 0 +0.388409 0.15406 0.161688 0.00604898 1 0 +0.212628 0.0445981 0.0184521 0.00840461 1 0 +0.287804 0.0212483 0.00898011 0.00524894 1 0 +0.103648 0.142195 0.00915061 0.00918398 1 0 +0.0249615 0.0944006 0.00999414 0.00581314 1 0 +0.17413 0.101717 0.0223372 0.0090261 1 0 +0.221216 0.134036 0.0650604 0.0125142 1 0 +0.204726 0.070397 0.144764 0.0101323 1 0 +0.283783 0.00667192 0.0360334 0.00686949 1 0 +0.307041 0.163873 0.140303 0.00930808 1 0 +0.372003 0.107226 0.0994053 0.00614495 1 0 +0.0436736 0.10863 0.12283 0.00579659 1 0 +0.34477 0.108323 0.0249005 0.00519196 1 0 +0.0377104 0.15348 0.184693 0.00731607 1 0 +0.121077 0.183774 0.11724 0.0057677 1 0 +0.185975 0.191251 0.0741351 0.00816562 1 0 +0.229029 0.132523 0.15296 0.00991017 1 0 +0.190654 0.0233533 0.00723081 0.00727075 1 0 +0.36508 0.126326 0.0566512 0.0060488 1 0 +0.118158 0.188182 0.158422 0.00502799 1 0 +0.0271665 0.103701 0.1749 0.00535086 1 0 +0.279837 0.149124 0.0515643 0.00524692 1 0 +0.207288 0.192364 0.0321903 0.00772377 1 0 +0.23098 0.0647793 0.0732639 0.0135387 1 0 +0.214176 0.0155511 0.163247 0.00500672 1 0 +0.0209887 0.072175 0.0348169 0.00943272 1 0 +0.0305213 0.134868 0.138194 0.00660834 1 0 +0.0777922 0.0430145 0.0142222 0.0049973 1 0 +0.250382 0.186598 0.0369264 0.00617653 1 0 +0.369015 0.0664313 0.1323 0.00841746 1 0 +0.163825 0.0656479 0.0972373 0.0128942 1 0 +0.0139643 0.101756 0.151244 0.00749925 1 0 +0.105723 0.0334557 0.118016 0.00623026 1 0 +0.0242189 0.0717604 0.155129 0.00504056 1 0 +0.244405 0.0437342 0.178231 0.00539913 1 0 +0.0868532 0.0275435 0.013115 0.00530271 1 0 +0.238417 0.118837 0.138676 0.00530997 1 0 +0.248809 0.0411882 0.0203365 0.00538804 1 0 +0.271743 0.164873 0.0723046 0.00690452 1 0 +0.259549 0.0164941 0.0393869 0.00505907 1 0 +0.0336293 0.184633 0.106557 0.00512952 1 0 +0.0961337 0.069566 0.0259228 0.00794518 1 0 +0.261421 0.0946635 0.0449094 0.0109537 1 0 +0.16721 0.147574 0.178979 0.0079243 1 0 +0.162571 0.0175805 0.15751 0.00737101 1 0 +0.0681521 0.156908 0.15673 0.00858295 1 0 +0.371311 0.188827 0.17645 0.0056506 1 0 +0.382051 0.0326958 0.0660182 0.00679611 1 0 +0.370883 0.097932 0.0722141 0.00607098 1 0 +0.189375 0.0100781 0.0223039 0.00496697 1 0 +0.293977 0.128725 0.155733 0.0107931 1 0 +0.294931 0.106278 0.116439 0.0147434 1 0 +0.319684 0.118009 0.126407 0.0144058 1 0 +0.333114 0.100453 0.142545 0.0129627 1 0 +0.233056 0.0821008 0.185284 0.0135691 1 0 +0.222491 0.107745 0.186574 0.0136897 1 0 +0.205985 0.0856081 0.180974 0.0140664 1 0 +0.209378 0.0980333 0.160304 0.0102888 1 0 +0.0183493 0.171206 0.088728 0.00963997 1 0 +0.0138642 0.130553 0.0946587 0.0092099 1 0 +0.00880729 0.14103 0.109349 0.00910086 1 0 +0.0455904 0.15159 0.109242 0.0112735 1 0 +0.0248175 0.0292809 0.0651985 0.00564902 1 0 +0.051736 0.0417432 0.0745267 0.00867866 1 0 +0.34206 0.0568584 0.130516 0.00768926 1 0 +0.351697 0.0751907 0.157835 0.00494477 1 0 +0.0670896 0.0864491 0.169068 0.00786377 1 0 +0.0512748 0.0963263 0.151133 0.0056313 1 0 +0.0507137 0.0645705 0.163841 0.00779486 1 0 +0.0223355 0.190893 0.0607357 0.00673162 1 0 +0.0467381 0.191243 0.0507055 0.00669401 1 0 +0.299576 0.14876 0.0144156 0.008239 1 0 +0.323826 0.152206 0.0430153 0.00768271 1 0 +0.00617829 0.0719664 0.0851172 0.00587751 1 0 +0.00679986 0.0963776 0.0785689 0.00696181 1 0 +0.0420936 0.072806 0.0881394 0.0101486 1 0 +0.392841 0.0130103 0.0241736 0.00733186 1 0 +0.392432 0.0186516 0.0607261 0.00636687 1 0 +0.374975 0.0243043 0.043706 0.00560077 1 0 +0.0871383 0.0936887 0.187415 0.0112409 1 0 +0.142323 0.107156 0.187424 0.0100812 1 0 +0.145518 0.0811453 0.142775 0.0091413 1 0 +0.215674 0.0140729 0.108765 0.0140781 1 0 +0.222644 0.016739 0.0890476 0.011201 1 0 +0.249043 0.0290925 0.103992 0.0127402 1 0 +0.393134 0.1016 0.0826434 0.00716053 1 0 +0.366581 0.0771724 0.102847 0.0105666 1 0 +0.22542 0.187068 0.0617098 0.00676333 1 0 +0.245887 0.16773 0.0766569 0.00611283 1 0 +0.0157395 0.037729 0.189653 0.0105725 1 0 +0.0536306 0.0208592 0.191866 0.00717096 1 0 +0.214248 0.187878 0.137434 0.00777375 1 0 +0.231408 0.167212 0.109112 0.00631698 1 0 +0.370209 0.186792 0.131924 0.0115277 1 0 +0.348265 0.188219 0.175274 0.00525853 1 0 +0.357703 0.16075 0.122765 0.00699528 1 0 +0.348719 0.169077 0.156321 0.00518334 1 0 +0.0777636 0.163008 0.0142156 0.00567678 1 0 +0.0703331 0.140071 0.0105286 0.00591555 1 0 +0.107597 0.0123506 0.0706847 0.00622383 1 0 +0.0858999 0.00550219 0.062506 0.00576121 1 0 +0.369151 0.00818008 0.0940423 0.00506689 1 0 +0.380448 0.0115654 0.123048 0.0101947 1 0 +0.361218 0.00529068 0.0991335 0.00545717 1 0 +0.21127 0.107896 0.037595 0.0136718 1 0 +0.19285 0.098723 0.0741582 0.0146363 1 0 +0.208754 0.0675954 0.0481468 0.00789315 1 0 +0.190332 0.0714759 0.0516261 0.0112526 1 0 +0.386695 0.0279839 0.124866 0.0057263 1 0 +0.391511 0.0471853 0.123585 0.00691367 1 0 +0.362912 0.0409177 0.12372 0.00493753 1 0 +0.0819763 0.098613 0.165435 0.0117008 1 0 +0.0732932 0.088342 0.117281 0.00888556 1 0 +0.0762444 0.12042 0.121165 0.00973858 1 0 +0.109151 0.0843392 0.112748 0.0131693 1 0 +0.157145 0.101235 0.154726 0.00609758 1 0 +0.156414 0.0947529 0.141875 0.00831439 1 0 +0.141156 0.0819152 0.157509 0.0062447 1 0 +0.311691 0.0225125 0.189756 0.0105256 1 0 +0.277398 0.0425153 0.191083 0.00918601 1 0 +0.31954 0.0655847 0.185438 0.0122109 1 0 +0.199689 0.169705 0.103668 0.00509297 1 0 +0.202364 0.140992 0.101095 0.011787 1 0 +0.222034 0.150856 0.0986253 0.010356 1 0 +0.195383 0.149282 0.0816558 0.00618624 1 0 +0.234882 0.00824154 0.0210247 0.00850376 1 0 +0.227894 0.00967124 0.0485354 0.00981243 1 0 +0.119952 0.0603646 0.0313592 0.0116434 1 0 +0.0929176 0.0649489 0.0678022 0.00749513 1 0 +0.119362 0.0624182 0.0864577 0.00812881 1 0 +0.103313 0.0438392 0.0487169 0.00568792 1 0 +0.189872 0.191278 0.190833 0.00620737 1 0 +0.218556 0.191504 0.183794 0.0088616 1 0 +0.256878 0.0572496 0.0376613 0.00725023 1 0 +0.272126 0.0751648 0.0726282 0.0106756 1 0 +0.251638 0.0481765 0.0465315 0.00647808 1 0 +0.226605 0.183384 0.0126504 0.0076126 1 0 +0.22612 0.155581 0.0104955 0.00612659 1 0 +0.225868 0.00918519 0.182376 0.00962794 1 0 +0.217444 0.0456945 0.187116 0.00535896 1 0 +0.216067 0.126527 0.0469773 0.00773177 1 0 +0.225386 0.109266 0.0975421 0.00970786 1 0 +0.243769 0.106338 0.0993678 0.00899614 1 0 +0.227952 0.126064 0.0859668 0.0108525 1 0 +0.316443 0.042169 0.113368 0.00724812 1 0 +0.326654 0.0517949 0.108777 0.00751662 1 0 +0.301954 0.0583803 0.0999651 0.00524896 1 0 +0.29274 0.0347683 0.132402 0.0060409 1 0 +0.343566 0.0597269 0.018211 0.005781 1 0 +0.352622 0.0502169 0.0402065 0.00618205 1 0 +0.369643 0.0714826 0.0475044 0.00510766 1 0 +0.394229 0.0737736 0.0944807 0.0059695 1 0 +0.394376 0.0620919 0.0670473 0.00591046 1 0 +0.394948 0.0939468 0.076593 0.00516962 1 0 +0.367544 0.0646222 0.0903772 0.00715182 1 0 +0.00818188 0.0126094 0.0228858 0.00838371 1 0 +0.0147844 0.0120618 0.0580327 0.00618432 1 0 +0.31466 0.0730897 0.166658 0.00859344 1 0 +0.319997 0.0525574 0.119828 0.00540728 1 0 +0.300218 0.0662144 0.131142 0.0059512 1 0 +0.282304 0.0498985 0.104559 0.00836055 1 0 +0.251758 0.18354 0.132704 0.00661005 1 0 +0.256939 0.160326 0.111173 0.00568273 1 0 +0.247454 0.15754 0.141038 0.00505592 1 0 +0.236481 0.165237 0.140116 0.00649243 1 0 +0.249384 0.00719792 0.180125 0.00758229 1 0 +0.285711 0.0124466 0.191211 0.0059783 1 0 +0.21456 0.186543 0.153079 0.00793122 1 0 +0.226593 0.180792 0.150642 0.00562627 1 0 +0.083875 0.0293033 0.189486 0.0110144 1 0 +0.0897915 0.0688272 0.191879 0.00823789 1 0 +0.106315 0.053566 0.188127 0.0119741 1 0 +0.111229 0.0439213 0.176073 0.00592557 1 0 +0.0550361 0.190197 0.174822 0.00669825 1 0 +0.0652126 0.164537 0.14202 0.00824646 1 0 +0.267189 0.18792 0.191085 0.00915243 1 0 +0.301131 0.171959 0.186846 0.00580245 1 0 +0.275044 0.14871 0.191894 0.00855422 1 0 +0.0975724 0.182156 0.191598 0.00897754 1 0 +0.117908 0.145176 0.184706 0.0136715 1 0 +0.10357 0.157952 0.149551 0.01374 1 0 +0.202562 0.121477 0.182416 0.0104515 1 0 +0.186167 0.0940157 0.190579 0.00955469 1 0 +0.185097 0.109625 0.185197 0.00700597 1 0 +0.193167 0.106857 0.167044 0.00584006 1 0 +0.325806 0.0498837 0.176862 0.00674515 1 0 +0.327219 0.0434707 0.135695 0.009877 1 0 +0.306853 0.00505278 0.129738 0.00514724 1 0 +0.284301 0.00970246 0.141233 0.00973076 1 0 +0.279911 0.024517 0.14259 0.00577992 1 0 +0.0139161 0.0761074 0.0511881 0.00684258 1 0 +0.0193503 0.0978804 0.0773104 0.00537681 1 0 +0.0312895 0.104381 0.0594031 0.008106 1 0 +0.338556 0.0576895 0.071032 0.00952752 1 0 +0.345385 0.0400912 0.0418604 0.00637311 1 0 +0.259771 0.012368 0.0639415 0.00821059 1 0 +0.234281 0.00979121 0.078925 0.00571495 1 0 +0.261934 0.0151467 0.0961373 0.00781128 1 0 +0.259949 0.0384556 0.0902831 0.00712307 1 0 +0.391538 0.0733979 0.181127 0.00823926 1 0 +0.386642 0.103587 0.194456 0.0057173 1 0 +0.353589 0.0933042 0.189377 0.00930854 1 0 +0.368799 0.0965009 0.17087 0.00800324 1 0 +0.0172555 0.065784 0.127555 0.00757448 1 0 +0.0482225 0.0691234 0.119297 0.00644119 1 0 +0.0294276 0.0847632 0.137985 0.00519188 1 0 +0.0442468 0.0580953 0.140519 0.00765787 1 0 +0.0114241 0.186249 0.0561499 0.0059827 1 0 +0.262791 0.180463 0.0104332 0.00712933 1 0 +0.294967 0.180725 0.00756748 0.00765309 1 0 +0.285215 0.168706 0.0109914 0.00819881 1 0 +0.381007 0.113507 0.121441 0.00943741 1 0 +0.356667 0.0880347 0.110332 0.00593489 1 0 +0.351332 0.127097 0.103414 0.00650858 1 0 +0.361994 0.107448 0.135957 0.00580829 1 0 +0.155172 0.0084073 0.0683292 0.00890522 1 0 +0.188438 0.012146 0.0976326 0.00587874 1 0 +0.188594 0.00995718 0.0690544 0.00765734 1 0 +0.164749 0.0103023 0.0215641 0.0106028 1 0 +0.148297 0.0125693 0.0549953 0.00651599 1 0 +0.181856 0.00865314 0.0566887 0.00648529 1 0 +0.18013 0.0188051 0.0511475 0.00512225 1 0 +0.0135713 0.0284825 0.0616517 0.00617028 1 0 +0.0168143 0.0550432 0.0534702 0.00544081 1 0 +0.0409675 0.0512215 0.0813284 0.00719788 1 0 +0.0315788 0.0446527 0.0458322 0.00519363 1 0 +0.385172 0.0707297 0.121669 0.00688846 1 0 +0.374496 0.103002 0.129779 0.00547061 1 0 +0.36727 0.0697245 0.118015 0.00634524 1 0 +0.00772959 0.129551 0.178176 0.00705005 1 0 +0.00816046 0.138058 0.150509 0.00565004 1 0 +0.0289621 0.137907 0.164141 0.00587197 1 0 +0.0308662 0.118011 0.16402 0.00573116 1 0 +0.369926 0.0178553 0.192356 0.00551679 1 0 +0.340425 0.0223486 0.192848 0.00726053 1 0 +0.11975 0.194485 0.158475 0.00575396 1 0 +0.123894 0.160886 0.143244 0.00774069 1 0 +0.38433 0.118675 0.0362219 0.00799993 1 0 +0.383284 0.102965 0.0460323 0.00553318 1 0 +0.375325 0.156401 0.0123866 0.00920127 1 0 +0.357718 0.185092 0.00968631 0.00792417 1 0 +0.347141 0.186821 0.121368 0.0135055 1 0 +0.326057 0.18231 0.153424 0.00539424 1 0 +0.350309 0.194901 0.11039 0.0056395 1 0 +0.346212 0.166812 0.115758 0.00776566 1 0 +0.386697 0.192126 0.181588 0.00799521 1 0 +0.391156 0.161996 0.18664 0.00904983 1 0 +0.390692 0.174385 0.154308 0.00550439 1 0 +0.0981993 0.0080985 0.00848544 0.00689311 1 0 +0.130053 0.00832591 0.00885153 0.00577159 1 0 +0.394383 0.0239477 0.190847 0.00572692 1 0 +0.134318 0.12667 0.172249 0.0140221 1 0 +0.120571 0.140802 0.136533 0.0136934 1 0 +0.153977 0.137668 0.159255 0.0119829 1 0 +0.238908 0.0637111 0.182828 0.00588483 1 0 +0.217927 0.0660372 0.181256 0.00886203 1 0 +0.213215 0.0882626 0.14329 0.00970245 1 0 +0.23219 0.049967 0.161982 0.00523379 1 0 +0.150521 0.116093 0.0410209 0.0080791 1 0 +0.171537 0.113958 0.0821499 0.0127537 1 0 +0.14791 0.109908 0.0683858 0.00711928 1 0 +0.119181 0.11991 0.0577099 0.0123017 1 0 +0.280074 0.0657231 0.0256561 0.00865399 1 0 +0.315154 0.0634001 0.0129531 0.00810922 1 0 +0.284546 0.0676017 0.0591255 0.00532716 1 0 +0.287665 0.0444101 0.0328449 0.00617395 1 0 +0.103511 0.0542246 0.0293572 0.00602015 1 0 +0.0695044 0.04998 0.0409961 0.00763791 1 0 +0.0777995 0.108209 0.0272184 0.0085827 1 0 +0.105089 0.0831391 0.0281576 0.00846905 1 0 +0.085815 0.0790746 0.0487101 0.00653949 1 0 +0.0968583 0.116976 0.0358279 0.00730169 1 0 +0.23678 0.180027 0.189891 0.00785738 1 0 +0.227769 0.143544 0.185132 0.00749987 1 0 +0.374033 0.01768 0.00744475 0.0059995 1 0 +0.339686 0.00677261 0.00975196 0.00758317 1 0 +0.345282 0.0433209 0.00911013 0.00944693 1 0 +0.172435 0.170777 0.00790899 0.0071469 1 0 +0.164953 0.130355 0.0128714 0.00969678 1 0 +0.158948 0.0614367 0.187529 0.00543337 1 0 +0.189978 0.0716632 0.177366 0.00746771 1 0 +0.178238 0.0797084 0.15281 0.00515117 1 0 +0.165762 0.0528525 0.170612 0.00763925 1 0 +0.0212608 0.0094604 0.0249075 0.00532478 1 0 +0.0403634 0.0072238 0.0517658 0.00747519 1 0 +0.272598 0.106078 0.104539 0.0105631 1 0 +0.287075 0.128572 0.101641 0.0131448 1 0 +0.267112 0.132629 0.0860788 0.01249 1 0 +0.268691 0.137384 0.115787 0.0116689 1 0 +0.27002 0.0257179 0.0636094 0.00820884 1 0 +0.273264 0.0592573 0.0810021 0.00733734 1 0 +0.28318 0.0354773 0.0531312 0.00527753 1 0 +0.389817 0.174931 0.080054 0.0106338 1 0 +0.384445 0.132558 0.0788915 0.00836827 1 0 +0.378701 0.139814 0.0972384 0.00602715 1 0 +0.368813 0.141861 0.0898128 0.00650638 1 0 +0.375492 0.110279 0.0315041 0.00507147 1 0 +0.357443 0.0867631 0.00909228 0.00870971 1 0 +0.375897 0.17125 0.088069 0.00602091 1 0 +0.393984 0.161636 0.116134 0.00619005 1 0 +0.373611 0.161616 0.0966576 0.00525879 1 0 +0.238039 0.00638372 0.171919 0.00666163 1 0 +0.216858 0.00703131 0.152268 0.00513398 1 0 +0.248589 0.00675868 0.148564 0.00687499 1 0 +0.250447 0.0211514 0.146636 0.00776452 1 0 +0.0113154 0.182689 0.00817339 0.00538843 1 0 +0.0238939 0.157768 0.00645951 0.00671657 1 0 +0.0526952 0.185282 0.00720097 0.00766273 1 0 +0.352161 0.0664096 0.0153645 0.00547242 1 0 +0.366475 0.0528956 0.0311387 0.00678152 1 0 +0.365266 0.0710618 0.0239861 0.00520902 1 0 +0.00559995 0.0871335 0.18492 0.00598431 1 0 +0.0112576 0.123259 0.187933 0.00508419 1 0 +0.00679544 0.114603 0.150888 0.00721419 1 0 +0.331843 0.0232473 0.0106033 0.00564219 1 0 +0.332571 0.0501213 0.0120277 0.00731454 1 0 +0.163805 0.12126 0.0301393 0.0098536 1 0 +0.183228 0.124309 0.0663082 0.00949033 1 0 +0.16112 0.149977 0.0571642 0.00646968 1 0 +0.0363436 0.00966209 0.145313 0.0101949 1 0 +0.0688378 0.00717589 0.182349 0.00732408 1 0 +0.0901335 0.00996526 0.15139 0.00727241 1 0 +0.0971279 0.0886962 0.172203 0.0076308 1 0 +0.123389 0.0696746 0.17629 0.0142557 1 0 +0.124882 0.0519842 0.162996 0.00792356 1 0 +0.0127854 0.18895 0.187874 0.0060405 1 0 +0.0175302 0.185317 0.159172 0.0049817 1 0 +0.0525532 0.194899 0.185444 0.00556042 1 0 +0.293887 0.136954 0.174997 0.0101556 1 0 +0.292423 0.15219 0.135484 0.0100152 1 0 +0.282663 0.14629 0.152378 0.0103683 1 0 +0.38584 0.172299 0.134702 0.0055863 1 0 +0.382557 0.152871 0.130059 0.00586379 1 0 +0.357948 0.153628 0.110573 0.00712653 1 0 +0.370099 0.157471 0.142381 0.00630911 1 0 +0.231197 0.192268 0.0787137 0.0079133 1 0 +0.255548 0.165874 0.0866424 0.00790485 1 0 +0.104601 0.0696791 0.0710747 0.0055276 1 0 +0.121347 0.0793999 0.0725771 0.0138937 1 0 +0.107036 0.102437 0.0568728 0.00899351 1 0 +0.283468 0.0231189 0.0642061 0.005501 1 0 +0.302915 0.0162019 0.0810346 0.00755809 1 0 +0.0142963 0.174362 0.133416 0.00796007 1 0 +0.0146243 0.145628 0.132304 0.00815538 1 0 +0.0505912 0.153131 0.131263 0.00849822 1 0 +0.0272526 0.160361 0.151105 0.00523692 1 0 +0.13311 0.0181588 0.0707926 0.00755563 1 0 +0.127461 0.0404578 0.0564505 0.00617569 1 0 +0.151748 0.0383447 0.0530821 0.00513729 1 0 +0.00679267 0.169096 0.0536077 0.00703579 1 0 +0.00651555 0.128824 0.0595809 0.00620981 1 0 +0.199676 0.173535 0.126518 0.0114282 1 0 +0.355852 0.0615542 0.189935 0.00529508 1 0 +0.363794 0.0772269 0.169744 0.00559938 1 0 +0.0653017 0.125566 0.174107 0.00626643 1 0 +0.0706548 0.124027 0.145934 0.00508993 1 0 +0.0437616 0.136513 0.163371 0.00505446 1 0 +0.0403833 0.118787 0.153449 0.00513884 1 0 +0.0668814 0.177731 0.0527361 0.00573733 1 0 +0.0839827 0.158633 0.0492808 0.00631288 1 0 +0.244283 0.130233 0.0206647 0.0134639 1 0 +0.248042 0.142193 0.0542112 0.00985963 1 0 +0.127496 0.0235248 0.0806035 0.00495716 1 0 +0.112549 0.0236483 0.117163 0.00574903 1 0 +0.102698 0.0328561 0.104551 0.00541704 1 0 +0.121179 0.0419633 0.103427 0.00550955 1 0 +0.0949815 0.00750083 0.183421 0.00782525 1 0 +0.134652 0.00614998 0.184862 0.00637881 1 0 +0.116839 0.0088601 0.154427 0.00920183 1 0 +0.31655 0.00700799 0.0248976 0.00751269 1 0 +0.290853 0.00645499 0.0473027 0.00643783 1 0 +0.198169 0.0451707 0.154014 0.00652276 1 0 +0.202753 0.0641795 0.129688 0.00629429 1 0 +0.231726 0.0501635 0.148392 0.00836477 1 0 +0.0806751 0.186693 0.0947945 0.00588621 1 0 +0.0980923 0.193862 0.114456 0.00659301 1 0 +0.0927057 0.166659 0.0945026 0.00576554 1 0 +0.131037 0.0979195 0.0154893 0.00529982 1 0 +0.119759 0.0958302 0.0614018 0.00604038 1 0 +0.117254 0.105551 0.0450691 0.00692544 1 0 +0.180049 0.0968475 0.0963658 0.0110648 1 0 +0.147055 0.0681246 0.0888777 0.00600637 1 0 +0.165325 0.0730319 0.0647813 0.0116055 1 0 +0.356389 0.0605846 0.0771528 0.00670138 1 0 +0.0090253 0.146891 0.178321 0.00943242 1 0 +0.00766876 0.177799 0.147082 0.00803472 1 0 +0.284654 0.182165 0.0777849 0.00603437 1 0 +0.317912 0.188593 0.0821801 0.010196 1 0 +0.31526 0.170622 0.0967369 0.00954165 1 0 +0.157011 0.0204114 0.191012 0.00814343 1 0 +0.0246857 0.126178 0.0110075 0.00674863 1 0 +0.0583965 0.11981 0.0136165 0.00686965 1 0 +0.0423734 0.131397 0.0355498 0.00524297 1 0 +0.359164 0.0155869 0.1297 0.0124649 1 0 +0.346991 0.00989893 0.149958 0.00993081 1 0 +0.335926 0.0173156 0.11971 0.0128885 1 0 +0.351904 0.0330754 0.118387 0.00959278 1 0 +0.394389 0.0194257 0.0160663 0.00555908 1 0 +0.379919 0.129378 0.133306 0.00674795 1 0 +0.343983 0.13786 0.10791 0.00727759 1 0 +0.353387 0.128435 0.141138 0.00927701 1 0 +0.360604 0.139797 0.147167 0.0054718 1 0 +0.210437 0.166175 0.0492754 0.00567761 1 0 +0.333137 0.0762092 0.17059 0.0105529 1 0 +0.304235 0.104976 0.172225 0.0128493 1 0 +0.335688 0.0988857 0.167769 0.0124404 1 0 +0.202789 0.187561 0.113687 0.00783417 1 0 +0.216769 0.19414 0.0854304 0.0061718 1 0 +0.226591 0.167311 0.0969075 0.00680436 1 0 +0.0790982 0.1003 0.091185 0.00741713 1 0 +0.0916304 0.108721 0.10674 0.00642763 1 0 +0.124019 0.099056 0.0987964 0.0119762 1 0 +0.099967 0.113614 0.0782024 0.00766243 1 0 +0.322068 0.132654 0.161504 0.0110719 1 0 +0.328964 0.137853 0.116072 0.00981602 1 0 +0.347629 0.119456 0.153756 0.00724557 1 0 +0.343041 0.14108 0.150765 0.00968606 1 0 +0.308067 0.0305676 0.014255 0.00749486 1 0 +0.288222 0.0486479 0.00726165 0.00755659 1 0 +0.322953 0.0477624 0.00519321 0.00532254 1 0 +0.163145 0.0244185 0.00512858 0.00531505 1 0 +0.143503 0.0521922 0.0141325 0.00602348 1 0 +0.176407 0.0560466 0.00779385 0.00804853 1 0 +0.171945 0.0484305 0.027348 0.00827547 1 0 +0.170499 0.19428 0.0274052 0.00537 1 0 +0.156708 0.193111 0.0620835 0.0071917 1 0 +0.185673 0.194117 0.0511677 0.00612124 1 0 +0.0331075 0.0116814 0.124277 0.0112441 1 0 +0.0663881 0.017704 0.134001 0.00695376 1 0 +0.0509858 0.0401183 0.121398 0.00752897 1 0 +0.0536159 0.0388033 0.139584 0.00586791 1 0 +0.377357 0.0513898 0.0351915 0.00492737 1 0 +0.379201 0.0777361 0.037615 0.00548177 1 0 +0.205056 0.128243 0.0246036 0.0112558 1 0 +0.194766 0.119397 0.0757122 0.00618427 1 0 +0.181175 0.145115 0.0499636 0.00675236 1 0 +0.148696 0.0473235 0.193751 0.00638771 1 0 +0.1805 0.0491506 0.19158 0.00866147 1 0 +0.0218087 0.0143186 0.108466 0.00836723 1 0 +0.0478675 0.0219623 0.0896958 0.00823181 1 0 +0.0512223 0.0421287 0.0966569 0.0133643 1 0 +0.0207695 0.021881 0.119874 0.00535858 1 0 +0.0215509 0.0467649 0.120322 0.00641871 1 0 +0.0360928 0.0484112 0.104878 0.00496479 1 0 +0.0326386 0.0525415 0.145065 0.00598976 1 0 +0.381427 0.190418 0.194281 0.00586092 1 0 +0.373734 0.16649 0.18837 0.00498055 1 0 +0.205452 0.154565 0.177984 0.00579418 1 0 +0.194448 0.131231 0.149231 0.00721404 1 0 +0.0229852 0.137984 0.00566485 0.00616599 1 0 +0.0607551 0.169837 0.00924403 0.00965325 1 0 +0.0609532 0.142705 0.00515321 0.00524266 1 0 +0.0845293 0.0785676 0.161161 0.00895357 1 0 +0.0760292 0.0631132 0.116189 0.00934452 1 0 +0.109096 0.0662653 0.125401 0.0088934 1 0 +0.0992344 0.0589427 0.143979 0.0105978 1 0 +0.273636 0.0785978 0.180015 0.0113957 1 0 +0.31771 0.0863584 0.178826 0.00966627 1 0 +0.29521 0.110289 0.191138 0.00918946 1 0 +0.381769 0.130907 0.179692 0.00549505 1 0 +0.384951 0.132232 0.147066 0.00501827 1 0 +0.368263 0.120297 0.157834 0.00564419 1 0 +0.372344 0.141301 0.154623 0.00518439 1 0 +0.015366 0.18067 0.0718755 0.00991698 1 0 +0.0571934 0.17713 0.0784465 0.00840477 1 0 +0.0608721 0.163181 0.103517 0.00874349 1 0 +0.0402299 0.146705 0.0580912 0.00834893 1 0 +0.254209 0.193453 0.0136025 0.00692331 1 0 +0.293146 0.193828 0.00583723 0.00565197 1 0 +0.273439 0.190697 0.0328814 0.00943147 1 0 +0.148158 0.0224957 0.0751162 0.0086904 1 0 +0.169855 0.0328631 0.104474 0.00539417 1 0 +0.161211 0.0397703 0.0704683 0.00845554 1 0 +0.14024 0.0469873 0.122983 0.010578 1 0 +0.336735 0.0202872 0.0803728 0.00662079 1 0 +0.331173 0.0418264 0.0836408 0.00698085 1 0 +0.248925 0.170773 0.189235 0.0074251 1 0 +0.262339 0.153703 0.191569 0.00510053 1 0 +0.228363 0.152946 0.194167 0.00555304 1 0 +0.253545 0.153824 0.172184 0.00501869 1 0 +0.085673 0.0219495 0.128863 0.00810289 1 0 +0.0787165 0.0476022 0.114194 0.00652346 1 0 +0.0980547 0.0314382 0.144821 0.00871123 1 0 +0.0709612 0.0486933 0.147475 0.00741279 1 0 +0.146003 0.180965 0.107392 0.00615155 1 0 +0.151935 0.175908 0.116585 0.00590186 1 0 +0.131311 0.156841 0.111757 0.006995 1 0 +0.156763 0.164613 0.0522469 0.00700493 1 0 +0.166162 0.131633 0.0940256 0.00920817 1 0 +0.169708 0.159475 0.0531444 0.00695105 1 0 +0.391928 0.014103 0.0938668 0.00815456 1 0 +0.391359 0.0211293 0.117583 0.00530946 1 0 +0.375048 0.0429505 0.106888 0.00530811 1 0 +0.0461391 0.0699646 0.193821 0.00627914 1 0 +0.0622147 0.0345418 0.191454 0.00898645 1 0 +0.0593306 0.0684257 0.192418 0.00508167 1 0 +0.0397629 0.0590945 0.170099 0.00595525 1 0 +0.0661161 0.112209 0.0346864 0.00584907 1 0 +0.0797103 0.0967227 0.0580186 0.00598094 1 0 +0.0568741 0.114675 0.041708 0.00601682 1 0 +0.0822935 0.121589 0.0590197 0.00649001 1 0 +0.0084868 0.184703 0.0965632 0.0086322 1 0 +0.00581197 0.179899 0.124047 0.00594827 1 0 +0.0193344 0.15123 0.117751 0.00813427 1 0 +0.0373418 0.165335 0.111805 0.00496009 1 0 +0.0069464 0.0203693 0.00957193 0.00729029 1 0 +0.0114912 0.0644476 0.0186047 0.00692857 1 0 +0.23016 0.15093 0.070759 0.00659564 1 0 +0.239476 0.169123 0.048159 0.00817471 1 0 +0.216925 0.162977 0.0570597 0.00494897 1 0 +0.256492 0.108796 0.106756 0.00592031 1 0 +0.239834 0.122034 0.101258 0.0072955 1 0 +0.250444 0.134166 0.118395 0.00695265 1 0 +0.0669046 0.0202201 0.0606514 0.00578 1 0 +0.0667976 0.109972 0.163301 0.00737749 1 0 +0.0647391 0.0862293 0.128114 0.00507807 1 0 +0.0730489 0.126344 0.135373 0.0059837 1 0 +0.0487948 0.104113 0.14207 0.00657197 1 0 +0.028089 0.193995 0.0135611 0.00631276 1 0 +0.0674378 0.193956 0.0187955 0.00627478 1 0 +0.0560479 0.192898 0.040999 0.00736616 1 0 +0.262174 0.14737 0.00768132 0.00782649 1 0 +0.110871 0.0181432 0.0802059 0.00539194 1 0 +0.0877005 0.00667963 0.0836161 0.00686255 1 0 +0.104632 0.00847264 0.10825 0.00872388 1 0 +0.0824788 0.0353326 0.0842433 0.00699519 1 0 +0.14732 0.10401 0.105935 0.0128921 1 0 +0.131004 0.0929942 0.120441 0.0115619 1 0 +0.124891 0.124973 0.0770648 0.00850322 1 0 +0.367253 0.00909377 0.0543428 0.00926423 1 0 +0.341448 0.00752611 0.0337154 0.00700713 1 0 +0.353185 0.00671947 0.0658624 0.00679685 1 0 +0.360068 0.0192233 0.0458931 0.00610856 1 0 +0.00526509 0.0409069 0.136276 0.00535184 1 0 +0.00810634 0.0521513 0.169336 0.00845315 1 0 +0.00898223 0.0516547 0.135279 0.00540738 1 0 +0.0272917 0.0344396 0.154164 0.00617013 1 0 +0.28683 0.192156 0.0542309 0.00529478 1 0 +0.306074 0.167622 0.0566469 0.00537123 1 0 +0.19755 0.0334298 0.0932068 0.00801512 1 0 +0.189077 0.0691788 0.103897 0.0121385 1 0 +0.191992 0.113584 0.0278815 0.0086521 1 0 +0.162067 0.106234 0.0287016 0.00534139 1 0 +0.179901 0.11 0.0638109 0.00541145 1 0 +0.17278 0.0881559 0.0541978 0.00830241 1 0 +0.288755 0.0755487 0.0109192 0.0110888 1 0 +0.308569 0.0795913 0.0189497 0.01037 1 0 +0.296556 0.0983438 0.0125803 0.0125357 1 0 +0.383772 0.0259801 0.193385 0.00537695 1 0 +0.386942 0.0526887 0.193606 0.00647137 1 0 +0.365199 0.047121 0.19492 0.0052279 1 0 +0.0519969 0.181546 0.139662 0.00596041 1 0 +0.073405 0.189449 0.169333 0.0107301 1 0 +0.0877081 0.173817 0.141113 0.00870525 1 0 +0.341047 0.169722 0.193253 0.00654535 1 0 +0.330376 0.150117 0.192594 0.00496002 1 0 +0.114133 0.161206 0.130073 0.00865552 1 0 +0.303032 0.188236 0.140302 0.0117671 1 0 +0.315833 0.195507 0.114557 0.00504126 1 0 +0.284501 0.189967 0.127885 0.0104032 1 0 +0.312477 0.161431 0.113936 0.0101568 1 0 +0.327634 0.0793802 0.0481969 0.0100093 1 0 +0.324448 0.0752109 0.0906867 0.0083484 1 0 +0.31356 0.104172 0.0805245 0.00749451 1 0 +0.327746 0.101416 0.0747659 0.00806205 1 0 +0.121771 0.0134248 0.122837 0.00852373 1 0 +0.159631 0.0150029 0.128638 0.00980085 1 0 +0.392511 0.0522317 0.169404 0.00783913 1 0 +0.395162 0.0501463 0.134408 0.00498903 1 0 +0.272107 0.170806 0.116229 0.013434 1 0 +0.281281 0.155885 0.101404 0.0095132 1 0 +0.266574 0.153585 0.107595 0.00660862 1 0 +0.264709 0.153674 0.119585 0.00552586 1 0 +0.38681 0.0562555 0.00638283 0.00528748 1 0 +0.365455 0.0437473 0.0107899 0.00529517 1 0 +0.210022 0.123308 0.12934 0.00556125 1 0 +0.250494 0.193287 0.180336 0.00706519 1 0 +0.258499 0.192009 0.147804 0.00855534 1 0 +0.281498 0.0581577 0.178437 0.0105608 1 0 +0.303242 0.0692587 0.176297 0.00683264 1 0 +0.279185 0.0677835 0.134067 0.00571929 1 0 +0.283695 0.0478234 0.161373 0.00950883 1 0 +0.201343 0.0885042 0.00865546 0.00499766 1 0 +0.217247 0.0657974 0.0338116 0.00681007 1 0 +0.184647 0.0707221 0.0238142 0.00558769 1 0 +0.109721 0.184804 0.00937792 0.00961705 1 0 +0.329991 0.0107204 0.0287114 0.0058961 1 0 +0.324986 0.00709674 0.065553 0.00544047 1 0 +0.320655 0.0241156 0.0465631 0.00626207 1 0 +0.246404 0.0691349 0.00781767 0.00828671 1 0 +0.222941 0.103933 0.0111598 0.011335 1 0 +0.216109 0.0696221 0.00799544 0.00826141 1 0 +0.157528 0.0899447 0.191168 0.00507421 1 0 +0.155537 0.100229 0.191591 0.00540953 1 0 +0.177525 0.0943671 0.150699 0.00861897 1 0 +0.0348592 0.00813347 0.015002 0.00828149 1 0 +0.0512365 0.0192255 0.0153088 0.00517423 1 0 +0.342655 0.0798171 0.0361204 0.00926937 1 0 +0.3391 0.0812289 0.0784261 0.0116296 1 0 +0.338672 0.111785 0.0750774 0.00700406 1 0 +0.330466 0.129438 0.187166 0.00655636 1 0 +0.361513 0.120336 0.171651 0.00498052 1 0 +0.205291 0.0302313 0.114125 0.0145174 1 0 +0.20977 0.0267413 0.0944394 0.00597039 1 0 +0.205971 0.0681862 0.0953955 0.00680038 1 0 +0.233346 0.0592739 0.0989024 0.0127908 1 0 +0.0729556 0.0741033 0.169157 0.00580497 1 0 +0.0687666 0.0753065 0.130455 0.00601045 1 0 +0.0846709 0.0567052 0.156714 0.00887761 1 0 +0.0667951 0.0521266 0.1623 0.00836534 1 0 +0.29608 0.0149178 0.0917873 0.00524784 1 0 +0.283962 0.0466343 0.0910075 0.00567696 1 0 +0.0622957 0.0349496 0.0106501 0.0110784 1 0 +0.0495625 0.0756888 0.00622606 0.00642553 1 0 +0.386469 0.0770333 0.0583167 0.00552774 1 0 +0.387526 0.0946109 0.0661314 0.00621954 1 0 +0.362512 0.0757967 0.0849561 0.00624904 1 0 +0.241755 0.185676 0.178927 0.00544206 1 0 +0.234978 0.173823 0.150145 0.00528781 1 0 +0.0631754 0.100075 0.0342654 0.00630015 1 0 +0.0490861 0.10025 0.0597905 0.00612054 1 0 +0.0448321 0.0676857 0.0483798 0.00556852 1 0 +0.357979 0.0272896 0.0764568 0.00501178 1 0 +0.278958 0.131248 0.150856 0.00519829 1 0 +0.280979 0.110705 0.130202 0.00534803 1 0 +0.302751 0.12933 0.116301 0.00833201 1 0 +0.27393 0.140098 0.141054 0.00521546 1 0 +0.0373405 0.191358 0.126653 0.00727957 1 0 +0.0677841 0.191625 0.0901428 0.00869273 1 0 +0.0844209 0.191969 0.124443 0.00855905 1 0 +0.0775465 0.162201 0.109099 0.0082593 1 0 +0.0207555 0.0053754 0.188771 0.00547368 1 0 +0.0455209 0.00928189 0.181845 0.00561126 1 0 +0.0245158 0.167059 0.0749526 0.00601204 1 0 +0.0568859 0.143012 0.0981434 0.00673649 1 0 +0.0311157 0.142002 0.0668341 0.00512793 1 0 +0.00911201 0.111889 0.0105822 0.00952419 1 0 +0.0289019 0.0987202 0.0405127 0.00667252 1 0 +0.0153248 0.0709797 0.185047 0.00708259 1 0 +0.0468106 0.0975161 0.194603 0.00576613 1 0 +0.0330428 0.088288 0.168913 0.00655303 1 0 +0.0319882 0.0658179 0.164622 0.00569154 1 0 +0.327932 0.0661663 0.0347492 0.00884639 1 0 +0.325235 0.0611186 0.0757851 0.00502571 1 0 +0.301791 0.0719827 0.0647257 0.00502249 1 0 +0.140444 0.0208043 0.00857616 0.00554489 1 0 +0.13206 0.0531746 0.0191259 0.00650001 1 0 +0.0227459 0.0405526 0.0137735 0.00512675 1 0 +0.0139517 0.0311413 0.0791735 0.00739728 1 0 +0.00812655 0.0462816 0.0920372 0.00831147 1 0 +0.0100434 0.0603263 0.0679059 0.00747143 1 0 +0.0330852 0.0595645 0.0875891 0.00587617 1 0 +0.01453 0.0759006 0.120222 0.00521403 1 0 +0.0151535 0.103402 0.114357 0.0067483 1 0 +0.0287214 0.0951057 0.139099 0.00523436 1 0 +0.0133948 0.0273098 0.111089 0.00733124 1 0 +0.00839056 0.0392552 0.105564 0.00674924 1 0 +0.0335484 0.0517292 0.0957753 0.00505228 1 0 +0.321536 0.0865347 0.161924 0.00723207 1 0 +0.336144 0.0797876 0.125265 0.00618701 1 0 +0.306447 0.0747709 0.124501 0.0063047 1 0 +0.338491 0.0802498 0.140048 0.00809261 1 0 +0.0724714 0.172504 0.0723751 0.00867382 1 0 +0.077077 0.155879 0.0945479 0.00612652 1 0 +0.0603146 0.142855 0.064314 0.00788684 1 0 +0.0873398 0.153674 0.0624092 0.00743584 1 0 +0.0837064 0.168728 0.18948 0.00889523 1 0 +0.0796081 0.136161 0.182522 0.013425 1 0 +0.0984236 0.143756 0.174245 0.00848882 1 0 +0.090862 0.139998 0.157997 0.00982194 1 0 +0.120833 0.0169722 0.0755527 0.00566548 1 0 +0.0890771 0.0475762 0.0844198 0.00691428 1 0 +0.111386 0.0351207 0.0575203 0.0053293 1 0 +0.220659 0.0305815 0.129607 0.00729992 1 0 +0.20806 0.0804905 0.123909 0.0118058 1 0 +0.238003 0.061297 0.120152 0.00905688 1 0 +0.233536 0.0524825 0.134068 0.00625855 1 0 +0.246734 0.133539 0.040288 0.00658618 1 0 +0.261156 0.116553 0.0379597 0.0120144 1 0 +0.253954 0.128533 0.0491202 0.00587193 1 0 +0.0659653 0.0123689 0.0890542 0.0122615 1 0 +0.0753169 0.0123982 0.113778 0.0125732 1 0 +0.0701547 0.0311356 0.0969636 0.00853032 1 0 +0.124168 0.167553 0.0768115 0.00984731 1 0 +0.154698 0.126665 0.106738 0.00861596 1 0 +0.114986 0.142254 0.0860121 0.0133329 1 0 +0.346007 0.153192 0.00795461 0.00800824 1 0 +0.318942 0.148861 0.0113152 0.0115475 1 0 +0.318348 0.0348712 0.0863439 0.00785672 1 0 +0.303844 0.0561051 0.0899067 0.0052354 1 0 +0.313022 0.0358319 0.074224 0.00541661 1 0 +0.152268 0.0076484 0.173852 0.00786869 1 0 +0.13011 0.0144056 0.154271 0.00585279 1 0 +0.165082 0.0119672 0.144377 0.00713006 1 0 +0.0871026 0.125344 0.0093217 0.0096307 1 0 +0.122601 0.0934845 0.0107429 0.00534686 1 0 +0.390935 0.0680353 0.16854 0.00545651 1 0 +0.391784 0.0660705 0.137733 0.0081494 1 0 +0.368876 0.0851037 0.157602 0.00681751 1 0 +0.3435 0.0532953 0.184997 0.0059137 1 0 +0.0678451 0.116635 0.0213229 0.00572971 1 0 +0.0831903 0.134643 0.0428236 0.0073013 1 0 +0.315142 0.019002 0.154585 0.00509271 1 0 +0.318834 0.0287574 0.124498 0.0082297 1 0 +0.331325 0.0384706 0.118156 0.00881685 1 0 +0.257485 0.0774816 0.0149607 0.00737033 1 0 +0.243986 0.105343 0.0208968 0.0114293 1 0 +0.250893 0.0810976 0.056506 0.00976706 1 0 +0.241362 0.071397 0.0497077 0.00543699 1 0 +0.0951764 0.190812 0.0352198 0.005069 1 0 +0.105909 0.1918 0.0552695 0.0055431 1 0 +0.0838705 0.19237 0.0559887 0.00778471 1 0 +0.0909871 0.168167 0.046449 0.00585127 1 0 +0.138129 0.0547823 0.0314965 0.00737256 1 0 +0.133008 0.0625234 0.0701167 0.00598886 1 0 +0.131467 0.0487513 0.0499509 0.0050972 1 0 +0.392729 0.17308 0.0543232 0.00762244 1 0 +0.390069 0.140352 0.0309828 0.00894587 1 0 +0.392483 0.144524 0.0675016 0.00668198 1 0 +0.365541 0.139541 0.0628607 0.00767386 1 0 +0.181917 0.072989 0.122854 0.00848093 1 0 +0.151607 0.0618207 0.010758 0.00684657 1 0 +0.170956 0.0743141 0.00515087 0.00535107 1 0 +0.151276 0.100927 0.0089466 0.00913301 1 0 +0.159422 0.0740842 0.0305408 0.00543141 1 0 +0.115667 0.180416 0.0794904 0.0058022 1 0 +0.0994465 0.155809 0.0923839 0.00718233 1 0 +0.0999845 0.154006 0.0694851 0.00705776 1 0 +0.273075 0.179113 0.0764238 0.0060175 1 0 +0.273414 0.182999 0.100381 0.00660443 1 0 +0.294461 0.167567 0.0871492 0.00611654 1 0 +0.267046 0.169424 0.0821706 0.00493232 1 0 +0.263157 0.0743531 0.166745 0.00603747 1 0 +0.236311 0.0883744 0.127816 0.00866709 1 0 +0.262038 0.0760561 0.129225 0.00937597 1 0 +0.260806 0.0583165 0.152129 0.00554879 1 0 +0.127913 0.0401208 0.0105813 0.0108419 1 0 +0.139949 0.188361 0.0447094 0.00963688 1 0 +0.156756 0.175254 0.0466356 0.00502514 1 0 +0.0182403 0.0775134 0.0207589 0.00585374 1 0 +0.049628 0.0861852 0.0125484 0.00534557 1 0 +0.0386586 0.104817 0.0341019 0.00649825 1 0 +0.0504136 0.0672162 0.0286269 0.00617965 1 0 +0.241949 0.0263184 0.0058993 0.00573091 1 0 +0.252068 0.0556234 0.00921361 0.00637274 1 0 +0.22369 0.0526726 0.0183509 0.0052916 1 0 +0.232494 0.0511763 0.0241064 0.00533218 1 0 +0.19112 0.181137 0.00602503 0.00537962 1 0 +0.202486 0.159753 0.0127104 0.00927223 1 0 +0.0752446 0.00953644 0.0199405 0.00785062 1 0 +0.0762035 0.00765303 0.0478329 0.00785765 1 0 +0.0933209 0.00532339 0.0221773 0.00561959 1 0 +0.103481 0.0147954 0.0398048 0.00544491 1 0 +0.0277912 0.120626 0.096511 0.00511143 1 0 +0.0426447 0.126149 0.114312 0.00516252 1 0 +0.196674 0.18675 0.102437 0.0049966 1 0 +0.211756 0.1794 0.0726919 0.00523377 1 0 +0.223498 0.175543 0.0884482 0.00502362 1 0 +0.200843 0.175993 0.0680288 0.00711302 1 0 +0.342708 0.0784478 0.109096 0.0064675 1 0 +0.301046 0.0851749 0.11395 0.00736852 1 0 +0.326322 0.112953 0.104599 0.00894385 1 0 +0.33831 0.112264 0.129564 0.00533993 1 0 +0.146632 0.119854 0.155857 0.00758345 1 0 +0.113839 0.124127 0.123163 0.00871534 1 0 +0.147431 0.104285 0.128928 0.0101026 1 0 +0.130699 0.107509 0.132492 0.00730589 1 0 +0.110278 0.121507 0.179754 0.0116856 1 0 +0.0956274 0.122005 0.158534 0.00879957 1 0 +0.119865 0.0920174 0.138423 0.00961322 1 0 +0.219451 0.0418365 0.0510078 0.00644042 1 0 +0.173338 0.00866538 0.177793 0.00550045 1 0 +0.211117 0.00543773 0.18246 0.00561183 1 0 +0.187216 0.00837054 0.155071 0.00872445 1 0 +0.1664 0.17484 0.0827415 0.00501124 1 0 +0.177999 0.143392 0.0931468 0.00585077 1 0 +0.109717 0.192946 0.0137512 0.00724742 1 0 +0.14143 0.184762 0.0195865 0.00684492 1 0 +0.122259 0.190175 0.0476535 0.0102463 1 0 +0.0667361 0.169401 0.0599992 0.00531496 1 0 +0.082868 0.146826 0.0508342 0.00564837 1 0 +0.0552552 0.154891 0.0433447 0.00561681 1 0 +0.310689 0.151778 0.181469 0.00665367 1 0 +0.330094 0.152012 0.136954 0.0113277 1 0 +0.345349 0.156333 0.145413 0.00664196 1 0 +0.251394 0.126454 0.166924 0.0114056 1 0 +0.267385 0.108622 0.136848 0.00553662 1 0 +0.263938 0.13864 0.160479 0.00723252 1 0 +0.360484 0.188651 0.0759512 0.00497759 1 0 +0.331221 0.188418 0.0363829 0.00560804 1 0 +0.331933 0.192602 0.0717644 0.00779763 1 0 +0.347752 0.165414 0.0682079 0.00796147 1 0 +0.136251 0.00928 0.0920064 0.00859638 1 0 +0.129956 0.0161276 0.112292 0.00509584 1 0 +0.152579 0.0159942 0.115424 0.00520984 1 0 +0.136703 0.036157 0.110096 0.00662378 1 0 +0.100961 0.00493377 0.154156 0.00544695 1 0 +0.13546 0.0669218 0.0237618 0.0068288 1 0 +0.142736 0.0815677 0.0644031 0.00910591 1 0 +0.155592 0.0620349 0.0396216 0.00727139 1 0 +0.23071 0.0941707 0.0424577 0.0106171 1 0 +0.231904 0.080267 0.0565854 0.00924055 1 0 +0.136849 0.055555 0.169806 0.00630133 1 0 +0.13647 0.0580592 0.135486 0.00654245 1 0 +0.150866 0.0539106 0.165846 0.00803573 1 0 +0.394575 0.07269 0.0171423 0.0055546 1 0 +0.39486 0.110574 0.0118853 0.00550496 1 0 +0.387951 0.0869387 0.0294716 0.00538716 1 0 +0.254035 0.0737726 0.183436 0.00907811 1 0 +0.227242 0.108008 0.16302 0.0103512 1 0 +0.22171 0.101723 0.147955 0.0068841 1 0 +0.246732 0.0983579 0.13288 0.00662683 1 0 +0.0335488 0.109202 0.0863888 0.00581296 1 0 +0.0513091 0.0948092 0.0875527 0.00611939 1 0 +0.05679 0.122306 0.0980262 0.00517686 1 0 +0.052778 0.114593 0.0733525 0.00678807 1 0 +0.377528 0.112721 0.191255 0.00636142 1 0 +0.323421 0.118747 0.192828 0.00747939 1 0 +0.0202821 0.127857 0.185543 0.00532221 1 0 +0.0456882 0.121297 0.189231 0.00619298 1 0 +0.0278125 0.163137 0.185644 0.00614243 1 0 +0.0407269 0.184969 0.19302 0.00713734 1 0 +0.202317 0.0380069 0.0635513 0.0061186 1 0 +0.196048 0.0690914 0.0860807 0.00683945 1 0 +0.214552 0.0556608 0.0504908 0.00558046 1 0 +0.187018 0.0565114 0.0598651 0.00514824 1 0 +0.192447 0.179866 0.191769 0.00552928 1 0 +0.382976 0.00933654 0.00800821 0.00624409 1 0 +0.379462 0.00880123 0.0393308 0.00900867 1 0 +0.228739 0.0357554 0.140074 0.00689897 1 0 +0.253924 0.0245816 0.129793 0.00977247 1 0 +0.254756 0.0552683 0.117164 0.0089968 1 0 +0.244956 0.0366762 0.141704 0.00942559 1 0 +0.284401 0.126071 0.0301732 0.0129644 1 0 +0.280462 0.111896 0.0848258 0.0114435 1 0 +0.294088 0.127276 0.0774395 0.0103921 1 0 +0.280271 0.135028 0.0733615 0.0059669 1 0 +0.00563921 0.0823402 0.11215 0.00596405 1 0 +0.00684844 0.11187 0.11031 0.0057838 1 0 +0.0379734 0.0850477 0.0989356 0.00668566 1 0 +0.148048 0.0673697 0.16165 0.00634128 1 0 +0.16632 0.0802059 0.13652 0.0100819 1 0 +0.153832 0.0715793 0.136283 0.00509827 1 0 +0.164782 0.0570918 0.149821 0.00786055 1 0 +0.0708317 0.0947185 0.0813214 0.00661075 1 0 +0.0669437 0.121838 0.0984773 0.00499761 1 0 +0.0644455 0.124797 0.0716368 0.00615064 1 0 +0.090339 0.120408 0.0696529 0.00689617 1 0 +0.262019 0.0920942 0.188904 0.00850721 1 0 +0.266705 0.121891 0.180648 0.00733623 1 0 +0.243492 0.119364 0.185346 0.00985739 1 0 +0.265228 0.0970171 0.153937 0.00631729 1 0 +0.386732 0.186006 0.0490056 0.0075897 1 0 +0.391645 0.153252 0.0174181 0.00874731 1 0 +0.0164142 0.169571 0.0469836 0.00510672 1 0 +0.0162801 0.146559 0.0269116 0.00578039 1 0 +0.0336765 0.137425 0.0465965 0.00640884 1 0 +0.0352034 0.153494 0.042736 0.00671905 1 0 +0.393189 0.145792 0.170589 0.00703241 1 0 +0.393971 0.175462 0.142844 0.00637997 1 0 +0.393145 0.142807 0.143542 0.006247 1 0 +0.378366 0.151838 0.156955 0.00527381 1 0 +0.381775 0.0767982 0.00497851 0.00506417 1 0 +0.384389 0.110274 0.00625307 0.00656743 1 0 +0.0501918 0.0651677 0.0751153 0.00698474 1 0 +0.0522839 0.0587165 0.0514569 0.00649157 1 0 +0.217106 0.0220904 0.0114196 0.00546845 1 0 +0.20051 0.0538623 0.0172405 0.0068967 1 0 +0.222733 0.0473342 0.0279354 0.00572104 1 0 +0.261327 0.0222179 0.186737 0.00551997 1 0 +0.300809 0.0175195 0.00825984 0.0083 1 0 +0.278985 0.0387736 0.00632486 0.00651096 1 0 +0.0868131 0.14207 0.00723754 0.00730909 1 0 +0.111635 0.139991 0.0197633 0.00503765 1 0 +0.103829 0.139937 0.0308627 0.00710114 1 0 +0.0486418 0.113671 0.00631207 0.00680382 1 0 +0.0276864 0.110959 0.0271024 0.00789287 1 0 +0.189698 0.0844834 0.00680689 0.00718779 1 0 +0.165699 0.11211 0.0107286 0.00868968 1 0 +0.213054 0.136856 0.0377959 0.00641269 1 0 +0.205379 0.128465 0.0764922 0.00779672 1 0 +0.210352 0.14347 0.0803363 0.00847115 1 0 +0.19583 0.147316 0.0427877 0.00687583 1 0 +0.203294 0.0652706 0.173102 0.00790694 1 0 +0.195976 0.0856479 0.140561 0.00794527 1 0 +0.198447 0.0571985 0.161029 0.00740396 1 0 +0.271409 0.00778163 0.00978963 0.0049704 1 0 +0.294126 0.00627609 0.01453 0.00645502 1 0 +0.28841 0.183894 0.158858 0.00510762 1 0 +0.269919 0.183373 0.131083 0.00614539 1 0 +0.29785 0.164485 0.122122 0.00688027 1 0 +0.377503 0.106226 0.0875702 0.00694416 1 0 +0.386703 0.127112 0.110159 0.00913205 1 0 +0.357162 0.124419 0.0912405 0.00725182 1 0 +0.0095061 0.116134 0.0277109 0.0070637 1 0 +0.00773396 0.0931842 0.051083 0.0050224 1 0 +0.00649546 0.115939 0.0538062 0.00667849 1 0 +0.0282208 0.110772 0.0450519 0.00622384 1 0 +0.394226 0.100204 0.186616 0.00590458 1 0 +0.390406 0.111207 0.183462 0.0060037 1 0 +0.371822 0.111796 0.16477 0.00589004 1 0 +0.320528 0.0880744 0.0322957 0.00945677 1 0 +0.298506 0.108633 0.0320131 0.00953935 1 0 +0.27853 0.09926 0.0699264 0.00818781 1 0 +0.31458 0.122673 0.0613605 0.0144611 1 0 +0.0293974 0.107606 0.126394 0.00543034 1 0 +0.0588922 0.124193 0.121338 0.00801985 1 0 +0.0424652 0.102032 0.131962 0.00553475 1 0 +0.364571 0.122046 0.00864333 0.0089966 1 0 +0.327554 0.121654 0.0148372 0.0124066 1 0 +0.333016 0.124314 0.0330948 0.00683507 1 0 +0.105421 0.0127092 0.192345 0.00790702 1 0 +0.137771 0.0134939 0.193549 0.00659779 1 0 +0.122142 0.0399081 0.190873 0.00657837 1 0 +0.0360628 0.14824 0.195135 0.00498531 1 0 +0.0487747 0.130945 0.194461 0.00520711 1 0 +0.0114236 0.190122 0.0171162 0.00624084 1 0 +0.00719424 0.185625 0.043441 0.00751635 1 0 +0.150284 0.191552 0.11361 0.00847748 1 0 +0.113937 0.170589 0.114578 0.00946019 1 0 +0.157242 0.195396 0.0739888 0.004951 1 0 +0.187169 0.191199 0.104521 0.0057037 1 0 +0.191458 0.179219 0.0751372 0.00509441 1 0 +0.179449 0.177476 0.0741934 0.00707689 1 0 +0.220758 0.131729 0.172635 0.0110727 1 0 +0.235641 0.14715 0.150809 0.00628522 1 0 +0.174845 0.0173726 0.00679136 0.00715132 1 0 +0.389 0.0262074 0.0765676 0.00740518 1 0 +0.385725 0.0576259 0.0716836 0.00494275 1 0 +0.369562 0.0489367 0.0886258 0.00875958 1 0 +0.389473 0.125925 0.0240515 0.00707116 1 0 +0.384935 0.114117 0.0640126 0.00517591 1 0 +0.358025 0.129177 0.065102 0.00532288 1 0 +0.380652 0.114116 0.0493804 0.00640348 1 0 +0.0925978 0.185847 0.176284 0.00817487 1 0 +0.103581 0.173693 0.138901 0.00526595 1 0 +0.169338 0.185191 0.144271 0.0124284 1 0 +0.190417 0.142733 0.141853 0.00703344 1 0 +0.0145455 0.0827114 0.192269 0.00671582 1 0 +0.0201515 0.118411 0.19059 0.00538755 1 0 +0.0447295 0.10804 0.190066 0.00589401 1 0 +0.372658 0.186009 0.0342008 0.00586944 1 0 +0.341111 0.193211 0.0297643 0.00725038 1 0 +0.291381 0.145337 0.0695375 0.00537388 1 0 +0.262009 0.149087 0.0586692 0.0063418 1 0 +0.272672 0.157725 0.0471531 0.00678475 1 0 +0.177522 0.191702 0.0151077 0.00871947 1 0 +0.195951 0.192488 0.042442 0.0075691 1 0 +0.223972 0.0441119 0.066088 0.00943428 1 0 +0.216549 0.0621694 0.0881533 0.00736065 1 0 +0.248009 0.0605968 0.0849173 0.00751538 1 0 +0.222064 0.056697 0.058959 0.005155 1 0 +0.204322 0.00865878 0.149084 0.00905007 1 0 +0.0179466 0.0646345 0.0477057 0.00580657 1 0 +0.0279603 0.0870887 0.0391257 0.00507917 1 0 +0.0583118 0.138382 0.13237 0.0075169 1 0 +0.0310013 0.14362 0.151161 0.00524737 1 0 +0.0781885 0.0332649 0.0108671 0.00532103 1 0 +0.0690375 0.0649554 0.0147177 0.00651158 1 0 +0.104554 0.0463637 0.00797568 0.00810448 1 0 +0.291221 0.19255 0.0657017 0.00699412 1 0 +0.314641 0.193615 0.0665849 0.00653856 1 0 +0.322512 0.174957 0.0655721 0.0117791 1 0 +0.246221 0.191961 0.0279806 0.00505252 1 0 +0.26026 0.192866 0.0414315 0.0063595 1 0 +0.226439 0.194878 0.0503368 0.0053088 1 0 +0.381734 0.0627377 0.129848 0.00505232 1 0 +0.359458 0.0626474 0.123117 0.00536539 1 0 +0.368474 0.0544648 0.140028 0.00583783 1 0 +0.371952 0.186986 0.0786659 0.00541608 1 0 +0.373149 0.188049 0.110842 0.00521248 1 0 +0.348782 0.174456 0.0904933 0.0109357 1 0 +0.370339 0.0202183 0.145735 0.00762078 1 0 +0.353047 0.036823 0.134046 0.00654898 1 0 +0.173665 0.0688826 0.112392 0.00546214 1 0 +0.00601937 0.0813715 0.168923 0.00635341 1 0 +0.00602729 0.0869847 0.136085 0.00609493 1 0 +0.0184638 0.0953948 0.160977 0.0049687 1 0 +0.0871605 0.0551548 0.11149 0.00512355 1 0 +0.121243 0.0613418 0.116861 0.00674977 1 0 +0.0104101 0.0605892 0.180056 0.00544866 1 0 +0.0264487 0.0799943 0.162158 0.00601263 1 0 +0.0255322 0.0594446 0.155259 0.00734574 1 0 +0.236368 0.0362734 0.192409 0.00762552 1 0 +0.220529 0.0553839 0.190136 0.00524858 1 0 +0.251545 0.0452632 0.169149 0.00625398 1 0 +0.258667 0.0495091 0.076911 0.0063627 1 0 +0.0844778 0.0171772 0.0142944 0.00539746 1 0 +0.104986 0.0472937 0.020959 0.00496815 1 0 +0.234017 0.123122 0.165273 0.00636487 1 0 +0.215883 0.114482 0.137351 0.00607392 1 0 +0.255968 0.107054 0.134688 0.00618773 1 0 +0.242199 0.129011 0.143615 0.00661564 1 0 +0.254621 0.0204878 0.0150463 0.0063895 1 0 +0.260334 0.046401 0.00768789 0.00812479 1 0 +0.247893 0.0462226 0.0316245 0.00700558 1 0 +0.221421 0.134262 0.0282345 0.00655456 1 0 +0.180824 0.0228593 0.187996 0.00638733 1 0 +0.213041 0.0133159 0.19237 0.00780685 1 0 +0.194998 0.0338827 0.189502 0.0106018 1 0 +0.272619 0.189084 0.0698362 0.00594189 1 0 +0.302616 0.154225 0.0769161 0.00934855 1 0 +0.264127 0.154008 0.0797161 0.00829343 1 0 +0.269698 0.168629 0.0506287 0.0050404 1 0 +0.254899 0.0132178 0.0246547 0.00566253 1 0 +0.262937 0.0114562 0.0492763 0.00654525 1 0 +0.0232101 0.185494 0.094236 0.00642635 1 0 +0.0189702 0.190582 0.104327 0.00564361 1 0 +0.394705 0.0831134 0.171396 0.0056949 1 0 +0.384951 0.0759243 0.143572 0.00518763 1 0 +0.391195 0.109337 0.137347 0.00919292 1 0 +0.377732 0.0936121 0.147022 0.00939294 1 0 +0.0611988 0.0576605 0.0342762 0.00552004 1 0 +0.132409 0.152308 0.0376554 0.00724506 1 0 +0.257384 0.0913827 0.0213397 0.00792487 1 0 +0.273538 0.105342 0.0198757 0.0126039 1 0 +0.249536 0.115752 0.00706078 0.00712634 1 0 +0.273484 0.0906132 0.0559532 0.00589563 1 0 +0.00905115 0.0140949 0.160302 0.00928439 1 0 +0.195107 0.12811 0.194695 0.00562069 1 0 +0.170111 0.136759 0.187004 0.00585134 1 0 +0.17994 0.131085 0.156562 0.00832547 1 0 +0.164357 0.0123431 0.173025 0.00531164 1 0 +0.17351 0.0145689 0.152659 0.00496858 1 0 +0.0701793 0.171675 0.188997 0.00495742 1 0 +0.0748005 0.143099 0.15452 0.00690164 1 0 +0.374541 0.189631 0.155372 0.00811457 1 0 +0.380585 0.0188822 0.0672238 0.00714736 1 0 +0.382836 0.0435632 0.061077 0.00516762 1 0 +0.364107 0.0429161 0.0770429 0.00538829 1 0 +0.37229 0.041059 0.0512871 0.00493193 1 0 +0.355692 0.0860349 0.0875009 0.00631317 1 0 +0.349618 0.116454 0.0780361 0.00525851 1 0 +0.364768 0.0981785 0.0596607 0.00789483 1 0 +0.179002 0.0116066 0.0194827 0.00589101 1 0 +0.216723 0.0137037 0.0181508 0.00529219 1 0 +0.198619 0.00990967 0.0413414 0.0101715 1 0 +0.186634 0.0338454 0.044727 0.00608557 1 0 +0.4 0.03 0.03 0.01 2 0 +0 0.03 0.03 0.01 2 0 +0.4 0.03 0.17 0.01 2 0 +0 0.03 0.17 0.01 2 0 +0.4 0.17 0.03 0.01 2 0 +0 0.17 0.03 0.01 2 0 +0.4 0.17 0.17 0.01 2 0 +0 0.17 0.17 0.01 2 0 +0.02 0.03 0.03 0.01 2 0 +0.04 0.03 0.03 0.01 2 0 +0.06 0.03 0.03 0.01 2 0 +0.08 0.03 0.03 0.01 2 0 +0.1 0.03 0.03 0.01 2 0 +0.12 0.03 0.03 0.01 2 0 +0.14 0.03 0.03 0.01 2 0 +0.16 0.03 0.03 0.01 2 0 +0.18 0.03 0.03 0.01 2 0 +0.2 0.03 0.03 0.01 2 0 +0.22 0.03 0.03 0.01 2 0 +0.24 0.03 0.03 0.01 2 0 +0.26 0.03 0.03 0.01 2 0 +0.28 0.03 0.03 0.01 2 0 +0.3 0.03 0.03 0.01 2 0 +0.32 0.03 0.03 0.01 2 0 +0.34 0.03 0.03 0.01 2 0 +0.36 0.03 0.03 0.01 2 0 +0.38 0.03 0.03 0.01 2 0 +0.02 0.03 0.17 0.01 2 0 +0.04 0.03 0.17 0.01 2 0 +0.06 0.03 0.17 0.01 2 0 +0.08 0.03 0.17 0.01 2 0 +0.1 0.03 0.17 0.01 2 0 +0.12 0.03 0.17 0.01 2 0 +0.14 0.03 0.17 0.01 2 0 +0.16 0.03 0.17 0.01 2 0 +0.18 0.03 0.17 0.01 2 0 +0.2 0.03 0.17 0.01 2 0 +0.22 0.03 0.17 0.01 2 0 +0.24 0.03 0.17 0.01 2 0 +0.26 0.03 0.17 0.01 2 0 +0.28 0.03 0.17 0.01 2 0 +0.3 0.03 0.17 0.01 2 0 +0.32 0.03 0.17 0.01 2 0 +0.34 0.03 0.17 0.01 2 0 +0.36 0.03 0.17 0.01 2 0 +0.38 0.03 0.17 0.01 2 0 +0.02 0.17 0.03 0.01 2 0 +0.04 0.17 0.03 0.01 2 0 +0.06 0.17 0.03 0.01 2 0 +0.08 0.17 0.03 0.01 2 0 +0.1 0.17 0.03 0.01 2 0 +0.12 0.17 0.03 0.01 2 0 +0.14 0.17 0.03 0.01 2 0 +0.16 0.17 0.03 0.01 2 0 +0.18 0.17 0.03 0.01 2 0 +0.2 0.17 0.03 0.01 2 0 +0.22 0.17 0.03 0.01 2 0 +0.24 0.17 0.03 0.01 2 0 +0.26 0.17 0.03 0.01 2 0 +0.28 0.17 0.03 0.01 2 0 +0.3 0.17 0.03 0.01 2 0 +0.32 0.17 0.03 0.01 2 0 +0.34 0.17 0.03 0.01 2 0 +0.36 0.17 0.03 0.01 2 0 +0.38 0.17 0.03 0.01 2 0 +0.02 0.17 0.17 0.01 2 0 +0.04 0.17 0.17 0.01 2 0 +0.06 0.17 0.17 0.01 2 0 +0.08 0.17 0.17 0.01 2 0 +0.1 0.17 0.17 0.01 2 0 +0.12 0.17 0.17 0.01 2 0 +0.14 0.17 0.17 0.01 2 0 +0.16 0.17 0.17 0.01 2 0 +0.18 0.17 0.17 0.01 2 0 +0.2 0.17 0.17 0.01 2 0 +0.22 0.17 0.17 0.01 2 0 +0.24 0.17 0.17 0.01 2 0 +0.26 0.17 0.17 0.01 2 0 +0.28 0.17 0.17 0.01 2 0 +0.3 0.17 0.17 0.01 2 0 +0.32 0.17 0.17 0.01 2 0 +0.34 0.17 0.17 0.01 2 0 +0.36 0.17 0.17 0.01 2 0 +0.38 0.17 0.17 0.01 2 0 +0.347522 0.0798775 0.189345 0.00542525 1 0 +0.0978862 0.0443223 0.157324 0.00881265 1 0 +0.392662 0.0990343 0.149296 0.00665244 1 0 +0.27 0.186275 0.156019 0.00669684 1 0 +0.356951 0.162534 0.138065 0.00842663 1 0 +0.282853 0.0239922 0.154694 0.00641626 1 0 +0.03 0.152792 0.169299 0.005642 1 0 +0.102813 0.149965 0.130103 0.0072981 1 0 +0.246362 0.124006 0.0878291 0.00776572 1 0 +0.134712 0.0782273 0.00486166 0.00518048 1 0 +0.145202 0.0420336 0.160607 0.00612731 1 0 +0.1331 0.191055 0.13096 0.00492461 1 0 +0.178568 0.0439534 0.0151785 0.00628492 1 0 +0.0369389 0.0587979 0.0362255 0.00831987 1 0 +0.324462 0.104118 0.060766 0.00656953 1 0 +0.242014 0.156239 0.0521801 0.00555918 1 0 +0.347445 0.0793337 0.0940084 0.00614759 1 0 +0.176037 0.0328676 0.119084 0.00524276 1 0 +0.203878 0.107329 0.173163 0.00650472 1 0 +0.275532 0.0453966 0.0313616 0.00608947 1 0 +0.26178 0.155021 0.0948649 0.00706965 1 0 +0.39 0.158236 0.172488 0.00563869 1 0 +0.105644 0.0790286 0.176016 0.00580547 1 0 +0.20005 0.0366356 0.0493124 0.0083647 1 0 +0.0185075 0.160973 0.130327 0.00501499 1 0 +0.300174 0.155686 0.0955826 0.00747518 1 0 +0.113642 0.0321054 0.151243 0.00816062 1 0 +0.0507885 0.15975 0.145708 0.00739224 1 0 +0.271194 0.0350138 0.151904 0.00620918 1 0 +0.201468 0.195442 0.130678 0.00505288 1 0 +0.188726 0.172934 0.0671716 0.0054132 1 0 +0.370376 0.156378 0.178879 0.00812185 1 0 +0.212651 0.051505 0.0344394 0.00698105 1 0 +0.0728915 0.0702816 0.0670054 0.00733578 1 0 +0.27 0.0222001 0.0413285 0.00700511 1 0 +0.209838 0.078913 0.158632 0.00692605 1 0 +0.0313396 0.169454 0.0453461 0.00637858 1 0 +0.380556 0.0338425 0.153157 0.00728474 1 0 +0.327886 0.154734 0.118663 0.00729678 1 0 +0.35 0.157495 0.0225033 0.00747683 1 0 +0.0649327 0.050875 0.110639 0.00808269 1 0 +0.152733 0.0857879 0.179473 0.00634748 1 0 +0.288763 0.147693 0.0341985 0.0094579 1 0 +0.11 0.0433635 0.0307431 0.00670737 1 0 +0.361312 0.0139639 0.152203 0.00512422 1 0 +0.220006 0.095739 0.171783 0.00552227 1 0 +0.339671 0.181996 0.18433 0.00656595 1 0 +0.267395 0.194784 0.109548 0.00565878 1 0 +0.308662 0.0124479 0.173343 0.00896394 1 0 +0.253811 0.0926573 0.109213 0.0106235 1 0 +0.392837 0.123744 0.178331 0.00689455 1 0 +0.220257 0.0991966 0.0646123 0.0143061 1 0 +0.149942 0.0119581 0.0950768 0.00568721 1 0 +0.021108 0.192638 0.0225377 0.00513947 1 0 +0.202093 0.121861 0.111158 0.00779051 1 0 +0.364844 0.0434614 0.0223083 0.00624303 1 0 +0.0816671 0.141038 0.0995708 0.00766147 1 0 +0.356446 0.160479 0.0471579 0.00994152 1 0 +0.0450777 0.18198 0.0420726 0.0054801 1 0 +0.359875 0.0893131 0.177108 0.00504329 1 0 +0.0983642 0.130066 0.170333 0.00574947 1 0 +0.0337275 0.0454853 0.184088 0.00520309 1 0 +0.260472 0.183642 0.0225082 0.00557063 1 0 +0.320423 0.153275 0.0290202 0.00675951 1 0 +0.142201 0.152845 0.144419 0.0122891 1 0 +0.236239 0.12794 0.17545 0.00511185 1 0 +0.253457 0.0513097 0.0973065 0.00889881 1 0 +0.292661 0.0883479 0.0835194 0.0103442 1 0 +0.29 0.019008 0.0211345 0.00730379 1 0 +0.206797 0.109716 0.0158543 0.00644442 1 0 +0.00485992 0.157468 0.0570503 0.00495699 1 0 +0.151719 0.0532074 0.109485 0.00820095 1 0 +0.207828 0.170978 0.14269 0.00686123 1 0 +0.207725 0.0420149 0.179824 0.00733632 1 0 +0.270731 0.14459 0.099749 0.0060311 1 0 +0.184316 0.122176 0.187476 0.00577481 1 0 +0.140387 0.0445274 0.0230222 0.00612092 1 0 +0.160369 0.1527 0.0415237 0.00674556 1 0 +0.300792 0.147145 0.15994 0.00929333 1 0 +0.0525256 0.0915602 0.179181 0.00589334 1 0 +0.129693 0.175565 0.0426627 0.00688964 1 0 +0.299318 0.0291523 0.0845295 0.00620838 1 0 +0.00962742 0.0170789 0.119014 0.00614714 1 0 +0.26608 0.0134887 0.0245473 0.0055224 1 0 +0.359626 0.145645 0.018462 0.00831556 1 0 +0.264127 0.048321 0.0226428 0.00742254 1 0 +0.296538 0.081742 0.100029 0.00744197 1 0 +0.345111 0.0473617 0.172251 0.00823776 1 0 +0.0868119 0.108331 0.0569456 0.00585534 1 0 +0.174245 0.131957 0.0322579 0.00524247 1 0 +0.12388 0.118941 0.0234175 0.00751806 1 0 +0.334458 0.0902555 0.188535 0.0100815 1 0 +0.195442 0.0683869 0.120811 0.00520101 1 0 +0.33765 0.0317681 0.0132248 0.00499725 1 0 +0.0139264 0.0661681 0.057607 0.00498931 1 0 +0.366444 0.176412 0.0907763 0.00508473 1 0 +0.251406 0.18165 0.175613 0.00552709 1 0 +0.0480855 0.0204338 0.179939 0.00598943 1 0 +0.342488 0.0409397 0.10765 0.00671023 1 0 +0.172764 0.118587 0.043688 0.00660758 1 0 +0.33 0.0380642 0.179743 0.0061229 1 0 +0.177923 0.116745 0.0232329 0.00649864 1 0 +0.0434834 0.147828 0.0215313 0.0051462 1 0 +0.130758 0.0473001 0.0975034 0.00695327 1 0 +0.272865 0.0192001 0.132966 0.00589323 1 0 +0.265019 0.150946 0.168352 0.00741564 1 0 +0.24418 0.0141703 0.0369479 0.00778553 1 0 +0.336748 0.170515 0.077577 0.0071526 1 0 +0.184562 0.178649 0.112849 0.00958207 1 0 +0.362317 0.110098 0.0861787 0.00522559 1 0 +0.157402 0.116345 0.119616 0.00810692 1 0 +0.208746 0.0329015 0.0825929 0.00742124 1 0 +0.0497274 0.0591842 0.0861661 0.00559058 1 0 +0.01 0.0185565 0.177554 0.00697095 1 0 +0.0728359 0.0892859 0.0249654 0.00991388 1 0 +0.00664008 0.149212 0.0165518 0.00717416 1 0 +0.214491 0.0193724 0.180289 0.00578476 1 0 +0.277068 0.191227 0.0809189 0.00619127 1 0 +0.0424533 0.104161 0.0716172 0.00799191 1 0 +0.31 0.0402308 0.0232237 0.00582996 1 0 +0.27754 0.184929 0.172891 0.00540447 1 0 +0.252694 0.158786 0.0735484 0.00554851 1 0 +0.3085 0.0988447 0.149485 0.0110854 1 0 +0.146358 0.0654075 0.176887 0.00852953 1 0 +0.15028 0.0850435 0.124902 0.0097613 1 0 +0.331544 0.04829 0.19056 0.00819168 1 0 +0.351251 0.101156 0.135048 0.00589803 1 0 +0.186782 0.165407 0.083592 0.00584857 1 0 +0.325097 0.166509 0.185233 0.00643767 1 0 +0.0918322 0.180427 0.0413606 0.00745013 1 0 +0.160433 0.0312632 0.152793 0.00725904 1 0 +0.17575 0.0105614 0.189975 0.00706232 1 0 +0.11 0.0178155 0.165291 0.00645094 1 0 +0.209597 0.178443 0.177733 0.00493965 1 0 +0.0474925 0.152649 0.157478 0.00674388 1 0 +0.0970484 0.187071 0.0748803 0.00561971 1 0 +0.127766 0.16398 0.184554 0.00756034 1 0 +0.138599 0.0762437 0.189021 0.00663846 1 0 +0.091639 0.0976758 0.118241 0.00951811 1 0 +0.37809 0.0472335 0.165234 0.0079822 1 0 +0.09 0.155653 0.166236 0.0078889 1 0 +0.222052 0.0741071 0.101548 0.00603959 1 0 +0.360558 0.156651 0.0895007 0.0104342 1 0 +0.0209121 0.156322 0.178298 0.00574117 1 0 +0.275085 0.0433949 0.176447 0.00565728 1 0 +0.132939 0.151075 0.159076 0.00513916 1 0 +0.338449 0.0869435 0.0180735 0.00838531 1 0 +0.255873 0.0180861 0.178487 0.00519845 1 0 +0.19 0.178801 0.0412054 0.0074073 1 0 +0.24197 0.150359 0.0663884 0.00600992 1 0 +0.366987 0.0483655 0.151525 0.00651083 1 0 +0.258204 0.0950625 0.0787748 0.0143746 1 0 +0.313156 0.0696813 0.0855558 0.00515003 1 0 +0.0224121 0.161022 0.017894 0.00526373 1 0 +0.378217 0.181987 0.0709204 0.0053618 1 0 +0.336389 0.151644 0.165116 0.00933514 1 0 +0.265528 0.039234 0.181998 0.00611775 1 0 +0.0774819 0.181246 0.0528093 0.0054311 1 0 +0.366662 0.0219018 0.158378 0.00565348 1 0 +0.159721 0.0359766 0.0451912 0.00632694 1 0 +0.153264 0.142666 0.0149768 0.00576333 1 0 +0.267688 0.0789297 0.0281816 0.00545798 1 0 +0.359133 0.041135 0.10209 0.00997302 1 0 +0.27 0.156557 0.179664 0.00615902 1 0 +0.157162 0.0928317 0.0624114 0.00930508 1 0 +0.24382 0.0682745 0.191382 0.00498369 1 0 +0.030824 0.119874 0.0824092 0.00589828 1 0 +0.0607368 0.0719771 0.121373 0.006561 1 0 +0.182555 0.0870453 0.131754 0.00758449 1 0 +0.249288 0.171861 0.00902276 0.00894261 1 0 +0.128277 0.140272 0.0478176 0.00904013 1 0 +0.39 0.0408851 0.179636 0.00764493 1 0 +0.344839 0.0246951 0.0561511 0.00517938 1 0 +0.374681 0.124486 0.022 0.00793192 1 0 +0.172293 0.0940976 0.0800211 0.00723459 1 0 +0.189986 0.15199 0.152309 0.0069387 1 0 +0.0808306 0.0847432 0.0775252 0.00801434 1 0 +0.35 0.0244296 0.180218 0.00534406 1 0 +0.29128 0.068629 0.089716 0.00854914 1 0 +0.370536 0.00948985 0.15318 0.00517375 1 0 +0.138975 0.029358 0.0148715 0.0051768 1 0 +0.229972 0.163942 0.156787 0.0076274 1 0 +0.0458068 0.0745883 0.105977 0.00800304 1 0 +0.387971 0.13337 0.0972578 0.005263 1 0 +0.267418 0.123144 0.103298 0.00717081 1 0 +0.289479 0.116327 0.166929 0.00597564 1 0 +0.0076274 0.0163704 0.0386944 0.00763753 1 0 +0.168509 0.182954 0.0239459 0.006639 1 0 +0.15 0.176936 0.0391289 0.00521332 1 0 +0.37 0.158822 0.0381978 0.00639058 1 0 +0.393535 0.192952 0.0828301 0.0070797 1 0 +0.220561 0.152147 0.0267704 0.0081512 1 0 +0.0723325 0.16431 0.182063 0.00538414 1 0 +0.334911 0.0744686 0.0606515 0.00522857 1 0 +0.235834 0.026813 0.0458702 0.00671452 1 0 +0.195435 0.0170165 0.162842 0.00551265 1 0 +0.269892 0.0607274 0.109486 0.00883171 1 0 +0.343501 0.0709899 0.0209709 0.0057976 1 0 +0.222355 0.0915619 0.123518 0.00627954 1 0 +0.14512 0.0672658 0.124313 0.00713699 1 0 +0.0789361 0.0122478 0.165528 0.00833777 1 0 +0.303859 0.120825 0.105216 0.00568384 1 0 +0.0668885 0.187978 0.0470427 0.0057157 1 0 +0.0474666 0.176518 0.156891 0.00643413 1 0 +0.0546208 0.00894538 0.0542628 0.00499468 1 0 +0.163881 0.165255 0.0156245 0.00562792 1 0 +0.264941 0.0352942 0.0524973 0.00526542 1 0 +0.0682807 0.131748 0.126804 0.00521273 1 0 +0.31 0.0414694 0.0383149 0.00734023 1 0 +0.293551 0.0411853 0.146535 0.00674033 1 0 +0.259371 0.101169 0.0300947 0.00535567 1 0 +0.0236936 0.19498 0.117863 0.00556218 1 0 +0.194553 0.0960323 0.00662746 0.00534121 1 0 +0.144031 0.00765253 0.0145084 0.00790903 1 0 +0.387286 0.0283025 0.183463 0.00540204 1 0 +0.190849 0.085665 0.042132 0.0058276 1 0 +0.337163 0.0493615 0.0297166 0.00957028 1 0 +0.0702921 0.0508055 0.0761078 0.00887061 1 0 +0.363204 0.079317 0.031058 0.00585507 1 0 +0.0239026 0.0171388 0.0732999 0.00558563 1 0 +0.31 0.171377 0.0410328 0.00495384 1 0 +0.242897 0.0431399 0.157876 0.00764433 1 0 +0.0232279 0.0625859 0.0569853 0.00499752 1 0 +0.308419 0.154837 0.0283139 0.00536722 1 0 +0.351596 0.0296902 0.0490809 0.00580207 1 0 +0.00724216 0.107479 0.173821 0.0052565 1 0 +0.320234 0.110807 0.157754 0.00760032 1 0 +0.10993 0.17569 0.0450187 0.00888243 1 0 +0.0556015 0.0166924 0.153296 0.0118053 1 0 +0.255539 0.0102831 0.109715 0.00796557 1 0 +0.146502 0.158052 0.0927089 0.00542645 1 0 +0.105747 0.0703886 0.184545 0.00523508 1 0 +0.32732 0.123906 0.044966 0.00633809 1 0 +0.167621 0.160837 0.179697 0.00536462 1 0 +0.156603 0.15324 0.169971 0.00710102 1 0 +0.308484 0.164311 0.181512 0.00539052 1 0 +0.0635922 0.181646 0.183508 0.00623565 1 0 +0.0488876 0.155546 0.17358 0.00734124 1 0 +0.05 0.161337 0.0228354 0.00504587 1 0 +0.333482 0.115593 0.0639115 0.00588445 1 0 +0.311958 0.0137062 0.111868 0.00852586 1 0 +0.07 0.0223539 0.18273 0.00591058 1 0 +0.00719179 0.070529 0.0416848 0.00606667 1 0 +0.101032 0.0195252 0.155612 0.00685467 1 0 +0.0254764 0.0434224 0.0366104 0.00593267 1 0 +0.0697503 0.14859 0.103677 0.00703194 1 0 +0.0853097 0.164886 0.121862 0.00635816 1 0 +0.261932 0.182529 0.103209 0.0052298 1 0 +0.287308 0.151721 0.168501 0.00726717 1 0 +0.0169782 0.15904 0.0805994 0.0050559 1 0 +0.14364 0.0674591 0.13787 0.0055179 1 0 +0.231317 0.120206 0.0514386 0.00936835 1 0 +0.0573314 0.122786 0.19436 0.00603971 1 0 +0.0281059 0.0184631 0.021398 0.00499663 1 0 +0.263591 0.0639061 0.174787 0.00656042 1 0 +0.0581201 0.184798 0.163389 0.00631669 1 0 +0.179217 0.044415 0.158281 0.00766895 1 0 +0.0731051 0.0881463 0.180648 0.00529527 1 0 +0.09 0.0359271 0.0194755 0.0049713 1 0 +0.23 0.157473 0.178684 0.0080108 1 0 +0.341498 0.194203 0.0117522 0.00650448 1 0 +0.391166 0.10602 0.0685237 0.0059928 1 0 +0.358029 0.0186959 0.108716 0.00560939 1 0 +0.329772 0.17071 0.121833 0.00909976 1 0 +0.335608 0.0574737 0.173582 0.0057023 1 0 +0.331246 0.159417 0.103862 0.0085865 1 0 +0.0563992 0.0102386 0.185024 0.00576244 1 0 +0.213145 0.0479381 0.170198 0.00519868 1 0 +0.0938822 0.161259 0.0546349 0.00524396 1 0 +0.264021 0.027745 0.0944503 0.00506959 1 0 +0.140928 0.0777212 0.176066 0.00495326 1 0 +0.161296 0.115551 0.0985767 0.00666958 1 0 +0.280353 0.130673 0.178957 0.00528118 1 0 +0.156516 0.136071 0.178792 0.00753372 1 0 +0.251158 0.0395676 0.121355 0.00764725 1 0 +0.326188 0.034959 0.0461858 0.00591718 1 0 +0.217008 0.055159 0.00923339 0.0062824 1 0 +0.24511 0.0178481 0.0217576 0.00554719 1 0 +0.192659 0.033758 0.130776 0.00667871 1 0 +0.134313 0.00674791 0.123773 0.00571604 1 0 +0.25894 0.00798155 0.142322 0.00527416 1 0 +0.0989189 0.138211 0.188624 0.00693057 1 0 +0.188807 0.154195 0.0314249 0.00814928 1 0 +0.0465999 0.0158036 0.0306726 0.00566998 1 0 +0.21 0.178166 0.16205 0.00516225 1 0 +0.103462 0.0747507 0.162656 0.00566988 1 0 +0.232491 0.186303 0.165265 0.00743411 1 0 +0.0948224 0.0458309 0.0355574 0.00719834 1 0 +0.137066 0.0453262 0.182442 0.00595659 1 0 +0.0606057 0.0509872 0.139644 0.00577137 1 0 +0.118997 0.0803134 0.0169132 0.00963772 1 0 +0.0648667 0.150087 0.129484 0.00620631 1 0 +0.13 0.168638 0.0183636 0.00540324 1 0 +0.328037 0.00816448 0.0789749 0.00836521 1 0 +0.261785 0.0674796 0.0860459 0.00765607 1 0 +0.265839 0.12281 0.0595413 0.0109387 1 0 +0.125226 0.108533 0.114202 0.00615113 1 0 +0.215342 0.155261 0.153166 0.0062645 1 0 +0.189995 0.0465725 0.0353405 0.00970266 1 0 +0.0885535 0.0561785 0.184883 0.00626981 1 0 +0.121939 0.169556 0.15395 0.00617321 1 0 +0.11 0.174743 0.0199283 0.00496436 1 0 +0.331276 0.142067 0.0574075 0.0114335 1 0 +0.0469555 0.0789318 0.0751397 0.00502226 1 0 +0.20313 0.095668 0.132944 0.0065325 1 0 +0.0264725 0.182658 0.163376 0.00525103 1 0 +0.0549236 0.0830398 0.0891248 0.0062925 1 0 +0.309388 0.0495779 0.0630322 0.00534495 1 0 +0.234442 0.182998 0.0358474 0.00529841 1 0 +0.206371 0.159944 0.0584285 0.00581264 1 0 +0.306901 0.125028 0.0251801 0.010106 1 0 +0.101812 0.0251804 0.129384 0.00802216 1 0 +0.369995 0.173609 0.181455 0.00562445 1 0 +0.349316 0.114621 0.138382 0.00538571 1 0 +0.330089 0.129989 0.143439 0.00887266 1 0 +0.115997 0.191979 0.0698904 0.00832214 1 0 +0.177899 0.0857562 0.112367 0.00852281 1 0 +0.072263 0.030518 0.111296 0.00596901 1 0 +0.352759 0.136596 0.0576396 0.00644424 1 0 +0.230748 0.0966892 0.17339 0.00538032 1 0 +0.0458287 0.0433015 0.158147 0.00874561 1 0 +0.0980732 0.171347 0.0552498 0.00569654 1 0 +0.305693 0.155839 0.127417 0.00593698 1 0 +0.335309 0.126327 0.0706772 0.00693509 1 0 +0.314188 0.143696 0.103088 0.0107034 1 0 +0.142819 0.173227 0.0858479 0.0112391 1 0 +0.31 0.0348089 0.180252 0.00510729 1 0 +0.15 0.0436508 0.0331577 0.00721381 1 0 +0.19796 0.0280548 0.148822 0.00873619 1 0 +0.287608 0.0440149 0.181191 0.00508972 1 0 +0.234242 0.0276053 0.0936894 0.00535566 1 0 +0.200447 0.0930969 0.0415927 0.00509391 1 0 +0.33 0.0395939 0.0215398 0.00504401 1 0 +0.108655 0.0741489 0.140021 0.00772278 1 0 +0.23 0.0336803 0.155098 0.00832 1 0 +0.05 0.0465513 0.0365519 0.0095876 1 0 +0.102633 0.158261 0.0806719 0.00520046 1 0 +0.30296 0.116641 0.142802 0.00871556 1 0 +0.146636 0.0086573 0.0811116 0.00646753 1 0 +0.248061 0.150212 0.159558 0.00759468 1 0 +0.0511742 0.126609 0.00468162 0.00493993 1 0 +0.160765 0.14026 0.105735 0.00630499 1 0 +0.203703 0.132457 0.169937 0.00620977 1 0 +0.287417 0.086656 0.114549 0.00635341 1 0 +0.0993595 0.191428 0.098834 0.00507766 1 0 +0.313624 0.145026 0.121706 0.00796975 1 0 +0.122538 0.15124 0.154183 0.00635657 1 0 +0.0604078 0.0152021 0.123458 0.00542286 1 0 +0.0223381 0.125728 0.135981 0.00585759 1 0 +0.315672 0.149159 0.0607302 0.00602484 1 0 +0.3332 0.164513 0.0123964 0.00965288 1 0 +0.37582 0.172889 0.0147571 0.00606757 1 0 +0.052553 0.0588091 0.110053 0.00604681 1 0 +0.140355 0.0965998 0.0802462 0.0122419 1 0 +0.0973348 0.00627142 0.0760289 0.00540746 1 0 +0.279178 0.0296219 0.185108 0.00513539 1 0 +0.00445371 0.0476518 0.120346 0.00494855 1 0 +0.0644919 0.191953 0.00716455 0.00588954 1 0 +0.0869399 0.0810823 0.10593 0.0102924 1 0 +0.340655 0.171237 0.140926 0.00589336 1 0 +0.237862 0.110315 0.0055321 0.00579299 1 0 +0.320733 0.173027 0.0827873 0.00563505 1 0 +0.144934 0.0258788 0.0882664 0.00526551 1 0 +0.101809 0.0163963 0.176675 0.00526074 1 0 +0.107666 0.0199858 0.0178321 0.00611792 1 0 +0.0498234 0.0372418 0.0210861 0.00511295 1 0 +0.110865 0.0949012 0.0684746 0.0052638 1 0 +0.38057 0.176806 0.0552643 0.00512958 1 0 +0.197033 0.114059 0.138863 0.0084076 1 0 +0.249194 0.109915 0.166191 0.00529529 1 0 +0.0862794 0.154898 0.17898 0.00540933 1 0 +0.27033 0.12531 0.0158457 0.00713187 1 0 +0.32917 0.0623616 0.0191554 0.00725251 1 0 +0.0834931 0.060607 0.0794537 0.00790831 1 0 +0.00791223 0.0940613 0.176638 0.00505783 1 0 +0.0787373 0.191473 0.104354 0.00497613 1 0 +0.354648 0.0809409 0.165555 0.00512381 1 0 +0.195343 0.0213989 0.0684254 0.00535459 1 0 +0.202165 0.116979 0.091119 0.011077 1 0 +0.183385 0.12038 0.10588 0.0066955 1 0 +0.100627 0.181147 0.150607 0.00892321 1 0 +0.184063 0.182109 0.0222756 0.00492635 1 0 +0.269945 0.0508076 0.0970386 0.00613515 1 0 +0.223061 0.0442632 0.164099 0.00573653 1 0 +0.379559 0.149125 0.1674 0.00558254 1 0 +0.166691 0.0981379 0.126571 0.0102523 1 0 +0.146453 0.178477 0.0102732 0.00546206 1 0 +0.250081 0.139208 0.105752 0.00666326 1 0 +0.0828624 0.049343 0.0991538 0.00917406 1 0 +0.114047 0.157075 0.0674288 0.00748147 1 0 +0.325864 0.0379841 0.102682 0.00760018 1 0 +0.0865448 0.0333339 0.154956 0.00674158 1 0 +0.179765 0.0194648 0.159028 0.00521305 1 0 +0.158754 0.0550893 0.0664386 0.00526478 1 0 +0.268519 0.0975019 0.126336 0.00843922 1 0 +0.0780719 0.0491939 0.0311838 0.00541201 1 0 +0.333234 0.187204 0.0596276 0.00554904 1 0 +0.209764 0.108087 0.146589 0.0067205 1 0 +0.184083 0.128664 0.130131 0.0123429 1 0 +0.286562 0.0698164 0.19439 0.0059234 1 0 +0.03 0.167223 0.158687 0.00535208 1 0 +0.0905275 0.0637841 0.115981 0.00517076 1 0 +0.0903216 0.0828469 0.0253702 0.00656234 1 0 +0.21 0.181115 0.0256308 0.00557701 1 0 +0.392997 0.123522 0.0881469 0.00713771 1 0 +0.27 0.177572 0.0390839 0.00548721 1 0 +0.341463 0.070823 0.130651 0.00521516 1 0 +0.0368654 0.0432709 0.0217789 0.00592252 1 0 +0.19 0.0188588 0.0323655 0.00515657 1 0 +0.11 0.0373125 0.019389 0.00609366 1 0 +0.102 0.0568128 0.166768 0.00737801 1 0 +0.37 0.16207 0.157379 0.0079496 1 0 +0.372113 0.105287 0.140051 0.00531965 1 0 +0.00599721 0.0650202 0.0947234 0.00597836 1 0 +0.24746 0.0529289 0.0668096 0.00629904 1 0 +0.0104065 0.136024 0.0294409 0.00654423 1 0 +0.231698 0.130363 0.18604 0.00628433 1 0 +0.29 0.0290446 0.044384 0.00754462 1 0 +0.028435 0.130973 0.0597217 0.00492464 1 0 +0.367429 0.109044 0.174486 0.00512261 1 0 +0.262766 0.0628471 0.0261988 0.00537886 1 0 +0.258736 0.100411 0.00488452 0.00494719 1 0 +0.321904 0.12017 0.085994 0.0113597 1 0 +0.262991 0.0494983 0.190873 0.00682598 1 0 +0.03 0.0189616 0.177748 0.00507591 1 0 +0.0587581 0.0997631 0.159554 0.00614663 1 0 +0.0638137 0.00656929 0.0414673 0.00611381 1 0 +0.11821 0.026267 0.0533042 0.00661779 1 0 +0.33 0.0415766 0.169184 0.005005 1 0 +0.345193 0.112954 0.163829 0.00498868 1 0 +0.392254 0.0472032 0.0473154 0.0054577 1 0 +0.0286494 0.11133 0.0760248 0.0049874 1 0 +0.27 0.0365696 0.0170355 0.00686145 1 0 +0.13948 0.0741076 0.165953 0.00537703 1 0 +0.152228 0.180271 0.0220904 0.00511481 1 0 +0.11 0.159652 0.175278 0.0053276 1 0 +0.130342 0.0749803 0.127891 0.00794283 1 0 +0.37 0.0455926 0.178018 0.007069 1 0 +0.174904 0.15232 0.159484 0.0097671 1 0 +0.258567 0.142183 0.0704392 0.00773136 1 0 +0.221933 0.0370084 0.0950801 0.00995903 1 0 +0.273527 0.0201031 0.0938819 0.00499655 1 0 +0.374264 0.12754 0.119256 0.00628468 1 0 +0.118483 0.012984 0.0645129 0.00630568 1 0 +0.241122 0.0152128 0.15513 0.00617623 1 0 +0.0291186 0.0337872 0.0429776 0.00617698 1 0 +0.333775 0.066065 0.0826432 0.00498637 1 0 +0.37 0.181074 0.0233665 0.00632907 1 0 +0.223202 0.18819 0.0923402 0.00498807 1 0 +0.346015 0.0269364 0.134661 0.00559914 1 0 +0.07 0.0416623 0.178629 0.00762019 1 0 +0.280236 0.0510122 0.119443 0.00670719 1 0 +0.261681 0.0470373 0.126408 0.005187 1 0 +0.307872 0.125585 0.172237 0.00807778 1 0 +0.37691 0.0493025 0.0547169 0.00512084 1 0 +0.199809 0.142591 0.11782 0.00520728 1 0 +0.290256 0.175235 0.122551 0.00533184 1 0 +0.359318 0.162337 0.0625626 0.00527138 1 0 +0.266254 0.130207 0.0276945 0.00581197 1 0 +0.216126 0.102205 0.136049 0.00568307 1 0 +0.305954 0.165733 0.085845 0.00559505 1 0 +0.194504 0.103429 0.179534 0.00548175 1 0 +0.0595778 0.17011 0.0454706 0.00547673 1 0 +0.120786 0.149805 0.0197906 0.00838019 1 0 +0.226249 0.0744244 0.115243 0.00828741 1 0 +0.229092 0.0811134 0.0322316 0.0060468 1 0 +0.0234179 0.155289 0.0264939 0.00512035 1 0 +0.0953025 0.0401542 0.182022 0.00642255 1 0 +0.24542 0.121912 0.0445354 0.0058618 1 0 +0.250948 0.153357 0.027401 0.00830815 1 0 +0.14512 0.0114577 0.184325 0.00537055 1 0 +0.229495 0.0740694 0.0408994 0.00512964 1 0 +0.245814 0.0900784 0.0454943 0.00532297 1 0 +0.3414 0.125404 0.0907135 0.00854917 1 0 +0.0809721 0.133603 0.142928 0.00715165 1 0 +0.01 0.0400683 0.175232 0.00512425 1 0 +0.175861 0.00960354 0.101807 0.0076149 1 0 +0.110409 0.0662879 0.0110968 0.00780684 1 0 +0.068644 0.126408 0.0158185 0.00551588 1 0 +0.341462 0.0416071 0.0919578 0.00625069 1 0 +0.237534 0.0419936 0.0999316 0.00501946 1 0 +0.0621255 0.0923219 0.0886774 0.0050362 1 0 +0.219184 0.0495255 0.0811933 0.00731089 1 0 +0.387963 0.0545049 0.0624941 0.00501702 1 0 +0.139508 0.0367064 0.0988393 0.00498963 1 0 +0.11 0.0189772 0.0326292 0.00511339 1 0 +0.266344 0.0950372 0.139461 0.00509116 1 0 +0.307471 0.185989 0.0714464 0.00500287 1 0 +0.181953 0.0950166 0.00545282 0.0055711 1 0 +0.118734 0.0115042 0.0312841 0.00512618 1 0 +0.15 0.160336 0.0231105 0.00551957 1 0 +0.22619 0.0202691 0.0185718 0.00623608 1 0 +0.276323 0.0724613 0.123032 0.00660369 1 0 +0.220569 0.076601 0.172806 0.00492155 1 0 +0.100232 0.131322 0.143208 0.00639902 1 0 +0.394782 0.0236983 0.13031 0.00492271 1 0 +0.382281 0.0185517 0.0169469 0.00592855 1 0 +0.22683 0.0322293 0.0166972 0.00511889 1 0 +0.0489981 0.170903 0.18261 0.0055178 1 0 +0.349467 0.075473 0.0104567 0.00518068 1 0 +0.139219 0.109415 0.0104712 0.00569116 1 0 +0.240757 0.104913 0.175124 0.00559638 1 0 +0.0937754 0.0174482 0.140284 0.00660599 1 0 +0.218027 0.187273 0.070854 0.00499785 1 0 +0.246405 0.0970869 0.0365274 0.00607323 1 0 +0.287901 0.18408 0.0332955 0.00647807 1 0 +0.376126 0.100972 0.0352068 0.00496434 1 0 +0.209984 0.155755 0.164863 0.00661049 1 0 +0.0273352 0.180617 0.179068 0.00577195 1 0 +0.175007 0.0219339 0.0183149 0.00505085 1 0 +0.0228989 0.0946957 0.170487 0.00554807 1 0 +0.0314103 0.0460342 0.165143 0.00751195 1 0 +0.370248 0.0450066 0.0410144 0.00626097 1 0 +0.27816 0.0123399 0.0452198 0.00530145 1 0 +0.39 0.176178 0.18115 0.00620133 1 0 +0.256466 0.186298 0.11359 0.00675823 1 0 +0.357651 0.140973 0.158994 0.00677486 1 0 +0.33 0.176801 0.161228 0.00494019 1 0 +0.28369 0.16306 0.0484567 0.00552614 1 0 +0.209807 0.0179606 0.135153 0.00640212 1 0 +0.374754 0.113029 0.0121099 0.00503982 1 0 +0.0743218 0.0220835 0.0127212 0.00599074 1 0 +0.18094 0.170539 0.128215 0.00762202 1 0 +0.09 0.0187735 0.176421 0.00634823 1 0 +0.05 0.181269 0.0284853 0.00514207 1 0 +0.382123 0.104338 0.155546 0.00499356 1 0 +0.32331 0.0201479 0.0853821 0.0060223 1 0 +0.21477 0.166049 0.0164699 0.00503424 1 0 +0.360868 0.0609557 0.111799 0.00616562 1 0 +0.18085 0.0321406 0.14269 0.00675333 1 0 +0.376028 0.1525 0.195405 0.00500279 1 0 +0.23225 0.024593 0.184328 0.00716327 1 0 +0.101793 0.10075 0.0728653 0.00600643 1 0 +0.12091 0.179599 0.0179687 0.00541848 1 0 +0.0640294 0.159891 0.0402461 0.00494685 1 0 +0.154054 0.185061 0.159621 0.00923332 1 0 +0.132175 0.0181216 0.17936 0.0067317 1 0 +0.23 0.180445 0.0248208 0.00535967 1 0 +0.11 0.15442 0.0350109 0.00917913 1 0 +0.156521 0.0697386 0.118669 0.00582222 1 0 +0.309131 0.137733 0.184422 0.00778363 1 0 +0.366573 0.021133 0.181724 0.00603386 1 0 +0.358072 0.127979 0.121924 0.005249 1 0 +0.12069 0.108055 0.166835 0.00967485 1 0 +0.35182 0.095704 0.174893 0.00547524 1 0 +0.27 0.167186 0.0407491 0.00494861 1 0 +0.13 0.0433101 0.031196 0.00669101 1 0 +0.0081316 0.173136 0.0422839 0.00506157 1 0 +0.122184 0.140332 0.0098268 0.00543885 1 0 +0.0236819 0.0581298 0.119657 0.0051633 1 0 +0.07 0.0351198 0.155468 0.00836856 1 0 +0.10544 0.111304 0.04461 0.00622339 1 0 +0.31127 0.0701473 0.114328 0.0058661 1 0 +0.354215 0.0480357 0.0842499 0.00633501 1 0 +0.0804859 0.0484113 0.171363 0.0068126 1 0 +0.172237 0.115855 0.106807 0.00537165 1 0 +0.0455084 0.160394 0.0412336 0.00577368 1 0 +0.202462 0.0695508 0.160314 0.00511081 1 0 +0.127363 0.189716 0.0842867 0.00989146 1 0 +0.0967372 0.10246 0.175354 0.00649448 1 0 +0.29834 0.054986 0.0621569 0.00507099 1 0 +0.339449 0.0122872 0.0219697 0.00582351 1 0 +0.339266 0.172422 0.0589719 0.00640596 1 0 +0.33 0.0206983 0.181778 0.00636567 1 0 +0.266732 0.100053 0.0608677 0.00607784 1 0 +0.380859 0.0514983 0.1034 0.00560041 1 0 +0.335886 0.0528022 0.0844396 0.00499071 1 0 +0.35 0.0212422 0.0391845 0.00539966 1 0 +0.381987 0.173518 0.0456676 0.00511331 1 0 +0.271273 0.161918 0.0586698 0.00555117 1 0 +0.0925863 0.183631 0.132173 0.00543901 1 0 +0.258335 0.12838 0.125804 0.00532049 1 0 +0.332545 0.139745 0.1934 0.00566671 1 0 +0.269576 0.0853788 0.0088008 0.00833053 1 0 +0.230941 0.017388 0.16918 0.00554991 1 0 +0.339962 0.0828962 0.156992 0.00606685 1 0 +0.289594 0.099299 0.0979231 0.0057516 1 0 +0.140955 0.193737 0.0954806 0.00640935 1 0 +0.118922 0.195137 0.118187 0.00530406 1 0 +0.0591231 0.0998853 0.0219789 0.00663882 1 0 +0.319005 0.15718 0.158883 0.00699831 1 0 +0.127877 0.155594 0.057736 0.00769139 1 0 +0.318294 0.16287 0.129378 0.0064074 1 0 +0.36565 0.0218976 0.0162911 0.00645217 1 0 +0.00566143 0.076938 0.179819 0.00541591 1 0 +0.293948 0.0438732 0.017724 0.00529041 1 0 +0.0624557 0.0779759 0.0326174 0.00502421 1 0 +0.376266 0.00748401 0.0779648 0.00547678 1 0 +0.0682995 0.172801 0.154954 0.00740985 1 0 +0.0594805 0.127945 0.0586856 0.00525666 1 0 +0.347452 0.0663356 0.119742 0.0069306 1 0 +0.184045 0.0231649 0.0428077 0.00507048 1 0 +0.316409 0.118177 0.0351041 0.0052505 1 0 +0.351999 0.141379 0.092419 0.00731385 1 0 +0.09 0.0212043 0.159808 0.00506732 1 0 +0.0585221 0.017857 0.0399701 0.00578099 1 0 +0.155197 0.00481838 0.142725 0.00492107 1 0 +0.30678 0.0953303 0.0300757 0.00624598 1 0 +0.393444 0.136023 0.0439173 0.00510526 1 0 +0.125353 0.0507762 0.110233 0.00638261 1 0 +0.351042 0.145311 0.0675031 0.00682948 1 0 +0.254195 0.0381235 0.0413503 0.00511698 1 0 +0.01 0.180899 0.0322408 0.00495998 1 0 +0.0483639 0.0473283 0.0625009 0.00500291 1 0 +0.194708 0.0966212 0.170253 0.00499734 1 0 +0.187264 0.183601 0.170186 0.00542055 1 0 +0.222129 0.115037 0.148178 0.00643828 1 0 +0.0430709 0.0295178 0.149118 0.0066055 1 0 +0.128507 0.0100832 0.0801202 0.00561299 1 0 +0.233596 0.122146 0.0371903 0.00519093 1 0 +0.0971237 0.130173 0.0772085 0.00719146 1 0 +0.00891847 0.0214366 0.052843 0.00603167 1 0 +0.333253 0.0299159 0.0869719 0.00556045 1 0 +0.139007 0.146844 0.179856 0.00804221 1 0 +0.0889269 0.120896 0.17275 0.0069551 1 0 +0.076424 0.0383375 0.0433011 0.00610034 1 0 +0.115177 0.0947907 0.177942 0.00848365 1 0 +0.0519639 0.0337147 0.182047 0.00495052 1 0 +0.280811 0.147314 0.124882 0.00644796 1 0 +0.175351 0.0993935 0.0629356 0.00616285 1 0 +0.232532 0.140507 0.00982939 0.00553772 1 0 +0.3443 0.0707592 0.0655399 0.00521791 1 0 +0.148259 0.169095 0.147212 0.005276 1 0 +0.0806145 0.0696028 0.148441 0.00697314 1 0 +0.377057 0.0938253 0.0614691 0.00526754 1 0 +0.176727 0.163044 0.0167244 0.00534095 1 0 +0.3747 0.183974 0.167391 0.00517088 1 0 +0.116467 0.129337 0.00737408 0.00584774 1 0 +0.265312 0.0469076 0.162889 0.00895888 1 0 +0.210312 0.136884 0.0516481 0.0050044 1 0 +0.382313 0.188611 0.0781563 0.00508364 1 0 +0.256426 0.156039 0.0443755 0.00729046 1 0 +0.110251 0.153492 0.110863 0.00841988 1 0 +0.153164 0.18907 0.0510826 0.00505197 1 0 +0.135832 0.105832 0.171815 0.00641906 1 0 +0.215764 0.0305541 0.0723022 0.00525438 1 0 +0.310033 0.142274 0.171375 0.00606043 1 0 +0.15 0.164059 0.0410785 0.00606329 1 0 +0.372926 0.0870483 0.194764 0.00526985 1 0 +0.277151 0.166751 0.184336 0.00497332 1 0 +0.233981 0.116093 0.174304 0.00507903 1 0 +0.170618 0.0208526 0.19461 0.00533624 1 0 +0.31 0.176127 0.0200096 0.00540617 1 0 +0.101048 0.0762601 0.0845436 0.00718604 1 0 +0.0542891 0.0249088 0.109028 0.00805957 1 0 +0.124952 0.117632 0.0917484 0.00791336 1 0 +0.347463 0.0241088 0.106902 0.00563985 1 0 +0.0329361 0.150373 0.120633 0.00579599 1 0 +0.0245338 0.0119199 0.158341 0.00647275 1 0 +0.172233 0.146717 0.0576079 0.00512017 1 0 +0.0883143 0.156751 0.100443 0.00659294 1 0 +0.214205 0.160449 0.179897 0.00492494 1 0 +0.135415 0.0877608 0.169698 0.00815181 1 0 +0.170844 0.0282051 0.128725 0.00611538 1 0 +0.153428 0.110489 0.144807 0.00597814 1 0 +0.193851 0.0220132 0.0994787 0.00552583 1 0 +0.323255 0.167198 0.0474381 0.00795909 1 0 +0.179707 0.164925 0.0747548 0.005482 1 0 +0.0511664 0.13624 0.0541774 0.00719237 1 0 +0.338068 0.176277 0.153188 0.00581013 1 0 +0.364839 0.0856002 0.0571979 0.00492247 1 0 +0.245009 0.190734 0.148366 0.00500709 1 0 +0.208114 0.0832033 0.0596652 0.00637513 1 0 +0.295831 0.116129 0.0184326 0.0056183 1 0 +0.34496 0.165245 0.132476 0.00507771 1 0 +0.0531677 0.18774 0.096376 0.00534642 1 0 +0.382862 0.132041 0.0397941 0.00514877 1 0 +0.322868 0.132797 0.0297669 0.00680391 1 0 +0.345811 0.139817 0.0350729 0.00615701 1 0 +0.0995122 0.174468 0.0147702 0.00587915 1 0 +0.24042 0.0930751 0.0560819 0.0061489 1 0 +0.35639 0.0526969 0.14479 0.00524043 1 0 +0.288497 0.168296 0.150794 0.0110711 1 0 +0.337262 0.163029 0.0517847 0.00558953 1 0 +0.261944 0.0256295 0.116649 0.00566127 1 0 +0.251957 0.095516 0.0101884 0.0049549 1 0 +0.0698836 0.0221234 0.0433974 0.00682977 1 0 +0.0137647 0.149326 0.195047 0.00556836 1 0 +0.250432 0.074121 0.109003 0.00821935 1 0 +0.179564 0.047218 0.171751 0.006094 1 0 +0.0331699 0.0224626 0.0413483 0.00523962 1 0 +0.329789 0.137961 0.0388322 0.00571604 1 0 +0.249914 0.140583 0.151655 0.00499878 1 0 +0.319055 0.0264271 0.106152 0.0062552 1 0 +0.0386856 0.0573807 0.156485 0.00602484 1 0 +0.35 0.177304 0.0190771 0.00651221 1 0 +0.255732 0.0438077 0.179543 0.00496818 1 0 +0.23 0.01902 0.0359562 0.00600114 1 0 +0.29 0.0182661 0.035311 0.00630612 1 0 +0.190535 0.0462154 0.167995 0.00554567 1 0 +0.226768 0.173457 0.0439585 0.00508207 1 0 +0.195773 0.086384 0.0930442 0.00811178 1 0 +0.25161 0.0879304 0.126099 0.0067339 1 0 +0.244441 0.0160123 0.172747 0.00493063 1 0 +0.043296 0.151548 0.0918601 0.00625959 1 0 +0.307593 0.0272495 0.0769788 0.0051061 1 0 +0.00725937 0.181249 0.160883 0.00619715 1 0 +0.175231 0.00533157 0.147991 0.00552298 1 0 +0.0631338 0.0584774 0.0477833 0.00496594 1 0 +0.0472304 0.0284223 0.0444217 0.00620969 1 0 +0.0937202 0.0200306 0.00855072 0.00585216 1 0 +0.00876345 0.191816 0.153383 0.00684825 1 0 +0.317627 0.0683682 0.0252238 0.00535805 1 0 +0.0717525 0.130556 0.107098 0.00817232 1 0 +0.146068 0.0287704 0.0437635 0.00509176 1 0 +0.274603 0.0963645 0.03542 0.00537813 1 0 +0.190315 0.0552909 0.0503652 0.00498143 1 0 +0.34227 0.0424943 0.128558 0.00680922 1 0 +0.224546 0.0717756 0.0894152 0.0052022 1 0 +0.16485 0.144163 0.116683 0.00601526 1 0 +0.31 0.0264071 0.159299 0.00508062 1 0 +0.337475 0.0525507 0.117738 0.00655369 1 0 +0.223739 0.0170824 0.0614024 0.00560669 1 0 +0.299231 0.0440644 0.161454 0.00647538 1 0 +0.0335832 0.0789404 0.0370472 0.00503675 1 0 +0.167133 0.07712 0.119504 0.00701539 1 0 +0.222646 0.152381 0.0815839 0.00666112 1 0 +0.256102 0.122997 0.109313 0.00494815 1 0 +0.217144 0.136513 0.0915481 0.0051836 1 0 +0.248134 0.0112451 0.0967474 0.00654313 1 0 +0.13 0.024322 0.0192746 0.00572495 1 0 +0.269567 0.00820065 0.0760283 0.00789579 1 0 +0.221619 0.0680395 0.0552225 0.00679536 1 0 +0.33895 0.13243 0.132953 0.00507055 1 0 +0.119456 0.0226494 0.183848 0.00514542 1 0 +0.31 0.16474 0.156743 0.00495294 1 0 +0.333612 0.104095 0.0213189 0.0057855 1 0 +0.157879 0.174185 0.130036 0.00890416 1 0 +0.300888 0.122424 0.0414241 0.00732578 1 0 +0.365243 0.103443 0.182958 0.00526612 1 0 +0.052821 0.0599626 0.150999 0.00601089 1 0 +0.389875 0.0408276 0.0233581 0.00608927 1 0 +0.00600313 0.18337 0.178389 0.00501887 1 0 +0.34897 0.111007 0.128003 0.00550693 1 0 +0.294398 0.161156 0.0182559 0.00573325 1 0 +0.181712 0.0226184 0.108846 0.00628206 1 0 +0.232003 0.0810735 0.0908776 0.00680589 1 0 +0.192502 0.0130733 0.0573592 0.00506112 1 0 +0.0286125 0.0500802 0.0806136 0.00523027 1 0 +0.0493994 0.121428 0.149163 0.00518747 1 0 +0.08476 0.158288 0.0380256 0.00497445 1 0 +0.0951523 0.159668 0.111227 0.0065057 1 0 +0.112207 0.191503 0.168551 0.00718012 1 0 +0.114836 0.191245 0.149378 0.00508199 1 0 +0.0904455 0.160321 0.133842 0.00686734 1 0 +0.118465 0.0969585 0.152407 0.00528396 1 0 +0.325616 0.136625 0.131179 0.00570646 1 0 +0.170373 0.171284 0.0552724 0.00506681 1 0 +0.279831 0.0253967 0.0541884 0.00539711 1 0 +0.204186 0.100462 0.0951556 0.00604524 1 0 +0.241027 0.140553 0.188586 0.00632563 1 0 +0.01 0.0305452 0.158786 0.00503532 1 0 +0.154831 0.106833 0.0213084 0.00502078 1 0 +0.356808 0.00512252 0.0769753 0.00500051 1 0 +0.148342 0.0586842 0.18959 0.0057163 1 0 +0.171288 0.0323468 0.182402 0.00533673 1 0 +0.277129 0.149936 0.137827 0.00562111 1 0 +0.172076 0.136966 0.111224 0.00555198 1 0 +0.285579 0.118203 0.066372 0.00625782 1 0 +0.0164121 0.194575 0.0894871 0.00544476 1 0 +0.375279 0.142649 0.0228584 0.00808403 1 0 +0.15 0.0391444 0.176967 0.00523655 1 0 +0.154845 0.0844059 0.0919995 0.00855174 1 0 +0.114847 0.0530955 0.17119 0.00507901 1 0 +0.161215 0.0463565 0.158803 0.00658422 1 0 +0.244294 0.0749789 0.127429 0.00693151 1 0 +0.201621 0.169863 0.056274 0.00539321 1 0 +0.115581 0.0706966 0.056733 0.00508092 1 0 +0.106464 0.112311 0.0671209 0.00524916 1 0 +0.291234 0.0553339 0.0958209 0.00526472 1 0 +0.316176 0.00764799 0.0855426 0.0052024 1 0 +0.275018 0.050031 0.0102281 0.0060469 1 0 +0.150034 0.0206415 0.178561 0.0061308 1 0 +0.263767 0.191992 0.0760477 0.00525599 1 0 +0.130293 0.116906 0.0351914 0.00604257 1 0 +0.186688 0.0872507 0.0579731 0.0061373 1 0 +0.230104 0.106871 0.03155 0.00613509 1 0 +0.173996 0.0709171 0.0506331 0.00512285 1 0 +0.0207845 0.0177262 0.0390978 0.0052981 1 0 +0.0211481 0.0258574 0.0156478 0.00498216 1 0 +0.332627 0.115789 0.15882 0.00579958 1 0 +0.263268 0.157899 0.0664981 0.00551224 1 0 +0.337952 0.0433085 0.15956 0.006886 1 0 +0.34815 0.131308 0.0763117 0.00704923 1 0 +0.115767 0.0923186 0.0853965 0.00514234 1 0 +0.39 0.0189152 0.0358037 0.00601733 1 0 +0.136347 0.131356 0.149812 0.00898847 1 0 +0.34661 0.147226 0.136929 0.00586783 1 0 +0.295703 0.0581534 0.0112684 0.00518624 1 0 +0.0870268 0.00678368 0.0133139 0.00534889 1 0 +0.047278 0.0610762 0.0980137 0.0060369 1 0 +0.178609 0.136398 0.0584243 0.0056634 1 0 +0.202349 0.1576 0.189478 0.00649183 1 0 +0.270943 0.132336 0.191526 0.00832911 1 0 +0.110306 0.122519 0.0741579 0.00656935 1 0 +0.114864 0.0526981 0.143973 0.00623323 1 0 +0.293011 0.0684381 0.183414 0.00565701 1 0 +0.274135 0.0932081 0.160943 0.00563774 1 0 +0.303815 0.140585 0.0694233 0.00626015 1 0 +0.302754 0.191723 0.0147281 0.00550898 1 0 +0.127051 0.00535971 0.111888 0.0055462 1 0 +0.16405 0.0356627 0.0568418 0.00542532 1 0 +0.216114 0.15051 0.19106 0.00731595 1 0 +0.0585163 0.0784101 0.193673 0.00501427 1 0 +0.164093 0.159871 0.117253 0.00903389 1 0 +0.171427 0.150318 0.145285 0.00498805 1 0 +0.01 0.178751 0.0212145 0.00593001 1 0 +0.126596 0.135693 0.0659915 0.00700331 1 0 +0.142394 0.0700104 0.010219 0.00549211 1 0 +0.28283 0.0597229 0.012684 0.00496416 1 0 +0.346125 0.065843 0.0811333 0.00549911 1 0 +0.0192994 0.155884 0.0432481 0.00504566 1 0 +0.0620351 0.177468 0.141743 0.00507261 1 0 +0.395852 0.0603702 0.179532 0.00498008 1 0 +0.196962 0.184654 0.0324529 0.0051657 1 0 +0.279714 0.0566011 0.19426 0.005438 1 0 +0.33 0.0204775 0.0218146 0.00605239 1 0 +0.166458 0.103195 0.147391 0.00591854 1 0 +0.0617074 0.043718 0.0241343 0.00501687 1 0 +0.193501 0.0107412 0.111082 0.00846357 1 0 +0.196351 0.0464952 0.106671 0.00548262 1 0 +0.075906 0.0671244 0.160934 0.00537679 1 0 +0.309361 0.00933187 0.0990245 0.0052889 1 0 +0.0627181 0.0634413 0.168156 0.00501112 1 0 +0.046994 0.112873 0.0364355 0.00532616 1 0 +0.183407 0.0707565 0.00836969 0.00530652 1 0 +0.232014 0.188669 0.181532 0.00507663 1 0 +0.0555723 0.144624 0.121058 0.00569052 1 0 +0.0343206 0.0954366 0.0503161 0.0050001 1 0 +0.0290338 0.173999 0.187396 0.00492787 1 0 +0.309574 0.0839854 0.160007 0.00514783 1 0 +0.0969705 0.169712 0.184873 0.00518084 1 0 +0.0658485 0.150003 0.179707 0.0062944 1 0 +0.108433 0.119435 0.163223 0.00507743 1 0 +0.206247 0.0766513 0.105902 0.0066949 1 0 +0.0864436 0.0643843 0.103629 0.00657068 1 0 +0.182244 0.133136 0.104595 0.00617659 1 0 +0.0471952 0.00624242 0.0945309 0.00649479 1 0 +0.0527607 0.0109981 0.11591 0.00611499 1 0 +0.10993 0.119343 0.0931493 0.0072709 1 0 +0.103207 0.0969803 0.129359 0.00654085 1 0 +0.170271 0.0187429 0.167282 0.00512471 1 0 +0.164726 0.0409384 0.179983 0.00554499 1 0 +0.326465 0.0283705 0.135593 0.00524226 1 0 +0.140623 0.0711734 0.0772951 0.00581126 1 0 +0.0850976 0.191205 0.0768463 0.00685597 1 0 +0.09 0.183533 0.0269557 0.00709967 1 0 +0.375296 0.139669 0.0715155 0.00536797 1 0 +0.156343 0.119418 0.0174718 0.00496351 1 0 +0.201745 0.155548 0.03555 0.00549744 1 0 +0.283227 0.187762 0.109832 0.00782879 1 0 +0.0651253 0.0846026 0.00611225 0.00671016 1 0 +0.165103 0.164994 0.0433823 0.00517184 1 0 +0.230469 0.142552 0.0262646 0.00565048 1 0 +0.0644654 0.0126289 0.0524089 0.00567865 1 0 +0.193453 0.160545 0.0700145 0.00542249 1 0 +0.169417 0.181557 0.0684874 0.00516442 1 0 +0.334088 0.127733 0.102483 0.00550088 1 0 +0.0956108 0.121131 0.123297 0.00975789 1 0 +0.123709 0.127416 0.188492 0.00539229 1 0 +0.0979571 0.108096 0.156332 0.00547353 1 0 +0.251707 0.016391 0.19019 0.00624543 1 0 +0.0661633 0.179022 0.041344 0.00492435 1 0 +0.355416 0.193746 0.0841065 0.00589224 1 0 +0.347893 0.18288 0.0297822 0.00510758 1 0 +0.136844 0.0199226 0.083017 0.00534729 1 0 +0.39235 0.147579 0.183273 0.00580378 1 0 +0.01 0.0411491 0.0267012 0.00533574 1 0 +0.0588663 0.13177 0.102265 0.00539944 1 0 +0.0978903 0.11469 0.0541935 0.00643746 1 0 +0.308283 0.122379 0.18741 0.00743573 1 0 +0.381485 0.00477065 0.0532761 0.0053672 1 0 +0.274681 0.123917 0.0450574 0.00494249 1 0 +0.274508 0.141622 0.0266256 0.00580575 1 0 +0.170151 0.0674314 0.143951 0.00518518 1 0 +0.158682 0.0638484 0.15911 0.0051452 1 0 +0.378616 0.181401 0.180389 0.00548654 1 0 +0.0459767 0.150753 0.0466611 0.00506987 1 0 +0.0454381 0.151814 0.0341858 0.00532984 1 0 +0.188361 0.0534292 0.0140177 0.00568027 1 0 +0.217714 0.125165 0.0343887 0.00503698 1 0 +0.110836 0.126499 0.0383373 0.00694862 1 0 +0.096231 0.130024 0.0342919 0.0058512 1 0 +0.201951 0.139456 0.0361176 0.00511319 1 0 +0.191894 0.0976474 0.14744 0.00647568 1 0 +0.35 0.171579 0.181703 0.00547433 1 0 +0.30885 0.0795745 0.0344666 0.00514951 1 0 +0.296265 0.116872 0.0586386 0.00494227 1 0 +0.314284 0.103333 0.0673027 0.00555231 1 0 +0.092611 0.0159843 0.19321 0.0053434 1 0 +0.13 0.0210197 0.163043 0.00513412 1 0 +0.027443 0.148809 0.189908 0.00511117 1 0 +0.121856 0.159182 0.118894 0.00508099 1 0 +0.156883 0.181267 0.0743892 0.0062106 1 0 +0.363206 0.0564425 0.0982633 0.00501058 1 0 +0.00889184 0.161165 0.0418842 0.00670472 1 0 +0.39 0.156352 0.032948 0.00717421 1 0 +0.196193 0.191986 0.0554252 0.00542604 1 0 +0.212932 0.0369344 0.0594619 0.00530686 1 0 +0.246057 0.0820164 0.0902824 0.00684233 1 0 +0.262212 0.194978 0.0528194 0.005386 1 0 +0.349212 0.161169 0.102484 0.00696689 1 0 +0.0212278 0.053785 0.165676 0.00526682 1 0 +0.25 0.0222123 0.160308 0.00595556 1 0 +0.13 0.0338003 0.0415081 0.00571234 1 0 +0.251398 0.0134418 0.052878 0.00555353 1 0 +0.283035 0.0380155 0.017362 0.00527007 1 0 +0.256399 0.00663239 0.0405731 0.00536133 1 0 +0.277872 0.0151422 0.0301644 0.00501034 1 0 +0.29 0.0331873 0.156387 0.00535195 1 0 +0.310051 0.159968 0.0644506 0.00626058 1 0 +0.23 0.0400195 0.0252136 0.00494321 1 0 +0.00597915 0.192857 0.113782 0.00614966 1 0 +0.26478 0.0856209 0.0621084 0.0058759 1 0 +0.170888 0.0249568 0.149919 0.00528423 1 0 +0.0862489 0.178614 0.156334 0.00675943 1 0 +0.0765856 0.148868 0.166939 0.00690831 1 0 +0.0769549 0.152726 0.14279 0.00842565 1 0 +0.374428 0.101105 0.0515387 0.00506034 1 0 +0.208821 0.0315649 0.0177298 0.00519243 1 0 +0.213451 0.0158765 0.0324657 0.00576198 1 0 +0.2004 0.0441061 0.0243762 0.00519107 1 0 +0.21 0.0282458 0.0431743 0.00602494 1 0 +0.258037 0.0375442 0.0171449 0.00503405 1 0 +0.13 0.0397069 0.163478 0.00538679 1 0 +0.341736 0.0212705 0.157989 0.00494933 1 0 +0.33 0.0107095 0.169753 0.00926678 1 0 +0.39 0.0244424 0.159316 0.00565326 1 0 +0.123273 0.184883 0.0328546 0.00550323 1 0 +0.0582003 0.154851 0.0294708 0.00526422 1 0 +0.0687158 0.163556 0.0197289 0.0049326 1 0 +0.343047 0.152234 0.0418529 0.00666205 1 0 +0.260138 0.181753 0.0392608 0.00496383 1 0 +0.323838 0.181046 0.0401489 0.00548408 1 0 +0.39 0.17758 0.03934 0.00564262 1 0 +0.0114031 0.169948 0.157271 0.00536005 1 0 +0.105449 0.182977 0.175277 0.00503166 1 0 +0.181274 0.184416 0.182946 0.0067919 1 0 +0.17 0.174714 0.158959 0.00562471 1 0 +0.243437 0.155565 0.17436 0.0054662 1 0 +0.198594 0.177974 0.183018 0.00533083 1 0 +0.357939 0.155902 0.154586 0.00588195 1 0 +0.0678736 0.0194877 0.193962 0.00587428 1 0 +0.261094 0.031613 0.143196 0.00641111 1 0 +0.0544616 0.0115973 0.103615 0.00631154 1 0 +0.300243 0.18462 0.180876 0.00679718 1 0 +0.381482 0.00919082 0.15216 0.00582408 1 0 +0.3597 0.172902 0.127903 0.00513018 1 0 +0.27607 0.152288 0.115059 0.00497727 1 0 +0.33 0.165643 0.156947 0.00701088 1 0 +0.168644 0.0373118 0.1599 0.00517157 1 0 +0.110805 0.0247741 0.0426208 0.00646617 1 0 +0.277394 0.0310717 0.04549 0.00527087 1 0 +0.370097 0.00686026 0.142097 0.00622595 1 0 +0.10914 0.070801 0.0234014 0.00536059 1 0 +0.062817 0.0164829 0.177108 0.00493717 1 0 +0.340361 0.121672 0.136124 0.00623336 1 0 +0.177069 0.166193 0.116547 0.00541707 1 0 +0.0717111 0.0222557 0.159741 0.00529445 1 0 +0.173048 0.126401 0.105637 0.00526965 1 0 +0.370588 0.141092 0.00823424 0.00735295 1 0 +0.311732 0.112762 0.10386 0.00566651 1 0 +0.224861 0.0108757 0.0111513 0.00580825 1 0 +0.282403 0.0378644 0.150256 0.00501103 1 0 +0.183624 0.0128695 0.0433923 0.00525007 1 0 +0.342352 0.149326 0.0994858 0.00704412 1 0 +0.23 0.164717 0.0399481 0.00506231 1 0 +0.342001 0.179746 0.00588706 0.0062753 1 0 +0.198251 0.0879786 0.0521409 0.00683441 1 0 +0.0955366 0.15961 0.122925 0.00519878 1 0 +0.00593014 0.161319 0.084341 0.00603019 1 0 +0.18029 0.154792 0.145996 0.00496599 1 0 +0.0281417 0.143236 0.173569 0.0049881 1 0 +0.352748 0.152499 0.0343066 0.00563147 1 0 +0.260024 0.10054 0.0972566 0.00498755 1 0 +0.159265 0.0614676 0.177132 0.00496797 1 0 +0.273013 0.066809 0.168266 0.00526 1 0 +0.0961053 0.158912 0.0425576 0.00541757 1 0 +0.38805 0.112274 0.0864649 0.00526447 1 0 +0.264119 0.0605599 0.0966643 0.00523095 1 0 +0.329591 0.159137 0.176464 0.00586786 1 0 +0.0730028 0.0412287 0.106009 0.00531919 1 0 +0.264012 0.178833 0.14807 0.00573042 1 0 +0.263282 0.135521 0.013621 0.00547357 1 0 +0.0967876 0.145384 0.10407 0.00751144 1 0 +0.230225 0.160641 0.0228926 0.00528607 1 0 +0.325794 0.0266551 0.0951936 0.00597996 1 0 +0.149199 0.0800426 0.0786346 0.00659869 1 0 +0.238664 0.165512 0.184353 0.00509723 1 0 +0.21 0.0406293 0.1602 0.00757887 1 0 +0.187799 0.0417546 0.177593 0.00538602 1 0 +0.0331917 0.0228511 0.156774 0.0065044 1 0 +0.216288 0.0735893 0.0648644 0.00554062 1 0 +0.19795 0.00662434 0.163804 0.00522254 1 0 +0.332126 0.00415627 0.0198734 0.0051572 1 0 +0.205507 0.0201568 0.158537 0.00588187 1 0 +0.170897 0.173902 0.120927 0.00523435 1 0 +0.248516 0.014429 0.120122 0.005256 1 0 +0.0361242 0.15799 0.157262 0.00581867 1 0 +0.138548 0.147702 0.123506 0.00907352 1 0 +0.149799 0.0372451 0.151687 0.00499184 1 0 +0.177965 0.159118 0.180328 0.00514079 1 0 +0.392538 0.0189895 0.180817 0.00561277 1 0 +0.382243 0.101195 0.0750976 0.00609491 1 0 +0.0836357 0.0219441 0.00512935 0.00496743 1 0 +0.33 0.161926 0.0377196 0.00499244 1 0 +0.336211 0.177032 0.132754 0.00506622 1 0 +0.0532224 0.134385 0.114407 0.0055477 1 0 +0.276116 0.156167 0.163396 0.0058125 1 0 +0.0348003 0.115308 0.0677341 0.00607577 1 0 +0.0440233 0.146143 0.166868 0.00519442 1 0 +0.120917 0.0561351 0.0994865 0.00526409 1 0 +0.256449 0.107859 0.061902 0.00687425 1 0 +0.171845 0.0215487 0.117558 0.00692348 1 0 +0.255654 0.161876 0.0169731 0.00532026 1 0 +0.144031 0.154944 0.170114 0.00558662 1 0 +0.145005 0.0179794 0.0223569 0.00509841 1 0 +0.128038 0.0977233 0.174511 0.00514635 1 0 +0.201338 0.0257893 0.133051 0.00532102 1 0 +0.166681 0.100641 0.0706421 0.00550443 1 0 +0.12131 0.166423 0.0453309 0.00579703 1 0 +0.0658001 0.154509 0.11645 0.00642682 1 0 +0.0572581 0.165887 0.155183 0.0056194 1 0 +0.187662 0.191443 0.00509992 0.00542144 1 0 +0.0944134 0.0383697 0.0973631 0.0068424 1 0 +0.318418 0.111156 0.144915 0.00537137 1 0 +0.286851 0.123289 0.175289 0.00521646 1 0 +0.161594 0.113297 0.134391 0.00755107 1 0 +0.18948 0.156254 0.0181854 0.00526632 1 0 +0.177931 0.155843 0.0650242 0.00562178 1 0 +0.100584 0.0462843 0.173604 0.00525411 1 0 +0.169914 0.139955 0.1501 0.00653871 1 0 +0.263616 0.116388 0.0769037 0.00648812 1 0 +0.31668 0.128179 0.105431 0.00518582 1 0 +0.100492 0.0735251 0.0144766 0.00492659 1 0 +0.132923 0.17577 0.18249 0.00547233 1 0 +0.34078 0.0083426 0.186336 0.00818907 1 0 +0.29936 0.142034 0.0951274 0.00620868 1 0 +0.269367 0.0277092 0.18182 0.00525473 1 0 +0.325954 0.0773823 0.00821037 0.00871461 1 0 +0.385832 0.013629 0.080827 0.0053703 1 0 +0.271175 0.194405 0.138624 0.00629957 1 0 +0.176297 0.1553 0.0249634 0.00597416 1 0 +0.367395 0.155817 0.0259312 0.00650447 1 0 +0.242305 0.045509 0.129572 0.00581364 1 0 +0.170596 0.167333 0.189563 0.00681678 1 0 +0.143312 0.0364464 0.184318 0.0050612 1 0 +0.0838401 0.0723796 0.0732097 0.00542222 1 0 +0.164174 0.164756 0.146673 0.00878473 1 0 +0.343979 0.0134402 0.16495 0.00576487 1 0 +0.296169 0.0723779 0.0760541 0.00519839 1 0 +0.343016 0.131144 0.0620836 0.00527033 1 0 +0.176588 0.144532 0.0358948 0.0059744 1 0 +0.257508 0.0394345 0.133334 0.00567279 1 0 +0.0289996 0.142579 0.113386 0.00555155 1 0 +0.11092 0.182559 0.0602423 0.00608611 1 0 +0.0607705 0.0760141 0.076644 0.00607164 1 0 +0.16066 0.0425497 0.147777 0.00509363 1 0 +0.0715801 0.0833763 0.104049 0.00535127 1 0 +0.15 0.170787 0.157919 0.00570278 1 0 +0.327064 0.164185 0.0820805 0.00526258 1 0 +0.317055 0.15458 0.171205 0.00574498 1 0 +0.347806 0.153938 0.0778674 0.00703853 1 0 +0.293947 0.171232 0.133718 0.00556929 1 0 +0.156684 0.149419 0.111223 0.00512589 1 0 +0.252732 0.131396 0.0968162 0.00549874 1 0 +0.01425 0.0215658 0.0708055 0.00532287 1 0 +0.0763223 0.156665 0.176082 0.00511058 1 0 +0.124446 0.127437 0.039473 0.00674084 1 0 +0.359546 0.118236 0.0804413 0.00511047 1 0 +0.0887918 0.0346958 0.0417275 0.00539093 1 0 +0.177854 0.108789 0.142292 0.00537027 1 0 +0.0968535 0.0707884 0.111793 0.00515489 1 0 +0.302334 0.145584 0.0277289 0.00572278 1 0 +0.3148 0.0091965 0.159974 0.00610153 1 0 +0.0650404 0.077735 0.111273 0.00583579 1 0 +0.35 0.157852 0.167876 0.00587751 1 0 +0.0687925 0.0473 0.0986019 0.00505409 1 0 +0.351393 0.00584318 0.10956 0.00633544 1 0 +0.250902 0.0661362 0.0970678 0.00614813 1 0 +0.199256 0.151144 0.160893 0.00572317 1 0 +0.081642 0.174244 0.0457985 0.00526858 1 0 +0.21976 0.170596 0.149192 0.00673337 1 0 +0.310973 0.131476 0.0090823 0.00770685 1 0 +0.171225 0.0350769 0.148616 0.00492495 1 0 +0.0422254 0.0146582 0.108313 0.00714824 1 0 +0.25391 0.150461 0.103458 0.00533219 1 0 +0.328306 0.142198 0.15002 0.00511059 1 0 +0.382755 0.00781137 0.0192031 0.00505643 1 0 +0.127577 0.145161 0.167248 0.0062849 1 0 +0.0600214 0.130019 0.0487371 0.0049201 1 0 +0.110918 0.0619143 0.156475 0.00676531 1 0 +0.293273 0.158535 0.00771861 0.00518306 1 0 +0.1195 0.145898 0.0623842 0.0059389 1 0 +0.109317 0.0465481 0.165315 0.00531158 1 0 +0.315361 0.10605 0.0312039 0.007184 1 0 +0.0817801 0.153306 0.155881 0.00553843 1 0 +0.0487354 0.0470424 0.0517937 0.005316 1 0 +0.37 0.0372783 0.160129 0.0058242 1 0 +0.0778138 0.0743938 0.0561003 0.00531565 1 0 +0.346521 0.143338 0.049699 0.00569743 1 0 +0.307383 0.11418 0.156362 0.00575875 1 0 +0.277046 0.158534 0.144477 0.00524865 1 0 +0.13 0.0361391 0.180559 0.00578534 1 0 +0.206529 0.0269038 0.0694898 0.00506644 1 0 +0.358316 0.0430172 0.177113 0.00492916 1 0 +0.00654987 0.0696918 0.0295163 0.00614741 1 0 +0.0899527 0.0570072 0.172679 0.00604206 1 0 +0.202863 0.180004 0.154309 0.00552596 1 0 +0.148523 0.177151 0.140333 0.00532119 1 0 +0.13307 0.0630071 0.121302 0.00599325 1 0 +0.0780002 0.0448977 0.160222 0.00513026 1 0 +0.238688 0.0248954 0.150031 0.0050339 1 0 +0.33353 0.0754906 0.0249002 0.00582627 1 0 +0.256402 0.0357335 0.152655 0.00492271 1 0 +0.234174 0.0957673 0.0900119 0.00807268 1 0 +0.192198 0.127593 0.100147 0.00505887 1 0 +0.00825974 0.126987 0.0225671 0.00501073 1 0 +0.13 0.174395 0.159837 0.00492019 1 0 +0.367878 0.149816 0.162361 0.00544743 1 0 +0.274419 0.0773807 0.019342 0.00563982 1 0 +0.262616 0.0135154 0.193305 0.00545901 1 0 +0.306531 0.148053 0.0546813 0.00499252 1 0 +0.0517323 0.176959 0.0189973 0.00510163 1 0 +0.237788 0.0894483 0.0749374 0.00714378 1 0 +0.0893069 0.189382 0.152218 0.00516745 1 0 +0.0323866 0.126165 0.0357112 0.00522841 1 0 +0.314882 0.0714174 0.0349756 0.00500877 1 0 +0.33603 0.1587 0.0660504 0.00571842 1 0 +0.108816 0.151105 0.169317 0.00516 1 0 +0.364708 0.051628 0.167371 0.00626463 1 0 +0.100349 0.108523 0.130208 0.0053807 1 0 +0.170666 0.0892365 0.068069 0.0057705 1 0 +0.0999165 0.142379 0.0745268 0.00561466 1 0 +0.0597816 0.185221 0.027062 0.0055033 1 0 +0.238633 0.179517 0.0175771 0.005709 1 0 +0.107246 0.085266 0.131911 0.00559302 1 0 +0.0861326 0.0451846 0.187098 0.00520339 1 0 +0.0637139 0.192531 0.157391 0.00495527 1 0 +0.267139 0.069563 0.0327794 0.00499077 1 0 +0.0864614 0.191535 0.0401115 0.0049512 1 0 +0.0237336 0.120119 0.0736767 0.00535296 1 0 +0.211425 0.00508826 0.0340575 0.00523903 1 0 +0.0575627 0.0483748 0.151254 0.00577812 1 0 +0.243244 0.0831797 0.102024 0.00528776 1 0 +0.261577 0.0796649 0.114792 0.00550874 1 0 +0.103909 0.0607845 0.0205837 0.00494194 1 0 +0.227431 0.195448 0.0180965 0.00494513 1 0 +0.29699 0.120724 0.180025 0.00600489 1 0 +0.122431 0.0146929 0.166487 0.00531487 1 0 +0.356335 0.0460945 0.0299408 0.00548702 1 0 +0.25 0.165995 0.040633 0.0051361 1 0 +0.245868 0.116582 0.0693421 0.00872676 1 0 +0.215962 0.117685 0.0750916 0.0073751 1 0 +0.0934349 0.0583919 0.0966913 0.00495829 1 0 +0.359399 0.151389 0.059909 0.00510121 1 0 +0.259602 0.143109 0.0989046 0.00502089 1 0 +0.0356892 0.0457494 0.032632 0.00527195 1 0 +0.180834 0.0330256 0.1308 0.0051692 1 0 +0.0631825 0.193286 0.183238 0.00541475 1 0 +0.343546 0.167816 0.0445428 0.00512738 1 0 +0.239506 0.0748118 0.116467 0.00503154 1 0 +0.365153 0.133028 0.0180349 0.0054654 1 0 +0.214868 0.00513958 0.006055 0.00538041 1 0 +0.0779447 0.00933514 0.133271 0.00725011 1 0 +0.37 0.171148 0.0439746 0.00681272 1 0 +0.106816 0.139283 0.119261 0.00843925 1 0 +0.394734 0.0786798 0.128928 0.00549414 1 0 +0.096486 0.153221 0.187963 0.00829006 1 0 +0.17 0.156997 0.0352682 0.00551604 1 0 +0.0556346 0.033688 0.15095 0.0053515 1 0 +0.226596 0.0350846 0.043546 0.00590146 1 0 +0.0777909 0.165915 0.148506 0.00597243 1 0 +0.340627 0.165863 0.150643 0.00521091 1 0 +0.393511 0.111386 0.0591638 0.00504775 1 0 +0.324017 0.170214 0.108676 0.00527019 1 0 +0.278548 0.116772 0.191291 0.00493263 1 0 +0.208638 0.131575 0.0885691 0.00509302 1 0 +0.24394 0.127435 0.0543049 0.00545812 1 0 +0.233082 0.0215367 0.159692 0.00502445 1 0 +0.0802463 0.0952664 0.128303 0.00587113 1 0 +0.35373 0.142419 0.0794802 0.00578166 1 0 +0.332804 0.119654 0.0538804 0.00495888 1 0 +0.14254 0.0523675 0.0987686 0.00593462 1 0 +0.238221 0.0238557 0.0159596 0.005277 1 0 +0.276343 0.155965 0.0355368 0.00552434 1 0 +0.389898 0.166025 0.0423102 0.00628806 1 0 +0.20734 0.0934415 0.107873 0.00881958 1 0 +0.113326 0.0465527 0.155473 0.00531565 1 0 +0.287784 0.152427 0.0201663 0.0053834 1 0 +0.19 0.174441 0.0195105 0.00515748 1 0 +0.0933412 0.0662663 0.0868433 0.0056423 1 0 +0.0535527 0.170707 0.140558 0.00502638 1 0 +0.056345 0.0447936 0.167543 0.00543527 1 0 +0.319714 0.173203 0.0138602 0.00532904 1 0 +0.318993 0.159075 0.14688 0.00515332 1 0 +0.132172 0.073621 0.0140572 0.00541312 1 0 +0.186738 0.0355362 0.152613 0.00527402 1 0 +0.354687 0.148664 0.144741 0.00546032 1 0 +0.143956 0.0873215 0.111676 0.00507434 1 0 +0.33 0.0263341 0.0406133 0.00503598 1 0 +0.239281 0.184703 0.0268573 0.00494154 1 0 +0.317241 0.121443 0.147915 0.0054086 1 0 +0.0155774 0.155562 0.0334974 0.00539621 1 0 +0.0467048 0.164738 0.157204 0.0051868 1 0 +0.0907433 0.128642 0.136023 0.00580039 1 0 +0.226558 0.0050429 0.0680593 0.00546214 1 0 +0.160818 0.101567 0.0900302 0.00542622 1 0 +0.0578329 0.0527018 0.0826996 0.00535188 1 0 +0.190631 0.0849595 0.120363 0.00498892 1 0 +0.0855674 0.0739422 0.0906115 0.00557382 1 0 +0.079236 0.0838242 0.173243 0.00524543 1 0 +0.317096 0.134697 0.0196558 0.00492943 1 0 +0.22179 0.164076 0.0472665 0.005254 1 0 +0.035521 0.024448 0.141908 0.00500036 1 0 +0.121423 0.153796 0.0454467 0.00630561 1 0 +0.243375 0.157456 0.0401662 0.0056814 1 0 +0.216065 0.156221 0.0151389 0.00496757 1 0 +0.288644 0.161478 0.0391054 0.00517417 1 0 +0.305165 0.129215 0.093237 0.00602047 1 0 +0.326572 0.0931579 0.0556699 0.0057005 1 0 +0.142905 0.188128 0.00832213 0.00500349 1 0 +0.303122 0.183253 0.0234914 0.0050918 1 0 +0.0513195 0.131298 0.0421085 0.00585023 1 0 +0.137058 0.141451 0.159999 0.00536984 1 0 +0.257599 0.0941042 0.134262 0.00512517 1 0 +0.19656 0.0472216 0.181399 0.0050835 1 0 +0.133371 0.0855867 0.135726 0.00558747 1 0 +0.297211 0.148201 0.0460843 0.00513294 1 0 +0.27 0.18092 0.166627 0.00518641 1 0 +0.117472 0.0642168 0.142115 0.00572231 1 0 +0.37979 0.152412 0.0390155 0.0053394 1 0 +0.21 0.0393582 0.0384033 0.00606831 1 0 +0.227068 0.0301736 0.0720983 0.00605722 1 0 +0.341191 0.0251477 0.0454776 0.00610949 1 0 +0.364764 0.147722 0.152537 0.00496601 1 0 +0.331248 0.0630576 0.114729 0.00602514 1 0 +0.187503 0.160421 0.160779 0.00526709 1 0 +0.0607547 0.036431 0.111339 0.00504444 1 0 +0.0249341 0.140233 0.059008 0.00500066 1 0 +0.277753 0.0891442 0.0799665 0.00500209 1 0 +0.241637 0.138237 0.158558 0.00502118 1 0 +0.107164 0.192643 0.156784 0.00567281 1 0 +0.121535 0.112179 0.124726 0.00558203 1 0 +0.12679 0.109329 0.188589 0.00564646 1 0 +0.192212 0.116073 0.195068 0.00500692 1 0 +0.176887 0.14295 0.103954 0.00502218 1 0 +0.200576 0.118743 0.0119407 0.00519626 1 0 +0.179161 0.124561 0.0950849 0.00562769 1 0 +0.101414 0.125129 0.0662452 0.00561635 1 0 +0.252938 0.0644826 0.194418 0.00534798 1 0 +0.241464 0.0230279 0.194617 0.00567851 1 0 +0.14585 0.154364 0.0333299 0.0049201 1 0 +0.13 0.162097 0.15985 0.00629393 1 0 +0.374508 0.0444107 0.028399 0.00521957 1 0 +0.163322 0.111215 0.109167 0.00495191 1 0 +0.325752 0.186974 0.117146 0.00507565 1 0 +0.235053 0.112389 0.0399197 0.0050449 1 0 +0.310614 0.134246 0.0798435 0.00715402 1 0 +0.337052 0.175091 0.108946 0.00633609 1 0 +0.258904 0.122522 0.0117143 0.00533354 1 0 +0.119023 0.128715 0.105223 0.0094116 1 0 +0.0621116 0.0146283 0.00623177 0.00653622 1 0 +0.0110308 0.159897 0.123493 0.00517159 1 0 +0.106056 0.161993 0.0567978 0.00669773 1 0 +0.319037 0.128427 0.186393 0.00494375 1 0 +0.0707372 0.138259 0.1358 0.00492486 1 0 +0.0951466 0.0410084 0.194366 0.00577235 1 0 +0.215184 0.0511051 0.0949013 0.00567067 1 0 +0.152345 0.15892 0.128689 0.00738893 1 0 +0.116686 0.0767524 0.0309955 0.00507058 1 0 +0.208835 0.142381 0.170932 0.00500633 1 0 +0.0834147 0.087441 0.0911852 0.00614729 1 0 +0.113349 0.0109536 0.0221073 0.00516509 1 0 +0.00816041 0.155564 0.108356 0.00548171 1 0 +0.0962057 0.0661762 0.15883 0.0061964 1 0 +0.327568 0.116904 0.148792 0.00548791 1 0 +0.164068 0.082941 0.0800338 0.00662652 1 0 +0.244047 0.0375086 0.0431852 0.00521327 1 0 +0.141397 0.176393 0.154309 0.00518206 1 0 +0.178245 0.168578 0.0633801 0.00529997 1 0 +0.160227 0.051309 0.0342094 0.00494937 1 0 +0.170286 0.124796 0.116129 0.00569779 1 0 +0.203984 0.106266 0.184599 0.0049814 1 0 +0.325401 0.161488 0.0918087 0.00496851 1 0 +0.306247 0.00665273 0.089148 0.00540779 1 0 +0.316238 0.015265 0.0931219 0.0055432 1 0 +0.240079 0.145881 0.0296839 0.00507972 1 0 +0.32467 0.132036 0.0980336 0.00576958 1 0 +0.157623 0.132844 0.122606 0.00866231 1 0 +0.169955 0.15008 0.108074 0.00561139 1 0 +0.194824 0.187497 0.0649106 0.00515682 1 0 +0.239326 0.0150519 0.181313 0.00509233 1 0 +0.318191 0.159187 0.0564939 0.00514918 1 0 +0.0792881 0.016866 0.180201 0.00517011 1 0 +0.260323 0.0685592 0.11658 0.00542457 1 0 +0.239286 0.106852 0.0810619 0.00706377 1 0 +0.334289 0.0982624 0.0122789 0.00499393 1 0 +0.226025 0.118987 0.0279885 0.00713664 1 0 +0.344845 0.189664 0.0855549 0.00553181 1 0 +0.253551 0.0766101 0.0827654 0.00506928 1 0 +0.345822 0.188583 0.154599 0.00880407 1 0 +0.0827703 0.0235384 0.0445821 0.00618839 1 0 +0.333985 0.040954 0.0412785 0.00507434 1 0 +0.278721 0.0594964 0.0952742 0.00546562 1 0 +0.019205 0.0449667 0.0239296 0.00500779 1 0 +0.149741 0.117969 0.0247541 0.00497227 1 0 +0.148458 0.0655767 0.112763 0.00500424 1 0 +0.185581 0.113329 0.0951075 0.00636539 1 0 +0.314931 0.172481 0.184316 0.00538804 1 0 +0.151307 0.191604 0.0841679 0.00742714 1 0 +0.215391 0.0778268 0.0526149 0.00509487 1 0 +0.116938 0.0537562 0.0111887 0.00632439 1 0 +0.123704 0.145119 0.105583 0.0063281 1 0 +0.349979 0.189299 0.0221952 0.00504719 1 0 +0.373207 0.0786809 0.0262685 0.00511944 1 0 +0.119987 0.0826799 0.127211 0.00497848 1 0 +0.119432 0.0344412 0.044668 0.00533614 1 0 +0.125114 0.0259125 0.148706 0.00512049 1 0 +0.0491786 0.102561 0.165362 0.00540046 1 0 +0.187451 0.00754914 0.195216 0.00498253 1 0 +0.0993061 0.0379254 0.016514 0.00499695 1 0 +0.134012 0.118456 0.140878 0.00687643 1 0 +0.274864 0.191863 0.165327 0.00519948 1 0 +0.338764 0.184065 0.0196136 0.00661261 1 0 +0.30909 0.155887 0.1521 0.00508481 1 0 +0.281038 0.179616 0.0434301 0.00495262 1 0 +0.284824 0.0573723 0.0853284 0.00514923 1 0 +0.152 0.0776878 0.110776 0.0062575 1 0 +0.352751 0.0402944 0.0504634 0.00495409 1 0 +0.06202 0.039623 0.0415275 0.00515143 1 0 +0.253638 0.0493509 0.152362 0.00593247 1 0 +0.17658 0.177998 0.016944 0.00513947 1 0 +0.315916 0.152921 0.0712626 0.00516184 1 0 +0.222567 0.157874 0.0384202 0.00498412 1 0 +0.178055 0.0404681 0.180863 0.00497207 1 0 +0.0640356 0.136959 0.117493 0.00598804 1 0 +0.269722 0.170038 0.152649 0.00571176 1 0 +0.252802 0.16212 0.0986709 0.00499185 1 0 +0.209844 0.0545797 0.177965 0.00554082 1 0 +0.13029 0.0507768 0.0401549 0.00497506 1 0 +0.366953 0.146216 0.0774128 0.00517086 1 0 +0.371563 0.0342949 0.0441009 0.00496373 1 0 +0.27 0.178611 0.0224494 0.00494871 1 0 +0.26675 0.138843 0.061017 0.00518781 1 0 +0.114366 0.178677 0.155083 0.0057361 1 0 +0.221864 0.0366978 0.0793678 0.00501016 1 0 +0.207612 0.0444648 0.193214 0.00627639 1 0 +0.271776 0.149236 0.0896579 0.00512745 1 0 +0.121741 0.14519 0.0354793 0.00576319 1 0 +0.262546 0.0115259 0.0141798 0.00560513 1 0 +0.300685 0.155898 0.0352115 0.00504962 1 0 +0.174803 0.111176 0.0338426 0.00552079 1 0 +0.104673 0.128424 0.132813 0.00527037 1 0 +0.315444 0.160233 0.189164 0.00572812 1 0 +0.251209 0.129378 0.074338 0.00601193 1 0 +0.190451 0.192302 0.115137 0.00546215 1 0 +0.249307 0.185526 0.00612246 0.00502701 1 0 +0.203934 0.0225659 0.18315 0.0051549 1 0 +0.302241 0.100337 0.134276 0.00542858 1 0 +0.347415 0.0902788 0.136673 0.00554412 1 0 +0.0795558 0.105547 0.122701 0.00494732 1 0 +0.301311 0.0235448 0.154751 0.00513585 1 0 +0.114762 0.0984224 0.124566 0.00605229 1 0 +0.0258172 0.13008 0.0854701 0.00587416 1 0 +0.192188 0.0787302 0.129962 0.00526632 1 0 +0.24031 0.16392 0.0162384 0.00504816 1 0 +0.173767 0.182968 0.0343716 0.00503739 1 0 +0.187492 0.121202 0.145904 0.00543631 1 0 +0.121307 0.0911066 0.163532 0.00760362 1 0 +0.306121 0.039887 0.0508405 0.00586732 1 0 +0.0313969 0.187535 0.116287 0.00526618 1 0 +0.205244 0.0530181 0.00474848 0.00533448 1 0 +0.318555 0.0988328 0.162645 0.00544252 1 0 +0.384049 0.150213 0.18977 0.00506271 1 0 +0.35882 0.0705367 0.0071012 0.00591425 1 0 +0.374241 0.107337 0.150027 0.0050843 1 0 +0.0585332 0.133693 0.194195 0.00493436 1 0 +0.0855199 0.118215 0.184505 0.0055736 1 0 +0.158235 0.138137 0.19419 0.00609273 1 0 +0.122416 0.07642 0.139554 0.0062318 1 0 +0.00479918 0.192651 0.081544 0.00545332 1 0 +0.27634 0.123768 0.072765 0.00597458 1 0 +0.0486482 0.0469649 0.145052 0.00514093 1 0 +0.19 0.0254881 0.159743 0.00501905 1 0 +0.37864 0.120691 0.00630193 0.00533048 1 0 +0.25 0.162741 0.161173 0.0051858 1 0 +0.0566476 0.069124 0.110854 0.00507997 1 0 +0.0116925 0.0406675 0.11733 0.0050623 1 0 +0.315558 0.141102 0.0689741 0.00550287 1 0 +0.180223 0.0123022 0.113459 0.00511602 1 0 +0.273171 0.138287 0.0113356 0.00504578 1 0 +0.26461 0.0518817 0.0868252 0.00543732 1 0 +0.229094 0.159702 0.0881235 0.00508333 1 0 +0.26882 0.0938501 0.113402 0.00500452 1 0 +0.349178 0.0540905 0.119842 0.00543585 1 0 +0.0760557 0.0196645 0.0539054 0.00560242 1 0 +0.242801 0.116421 0.034328 0.00532604 1 0 +0.313923 0.155856 0.0894238 0.00559722 1 0 +0.195836 0.105589 0.190249 0.00495681 1 0 +0.0559161 0.179048 0.149599 0.00500992 1 0 +0.252497 0.141266 0.0333844 0.00527091 1 0 +0.0868643 0.111262 0.116764 0.0049587 1 0 +0.350233 0.0345982 0.092011 0.00497733 1 0 +0.09 0.0412077 0.171813 0.00512945 1 0 +0.155742 0.155361 0.0307641 0.00526499 1 0 +0.15503 0.153165 0.0171135 0.00496063 1 0 +0.275704 0.0855324 0.120332 0.0067578 1 0 +0.102093 0.142054 0.139593 0.00507751 1 0 +0.239529 0.0907062 0.0290642 0.00558211 1 0 +0.206284 0.164971 0.15529 0.00676775 1 0 +0.033003 0.0442111 0.152608 0.00525452 1 0 +0.0275063 0.0428424 0.176527 0.0049388 1 0 +0.35862 0.149303 0.170872 0.00662706 1 0 +0.07 0.0215284 0.0227742 0.00496596 1 0 +0.115516 0.132417 0.0702031 0.00529467 1 0 +0.366673 0.153534 0.191388 0.00523087 1 0 +0.0213304 0.141411 0.194224 0.00541128 1 0 +0.106137 0.148663 0.0220844 0.00548936 1 0 +0.295259 0.0650414 0.0597985 0.0057072 1 0 +0.116732 0.150019 0.00547719 0.0057373 1 0 +0.298718 0.00847115 0.0998693 0.00542173 1 0 +0.103477 0.0980706 0.184761 0.00544981 1 0 +0.380526 0.154388 0.0254918 0.00504119 1 0 +0.270214 0.0877797 0.0223453 0.00543992 1 0 +0.314709 0.146188 0.157199 0.00492299 1 0 +0.0183423 0.184847 0.028443 0.00502048 1 0 +0.0872812 0.109532 0.177678 0.00554028 1 0 +0.104323 0.10894 0.166694 0.00671663 1 0 +0.310986 0.115453 0.0435155 0.0051215 1 0 +0.261494 0.0451588 0.14497 0.00564139 1 0 +0.214052 0.0206624 0.0755293 0.00529037 1 0 +0.106893 0.081013 0.0946225 0.00539707 1 0 +0.298594 0.155871 0.148747 0.00506892 1 0 +0.2684 0.0265539 0.125613 0.00542403 1 0 +0.271924 0.155568 0.128731 0.00627654 1 0 +0.190981 0.0881231 0.1063 0.00609074 1 0 +0.317668 0.162075 0.0759067 0.00525105 1 0 +0.161501 0.138712 0.135025 0.00560976 1 0 +0.275306 0.11263 0.0685627 0.0056332 1 0 +0.224296 0.141754 0.0863415 0.00509894 1 0 +0.162671 0.187559 0.127827 0.00547326 1 0 +0.215382 0.0994359 0.0865149 0.00813389 1 0 +0.175097 0.141131 0.121027 0.00551912 1 0 +0.142482 0.0456714 0.17182 0.00515071 1 0 +0.0871426 0.193219 0.0885682 0.00521211 1 0 +0.1382 0.120316 0.0420877 0.00498942 1 0 +0.182786 0.0783669 0.0664189 0.0067264 1 0 +0.139911 0.136595 0.136115 0.00610351 1 0 +0.153271 0.127392 0.136792 0.00714609 1 0 +0.134296 0.0259236 0.153496 0.00523579 1 0 +0.177675 0.144073 0.138335 0.00625251 1 0 +0.307612 0.0216726 0.0937214 0.00521856 1 0 +0.23 0.179957 0.176114 0.0053792 1 0 +0.190162 0.128682 0.112774 0.00604757 1 0 +0.0763513 0.183133 0.0817355 0.00601091 1 0 +0.202471 0.162916 0.0429757 0.0049886 1 0 +0.059156 0.0903555 0.0147954 0.00529511 1 0 +0.0774759 0.0907105 0.00848086 0.0064719 1 0 +0.104679 0.12904 0.107042 0.00505079 1 0 +0.156928 0.0667936 0.168618 0.00496171 1 0 +0.109435 0.114802 0.0340654 0.00526307 1 0 +0.202898 0.0988148 0.144118 0.00507812 1 0 +0.247038 0.0932036 0.0948382 0.00527664 1 0 +0.25441 0.0812233 0.0986122 0.00498023 1 0 +0.207974 0.0236615 0.0530072 0.00501154 1 0 +0.216544 0.0188464 0.0440366 0.0054604 1 0 +0.347109 0.15848 0.157316 0.00508872 1 0 +0.275296 0.175117 0.141214 0.00660876 1 0 +0.00496755 0.1067 0.119988 0.00513157 1 0 +0.297911 0.040944 0.040722 0.00499771 1 0 +0.159434 0.0956141 0.0793437 0.00573121 1 0 +0.283499 0.191069 0.14746 0.0062051 1 0 +0.0369525 0.147064 0.159164 0.00530303 1 0 +0.22607 0.161718 0.14422 0.00523933 1 0 +0.108679 0.0368119 0.0413441 0.00582451 1 0 +0.160892 0.158663 0.1593 0.00561418 1 0 +0.117058 0.136609 0.0401125 0.0050545 1 0 +0.307358 0.147503 0.0894396 0.00502678 1 0 +0.245616 0.147308 0.0379248 0.0049501 1 0 +0.392621 0.0111457 0.151269 0.00552043 1 0 +0.236105 0.110368 0.0613037 0.00536382 1 0 +0.245302 0.0805854 0.0785322 0.0050188 1 0 +0.0652638 0.0836976 0.083807 0.00535354 1 0 +0.32297 0.0769884 0.0234433 0.00493859 1 0 +0.0138609 0.156448 0.0232398 0.0050417 1 0 +0.360794 0.0355947 0.0448732 0.00591051 1 0 +0.178974 0.0989185 0.137006 0.00588317 1 0 +0.175662 0.152784 0.0430227 0.00496919 1 0 +0.0770389 0.0916468 0.0998569 0.00500567 1 0 +0.130172 0.156197 0.173118 0.0064809 1 0 +0.357743 0.0473451 0.116656 0.00592238 1 0 +0.195747 0.0966667 0.101135 0.00497218 1 0 +0.00530248 0.149842 0.121771 0.00585836 1 0 +0.306338 0.116316 0.00953368 0.00781098 1 0 +0.244299 0.0236894 0.182913 0.00500193 1 0 +0.215106 0.0280928 0.153081 0.00542675 1 0 +0.361435 0.15433 0.070999 0.00655125 1 0 +0.0932397 0.190976 0.141447 0.00640936 1 0 +0.153466 0.0468423 0.0457411 0.00518046 1 0 +0.191824 0.0385899 0.160921 0.00493513 1 0 +0.115354 0.154888 0.0550774 0.00509607 1 0 +0.0862296 0.163467 0.155967 0.00555456 1 0 +0.328104 0.185124 0.128874 0.00534291 1 0 +0.34386 0.153777 0.0544861 0.00600231 1 0 +0.162034 0.132633 0.144444 0.00561354 1 0 +0.360377 0.177149 0.0473056 0.00500698 1 0 +0.236277 0.19483 0.0243622 0.00525474 1 0 +0.118413 0.11688 0.0402421 0.0054435 1 0 +0.305835 0.155644 0.170986 0.00552785 1 0 +0.129544 0.141282 0.114632 0.00510482 1 0 +0.376786 0.018156 0.156706 0.00527047 1 0 +0.147745 0.185436 0.146696 0.00515421 1 0 +0.272266 0.15953 0.0903589 0.00520212 1 0 +0.22723 0.104227 0.0822697 0.00533343 1 0 +0.385255 0.175893 0.0649707 0.00515363 1 0 +0.23857 0.129915 0.0724236 0.00678225 1 0 +0.0351287 0.115887 0.0370834 0.00515281 1 0 +0.23379 0.0768889 0.102961 0.00529133 1 0 +0.311395 0.182793 0.00742186 0.00531421 1 0 +0.35 0.0342712 0.0193649 0.00502089 1 0 +0.163462 0.0941723 0.0980868 0.00582487 1 0 +0.220885 0.0883698 0.0979634 0.0050954 1 0 +0.381986 0.112109 0.0779516 0.00518927 1 0 +0.349757 0.149435 0.160687 0.00492043 1 0 +0.163168 0.149353 0.150924 0.00505876 1 0 +0.320919 0.180746 0.109715 0.00575719 1 0 +0.335675 0.0176508 0.0384298 0.00556491 1 0 +0.126986 0.115883 0.105086 0.00569101 1 0 +0.325076 0.0168704 0.0376704 0.0050906 1 0 +0.145427 0.117636 0.136501 0.00537628 1 0 +0.236591 0.177699 0.00704534 0.00517186 1 0 +0.149068 0.155496 0.116311 0.00586574 1 0 +0.155506 0.124715 0.148408 0.00498151 1 0 +0.170478 0.112538 0.118343 0.00557105 1 0 +0.25196 0.095997 0.0590623 0.00538771 1 0 +0.363179 0.151149 0.00658791 0.00524601 1 0 +0.333096 0.138546 0.101435 0.0054084 1 0 +0.0678159 0.156327 0.170413 0.0051171 1 0 +0.110907 0.0718823 0.0908004 0.00528436 1 0 +0.0145692 0.194365 0.0779644 0.0050922 1 0 +0.0956187 0.0132816 0.129183 0.00509384 1 0 +0.165112 0.161259 0.132262 0.00607402 1 0 +0.209558 0.0908923 0.0945273 0.00494699 1 0 +0.251909 0.00908388 0.014914 0.00533323 1 0 +0.261593 0.118391 0.0209312 0.00511847 1 0 +0.196188 0.0813525 0.0631632 0.00502461 1 0 +0.0258493 0.18743 0.124911 0.00498865 1 0 +0.360149 0.042735 0.161188 0.00548699 1 0 +0.35 0.0403613 0.161944 0.004963 1 0 +0.309527 0.124847 0.154487 0.00528182 1 0 +0.244683 0.101301 0.0655127 0.0055659 1 0 +0.325279 0.155186 0.066466 0.00559949 1 0 +0.226617 0.195266 0.00806071 0.00512528 1 0 +0.169953 0.0999186 0.108535 0.00504286 1 0 +0.137916 0.186472 0.184189 0.0064584 1 0 +0.0972162 0.134765 0.110854 0.00509749 1 0 +0.303273 0.0299333 0.0454584 0.00580123 1 0 +0.279745 0.0864832 0.0184272 0.0049462 1 0 +0.116074 0.0774425 0.154193 0.00638064 1 0 +0.248656 0.176256 0.0414543 0.00524495 1 0 +0.11719 0.0834206 0.0961585 0.00528837 1 0 +0.229539 0.118874 0.0684492 0.00511033 1 0 +0.173123 0.195374 0.111901 0.00501602 1 0 +0.136327 0.0396209 0.195201 0.0051349 1 0 +0.158862 0.147049 0.127001 0.00625819 1 0 +0.0991766 0.0235319 0.0445431 0.00538577 1 0 +0.201657 0.0487937 0.17149 0.00500199 1 0 +0.312662 0.0232805 0.0848822 0.00508772 1 0 +0.35945 0.135313 0.00927028 0.00523795 1 0 +0.104058 0.068495 0.17199 0.00518088 1 0 +0.037409 0.146655 0.174523 0.00493557 1 0 +0.148805 0.153965 0.160534 0.0051623 1 0 +0.328463 0.160841 0.0579076 0.00514102 1 0 +0.0221549 0.157353 0.194761 0.00500206 1 0 +0.322146 0.13593 0.192839 0.00542503 1 0 +0.286297 0.149768 0.00908993 0.00610423 1 0 +0.280655 0.166453 0.133761 0.00601319 1 0 +0.169994 0.185952 0.11743 0.00584323 1 0 +0.132045 0.045301 0.172359 0.00530653 1 0 +0.0777551 0.161616 0.131273 0.00552788 1 0 +0.199873 0.172401 0.0460262 0.00510947 1 0 +0.272171 0.0594096 0.160745 0.00532387 1 0 +0.165046 0.00600323 0.109875 0.00521884 1 0 +0.142337 0.0812874 0.0878795 0.00498215 1 0 +0.0924792 0.0732765 0.150215 0.00557322 1 0 +0.056255 0.16237 0.117474 0.00507203 1 0 +0.175249 0.16002 0.136546 0.00500027 1 0 +0.155973 0.175395 0.149447 0.00493138 1 0 +0.118544 0.152403 0.165247 0.00546414 1 0 +0.343404 0.159514 0.0899321 0.00495426 1 0 +0.128004 0.141325 0.154959 0.0049926 1 0 +0.244713 0.0307325 0.155689 0.00508441 1 0 +0.181902 0.141143 0.112709 0.00522801 1 0 +0.307729 0.138997 0.0192935 0.00507574 1 0 +0.331633 0.0900834 0.00664554 0.00528612 1 0 +0.216766 0.0824641 0.108869 0.00568365 1 0 +0.211694 0.10536 0.101931 0.00519168 1 0 +0.179299 0.0286834 0.154585 0.00503082 1 0 +0.00631072 0.147677 0.0345265 0.00543485 1 0 +0.332261 0.0910282 0.0655754 0.00591895 1 0 +0.326055 0.0639492 0.104676 0.00532498 1 0 +0.104297 0.0712327 0.152123 0.0049905 1 0 +0.337664 0.118813 0.146655 0.00500696 1 0 +0.16796 0.0880324 0.0899783 0.00520396 1 0 +0.151126 0.144095 0.117993 0.0051249 1 0 +0.167151 0.122918 0.127015 0.00578551 1 0 +0.347149 0.181876 0.140827 0.00657185 1 0 +0.353949 0.191197 0.140731 0.00496706 1 0 +0.28435 0.189917 0.0435792 0.00586862 1 0 +0.222213 0.0143369 0.0721368 0.00557794 1 0 +0.279042 0.0945755 0.11326 0.00519755 1 0 +0.210267 0.0545971 0.188648 0.00515023 1 0 +0.317678 0.147377 0.193662 0.00621996 1 0 +0.250707 0.128303 0.0628843 0.00550307 1 0 +0.0200183 0.12112 0.083305 0.00501581 1 0 +0.177239 0.118432 0.146594 0.00520612 1 0 +0.111327 0.0824235 0.166339 0.00591941 1 0 +0.155479 0.146328 0.186009 0.0050508 1 0 +0.0919961 0.140076 0.142581 0.00563671 1 0 +0.269316 0.144037 0.131719 0.00560771 1 0 +0.150699 0.139069 0.133101 0.00536768 1 0 +0.209042 0.108432 0.0794174 0.00496219 1 0 +0.216699 0.112429 0.0861932 0.00492981 1 0 +0.101038 0.133943 0.0973033 0.00644403 1 0 +0.0966487 0.120678 0.107036 0.00523101 1 0 +0.202327 0.02737 0.0603728 0.0049831 1 0 +0.193253 0.0784818 0.0730075 0.00564155 1 0 +0.0868005 0.12796 0.110006 0.00531148 1 0 +0.238097 0.139762 0.0654122 0.00531521 1 0 +0.154478 0.00735788 0.109484 0.00544259 1 0 +0.103283 0.128144 0.0876139 0.0050692 1 0 +0.270558 0.16535 0.0986312 0.00505513 1 0 +0.157723 0.105315 0.0744188 0.00528203 1 0 +0.169762 0.151644 0.135452 0.00507249 1 0 +0.125053 0.149402 0.11914 0.00521138 1 0 +0.268898 0.165729 0.134488 0.00578782 1 0 +0.170716 0.13527 0.138964 0.00498681 1 0 +0.171308 0.127188 0.14667 0.00536928 1 0 +0.277086 0.157709 0.00506657 0.00534523 1 0 +0.1712 0.00837622 0.119438 0.00530001 1 0 +0.355425 0.144238 0.00571681 0.00517774 1 0 +0.166652 0.083414 0.0995339 0.00524142 1 0 +0.119505 0.139675 0.114664 0.00506236 1 0 +0.204039 0.110165 0.106515 0.00494264 1 0 +0.39225 0.188542 0.150983 0.00565893 1 0 +0.148649 0.136782 0.143078 0.00506248 1 0 +0.22953 0.112953 0.0768692 0.0051831 1 0 +0.0694713 0.0871968 0.0941546 0.00546271 1 0 +0.259241 0.149048 0.128677 0.00604828 1 0 +0.170321 0.17059 0.134922 0.00493746 1 0 +0.275538 0.146728 0.00487594 0.00536114 1 0 +0.169881 0.149538 0.125133 0.00519197 1 0 +0.159116 0.151902 0.139595 0.00532538 1 0 +0.165307 0.125014 0.137586 0.00514797 1 0 +0.187238 0.105113 0.140191 0.00492454 1 0 +0.0980079 0.195089 0.151294 0.00527918 1 0 +0.0896188 0.149924 0.139441 0.00497002 1 0 +0.109522 0.139505 0.105479 0.0049669 1 0 +0.168901 0.107365 0.101898 0.00498716 1 0 +0.0467424 0.143688 0.176425 0.00504098 1 0 +0.258472 0.137915 0.130327 0.00523309 1 0 +0.264116 0.173093 0.0996929 0.00507272 1 0 +0.175687 0.0787602 0.0994242 0.00492242 1 0 +0.190441 0.0686624 0.0757399 0.00493151 1 0 diff --git a/src/Tools/padder/resources/padderexe/med2/concrete.med b/src/Tools/padder/resources/padderexe/med2/concrete.med new file mode 100644 index 0000000000000000000000000000000000000000..3b39b27a3e6d0ed6c6e0159d0615e16c8818ef80 GIT binary patch literal 32448 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 a&Zu9mqt$?NNUf5`Jis-%7l^~1G4MYr?XKVe literal 0 HcmV?d00001 diff --git a/src/Tools/padder/resources/padderexe/med2/ferrtran.med b/src/Tools/padder/resources/padderexe/med2/ferrtran.med new file mode 100644 index 0000000000000000000000000000000000000000..0a8667061f25ff4e48ebe4622baa6fddda09b619 GIT binary patch literal 17880 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 z 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%0A#);CDxAI@rs%X4(ovywUQwsL+-$!! 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 z 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%0A|>Sb*PI@8HBu2bqQU}d6# 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�g+Oty233I-s0Mg?I{f=}n44q_K6 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

                    ;hk8yGP4RMu1Ln3=n1pZ8+l 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_steelbar.png b/src/Tools/padder/doc/images/SMESH_spadder_inputdialog_steelbar.png new file mode 100644 index 0000000000000000000000000000000000000000..8e692dcffc45f6bbbe0e30e788e61e15e48b71e2 GIT binary patch literal 87059 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_menu.png b/src/Tools/padder/doc/images/SMESH_spadder_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..e8914069003519bd0c0855b1343677460f131c16 GIT binary patch literal 116970 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 literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_ready.png b/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_ready.png new file mode 100644 index 0000000000000000000000000000000000000000..f86790b0c6a2ce104c2063ecc1ec02dfb847d649 GIT binary patch literal 80869 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 bK0IM5P7g@5*JvsLe?pWVsliI*O@jUpLSxMy literal 0 HcmV?d00001 diff --git a/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_running.png b/src/Tools/padder/doc/images/SMESH_spadder_plugindialog_compute_running.png new file mode 100644 index 0000000000000000000000000000000000000000..b3c9ef169fcae15c1bf4657c37f70f25f641cea5 GIT binary patch literal 89493 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